From 4f5b59626937b7f1d94dc6d9b7690077ac76ebbb Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 1 Nov 2024 18:07:01 +0900 Subject: [PATCH 001/648] =?UTF-8?q?settings:=20nextjs=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=20=EC=A0=95=EB=A6=AC=20=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 37 +-------- app/fonts/GeistMonoVF.woff | Bin 67864 -> 0 bytes app/fonts/GeistVF.woff | Bin 66268 -> 0 bytes app/globals.css | 42 ---------- app/layout.tsx | 37 +++------ app/page.module.css | 165 ------------------------------------- app/page.tsx | 98 +--------------------- 7 files changed, 18 insertions(+), 361 deletions(-) delete mode 100644 app/fonts/GeistMonoVF.woff delete mode 100644 app/fonts/GeistVF.woff delete mode 100644 app/globals.css diff --git a/README.md b/README.md index e215bc4c..b2da838b 100644 --- a/README.md +++ b/README.md @@ -1,36 +1 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +# InvestMetic \ No newline at end of file diff --git a/app/fonts/GeistMonoVF.woff b/app/fonts/GeistMonoVF.woff deleted file mode 100644 index f2ae185cbfd16946a534d819e9eb03924abbcc49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67864 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ϣ%UY8`RWp~V-U_7 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 CLkb=M diff --git a/app/fonts/GeistVF.woff b/app/fonts/GeistVF.woff deleted file mode 100644 index 1b62daacff96dad6584e71cd962051b82957c313..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66268 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 >-Aun5A4Gp>4U0IQJ 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 G)cz05(PHWV diff --git a/app/globals.css b/app/globals.css deleted file mode 100644 index e3734be1..00000000 --- a/app/globals.css +++ /dev/null @@ -1,42 +0,0 @@ -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -a { - color: inherit; - text-decoration: none; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } -} diff --git a/app/layout.tsx b/app/layout.tsx index dca06aee..c8e99af1 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,33 +1,22 @@ -import type { Metadata } from "next"; -import localFont from "next/font/local"; -import "./globals.css"; +import type { Metadata } from 'next' -const geistSans = localFont({ - src: "./fonts/GeistVF.woff", - variable: "--font-geist-sans", - weight: "100 900", -}); -const geistMono = localFont({ - src: "./fonts/GeistMonoVF.woff", - variable: "--font-geist-mono", - weight: "100 900", -}); +import './globals.css' export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; + title: 'InvestMetic', + description: '', +} -export default function RootLayout({ +const RootLayout=({ children, }: Readonly<{ - children: React.ReactNode; -}>) { + children: React.ReactNode +}>) =>{ return ( - - - {children} - + + {children} - ); + ) } + +export default RootLayout diff --git a/app/page.module.css b/app/page.module.css index 8a460419..e69de29b 100644 --- a/app/page.module.css +++ b/app/page.module.css @@ -1,165 +0,0 @@ -.page { - --gray-rgb: 0, 0, 0; - --gray-alpha-200: rgba(var(--gray-rgb), 0.08); - --gray-alpha-100: rgba(var(--gray-rgb), 0.05); - - --button-primary-hover: #383838; - --button-secondary-hover: #f2f2f2; - - display: grid; - grid-template-rows: 20px 1fr 20px; - align-items: center; - justify-items: center; - min-height: 100svh; - padding: 80px; - gap: 64px; - font-family: var(--font-geist-sans); -} - -@media (prefers-color-scheme: dark) { - .page { - --gray-rgb: 255, 255, 255; - --gray-alpha-200: rgba(var(--gray-rgb), 0.145); - --gray-alpha-100: rgba(var(--gray-rgb), 0.06); - - --button-primary-hover: #ccc; - --button-secondary-hover: #1a1a1a; - } -} - -.main { - display: flex; - flex-direction: column; - gap: 32px; - grid-row-start: 2; -} - -.main ol { - font-family: var(--font-geist-mono); - padding-left: 0; - margin: 0; - font-size: 14px; - line-height: 24px; - letter-spacing: -0.01em; - list-style-position: inside; -} - -.main li:not(:last-of-type) { - margin-bottom: 8px; -} - -.main code { - font-family: inherit; - background: var(--gray-alpha-100); - padding: 2px 4px; - border-radius: 4px; - font-weight: 600; -} - -.ctas { - display: flex; - gap: 16px; -} - -.ctas a { - appearance: none; - border-radius: 128px; - height: 48px; - padding: 0 20px; - border: none; - border: 1px solid transparent; - transition: background 0.2s, color 0.2s, border-color 0.2s; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - line-height: 20px; - font-weight: 500; -} - -a.primary { - background: var(--foreground); - color: var(--background); - gap: 8px; -} - -a.secondary { - border-color: var(--gray-alpha-200); - min-width: 180px; -} - -.footer { - grid-row-start: 3; - display: flex; - gap: 24px; -} - -.footer a { - display: flex; - align-items: center; - gap: 8px; -} - -.footer img { - flex-shrink: 0; -} - -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - a.primary:hover { - background: var(--button-primary-hover); - border-color: transparent; - } - - a.secondary:hover { - background: var(--button-secondary-hover); - border-color: transparent; - } - - .footer a:hover { - text-decoration: underline; - text-underline-offset: 4px; - } -} - -@media (max-width: 600px) { - .page { - padding: 32px; - padding-bottom: 80px; - } - - .main { - align-items: center; - } - - .main ol { - text-align: center; - } - - .ctas { - flex-direction: column; - } - - .ctas a { - font-size: 14px; - height: 40px; - padding: 0 16px; - } - - a.secondary { - min-width: auto; - } - - .footer { - flex-wrap: wrap; - align-items: center; - justify-content: center; - } -} - -@media (prefers-color-scheme: dark) { - .logo { - filter: invert(); - } -} diff --git a/app/page.tsx b/app/page.tsx index df5d0424..83ca4582 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,95 +1,5 @@ -import Image from "next/image"; -import styles from "./page.module.css"; - -export default function Home() { - return ( - - ); +const Home = () => { + return
Home
} + +export default Home From 8bf0d01438cbcd054bdf965126e65ab25750579e Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 1 Nov 2024 18:12:55 +0900 Subject: [PATCH 002/648] =?UTF-8?q?settings:=20eslint=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 129 +++++++++++++++++++++++++++++ .eslintrc.json | 3 - package-lock.json | 203 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 22 +++-- 4 files changed, 341 insertions(+), 16 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..286ef67a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,129 @@ +module.exports = { + root: true, + env: { browser: true, es2021: true, node: true }, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.json', + tsconfigRootDir: __dirname, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:jsx-a11y/recommended', + 'plugin:prettier/recommended', + 'next/core-web-vitals', + 'next/typescript', + ], + plugins: ['@typescript-eslint', 'react', 'jsx-a11y', 'import', 'prettier', 'filenames'], + rules: { + 'prettier/prettier': 'error', + 'filenames/match-regex': ['error', '^[a-z-]+.[a-z]+$'], + 'react/jsx-pascal-case': 'error', + camelcase: ['error', { properties: 'never' }], + 'react/jsx-handler-names': [ + 'error', + { + eventHandlerPrefix: 'handle', + eventHandlerPropPrefix: 'on', + }, + ], + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'variable', + types: ['boolean'], + format: ['PascalCase'], + prefix: ['is', 'has', 'should'], + }, + { + selector: 'variable', + types: ['function'], + format: ['camelCase'], + suffix: ['Ref'], + filter: { + regex: 'Ref$', + match: true, + }, + }, + { + selector: 'interface', + format: ['PascalCase'], + custom: { + regex: '(Props|Model)$', + match: true, + }, + }, + { + selector: 'typeAlias', + format: ['PascalCase'], + suffix: ['Type'], + }, + ], + 'react/destructuring-assignment': ['error', 'always'], + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/consistent-type-definitions': ['error', 'interface'], + 'react/function-component-definition': [ + 2, + { + namedComponents: 'arrow-function', + unnamedComponents: 'arrow-function', + }, + ], + 'react/jsx-key': ['error', { checkFragmentShorthand: true }], + 'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }], + 'jsx-a11y/alt-text': [ + 'error', + { + elements: ['img', 'object', 'area', 'input[type="image"]'], + img: ['Image'], + object: [], + area: [], + 'input[type="image"]': [], + }, + ], + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'react/prop-types': 'off', + 'react/react-in-jsx-scope': 'off', + 'import/no-default-export': 'off', + 'import/prefer-default-export': 'off', + 'import/no-unresolved': 'off', + 'prefer-const': 'error', + 'no-var': 'error', + eqeqeq: 'error', + 'prefer-destructuring': [ + 'error', + { + array: false, + object: true, + }, + ], + 'no-useless-rename': 'error', + 'object-shorthand': 'error', + }, + settings: { + react: { + version: 'detect', + }, + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + paths: ['src', '.'], + }, + }, + }, + ignorePatterns: ['dist', '.eslintrc.cjs'], +} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 37224185..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["next/core-web-vitals", "next/typescript"] -} diff --git a/package-lock.json b/package-lock.json index 76fe5266..971dcb82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "final_team3_fe", + "name": "investmetic", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "final_team3_fe", + "name": "investmetic", "version": "0.1.0", "dependencies": { "next": "14.2.16", @@ -13,11 +13,21 @@ "react-dom": "^18" }, "devDependencies": { + "@next/eslint-plugin-next": "^15.0.2", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", - "eslint": "^8", + "@typescript-eslint/eslint-plugin": "^8.12.2", + "@typescript-eslint/parser": "^8.12.2", + "eslint": "^8.57.1", "eslint-config-next": "14.2.16", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-filenames": "^1.3.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react": "^7.37.2", + "prettier": "^3.3.3", "typescript": "^5" } }, @@ -165,12 +175,40 @@ "integrity": "sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==" }, "node_modules/@next/eslint-plugin-next": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.16.tgz", - "integrity": "sha512-noORwKUMkKc96MWjTOwrsUCjky0oFegHbeJ1yEnQBGbMHAaTEIgLZIIfsYF0x3a06PiS+2TXppfifR+O6VWslg==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.2.tgz", + "integrity": "sha512-R9Jc7T6Ge0txjmqpPwqD8vx6onQjynO9JT73ArCYiYPvSrwYXepH/UY/WdKDY8JPWJl72sAE4iGMHPeQ5xdEWg==", "dev": true, "dependencies": { - "glob": "10.3.10" + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "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.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@next/eslint-plugin-next/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/@next/swc-darwin-arm64": { @@ -362,6 +400,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1460,6 +1510,27 @@ } } }, + "node_modules/eslint-config-next/node_modules/@next/eslint-plugin-next": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.16.tgz", + "integrity": "sha512-noORwKUMkKc96MWjTOwrsUCjky0oFegHbeJ1yEnQBGbMHAaTEIgLZIIfsYF0x3a06PiS+2TXppfifR+O6VWslg==", + "dev": true, + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -1541,6 +1612,21 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-filenames": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz", + "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==", + "dev": true, + "dependencies": { + "lodash.camelcase": "4.3.0", + "lodash.kebabcase": "4.1.1", + "lodash.snakecase": "4.1.1", + "lodash.upperfirst": "4.3.1" + }, + "peerDependencies": { + "eslint": "*" + } + }, "node_modules/eslint-plugin-import": { "version": "2.31.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", @@ -1633,6 +1719,36 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.37.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", @@ -1808,6 +1924,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -2820,12 +2942,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, "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 }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3263,6 +3409,33 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3886,6 +4059,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", diff --git a/package.json b/package.json index 0b60c16f..c85f11cc 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "final_team3_fe", + "name": "investmetic", "version": "0.1.0", "private": true, "scripts": { @@ -9,16 +9,26 @@ "lint": "next lint" }, "dependencies": { + "next": "14.2.16", "react": "^18", - "react-dom": "^18", - "next": "14.2.16" + "react-dom": "^18" }, "devDependencies": { - "typescript": "^5", + "@next/eslint-plugin-next": "^15.0.2", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", - "eslint": "^8", - "eslint-config-next": "14.2.16" + "@typescript-eslint/eslint-plugin": "^8.12.2", + "@typescript-eslint/parser": "^8.12.2", + "eslint": "^8.57.1", + "eslint-config-next": "14.2.16", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-filenames": "^1.3.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react": "^7.37.2", + "prettier": "^3.3.3", + "typescript": "^5" } } From e0fcf65d7814040a8a01a67b69380579e4a61a39 Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 1 Nov 2024 18:13:09 +0900 Subject: [PATCH 003/648] =?UTF-8?q?settings:=20prettier=20=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierrc | 9 +++++++++ app/layout.tsx | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..0dc4fef2 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "singleQuote": true, + "bracketSpacing": true, + "semi": false, + "trailingComma": "es5", + "arrowParens": "always" +} diff --git a/app/layout.tsx b/app/layout.tsx index c8e99af1..07fbbfbb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -7,11 +7,11 @@ export const metadata: Metadata = { description: '', } -const RootLayout=({ +const RootLayout = ({ children, }: Readonly<{ children: React.ReactNode -}>) =>{ +}>) => { return ( {children} From cc0558eae5fb92ad042374daae3d8bf346d582c9 Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 1 Nov 2024 18:25:53 +0900 Subject: [PATCH 004/648] =?UTF-8?q?fix:=20layout=EC=97=90=EC=84=9C=20./glo?= =?UTF-8?q?bal.css=20import=20=EC=A0=9C=EA=B1=B0=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 07fbbfbb..81ef93c4 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,7 +1,5 @@ import type { Metadata } from 'next' -import './globals.css' - export const metadata: Metadata = { title: 'InvestMetic', description: '', From cf2ecf850b0dd4d7874b7f5799cf7c65022176ea Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 1 Nov 2024 18:32:28 +0900 Subject: [PATCH 005/648] =?UTF-8?q?settings:=20sass=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 369 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 + 2 files changed, 363 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 971dcb82..8ac66cec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "investmetic", "version": "0.1.0", "dependencies": { + "clsx": "^2.1.1", "next": "14.2.16", "react": "^18", "react-dom": "^18" @@ -28,6 +29,7 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", "prettier": "^3.3.3", + "sass": "^1.80.5", "typescript": "^5" } }, @@ -390,6 +392,279 @@ "node": ">=12.4.0" } }, + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "devOptional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -975,7 +1250,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "devOptional": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -1057,11 +1332,34 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "devOptional": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1220,6 +1518,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "devOptional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1995,7 +2305,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, + "devOptional": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2356,6 +2666,12 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "devOptional": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2541,7 +2857,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -2586,7 +2902,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2622,7 +2938,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12.0" } @@ -3002,7 +3318,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "devOptional": true, "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -3119,6 +3435,12 @@ } } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "devOptional": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3356,7 +3678,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8.6" }, @@ -3505,6 +3827,19 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "devOptional": true, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -3684,6 +4019,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sass": { + "version": "1.80.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", + "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", + "devOptional": true, + "dependencies": { + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -4094,7 +4447,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "devOptional": true, "dependencies": { "is-number": "^7.0.0" }, diff --git a/package.json b/package.json index c85f11cc..4cbda4ea 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "clsx": "^2.1.1", "next": "14.2.16", "react": "^18", "react-dom": "^18" @@ -29,6 +30,7 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", "prettier": "^3.3.3", + "sass": "^1.80.5", "typescript": "^5" } } From d44470a9e689c441fe82e8648107b0acb758f6fe Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 1 Nov 2024 18:55:31 +0900 Subject: [PATCH 006/648] =?UTF-8?q?setting:=20=ED=95=84=EC=9A=94=ED=95=9C?= =?UTF-8?q?=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=93=A4=20=EC=84=A4=EC=B9=98=20?= =?UTF-8?q?(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Zustand, tanstack query, highcharts, storybook --- .eslintrc.js | 1 + .gitignore | 2 + package-lock.json | 9418 ++++++++++++++++++++++++++++++++++++++++----- package.json | 19 +- 4 files changed, 8545 insertions(+), 895 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 286ef67a..a62490e4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,6 +19,7 @@ module.exports = { 'plugin:prettier/recommended', 'next/core-web-vitals', 'next/typescript', + 'plugin:storybook/recommended', ], plugins: ['@typescript-eslint', 'react', 'jsx-a11y', 'import', 'prettier', 'filenames'], rules: { diff --git a/.gitignore b/.gitignore index fd3dbb57..a5abcf01 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +*storybook.log diff --git a/package-lock.json b/package-lock.json index 8ac66cec..b2996375 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,24 @@ "name": "investmetic", "version": "0.1.0", "dependencies": { + "@tanstack/react-query": "^5.59.16", "clsx": "^2.1.1", + "highcharts": "^11.4.8", "next": "14.2.16", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "zustand": "^5.0.1" }, "devDependencies": { + "@chromatic-com/storybook": "^3.2.2", "@next/eslint-plugin-next": "^15.0.2", + "@storybook/addon-essentials": "^8.4.0", + "@storybook/addon-interactions": "^8.4.0", + "@storybook/addon-onboarding": "^8.4.0", + "@storybook/blocks": "^8.4.0", + "@storybook/nextjs": "^8.4.0", + "@storybook/react": "^8.4.0", + "@storybook/test": "^8.4.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -28,1235 +39,5645 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", + "eslint-plugin-storybook": "^0.10.1", "prettier": "^3.3.3", "sass": "^1.80.5", + "storybook": "^8.4.0", "typescript": "^5" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "engines": { + "node": ">=6.9.0" } }, - "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==", + "node_modules/@babel/compat-data": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.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" + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.9.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "bin": { + "json5": "lib/cli.js" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { - "node": ">=10.10.0" + "node": ">=6.9.0" } }, - "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==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, - "engines": { - "node": ">=12.22" + "dependencies": { + "@babel/types": "^7.25.9" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=12" + "node": ">=6.9.0" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" + }, "engines": { - "node": ">=12" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=12" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@next/env": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.16.tgz", - "integrity": "sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==" + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.2.tgz", - "integrity": "sha512-R9Jc7T6Ge0txjmqpPwqD8vx6onQjynO9JT73ArCYiYPvSrwYXepH/UY/WdKDY8JPWJl72sAE4iGMHPeQ5xdEWg==", + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, "dependencies": { - "fast-glob": "3.3.1" + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "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.4" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { - "node": ">=8.6.0" + "node": ">=6.9.0" } }, - "node_modules/@next/eslint-plugin-next/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==", + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { - "node": ">= 6" + "node": ">=6.9.0" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.16.tgz", - "integrity": "sha512-uFT34QojYkf0+nn6MEZ4gIWQ5aqGF11uIZ1HSxG+cSbj+Mg3+tYm8qXYd3dKN5jqKUm5rBVvf1PBRO/MeQ6rxw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.16.tgz", - "integrity": "sha512-mCecsFkYezem0QiZlg2bau3Xul77VxUD38b/auAjohMA22G9KTJneUYMv78vWoCCFkleFAhY1NIvbyjj1ncG9g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.16.tgz", - "integrity": "sha512-yhkNA36+ECTC91KSyZcgWgKrYIyDnXZj8PqtJ+c2pMvj45xf7y/HrgI17hLdrcYamLfVt7pBaJUMxADtPaczHA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.16.tgz", - "integrity": "sha512-X2YSyu5RMys8R2lA0yLMCOCtqFOoLxrq2YbazFvcPOE4i/isubYjkh+JCpRmqYfEuCVltvlo+oGfj/b5T2pKUA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.16.tgz", - "integrity": "sha512-9AGcX7VAkGbc5zTSa+bjQ757tkjr6C/pKS7OK8cX7QEiK6MHIIezBLcQ7gQqbDW2k5yaqba2aDtaBeyyZh1i6Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.16.tgz", - "integrity": "sha512-Klgeagrdun4WWDaOizdbtIIm8khUDQJ/5cRzdpXHfkbY91LxBXeejL4kbZBrpR/nmgRrQvmz4l3OtttNVkz2Sg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@babel/helper-simple-access": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.16.tgz", - "integrity": "sha512-PwW8A1UC1Y0xIm83G3yFGPiOBftJK4zukTmk7DI1CebyMOoaVpd8aSy7K6GhobzhkjYvqS/QmzcfsWG2Dwizdg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.16.tgz", - "integrity": "sha512-jhPl3nN0oKEshJBNDAo0etGMzv0j3q3VYorTSFqH1o3rwv1MQRdor27u1zhkgsHPNeY1jxcgyx1ZsCkDD1IHgg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.16.tgz", - "integrity": "sha512-OA7NtfxgirCjfqt+02BqxC3MIgM/JaGjw9tOe4fyZgPsqfseNiMPnCRP44Pfs+Gpo9zPN+SXaFsgP6vk8d571A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "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==", + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "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==", + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "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==", + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, "engines": { - "node": ">=12.4.0" + "node": ">=6.0.0" } }, - "node_modules/@parcel/watcher": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", - "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", - "devOptional": true, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.4.1", - "@parcel/watcher-darwin-arm64": "2.4.1", - "@parcel/watcher-darwin-x64": "2.4.1", - "@parcel/watcher-freebsd-x64": "2.4.1", - "@parcel/watcher-linux-arm-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-musl": "2.4.1", - "@parcel/watcher-linux-x64-glibc": "2.4.1", - "@parcel/watcher-linux-x64-musl": "2.4.1", - "@parcel/watcher-win32-arm64": "2.4.1", - "@parcel/watcher-win32-ia32": "2.4.1", - "@parcel/watcher-win32-x64": "2.4.1" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", - "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", - "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", - "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.13.0" } }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", - "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", - "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", - "cpu": [ - "arm" - ], + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", - "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", - "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", - "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", - "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", - "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", - "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", - "cpu": [ - "ia32" - ], + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", - "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", - "cpu": [ - "x64" - ], + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, - "optional": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">=14" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">=6.9.0" }, - "funding": { - "url": "https://opencollective.com/unts" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", - "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", - "dev": true - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" - }, - "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "dev": true, "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.17.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.4.tgz", - "integrity": "sha512-Fi1Bj8qTJr4f1FDdHFR7oMlOawEYSzkHNdBJK+aRjcDDNHwEV3jPPjuZP2Lh2QNgXeqzM8Y+U6b6urKAog2rZw==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dev": true, "dependencies": { - "undici-types": "~6.19.2" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true - }, - "node_modules/@types/react": { - "version": "18.3.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", - "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dev": true, "dependencies": { - "@types/react": "*" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", - "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/type-utils": "8.12.2", - "@typescript-eslint/utils": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.12.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", - "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", - "debug": "^4.3.4" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", - "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", - "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/utils": "8.12.2", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", - "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", - "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", - "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": "^1.3.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.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==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", - "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6.9.0" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", - "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.12.2", - "eslint-visitor-keys": "^3.4.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-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==", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@babel/core": "^7.0.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==", + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dev": true, "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" + "@babel/helper-plugin-utils": "^7.25.9" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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==", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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 - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dev": true, "dependencies": { - "possible-typed-array-names": "^1.0.0" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/axe-core": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", - "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", + "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", + "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", + "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-typescript": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@chromatic-com/storybook": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.2.tgz", + "integrity": "sha512-xmXt/GW0hAPbzNTrxYuVo43Adrtjue4DeVrsoIIEeJdGaPNNeNf+DHMlJKOBdlHmCnFUoe9R/0mLM9zUp5bKWw==", + "dev": true, + "dependencies": { + "chromatic": "^11.15.0", + "filesize": "^10.0.12", + "jsonfile": "^6.1.0", + "react-confetti": "^6.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16.0.0", + "yarn": ">=1.22.18" + }, + "peerDependencies": { + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" + } + }, + "node_modules/@chromatic-com/storybook/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@chromatic-com/storybook/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "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/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", + "dev": true, + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@next/env": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.16.tgz", + "integrity": "sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.2.tgz", + "integrity": "sha512-R9Jc7T6Ge0txjmqpPwqD8vx6onQjynO9JT73ArCYiYPvSrwYXepH/UY/WdKDY8JPWJl72sAE4iGMHPeQ5xdEWg==", + "dev": true, + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "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.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@next/eslint-plugin-next/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.16.tgz", + "integrity": "sha512-uFT34QojYkf0+nn6MEZ4gIWQ5aqGF11uIZ1HSxG+cSbj+Mg3+tYm8qXYd3dKN5jqKUm5rBVvf1PBRO/MeQ6rxw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.16.tgz", + "integrity": "sha512-mCecsFkYezem0QiZlg2bau3Xul77VxUD38b/auAjohMA22G9KTJneUYMv78vWoCCFkleFAhY1NIvbyjj1ncG9g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.16.tgz", + "integrity": "sha512-yhkNA36+ECTC91KSyZcgWgKrYIyDnXZj8PqtJ+c2pMvj45xf7y/HrgI17hLdrcYamLfVt7pBaJUMxADtPaczHA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.16.tgz", + "integrity": "sha512-X2YSyu5RMys8R2lA0yLMCOCtqFOoLxrq2YbazFvcPOE4i/isubYjkh+JCpRmqYfEuCVltvlo+oGfj/b5T2pKUA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.16.tgz", + "integrity": "sha512-9AGcX7VAkGbc5zTSa+bjQ757tkjr6C/pKS7OK8cX7QEiK6MHIIezBLcQ7gQqbDW2k5yaqba2aDtaBeyyZh1i6Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.16.tgz", + "integrity": "sha512-Klgeagrdun4WWDaOizdbtIIm8khUDQJ/5cRzdpXHfkbY91LxBXeejL4kbZBrpR/nmgRrQvmz4l3OtttNVkz2Sg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.16.tgz", + "integrity": "sha512-PwW8A1UC1Y0xIm83G3yFGPiOBftJK4zukTmk7DI1CebyMOoaVpd8aSy7K6GhobzhkjYvqS/QmzcfsWG2Dwizdg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.16.tgz", + "integrity": "sha512-jhPl3nN0oKEshJBNDAo0etGMzv0j3q3VYorTSFqH1o3rwv1MQRdor27u1zhkgsHPNeY1jxcgyx1ZsCkDD1IHgg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.16", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.16.tgz", + "integrity": "sha512-OA7NtfxgirCjfqt+02BqxC3MIgM/JaGjw9tOe4fyZgPsqfseNiMPnCRP44Pfs+Gpo9zPN+SXaFsgP6vk8d571A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "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, + "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, + "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, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "devOptional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", + "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", + "dev": true, + "dependencies": { + "ansi-html": "^0.0.9", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^4.2.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <5.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x || 5.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", + "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", + "dev": true + }, + "node_modules/@storybook/addon-actions": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.0.tgz", + "integrity": "sha512-xQ84mDIl+jyDpjt8SnCfhqVECQu7k1dLyhiAi983Tp5nyW8KRJa/tEATDLOCpz1eL9AMf2WjAypi+vIiNIul8w==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@types/uuid": "^9.0.1", + "dequal": "^2.0.2", + "polished": "^4.2.2", + "uuid": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-backgrounds": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.0.tgz", + "integrity": "sha512-2LpA7Ja7s76rFjSQHTPhbfmwsCmAuyU5k05CIbbUxM+iBVOaBXUYLaoi8dl448W/o/rmNHeW5YCtxzmMPlScrQ==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-controls": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.0.tgz", + "integrity": "sha512-KoqwWHi6cUv1WXcANH4l175kNkuFPVhexP/8F9tE9uhv2xHNx5cTefmB174dWpfOO2H3IdUk0RuMWjOZFpztqQ==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "dequal": "^2.0.2", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-docs": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.0.tgz", + "integrity": "sha512-n/tAu8xmfdxTkr7ooDM3h+QwDyP9eoKoKuaKXfiPPevrFk0FXRw5KzNhTHTlHniJ2LD+gyaomPGV6D2oBl1KIg==", + "dev": true, + "dependencies": { + "@mdx-js/react": "^3.0.0", + "@storybook/blocks": "8.4.0", + "@storybook/csf-plugin": "8.4.0", + "@storybook/react-dom-shim": "8.4.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-essentials": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.0.tgz", + "integrity": "sha512-45CI0LpNr8ASHEckxbW/osgnsFMWl847S9rALNQUAN3VaqlDQeF/VIDt1s9vtV9ZYNHASxPFmW4qjgylxv8HpQ==", + "dev": true, + "dependencies": { + "@storybook/addon-actions": "8.4.0", + "@storybook/addon-backgrounds": "8.4.0", + "@storybook/addon-controls": "8.4.0", + "@storybook/addon-docs": "8.4.0", + "@storybook/addon-highlight": "8.4.0", + "@storybook/addon-measure": "8.4.0", + "@storybook/addon-outline": "8.4.0", + "@storybook/addon-toolbars": "8.4.0", + "@storybook/addon-viewport": "8.4.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-highlight": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.0.tgz", + "integrity": "sha512-tshX/2HnPzGQ9Kza2DARNfirBRhE/Ts7bldbhMiJu20YhJD1jQzXSDEX1cCgHsDc8HKYOsV/Kuu5WDzp/1i97w==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-interactions": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.0.tgz", + "integrity": "sha512-yXPAyGRjElYZ0ObUo7Ipww4CwgScc2FXMxeQHKSZ+9wuDOU8uSaWpINB++8nS6yPZyhHeUqgzGCF/w3ZusNvzA==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/instrumenter": "8.4.0", + "@storybook/test": "8.4.0", + "polished": "^4.2.2", + "ts-dedent": "^2.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-measure": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.0.tgz", + "integrity": "sha512-Zews/03IL/UUJMaheduGxJKG1mEwfpGq7SP1RtK0kK3l/yh6kVcKG63RXw5zVEoDwG4wzuuH9vi06Mlzhu8/rA==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-onboarding": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.4.0.tgz", + "integrity": "sha512-q9nvMFxvjwDvkumIO0VEa2RhIfxwU8YiCCLVXrGw73XtWs2UViXBLaVl/W2USDGDq27T6VFTh6KzZVMikPJrUQ==", + "dev": true, + "dependencies": { + "react-confetti": "^6.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-outline": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.0.tgz", + "integrity": "sha512-qZdHaWq/DXoVycKzcynvVxg3MNzavsGCuq9HUl2X/oBKNii00NEZgYVLo4dQ8iDNlmykuJ9ReyXKBOKF7AU+9w==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-toolbars": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.0.tgz", + "integrity": "sha512-fXDeLsAweC1/roe5qNys+pBrjf1Mxof/7O/dZtQZJtcKox4WwzgirxexFFAZLfXOE9awm5svzo0YWYxWk+Lfwg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/addon-viewport": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.0.tgz", + "integrity": "sha512-hbHJzz7PcZ/bazUH3nAdG9yP3CUfF+wPdDwzcqSEVBRjdWSLZ4DHAtB0wajqhUoCsiRehg9avft1NokAc+KOgg==", + "dev": true, + "dependencies": { + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/blocks": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.0.tgz", + "integrity": "sha512-LeXsZLTNcmKtgt0ZRdgzBa2Z8A5CH3gGyjG7QT3M+3yH9fVAXB2XplKOIejDsvR9jSBww3mKXyabX12NVZKz0A==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.1.11", + "@storybook/icons": "^1.2.12", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.4.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/builder-webpack5": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.0.tgz", + "integrity": "sha512-NVPEB31x1LU73ghgPaynY603Pi0MKPlM/YovevlwZtTIU9st+DSEss1qSjC0As2Lq/bHZTJu+jhTCIB76MK7wQ==", + "dev": true, + "dependencies": { + "@storybook/core-webpack": "8.4.0", + "@types/node": "^22.0.0", + "@types/semver": "^7.3.4", + "browser-assert": "^1.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "cjs-module-lexer": "^1.2.3", + "constants-browserify": "^1.0.0", + "css-loader": "^6.7.1", + "es-module-lexer": "^1.5.0", + "fork-ts-checker-webpack-plugin": "^8.0.0", + "html-webpack-plugin": "^5.5.0", + "magic-string": "^0.30.5", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "semver": "^7.3.7", + "style-loader": "^3.3.1", + "terser-webpack-plugin": "^5.3.1", + "ts-dedent": "^2.0.0", + "url": "^0.11.0", + "util": "^0.12.4", + "util-deprecate": "^1.0.2", + "webpack": "5", + "webpack-dev-middleware": "^6.1.2", + "webpack-hot-middleware": "^2.25.1", + "webpack-virtual-modules": "^0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/builder-webpack5/node_modules/@types/node": { + "version": "22.8.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", + "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@storybook/components": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.0.tgz", + "integrity": "sha512-o2jPW05YN2rbSLNMzPV769c4zCy3Vn0DhJbIQZsxUmUXAMX/n1+V1jlV3kbY0kCjiI6i/PH7i6PJnxICdJ35mQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/core": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.0.tgz", + "integrity": "sha512-RlvkBNPPLbHtJQ5M3SKfLLtn5GssRBOLBbJLJf8HjraeDI+YRt+J9FVXqNa9aHhOGoxam+hFinmuy9gyMbPW1A==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.1.11", + "better-opn": "^3.0.2", + "browser-assert": "^1.2.1", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0", + "esbuild-register": "^3.5.0", + "jsdoc-type-pratt-parser": "^4.0.0", + "process": "^0.11.10", + "recast": "^0.23.5", + "semver": "^7.6.2", + "util": "^0.12.5", + "ws": "^8.2.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "prettier": "^2 || ^3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/@storybook/core-webpack": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.0.tgz", + "integrity": "sha512-14UnJ7zFSLEyaBvYe7+K1t/TWJc41KxstMHgVxHyE6TDy9MGi+GLfmq2xB5OIVE4nxtjSon3tIOf/hVBrtbt0A==", + "dev": true, + "dependencies": { + "@types/node": "^22.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/core-webpack/node_modules/@types/node": { + "version": "22.8.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", + "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@storybook/csf": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.11.tgz", + "integrity": "sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==", + "dev": true, + "dependencies": { + "type-fest": "^2.19.0" + } + }, + "node_modules/@storybook/csf-plugin": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.0.tgz", + "integrity": "sha512-l4vD1XboHh3nFOvcCIjoTED6bQZtRx+T/CUFfuZu3KEA7uJnXt/kUCXair9+Cgky9XvSEMvBPhoqa2dRx9ibBQ==", + "dev": true, + "dependencies": { + "unplugin": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/csf/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@storybook/global": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", + "dev": true + }, + "node_modules/@storybook/icons": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.12.tgz", + "integrity": "sha512-UxgyK5W3/UV4VrI3dl6ajGfHM4aOqMAkFLWe2KibeQudLf6NJpDrDMSHwZj+3iKC4jFU7dkKbbtH2h/al4sW3Q==", + "dev": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/instrumenter": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.0.tgz", + "integrity": "sha512-iqQdH2lhyRVcCBnVOmjn/r/pFwIJ5X1isUkvyavwPf0KOB2bz+QuXXkvKdzirwQFu9jSLOEdu0v3Fr+PHUbIfA==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@vitest/utils": "^2.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/manager-api": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.0.tgz", + "integrity": "sha512-duYoAtx3VkTHpoXd+NaMqBQNqIovmbTN7w/244O0LWyhF6AmQXnrY1Z72rjvvpxY6c1boRs6YdDLXPKxGVeRxw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/nextjs": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.4.0.tgz", + "integrity": "sha512-izOpE8m8TjPFhkGPB+4Lzt5gPzDxiu0v+sdnKmi41BvY4XGSXFqhBhLC5+M200VDqolJJzqZ9WCXp21pscyf+g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.1", + "@babel/plugin-transform-runtime": "^7.24.3", + "@babel/preset-env": "^7.24.4", + "@babel/preset-react": "^7.24.1", + "@babel/preset-typescript": "^7.24.1", + "@babel/runtime": "^7.24.4", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", + "@storybook/builder-webpack5": "8.4.0", + "@storybook/preset-react-webpack": "8.4.0", + "@storybook/react": "8.4.0", + "@storybook/test": "8.4.0", + "@types/node": "^22.0.0", + "@types/semver": "^7.3.4", + "babel-loader": "^9.1.3", + "css-loader": "^6.7.3", + "find-up": "^5.0.0", + "image-size": "^1.0.0", + "loader-utils": "^3.2.1", + "node-polyfill-webpack-plugin": "^2.0.1", + "pnp-webpack-plugin": "^1.7.0", + "postcss": "^8.4.38", + "postcss-loader": "^8.1.1", + "react-refresh": "^0.14.0", + "resolve-url-loader": "^5.0.0", + "sass-loader": "^13.2.0", + "semver": "^7.3.5", + "style-loader": "^3.3.1", + "styled-jsx": "^5.1.6", + "ts-dedent": "^2.0.0", + "tsconfig-paths": "^4.0.0", + "tsconfig-paths-webpack-plugin": "^4.0.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "optionalDependencies": { + "sharp": "^0.33.3" + }, + "peerDependencies": { + "next": "^13.5.0 || ^14.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.4.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@storybook/nextjs/node_modules/@types/node": { + "version": "22.8.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", + "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@storybook/nextjs/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@storybook/nextjs/node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@storybook/nextjs/node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "dev": true, + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/@storybook/nextjs/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@storybook/preset-react-webpack": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.0.tgz", + "integrity": "sha512-me5gqQqfU/jxQMJJljdID3lbKH2RGvdgxVwLhvrUSmEhimcuWXgJxvxE4hHGbUiYcwiM/xmQLrf286/B3agN7w==", + "dev": true, + "dependencies": { + "@storybook/core-webpack": "8.4.0", + "@storybook/react": "8.4.0", + "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", + "@types/node": "^22.0.0", + "@types/semver": "^7.3.4", + "find-up": "^5.0.0", + "magic-string": "^0.30.5", + "react-docgen": "^7.0.0", + "resolve": "^1.22.8", + "semver": "^7.3.7", + "tsconfig-paths": "^4.2.0", + "webpack": "5" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/preset-react-webpack/node_modules/@types/node": { + "version": "22.8.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", + "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@storybook/preset-react-webpack/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@storybook/preset-react-webpack/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@storybook/preview-api": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.0.tgz", + "integrity": "sha512-Z9yduQRqzqeV85GEFyaTKtRtg/QYCb89bKhi4xcxY9l7DMAr7/lqpUxqngW5ogiNslusQzct3zI7os6INBlMFg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/react": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.4.0.tgz", + "integrity": "sha512-jB7SNGdxFHFR9GgAPjrUUigE0pgOy3Bv3MaR9VdSGOZOnP+mjvZAO+ItPeKWHcQ7JnNujjtmMa2A80YcBfqBzQ==", + "dev": true, + "dependencies": { + "@storybook/components": "^8.4.0", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "^8.4.0", + "@storybook/preview-api": "^8.4.0", + "@storybook/react-dom-shim": "8.4.0", + "@storybook/theming": "^8.4.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@storybook/test": "8.4.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.4.0", + "typescript": ">= 4.2.x" + }, + "peerDependenciesMeta": { + "@storybook/test": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin": { + "version": "1.0.6--canary.9.0c3f3b7.0", + "resolved": "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz", + "integrity": "sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "endent": "^2.0.1", + "find-cache-dir": "^3.3.1", + "flat-cache": "^3.0.4", + "micromatch": "^4.0.2", + "react-docgen-typescript": "^2.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.x", + "webpack": ">= 4" + } + }, + "node_modules/@storybook/react-dom-shim": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.0.tgz", + "integrity": "sha512-PYYZVdQ6/ts6hBMAwMEu4hfbyHFPzUYmVsZNtF2egaVJQ44xM4i1Zt+RJuo2NOt5VyBCfXJOs+lSIdmSBY2arw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/test": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.0.tgz", + "integrity": "sha512-uHZ6+8RfEauwxi7Zy/LijfyIXrjCD7iTHmnTdT3BdP+2c/lDFAKXzHmbQJitefDFEgz1eHx/MArHZ8V3qu1ogg==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.1.11", + "@storybook/global": "^5.0.0", + "@storybook/instrumenter": "8.4.0", + "@testing-library/dom": "10.4.0", + "@testing-library/jest-dom": "6.5.0", + "@testing-library/user-event": "14.5.2", + "@vitest/expect": "2.0.5", + "@vitest/spy": "2.0.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@storybook/theming": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.0.tgz", + "integrity": "sha512-S7Iv5HMiYEJZlkQM0K9bxACLN7s8lCSG3M2CN6A82LSoXayFauuaPpn3LrNE2BvkTpdu17w19YiGbVYhPtRqsg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.4.0" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.4.tgz", + "integrity": "sha512-Fi1Bj8qTJr4f1FDdHFR7oMlOawEYSzkHNdBJK+aRjcDDNHwEV3jPPjuZP2Lh2QNgXeqzM8Y+U6b6urKAog2rZw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "devOptional": true + }, + "node_modules/@types/react": { + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "devOptional": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", + "integrity": "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", + "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/type-utils": "8.12.2", + "@typescript-eslint/utils": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "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" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", + "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", + "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" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", + "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2" + }, + "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.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", + "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/utils": "8.12.2", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", + "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", + "dev": true, + "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.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", + "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", + "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": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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, + "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, + "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.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", + "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2" + }, + "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" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", + "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.12.2", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitest/expect": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", + "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "dev": true, + "dependencies": { + "@vitest/spy": "2.0.5", + "@vitest/utils": "2.0.5", + "chai": "^5.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/pretty-format": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", + "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "dev": true, + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", + "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "2.0.5", + "estree-walker": "^3.0.3", + "loupe": "^3.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", + "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "dev": true, + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", + "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "dev": true, + "dependencies": { + "tinyspy": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", + "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "2.1.4", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.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, + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-html": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", + "integrity": "sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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 + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", "dev": true, "engines": { "node": ">=4" } }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-loader": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", + "dev": true, + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-loader/node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/babel-loader/node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.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 + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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" + } + ] + }, + "node_modules/better-opn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "dev": true, + "dependencies": { + "open": "^8.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "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, + "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==", + "devOptional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browser-assert": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz", + "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browserify-sign/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "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==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true }, - "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==", - "devOptional": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true }, "node_modules/busboy": { "version": "1.6.0", @@ -1297,6 +5718,16 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001676", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", @@ -1316,6 +5747,31 @@ } ] }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "dev": true, + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1332,6 +5788,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", @@ -1347,6 +5812,75 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/chromatic": { + "version": "11.16.3", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.16.3.tgz", + "integrity": "sha512-bckarRbZ3M1BvsmhLqEMschuQPk2FlSD9cvy8383JwoVvaIqLr0dv1tI/DPM4LMuXOjTjeBSZZINVH9r3RMiiA==", + "dev": true, + "bin": { + "chroma": "dist/bin.js", + "chromatic": "dist/bin.js", + "chromatic-cli": "dist/bin.js" + }, + "peerDependencies": { + "@chromatic-com/cypress": "^0.*.* || ^1.0.0", + "@chromatic-com/playwright": "^0.*.* || ^1.0.0" + }, + "peerDependenciesMeta": { + "@chromatic-com/cypress": { + "optional": true + }, + "@chromatic-com/playwright": { + "optional": true + } + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "dev": true + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -1360,6 +5894,20 @@ "node": ">=6" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1378,12 +5926,157 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "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 }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-js-compat": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", + "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "dev": true, + "dependencies": { + "browserslist": "^4.24.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.39.0.tgz", + "integrity": "sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1398,11 +6091,146 @@ "node": ">= 8" } }, + "node_modules/crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-loader/node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "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 + "devOptional": true }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -1478,12 +6306,36 @@ } } }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "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 }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -1501,6 +6353,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -1518,6 +6379,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -1530,6 +6410,23 @@ "node": ">=0.10" } }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1542,18 +6439,157 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domain-browser": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.23.0.tgz", + "integrity": "sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/electron-to-chromium": { + "version": "1.5.50", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", + "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/endent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/endent/-/endent-2.1.0.tgz", + "integrity": "sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==", + "dev": true, + "dependencies": { + "dedent": "^0.7.0", + "fast-json-parse": "^1.0.3", + "objectorarray": "^1.0.5" + } + }, "node_modules/enhanced-resolve": { "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", @@ -1567,6 +6603,42 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, "node_modules/es-abstract": { "version": "1.23.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", @@ -1673,6 +6745,12 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -1725,6 +6803,66 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2141,6 +7279,23 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-storybook": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.10.1.tgz", + "integrity": "sha512-YpxkdqyiKpMIrRquuvBaCinsqmZJ86JvXRX/gtRa4Qctpk0ipFt2cWqEjkB1HHWWG0DVRXlUBKHjRogC2Ig1fg==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.1.11", + "@typescript-eslint/utils": "^8.8.1", + "ts-dedent": "^2.2.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "eslint": ">=6" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -2186,6 +7341,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -2219,6 +7387,15 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2228,6 +7405,34 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2268,6 +7473,12 @@ "node": ">= 6" } }, + "node_modules/fast-json-parse": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", + "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", + "dev": true + }, "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", @@ -2280,6 +7491,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -2301,6 +7518,15 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filesize": { + "version": "10.1.6", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", + "integrity": "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==", + "dev": true, + "engines": { + "node": ">= 10.4.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2313,6 +7539,32 @@ "node": ">=8" } }, + "node_modules/filter-obj": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.2.tgz", + "integrity": "sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2374,12 +7626,140 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", + "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=12.13.0", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "dev": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "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", @@ -2416,6 +7796,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -2498,6 +7887,12 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -2645,6 +8040,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2666,6 +8084,21 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "dev": true, + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, "node_modules/immutable": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", @@ -2697,6 +8130,15 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2728,6 +8170,22 @@ "node": ">= 0.4" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -2744,6 +8202,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", @@ -2771,6 +8235,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -2853,6 +8329,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2922,6 +8413,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -3095,6 +8602,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -3141,6 +8660,44 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3158,12 +8715,39 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "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 }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3188,6 +8772,18 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -3243,6 +8839,30 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3258,12 +8878,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, "node_modules/lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", @@ -3299,12 +8931,113 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-or-similar": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", + "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoizerific": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", + "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", + "dev": true, + "dependencies": { + "map-or-similar": "^1.5.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3327,6 +9060,67 @@ "node": ">=8.6" } }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3386,6 +9180,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, "node_modules/next": { "version": "14.2.16", "resolved": "https://registry.npmjs.org/next/-/next-14.2.16.tgz", @@ -3435,11 +9235,105 @@ } } }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "devOptional": true + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "devOptional": true + }, + "node_modules/node-polyfill-webpack-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-2.0.1.tgz", + "integrity": "sha512-ZUMiCnZkP1LF0Th2caY6J/eKKoA0TefpoVa68m/LQU1I/mE8rGt4fNYGgNuCcK+aG8P8P43nbeJ2RqJMOL/Y1A==", + "dev": true, + "dependencies": { + "assert": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^6.0.3", + "console-browserify": "^1.2.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.12.0", + "domain-browser": "^4.22.0", + "events": "^3.3.0", + "filter-obj": "^2.0.2", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "punycode": "^2.1.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^4.0.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.3.0", + "timers-browserify": "^2.0.12", + "tty-browserify": "^0.0.1", + "type-fest": "^2.14.0", + "url": "^0.11.0", + "util": "^0.12.4", + "vm-browserify": "^1.1.2" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "webpack": ">=5" + } + }, + "node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } }, "node_modules/object-assign": { "version": "4.1.1", @@ -3462,6 +9356,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -3552,6 +9462,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/objectorarray": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.5.tgz", + "integrity": "sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==", + "dev": true + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3561,6 +9477,23 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3578,6 +9511,12 @@ "node": ">= 0.8.0" } }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3608,6 +9547,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3620,6 +9584,57 @@ "node": ">=6" } }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3669,6 +9684,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3686,42 +9735,265 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pnp-webpack-plugin": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.7.0.tgz", + "integrity": "sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==", + "dev": true, + "dependencies": { + "ts-pnp": "^1.1.6" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, "engines": { - "node": ">= 0.4" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "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" - } - ], + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=4" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3758,6 +10030,63 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3769,6 +10098,26 @@ "react-is": "^16.13.1" } }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3778,6 +10127,39 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3798,6 +10180,34 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -3809,6 +10219,51 @@ "node": ">=0.10.0" } }, + "node_modules/react-confetti": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.1.0.tgz", + "integrity": "sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==", + "dev": true, + "dependencies": { + "tween-functions": "^1.2.0" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.1 || ^18.0.0" + } + }, + "node_modules/react-docgen": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.0.tgz", + "integrity": "sha512-APPU8HB2uZnpl6Vt/+0AFoVYgSRtfiP6FLrZgPPTDmqSb2R4qZRbgd0A3VzIFxDt5e+Fozjx79WjLWnF69DK8g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.18.9", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "@types/babel__core": "^7.18.0", + "@types/babel__traverse": "^7.18.0", + "@types/doctrine": "^0.0.9", + "@types/resolve": "^1.20.2", + "doctrine": "^3.0.0", + "resolve": "^1.22.1", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=16.14.0" + } + }, + "node_modules/react-docgen-typescript": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", + "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", + "dev": true, + "peerDependencies": { + "typescript": ">= 4.3.x" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -3827,6 +10282,31 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/readdirp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", @@ -3840,6 +10320,56 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/recast": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.9.tgz", + "integrity": "sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==", + "dev": true, + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -3861,6 +10391,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "dev": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", @@ -3879,6 +10448,72 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexpu-core": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", + "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -3914,6 +10549,63 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/resolve-url-loader/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3961,6 +10653,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4002,6 +10704,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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" + } + ] + }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -4037,6 +10759,43 @@ "node": ">=14.0.0" } }, + "node_modules/sass-loader": { + "version": "13.3.3", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.3.tgz", + "integrity": "sha512-mt5YN2F1MOZr3d/wBRcZxeFgwgkH44wVc2zohO2YF6JiOMkiXe4BYRZpSu2sO1g71mo/j16txzUhsKZlqjVGzA==", + "dev": true, + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -4045,6 +10804,59 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -4057,6 +10869,15 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -4089,6 +10910,75 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/sharp/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==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4140,6 +11030,32 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true, + "optional": true + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "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", @@ -4148,6 +11064,107 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true + }, + "node_modules/storybook": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.0.tgz", + "integrity": "sha512-hLfXPtqfoQUMKVortxXdnQoUwDwtH85eSj9LbqGT/z1f/gLLYGNG3Mv3QbsRjHXhn+EfYffh7wuLpAn+Cicijw==", + "dev": true, + "dependencies": { + "@storybook/core": "8.4.0" + }, + "bin": { + "getstorybook": "bin/index.cjs", + "sb": "bin/index.cjs", + "storybook": "bin/index.cjs" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "prettier": "^2 || ^3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/stream-http/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -4156,6 +11173,15 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4354,6 +11380,21 @@ "node": ">=4" } }, + "node_modules/strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4366,6 +11407,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -4437,12 +11494,124 @@ "node": ">=6" } }, + "node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4452,19 +11621,42 @@ "is-number": "^7.0.0" }, "engines": { - "node": ">=8.0" + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "engines": { + "node": ">=6.10" } }, - "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "node_modules/ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", "dev": true, "engines": { - "node": ">=16" + "node": ">=6" }, - "peerDependencies": { - "typescript": ">=4.2.0" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/tsconfig-paths": { @@ -4479,11 +11671,63 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tslib": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "node_modules/tween-functions": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz", + "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4615,6 +11859,106 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unplugin": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.15.0.tgz", + "integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "webpack-sources": "^3" + }, + "peerDependenciesMeta": { + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4624,6 +11968,221 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "dev": true, + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.96.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.0.tgz", + "integrity": "sha512-gvn84AfQ4f6vUeNWmFuRp3vGERyxK4epADKTaAo60K0EQbY/YBNQbXH3Ji/ZRK5M25O/XneAOuChF4xQZjQ4xA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.3.tgz", + "integrity": "sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.12", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-hot-middleware": { + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz", + "integrity": "sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==", + "dev": true, + "dependencies": { + "ansi-html-community": "0.0.8", + "html-entities": "^2.1.0", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4827,6 +12386,51 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -4838,6 +12442,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz", + "integrity": "sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 4cbda4ea..219056b9 100644 --- a/package.json +++ b/package.json @@ -6,16 +6,29 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "dependencies": { + "@tanstack/react-query": "^5.59.16", "clsx": "^2.1.1", + "highcharts": "^11.4.8", "next": "14.2.16", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "zustand": "^5.0.1" }, "devDependencies": { + "@chromatic-com/storybook": "^3.2.2", "@next/eslint-plugin-next": "^15.0.2", + "@storybook/addon-essentials": "^8.4.0", + "@storybook/addon-interactions": "^8.4.0", + "@storybook/addon-onboarding": "^8.4.0", + "@storybook/blocks": "^8.4.0", + "@storybook/nextjs": "^8.4.0", + "@storybook/react": "^8.4.0", + "@storybook/test": "^8.4.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -29,8 +42,10 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", + "eslint-plugin-storybook": "^0.10.1", "prettier": "^3.3.3", "sass": "^1.80.5", + "storybook": "^8.4.0", "typescript": "^5" } } From d72e4a5c23b661ef6cdded5ef9a40d1374670628 Mon Sep 17 00:00:00 2001 From: deun Date: Sat, 2 Nov 2024 14:53:49 +0900 Subject: [PATCH 007/648] =?UTF-8?q?settings:=20.gitmessage=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 커밋 컨벤션 템플릿 및 템플릿 설정 방법 --- .github/.gitmessage.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/.gitmessage.txt diff --git a/.github/.gitmessage.txt b/.github/.gitmessage.txt new file mode 100644 index 00000000..936cd276 --- /dev/null +++ b/.github/.gitmessage.txt @@ -0,0 +1,27 @@ +# <타입>: <제목> (#이슈 번호) 의 형식으로 제목 작성 + +# 변경 사항이 "무엇"인지 명확히 작성 / 끝에 마침표(.) 금지 +# 예) feat: 마이페이지에 개인정보 수정 버튼 추가 (#1) + +# 본문은 아래에 작성 + +# 여러 줄의 메시지를 작성할 땐 "-"로 구분 +# 본문은 "어떻게" 보다 "무엇을", "왜"를 설명 + +# --- COMMIT END --- +# <타입> 리스트 +# feat : 기능 (새로운 기능) +# fix : 버그 수정 +# refactor : 리팩토링 +# design : CSS 등 사용자 UI 디자인 변경 +# docs : 문서 수정 (문서 추가, 수정, 삭제, README) +# test : 테스트 (테스트 코드 추가, 수정, 삭제: 비즈니스 로직에 변경 없음) +# settings : 프로젝트 세팅 관련 +# chore : 패키지 매니저 수정, 그 외 기타 수정 ex) .gitignore +# init : 초기 생성 +# rename : 파일 혹은 폴더명을 수정하거나 옮기는 작업만 한 경우 +# remove : 파일을 삭제하는 작업만 수행한 경우 +# ------------------ + +# 템플릿 설정 방법 +# git config commit.template .github/.gitmessage.txt \ No newline at end of file From 1d9f1729a8f762dda478afb6148c718d795144b3 Mon Sep 17 00:00:00 2001 From: deun Date: Sat, 2 Nov 2024 14:56:32 +0900 Subject: [PATCH 008/648] =?UTF-8?q?chore:=20package-lock.json=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EB=B3=80=EA=B2=BD=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 307 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) diff --git a/package-lock.json b/package-lock.json index b2996375..42e29596 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4227,6 +4227,181 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.16.tgz", + "integrity": "sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.16.tgz", + "integrity": "sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==", + "dependencies": { + "@tanstack/query-core": "5.59.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", + "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/doctrine": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", + "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "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 + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -8075,6 +8250,138 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highcharts": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-11.4.8.tgz", + "integrity": "sha512-5Tke9LuzZszC4osaFisxLIcw7xgNGz4Sy3Jc9pRMV+ydm6sYqsPYdU8ELOgpzGNrbrRNDRBtveoR5xS3SzneEA==" + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", + "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", From aaec1622f99bf4d28372184466c0d67371a09e1f Mon Sep 17 00:00:00 2001 From: deun Date: Sat, 2 Nov 2024 15:07:36 +0900 Subject: [PATCH 009/648] =?UTF-8?q?settings:=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF=20=EC=B6=94=EA=B0=80=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - feature, bug, docs, refactor 템플릿 추가 --- .github/ISSUE_TEMPLATE/bug.yml | 36 ++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/documentation.yml | 21 ++++++++++++++ .github/ISSUE_TEMPLATE/feature.yml | 21 ++++++++++++++ .github/ISSUE_TEMPLATE/refactor.yml | 21 ++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation.yml create mode 100644 .github/ISSUE_TEMPLATE/feature.yml create mode 100644 .github/ISSUE_TEMPLATE/refactor.yml diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 00000000..9233ccf4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,36 @@ +name: Bug +description: 버그가 발생했나요? +title: '[Bug] ' +labels: ['🐛 Bug'] +projects: ['FC-DEV-FinalProject/6'] +body: + - type: textarea + id: bug-description + attributes: + label: 🐞 설명 + description: 버그에 대한 설명을 작성해 주세요. + validations: + required: true + - type: textarea + id: bug-solution + attributes: + label: 💡 해결 방법 + description: 해결 방법을 알고 있다면 작성해 주세요. + validations: + required: false + - type: textarea + id: bug-os + attributes: + label: 🌏 환경 + description: 버그가 발생한 환경에 대해 작성해 주세요. + placeholder: | + OS: macOS 14.5 + validations: + required: false + - type: textarea + id: bug-more + attributes: + label: 📝 메모 + description: 더 하고 싶은 말이 있다면 작성해 주세요. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 00000000..4f3c0f4b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,21 @@ +name: Documentation +description: 문서 추가/수정/삭제가 필요한가요? +title: '[Docs] ' +labels: ['📃 Docs'] +projects: ['FC-DEV-FinalProject/6'] +body: + - type: textarea + id: docs-description + attributes: + label: 📄 설명 + description: 추가/수정/삭제할 내용을 작성해 주세요. + placeholder: ex) README.md에 팀원 소개 추가 + validations: + required: true + - type: textarea + id: docs-memo + attributes: + label: 📝 메모 + description: 더 하고 싶은 말이 있다면 작성해 주세요. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 00000000..1cb847af --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,21 @@ +name: Feature +description: 새로운 기능이나 명세가 있나요? +title: '[Feat] ' +labels: ['✨ Feature'] +projects: ['FC-DEV-FinalProject/6'] +body: + - type: textarea + id: feature-todo + attributes: + label: ✅ 해야 할 일 + description: 해야 할 일에 대한 Tasks를 작성해 주세요. + placeholder: 최대한 세분화해서 작성! (체크박스 활용하기) + validations: + required: true + - type: textarea + id: feature-memo + attributes: + label: 📝 메모 + description: 더 하고 싶은 말이 있다면 작성해 주세요. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/refactor.yml b/.github/ISSUE_TEMPLATE/refactor.yml new file mode 100644 index 00000000..f2a6afcc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/refactor.yml @@ -0,0 +1,21 @@ +name: Refactor +description: 리팩토링이 필요한가요? +title: '[Refactor] ' +labels: ['🔨 Refactor'] +projects: ['FC-DEV-FinalProject/6'] +body: + - type: textarea + id: refactor-todo + attributes: + label: ✅ 해야 할 일 + description: 해야 할 일에 대한 Tasks를 작성해 주세요. + placeholder: 최대한 세분화해서 작성! (체크박스 활용하기) + validations: + required: true + - type: textarea + id: refactor-memo + attributes: + label: 📝 메모 + description: 더 하고 싶은 말이 있다면 작성해 주세요. + validations: + required: false From b6f6463416472d04fc5c4860055ec4ea2858bf8c Mon Sep 17 00:00:00 2001 From: deun Date: Sat, 2 Nov 2024 15:31:03 +0900 Subject: [PATCH 010/648] =?UTF-8?q?settings:=20PR=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=B6=94=EA=B0=80=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..e34b9491 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +# 🚀 풀 리퀘스트 제안 + +## 🔍 작업 내용 + +> 작업한 내용에 대해 자세히 설명해 주세요. + +## 🔧 변경 사항 + +> 주요 변경 사항을 요약해 주세요. ex) validate 로직 수정, package.json 수정, 파일 수정/삭제 등 + +## 📸 스크린샷 (권장) + +> 수정된 화면 또는 기능을 시연할 수 있는 스크린샷을 첨부해 주세요. + +## 🙏 리뷰 참고 (선택 사항) + +> 개발 과정에서 다른 분들의 의견이 궁금했거나 크로스 체크가 필요하다고 느껴진 코드가 있다면 남겨주세요. + +## 📄 기타 (선택 사항) + +> 그 외 전달하고 싶은 내용이나 특별한 요구 사항이 있으면 작성해 주세요. From 8bd18d4b5e509966100e20b3befc6ad123c2cb79 Mon Sep 17 00:00:00 2001 From: deun Date: Sat, 2 Nov 2024 16:01:25 +0900 Subject: [PATCH 011/648] =?UTF-8?q?settings:=20lefthook=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - commit 전에 eslint, prettier, type 검사 - push 전에 audit 검사 --- lefthook.yml | 16 +++++ package-lock.json | 172 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 3 files changed, 191 insertions(+) create mode 100644 lefthook.yml diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 00000000..fdd2e442 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,16 @@ +pre-commit: + parallel: true + commands: + eslint: + glob: '*.{js,ts,jsx,tsx}' + run: npm run lint {staged_files} --fix + prettier: + glob: '*.{js,ts,jsx,tsx,json,yaml,md}' + run: npm run prettier {staged_files} + type-check: + run: npm run type-check + +pre-push: + commands: + audit-check: + run: npm audit diff --git a/package-lock.json b/package-lock.json index 42e29596..55f7c91a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", "eslint-plugin-storybook": "^0.10.1", + "lefthook": "^1.8.2", "prettier": "^3.3.3", "sass": "^1.80.5", "storybook": "^8.4.0", @@ -8344,6 +8345,25 @@ } } }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -9133,6 +9153,158 @@ "node": ">=0.10" } }, + "node_modules/lefthook": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.8.2.tgz", + "integrity": "sha512-lMXbcFHNDr+gzy/7ghuJDVB/Yyycj+ZL/7pN3Gm/s5Xqrc9+5sj3IrDAPylcEJ1cKCbUnXbwESrhhqpcYv4d4g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "lefthook": "bin/index.js" + }, + "optionalDependencies": { + "lefthook-darwin-arm64": "1.8.2", + "lefthook-darwin-x64": "1.8.2", + "lefthook-freebsd-arm64": "1.8.2", + "lefthook-freebsd-x64": "1.8.2", + "lefthook-linux-arm64": "1.8.2", + "lefthook-linux-x64": "1.8.2", + "lefthook-openbsd-arm64": "1.8.2", + "lefthook-openbsd-x64": "1.8.2", + "lefthook-windows-arm64": "1.8.2", + "lefthook-windows-x64": "1.8.2" + } + }, + "node_modules/lefthook-darwin-arm64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.8.2.tgz", + "integrity": "sha512-g41SoFUv8SzHpG1NOPkHUEPhC1tJM5FF3Vo+HESmLmL9cDfd7JncHFPy59rVnC9Q8nOS0rvoik5HTq+3/wcfww==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/lefthook-darwin-x64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.8.2.tgz", + "integrity": "sha512-IlCm4PrA/aAZ1MChiExYTbladC87GaxmYHOMHCeChLecqn+lypAuiYLgf7w5r2s3MjH5rbXImfU925NRKi6RXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/lefthook-freebsd-arm64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.8.2.tgz", + "integrity": "sha512-f7AIvuIEXUUR1ZutIFxjYKFDAVUBrdsLm+cbwOCjdfpJh7j2Fjg6nKXbDcglPXlX9Ix+nw9pHbJE2DAgzkI1Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/lefthook-freebsd-x64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.8.2.tgz", + "integrity": "sha512-DSDL64fRLSNSWOa1y2bGXwXPiwU1fbAXpj63j6jeQ0jkgu6k+3XL/PBXKh80cI6MvCKz/KQKCtIencXZZ2Ua4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/lefthook-linux-arm64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.8.2.tgz", + "integrity": "sha512-sJ95X+ZH8ayIE7ApiGEq5ZF9KGA+eKiocJU+536bLbAIHw5WjGmv2x3llFqUxH/zAmLe3k542oZ4d84wEO0EGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/lefthook-linux-x64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.8.2.tgz", + "integrity": "sha512-2eirc61M0WjlbSHamAgGf9iWsQTYz4IT6PAPm66vUaeG34+5D66xFicIV6pK2niRGUOPtNs8Kt4lboKtW+ba5g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/lefthook-openbsd-arm64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.8.2.tgz", + "integrity": "sha512-ZMop7htaSwP3MiL06WHUV36EX05N33o0WzNzC8NO5KEubn8Z74vbXcaq6qYezmgi+erkG6dtTnlbcZy5PFvFIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/lefthook-openbsd-x64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.8.2.tgz", + "integrity": "sha512-jXFoxmpYXO6ZafgQJVvk3MYlRgOBJD3n7H8A1Bj1E2yrLzOhKevUKlTNwZTxQdxlnvoo33yD6SjVSujZavEGpw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/lefthook-windows-arm64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.8.2.tgz", + "integrity": "sha512-hsQUSk6kmB8E0UMD3Mk6ROoa7qv6XmigfPsn9tFjmbZ2aO+kpBfWitZ5v+gcjNp44yaECs3YTMIfv3QFwXlRCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/lefthook-windows-x64": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.8.2.tgz", + "integrity": "sha512-YypbMhvgAtkL7y+O3OlF81vwua7X4jloBz5hO3fILAuzaGAiPE1VbeuqRweV8VuKK4L/ZVVKqmpesygBUNDp9w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", diff --git a/package.json b/package.json index 219056b9..efa315b1 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "build": "next build", "start": "next start", "lint": "next lint", + "prettier": "prettier --write .", "storybook": "storybook dev -p 6006", + "type-check": "tsc --noEmit -p ./tsconfig.json", "build-storybook": "storybook build" }, "dependencies": { @@ -43,6 +45,7 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", "eslint-plugin-storybook": "^0.10.1", + "lefthook": "^1.8.2", "prettier": "^3.3.3", "sass": "^1.80.5", "storybook": "^8.4.0", From f08b8643bb9cfca0b5653a431b9fa481c998e91c Mon Sep 17 00:00:00 2001 From: deun Date: Sat, 2 Nov 2024 16:02:27 +0900 Subject: [PATCH 012/648] =?UTF-8?q?chore:=20prettier=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=9E=90=EB=8F=99=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- next.config.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2da838b..b798611d 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# InvestMetic \ No newline at end of file +# InvestMetic diff --git a/next.config.mjs b/next.config.mjs index 4678774e..1d614782 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,4 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = {} -export default nextConfig; +export default nextConfig From 8bd5e57ab76287ec644b9a2ad92735262a649f0b Mon Sep 17 00:00:00 2001 From: deun Date: Sat, 2 Nov 2024 16:06:31 +0900 Subject: [PATCH 013/648] =?UTF-8?q?fix:=20ESLint=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20tsconfig.json=20=EC=88=98=EC=A0=95=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index e7ff90fd..e3da2f9a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,6 @@ "@/*": ["./*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "next.config.mjs"], "exclude": ["node_modules"] } From 19ca5c725aaa134196b288d5370e467f6bbdbca9 Mon Sep 17 00:00:00 2001 From: kimpra Date: Sat, 2 Nov 2024 16:33:42 +0900 Subject: [PATCH 014/648] =?UTF-8?q?fix:=20clsx=EB=A5=BC=20classnames?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 15 ++++++--------- package.json | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 55f7c91a..f894aa2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@tanstack/react-query": "^5.59.16", - "clsx": "^2.1.1", + "classnames": "^2.5.1", "highcharts": "^11.4.8", "next": "14.2.16", "react": "^18", @@ -6036,6 +6036,11 @@ "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", "dev": true }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -6062,14 +6067,6 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "engines": { - "node": ">=6" - } - }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", diff --git a/package.json b/package.json index efa315b1..9274b29a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@tanstack/react-query": "^5.59.16", - "clsx": "^2.1.1", + "classnames": "^2.5.1", "highcharts": "^11.4.8", "next": "14.2.16", "react": "^18", From 1bcf897005098c0b1f981af9eee343c511350b2a Mon Sep 17 00:00:00 2001 From: deun Date: Mon, 4 Nov 2024 12:43:42 +0900 Subject: [PATCH 015/648] =?UTF-8?q?chore:=20prettier=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=97=90=20endOfLine=20auto=20=EC=B6=94=EA=B0=80=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OS 간 줄바꿈 문자 호환을 위해 endOfLine 설정 추가 --- .prettierrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.prettierrc b/.prettierrc index 0dc4fef2..b74675d4 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,5 +5,6 @@ "bracketSpacing": true, "semi": false, "trailingComma": "es5", - "arrowParens": "always" + "arrowParens": "always", + "endOfLine": "auto" } From 024728319e32410ad13a11a61b0ca1259343a4b8 Mon Sep 17 00:00:00 2001 From: deun Date: Mon, 4 Nov 2024 16:27:04 +0900 Subject: [PATCH 016/648] =?UTF-8?q?settings:=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/{ => (home)}/layout.tsx | 0 app/{ => (home)}/page.module.css | 0 app/{ => (home)}/page.tsx | 0 app/(home)/ui/.gitkeep | 0 lefthook.yml | 2 +- shared/api/.gitkeep | 0 shared/hooks/custom/.gitkeep | 0 shared/hooks/query/.gitkeep | 0 shared/stores/.gitkeep | 0 shared/styles/reset.scss | 0 shared/ui/button/index.tsx | 0 shared/ui/button/styles.module.scss | 0 tsconfig.json | 1 + 13 files changed, 2 insertions(+), 1 deletion(-) rename app/{ => (home)}/layout.tsx (100%) rename app/{ => (home)}/page.module.css (100%) rename app/{ => (home)}/page.tsx (100%) create mode 100644 app/(home)/ui/.gitkeep create mode 100644 shared/api/.gitkeep create mode 100644 shared/hooks/custom/.gitkeep create mode 100644 shared/hooks/query/.gitkeep create mode 100644 shared/stores/.gitkeep create mode 100644 shared/styles/reset.scss create mode 100644 shared/ui/button/index.tsx create mode 100644 shared/ui/button/styles.module.scss diff --git a/app/layout.tsx b/app/(home)/layout.tsx similarity index 100% rename from app/layout.tsx rename to app/(home)/layout.tsx diff --git a/app/page.module.css b/app/(home)/page.module.css similarity index 100% rename from app/page.module.css rename to app/(home)/page.module.css diff --git a/app/page.tsx b/app/(home)/page.tsx similarity index 100% rename from app/page.tsx rename to app/(home)/page.tsx diff --git a/app/(home)/ui/.gitkeep b/app/(home)/ui/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/lefthook.yml b/lefthook.yml index fdd2e442..8bfe2085 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -3,7 +3,7 @@ pre-commit: commands: eslint: glob: '*.{js,ts,jsx,tsx}' - run: npm run lint {staged_files} --fix + run: npx eslint {staged_files} --fix prettier: glob: '*.{js,ts,jsx,tsx,json,yaml,md}' run: npm run prettier {staged_files} diff --git a/shared/api/.gitkeep b/shared/api/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/hooks/custom/.gitkeep b/shared/hooks/custom/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/hooks/query/.gitkeep b/shared/hooks/query/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/stores/.gitkeep b/shared/stores/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/styles/reset.scss b/shared/styles/reset.scss new file mode 100644 index 00000000..e69de29b diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx new file mode 100644 index 00000000..e69de29b diff --git a/shared/ui/button/styles.module.scss b/shared/ui/button/styles.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/tsconfig.json b/tsconfig.json index e3da2f9a..6430397f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "name": "next" } ], + "baseUrl": ".", "paths": { "@/*": ["./*"] } From 8fdfd1f3ed17860bc68ebea4248ad77bd3856a2e Mon Sep 17 00:00:00 2001 From: James Date: Mon, 4 Nov 2024 18:27:08 +0900 Subject: [PATCH 017/648] =?UTF-8?q?feat:=20TanStack=20Query=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20(#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/get-query-client.ts | 26 ++++++++++++++++++++++++++ app/layout.tsx | 13 +++++++++++++ app/providers.tsx | 11 +++++++++++ 3 files changed, 50 insertions(+) create mode 100644 app/get-query-client.ts create mode 100644 app/layout.tsx create mode 100644 app/providers.tsx diff --git a/app/get-query-client.ts b/app/get-query-client.ts new file mode 100644 index 00000000..38f081b4 --- /dev/null +++ b/app/get-query-client.ts @@ -0,0 +1,26 @@ +import { isServer, QueryClient, defaultShouldDehydrateQuery } from '@tanstack/react-query' + +const makeQueryClient = () => { + return new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, + }, + dehydrate: { + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || query.state.status === 'pending', + }, + }, + }) +} + +let browserQueryClient: QueryClient | undefined = undefined + +export const getQueryClient = () => { + if (isServer) { + return makeQueryClient() + } else { + if (!browserQueryClient) browserQueryClient = makeQueryClient() + return browserQueryClient + } +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 00000000..f53605a5 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,13 @@ +import { Providers } from './providers' + +const RootLayout = ({ children }: { children: React.ReactNode }) => { + return ( + + + {children} + + + ) +} + +export default RootLayout diff --git a/app/providers.tsx b/app/providers.tsx new file mode 100644 index 00000000..02a6d086 --- /dev/null +++ b/app/providers.tsx @@ -0,0 +1,11 @@ +'use client' + +import { QueryClientProvider } from '@tanstack/react-query' +import { ReactNode } from 'react' +import { getQueryClient } from './get-query-client' + +export const Providers = ({ children }: { children: ReactNode }) => { + const queryClient = getQueryClient() + + return {children} +} From 26edef47016da15564451fdad1b2cc4a04e1eb5f Mon Sep 17 00:00:00 2001 From: James Date: Mon, 4 Nov 2024 18:28:16 +0900 Subject: [PATCH 018/648] =?UTF-8?q?chore:=20TanStack=20Query=20=EC=B5=9C?= =?UTF-8?q?=EC=8B=A0=20=EB=B2=84=EC=A0=84=20=EC=82=AC=EC=9A=A9=20(#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 18 ++++++++++-------- package.json | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index f894aa2e..fccf76f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "investmetic", "version": "0.1.0", "dependencies": { - "@tanstack/react-query": "^5.59.16", + "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", "next": "14.2.16", @@ -4229,20 +4229,22 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.59.16", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.16.tgz", - "integrity": "sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==", + "version": "5.59.17", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.17.tgz", + "integrity": "sha512-jWdDiif8kaqnRGHNXAa9CnudtxY5v9DUxXhodgqX2Rwzj+1UwStDHEbBd9IA5C7VYAaJ2s+BxFR6PUBs8ERorA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "5.59.16", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.16.tgz", - "integrity": "sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==", + "version": "5.59.19", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.19.tgz", + "integrity": "sha512-xLRfyFyQOFcLltKCds0LijfC6/HQJrrTTnZB8ciyn74LIkVAm++vZJ6eUVG20RmJtdP8REdy7vSOYW4M3//XLA==", + "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.59.16" + "@tanstack/query-core": "5.59.17" }, "funding": { "type": "github", diff --git a/package.json b/package.json index 9274b29a..eea4a78f 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build-storybook": "storybook build" }, "dependencies": { - "@tanstack/react-query": "^5.59.16", + "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", "next": "14.2.16", From b33ad136b9553a736ada6e3925aae06a3b3c971d Mon Sep 17 00:00:00 2001 From: deun Date: Mon, 4 Nov 2024 18:31:08 +0900 Subject: [PATCH 019/648] =?UTF-8?q?settings:=20import=20=EC=A0=95=EB=A0=AC?= =?UTF-8?q?=20=EC=88=9C=EC=84=9C=20=EA=B7=9C=EC=B9=99=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - React, Next.js, 외부 라이브러리, shared, 상대 경로 순으로 정렬되도록 구성 --- .prettierrc | 6 +- package-lock.json | 207 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 213 insertions(+), 1 deletion(-) diff --git a/.prettierrc b/.prettierrc index b74675d4..f8c96102 100644 --- a/.prettierrc +++ b/.prettierrc @@ -6,5 +6,9 @@ "semi": false, "trailingComma": "es5", "arrowParens": "always", - "endOfLine": "auto" + "endOfLine": "auto", + "plugins": ["@trivago/prettier-plugin-sort-imports"], + "importOrder": ["^react$", "^next(/.*)?$", "", "^@/shared/(.*)$", "^[./]"], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true } diff --git a/package-lock.json b/package-lock.json index f894aa2e..0f8e701f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@storybook/nextjs": "^8.4.0", "@storybook/react": "^8.4.0", "@storybook/test": "^8.4.0", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -287,6 +288,43 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", @@ -411,6 +449,18 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", @@ -4332,6 +4382,148 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@trivago/prettier-plugin-sort-imports": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", + "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", + "dev": true, + "dependencies": { + "@babel/generator": "7.17.7", + "@babel/parser": "^7.20.5", + "@babel/traverse": "7.23.2", + "@babel/types": "7.17.0", + "javascript-natural-sort": "0.7.1", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@vue/compiler-sfc": "3.x", + "prettier": "2.x - 3.x" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + } + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse/node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/traverse/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -8984,6 +9176,12 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "dev": true + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -12088,6 +12286,15 @@ "node": ">=14.0.0" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 9274b29a..9054323e 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@storybook/nextjs": "^8.4.0", "@storybook/react": "^8.4.0", "@storybook/test": "^8.4.0", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", From 6af3a7b34f97cf1a9ff93d0dc21c2102ba7d6a5a Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 5 Nov 2024 10:15:12 +0900 Subject: [PATCH 020/648] =?UTF-8?q?design:=20=5Freset.scss=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Partial 파일임을 나타내기 위해 reset.scss에서 _reset.scss로 이름 변경 --- shared/styles/base/_reset.scss | 82 ++++++++++++++++++++++++++++++++++ shared/styles/reset.scss | 0 2 files changed, 82 insertions(+) create mode 100644 shared/styles/base/_reset.scss delete mode 100644 shared/styles/reset.scss diff --git a/shared/styles/base/_reset.scss b/shared/styles/base/_reset.scss new file mode 100644 index 00000000..58df2786 --- /dev/null +++ b/shared/styles/base/_reset.scss @@ -0,0 +1,82 @@ +*, +*::before, +*::after { + padding: 0; + margin: 0; + border: 0; + font-size: inherit; + vertical-align: baseline; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + -webkit-text-size-adjust: none; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} + +ol, +ul { + list-style: none; +} + +button { + outline: 0; + border: 0; + font: inherit; + cursor: pointer; +} + +input, +textarea { + border: none; + outline: none; + background: none; + font: inherit; + + &:focus { + outline: none; + } +} + +textarea { + resize: none; +} + +a { + text-decoration: none; + color: inherit; + + &:link, + &:visited, + &:hover, + &:active { + text-decoration: none; + color: inherit; + } +} + +img { + max-width: 100%; + height: auto; + vertical-align: top; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/shared/styles/reset.scss b/shared/styles/reset.scss deleted file mode 100644 index e69de29b..00000000 From 9d46dbeb79c79610aa7a9beb734de8b2083f7165 Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 5 Nov 2024 10:16:07 +0900 Subject: [PATCH 021/648] =?UTF-8?q?design:=20css=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 색상, 폰트 사이즈, 반응형 중단점, z-index 변수 추가 --- shared/styles/base/_variables.scss | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 shared/styles/base/_variables.scss diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss new file mode 100644 index 00000000..8ed366a2 --- /dev/null +++ b/shared/styles/base/_variables.scss @@ -0,0 +1,57 @@ +/* Colors */ +// Grayscale +$color-black: #0a0a0a; +$color-gray-800: #1d1d1d; +$color-gray-700: #363636; +$color-gray-600: #4d4d4d; +$color-gray-500: #797979; +$color-gray-400: #a7a7a7; +$color-gray-300: #cccccc; +$color-gray-200: #ececec; +$color-gray-100: #fafafa; +$color-white: #ffffff; + +// Primary +$color-indigo-800: #0f21a9; +$color-indigo-700: #1228ce; +$color-indigo-600: #1e37eb; +$color-indigo-500: #4533eb; +$color-indigo-400: #5668f0; +$color-indigo-300: #7c8af4; +$color-indigo-200: #a1abf7; +$color-indigo-100: #d9ddfc; + +// Secondary +$color-salmon-800: #fb2a23; +$color-salmon-700: #fb514b; +$color-salmon-600: #fc6e69; +$color-salmon-500: #fd8b87; +$color-salmon-400: #fd9e9b; +$color-salmon-300: #fdb2af; +$color-salmon-200: #fed8d7; +$color-salmon-100: #ffeceb; + +/* Typography */ +$text-h1: 54px; +$text-h2: 36px; +$text-h3: 30px; +$text-h4: 24px; +$text-b1: 20px; +$text-b2: 16px; +$text-b3: 14px; +$text-c1: 12px; +$text-c2: 10px; + +/* Breakpoints */ +$breakpoint-sm: 576px; +$breakpoint-md: 768px; +$breakpoint-lg: 992px; +$breakpoint-xl: 1200px; + +/* Z-index */ +$z-index: ( + modal: 1000, + header: 100, + base: 1, + hidden: -1, +); From a28825456aaa8176abe665a9c587c2b32a77c914 Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 5 Nov 2024 10:18:41 +0900 Subject: [PATCH 022/648] =?UTF-8?q?design:=20=EB=AF=B8=EB=94=94=EC=96=B4?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20mixins=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 재사용 가능한 반응형 미디어쿼리 mixin 추가 - mobile, tablet-sm, tablet-md, desktop, desktop-xl 중단점 설정 --- shared/styles/base/_mixins.scss | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 shared/styles/base/_mixins.scss diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss new file mode 100644 index 00000000..9cca6daf --- /dev/null +++ b/shared/styles/base/_mixins.scss @@ -0,0 +1,29 @@ +@mixin mobile { + @media (max-width: #{$breakpoint-sm - 1}) { + @content; + } +} + +@mixin tablet-sm { + @media (min-width: #{$breakpoint-sm}) and (max-width: #{$breakpoint-md - 1}) { + @content; + } +} + +@mixin tablet-md { + @media (min-width: #{$breakpoint-md}) and (max-width: #{$breakpoint-lg - 1}) { + @content; + } +} + +@mixin desktop { + @media (min-width: #{$breakpoint-lg}) and (max-width: #{$breakpoint-xl - 1}) { + @content; + } +} + +@mixin desktop-xl { + @media (min-width: #{$breakpoint-xl}) { + @content; + } +} From afb541d82e9574e0679df2efe7da6b193a740a3f Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 5 Nov 2024 10:19:52 +0900 Subject: [PATCH 023/648] =?UTF-8?q?design:=20z-index=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=9C=A0=ED=8B=B8=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_functions.scss | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 shared/styles/base/_functions.scss diff --git a/shared/styles/base/_functions.scss b/shared/styles/base/_functions.scss new file mode 100644 index 00000000..676040bd --- /dev/null +++ b/shared/styles/base/_functions.scss @@ -0,0 +1,3 @@ +@function zIndex($name) { + @return map-get($z-index, $name); +} From 20275c97057ab8afb36617585848e95e16aaf427 Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 5 Nov 2024 10:44:08 +0900 Subject: [PATCH 024/648] =?UTF-8?q?settings:=20SCSS=20=EC=A0=84=EC=97=AD?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - variables, mixins, functions 자동 import 설정 --- next.config.mjs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/next.config.mjs b/next.config.mjs index 1d614782..d97a24a4 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,13 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {} +const nextConfig = { + sassOptions: { + includePaths: ['./shared/styles'], + prependData: ` + @import "@/shared/styles/base/variables"; + @import "@/shared/styles/base/mixins"; + @import "@/shared/styles/base/functions"; + `, + }, +} export default nextConfig From d42dc0fda431d3509d55861e3bbf0d3d385283a1 Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 5 Nov 2024 12:48:12 +0900 Subject: [PATCH 025/648] =?UTF-8?q?feat:=20Pretendard=20=ED=8F=B0=ED=8A=B8?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - localFont를 사용해 Pretendard 폰트 설정 --- app/(home)/layout.tsx | 21 +++++---------------- app/fonts.ts | 8 ++++++++ app/layout.tsx | 22 ++++++++++++++++++++++ public/fonts/PretendardVariable.woff2 | Bin 0 -> 2057688 bytes 4 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 app/fonts.ts create mode 100644 app/layout.tsx create mode 100644 public/fonts/PretendardVariable.woff2 diff --git a/app/(home)/layout.tsx b/app/(home)/layout.tsx index 81ef93c4..59f738c6 100644 --- a/app/(home)/layout.tsx +++ b/app/(home)/layout.tsx @@ -1,20 +1,9 @@ -import type { Metadata } from 'next' - -export const metadata: Metadata = { - title: 'InvestMetic', - description: '', +interface Props { + children: React.ReactNode } -const RootLayout = ({ - children, -}: Readonly<{ - children: React.ReactNode -}>) => { - return ( - - {children} - - ) +const HomeLayout = ({ children }: Props) => { + return <>{children} } -export default RootLayout +export default HomeLayout diff --git a/app/fonts.ts b/app/fonts.ts new file mode 100644 index 00000000..e0d31361 --- /dev/null +++ b/app/fonts.ts @@ -0,0 +1,8 @@ +import localFont from 'next/font/local' + +export const pretendard = localFont({ + src: '../public/fonts/PretendardVariable.woff2', + variable: '--font-pretendard', + display: 'swap', + preload: true, +}) diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 00000000..735755b1 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from 'next' + +import { pretendard } from './fonts' + +export const metadata: Metadata = { + title: 'InvestMetic', + description: '', +} + +const RootLayout = ({ + children, +}: Readonly<{ + children: React.ReactNode +}>) => { + return ( + + {children} + + ) +} + +export default RootLayout diff --git a/public/fonts/PretendardVariable.woff2 b/public/fonts/PretendardVariable.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..49c54b5152a184a243e85f7281ed338b6569987e GIT binary patch literal 2057688 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

?$_b;B!N~uOJiB5eyyO!yj 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%0DSn%BqPlxDrxkD$6R^4xt*ppwK5{YxO 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�^DPbZlNTy4&O$So>`xxNjN% z(h6_B=eP8D)aGA$n5%PJxgp0Jed~DD=;-kTP^T%8J;*2*?DNT#^FSfj+Y0yltd7Xm z$*Of2*yKUuig+bk`(1ByuNqLEdq(gV@4gtw<;Dw=W#0LZeuLr?e{Dqm*E0zvs{gR z%DK&N!f6Tbhc*~JTy9djNcpc1JEW;h0mhNMFBe&0^aDV z0Cxm2c!T{J%^B`b2wkQc-KPL4PqA*xgCz-p=vkIXsnrR{>?5f{d(iwx1SQ8OimVzU zE3x+wEuq+aAyldYY1du@MuS9inpHq9F&cf$7%~&=Axd@XYS8BwrvVf!&ZDY|PIs+n z4Z2oc${WYb=|rC_{7&XzvKTB>MFYHj9YsADKi8VF;JvN3ZO1f#8oEV-%HnqVVuqw9 zqdDUlg>OJa3HH7D7z)KzGM%Oeg`V|M&}+?A*7;~uuVMH-LYnH&0QKHa`6nbN`R|9N z6oLs~-ID9A!kvV&S4ADxOch??w0lHIoA3<{RS_q1VK3DVCuxof=m2yKrUk3j-hB*; z*bd>l9M^02XFUlE+B1-i2#+e#ZzuN(dn2ZFdVVvmg}Ndx_WJn*&l#%k=I;G%jKK%} z_jgnzA01`AfopdHM(TWQ4bmbB#ap>`V!kb9Ved(ew@c%~35CeckU!(zaEE*4QTEbiTRBS*+^r>-Wvn0MdNrP1l+6D7c* zM`921$2FToND5CL@gx&Di=DOT*XPdEiA9_l{$ddf9kt1&Q#lAA|2#5a%qY&V;c_2Y zvtEfPz&4CFaZTE!XPMR;zv0s;rB#2nwP=>4^*w7Mf;-@un8Ir(-+33lwJ^8wli!(K z7fmxuh?B zu~$mORuRmK8Xl7wm;vkaJBq?F^mi*;#+SFiH<*Mz@* zA7qPtT+X?etQJ+joetl9aZHc-u5ujA>)fIo5qhHmmL&SwE}g1mbrge6HR*>& z9i?7*JI0B(a_XQMGYxtf;O}v1Z^_^a`ZrXIOZijXkC_rX@DLtFw?2Bid~!L5@ojzR za0vr@a+qpfjGgaGvmU$@d;3$m+kT&Pzv}&5pAF&Aad_^h819PjBBl+Gl8neU) z0#Q$}H%>4(OFd`HGWXesIqnG~@&6G@hnhU)G6BEqAENE~yg)$ukUJ$`qWt_GDD4gm z5t@1otgzk^AD2$BWRQ6cFIVyHZ2)eSfUwqI$qfGTRHr%jZ#X#uZXZd?Pem4a)Q|v!BK%NaQyuYYXGv%a>FJ5mP+)O^8(uttg(|jpAR?ukX*oOy(I@^ ze$pYnBG2V`P%le_heN9H>DcN&F(>TY)4`>x5%%Hysf9ji@b#WB>lx!7wPGRt3>uM9 z9XjLRQ*(}nbwBTt`JmrsA3!=IW2|p**S!tB*@nUC7yY^knq|hAGfhlSdPp?n3$#&G zC;3Gfgv93{_H1bZ5z$&+Qz5E?paR^0R#q4i1Zrrjh%ll*-SoXNQsSW15kwv^-Dz!L zr(ZNAR7kDs>I>qnW5Qb>kY-$op-}Avaj5bUGc^b#!Ys@%y!eK6)wf1VN%8z()DXqB z?oCtxRXm^svxaI{#X)pN`05KHGWz8vfuHPl2cNs59Ifxy|Li~_r}y8byI)2r-T(4@ z;#_eN!p+1CrMZ7kROC;g76$lzFlMW=)aVE3WvcG`c3u?3d4!*va`qs2I)=5M;{6k& zpe^henko!b^R{hTs&ANI=p=IIYZ36+WLM;6=Z?E9b8#9`W1y5~?4B zYbsdBBizzBrUh%+^XI*ZPX^W3?;B~S>-q8f%)p>p;Y94$oWDwuDG9%(!|H(Cu_m?9 z6aI%m)$j`)UW#MaE33~spXZ7v-a#XyZXxO&wWpal5G1V_Vk2iXR5Im z=r0#QRus@FzJy(bZYi0FPVtaT#V4TNQ*;X}s(jbvacO@;`D9F}uf>Wd*L* zk7MT-KCQN%Riv4liYyU@Obln8!pV2u{yZ(dZch$^3uUcm^p));0UOUnHgr6uAInb9 z@tcY1atre^_p=(kby6B%0P^vaMfSeW5I50pMet$m37QC3#&)ka2bh;I^NugWqvU_* z@?r77^K-PNUE-N{Juf-;-5w*M%62z|hv&O_wbjM6C(UGrTddP~My4hXEH4r4Pb9iM zn`P|#EROAqdWT1By=af<`jcgMAKclgp2@mXjnjViohAC*r|oOjLGvz+Rzon{F%i%Q z<$14V2mAN4^RP8xd+QpCdtQdl>z45&2q zqTd3Wf+^!|5m!n60+N_14{SYYq0{ij{{(zu^5OdFm7EuI>2p^LgW=OQe&2ksFXM*srC|E~z_561TD-et=YitsTd9jz(3VlnRIWbmsfy{+DSZ+7M#a;J#FiLFVOA2%oSy205t$VT@mnok2@<@Vp z(UX}2pXP_9br(pPcg_d`sl`?b41W})t zp3p9!S$$z)jchOQoiedvn}1BmDhMK~HjIMx>XfW6W3uLrKJEU(Rb$wOxnzo(j=^vnvToR(jwmauVIUT76aOXrEBshjk%{IX;(H!Bat6Xcv3KI~qW` zDul=*4N|LHB|_!Z3KrXUl<23fiEdGx^u;5?*JZkS9#lom2*r}@TN}(Q4bY`lN>`1T zdAcK8O`_#a6^R!PW4lpcdQBjXMVpBrmt-3i|N4cuCWe&(YKU#Z5r z`)2Ex4rTCVFNzty{XEdtm(v)KIf=k~Hu=mN%ppBGR+6+zgk7Y zK)1hiT~lx0i&fqaRJAT&(XbC%?xD@Vrhs`uol5ix#w1fR=X^F5nN4)2@~CPdC`o)< zE37Z*XNpj@F3oAH3ZBjqlZyDcJkkNLH$8TX_}^MNC(`2pw1K*+1$d5J@bNu>ZUzTb zq=&o%T>?KE&=++9IWNVY<@)j<)Oj7i{uXd$`pBZ#QQAC|yAEl;!JmZK@O@?UF07Q> zEW`v7O#)J-_YBYe5O_s`ID^^mj}wv;=dID_rzwvA?{F*K4CNt)`?rLi%Vf+N9cx*cD^`Fo)3Bt>Y0V@`djUUfeim<8Oj5Y8&mosj<9ZRl)JT z&{$!~d@*jBXDzl`_jTOrmdl?^6Q?t7lXdL|%a_kgzX_~)JB^fsy72tRima2nEt69M zYp_VZT5IH|-c!NVS|RW6zbaF9S^(2qWu;@qxTejXo8HopP1W~7 zQ}*`7vO>7CC4lav>Du76e!{!JU(zMk9=L;LH|W_&@WktD*PYh}kZW=01Q!FQ7S5M@ z-gPE4AqKclh@PU^B)_pjah0!VkC}Y#?lo|4jIIbZFz07%ZAj#VFx zY3BH^aXtJy6)W~^MO&>aAyE5~Nn5j-hJ0(xV3T}STdcC=u@PfvNJf4qTc3Pp=Pmnr z!vj)o>OT{H;eYbI855NwVfaap`hKUOTIkD+Nunfd2cSlcX89W$!1{ zg+kPQyV}$j0u+bhy2w(Q2q(5ql=q->XeFQ6M%>=&YCe3`{IF9eXU0$`^nyr3Gh~<4 z0Xvx&Fy_|vK<|`g&h}aLiy1&Ug9gdtHktB6_(D3Vg<@T7Moln@Jbn$QdsjYbuZrGr zNYcVnu*bj;@J`OOeZSw5q_DB(^y}HlH|DJcyO8iWMpFRl@?=!GE+C2QSP(s|BT8YN z0|(1O6;CE7EW?;KMDf&#|EL>dY#TV{PM2U7(VQ$%JZvS+vwb8lpMG{H4#u*pJT~op z&rVRx-Vvmutw(YctJaU@&bB;VElN|{sJ3V)%ZY(`Ig5BEC;P~=Vu;jV)VnzXnt zx@JQ1W6SNiWEea3|D8ya(>OK$GG88autQJ8R`sd#oCY$5&Ma_Xp5qhl%cRz4VD)Hp zaduCLVCvbw-x2HaSuYAisrQ_AJB8#1&Bas4*mWuRx<0OYpfb)76Q zQ*2*W`U*eMM~B>3(tqoeGuC_YlN>RVSgslp;Q*P;a`A?$>h-a%8}D)PE5RPofLSB zYuc~dTORj}G;0&mxto2+eHorU37u2T|Hxf}|DI1dqqyb!1(YA|4&X}Z5H7foiDpTj zBQwrXz?uIV%;{eDgGN<;v``UF0e-~{&d6LDff0&VYh1@cXHo~E=eWm&pHckuJ;e? zdVUVXkbq$~FqQ~sc6(aNrMxW@+3QG__cC_oChPT7ifSDbBA7UV*E90jEG6Ez{}voS zWiO^+Qk}z;6i$_cdllRAUfhI?Yy}QLC(wC!d?MZB%6L5mVmn@i8t!1Q@ijE#XWUDf zKcD68V+!FoI%tR4S5$)i;~ryI!;W%1q)49kMcb0IbK7(*6>`iyV9rlqwHRr8m#)V- zs+R@Cp&-IEmlQ_(Lw3QPY+`O>@@)32sTYE0@cNnlYBsZm_85BSQ#f@tDVghhBdiFz zOeGZ$7yaC7ac;Q1iImL_4sf`8ZA!&zOUZYi(7s=`={ZF)gERT8MJfT z+P6+94vs6#czuuJedQYCcH*FEe?vwjP;SoydDu(Rcq7{jC%?<21Gm*P{N>DLGG7b( zs@Ynpv742L9De%`hVPtf-XM+7h)5FCgg3a@VBPjB9BD{`7zDii#`9Vu=1q2N{vih3 zzerJn?$loukgo0ju>N7N5+w)t$L=6*CCwSpOBNzN`P>mZ5{Y=&hRwv}_-E%K*f$S8 z`sAqQn#(R;jr7dS-Q+!7k*Wv|>g&U8y~ykK*bC?xzuCZoW zg~n@4yvj;PAp+O1>h76cD_<5Of4);?S-vw{>5r`qrUZ2Cr4?C>cHhk!h%{K?JB_1iHhz_K|71ha z9BiwB#zA%xB_ShY*ZD3(5h~)|&>lAl{WDeyROTuFFjhQSQ)CHgQ4eeW& zsqzPfFJsJIy$a3@pC+Aao3XNny&+x6LC*L9KVHM<3T1{eLTZ(TR0>T}2e zEt|SI;C7FL(d3XoMrRAEfG;r`=6&IsofH}Z=*axv{XI8!eFd1>Do_DW^5;oji1w8ahcI> zf=sg7x~3^IS((jnf{xh{NEjm|Fc?jn-&Ve4UGfI6X^8wnzS|6A!ts%2r-bYBU1~sS z_r-Y#3h%sg4fmlCwf;2a4EMz}_a8#zHLtSn-a-n)m}^XjamHMV1}nyJ_yWC)#lUf< zXQ#jAq^hGSGA1!8!$wqfqUQ+jN4?trtfx9HkM^=Gu{tl8n+Esh$D>%5D}%`0ob|CV zZ!BT#A^Jc5>cB2`F_QRG;%KWwfnh-J_=mCas&58#=-ME|81VsZ&x2-Igs`aB2Iq<- z3F)%duRX`kDWSr;SM{fW2STq@>@vSYyeQ$l(D!J7k4=_@_eg)@fUQOuytnQMR$=`y z5WBd9-PM%cmu19&pY9GT!Yh#++ z87SB~Da5NJ@cAlJFxY7}gSKU5IKPFq{`EbI7v7!OCTQi4MKU5NwmB#RR(o?ghqy+d?x==Udu*^mS`6VXn!}7BiuJiK6A3DZRT-l1CbO z!+UenhupP<GG6kZ`$$XPmDbzz*P%-zVMS#343_L{>^ zf3*~UNt?Sl2~&~4ny3CMH?uN{C9ep?gcqfYroQf)Z$YQG=GdIh%3$yptl7cbsBgTM zzf#S|oOhv7=vqdL0&t31EMwd1wtnRb(4@Tqi<}gdluFPEl~Y}3^kPrfQp{<{I_77C zYL%SX`Nd@BE2VmQp+-EC#(jJv3b7dyy|pF7bF9#}|DpRzA^k5U8?(7h!&}(1j1`V4 z%wC}WGIXTt+k5b`R+7|KHj)FNw}w>uu1$S&U%sixQnu*)9)1shu5@1anGHH0z%mWQ z7y#SQqsTr+XS0Yod+Uro8@a~KC3Y`hd!4S4glY3(|D}PvwF6g0^+U^GYyQqeFLI%i zVc05v%iG6aPSw3{vw<-;xt)WKJP-nuHEHhfchafI05Cw|Fc}@1&)Le;0eOz*X8p>z zp>6UjkQw`cM<8^#p%}t^CxpdwOlBYHbzFbUf6fDpecE3JTXtD- zEjFT;K8{Oa==` z8MB{w*qA1NI)3l1t0Dtrl0unHOKSM@j33Xe5BAQx&V-{}Lc8RxFB6`Sea1%JqN{oS zt=Qw*p_6u}*3v49J2sxI-$jm<7(Sb2w)muRt<+M)nGb& zQX88$1kyaHpHd5B4GGOU6QRcLFP`!q41;C@%q-_2t6fi_dT@%}%bZKBmUo;+LS`qo zDnBeEI7#33SS_QV4jdXIZ(-Lj8Ox#J%;ObXz@hGszFRF$tg8)(;{Gb$q;4%liYhOC zE;b|z;o3rxcK3_2>32t3nTB7cbl(x$q^LM8Z;OfHemyIJ()Kw(tC7CnfKH6|BWu4@ zQ}^*WgPgux8tN{sM)GvsVV{|uecWo>A}p}{aW z%46}mfP82_7}v#h0`sVpUSRH-b-cKsJSe3jn8=`_ITdvoUBOS9XQE|%pazNFz_rtU z(T2d|jZKWS;!cV^Eo%K?Yoa@x89B?B=NJPOyGUKafHJeBP!J@GPFwnri+^O(|1mnM zpYkYgie`+dGsc_`lkjG#B*@O+7q874AmJRh`YVuGWU*AKK;*<7Ro+gi-YoSg93B>* zo$uE+xu!~wdw$|Pr$NeGS7rZ}$<5EvIY*akZ=gQy>DWG0@CmsCR`K0)0bS~~0_d3U zd}RBluVs%DQv0Pj-6-iV;FNvUJYSWc3GxEek#v~CFVTg+&Z}N83&CeF!+ssJp`$w3;Cip!-1nRyOb*t${hTAoPpOdt)s>r462D#a?jPAWgC+c%oWP+Y`$un{ef=|ciZlTr1 z^7uRLXNj|Xb&CPxBw2rul)=M62&XUZ{joES(ec{DdA=uT=LH-$zDs7C@_-<%1+cq5VFL3IKi#s#ApS#F;H;C=S(lCGV2Y!r>`s;1lt{5{4DF1mDe`klj5Z~4*mq(k#Nn%ibF2{# z?S$b&0ClG0S%sld^(uF$I!fpFt4@LH)Vo#7NgB+MGVAOT9FqBW>Nw8Ki{_0b{BL=q z?uxTnnSxph(7X1eLTM#{BkdFWEsT;%XnTlqhl>Vc&MF0jYoTI5!0^fe<(^qb3k%MJ z@)`X^%<*PL2wHfD!XM7g1CCuGGsWR)P6a`_glM>R=||i{;F2nYaj5G8UMHCrQIs!5 zf`tHrG>SL~oxV)93fdg&b3(@)46$CCT<~-;=$L!`yN!Ti@kRod0km8)tf-Lj zFPq24S##Pf6sD_gq>sBcm}{-xXuU7Z7omuI?c}VqE+pQ=BRVl`H%}RxUsy}6$S(O` zcz&F@d7ao^Yj*g?MY6}WIWtLMV?q!EN}}hM3)Y#CDc39R7$^xFk(iw5}&w%6KR@1Xw*fHFC?pg>rd%q@@8k05Ggy*$iU8&9xFnTJ9Mb}>& zrG1QTI=j?K-m+>5Q?5PSq{igRknCt;Wo=;0HOfCZg~i7JR++5m`2XCq8hLz_4EU4a z0dOzqmVvn}m1ZEWyZW66)Sm6#DXMC`A1tXcFlTA@{5;6D8z7CF2XZ#=^Y`}oCRj5@Ry1W zP4J$3HKu&S(_U~Qwr+Nlg*qDh^}{~tKK3J68MofGp{P7037ft)>AJ#g7TuyHDwceU zqETC3KAmGIM2lXiSizu3VbZ$gju#;;1h2h#JPc4FBtYDsOy8cY=`42a4@sgHfSQZ7 z1opumnRhkMj|m!tZ{&}^1}o0 z@%r2#>{_WlG3g)&+!~NgR>WZA>J0OXmH zdcAmRlkk_aH-i(yn^jbzxHHY9k6?$%s&k`P=OzoEQmr zf9{xd+krB(D)r(l?JygOr_x2s0~k`C?zY?0R=Jm(sAq!2mU!ZLj*6~uj>}nXsx@(2 zu}^A-%3?s@2=kt^KIt}ew;ks^1DlR*mSakPdlx6q5+D}Tjg|74Jhvr4D8si*;0a*9 zU2w{MdGohTNmTKvR`jb`tl%=(@tvVxbut04qR!t(;2ri@OJpC6J}@)5a2CRY3K`Pn zmw5noiYO;x<1X(B%;YxKqOf_CPLRIS%!{`gn|1KyTW2 zNyrv@&Xi@6?!mS1*k+y>f<%Ey8ZJDD*fjcwwvo$i<<&gL$}h>t#H{_pq_n0a*ow4{ zYvEe0l-Y5x1s|+OuOwXA>Y62DQEQT=Si5aZ#Lw zL?Y4Mq?U*f#U&ut<*?YwQby-&u_aMYGc%(5t7NNOYNsfccA(7-Z%Q}cUa6&rU>6u( zU9@s*qMP)dm{HyGbzJ5U6Yk1FWDVC#fhx#h7zDGV4Aa-e^Mj6o@qbUs(WdA7`PseM zTKDiZx^X`5tPM@J#6I;1_rz2{UY1+g%}*x+KWJiX)njcBOSA9+;H7R@?wI}lW=k=j z=AGTIvt`osqVhs!`m;GJt!To3nu*iW+4KA)LFRMm4^Xre^J#?-2>Bz_?mFg1WRACb z`OFSsg_V2n{aMB$uLsa9{E1Jge6udUs?MveIR_U3#Br?)C6HWZv;E`K7kebnhqVCSOr6=5(q*&qH%YWT+ZI=Yma z2@zP~+1pZvNf{27HN>Z%uK+IFel~GHqy~h&tHKLEqGU_2ETjyEPw%rpW7&Z*l+^SL zG)S~LnmAL@PVM52U|$p{zo-1qE&OIt7RXKh#3!h+7}&0`Uz;zIGf3Cg^`#kXF@u)h zS6=^o1y4DnKi?-=_n%aJU-|myXZW9%BuF^t=`ZKA6DfDG*xHD*PW)|i#3-E4+{ZX< zlw-!EW~*EA$M<>i(6PS?edGP8Ru-D*f1#H|B{5 zqn7u+wkH|leP2gZsURcv-htGk@6quHthXkH%0wGVNf1$D@=_6J6QgmO?PTVCwlb|k zpYM-G#62&ZWzzK_=%7a#?UlPjehW*-KTq<2p24x^pog&WV)i|pzkFVdhAMJ*RlA!~ zeF9i_svo{Kg-5#e2L8a5=4b8GnFpz-ZdStnkkseFg8^D_`CW8;_uh>HOBc%!pKo*r z&i26=xfVhY;#j&QM6%E;nOIhWlFjD|J>ykA(c)Egd)hJ{`@)Ha(mD8?z&hsJGU{yak@voKvIuTM*9EMg;j?Xu(4^ zG;_1tSQ+@7MNOdCS*BHAxu{v6=Cx~4I`xaFgTIx8cz!keN<>xribQq$Iz^RxQBm#Y zfp+QMX4C1)h`Sf;5{3GlXdPGQYIGsl_f##k(#mzaubt{VKC{exey8qHI4iz16zu!` zJIwU-t2oxtm(M%1dKtfctM72DcXkjl@uNrDs^ou7vCIWt>(}%wOtowfL0@+5vx z{ax(#;@!=W9^csKTa5Eg!m(#zUQNXkGll-1K~v0~R_N6MNSZxOT4o>SnsaJgpyR7y zxSr3KOxG8C=h62$FxC0x9&){3yU^?Yp6DG}092LFG^+9e4qyI|TGw}RI>O10+kz5zW#QYF?V<7Hs4n z%q4Rn-g>*4XVCDbu-)XU)kbpCj3{+?Y4I$Z;A&A?aSk#`Vhjw4NHw?z{owq1nmt@( zNwOLL-62%$zGNQSOq}UI%xM3Hy;d6i;fglhN07&VZbTxVq+KQ%o54>Ct|2Q`wG$Y> zb3LYu0;?&!|IqG?OvI}Ixw@}<81RQ;Svn`b)hKCqx%elvr&;)-efTJko==iakpZ*@ zADC1jcD7)N`_ITQ&5`8~Y+k)6;!x68M2iVs72!bG3WL35zUwg-)w$Ak&q@8yqdjpG zKwzQ!JrymsUQi3%FI|nhZnp4YH|}zWQ&;P)4e0{+f#OAe9B3A92e~XFtu1e7^5o6a zA|_*1bjQW*AD}&1&9`P2B3$PuTgrTpNOIlw zaPPrn_$2 zMbzlWuw9hX?jMxzX{MukBF$-<^2~J|7N}Co0Eo-lWlPRlrr0B1ipwBL{kkMjKIa#u z3f0BY)H(cD`pJLs`QwDoPqRI(T{NTAs;S)aN0P_yS<4OQYP_qCQl%-o$s=HIq6#Uw zbv`mj7YTwdX$W{loZhC-?5TOLQ(7 zbzZ`Y-3rup-WzVIU-C;HOuW>4%rAL3KAT;;D5uG5S&FIiq%P$tY?x^Dgv)J@3(oU8 zSN`;bZe2~gKeRTPrgE09H6QZnMevE1+AXk_&qqCCDd(G(To5}x%^(gFROjbOx8db@ zvJd@FICaM3x&J>W>DQ=J9Yf>d|BzJePZ#Dd`O@j<1CEU3O2@Y31BtjJP3QfH7--oQ z?iMUpo+Gl|x{7?ixH##m6iVN150=KM49AYZ@g|Ux1~f+m=i~blD5hzbuw5*6$?4xL zzD)SiUa=MiSuc1A%gyzj31Lr4^p@}kdaSX&Y|b~@dBUi5ykLC?iADxX-anC)pWL!r zT6>>h%O`~b(mps#S#_9Y!lOW=wiQ}9WIRrKK*C3Cwmm=JB@(;Yz;94KNV~FEs=GXF z(SCk5JFWb?5QR;ySZjd1i#2XrT!BU1JpbH3nvW^m_X9qDXAI#N<@ROrF7@`V^iJPG z@AjUzpO__AQXfAFZ?`YPrv5)ppB0LavT|RxU@STt6E7(7 zsRiTjfuxjg+3aj`ZiicO$N%NN)EV1+bpFSn9Y)zq3g^^fJc$*^8t<%uPxxV^4Xy9b z&KPoho^5Li1sN+0hpJm^;N-Ip1=oHAMI^^{OV85!^x+qr|9W#_$b4DB2cPE6sPuu2JB9+tPwBTg>9z2 z?M(AQ`f)xp`exp(g7p0dl4q5Kydk&X+4e69Xv}7ev&LO%q?^|8&0|j`|&J@Xa;Qj2MMK_g_?x@{sagb_Ix}$O%crS=l0=wArWpf!` ziQZ=!@2MBreAWUVa{L$XyQ0Pp`KWpUf1A)xv%T`u(u;c2$}o&yh)&t2CA{V=!$c8} zEj0(B1f?u0YCs4P?v57x(U#@5W}ln9mU1Xx(#>w`3z*~)5Cna=Swrmj1M0A%><+`v zz11i)WX2Ug4Nt~Pf@U&!0|gluzYAmhTlT1Z0iyJ!CDdD8Q{{2*4pNDJ?rbpJKPGUE z5~4`a7kx`yfW_Ym*LV^57M&#}S(S9~Md9ANn)!?_4OjTA*|O%g#So@c|9iH8m}ifT zg%9=VkM?G$wqG0JZQh>)!uE^8a;c)+IHrf4Y*YvoQ}Rt56w*Ckcz$sU&bQd-LvBJ# zknEN=T`2D*aOYZaS^62e5p;>pTUY7p7gQe)cXx#+6drL8bs0p`foO<_(San=a6ElN z!o1R(0;T*&R{FX{7e728Ve1UdlEov`Nov9L+Jz^rk#8&zE8BDcppIz055f-RYtfz* z(PxLLgBG8k0C!*U?553^U8+FqKh-C6Gy7iNH=~r)4B^yt(9kGOZOi?h9BKM)6vPrC z=QU}?e8qh|;!u;mA7M1g`hwIu)4KOwd0ZrSAm7&pLn||M{`C4L(^Ul+GlJ?wm=uo@ zXh}5*?@<@7S4^X>M^KQB_d>(;66O0By^rD#pCg)H$;7|X8#MLzoN?ysgQ0WhB8Y!{ z8(Pe|iDsw;Aw;HhK?qPO6qy-Yn9h9BW$l6%v|0}fc9KcT6ig07d0ze`xFb%-w9^#C za}0nL(C98)6Yngt0l6z*LKBjDfQQT=8wiW8VUwwJ&8Ico|XO}-U)`JjF$4+I@)8O>Mlm< zkA*#44fl0xPf_of?`);qHJENd(*f$O6r_u&>m`Ger6BbF<;MR)?jf0uD6-V|d`ch7 zO|MTF1a%&|#Do~LoooFLFbj$qhBmGRWz?!eMU||Odw=UP%X81wc_l1uzhm1WdjICa zYws`+mA+7isHHJQmTI-JE7yezqBO_@R! z+m~=k&7~7I>tPUYaF>U8+WaC1P~)BX8+{si&^n`&$9(;+2h%|UytMp}a>F~LIxG!x zAG+5lCP*K2YA)=TI+vlAe9Tz@`OM4H(qOdc~3dT4l3l z8#RDRRM5IDwyJG|kBir;{MafM#grmv)WgUSs3a z>qhEhJh`oeIw_7Yl6axTt!srMB$^Y>e;5`JxSluz{*D13ooku=a`Nq6!K^GBjJgNb zS-4-1rE&NKY~0Mf=Pm~P(rxOR6OsR>rHy_3eDgR*jd-2<^|%5uDy_X0Y*g+<&usx3EWVP^{gftf*DZKkno33dvx}>LGKt?y6bKy4A9HZDY;eUqN-OpAojA zTe4;esBSsqT5r@Y}%=S|M?oowg}?bK$B9;nM~pk_#q0ZeMT-irE~w~rdQ#P4@x02ZJ2ZT z@(6nH+{l;16Sc7_M{nJ*qSWF&zpRv;X1@g*_cIQjzU3=Q|HJBh;&PsFJhMxyG6s2 z_<%*A)=!-H(|eKspq6F3cz4kHkv--MXA@I-b=&|mMzqdjqq6+V8P_dgi|17aC2x*& zwil?1G8|3JBZQ~dLkdKH#5ghG#Qlge^!~ds>(CpuezU8S0OuG2@9QBs} zpOq@YPR|-ozy9@7RhZKOa_JB9WwP3hCxcV)z6fQMMz}5}x~s&sSM=n-bl>up`GyOB zHzD7XEA}W)|Mqrb`^WmXQ`vldS8PWQK}4Ue1)C*4d(dkWj5|1ag^z|sNa*No8y;<( zD%hsF2Y3)fQ><#)jyL!eFd`-o0mCl$;vV@2WU>0{U}x{rirtaFL&?IYvrS=KU9z)_ zYG_X?9M;|kF(G{?V2YY>JWxR7ylV^Tbsft0Oc;Ri?mIMnpKT*jI>s*N_n>)vFcWd; zt8BJ*u}4lL{yzh&pI2MRjURdMRf;?S^t|%mebhHj>Q$Jl#0WmhIh;b+pL9J@~2U!}ns!Nd=1x`OBq}q0!X?8l={=iQp|W6d(gY47(?UBDac-O%-WP%&IO&2D@pp4bL8e-UqOXn&XGn+q z#p@W>t29K{xKKUgL;R5lb2k*IuCSPYK~*w(tarh*Uh%?@7KI?Lcx*1dCo#7CFux_A zP+u1Hk`(dF8z>=NuUS+?@`?(qOJZTJ(S-a@K~zdU2orlk+6^`?IOT5&T^)IAtbc11 z8eM_Gy&Wzxg@p=&QuKHl8Q0qqp=EM9=@=A=+l0%|hP4iE=qliLt z0X62h4=47pXY=l)p#m@%n8Q^3XL^lcL`;a*Ipg+L2!(W4zf%r(SF*6@yshp+@ic9 zBvgi;qFfX+DMrQfR8lc?^Vw@T1mA};0fwH`ek?*I9)2QSQIdqmG#FUM!nGnlS_|Accr9_?NyCh5iXOXtr z%P7o?W@v#dencB7ReMaU5M-$h8}}HiHqfM%cwm~53d1W!#DYK){ zY+AA~d!T&JyKfDPYwpxK9r7$|jVqk_D*a%5C0;x$?VBApN;}dIfGNpW6teJzGe093 zm#`9hJoe8`34lX|oL}`mE?w^VwcFeNvCLdY?*g+8q>L|=xivdWK2ygPUf4=Ng*gYP zWbsT6GGLx>2Rg<4(MdLJO3EP~A$PZV#Vb&o1LOFh?suH0YyM7!Y#1q~0?Fn5R>TJhoih(M>buSLMWSX~T*Afi{I z%S3{NV7b^%SZ+AojNU|@Ie6jY_Xz8n%{|dRBSOawXosObWFN%5sJJZl?3N-_yO~9z zhpu)^Z+ZLI0)_^i2q78~OmTD?!Cd(ahfJtFG$yPXkxO1Iab75wJQS-aDIiEA=KP$p zS5rosxD)CZuIAY$lF74Jcpuvz;U+@@O<9|xn0Sl2*4lTOBb={fGM@tak*#9MaAF1V*S#Ct$* zu${;GIeS?@3B^~MM<&uLZ&E+w!Xv)fZ~DY=T_i$Lf6;4ySSWCtG*09SOzhb?Lv8Z2 z0b^R2p_$H0NZ@?TZKwiz)K4Aa-XjnccgM4@5uD+BBg?HeZ~q-}I*TpwiC|tPt;txf zmZkkeiB)NNIXwFfd%F$o3d?CZ-U}U!@dZ#f-n9NXp>*afw7lkIm}hg~=IwG5M#ec0 z?t9m7s(yFPIRPm5bG+3G0u+HSXCQ@*{D^~ww-~pa@fl-E_Hp=zN z-tk;sw1eus1GaJ@e=ss{G4dvmouF3W>v7wqG`wAw4VC!z`tPTMod??tJqx$<87mU( z^DtVv;O>OkV{l;Z--ngMwYsNtBOtXmOUn%wU4??VaEk?Bp~nU=k&5 z=3u)=vVzGbmNAIcGy2iPBvj%dabB(=M>2mISbRgD?somj@USRqq9)hG@ZA}$y@NWK z6~eqrpR`_!AGOxb{*Ek5Pl&@8)SPxR$xTmh-M=nDsDPECLYo+W0{WD^)hv@R1?ZcZ z+XCI!FdI$rO=8zd;b!A9!<6QzN^%Sqmk{8gAQd0!@qI%@JGWW8{Y_ECBYe0fa50ZL z=w9pp#HQrF-Qu{&r@c_z)6_nx^uwI9R4Dc#m-+Uq?~LFmx0kkAlmdbHR7ntds6OLg zd8Eo&M<2hR+l#_ybS!k@{rx;Cav_V?AMbu}*N=lv12{y#NO@WRW3qXOsHRoGa1w`SO0A*u`vf{>r0=kf-;)5JwKBbH_fP(C zQC6SnD2V*xUR1)Od>4A-b_qx|3yip5FrGOIyCtgJA)0}nZFFJjWoBf@})A=z|L!JWcv)5J72kBg_Y+Y3vM(JKN$BJe#A-HMmC|MWUkRW9bdH|(4r+#UKX z@rCXvWy4uOtQBS9C%u@24kNCt9rWOHd&XqxaQl5T&(@)X z0&Zg_NJYhaQifwnDf5s3Z@rr$)ndx z6ss1%iaE#&SJFOg^U1JNKslkkdsQ1Wo(+_J70SHU23@ezEZ{OV%=m?^nG^Y$7fcLb zt1bjfNG0=l^{3%G$w{dqGpBfv@*~T$0i4Lbq@6*(^kS)GBZ&ZnJYvh+HH>KE2Mf+m z|A04hH?T{36h8ZM8Z)22W)!~Gt%X^LG0mid70GYdDDwA^qh8Fe$*&>2NQ1n7`nMPWx~{>U<_oyc zKenHv^Kbj#>&58B9UDI6@1)-uhv3Y{q-HMJdXam~SoApKw0>qHlm06%KQFF4)l1f^ zD5`1XmtCga#Aw%eng(1txO0aBea)smiRDf%Mb~yia;V_G*COjH3AwDAyY>#sy0|GP z$GIoR;AP*PC>$(Q$t17mq<&E+YA^k`K9OnEWGUBP>VDC-k0j9~A(VAzqNPd8g4J`> zl}<*+F|Yras@>-?ISv(I+YqE8rPhXU(z)y8zo*~9;#lAECDX?hpTz6GH;-8s(C4_@ z<=tn&E4yW=Rtf}FqmTtA?5_cNOO z(6dI%B#4(wZO`Ma(cI9xC~1DdV4@Bdgz%o}`PIBlo>PSVM>Q*vfJS49h`=Osb3vis zGln=AC^HLT2_|-zOWgn&2=LOcMu@(n1L?6>+`PUY$m0Jcx>t#&(gTF~r-r(!M`_BO z&dRU;QM_ah!L$rH>7PIjCm|1(ydXe_-83~NJr90?0E#dA@=9^%=fZ$TfZ zcT)#7AMNwhMfNMp)>Jv2TMP;Eyv@5R-vxk!&g6JnD$lJRNjg4fbm*Sei^X&HDZLEv z0a~$_k?c&MxZAJs=;>@u0yuh%2~JEs%q zX}W~#w#g^r(x>N=0_pnm*H3%gvQNt;50dH3;46#z!V+sy;jQsPpAh1oipyecWBy%Q zNC{-W7Gs7)B99?RHaDNSDsQZD%{#0-WpFPeUgHM2tWUl*Rv^`CP`mfjmAr|2s zCTrGwKBAFNlpb>vdwx4Uo|Rx6R?bc~ipx$3AM}S`-Phyi5}{5hH-+2*L~)|&U-+rv zb)xU$o+w+N59#ny(!bH89N%;}8N}A)Vcz+4=6ImJ$0fO9I9N#LWTfn8c~;`j)HvZ= z{0vp>7sq!<3Ufol#J7=vo{NzFC0`op{@qoSr)(z=JSvfu_%ZJ&9L%|f?s*WJuZs|i z^j_y$A<$`ONEKr5hp5j=bUgD+5gfNl*AX1cy@d(mwALo{3hUIM$Ut_xW+i%h+;f-r zS%@Zk_!OYgM`R_EsPLzuXTKJhH2A4Ka+^GF%UzZ1QPc}L$FrNEax8lw5`TvFT09Xr;-B?j z>4|$`WeuIlM)c7hWt_8WB^jIgkWWK4PZWBLRE0GxaRSl$>*NxN{bXRk!#hDgu;H`A zx{z!{zkU@~=_cdjE2%LZ1C%Ek4S%oFs&7hH_CTt7!Vi;7Kzd|05y>y8l%%;rR=v&g zX`V38OEH(_vk>#+?VT-qJJfva3}{>6EIH_$*?KYU^smyjL{CIR3!ilQ&(tw2BGjow zs39O(h+nm1qdzfzL#&246}*uL?VfH*b-g!5p5cRZ*q;7Ug>Z@g?PGDgR<}$ z>2|y{377O8PK4e9%|jS&fy+Y-Nn$jVXNoN~LOc`nE{ff7s`0)!uDZ0OU!BG;n#UPF zVG}llRi-0j5>%rbo)mMHA%R9*kw_fzxy7sen(WF%zZkDXRKAY~7cibUW@csvY(GxB z(b{Y{)2{=>B@1O;uI3HZ4%0?#qBaM1DaV$-;B*K?`4{n^UJ02;G+txmm2ViSvTvr3 zb(3GdDB4!K%rW&=i3l1f?VXs?_J|1V(%rUFo;E*ZjDMR)U=QdF&K&neAcCbl2PgcE z5qZAA%{5$_gK4~cX4gjXnc~?y13*o^tI-6kaQtJpte@LKe-xWj9vP)elfaHNiypFw z{|0>x>DxMP-}TdY*^SI{9%|Ti%|dIC>nJGzPe8E0kYjoyt~>!MFCjNp?qTkzEiSPrf;c$P%we)r!0@j$O6Y7G z@m^(Wg(awOjkg&vM2Ih(GZzJ)L`Ysj*U6`hJZW|5+*7+AR_;vW5B+?vJt{jjx>zTe z`dcOQEru?&&kB!|mClXEM~!B7_!G?ZiMylnQCGf1+Vi37A3R2^LD$$QmA%!)H0fvS z*W6=!f%8C}`eB|xZuqxoEIqT?D=WPk-TkG335S6HJvRw^HFV(@2cRLXP=lxt-_s}SQh=c`npWD8HH*D$sB_j_`Zg-YHE0CQ9ZNqMgdaKDT zz0VSulFtd`k(W&@G$y1wtZ9GwGCFEgLQoLyh$XjRmJGIVNqWRtd!*g7a>Fm5;APD> z)~gY8ZV@PZ^c!A|P-uJ`)kcT#1v+Smf*_S8In#I~P($Fo1Rn=2Eb-i2rt!tn+<;QT&VevO3 z2?0>$a9BVQFiC9;OM}hzAUevI&()|$-Dfb{I&b@s9}lm@C7`-oEezasa z0$IapqKq2mr;Z)9d#*B(TQvst>H8$kW8s_E$1@s-<0C1c+jaLBR;@fG?>uF9Pxp2I zE9V$;^7uY-psi=q?dWZL^Pe8htg>M_)?jOhDHK^g=TUiwKTJO<{{j=9;AZZ08+tF? zPY&Mkkc5N9N#3!tF7$0VK+eC{s!t7Hb$8lZs`=aoud@xvN6N$=U>bp(B|rp#zg_IC zqQZp#MZC|}I3zKzbHD0K;nXE?DNnP2fIO^t{Q^Uwc~-uE%QUD(aH*KYwiZ_^eNl#k zg1Ar;TME|U}4KziEFQy`a1vU zIJEk#Atz7HBLO1omEJo$!M)L`D5^lQm-B@&9lHd@Z^K?$o+t+s+5psF3FQ1UozoRb zQylGQ>H+^r5GFp2;9GlW9zJr1$JzC*B6x)k(iqD+5zgKqjz`lw0F8`>>d^UnQ`S`z zeW+W{^0^k(K^coS01Jvu=9?!0a0lo#l5gKWw zRMA4I#oss4I_X*`?$g&2xgZJ*9Br<~r{eE{+VvR9pMj`Bqpj$h5zw{&N0>phxxIx{ znTju`LBI0vy|G=y*!}9>P$}lSMUi2K_uM;gtp2Bki1zbehm|p5jV*ci`g7L>OtNME zo^l+wBE7p_zm|US3^G*B7;pb2X86tah_`q1?=a6mqQmn0uxcdiX^DBCZj)zKxvNrl z@n&o)kCMy!b*8hhELQQXjIrCcuz#Wv@O0ij(?*J&jtohd)t!#XeNZ*Z6L>!o`QDD( z8aoei-1KRmf2~byePS?pVP`rH`p^Gu(1 z&F!^2t8We<0Tf$*JK;Zo(iY$<+H`r2g<67(9<@1nf4RiFpjV?pkrq6vnpnuHH7^VTj{E|W-3HVnm0^`xGdlLDRj*J zJEVuCaG`j}myV<)QDb#EsyelsPgRfdVaZ8Vwtu`MTA%tgbka0CbY$|_kQlK`F^M~# z^{L@@ni979>(hz5OW0Tl;AZVAY(xTtznfW)m6^DMfnRx+LN5QUs;F*?Yax zm!_bhjX|fFc`QEJY;MujZ$A0V)1XgAza@iswL#37MYYP&*a^p6-fBQNAr0gu=%w!? z)+K{w??SWaNcLe_4nJSpH1dW zAGwrU5#@LMxI~{sm}kR~$&4nmr|X&D!609_n#1~EcPdcd$AJt_Jl&N>*<+B8(%6O$ zz6`xjqC=jZw5r*WYso=y{eP7JaK(;w!Gy(;BXsjb?eaEQ_Z6yU!5sTHZm*;p;g9i2?&Pu#>qX_ zDqtQqI<~t~V;Fc;s>eyx+)7}oE~zaA9;Z?A^c8exNG!Yc?!BmESg;vXE1z7=i3e2e zRN9OnwyoiRuj#4R1FxY(-q}e5IWv5%qux5ZKs+ouPDm?a z95eo#pFg4FR?CeKijukzs+RvELFOC`hs_!eD=(Bu`da8ll_Tr~jnuvzGtVN(X_O>| z?J4+#N#Y-dNOpG4x|T6~*+;TW3PW^~leu^72x|=5625WL`fkG9aLXl;4MLqvrZR@j z;2Gx_1q4HeFqmma0!y51HcWK&+fP38RPZdEa9uiQ7&^@u$L}DtT4nRFLd%&2**Y4f z^`ndrk(Sw-b+}A2+i5QIf$`yfPVg6Q_ey0{_s`QHfEkl=UM|J-ys8e z3*A+bkfix;?uu&n=|>U1Is>z@SL0&(>$UOch@Sd`&arTil&M7pJb!bcEr%@AADbvV zsK%&s8a2))-Pqxsd~I~0d%hS=G5&sT!E!f97+){rkG!E1$N8BU<-hv~5XyhU zGKcThmAHhL&ED=Gg^xLl@T%kWW*N^N9?*r#Hvkgyg`LZk;cEr$OW`P?BoZ ze_Rb^+O~tggOF>Mpz(s$y&QFk^vY3Ksg;wQDGb-cnO0yB=P_fo&Eo|_FDg@XWQ`ZWel>}Jp8!D~ zn%|G3%ov=oMWYZ<+p>|rkZJj2z-H&>_uqVSeB4=~KWwAb0!^Ue~G z=wio=<8|VD;baJHb!?R`#od0Dh8{!10R=DZ+v2{!$gOeKax0uZj2*?fb6~Z0R+r>ditXLh}G8m40INn_B5V|%>8SaKh^dpmUm`?)i|dJ83MS@ z@l20qTDV_wmfd>)!dp6~6e%B(Mn5l@QQoNTqsA_oC-;KmxziPMJl40lDbTgVw=<_K z2Wt#PiRBZ*vPI6JDc3TyZd#vY_CV9cd*gQM7tqY(Hz*3Y=|9LP+Vpk@6DN(mdmjvE z`K&XavJD6K3%2#)He?HSx${$S`(t94}Vva(mhu(8+d_3Z_F;PLPMfW&F=_P ztO?DBx4k%5SiyV0cW~#2tuhJJLdclOn(!|Q8Z(8r=~8FeP;>L|^kqk!l@kXI-&s|4 z8;2{aJ$Etl0!ZDYwMTw8F*MFcwz1ndjvn%e{R`1mapMEPYE|A zr1>%k=c2fo)gB(1uBoP0L$nqgmA5*+VaqM7e0(3}iQ8Or&g_8>ho&)uAUB*$7`5c-j}(BM*4G_MX@-2=jqWcZ^n~}9>3_j zOpj1A<#~Qjd>3}VO7foF<-X#pa@*$H1=YCi!012CrT&wpfdBN4{`0$20k@PH**GHX zkBon&M>iZwl}Q1vUr*QBMo44z6+fGvEvvYs^ zv&nS;?KFpe1d~ZBFb*otev>w{7wTTy7@n=5e;mW^$#F0-{py5QfJuLnB z^9y(JEC~3wRnh@+%_PPiyPw~fky>_JoFeUAlr>oE_P^tNZWYm{H_hohI($VV5qmk# zmXIhp%yicTihgmE=li6s@xmxT{^%v_$|0s^#GF-AX5g7~1GgRCmVSFDCVvKRM?sJ9 zcW#u5$h?jwi@>!8FbJ*IT-Qe1@}Avb5Vr(@t*%XueL2XREX~QDV3hBs?Xei?zXvE^ zDop>J#k?~?{Of6#vkB3@Axz?r7%DbY+Wzqa+UAg9xeU3UVKQ9q6UdO!Vl?kS8hkb+ zQQQej<8_qua-t%%;g1Zxf_DgMUClS9V=0rxp!ePq;SPki3tT_u=qaTmvLQg&8S|&Y zv_pfVzi*=vi-LSxm?a@opeO8vz8FS?6Vlt>C4+a*kD;<;b&;;Sc=0zN2`^^xcTx^O zxo$%fQkio=wunc^^=-cB*tABo{`lyB>-{-CcxU}MX1$qoF?L@laW7PYi9^v$XAXfS z6quW>9pke)BvlRVUPD`R6-cc?78W?Xm0dO^iVKUKY2rj{W7TOxv&jtWlv=pdLkbM*au%DKv~!$4*bV2@{&PP^f@c9-Gd5 zpCrX8aWCj4QFv|>X6bYuA*vUS<9SL^ zM;8t{1FE*B5uUuK6&!A0*%0PuP6os<2P#>@HK}FrScWjevGCMT&oJ~-hKg39BEsbo z5Mk%S*9EX9bYrm>b%Qm;{_6wC(V2KF0bCN^?5Tv{;tTan{k?;1mt9MIo3uEV_UYU! zss;mVa~zv%bR0mh)j5o8i{Q9+$6?GGp4kw$G9HGe$4?5b?TPPZ6~Iu#-5ggdC^;m5 ze*(f7cohmYARQ)Ss&NN5BrORil_uCXZVO0&-vP)WJ%}2uup~mi#-fvHf`8S308ROb0Mua|rgOgsJP>Kb04^apn$Jjz-^-i`!rQn9S~puIl_6I;EBOA4TH?3CVeA^nQ7O;H8LDtBbQDdg?xurwxp(+arP1- z4Lhf%L!tZT|F1yP)XD{GPwsbqkKST1jqOgGyzG%rDwzdsu7dFum->nvL1_}t;~A{@zL96RSi66dgkf~a)fKI5BZTHT-G+f78&&vHT=u-m;=$d{(FcOuoAu$o)u>`EFJ%rbEH;UE} zBrvN~#sWhUj+W5yhX9*PHU>k@E+Kp_$;iCYHcgs6uBb%&v*GYmSGIdr8>H&reZo!| zFyY6rCZGbR5G1LJ2f_ySH-QBKI(odJe%#IN@FJv!z(I&jGfr$FxOM1DK--IMD;ygD zJgUrbXs+DbRXmg`kZkmCqCevTL5!9ploE$tN1Z_`bwfjaQxO*4m-LGu|8?#lj=eR! z!tXMl=UmKVj7|5Ek~+p9#40urB=e%&Xpn3UTM?202|Ldq1BLAjIRo66j2Z0NEGwAf zzBR;HicMua4SA_TKk?@L z)}73aQNC`G0$8oARbANavsu6JkAl3u;jZfD(Vuw|7W@B1pv-=&5%&DQC0jn{$?YMG zT9w@#kIU}@&?X91SfgVQN(%IMl$~K8Cqu7bJJdg(S(x{*9;=glNxKwtqgGxVaj7@u z;bK%T$V0|A1rkemEiki?sRhGouypA&IiTeWA@qelOx1jpcwCW|o|l(gUtA##3+3I| zi)ke7&Pt)k^TQ9AGXB1M5hy{x!?B+z&_@4kKqr&@f_{)i>5<{{Q218?Kh7STLACy? z(WLGP8XV_H4Y4#-3y~}H9GxrjDbMl29C1FO@mu z==D87U(``0)8OZp#W3(d^Z@Yr4rQ^cin6D1cDZCb=i+Sh@@~!yAN&1tS!$t#_FJFS zr8JxVMK8g{NJ*q(R8V5u#C{vUg})n7%9Qt#$DlkN-N2yfy`C5VVy6^WTzb2M|6Kl4 zUV=N^aSh*LegGgY$%Rn7G#AsvJI^sV_QcfdzZ_H^!S>=0qa|e(qtdEL5Ta<$2;#ZE zGXf=6GaA}=O2a$ehX5tG{8UFJz&Kp8MOH?vQGpjjH%o&tfeEu)YX6fOgxNg~Vdp=h z!ylm9QSYJLBeYld*THEqCgsk8AOiE&PQdg-T7T^cqAsa?RkEEIGbA9jLW^L5g4o?+ z(2z+L;2`XlQw)Y>yfSQ@(aM&#E{as6%}F@gtAzZ?DG3tKGYFNhlj=$WR z)vTABJjk+67XWmHrOG2+zZpcq#N9iQ0nsK+Wjnkaxf%Sto$qOnPlLSA7<~zOLgGU_=eM9~Uq?d)Wj`V(%cU)Dc&^MI>Uz-B{wwL=R(HY3_P%0H}sucOU zgt#HKdXEZI3{;l)MyFpNN$j0W_KSgOFIw~-LOt2C@b|DK^O?QyH%9ONi=n8*n}U(E z4kuXDUkuI-RGo4Gyjqz!sPM!*S^)3tO!ehNq-A6^llU?D+5J94M|BzWIjT8l;*%TS8Hd zeL2%h4_n;^R*mNaqCLb*$_y8=sg`DK4JqM+s*e#mPd8WjZS?22Va?)^sD{srsg`4; zBwsNqD7YO7uz#Z8jVNW(-q=*8%lbXDp;R8r(?G1ya@Kq%^&Giapo=jt^%Y*eUQ2kmD-)J#a3M zgY(&ia@49CG<%uD$%hn#r&)VI5Eyi~Cpwe>@0AW|^5}BE_?xT^u~kM9JzM$`b`@B7 ztw}K~9X;I%Fi^KhRIfk}DUF><$H#3-lYrHm^WUnN(oMZY@X>=O6ePm3eJ zzM&#<)lPse5)uq%WOjXI8E**pA;?uXXwa`0q-l&OkZ66NI?cF`KlL_v`1sBjE5{Q*40*!O(lz5H0-cM*B?b#CV6rD2AHRW#+kgI} zZWcE_8dNR)(;U?p>-NO?2TqV{U<2#Xm>`(Vv2h19^N}r%^S1_L`{y7EP?yKj9g&XC zP}C7e`Ix(x@bI8c0EZfW*zZBcwEo%b>9H-pD|+Q$j3`hD8mUcQ^@PVuxe1TRz1AGLuOd z3Kz>@2ri)5d~G9aQ6l%98IE8;SjCsHze)67wl26KlkqNDu3vC1xMC_BsSAu>7DBaf zB_nvjr~UpW)evhAhh_pT(DfsJacL^pPUF5QzlewhaatA2S&;1smL{SBsSnzV8dV51 zMhNyciBI_e*6WkRU_k3@!Eg455+GA83ljql2{o)~aS#03R)85bF6|)NV^3TH4IaW| z>>Z)C98CA%!ht7u)xoEb)Ini@??wy-2Mu@*CXX~T7A{;nD-QZSgYNY?2?nX3CFl`q zWo_;CxX(#Ull!skRr9@Ci6g1z+3&FwnucdIz1HvRefo%tO=#UeHku=3-P6 zto1pGALX{gN%Tz1^B}4;!wb3a+^s7J>gbL#KxqALmIAb59>~B1Q>6u7i&n0-kTzfw z_h}Mx(fg3B-6^&6pcMSJFwf??9>*iE%_N{$r{Zy-AU>Vf?-tie)jp}Xjd4%esSchTHwf>VP#Ye6 zqa3n;8MxEXp4S7xzncV7#cB6SfA{OfJ~-3Lnwr-oKc}H#Lb~Bkh@n>~LOF!b?zopj zJZWLc!5SE)x_XmU9J8)@acrh_aR8lQ;$YZhJBGjliqD;T)-0@Jy^8gyN8laZ=v0FO zYoFGZm#Rh3{7%53?@8Xs5;@8wnJ%^DogT#*ZwN9R3zIOT^z};B0J?B6Z$|nI$k5Y} z{=~GEb7F=gL*SJ$N+T4L ztp5xQF!ZQdRV0UrPE&i#a;v*yZEcH^-jVPzA-<+-Z4z{Wi2D|cvR?)tyt35ZZiW(q zbGZihcwZ>YMkIg-lW<4oz3yslrA*~J00AhMsc~%PZ~*Rb5Un6o(dO6iVqw?v77eo` z3G6vn3zX244CqMExzCg3aiix_Xzp#-JMb_+yXs%QJ+}{QYc50BuI*GA{`5(OHct|}c41_RrC1GE5#kR7@20H-*z)gxV! z0J1lRp8$$KtA?r?`iYD0E?0|ZH_#Y{#&g5pRR2$ypX=FcizmqN+Sx#8&xc$^7iw{7K=Zln-w7Vucj+Q01e%S~dBLBU zz~9>)KlV*ugm5dAv@joUTiV^2`K4UM_1As#Zfc;aqjs2>SHqcvJ%@&-&(%{b`2BP$AAJAF zk-UHH===ECIrkpN_RTW(epeprE@??AVNiEo6l#a}H0&Rc|DQUV{}FuMnI~NLqya$pYxcdde~}QJwI27E@b1xFPuG*UZPJZ!hyqJ+W@i&pob}>jOxJ zNRuQt_Knb6#I5YXT}bv*7Oy*pP(L#J)KNqRAy%%{7F}(x0LE)#a)aEx(!U1up}1) z$VziL6fe<*5Zn+CpSx~#JK_v2-ksqT?S{QBM7Mh&#)nh#kELoGZv$CTFUXa~G=+V) zqBcmw6={<^T#|UBu`r7^4BhPHHV+aXPMXpi+`c7p>EL@5c}jYpv<>jUN%4405E_tR zC0d)`Sr0%!T;J$1C-z|~KlH<~#3exZH>DM#^fK2P9Kr%piY%do!q{9o;b6dwRy5_+ zjlgmxPp<>jA(}O+Z}ibl=#bf5oRVF^1fMe8`6wrfmq$P!4(Mrml#gx8VW@v`KwS7` zj+X>~KR4W1dAud_(4zk?HG9=E7Gy$pkw?!nBDFZYV@X37^%8b^h>Eamd~cdw-W<6z zChzacz>h?m4kL*%85ZwX5(nV(&wo4_5HFa+VWgk>-9%ID6&8iMg}-M>`J@~1pg<9$NkFq4y`Bt+fjdnF7y*K?Y z+r0ln>zPB)b6D7eB&~P&%d*ga4QArK|N9Qo@!{iH5&ax*l#BmL5VT{uGm_ON`F8o6 z2de)z?gN<*u0QP^gQ_s=_bQ2WsJArvvf-8XQI%-paUpHMv2x#$q?k>o6b3g`{-jHa zV69h;p!M+ni89|GPaa=wp&3JfQcy}y&SYOAJ@BF!TlVy2_TJePcRmn3d~c-R*Og*4 zdn3aD^_1Y?VD)hPhY1W@PYpsnggqP(RTR>M(`yk5uTWH*iIj5xc=rU0D8;}Hv~F({ zq04`BHpMWl7ZrhxkIx$G5{+DhgY9onoEdGpB|J;^fPpwsFt9$wbFZa9U(qm#iasR8 z;QJKo;KV53Tzpe}6kSG|aa;Pju@LHbee(J#&4C>gMo8Id?NYIh`F&<&ROTr@Hm7sW zC+Mf(TInpt9Pt0X=0Z!}XCw|VDVNl8Ke5tUJmt@2<-%;@A3UIal))vp6*`JtMgn_a zJ^ag)xT}wnW5M@II49W%7gYhLLdIEc*|yggZCKAOeH+}L?*R83tM2J*AEB$z09rGP@T@Jj(J zTuU=XMvG-MbiE+Bj@kxWD*BHr9Y< z*pRpW{uF!G2socMga6X{O$|alWJkpEM(6h?#b{}xnyFUCFj>~{S=3mGC5>c3<3opn zs~lWbF)mf3)G3&M;>TNWd){iJaQ4kfy}CEuboP{n(mS{h!WBMPn=(0Q>7pYX%B;uq zR<8Z6fo@D0RZZPqa=xl0cuMU1CB8Mmv-Wb8?N&}q!QvJRfmz&v8$dQDO0S%tTs*{; z{S(gj52t_v4Mowb+IJ=Gwn98D);LVbnZ*8-RRNnLQ@FF=0R7tM5M zX}OfL(Eo#IXge94k7-9t?kC>~Z413ga7*#|KgfxW@Vq2jENPwp(!_w4goC!Bb;mWT ziH9X@a`bQIBt2w)FC*mGoqsGE^#JWR7|fAiu<(S22F!nROS9>#oCGSE#LE5P*-#rk zrEW!?Te$nPIL%T5$_GGii8M7jclk-+u7qd0`_>NvA5wU=) zrTZ7XjtqIu?M&M}>}Ms=$GEakkL)y9YPaFv-?-ntF0XMmhIsBbc%4}#NHjrK@|{9U z|LG#mgtsv~`WlqG(ocGB&E4_qVGLmcGH;9rk|e)OzU_>D%4EZDjf^3ePgOuc~3X%Ty_gE2S7x_#o@iy)yA9$+Y3}~lL zjpCHW^gO4qZMX-S&GEqB6=)W~i^#W;ar@!%6n8$%=dmhfW+K76D(3SV-(r?+u1#P0 zPn*cdveBIqWHty~0Z$stds-Z^D_ht}6J=@iv#yC}R{7XxIiai^{xHBVx7tAY_CZa2nCM$FOwUxrI6RRQ>$;fSF|9ii z(^>4HG?v~cT3;P4Xf#ed>(fD6cDpDKmE}t%SX(pi+7{K~LzRw*V%|f-@da!a8xlgc zq*Y^y=Pu?~-9td@S$cyOBS}f!cr8)H@%z%L>DubOQ@PehAZh}eQ$4F`kq1Y)bjs#t zRdUI;2%5cQbDAcbY?eu?BwpmbyDDSNAu%WJ1ePS}mvZAI{-lp;R+Ztz7vs-t3TJsd zO7Aavn@^6CR3^4W_Rc1bw6cdzY<_3rltLFM9z&^PU6Cu6v(Ce^fBVym}g?^y-{lh0Boe%6gyKmtyH*^gedZs#POw^1V+9 z)ZKf}Rw$CZ7jjzlOO6Xx1bzPG*ZTd!XyRMT7J|hoWOvB~(@=7m$H)AR^6W0Z!#vB2 zgO6z&$gBK%udVDZ&;DQ@c_7jxp40i&m!QvNdv&;UuJ4qU=JbhcTgUhgHaya{*Tu51 zNhn#)L8bP{$)f39+UXfz|jXvO+xhW~caO1@A zzX=gdm%0jnnjA#8c--RXl#@=G(e3hpY0J{q0Sb~=I(r$b)#^N3L?oy5xLG05b!`j_ zkqr&qWj!^NUe`fO~bb!O`6tDHotSW5o(rSv#R-Uvd157xsmCZdjl-Y|LFo!fw~R z!J#jkLQfsel^MN{%yo0sCrS-^BEqtJg?feNaXQpQ3-yhJ`I*!`8euyLg zMjr`GX>>%@d9}H&sF@hPRVq>AF>cS>sVM)dEDQINaN%4(OW;K9QZ8B`dM>YZz@en& zjzFQiVUT3Ne=O3Hm zw9z$qsr-AFu7U)Lt@<|xauz$SH`wLv*0H;1O>XtjNRHd#^JD$R1R?`aoxRyNS&m3( zw_lTj87~)#h`sm`S2co2A9sN-8Dr*^j~VHkbsZm-5_u@)QP_T(iai#a-u5`jIorw1 z=MQaoCGc^ksP7jiRXh@#0rcLKBhvSSjJrG$PBk7ek8GsHir;P`tQtEg74_UL z3#A0~<#}?CbLKf*mXq?+xpa6-EPovT&2hYMnjrb|(7gH@r>%P)O+RFZ#vIe~1p{@} zK0p1n!)5)z!1_UU7Na`K@nVbmmhwvcB?vlkLKiICI&)ZhGR$(VKcHsG4;ZB3Ax==Wzb<$&X1n1TE%?ORAFIL0O%h%f$iZugE<4>^s^tf5C${vq zv#!*ZC}N+;>gv_4(hKVf3`>!KMvRfj12uDwV_7FizlJTeUm7N+q;Mm6pNh?JF)^x+ zM3mm@?^mJsua+gIU>?S*!I+CJ;!EVoxf;^Cw7Nb%5?kf zR{YBiud9zQRH%E|ucXGlM7%yXb`r)Ke8+D0)pzemRa+ld?py1bYOR0eP_jFb8u>DE z@1xp@#y&>!+JIx1T=p-F<0-CJE8G)i^J;0GK=j0{ykL}@`iL9KwqssnakI;s#gy$W zNl#xCRrZaEcPNA6N{_$CZ&|1{Co8^9^yClr%Peealhn2)ThSg*6b{sp3o z$@>?;E5h;5_EqwfCEoTn~owU0O^R=!4`5t?o0iaqO>Q|if#bAxh@Jm0Vq$@@T_Nf|BU zc`LayD8r#uWlp-}IAyImx4Ia`8=cUat~fh+l<(#scmCJvG}rluwwF$X|E{ zW9*v#%th`dK9jCO$78D`ub8)?v)uyob_dX+$8JCPN=AsfR%pENK#?U*D(WpoDVz0K zWxaop7Jx+Cyv8T+h$=2(N3piqyMFTtyKVNqi?l)NRreMFKu~?pWYa2q%QRtmH9j-_ zyei*9Et^;8o322EMi_l777eM`war9=Og&R|JuZC4>s0Ov`7XL_-VWzyw$w`Ji)T)! z^2<)}AW$vuw~*~O{L27X=uP^~=b)}#_HQwT?YGMRP5*L=l$vcN?PKGYqx(QOuwwsG zIWFAq;g4my&%;$-+uuRoauau^_fyYa4ukj(6h{m~4qI;Z3B6-lbeb5_!huAPd8P5o zMEC*S2Qn;;4`NtqdH};8i+k|Gb>agT{1x;)UoSyVD=R)!hnH(UP{AKp@f@IV9)GO` zz5h55OqhYc2E#ez>wUo~O%GK51594+Z3pYjux>7CV|qD)Uc z$?~yT{G?=hr**p6#g9j}{!P2x*Hn9!9T&VQLj(L=Kgr?r+J~qo@{P~!sPX}QP=E$Opa=Jh81wEw&3HM zYj!5}UG;ThER4E!09W!aJ0m4nzut^`pGvP@%=(n)Shdm|D^!+ajY@LpJqI#*M2+<( z-z9CTnrXc$`vM&m)naEpL~E;8dV_!A4kgX2QgTDLp6FM*F)OuUR%YWhXoInJ2VYhD z((cBZg?MegAMH#_V2+zT_02Xxyk|1_F%i1z>6>DG-#;$=?Or`5JZz4~T@=FLlO1SQ$hCM60#$31 zZ{GX%OSp9LUbn4ln;SabGM9K}E@jA{B(zHlUyz?kI^$myD)2V3CEk~`jjbvikLtGF zwy+&ZA!?oSFIZ3iGbEGz^{M_7(xO0t*=Mw%ErIEqfmtYnpI9ZLDB%Eh=?yn~eGOb9 zEYnnCk%#^cLY&Diz;@cqR1->4UPY;eZAtLmAeu=Mkt%j!eL8?6+;;71ln|f>@oI-S z&(%Q!zAJ{Jyf>y#*Zrnc9l|x@QIwzdoAEa9bOb-|Vd!YiTRT6tnQ*l#l4hMIgbb$4 zR}qA)*>81GT<~bwc9MJaUm>X$FGpUMHVZD}*2mX0(WpWCcXB&2#|fXgZqNQZD+TOn z7aH+DPO-ijaS=&pbK=s3rpi>VxmYaoOtCvU)&Z1zpw|6Tj80O?BXkGWA6Il<%mR)YL1ozq^VD7$~oF9mvZYw_i9tnmaA(BKccDWMR`g(uIOPS*UPc~ zxp`C6{kF}`QJgkqJ&~rW!z)41pTf?K~DzE=_r# z)Ks^Uf?2ba`L_Qd+(iyPdhtT)Yb_di#UvFD`j=QG$V`$N=e9pZuH^Aj*mb4M71D9F zgZdP@J}9@_B$@K?*GNkJscu97l=o*Zpd2n>!fQIekR=7Q(Db%|WBwhQv+Yd1pLka? zKvVEiZ;Dg#ft6D7T9WM+o22Mpbw5}`ihgf`Y-iwU8TN@oAz#Xg0FKVNe{P0QlI+?` z0%6}9efJY6m5Ye|?8!Q5^Vmxuec{Y!h_CM>lu}|ePyo$sBN+|~^J60yjds}IHtka>Ad&C^oAnB{2>tzK|1axHiN`XBiioK%XCyX1n6b3 z@VwF9gJ)?zHV5Dqx4ETuh+7bnTO3Mm#q_D}y4uMXNAPmNIChCAiRWVn-7YKba3gnK z9%_BjM|c~D7Kg#@>r2<0q@8_*ydgNbY&+?5>e)CSTjOP=;lj}@@jw#^zUL&DJ~Xfg z9`aXssf;nr2;#Qy;Nza&iDo`FCp>CoediaN;NLPhVsqbeTbV}6td)-O$-DHnh;Tq8HdoMxqUgNG z!6(@nO})N{s#Ze^+RLUa{-N`usXQ(G$Sz9n!V1(jz^G!uAs1X0eGsXoVO%h4MZmd1 z!`j=Qyss3mLl^HPM&m$>MK`%=c`-Y#gF_O#`K7Z{hHKm>?OMS7Gf`{(nr&&yw#cT(bi?L-O#1h)v(mTd29W1l-aNH74-KJbI*A>%a z=huts_gc^gA^=>C%`LKrU#wAVmqs0E z_Km`v`+-cPr*&=FE)NhHU2V&_%zIBZJn9rf_vj<1D8f5>z6ydeB{L;(mRn)Pf%D;F zUx8TLRXc4R^GxDm^g6I(>1`MOI{}ea86J5^Rd}_5@=k@%{AIPb_`Ari_4z=yd@{;l z+a4-l*4?-m7q&N+|6m-B%1tmGMVD-ee-00ro?U5E@9YQWZrb5%I1MSZrO0Ue9$sjZ zOxsD+bo@~_k@x5kZU0URfQ~8jr+k~Q_@>`rs1g#*Q|)UAb(@H|U3c-(NK%|Sub!mV zb$1*dfG4}S^>nKI?Z@Qla0Y-%Qw6RH9o7za=oA}hQo4?kvB?9w{DRkBPkD#3bR8{A zEMKK@kl5dl0BZT&YQhsDlE{~{*gb&ifyzAwuKo@2ben_xQDl-k4!Z+5Js>~5tO1nA z75K?nKG#`>{_qhy!lA5zYkb#Bg)_xA zI1YG>ue{Gii+G-dKU`GCh!V|!g~fW={lX1}J96sUwn4I+V>`bG+%)!wdbUtI5i@D&4QK;*XirGgd6bkTu;_=?mfXZZ1e@#KjX7$!@U z6LEVY^GptZBLJq(8fNqYU%5o;=lf_AZ|}L!2Rd6phRVxrXj-lD%xF7|mG0gP{9o5- z#`XVwvuNqlOcY5AWMM9;YmF>1&x!bhbi2y&21Jr)m=JVr>t$t=g&&!I?)z2_9ymd{ z0Qv(RYCuzL8JeI~lTX~Hog-ePg!SblWRMQiis#O^69JV^^m+!V+6iOM<_zG0TdJTGiDCw$aUbrIb|M*(khI%-SAjt+TXpxdYa+;e8PGf z#g1_Ht9MzS@$xCJz$kIZ+9=GkvX^$_Fw-;w@PcxJln}Pl!C$y32EJ2AB8#mDz<5Jt zp3yGj9BF+Cf08j%9#|U|zyM4XdofjD96Qqd`dmL0iqj<(x zeeh7!zxT~c>@ubhrHwv)qxb4v-0q8c^-f}bueaAwXWriPM?aK>e%aLSGsH?m4c;Qo z_a0j@$tW$c!mi(-a>)AnrscEF)2ELU9pQjUlckO3jDn!E@tpampOYGT%siALIIy!6 zUgsw}c-pqkWV04wZt=GGW>1~w z`C~A8cpi&!ISXSl7Td4;`K2Br7?$xWMV?u~S{SQA%k7WZU(Zl_VYQla%$oz;m88j?K8Pb}a2?(}j zG5lo7N#@e*#sHidD(8m}DgBa4_Wa4$o|T_6G)9hx19 z5^XurhZY^JvQ)W|m{ed?ii9c1ek>!}BiiZ^YM5y57_W%v_jYGGlQ?&_hYCJr&9Rnu zn=+svEMIe{Haj3ADp!f+J{eTOp4B?%psbGwp25?+GkR?pjgsL5zmp8GNwx{}(aKg- z+a$#`j7uuoM0zx!!);WE@x_PHVLJ^eh+(I^^Nm14sl`>1FPQbYf5x-xoKI6J*lVO7EjVF0~q(qE^-@K zdWX2BcOjVZ>9A|5f~UW`JDQeOqezc*e&Xu@e;Rj)g%6u)FcUsUu0+e z$4~|#9D?Q=k%HZ^!ed7smVE{L1|UqZPXM(MFXav>dFav)PobF7-wF1NmHuU+Ii|&9 zuHB%-7CLSsuh@Uh)8i!)dia*Uy5l6Gkh1>B*HU_Mg?$zp#OZ`bdm`F-sdn(vslUHw zF|{G<`S76L5+7n8y-d=^8doQ|I>$fAv$chD)j1wI0Z0F!dcJ7(=RJ)6ED1Te06C$C zYJMm^Eg2eEV^+>p%(PZH080GHY``z&Fg%l5<|vd;BOv|h2qhVpFq>Jqmpm?{p1==3 z&P+Do0gmjzc&1y9!)dfA_>WxUJmX1#b!R8@1L0x)Gwlb#?^e3db0LJvcIbAsJ4vKH z0LiiMp!W!JZ-QOgFD~cjR!v7*Cp%OhyA`RRGmE$LH6rEG(?`v*_-@kJ6`f1g7aNt7 zl_~3;iSI+%;?c45umh{Q@SgvIdpe!xZ}G%gmd<1>BVAqxuWgD!7!^76QvlcYihFlk6g9&cP_&N)M<>&;PGl@rUS#wB&T(MJd(Q;NKdcO3p-%T6P6 zd;6c?Pj_@sc5}vuhWA;1HvunzuAd?-NNBs&EN9u5Xe<7=|fFe(_j$?|}gWDoY;LmvKSZ(EqKc94Kv`-b{|K>V5Y zDf;vgi@UFS$847nl(mX0gu9hq*(eRQ;45W^f7H}~%V<61rdYfv{o2`23O4}DI{+KFb+{>cmQ8iTSrY8 z)vw=^p#2MH*({^YK)DzBrB7M9a5Epurw!XT_FtMl9Es7tx+vZo`M>v4Fi-_SS&5ve zOBhKv8q$ng;4#y4ZxjDbxRQ+$+kfN# zLCBC8D5ob`679v(U znk_(t)d;P90xRdYQV=e{oDf%<1a!VigWIIRFjaRV5cW z{>u3BeWhsUJ{&qoOb}p5hfpX2A@et%uq)<9ZABJEUgm-?YZ>G%RQV>`pSGWbUWvea zNU+MN2jhLA(uxZPsU55BY#Nb@-SauCwG`kX9 z?0`QFo5AqCHOCO#!c(8%y3fg>d7sZ{@Xmn9)~}?6uP-$#{49C1wd?hikv^)Fj*amup48CqJuKZr z4|v(n(aQ3x1B3Pruk)ah{EE}gZ4*)9SasGo z;{l+ol?w;^{ap7$Ra$U3C13ZahtfYC6Z})?>jUL^@nLENnLR+?Ct-FGXnWPkHbBwf zmH3;6G3u6(rWL0sFnBUWoti8Sd@xPtR*^!P*?Mn5CV}KM7BXPb-QtOmS6>#F!Y!to zbU=n!Rym`>XUM@7X1=>(&vCzoNgyvO)1rGQo-;Z^;|e?L9{1Y9nPc*{Z{{E+deh(% zvA3eP*i@xEy>w)>ds!cE3LQ<=mlqoU%f^n48$g%GD@So_=R(T9y0ozL=(H3_pC;v? zG+ipAlCxi#V=fk~Be7@0vraeFS0Wb6wQ}R4AFcsy?7d(rr9k&EO|@Yk zPlHKSN~upO2yTmK6{)WY1F6SrZIFDtSKB34?F?4hi%(&D!%D$iwe*sEyUl>L6MjlR zEug-IV0#<$wr&E`J&&g0S8ceuYA=v1o2Fksj5_d;BN>#-c}ll$DMQz+v~g*SyFyXQ zH6PzKtL+l^&lHv0sdMZIh#xD*EbQ%CdHdIg)#t-QzDGjK^CYvdF?#U!-2 zFvie&hBKIhx3RypgVr_Lq{9Od=v(>g1;{;-@|Bf$hku8gYXqprUy+^-jP>IcS}-2F zP($kD7<#(V8EhODk8kKEU*4nXOJJ_g>etB8!SMvn(X#w)O#72Dvh&an&-D&#KmZ#GSOfJ-T4 zPVjpAxup|2Ael16I!DcVKA_K#2&MH#v)lX~1?0V#cUQ!2+LspSn!Bc30)c2aJ~Q^O z=%V`re#rxK-7d3nchPvK+(Gd$b8siP*}Tt_5ZY=E1OtqXc{3P{4@bwP)4X+}M*xw> z{^IHg!K_#L>l-D|!GxTVmeQE>+c`nG&JZ9to8Ay`YL9qwnU=TXEbePCiq+3KHqs*k z_1gvEgo7w+%cp^(<=-SQyq0`?YSt|R9tiiWSc00^E=O0?ijTtpt+wR2ORj3er+;q3 zBO0qld0xFE2=6gbS>jK@O$FO#gOf@)hEfj+0!O5a1W&*RzdXbE3FizXtdj%))LlBQ zub%{KvC2?)u^|PULHXq44q{ExC-Gwlp12QEt_t~@K&R!X-vAdSsi#xY124?9 z;HRp5DH?;^R$xBW>Lwbr*gyvhJmQx z#O7NNexiK-N5okZTLjR$D%#~e17eWf0bKie z^pMf?Wk64qR&1GbLCu*>xoGMI!)Xb&ebv5d`7sg1nDp%%Iv9Q9ZE;X0ub0s{EAr#T z`zBzJ9bb_mf5Ep1}HPZb`eLB?ikcJ zQjdCcLOzgl1Vmfdy<-BptOK7ibQjzdlt+F>7!ClhHjof;LHWj&fD>6jPr12EZxVBy zpi=3%GTeRa(Wwmu&brHS?@gKNqk*dcU9tgky)@93Gvhu`&`$&Q(l=~O zUJkS0YK)&=f942O&>oMfmd(m4WnX`G)U#`es*+t_cBHdOSrsyW^|9C}+Gl@?UueF1 znQCBTHpj3PY>h7mEj!KdF&_idb)LvfaT2q{#rQVkWM`ann_j2j&#rmM`uwDFdUR?WfYEG(((~WVGYBS4Z)n8l+ z!O;WBxW*i08)(gedhc95KgnO!Ot}UfJP7>93)28HS*iv^p-l%HLxGI7OXgb+FsTDj z=IRfb@P;r%+L;NdtwjGCToo_WL8|raAn;OS26~D$WzVRJBw%ENJ9uBC;4RVg&151EuYOk>c1ecJgf_!Ze@zqk}iy&lLdC-G-Q2qKGMux9b(7=PrQpd&O zMCV#UZq;e&K>(r$MidZqo}qVc>tcim2nq^yS>3nIYfJ zhxQ06VKVuH4G#=}T=exPCv8@)7=mXXvuoXiRE(7OrZeaWtsaxSm+li^C}Se?fQC>f{bx^n2rwoDYk zN`H!>SYf!lc}M#aDVfRq?wWB(X$;7>?r6>J{B14iXRq1cxoIPK*+#^`e3t*#uVAXN|>h*cVfl9*eGtO)hlS;GoY;7%|Y8E6@&avdj{$+5XJ_b zguIFoaRSoTv|LD^>f70}ZG;T^Ml|(@`W1~#Fz*zKJQ;HsOLZ008{cKN3G0^Kal<)&99SssAb~5y~*myM$Hjzg4T^5^^fs`)uo7w)`5{E(Z4dP!!zaL zgjXgX{iI-~qJO>6jKxJg5~d!_$DhIYw*D3Lr2*a!ZVtU@>@O;_K(uZK)l#-lz7aJ=4u0&@OLNH6C(zA_NIl_v`~lK}0E$fZ3$Y7Jjfy5Qq#)BgOhm%3o@RQd``&J+~D;JnNTL zQm^pTWA2kd0GwB1N$B-UEk8vs>z7=b7otvhu3v(w_>}xo%th;qi5=212vMwKw~R!n zKa$A_SOJ`0s@dRo9?DB?RJOPm?W>cdS5fCTsEc|tlHgKKmW1Ncxfw*-xp6-}UA`HB zTbHX6`!$z(UP0Y|r8N=U4JoG^ER}%XL8C*Mkotuy9op991q+OJcq{WuI`tGPb#2p2 z#_+XZY>TW8tQ*ltmWy!cqC(s*(Qq;C6Q!EKV1;D|Ls%Ub9xUM{L=UF0!t;2fcj`7+ zUt3=IW-tIIhO+)pP2Sg1TpO<@Dc5Zeo1b4R&#E*tm|`r=0z2&X)Sh3*4f zG_B>O{Urka1GL-SzJ@XUaFrxaNmplO1evF}9;a%pl_XyRd~bO@uFU23l9EkwrlUWw zgyjKB+Ha7v9KB%mnHQMN%;0?HU4(MxCdFrV5`1Qynv;j_oj;P6c5kHQr5lQ8E&99< zl0j}*|KTNn^TwN-vRfQ{gX+S9cqwUUJ|8B zlV1m{Y*!HeL+r0@UZ1qmq#3>**RyVYp?>F@vn7w>lKz07t%aHY_6N{bzn}7K$|bVu zk|}$0X)&wvUR$~&1|@6p9(62!`LWoY6@IY4G>FmGYhj4b=fm6_B)@XO_2P|BDaSY8 z+x5#a7xN9#0WOLgq=RRP{JQC&UyLeCavkJow&Xg<;H0E`>ffMk$(D#tKU1~ zWGRQ^6)CA{V)*^W2_c>E@EBL1Bw3%_d@+39_U|%*f9-BIXdVV|{DHXfvIlJ5yWJn! zk5BZZt0r4iextMwtf7RPH#O^t{}2NXazmkKD?q;4S$>$3Pg{ULouE23?}3d%*S!%} zlQA3FgP!1ViORn5JRBJ6OnwiG_doy_(pbFHY^wn8NkG?SBQ>a3L4vAEy?Sp&M(C}y zRwC`Ub=dTR_uX?CZD`#cb6jfnLbb6(3 ze3U*|YS@%M=yaCBm2ioibo6rKR+4ia6oyJc)dO(!`UUZABQH4XpzPepm>d2NqMAc( zBZ0^(HYPV|gMkh@MBkuu7&xBJWMy$ObTAgDwROw8rs>-hpa0ebU5dk$RqQk`g5PD|G|if} zAfM7YdiIy;!@E-T-_@?ncJ6(L(8n@d2~amN^=gq#2^&do4M*TbwByV+$I&;8)_mm;^uXhyhwXyk!x_%dbgH4Nr*w>JZ*fb*aF_(LZ zcyS8a5rEVod%rH{Hd;FzJ68|zu5!KhL_C49yS^IPyMI#xsGD=qq5l6brY%E?`0WE6 z>}ynn-`QItFt&%gh$q*vb8FQ(xi`r=+^Wz@=>?fkL0;s9g?`f@YraA zOv&#x!A>Ch7h(56+!WS#=Qa6N8bj39O$dx_6`SY@a0Eb*Ve@uj(Ac*`p`QE1Uzr4E?X%sVvLUGJbs0T12lR5d#w0 zXfBbM8mS-=w89om==^3$n5hgRmH8xP3rcwHgD7l(q2ZyS3;0Un^(4& z`XBqE{H7<7cep68>xlLa&|q-kSbA1DEDrisQz+r!GD-AiICNxTo6J-~zfixy zO7NviYpNXEwFHMoS0nW_u}p|QOZd0?oV4}1^3<8B-98?c-=7ElRy4Y9*X#i*>RKzD zzPQ%n3dnI~T^kgbe6^K$a)C!Jm)tP2S}-lGlJXkKgwMy~!P@QiEK%;tN+%T53d0R? z;&XD#sw?#ZkN7L}p0J+$9+6g1n zwL@v&Z+olonz9nViwDd=$0R4l$r8KA9=db4{u(6ycvABWr69g&u%t5rrNG#8a)n(Y(bD2x)uK*?o5Esy7YXz)J8~ zcA9rz^sgRWt^f6qgkK?KJ&VGp^IDH>*4t+Pwww+vi`mDOG+WJV z!t^VRtixX>&J(#9vfXOhs~WlZ7M#)*InyhOlYna|SaBlnyFXW3UK3bOEc+pu^%ZJYe!m9>xnN#?ao(R4+G zRMYh=lwK*z`bp(v^XhDWBT4(WY!1Uc)=DVUzrQ;UCG(S2 z>^0^mLuaelvH0(GCAmJ!ll8d2ab+>aMhe+`+Fx4J0j*DpMb@JpP^pZ%GU z{RdwC8UoN&P$#|9_PfyyXNjA|t-{UoKE{*%FAf8*^b>9w7?KAp?-!Y}lh*-wx!GGV z`oXLFh^^CZ+G|<}PXe!n9FiQbaxvYQ?>eTIzLRY=+o(IMIM2>#aa}*$jL#Guhy%)e zzESGb)cwA#-aE`slJyZ2cv4SLJKdJ}afW7Lo(Br0%K+!P2^6FEO1&H}F=! zqKD#d|27-ui(!RnU*VDcbCASWt+bb8sy3Eoxk4m9c69EnMNa5)`WH5P4Ey?5bh2+| zZ)9`G@KfDRo-W??@77fBWIraR(d2dSC+uEZ$oR|=j%t_-^=eiKh%C+Kfpd-?)|&`mke8DLeYIt*QyeR z*UAs(swQeYaF*sBpFDlx=LYjd`fEOT>uPyIZ$$@f`48CYHb1%F4_n~`ImBOIFO;Km zESU~Tiu{JS{Fe)+--kh&pmIkEEy^*U|HgD{f~1euxcJTTlT)g4-wwUwQ+KVQzQz7I zK8yp(z9Z1RY8loNaUTa;<#AMB!1G|{r2f0C96{CU{PxIp>Qwz2fyYhtFB3mus+mZ; zH@p)4TFp*aNh-!{gaXCyuD)#OXD=i=#7Ra!7rUm(0aA{BYuZb03el?#JHuXGfw#=e zHvIHQ{L;@dvgM==U1P4+Ph*$jEodlLdOuvh1PyW{u#;|VyC4<;JZruG`04m9lt~rZ zw~=rd@P=n`F2(*bN9vZFbO3>7+P47Kq=QboJOEQ$=@qG0V2gq1@D{(|N<8b_|Y^dA@4+vi3 zVTJ8s>_Jcy8Ki^(4S?Ni7*gcp`Drb2ckMlC<&hPqi8moUhaem}yhrlZ2Cn?M*XM%G z;L+t7-;zXwy{mcTku!nj2GYR!XP6lWq3X!G_w;|*)%XO+J+R3Fn4jKTEh`aj)(;E4 zz_k{T`h6~I!QwT`&q5yH<*)U|hmXGkW98+3!XAA0Ne>{jv6f^s$+<3v6e+OAp*Z`M zOqX*MN#k+BcH_e@acbXNwFZihzuAQB85Y}<4&p>3OQ@OaiHCEPdCb-4GFTKd5VqaY+zrG6j+!<;Y2}4f1Qv_G zfLq00Z zPg88(#J5`7^k3gfNMetIBNCD5+*j@}?R0(-`iXesUYZvn({8#dQCbp7%M^s=FpBOq z*D6-;Urvd5jYR9Lt=PJkL^t5qfS_N@K3cO>+}>*m$qaMty^xnZfv@^sR3bOmeeVDr zSpp&ceQz~y(EhD&i1f=rVe$J`16kZi1c%m*maQ^v@3UH6iw}&JGbZ=~PaK4c<1;-V z+P)o9Fcr5K!gA4E?3bABOC99n+LKdE-)L`>iLkY`**g~0CTZe2h09*3v9dwWOBq6Y z6-zQ`+5+X6)@)qmP7ywv;fPy|&rC%q%BQws7K`(Nxp5%UW5Ry1KBVnPv~L+42N9BU zjhD7g1w|)ggH`}I3$%OX`$mld7u4A*ioMo2E#m@QwU@Tg zOneh{O+qGzD{h45K1t>xnEYc&2Jr-@iY zlEYFGIJnO+8$9($;KoGf0Vj4g9G%Tbw2eTD(CKmy^k82lfttHKhr{tyuu1 zES2u{Y)&uBe2+8WSzRbosWKi^HId1;gb~XEYG%eVHO5y%H!08tA~A~e{Qe3jA@YIl zjhoSNN7DVY zEjUk6XFfpu-wR)vV_cu41rrU4VYAuHcO+-ri`s!Y4d-a*p{+)Y z4Mc4nP|&Uyy+54K=+ec@)_6xk>2SZAo9DFkw~g@?adeqU2-D*CT?pb&j|Y%HO&(y* zbU6p+X>+Y_^5Sf%gSMjVDK#=JA^dByyHVcJ7Q}^kTwo74EW}m$;*=&`mTL)-LK7aZ zq?HNq90ms$(46*UeAFM*sNljp56*poo)d<~Y@OKujJlgp3)>$C@3lG)gmTG(ha)PrS>wKhyyt=bNP*EFguNWTM;Ckt+c9qmYGT?FFK~gKe7OjFR3`5P=_HWO zk8UxC#&zrQp$_um%#CnR*x zbqqS30!INhPct)@!|Ak+!|&qug>LnLeB!4yrs!6u=z}{i;})}OtKiFVaeWJrcYcmf zP;>!KxGN0h?TvJuIZ)RZNJ2tG^H*v4%c*?wNj(B2h9O_Ak?L1R8|QLYtt2NcX?3uZ zG6kX@-Z%B<>82LLhK-Px%@%m*n=ld~sK2J`XnC8dBmCErTg}cA3*;#eR3Frka z4|$ps|C0-=&fiO2tUimKXZt(WUY+0aj`D8k zNan(Cp~g?;h2Nfc=PgBG5DAt!hl z2+5QihiAa}%R>0+>-6TKCZPV?MCH@O?W6xl{2sNez;q`wr))8Iv@NHORWBzM zA8Lx9AE+Ydmp+`tkQ5l+$0c_*-6s^XnhqQtmP90&O$E$3LFMEb?upRvd!X^7Jo(@Q zj!~=$1HcB8SS?}){(OIw-qtt*GyTObW8$?61UApIYB~7@gx{xW0i4rnP!lJ{=)#8C zuR+Y%iq5X6$X!L=W1M$GP$Ch*ZyQcdheC%q1{x5p(uf~=z@x#o6<)Mkq;S8X?DlO= z=%deC;QeL4xWR!H99EB8=e7{+VQko^zd1aTGRmKF0HH$@Zo`V&W$VmuM(?MQVUAmn z{!g!@IZK@_;LZFeY&bb}7EuyDmZpt`N0I)Zni zb~g2BO^0P{GY?ri_j<-lu>m<*TW6m*GbOcQs5l|vEE3d#T1^vbrU1FWf(&-?dPbS- zdio-UArE7-tX;ckQ2j9DzuUU^PNw_rC-=)&{#3eflPhQr$766{1%__fjqp#)e31~* z&#l%w<$oJ?c~&Mx>>6caq~Q2NZ+~to^_?R*qF<6c#q+k^nd2mm`D@7-yHhz);8ZBi$$lWcT! zV<>^b$|$}@`qW{RL@~HAriPs*+j`=#Y5-U@-%Y|khnQ!XJfJb9N%62x2Lw2(YE*`j zQXy+({#d;_>Nmc&tdS#Uc}w9Hhk$5<( z%XkvQ0n4*KI2cb@06SNG+F6c{<(F-axw-oD=g4rnP1YZ(!YX$enXz!kS0p!9AmnRP zJp4;0BkE8LxEpp_<1Q;LJ&=KVZ88f5<|$f9|mf&JUy+(J}P?5-umX zG0!q}FU9>9kVjPs{bgp_O73zTPs8?)bOAMlm0ek}2C|h$@(7;2{P0Y4Y3 za6LrZ`M;G!zrI#EwXQ7TthLJi4AowSFgP|NBrQ8g{om1P!UQNkeWAF#pn=B*I8xy%j#6kpLLum}lGn*18LGPx{;i>qzUrTw^(`7z`LnaSz~@IF)Uq}X z-C}71e?{%|&n4Akk!_2dHr#c)fN5F-?#)&1;~tJaOETtM z@2a>nThx6<=s{c@R9?t1U2Fg8U_#kv2mHq?NGX(DfJvcorr8fBv z{gp&k{mf*}&L+tr>21uvV-Wl|RuYHTIWEq7OE2}FShNV0Kf;HE#u?D~cC%OyI`IuT z!VketNEKuXPxXILNlDl>M;k-C(F{Jqe?Vb_EOML<(gl=^2Q`+F7wUMqWaiIkFtQPs6;^Iv>?c;&uBL+pBxjkEW*lXp1u+pyn1#&{K z@vyEdkp-#yJu-@w`%aRfOxj%iV1th^Z3)MOLR6-b@oqQt)Md$V*_{ zGDB}Nydw>KBmFn31|uKbV=j{KP`t8>IWZ;AfC%#{E#|9+tHmO^CbwC4O39UV&j$t@ zQl}6?eK77uZqi7hLPV257W zl626Q*`)BbKUNnkU6F1ESsf0Uz~Wd#HoJ>yDI4P=B%-C}d9&jNKN)hdOJLD{HsAJc z$16!h0n}z0XUo|1viU$s~=C@?vk!8Q1xCb6&Xnpkp1Myt%g@h;^p_ zY9cShoTubC1JB;l)jNPdlp=$*y}P#vS1v%e8f|o}Hy<<-{0pQks&fI8=Ou55yD;39 zi4w7>Uvb>{9@nc3hHw3@@<4)hYXI7>evJH!r!a_1E;vqQfO%Gt($Mty2)%h{fKl;+ zH7!eZ0N(&4T7n7iS)2mNii!zRA;E=qlhljh?wzafwLAR+_=$Lc=HQTkrL^b%;&i`l z23vt-mglLHaLPN-GeT-xJw+a*rz(n&IpI%t2l?D>oCdNHxP`C67~P_r*LKk+OAufH_Thtp3>o=@x5@0C&STH z&-B1lbp=aOJHU8fwRVv}K>q)y{++{;+sH@Q%ran(h4`*_Ah5~@@zk7ni2C1VJQcpM zK96IqU1Wf&MtN_e`uG`6UkMJ=V9&$UsWyMZsQ3v{4zjVbaGG0Sn8kYKG9>WM!hG~p zZxND@6B~J1O}SV*A%^UD;yiTJ8IBQvAqUYj57}B|b3xXFE_HdS>DzQ5iz0Nx=JG)> zur9VhMip)!!fs-X$eMTMlNpC~B1if!!>2}QWoFJ7aFsR}q%YW|%{zL6d@ep+5w8qBO~KDeq$Zd*K7<##gBBwElz=WHJlI=l-Fps)S^Bm4 zrk^B5y3v|Zi??sR509lB1Y|h}Aez3C^i$aJuWEOOaagge~o3Lv2cTTov82}zBRIcFR|+KKNq?Za(t!4=iiC$ zz^K+b6{n=a4!r^cRz+|099*pZw48T%;V1u@2GT3IH2HpovixCyo9|fsliI~_-NB6@ z0&$d`9>;F- z!F;?&lzHl8XH%pm&+`oQ(hk+FL*KB)}YK<~_`prCu^a&JKHw$#Vpy!vflc^0SVLiWTUF~7M@HqXnggny>2lIp}9wBM7oX;3E5+y12Gl1C;_|D$SUU9P_3^@I1D6UZS2i zyjv$Fh5NY6{)$*~%}XNK<2{16`~GiE6XRb#H!uDZ7@<9RsP|3z9Rqk)#j263`7>e)^&agqL9ZJ9~KV!ki=I(RyZG}VJP3(z>PFw*ezlX_~Eh@61g=yts~j-Bd(c*UlK z&ho`m!u7}VTIU?~TCN#Z6?E_1*sf}6J@;kj`nKUOo(q#Z+#XorjxL-m^79Jj+Jo*P z@)2R0NmY_G9td-12VwTd{aY{l9|e-0gBhd7h*!F!_NDzt#nQ52+^GFvnRzLs#kP*W zM+^B`iSkDi9$n~tbk#vu0j zn^llo^1;jJwoP2JMEb4DP*!TF-|DK@2cGi`F?HW=esGWxa?2)VfIM8wanpgY_zc7I zLBfQ#tzzSX?3-6LLl|H|gS83F$ab01=HTs|Cw%lxZAj$E_< zx?M(x#^hXK@bdi=G_zs5<1F|M_mO~kkJpn2!Yz>E%)~sp28yShqRif!nYS4i-{qgS zrU;Ls-t!A83ZRxH5|0@7^9^(H4-mm}s4v?$P?-1hK#gsh3M{=1`etvP{j8=E8Ze50 zZ!GLB+Evhi9hTh+mK5UR|0+C?ZpIEfMx7MPCWBN1S^+%)-Us(*B|IY_F&9w^5G9EJ z-Qx4*dvEC1P${6l_(+Odw!((J<~x)O}KW;n3+8*N_~Dt&NKO`CYr!AF(oF zH?)||N3z=XDOMXCY@|3Qwq|sF1l$0pKZ@#MK-PdMtcdQn_o-3|X_GjfP<*DH8zyt7 z+SAaR0zzEqqj-53k4`cM1DNqf6J09E6zq{KP-7^p-V0Bw%difKYbR9!Psq`B){gFQWqCC&Q&jYDA%oWy)n*u!C{IUR+JpVD*VjRQ z5k!d(TSmPhG((jIu2&SI}Q1bMZO=D4N|hv1wD$o0sq zQu`zKfZ59yycOr5-pTm42UL&k3Nng~jXH1cLTs+7_S>^l*<87`b0P;|mMXI;wkn3< zS*yxPmc7P6Uzx`|R!SMqXMe^1Wu>C>admUL3E?M(SjdA^=(*JAc6ST1@TV-Ua z58B{N;_~>>CYvNNx2!Wcc}+^qU0^Z6P(1DQV)i-UO(hisu?jsXUtD3BN}XNUh>_;^ zBcNTw{9%`fb1{D+4$_HN>%S%2XpLWs>g;0`M#qd`%RYu*t~~$czG-E-_-He^r##SU56? zg=<#K=9<;aGzuXtbXqs6YH%MX{>j%2rfXLo7P-|>0`67zAO<*?VVyW?OOFAY%|`AyRpH-=5iy7<+h@atXUzIuV9gZa3VEnNoFK3bZ7Z4s>aiM&v3wuv+ybRmf{AbFSwumJj ze8{}FY>5q&EMd{wE?oi9=u1{KsX-;!p6T=2kBb~jYb~((2YmZp%Eik5{li?UI6-{~ z43T5_n{b7)0~1RfmzRww@ngCJCXBnwLhp$B--$t9^tm+*8W@(xI3}0iD>}qj4+cI` z52X&&ynGbSfN(z(BPd+56a%s1i``#uJdjw}?q?5Cck>kpBs#Mwb4@?Q$=;x~^gl4k70T z%1#`7I!4m(DZtiGS(xw=+V=0#N)lMIJcrX&iX)Hdt?w_OD-^0S^*rL^?ZSI|s!n-r z7yRKrp8d(^A&zLRy<92dL7prbaU)>mPAmjn9+;@@KvdHKZo`!uy=dZGzKxW>+~|jf z`8>qsMYE4THC_qE3%2%U2Kc8PBpdUMuZ?lYei(yqY)~Zbv{|g2h99bk zJ4rinoKGYXql^ID&4xIUzc1kt=wM)3^cy>Ikn}+{iie(U%zg2YZ#=BxR7H&(iR}Zc z@gm(G1o3c1@W2Tl2+vsye}7v;8ABOY&^fon+@?Mn;8KY-5e5Fj8*!-nG<)V;~eV2Zl2k8_55T0KZDYU)#kZ zUST0_?AklB317<%#)0w| z(;)&lIQV3*Wm=f`cntw%ULef?<#37{c?K__M9qX%!vq$(VVi2kLJRfsWF*`~Il;vc zK@I=(UN}H8o;0~pDtpg;J7rva5z2h8)5rIdilPbcT^2?YOhn^tya(~>Z|DfUG8b(2 z`W8XGH{e%_+W3V^mbZjc3X|bLv`syK)w~1L`(@b?pW@NGiB0*i5akCXAbMxu>2NHJ zMCHxqliyK6R%LjPTB#av)bNy1D@hfN_%*}S5#O~hkKuiaTYTbuNSH!8H`=w`fj-5S zC>z^rrBY1UpwTp!lu}*<;%ms^W_yH9sPw6yNKwwl--Raax%i460=P&q%sVqD%GSsR zbYDJMx4PgbP7pd4#f;zw&EV5@8Ty)M>iv2zoH^ggxk!@4J8!CwN@d;g7oUW}a~K?2 zBl@tq=NSj@{PQ~$9WNm$q-AO#kv!J`K|sF0y{iaxDc3NV2b>jv!tM7AUZw<`$j4|b zdN7{$+c>dSz~Oj$CJ=0)j8Uz;aP`t^Af8?_x?ZHkrOY(ya*;Yuk{@Tvzm}}FH>J?F zB^~6`+7WFO0U2k=ojIuNa6HYpGgRKq&wW0}OxNsDqe+2^S~PYvbYL6JuJglI>-IdoSXi(ra8%^w zM(YRhH28uUIAPA#!h^$PCZx>^JFFAj`)^(7(No0pCK}=`4N2M-f%%OqRL0{ic%MF# z3q@D0uCJT4mz1XiiQ%qo8n$-@Bg6Oo7A7Yg5xC_9q`j-wBr7PzfU#O0jF{*znlhAWuP5(*vQ0QxG*u4B8Q3C!@yzLN0&s=q;k3rSU^jPi4ui+q zx^E1ds91A^dnm&kq$NQ1JY&EF7n1*_+!Lk?4td)AV)-*j0(klHo}A*DFZOL6C2#AV zcoQ@hkiX-lTRAQKNpy2qD1;-o3O=09$#EewqhWf>L$KUOw8NrvdkHd82pgSbX~p&| z2=Y698lk9l?AXD1g)fa9&s?)J?b*}$2U~iM^Kthl-N*grFS1%Nk7A)Mi^w%H#)~yP z?;a^fnr?@+H{#z{ic*LCkV|>45NoijM=+AuLyYm=96|a{!Ud6^>Joj?!GC2l$XUk7QM(d2Ygk zwXW{^>DPCJr$rc5*8yjYy-w)*`u zsm*ZQpVJ3k^wFwk*mSMl8YEgg7;6iVNtU%7x=qd) zTspIUbJz4@SFY6L6Su|Ga4 z@k4DO06%al_ZD`BmW=m?ZHsP$8pjJyeATW>_Vjo+>;`cvAHA;<;+NTN1m}UXrSbcz=4~)im@pM#<*6;xEObE zKNyL?=`P^-l$5wg@3fj3Sd2FzNMi)Q()g*Ngiy;~bTZ!=t&wD&1Z<%@^HaOK59iCh zyWbkS7D#WMIjT6VwE@?=KW=^65PnJS)L!mR`0FNK>WUvhYL@t*&UC#uv?#9?b1*tG z4)aXE8*;`ZK|2Ei_*cOx06y2n@wjaPC|kK~&yxd>W&WGs3wI*+e02oR%6UwDAE1|y zg3yT(ovAT+^93*G{`>?n?617|Qm6=qcL86E3TED9ohJ%ZJTZeo)RzwvdyDVeltIE3 z3mZe$*$fEZWo5HkL0MZX3UR*l3X9?zPcH}@729CgzLNpO!M2-qU)epzRfDvrq%&W- z!#Yz$*I6M5MB?_YxEss%!~w7^FLT0Y8JBpF$NAv`XkDc!Cp?Id7- zdnU;N=sPy4BF|TT5<;!bB{2I;1mWh%Wy^V&6j2UhlValc9ln`X?NP$1pLpF06{5*_e)m%zI$CHk<_MhIdZkExG#{}eHFJ>6tP3M2zC?7R zb0o9KJ#~sb0C4BKoXc{%Oi)6fw|~nDG^goeNJ9nrgge(nkio<^G(G*rk0T@Oi60 zdO*@!8b5L@kq=XRfjJzKf@4x7vxM)&HfuN$B31Xp!edJ|aAb~55g)OcJT_zDcrhLM z_|3FYR{UMjS+bX{3&~^uid#aik!iQEj_2A1`??$`4m}|NV7XgE6N}e`MD-%O77z6;I0BN}NT8%RXy#ixbtr;$C6FuxNy z2KAt0g4Mb|Vl(vy&crMmtC?mzU&Rh(L?n^9!V4f8_*X(WigS1DR>OV9_&DgRP4ira zCd82L`%dS(`d9KL5hz3jL!B80aK8XY=@UK5paKiM?7UdjxQzOj;@gBit9EozWAnZwzwi3>PF0Y z+m~b78*T1k;f+e9O&<=<=YMa?%P09c3v@4xzKPSKm7oZ1?Wyp$DXtM=^Pl<(E84;yoAsyLxU$smI+67A(cO*B&BqPNqR znn=wG)apE%m-;u|HO_M?y5Qsq=3R^O2oyZ><0|)~u(E3Xt00n<8nc=3<+X4uShhMW zsrUp>fjm$!){3H|7GU$0_t}Ny>sO4b)|fKD{UxhQJs{>{|tC+rgrjTb8&AlwL?{WVz&%ps&KJyR)DLofNvIb z>2Eij&ZZaJO)vRp_TsiXwZAX5GkW z;Z7p-dDa4)%FdXTv>(JLw&S-J(ylGitp2{m;C}a>yT}iRMp&l>|MEz^rbDR(1uy&J zyxYI-dD~iS;u&>riRdTVI9~@(7x6@kuvmGWYB-x>58T`;7Qw-Ar@`j1*`z*a7vmKv zRHl4Ej)GVet-NJ8?@fC` zLM(&ktJ~zl#M1mB0j?P{>^dq`B#&+JZZe?t+RA_PtqNi32+QvB_K?7MUdSNhMRX>u z8sBmuWZZLuQ6GyAi;v`wUU4s>d$on9V|OF_QEV75q2~kZ(nr2g%!R~OO_grR(;?K9 zSHFM&C^_Y}yG(`39DPbl?ppD<>6Gj+^cd84C7T>-%!qG$p1I`n|QSD;6PsO~a-D#Nx?FT_i#^|nNq*fx%G)Jhcq4Avq z`!DLq8Tk@HNGset*PwQKiZ3z#z3@p1F&ZfmhjzhH^f^2>WO-VLAVsEknmt^OCD9J3 z-Y6(O?L)5?KK;W7slxgPmqI5pGzA@QLZfalQ%Z+}Kq?`UcuJR>Jj)zH>PW#xAz~vZxZe* zY0iCL+oek^sR5fU%%BePCcg73t*ays7Siy&WGJ1WxJc&UF;iThvM@Z7ozUTeu@uho ze1(H#QxcuKrJjmdGb&iN(Wb-Mll$DItAQ0oAsiXy4)7qq}%joYZep0 ze3lgxO{3DP+zV16CNFgHMSnT(n>#$Use_d1U?7Lev7J2Tm3^C5*&?E>xNu`oWl93j zRB}R&zY9iQ(BY98VTZ@$1s>k!g&uQ2&6sE?Qutv3c=p8G`ZvwSWn7A$T~FTV8LwzT z4$=9+p<0~NUfXan$>Ba(yw}(YYA`$DjNx7}mkbVfigxUp=Q&V0O{WTvmGcW|= z6i6vD%tr{DI5`I~7jr~b0-w5EDTc)#n$22{L(Ls zUgpofKYM>T0$eswL0L6o9?7C;1uD90C)ju6)-CF$32pRo-*V2WGGELH12ugr@-@Fr zKDv_$vOV${T^O9dFRNF-GoXsvb-(MI%wAi9>5OI!{l?USyqsvzC=-h7t z;GIt`1wozolE6JKR+5&Fii8q+ojeVu=A9+O6R84&kgM_76dUAiMj>5|BU)a_1M-qK z9z?_u^)&HZQ5p!1Gx9sd0(s-n>5~f3w4(e#gsWzhJS4#^7YVUyJORKAzvS`xstde# zT$+#HTzyprRpcEu69ni*`RwEC&xX`}E$&CfsNT-3+xJkFt32}_Rsf4&tdc<^6(R9M zj6@s*8Dbg?0bS*32F6&Sw08)iAd|WvIHdPRnBB)AqFNBg%!l(jW=irnW^N^Kcp2lD{Ku%;y0m*q%!ABcdi-$LLqcF?X=KcahoXj4pB{I~ zLsvb|7mMCjs=EFs%H8(HZ8ec*i$!O3OdO30gz}}(Kl!%%aLq3gS}bN~`>a*wUE?e{ z3je)T?VQY(JWMx|X?WlPRX!Xz!Vm>Y%)wxjKZ*B-cf%95<49n4FaTo^Y=gDa{3CXO zd>#@7LFOY7m^71sv0uf?PvSNU;|<5zqKlSr1&qGdlRp^=zIZ98U8qt50mIrb9@>uu zoTp^lD}WM4eXG->#qo%u%X>Np(VXPG0Omy zm50Xg^vo&+{4p#x7>spC}?1sX`n+QLgYUGP(F^*S^;ixK&VfdWv3d5pL zX_>zjA7C}I$k8TAfRAVIbv+M?)I1wQIsD8|w&LQ3hQBwtW**0J1WiW8H&z~_3lpg*epGX3P_`#mw zKXYSLTo6pOM2UdThotF*fc%M#y&6276Fr3`#@YH@lNR=y)(B12FR&LLamliHbfIMc z`{+CvXsRRxWzC;2fio`0Udr3@`V|hFwUgE~-j7 z4lYYRJ)lLavyj%WJU^Q42IN!_$o4^-AaX%O6yU z+>esMvWmG-xF$eTc`mYu$}ExPjl_suNM56bWKsEp|H?dP5^$Q(Myg|LSOKVj{pfub z!PsSh_OOVBz%HDOUW3CQ$~{}H$Jwd?=HV>w`WGi~>)lo01wQ}Rcu;K&*k6yj!H@ux z755YK5tdCMi+kR$1MJuI9Dl+av~?T76W$^KN?Nf)NbMJnE5efTN%4DF^Pnz-K1ERn z)lwPPjyRmXGH{sOVYSSOfFg7JDO`9Oqd6E>4PTC|u^uLvr?;pk4}J3-e`o_!Sl6k} zNP_!)4z=n<^Xtpz`Ri{9$x;s_17;_WSq@R&GO*0rN00ZIA|m^*I{)khJVxGbhQu3V zlXLR9Mg)X5=SVs}M>BKl#|2McO9na@X+T3dvPGOe{`3LJ=GkE7&S0ChRHg^}16l8P zkcKVY+$D+H3@AAwmkAl@91*D8P5^WJiIK0`1y<=ILUnl#zlH7@kS?n%$JHNKCIfLX ze|;zoK2R`;m= zAZA-_Ei7`B@IiOH$h<;3V!~DZcqUmk%4@)W6UmRc7UA!9$gJQG2rQRzLSPCz*lFJP zj=0F4r(JkAVfT?3-p^@Ap3PLflmw;&ljJ*f7G4*7MGVnIucY*F2#+coUE7Q73DFyf z-wVP$2&}{&5NTM!XM^Yud$3c}55&yMj$di{(%BI%^UIIq?`!JkkdsFMg%Nyyq}1M5 zmS6cLxFYQA@7d78kuO)L>|*jz!Wr}gbH?`^ol8K2w5h7Wq7LxgP2X#@F7Qq)bLsDW z59v7rlmDAvP=wf+r5AK(Ohn<;TPNo&aZ+B0G}AE!rs&kueF}$979Jy;VeoUs32`Ke zhA~(2kL#(O9%O^@k1|PqsFes*d1zo5eX>I{7*VArp2WsUZcH@$=hiPyY|l==I{^|C~7VR0o+JwLoTIvj5Dv=cf-fKd|{37 z^7@HTR_FE4->PI!IDog$ZF%nYfdh2AI1o=4c5q%QfMGR{kcLmIY|I9QSF~Xf8APFV z0iP8(=bIG38BXwa<>sGHAEqSPi3><(I|}iGsnE%q0==Qhj9bxP7sd+XzzTVv7FXAc z4ZNZOzhAf6A@>8NaSDi`r|XuN?+AfF+3`fF&Kd7Y_@_cr!XDSqN2#2K9;mOe0(vl} zwE}uTl~AmQro~TkNS?URPklN~pEl)PEQ65@tFIb|VvE&#Q`xQ&01`4wEzFIrjCJVY z+0oUwmvxGIaHgQUAs|0f%|dd(!IX96!D514IWZ@yZi|Whup>;G$u4*d`P8*O?G;R^ z3#^v>;ulYKM41d@3)WOIL?c9~DbYQQ2pXSyOZe;F^|L~?>m(!^q0RVMs`0vVm1FFM z`;rX+q?2ywZ7kt%IH>zpqeUehQsQcgc~WG$=jgVmITR}Vxp>u;dT<}uIFNi`+^gwJ zKLV0VHnz#v6S8Slk8S=ms9S|JWLMvX8RPJ5E4E~0mcC|3D$@7YY|ZX%C!I%dRZ6=2 z^c>6@OG>VVN=bh1cR^M5veE9z^crUKIs+W|8=|RyicT7`i0vmz$%)YCe$q}D5FeT# zl%RxXYAUVhr6^n21mKsX1WoIarp)&MxkSYQs;@cA$|Nf$zt%iT26~78#i@4eB>e>) zj%ftR-Fd^~MB^RJ2#Qb_$s+2|X(Z~2;=Dk-*2ipm6rCUYt!c%*oO;-Q5)WlFYW z7zMIBt0sk&nw9xaQ|U@fIYLckVYm5vunl#?sDFt-PE%}WO$d@Vz&Gd(o8*0m_$mS4 zJ_5y1l9S)aVOeQ+Ef}tqn(j?s)N^+E>nvKG zBJa`~SD0i_lI$iE`r#szlKdy4Z7ZpRcvR;5SzR2=-Od3zUYjYI z;K0R7@3i(h!2YYIL|C2Vs75myXv0n2(Jx0%I)p3r*&tb&nvH`vKga@+0>SJLF&NMK z&;!oO-=7WKzR;h_A$f+!e)_?5(|*zVjXshif%jpQk|Y(}Z5BF{8{9iQ$)Sf)B|G%+ zxJ=3R1In`|S;iS<{dnaVz{@BZf_4&;6v3oB;|IORxLY-M7O*$N;Rb$rS`wsE?qG(fuS8u?gMM%4bYw?6KNJ~~5T zQ*uBtPK25hA|rLCa6qbS>2O5+P=2n+FNI{}jQqlql{>Pjm*T|FB{?1qRn2K^$ML!e zsj9Yz_yzj70SBk#(DT1Cj>)GY@i;CvnwE2-n+sK5PrETUS26D5pv))SlL%}&#}8so z%A<4^dFp(KcbCfzZpw9#>q9ew08oFe1=Vv@Or-Nu%?8i)oD~b=L+ocszv&K=Ch6mC z?-m<3aa$HAoR)re`xh`vljsY5J;!Bo0|&o8TY|)CO!QVKIQceDafeaeRh1(JpbZ-wp=ihL%ceyaDPYKM?30$+rexHY9(B&kY{_#jJgE z5~DnaKe|!j2)7>Ap>52?D=Y#Q(lkK!10U$t>n{Ixg)&qv@f)$--QAy9AjXp%4u*7R z8i^d!8JG{>`K!)X^7Shl4bq?>I*QfL@{`S>b7hD&yFLeK+26m!37h1c>nInZ^*`M4 z(e2sg4_IS}>m06$5UeTwG_Z!J;EX#)8MeB+F5jzpk)Jp^54G>{wXWaCeRA)6Pu-rn z@xm=N(Y!{6bq1O6EIDU7BQHf|KTnOx4n=F_73OltK#Gj`@0afReY}H^;#-TI)nOs> zd)>kHAX>PyOs@O)966-EKl_+Sj*zmm#9;6a)f|u0<@rC0SiPs-xx#1Uy71wNFC&7` zU3BdG*q1ZSzY1C0GC@!in!wrbXE8i%{{u2cPI zjz|Y3DOfw3r2+Hz-}%kVLtaR6SpJYsc^th1m76tIpx)`N^7u!vs#1RbfD?y&w(&~C zziPbO7#GC_{130c#r6{~7%3J5k`G(sd=9;l7k>yP_Z!cqR1uoOuKdW(E9u^Tn??J; z;bRtwZB!BxJ$<H)Jz)}s5$qiVr_}zG^a#xb>BqC_ z+xH)76_sK^A~RH!Ju?{3d8%a=t;qvq=WehEy`L%HB1HbcvNVB`?;i|CpT38FO*vcE{e;94Gd|u_2+@up@Z|~XXy*6?D)G&RRk>diD6FQf zWd8b`S7e9g?v(kNd+Uivza#Edf^W>dnc>3xgG-_|+}pLL%N88WTNK_66z^%ht$p^* zxw#dx#bfbmyuP$a81+KgU;fxyvO6wCHp1ha)q>g5fmi4hBz~qWJ)bw+FDwG@AjBN} zo&TjZw2Z+K1G!>nB&0L(PuiNWR};*<1G0LbzqUP_;9F`CT*F_JJU04elbvZ#jYJ)J zi}}*;_^YLn#)3fAsh0r#EjDM`kOi-9rZ{5eS7)tP4A&AuI{REXB2)8`cf< z(_%WT_)eW2<>_@sX={qZtw5=?%|1#;j;l+i5;oYYLz5*ClGUylIZCRwzzc1r$Hb(U zZyPD5KyYsg5BP6`F5gGJGXcaLTO}o*_VyY@obVAcMBxuB+wdWcExJ5E=i2P|`KIJW zcx4D?eUvAf(cYxYDecGK=B*FUv=#1rN#;<<<&uNlc72p*wd~LOjjqEqt<3Zyj*fk^ zh39+wISHc=ar-}HXb>qQ{fv?{+Ommkvyak7{U4!_B#}6BNee6dbnYetsUr(L?JX*R zY!E1StzW5q#ShfdM;(X}29;zN=cSM=g<0>YP{VZoFWGBld-v8eC)6n^krdeskSrVX z-OX#bc10a+mZSPvATI6fq}1Uhl=2*5(*e%ts#?vbCleyrQBvu9Ecn)zvbo*49vlp| z8gl34i6hI<|DnkeeUGEacGY!HCIy~HKMH+qXuS4FyyCG8`_Hx6;inuouez5ENQA_p zs$ZE%nt3Y&eu*a?Fvp@RPu`*0S)MI;v}-j_><(i9Ylggt2{?2}D0V=OhXLvBL zfV@#r`Ism1yP+QY4y)A&X`{@GB#xQ&`r=rXq%WH>_3w|+Stlz-B|a{HEDO(TT`C@b z7D^JwgzTu=S>-SOQa?&8iL92=Ck$y$;muv;pLWwSF_pjY);cQ77%Sa%JBsHH2(f(* zG1L{M&x!KvwPO%RUp(qRdZ+6T!ylO_{1HAub;E3;(Yi7lE)Df}gMZ`@YTUEC zGjsa%a~jhsY4cAGTXFC*dS+RxG+@GHl%H2POh<9VJQMO3B2YC{J1#%3oBvP@`q4fi z#(3wu%L5oq}^6;fGM$T#!gVc zkQwKYDRUwhGG@*-Vdl(UC7<|=UGt|QtKgjFaqk4spbWz$YpQR7pML$!;nx?G z(R6-;xEDRPdqHn5LumH%jwo63dyZA+kO3~29@9L@;o?YlYU@(&mVOPe+O!BmORGzs znfM<+xo-Vijfn@e%gTmPnV}G@dQ>tF1UMU>G*7MFlysuDH)`s@_LT(%8U3(tiRu;LVK$j_)`{AP0YymnseLzQB)Kb5IboqLnN zjxN|PRJFNwRfH7y%%A!)M}7pwL;g;r@?KBsY;__n)Wbd!R&@^<>)n~%65%WAnV<=~ zTiPJ+ODt9!(kEiBI-~-@E#&MR?VQmeB!}Yl2I)i19dj-NGkZLfYF>V-`REXG+pbqA zUYW+nnyFY^Uh>wvozKsGQ!5QrpNrz$YY)ca`V>*N;a}r%4{Y&+WeU6&hlKhRuj|mF z48Z(4)#*F5oRZD!1NRBn?oYnDVNp=vT(Xpojys+TbLIWTi&P<|27Z|B7po5QEgicy zi`f-;HXtwm`yv35tXb=3Cz^`$lACohw}zr5Zu)||QRzY+6H`ITUi!&@I-QyJY?HrCMQTUJ^f_Gg}JKhZWUd4iyQTXoztcX6p( z7_3RHS9l}`WTt!nz5+i+wcvEWpQ6;W~&LWi7%DVn0#0P_kj{(pxQxbvAGrj3l^q z_FPF_+^@Gqk~m;n7aMBwOGRrW`nHAmSLZNyOwpqHVb}}1|4%n`*n7H1`a$?lPRmb+ z7Jbj;xM4@l(%rVzuo*IwJ~e+7l_wLBtSfBC>GUc=PlgC`Zl+?hc-x=i^t_`s6t-yq z>P`iOgQDw4oE}jaV64_%`-0fDX+@8!$R60{+VkV8v~RR}X5b8??;E3$F!`F>xhm668g(9%be;%=z?Uxx8umgHrYNBHIx_f zGLwX{yRmd5&-lTw(4YekFmL0QtOJdL-eO-9`w~R~v#kHvTc}FR6H0 zOp@g2+R%)$F?jns0366F0j_LGGbT80$u76MTdP*eO+f<&Rv*wJk}~S2jJv}%aD(hx(=b?L6h4AV-bp`^u6;7Is0tEZj8r9& zKzH?s3s$LTAZ#p|<@KMx`<=f&=bhV`I@B9f)W4UPLwxruGaBFrUlME8;XjvOuBLkX z3)yc9(LNb(YAhw2Q#pqtlSXRXK8A*@l*Y>pf`iAJYBMgroROxi%=R$7PlLdspMiEt zZJ8O1nUW7k;tZv~&+AB=JQFM{tdE@ngy>Oy2!v4?+y1bBrG}^E@AUj7p0@;_zI;;2bR^-_2{y7Rrn`nd zH5>W2qmUs(?{Y$h3mp4WdH-b<;c>3S`F->v$tj=Q$;oQz`*Z}6BDeD(;Q>SL1ucyEz?@TC21r1L@i+X4xSZ>M>I<-U%TaM zL!zwb{OigP)lX`A4fc^gLkvj8JOpYH57zdUty^O?p8hz?nES)S#Q-*i&;|}U#G7R3V(n7-h(feE9lgOsZ z?XyPsKv{9x(pF5tv#@UrrY2W>j3%lapTn4B?w?D&Or%-!&aEBXMJ~AAecn!KRFQe( zUt0d+Y58PIEjnNjdFSs+dwlkzUg<#AWSFpC7cT?ghjA!>>N~K~ttA}DJSdb)8L&u{ zn^o~?r07<_aJ*O+P%aivw6gC`7+w*8RA#F=Rx{*{$42tek8b}}dFN%z=>|;qe%7x+ zSK+)|8u#vo`IUNZr6S}g)L9O`=FU{@XK8B9)L4$=(<}Wd&+!O8XJM32k;w%b-=DJb z|CUmCrZ1T*8TqX4hMjo7-DK&IPTl>#B~!||%T(qYw#a=vXgYDc!WPXDmH~~j(@ir* z9+Ig3L0$cdoLqW@t0al(Dl+w*vvWR{nN>H%X9$lcP`;CW2nTZa1HqxBow6L_7FGWq zy8Zmy5C2G&f68xiZ|At~w^Vk%tQMU$LjrSmR97pPiJV|Z%_!oHfHWOTgYom^N|ZCN zwkNl_r)-#NmZ=fb#s-CACxV`(kn876X6E>a@_yhu+M-u9frRq_4SnVG;&eWBwP(#; zEM3POO`m_2S|PnfI1%Qf(h_RMaH9MJ)sG*W`-rAiGfub9-f8lr(oj9uwwml-H`#%{ z&Pw;umm=vheP48gmu|nmnexQJ>3@CWzU_8jVLsPSXk}wH!+$MTmaxblgo+gcPr4te z0CL*$aC*r?`^wm8yb`qC7M^ET{W83>%9p=(b>^y$E*@60h#^o+YO#A~kEdF-Av_NW zU!wN?($6-2;nd5IH&*no4nMiIhigYB*mQmAL~&N(qdq8nqt#QuCn;bkDNgJ|&}|YX zP28E@zifsQ9Q#!`=l#tb|5|iIv}xyG<*7%;C;Zrkks0&qL|yOltFFyiGbI7Yw$YE; z0;W8#BRumFaj3SOgZoaYE;#vT!x-$?<>#i6`2?wt3ml%*1mI&SjRU#aeA|44h8vj5 zn&8U-8y|{Pc`O4`@IcgX^|mUI&+)eaNDuHRCymsFP5(?OsDMH+z~1A=D&!^ER#W4C zH(6-KlJHo-T$z-T#DMU8v9jUz%_S6-e$Nnw&_=ZqvE{t!Vz_j5Tw0hTN;NG%7-E_d z<}`t3R69PL>C__S3Av|sg0z4FMmF>D>9I*ya+&)FhQN4E?h6Ze2#lF$1i4FhBgwUy z#Ekw5GCA*WQTB1K)HhR)HyaNZW1sMQ3~2Wie?IRUe$>{N1XL^6ydxu4n9S8$sL-+) zh@))ZvlAGH!432S!T{iFf9+0XNSIe=>vtb9>LSaBYQAXHMAZ7%~heC zQ-dNt>XT93664SKX1=k=9ZF0VadSjgZ-FCIBn*iqI~0JLIqK9mv+oOmm72U;Kp%$2 z#(=#=cbwE-gep<(Lh2B@SUR~#)nOZ?Ge!n<(Kxo7J;IecCX9oiQhxX^>;V!)V;hWx z8$V!>5+7#s)&FTsx3^YTZGBa(KS#sK zm(Bejprdp^5?R?Mux^F+W6SUrm1mqBJ)HNtCNlvut3{Ofi*M!Co9y@J8XHVHWLIrD zm}HBh=f3t!c88iA8CmadfY8eeCO(_g)C;Gv6kb~>sD?DH9m}uVg4~if$%%FKfW$LRncr%Z<}!SZ#O?=wKH6b-<8|?2q99kTI4=w3 zUT04%VGFZ1V3vraV5$1Xz$7XbR9NF>w#gJ)vHkhwo!-=3&H1LTZI6ANB3eIT2|J4N+|bpBsJOb|taEXlI$5Y@&k zGexYiU6)UlG<FwBin(=E}qmYs)VBwftO| zfWLeA;s;@)Wz^gDU_G>P7@KVMwGva`j8}FRi&9Qay&PqUQ3lfof}_9h#uEl4{Lz<6 z{n;9_HWy<~49%2Hrh$jmikFWl2(*D&+vh%j07&tqZtEODw=v!`Z|43I+tXaz(Ed4MIsRC9r*$2XzKB3 zN-SN3!*j5AIP`ubFC@+YHv6||$XjxYUdN427z**X?nkO*``mKP)&pp1bnNKtK-J4j z$xUsENnRj3xkmvVN!#-mpfsE4jlv-2i|~9&H!qM|L(w43t(xX&7JwR!uics|?R{$c zW7#m0){BLI9(TL>%$#nc?v3D=e}xjY`bbAaly2SFxo&*>77p)ciG;Yt@YVC$jJZmM zudQR|ZHDe+^+f4AzjQWh$a6lUv)H_S$b8lCWNOB_P{(A1w3OYklYPWaXA`Y$N z6r2IZA1&|1Z?iJdiNqn}h+t7QkGyIlj8MFo+G5f}J!f)r)w};?M`i6ti@kk+=EMq7 zQ@P($Ef%R%d^R_W=K-sETb;l6zHUbKURaFa>}RCZob&b?07his&=dDW-&VB};j9$I z=V~WyZ+)AoXEla(TyM>bd!T;J5WWdQrF-#)+LPQ#L@d*8yYK!vC1K=TiJY80U$~j> z3l(*$$r1(-Rm_Xi(eDFUnY<`ekR}QW=7VRck%cfdiuYGzE|8*a42%jxg{X0393>*% z9&N^(d=W{<6F%gXASjrKib}`pLdk~s5+Vvfwsy&dv)nS}sp&L#{H6|?64lV_kXi&+ z2m0Y^RoNU%`E;1MkencCo4(1fsZwIrv`QDnQ+XWa+caa)cAbBBRZ3jJY#z9Xf3Xw6Z1#Q%$imi!!Pi{teKtYhBIh z;K}Z))W{6`%XkaYObjgOBZwlBlW|FQ*w+BNu-T(Weo^y_6XeDmGU%-ZI)N{1eAov9 z(x8w6S#19hT^L^Q?mPVMhJc`*NHg)%vUNr0j0b#@8 zne!2Z+m1`oy_u-}UbBVH2TNX^aUbbPrY`>MtJ0}U;(SL!^;65G6!66*RsWJfbharAyip<4CnrUcPOEn4EGi%aK<7fD?loJsWN;{1~T{)bvj$Fcn?hE=CLCGcIGB&t73cE;2niLI6EB*U9B)-8|=()k|9BJG+${nJ!$E6grb z*OfZSTesDT(W0|Y8CPV4Bl`w#>hz8eW}`9)x3!d#eu1r|8hRoPlryc=E69fbL@FU5(vOqhptO zttu)7v}&weF{^o-8l);3W^`?FuvwjV?h1`j|Ll*6VcKemLQY}as3}%Zw@@eCy1kj} zuelz7T=U?di5we{=aMamh8dYNy6tV9kZdRsSJv?lFIYKNuXLAJ(`%$AddVWrL7G9V zwU#rpyMiB%odFFGY*;RRM0n;jtis`Tcl2!79sbuVy3Z`fYg}h+Zn=ARF&Y1PD4vi; zB2RThlvIB^>Dx2@ATRRW13y|U1!uM*-%Glzis3eOo{(B=(+n|+zG;3BcWydhtHm6V z^l%o&#l?I0v(NuL`e5|)8q%&)@HZPpoO6O^J`-_eSarw#Me|sC-0RUn(YN17-vs67 z?P2F~ZY_{+wn1dK*F_p|~7>y9@KQ!j~GT!6eQlCUw?45X~gyEY6vHLc!+t&-C=eaK#oS{JeT zJXoEcvGXHQ<$4}ZCY8|w%!qY!&6g$40ZMq=r{?!(MKqe%_|~iI^S5*^l_cO>cD^by zIy#`_R=pCnz3dMJ3k3=CC}K!Z^*|{8s?;9g4v0Uom&@Lu;;-pk20M>ZBO^M$vLOj~ zRqrFh<8>X-5gbuy(in82{(hLOv9eb&rA8nL|N93os1y_E)};~VNqxNRzo&c;WG^~f zvj20xT;1NSZbwoFia>0mhr8?aSl1QHrf;7Z{+yuJq}XTzb+Ch^4zfX@DT_ESK`CV? zZODT^^`~{ICgS{@#Z&0L`qrm-iPifHvmyAbX@-CbUu^ zvPzf9Vy#BHvVbipqGdrzBkISe2g5MWK-g%3LsVJ=1=+pW5J*}}VVW|I=D`lsZ!-_9 zM#?6ktU?-9TB%o56Qn%i_q4O4#wTmDb&=0rBX6K<@tcc4EVBrE)*|IJ629FX%{*(JBP%2_pqG-KAI%cG zlWjx3(~!(;=A2S-BuZWc9$MVP}9=0|MIs4k6af`yn}DL|9z zk_m>&2lWYg5hStRBCoD5yfLP+#(0$4;W$={1E(EfQj4cO<;LBo3(8#*YEDp82^Jsy z%N*!}y5SfmAf_ttzLP)Vrgm7r%}p_ouVLksn%G(@^4N5s4qHvCIwV}yG*Q7k+DOIr z78F>qC}Yq(QQGlr8kk6N9)|zd9Z=k=$SqUxQcF|A53SD_c@IXs{>W=Kc!9l{BWx%y zKnx4S6N=6a5&p^qt(TvA6I*oFSEo~PSih(?UtSuiPV&a?(?yZQ%^xVsup7hGls{x+ zK8ftdQ~tc4R^CF9w5!`-0zbc&VyM_B=_cq1r=Rw|IvE!d1)~t3)+2~9ezP^56&9!Y z3*%x5=dCkw3H}Z$GY>Eq_Ox~#h$^3ZNkA(Omh&y!Xp+&O>r(e4B`{G5tx?4XiBfxb zfy!Um$SB1Dm2nOJ+N$h05Zmld%rDZyimyJBoB&c~{EI)3k?sclA~8t#d4)ss*v7)n zmf#;{&o&d6kjSuy#3epgT~--T#s)1jaVZszGto-*7qT%g#Emrrj#asRW@grQP0IXl z4FZCN)(pbQT-LIo63|%_=5C8JjM}q#+MAQy5Q zICFPPCJNGaRVfJ z@)uZp?<)=XcCg~xaKU^r;8Y_a3FFUgiRxsa;Myw2r1X55 z@@J*!n->RcwKRBkEW6rYTD!Puvr5YTs_*ojme&6%HdxEfE|#%1eP0rtrj4GBvwYRg z)P3u_f$c7Dr02gRkQv^tuxc~W*w6=J0Q|eEGABdtqKwMEfN@m<(3Z$97tN`@=-0wnZNNmd@;$884yR`l-f_2`jCgN%0v8*R_K74mV1Jhh8o% z9hz#-fZv`$TC+hZZ%7&R|VyLuapPvqgm<)29p zFE&+{*m?OXnJ%a)G#KMdO-;~FI|?Ok4z+v_Wj3oRpDp3MD|^cK{L~wDjW1|AB~b0r zilNeZQOnP44mjZ{%_s%IHQelfaB^lrLvwli>Zh7^H?as&NFI zF^yfAMlPR3l^3&vY7osAcG3izow|05cuo ze(M0sbbtrdGUT@cB-7yxLJA<$k^dTSOoxId1B~g6&>kqJiv%fPHaPv)p$H7q5l#~j zrZe0O1k>5q0RYpPBwRCufL}TRCUN%bGvJ7Yam@-`4>+UM5jyStP| zGhza0uh;Fibt|X0ZEK6%y@0ppno>MN%8Q`!xCDqbF5iTnozcB#pNjW%HkfDSPndHY z^bzNDp&9#enHCv~TMnnEwEXM!BJZ(GmserLw;&TK{ly<+57+j@;$&7#dwaBCXtp9> zN}uT7N~c7RrgJXr`8MP2p_u&fF}tq>JbSnOfoZEi(#1Yc+QfOmMG3B%7wAq^wyC2Eux!ZTJR3*FC%MLD{_9Zw2I6L1#Gt1h@_H$_t!I zD%`9)_!bEZY&g+O4RXW^W4KH836QX9K+0+xbSeLa?9yJ$v1`~zMP;x)z8RB!DJkc2 zZ4oqI@(B{LbffyB(RYQ?O}4b}T}qDuFu-ZMrIQy!UXPGyN59RQhf4Sj<7yjklt1OY z@~P(H^q1}_?D)$1_Afq_C)Y2$bBBV%Dn^G0T~tKiANpt=N6f9U55x=K-c{=jN81$_ zy9Uu_7(a8z7U$OnmuG!dG-4A^v^Jh_Q5c#jadO@8nois^$HsOB`{$Vz-~<^lzu#gI zz4<*mUR`2>eeLo{l^QR0Z1Qr68ikE-#f;KoD94*0+-_j{XrRmIApu0dOZ=_`b)KMo znsk|1A8~nDv@O?gc&%}oqT#T5<05-Warp zlqBi?7?Fp>7pZ;;m*Ao(#50tJMejf;@I5??!{Gvqd(!ZE-a&~dlmd5`1SrQA6wrc5 zCtY_jp?hCP4)5+`feQ1=TyF2nhsnW$e%0 zR!>K*QG3SuJ?4WPL9h+nN*=ApBXBn!c9nJ)Y&f1Z5T4u$%rs{wcu+vxc<8z~SS!6X z;=c$Pv{4;GzSt_b0H@4LYN3dS<}#Gm5wDtt;GoK_Gu}@czlz)uKEwb`nbU+&1OQ?85yWX9lNySO*HyI@# zoM@O;ht+Ww+ECnipT)Guf#qJ08`p!P&;2mPpW_cAkh>3Jkkc`Uf@pr7v4Y3#|D;RR zkR6IcgBTH!kQl{Wvwnw(yJj^F6Z=t8kaa$~YS~$Eq%JZ^m2lIBdKkMqm zRl~uW=&#C&V`dfz(f^Zy5_u3olyRY3PuEW}YjWCNy1c`G-hvLHQ@UG@Qm-&BW{4Vf z873{xE-SrQwiLU3J4XVCDfi^z+N9|#lKIk8E zX8EPlO=1u`NFJCrgk%*#+0^9*lispJ2u1#xiAL3OtRz^y$%Oqjk8V56G>Bg5jml2X zrS=u-@6;p5e5ys2jtMmt9w*kMGlSS+QA$U1R;3dk&N5|3JlSB@!nuakbrnn&7a2R? z98dqOzuF(jrql3zclW0%(OhUTXd7*gtpyO%b5a?*oeEA%#`VNF)`PBJdfzMg_*hvstGpj;duiYBOpdp5-A<_UAGP4w zaP00xtm8k3hA#a=oSdUE71gb_5zi)pAlOf;*BTpKk(ki?0+_)ay|jKYX{y54Uq^kv zZ!&GO$)f`CR&v}}BR9W8QF5iMNrPRK!i=I&6!=7%jUNT`XSUd<&uh z2)+#|HS+R&CMu85n_LL2i+oT{TI97`)}CLut~m*95y3)JG`=CXF8x~L^BDX@Z^J4G z{G|PGhkW0Q{Styv`_$(D&ZlndUyGQAa;0jqRgh3S^|!p)*Ld@S5tvSC7O)kFkL5`h z=E~|cgVaG(#tZ0t)w4DvvzFV#v-r3I4#5| z1ajn`tV!l;w@l0K4G%~q$zBhj3#-Ve_9$J-sVW6bkzdMQbLd3J=q@V;Cr z|0!0^(;usl?yQ>G{nupCqNB~WJ;aKz71q2qF!i86!P(ACNq65 zI1--#vvC`2l>>)G$E+lF*UxHNkhs2WG>@VZ${w&;Ca@T@yvF6rwv559JnA2w8>nQ;#bx<#DzBJD!47JD!5AI({t@83e<-H)2xS zS?{Xk<&B@ehz_tAl|+lhKKn!2ZI|m{`Qrm)fsL)mSrkhik~`0i&en5R=KTFJ{tvKGM{I6EE4s)FNBq=cbbFNg8uVgv0u@dAzWTDKlG7x zYvMnq&`DL^@qWck_t25Oru!tG&`l-IHx0HntQ6#{`yJF`YPlVSJBh7c-Y=@Ye;tdp z)5rpEoGnJ9O_m^Xwt1#-bhVQy>aTi}^<(a2)WUQax*mrTK((JVZDvi+l?xS%-g%dK zyw88c_HBP7zG8N8yss0?bc~s|;XaOB9EiI00CAj)hGGqi4l|YVa}-t;j8(;R>l!Mo z1bC!{-j7VPvKriy#6H*jZuWeRE*FfWDVW(AJooXnO3;L6Mz;v-*R()?Vh;zI}DrJ7f~ z?y#~_F`GU#QM&r=U&}Q0sW{eQ+dFd;j3O)TUbZh^CLIo)XSCkWe12}7Y#4yKO*VLA zj=qj^=2f4*B^IDmST@z@jKCVK-bT|(@R*skuaq(455p4}xbEoHdxk2

b=R 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≤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 z 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

@x#ONE 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

<^Her{i2>5D=3(8Vn0Pz~rXWTo#I= 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 -E0C0@O2;TQ-`GOenxmB=n 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 z zl@|$C9`p^XXĶ+|&?H;hu+h)6WL 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�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%0Doup%KU^#n zQnn~8K)@p>)Y53l6R}1a;Tdt_)jJYb8&X+*8IxFN>J1TNv91LLqthAnz9MpMAM*A3 z(!9dap%DTe_x0oKfM*RgDme-jrPi^OB5)ZG%eWjG;n;zirU#B&&he;*FO@1eMu3XN zOU5YxEi{TsH>>^7G0zC`<>b?e2jwoCn5oH`jHdgW7od1uqF&by=%ReeEAWZx;JUkE z`(i7lyJLEuELqBR1BU)z-_P*lGfsSCx0c$7w^x3slA zBO;wny@ol{JN$&QvHNnu=uf@ZYe}DLkZ(HT4c!gb$s~m{nNf6lXE$TZNC7b~hOww^ zW&H%DMR@*A=3JFL(8Ic3?@JF~wDkCc%%{yBTppC}xwXQGHgzz~npSYyR6~%OSsRS_ zHze`hy8cGAv01Dks)@;?#0g3j;_L^XMFgJdd$iudr4nit9!abuq;}_fYx;Yruaux6 z7qnTCvD99#mAmjt9`bVYslIxM&w5jp`}+4d=0V2*hOt^>TFRBi z&`ur2Uouh`S$HJ2^y(FneuJ5XgJ(E}nY54FE3}2gf~6MuUO#g<*gGWk$dis)#p&Oi zA0*pWVk^XLrYAlmELqZ;L%I2c?o~LPpxsguZcHS`lBE_IGG4|mcifieyuKf^6gy$- zvBeKK5233fBce%Jg~|%yuZguUa>L?g^W4gvQ5%U+ntKcM+`B`Z*@|_duQ{l}T!SB? zWu6F@Sr9`AS&`JN3H2I!m(ea|m>k(>CE+*J@N&Q>)lGcQvinH0#G`t3RqicXka#@) z{**Bhs2kt{z-e3&MRE~fQ`I-{hApc{b2X{uQudEqJ?IVeVmp`Ev3c?&9gs=gB9gA_ zoQ*odho*a*BH@-{BvD=xx=6N&C+8+4>y6i=T5e%&oP0=_#n4o@&Stb+D~CRbDiUk| zoFVF~KrJbIdol6NfBtDq9o1}m`BX}tf5HA%Z*h|<*N>-)ak!qY-N<_&v3rxmX^OkW zMAwjx^xL zBv}2iTpmxZ<>VLaBPG{q4Dp}m(u35DfJ=|DHJNo4ZeE}Ci?Ok`gJp9 z{38AiY7T32v2mwCQGOG%3j{q6ipV=>IPp#!d2Cj3C_k0$W1c7_h#XX-P?r+#o5=7^ znj^{>ML;EV3CB`v2P+LU7c~os+_?kj+e1;`-D}fC-c%Gi=4Ck|$RejIvbbyJMvNBg za{ncrk*@}1s+AGaJVk+I42j0|H_QQsmSh3Q2X{-|gYO0R@nCAI&`HH%XnD$>k&m>b^ZY6M$WiO3-n@+>$<0E-~oAHeqTyX z1fM>Tll>t5!^|rodM|8B5NWHZ|2StuDl* z%xkZLGEBm|B&zf+tVZgMetE*y?)1A{Qz7*{XYu-<*|Yctel^|9AvL#&lILRyH`o?r zet;ml&u(Yw)&0>pWvV0dMLe|pgf;_c1D|$?!7*#5M2v$rJ>jv5gdzp=2-uO+N+V5WuEPI>lQ6W`0XBGk;X(?DvzQ{dO<_3$TlAi`I)Yut z8uPF*4WIm(RRC@8_}(f!OpjvJ(jSWyFdM6RmW(BFp?IZkQ>rXVBLv{#6gfPK{i_IZ zSMCwT(UwU$QnEDc?9h00o>qY_t0+vp3`~KSA$~Y3O_HvJ4 z-|!;gWU?l?q@XpFU`kMPF@G}eUk4hpw{;&p&0X4>cgnCyvB z+(_rtXRuwbPq>SP5&zGGI*?b85rfebn*&XpSFIMX&I_p6syLJ>ZWlz4eg*KF0HLb z%O`mtC@zFh7t;rxA|;hoZxKY7YgHGfK^nej$qw_Gntf|&q4zOY@^#bm=d^ilsY7Hp zIUV^N;~%2RHNeu-nh9}^5dgWWtlH>43mxr+>u=oGhMuK2ae32wCYIMUF1J=X!;7L= zBqPl^&S#-1+agAst66r_6)K(={_~4+PsW2U45t}`GlnZFil9ZW5(*2f5SXU28z`dj zGymHxV_{0S7oX*Z)-nx4ZvjloeaA*c_IC5TL-egt4jKoU*>EcH`~8%=@BTUYdyy~M z^>U$6RZrA)A)VcUhYlG6uf27p%$_=gBqXp%Czg&qB-YLKeK~`AU;uL9G0J%GneyDC zsp^!#U-S-=8iSkKg@Xb9H@XV7vFsTX&POzkZ-ew_Gst<-NiG_tl%fmgfe(c57dJx{ z-R4!B`fTuXV-b20yH!u+=MM`x;Vxz4sgpM(W#QjvXvLU^;N4!Um zKt1;W$vEP1yDMyY7E6}h;^c(DaBHSc_W2lRclTEw*RKz24c(GnRC56Mb`9NLb<3J< zbrOmew-bxi zAV}(_8zwlU0zm|q%N_WZGI3moM&46TBsoImbUX)$syeA=24N3%Kw$r)+%>$k%dJy5 zi=gY;ejG5(Posw|^>8i4ODPedY#7dRpS{uMOU)hcY)v;fm6>x%@R#OERBczaT7N?@ z_|jFe519fxZ}^x*6HL|=&+p)107raXTM=&DDg073&J{z&8;G!*E&SqH=Q-fv)Noxt zKu-(S)6tSXd-F|i3(OcR*ruv!N^t-Rx^r-oEEZiTI1%G37fRx7swc(*l|oz3_wAd< z=G@{#BM&49iTKgdjwW^V4#<{19YU>+!tvMG!Y4n%hl4Fht>n-`#`u})$w6a} z_{?{=%at0E>#(?7m0@=XbJ+gH zvya-DIF^RE5LPX)c5|+fZLJgA_@vm3$yzhsf7jpy9@D4rl(Pz=M?6Z2${ZRWT6>-l z*O?N`YCNk?j@nK?$RT!Z@!W`b-c5_H>^=WLA9JFk%>xY#O^)yXI}5OFXe@p zjW5lUeV+zSu<%4rl%QI_cHNu3P0uZmru&gfnF!=&ElCg1v@pm{89S`hlzso(Kqv+h zN|I+lfM+XJNc%|^bVK;A#8R<5{*8B%Ln-Zo%64trG+$-%c~>~7J?a(h9dEv%k|e1+ zv1OOmiLG1ct~}v6J&K+|8R37Nm!Yy`(wEiwVEOWutD_jb>IpM;N#PYK z&DK)UJ0fN_-F6BFYTt4JbUJX-!S?|li-~Q|(+@c#H1rYjoz~FkKt}5e@1MXKmXQY2 zl`WM6Q#o-m;L%evSGN7?Y%s-{r!o*$m4d!p1xXWE>fZ3=qh6- z(j-jCKG|nori3rTa&qb8m=x@A^tRQ#Owd&_iA9Xu5;m+>T8;}QqIonROLh7>?L>od zlzGXLrDp)o>r>rmyJO81_&n=aE{0$hxuX#wAe^MkBIsNpl&n0N(Y}g2x2pPo?4v)Y zP8$b+(Kv;+K7E-lPMOVc`eNrIXtGO<-dF~uZ}C=kJj+o}Y25HtJ4l7hP<*< z3bKanN70Dq9tCp7LN5(3kG&5}^ST|o**D>WM+$6~6+tecI+_>IPw81#m(JQHAXsAE zzkn-ibG8vJ&VurVC0m|;TRD6Zy}W&f7e$&w?vGCn&Sm0R5G$_aONuXSk^RQnbM<>G ze@th$$U#X6T@4p)dR%!EdG!FjxG#D%NVtEAJQ!HM=MYJ%Jt_LE|{f2TgIkJB>| zK;3COZ7xrb9~u9P{L#+ufcUdx`OS4lf4y5Ta?eDwXo>8I6RE5gsG0HUfvO8=`2;WG z(Jg~#82(_p@lP^uY9n4p*Rie{t<|0R;993!+eKo}Br}!Fccju!)<9se!W+tiJOsUb zXZ~^Xu9Cdu8xDJ1!-=`YZt_P3I10NI*_r+Lw7BkI!=^%fozS0lMj|7skS5eu7K<%4 zR{8|toAS+9TiNh!-f80lHq%ef?rnB{{j>2$aRo$WMKXw;-5+~!m|2OJK^txFucQm~NR7<>7S-Js&=#*!^NRskR<1)1{ zP}K9@E_%!YDxOxAWSuPwTdwYdT?>ro(`=-rB;i3TDDe~n+^1hs#oxNK_^ZN@FY2b{ zdMwq&r3(|uc-9Mjt-{9+dLVP~tie=;TZT4dks#isE-L~PpJJXO$9D9tq35E948sB4 zy76lTWoBk(9tzs0b(hfTFID-YUNu8+S^PR{3C?WmwQjcq^$b(v=WR`ImnStijKgAi zI9dhOlV69W`+S)Mj;=?K-%dgSGFR#6tx{c28}I596%A8mX`62AsiE+m-*?8cbZYyL z&m*$?`4QAG9%eBZei;sEHz!@q;&)G0bR&zeV0&s9SCPy?`u^&^u|-vj_~-SHr+J}^ zO^2pX7yaHFui1MVT+QNl7t+C*x2quf#=1!DtaNLs+`BxO-P8Cx))908`8U6WKMr+Z z85STcOw8DPSxyy4k*)*yT*Ps`WgqD<3R#b^0>)m$a`{5Vugxw-WYu;AYR46!*xEsv z$9#451{*n%{|tLTeA}_xZUZp_3=`9KZ7Fw-B?ibHry_5st!+xuh>$4MWjN2=cp)w& z&A6QC`zRUen6g*9ePhC6us4r-Uf6_^f*xr{!3j%h!rA)*-C6wCgCU*5ytwbMv3Q^Yul38KOY}x0%8y%pkO(Yz~qjbNuD}Nc6nifKlb#y z5V5Qdcqm8pQ1j~Klh~F}|Cl`UvP3R?#2_#xY5X@B{w}*Dpo2gd5AD{s+B707&+1PCQiNY zP--prD6=iU;E~cIfbb}YgH49+1KYaMD|LGP;{E+Bw?s=6d59m)r^HBuW%gyR4Ac%Z zCaFl_vgQ(~ma09$;3ajF$s}OuUF+l(uWH~;7D0$a4bHuc>w+#~dJ}~$@TCgsL|JKC z?J`~hE#JMwxO=1YQ(f$zp=vp0okY8{tE_X3{DAnh7N*=oo`yG=*;!A{94lv(I9 zAi&9{d&jzUsR!>A-@P(%sV%ygW^&vBdd`iBwMpCVb<>n&gw+SL1$X7!?&fDQSx|j5 z&k6QtSO4-N%U4N3X>`09`I@Nvh(&KiO=DP_uOxkdt`IBlrBSPu!$539Ljr=1Agm@F z1O(^Q7tqU?mem53R+ixgkTVp422mY z)OgM#F~_(GvNb2j0^#-~B^5P=E_)!~U!_yma@5ZsMJ8mVNes2aFd0C})!Q)5uSk4k4(+aTeJil}x31*CopwNjEL`eJMx=Y_>fFY?Ep@1$oX_0!TaB+=D&u9R3Po;% z09jp%M1n$?jrDK9=1*KoV`9WgR&Sz+HSe9u?8G)x$8JqN>g0=0`6Kea&_um2$d$u6 z|KpK8z>lL>)*mi1B$tk_fqZ0HMY6pqHaQofaIfQj`!{Fl9rd|}*I}EW z6;n}SOX3fjZd5-hn*g*0s`jnW{C?%}Z{|OnmVer0oL7OGF2oEYW-q%q?S|eO zL9+x7z9~{>@jJcR4GxlKp`%JNpT!1HY8oXZhiv8mkO75_{?^7gpe&>T8s)idjkH4& zE6Hg{{CLDP^@hZ_I-!q^U_=PY)f;_uXZv0HFnu!J6=lYQ(VK1lK~@)^PLuJYT2YVB z2A>*7B$p2F0&t`Lir3HwLy+S-NTkvJf`c!>_;bfFUlmU$q5+k=?c7Y+fsFw*;?OIE z2a3An7#}*-!m`3Vv2(2W1S{wnM2xMQZE7NXjIQJ3(zQKz3g-_6tYtc&LYtCtxv#bAjMWys zCQ*YfYIThyuBK50R#sbG3#}-nnJpZIU_-5Y#;K$0QLz5kFWy{|`R{LgS|Oz0z(}#Q zKX&e1*4zZKH**W<^o$`JXqjHh-q1^Ev-S;chOz1H64XHQJF}Q%og!m zdm)gD!RYSOx{lm#TU&7$(P>-4Q{0F>*!Ru~>eezf@#|Y(1FPdLNf-a75XHMqSc4S4 zi7^Y==*Fu;Bf?d4&omKTXazw)Rp(Aktg5#9n-Dg`bEP1%W7gQ{24i27^e0g#?|D5v zb+-@}(7ZYXqMO_q?CH5`arSTTzVGe^G`AnDjdIf{Qn@7C&9vXP^b>z`rH2C68o<`^ zM@sAm99tZR-FD;($xTJ9uTOvl%b(ml9nuPpQ%E z;T7IM*NyYvuK25ktHh+t(iXMOU>QT&oI)MkSrIRw1CG}{X#<|{4EOOdD0M6}l5MMv zp#_)vLAmd`nkF!IzWHuLVJi9a*WK@8V~4VYQIpl^-ev8z<=Vb}bZ!4pHI(MluS1!P z!<^=-BFp14TUe_!T4?(QXac<~Wh!)t479lbOus?QbBr^5up-*1;v-0r=gl}UrWiWO z8Hi_hnkT$EKm;p*i5?Ft3s)_RVtX{v5R^fLhJq!tL-G32J#w`S6;p)_%j_7siIk1> zf*{heUU?|W$BGZkO}O^@T%GS>;e1)!Io`gi+My(wROM zf45AI7%RHIxOy}6Pb<~CZ(*#(4p*6{aVU!qA`tSM9zId{1|_m*C9gBel17Rn)E#HK z4`qOb89_zJPM{e!PRikzuC4fsRCi%EDXC`P-nj^_JipyD(Oii(#yXY9UhtUNVqvbg z=y6STO6QFD7qsGZZs-~{r0q0ndDPwd+t+{^JTP;})&5M&4ZnVo&5l##u$huy;VbDEYf6CuQ<1T2I}<~Do`zY6acG)$8;s0& zMV1o3WpTcy7>a+4wl(#qE-kr}O14Lv72T~?7yR6ioO@UoTvu%&sG$pOTj$=TnrTDe zLrIu0pAvlE&Ek~+$V3Lyr&cXYdd7#7OPk_^M8OvNP>qpMsst$GD_?w(PCM|<_NMDx zRcvTaJ3Tq-rqVkNINJf($(!OZB^zrq)3+~qy2X#9-a2AdY9>=0|Lk#DNA9*YT)WSd z#C4XxF4%d;v?KvDGPZIhpZvq$}I4z5XIm5~XId(Mo@I@OK*b7<^V9ck>=m1W?OvJX2h~ zZowyJWQ;MDv~R$%iX>)VBDSx+az!rx`FH*qWZeO=2unVfS~_f7(z*(2cd<4eiS&H7yyLp(Z4B_P37K7=iQb+vtMO2sb!oGzUeXk`%)w^TV&Tr; zmSuLM#)f>Zyy?)-@hcb4P?mxj3r8+%AO?ragl)tZNtUFZ$=f%Y?{V#tE=GI23aNXaxqo^3623=(1ulV6MZcZO|ah z!&lS0sybtgJrMlu)C3yRP0h+ixS%Ar5-Cs0<2=hu?-rTk_;nV7Ys^>&+?K%Mr?l&8@NQhD z__)i@|Ax%312-!jLdd0_d@>G%sG!(7*ost(7{zOT2Ny9)Pl0 zBg0L21r8e`uPhil(L?-yXLmd0-YN#lV@x_wt| ztm%~$$r8I2hL&R+LRG#bIjYEkiiQ!sJ?327OeyeJk6{WLgGL+#)xtJVY4Z8h&2Q&UEYLCt;M&kgN9k&80g zwt<6&a($0R4SjwS>Z~bqy$YAQG^F*RPrlS)Lad(ZMyKoVL;@R zej&cP)T{6`By_ynp|infEriZUw#HG1AQEAK?ow15q(~d&MX>-rK*7K7`8m1yc(hZ~ z3N9&(s6Ljbxwna$!yS7l@*{%Cp%SQfZh54n1th8v(q6S+AGDmK#T*R2 zm{YFGf9($_Czq?c18uFUZzA2K>rp&p9!9>m&#!I1z|x!EyY1KA9n)S&yW+y5D916VvSwb5 z2Dx=oPHDRH=+odUawZ}mUIT&Na{8ExnowO5? zH~(l#LJ8^$vIMFpKc;GE``hG89r$acw6 zZpo%Mni1<>K*Np~_H8F=iTauq&tDaIJv9NJB;Lfr^uEul<@+d5iqo`&+ zfHO<#vn8V<}%$xkr<+HQrmT$3f41CG~!@g$Ays%s%b{NM&Kd$h|c}U+fzDrokc3`-`0#dxC>66%{$B>&VXd_VDmf2I-7m z2{-O2QT&Mq2oHdmxTN5I2mpaxQ5oJg;Ve;fk;P_&L-8bki|alcYlG!~{y#^EcW+={ z<|aTB2@u~xfQrso67SGCXXO`MD~!{b%qO1t&;)RzcO3oh=(_ENa|e!&!VoUe-l^SI zLa+E##wqZbzu!+)Nkc#v32IX&t$4#4z7xMe`wO-#XwsTV9XCvZB$Uo~pJ&%C-H~l^ z{o|MVlr`u;5hr*r!L)5cxCc+wYHC(X>SK@4w_7-bVbTXJHDTY*-|&{hJV{pPB~hFEtZ!{fkoe`W%K)*9snE z!8{f!MZ#Pk6)iK+D@`Qg4_3Ivn3u_PYtqFXU$C3CPvxkWMdnVElTi}(ivAWow29|A znBpTX0V}Rb-W0zg*79W^naZ;DC)&~vu*hO52!GnNGF=A&K-hChLRM!Shg zvG``VhGP3|w(0vNLn;R(*-S6T;U+_e6~z~KzY768_=`sYTnv9yyZt(#GKpg;kk5Rf zYz>tA)e|GF69A-uERuKISJ#(?Att8f`<}n${`x=5iw9{!C{Dk4*d*A3ioG5Lo?EPL z+igQFehm-ITWTN0g>W1N zUe_$T{-*S;j7?bz6tdIsU|;)>cil2Ro;Gib{>-L{t~nQkr8e&W8Gr7{HMYdbuF`r~ zlgd3~5r4P*E2iGFgRzORVc4F%X|^=t%I+tsIMg9(%pHjdl5ye=x?$?8{UeXJYmdT> zjLM8H&je@dV8ptU*&$;R4n^ll*Ninu?Yp7WB=d6~dc;$zRXlh-M_~gzv*iI(kJ1+( zQQj@Hoij52h&Lto6Mwp%>(~W{#GR6Vc;VAUN(p#<@Qfi2i94+zCio|)&w9zEB&wyL z3W1Mvf!P2LPArQDA-VMt%VAi^RpS$+ffAXOPZ6C2Bz8BXmdldNl-!5is|Co~wlj*P z+|UM@Br3SXP!+-+JE^ka535&%{O2!07%_$0Mw8dH>UVL2U?mJa!W7m-QMPCiZ^^mX zy_A9k7uxV7Yo~H+ZBT?9&acGKRHO=J+WevZHg29nIVzMWcc37r*BhnDTD5~Rq-+GF zIZ6^WM+0(o8qM;5qb8@H_cF$d^S(<%QYN~ludB-GNTJS-O2jh9k)r~-Q7Mwj z&rpsE_l+-VjG3WK^cWxYCr{;!Lq`taI7VsnPvtvM$EX`el%cO~ZV`~eltjQfex-!3 z2jvZJ7iL|-3@34dD2QBrp37(;mwg zb`oSk@|?>!UOhGj7MVs&AL`g?_7{GrYWk~NMd?x7*@ph>SHIiHZuc2t)I%sDOn{mZ z-6pr+Rzef#NV`2l)zutxSDF;104llS5>wL!7CK};PjG%LJ82jTrwt7POKQynoqn4+ zsXCZcZKf1D&gmx#-qW_+$yhjTs1IVYLmN;vq4bPECYgb*g7U*2OG_ZohEq&8Nk19^ zO=^WR(#8yM(uQg$ogrXSgKBbm6Uqg5J(J!SMxc`7Kvf>8+{=x%)N|IOSd6mCPUt+UH#Llt0I&_F7=64@DSCjltjuKTgG}o&-o=GKt!NGe|qOF!p1F z;LhtI7nM<65eX;b-@;V>W~Xd>7s3wbw&l8kySez@x`TCmyF{;_xKNgQ9c64~!4x-$ z)v#1RS9V5aHTGnx8V%JQO?f?pCZFDsWm%!CYBbi=TcHgviqW_5(-d=MSpy24SW-;) zuC|Fc;s}qH2NsLtQK)}Lk1L+l3EfRj52=0!fdWK}MbpigC%grZ$Xumy$f)nql1&Hk zsQX))qxD;#x)8q~?5QSnvxF99#GArJ!s(Q&jjHYfxQ~#+3S}I&{MvP)>rQ;&fn>8q z2%nBcXicP>_{qIQ;^L2sHkPKoKTf4KZC^feXLx_0g&H7ImV`FqiEDc0w2Ts{sFn^J z0}Fy{n61lTd>2~h%zi1HWXp)R`&?L5M_Jg*jY2v)Oi~o}w*$R?G3w9X*4bBVSztOT zK$J#^6OhhX$mG}USGOjW8n9a=KDGy@-XH0cEqW`Pbd}IGEiSS+YB;59BL4B`k;Km5 z#J`yDZ22l*yp`Qj?y&^qqC?{@?k<9i>@k{PA!g9%?$)k0>K_(k9a}{g%T70J4)`T; zrC#@Jr*v-^!Knysl<4t7*j~&H670C2bd|0Kaz%Ljq8ugOh{qT|Sm0V4#Q-kbp0P9F z#LC{^$|Dze2)SVdcS3#|dfG}Y*GQ6m2pSPq^5)(EFzcml0WMORYpUHYv^YwbvyKkJ zvsuy38zEI5l5k{$;^ph<6gqvg_S^l}?|RL8`Ta}&A^ z9|QeYoGq_SvJ(j-|95fyU@QW#&LoCZiODYplem7`{N~L0$QYrQ!DX<1PJ3j415Z?% zYik|Ij5{0lMJbyM-1%3!g#i!R&ryZ(*LiUhbM8d8IrA##Cl1H`4_scBLd@Zq|wcjSVx(fHKy>uwXzFXgy zHg(d;Va(^zcy9t2y*J)^m>8Izln30iTnQy*7=&zAsXr9!OKl{gvI7WM1PUz2nA=>* z*JC6n$Z#|^uT_|py)=x-I6vFVVcJ~<`|?=hWqeoQly!IWk602Qs9zk_xi`W4us5v% zrhrPEi2Mkxx(C6jTMa-`J%wo8j=(?4@(a-A)M!Sw(i_kG*U7OeGUl~i1qZyKYu!o0 zc^N6>VkrZfY28tJhm$n`tV0LQttuwB!?2RdMPOF$=xCg^^q0q zlg&l|pBPK(&XwVBUeNW;`65Ex=4$ScED?oQf+1`DrF#DNYNtLtjU_Og#Nbn?mJXUi z*O7hWK{+J0n%j?ixIPVuP#4Le*^15ITWyEwhd)Pn(iUy_v63uij;#G1242$Q0?_-- z{#{NkE9UpZ33iEAm#OJ0(t}|D8lFlf4B=*UJ{AxzJ*A_ z6C{o~!h8Wx>E26q$CoGGj;w-W*SpY_-fLM2aJb5|x^6^;l@#NA*00{bNF{VI+k}Ob>bV#r0GvVbB%4YqZ^ zOQLe8HzLZD#8&bl=QaJIh5->E)scUcRiT&d-B`F+9T|(DSHoDtqD52uh;a7wb0-@MURe|eRU+uL*Xmi1h2 zKb9`%6Lj}YDF`#ySyZjcj(PMvua*ndRA&ps2VSoBB2oT5+xY6&^#y-RB}SqUr6<`B zbkdzi!Ty)3j(BS0&o>dph?0@79B3DeadH|pmA)t+*M1jP&HK2phiq+G7pg3YVN{#> zamb0E;6YhXXV$l72(UbfYj=H1JhPiI+Vdxqn^G8=mwjBjV|J%iAFX<@H?6J{h8K%v z4nS>(rcGCtHK0UY24ha65$5|7lPEzo3r@|Y>9iP#X`q)tI9b6Y0bXz{&as%FqCgx- zMKNKxj0`(+Gm&D5r(7Zr`?rmq+}~oXj^yJ5`sjT&e*iV)=;GVrQO3G2wCyfQ;I|Ef z1`5Vhw=9!bo@b$X2h*YARKjj_u(Feg`C~jMNF>$}rfp+F<&b!pOy&yWDU$9-{7|Wh zlhcZJsoy3P0FX%Mi^UDCG>R@V#F7fu{Q!6&RLzAG<_C~26>UsRdzMv(E-hcFlZ>&= z4ghYzYuZ^wZOkBtwYGqBNmuzkf;71BeAQ=g1+$Miu-Vh0xQ{*w9(m9$QN)KIrTh#) z3;{TxNuxdO@@t6*M-y-%O`$ThmPix=P=*xEF1vnQpd*a4aDfDBEd7cLm#`sTBHEv@LKj%^%cGkz@{4B+!bSBOjzFwRzxD;O?Ofdypdf778Cc zK4Qdm?7e^zpTbqDX4rq1u=-Ie>gvjF8By6AtZ;2!D0I0e#{tx^2PvZ67lR-?p#%l! zR?t&|DDd(wqQ!(f2sRc$wdidkgGUgs936{4Qj~W{FfZ1>ZcxmMLEw|}Lx?gvm>?Y2 zbX;Vz*3h-5ng@ubzEQM{;QVE;Ho|DjlW@~Fi;(j$z0>48|Ok%%X?3 zhi;RYI&%}Hep?sf@<999}Vh7}53J81JHr(5A$LPk-YH^v{J+yC-@cf2DtXiyvg@m6`i z+IQ6ZN-r1&#q6z|pC~XO8nxx!um1g2e)HNZRXe%LA>G)RH5%TU>6Sn=T<`vD)NoqZ z{k4Z&w?2S@m+MJ4devxNX%G!q2kNm+m$ZV*^;l zIn7ct(;YQ+omPfpY7phJu&6%WPff-v`1R(Z=F6i-6qyH4=n+GYAlIuVREjUlEXl4x zwH848K6=8@SSvF?43v$;?#Y|H^bIosyo%C4KL56=lNE$Sf~*mcm4|O0{7#lgUSmFHPSiV$}hCO z*`KJ@##dt05q`ZZoEUiUN+lxxqf?Y)7J{ReljVsn*E#~tAx(x^3TRuiuIp9=QmybF z(7wxr;BETk%oy>yK*Y^7^^^cfP(i|ir8X7VP>)<7H+R4mx048rrv58~fNOVo+v(Kw zQFQEF`GiD#dby;8VQ!wfk%XaCA4vUL9y!q;AHFQZ1Ax%VPH>? zR(I_d9?(H@i&DI${AI0r?!WF7ds!#6C;JzshBgqB(KdKZ#al~Nch7C074OTz+)K8& z)r*}_=2f2MF(wT2q01a+IT)3BAoX>9S2~)Se_T_fTc*f8(J>P1R?5`IFyqwy zC5HR+t$;b)5Z)I`%_*dKjJJ<7gHagyb+O%;AI9?aM8@#&>mjah z4|F89G;kk&mYFPhV=d-e!G;;U$Sym*fD^;~OzZH;ViA+(is-hRjYiewPFkrXri_9G z6-$P}UF2M7lXKIWQHmf%NFx`)6R7=o5}9S8tiDZOQmL_il;WS)HTb7vmivqWoKYQb z?*f2K6%n52burhQMQIfEUF=ZhL?&v4qZPLwG+9h2W1O)gN)W&uva5s17S{B~c6Rt@ z4=Iy0Xgvhl#M;V}%6Z(?TcEDSLy~It#e(KTlgA#T{>&_Ft5>BtxztcZNv1%e-s1W> zgI;nH<$``prLzJP@i0d&(rxnp7J&g}1^(jy<+&Z1mfSoBvi7F!n||gXSMWe$ z2S?LD3C_DCYmGcs1fHpl2c13;0(D;9SyMluNTx`DMxyO>I^%1%b0hOSdK=!idZuMG z%xs~`c#C^`zS*dIjWKG9vbx*0=yT!ZqG#kR@iR8|?;{<7?t_~V%d#EcckHO`{_DFz z7{G?en~pU2`yP)%E5LSK&NQ+e8MCk>B){CBG#of}RzF$gYwFl`8&Y0pvy$2rqVbsV z-yi_^(tp6qT0hQaA2uU209njPHC2zc82_T4{PHC%%KVG&Ess9wS=$0Xp}=!*6)M-q zwGg|4V>qnq^vAa9=7l-uVTBsAOGMum6LWs*l)=xW?_-vzj(E1$elI`Wd$usXt{m7q z7cj;K@bs7(J1N+1=uCvR%J$9rBy&?5&@gZ{cm@l2&p@xxurq+>bt zM5Lz_o}!xaTbL`JUa2Q#gq?313mGw&(}zA3!%9`Dp3~Gs<8f+d{X}v`0SB$z)4H-X zJjJ4CGyi~ULCUjV!@H~|YDYxn!%EGKz&S>!bCH0k6-~ptN=RZ>^VRD@xdAtNMzn`l z+XXdsohm_i25tBO6N=e}_sN)E6-QK-o@@3H%&00jVZMIt4bM*H!Lnd`O`|gGL}?vmN8ApE&lIXjW>kuZ9gu)2OP%!Ph|kQ@nh2lHf%pO zTJ$fUbH1g_-oOp*EzTd3NH|y@p-*n1MzjNZ*VGjkb2G)y25w}G#aDD6p89VM*#Gzb_^B1mj6>@$&i zUY)&He5qy68Bk+<3FCNR-dFn~w#{UdO@y*x9Y5wJg zf1Jxd)EwZ)3T8inD{=%zNT5nNDq)t z8cl|_q0Py)_0Umysq+KDLuuA9hz&wanz>`{XZsj}HIGhqkEZc=qSK9SS zD0(5xAf8&CwrQ8g^NtsY`oUx9yl-o2jJ1SkZ6R7B^PUEQBtj6^v z7EX-oWUZW2GY7?iob5gbN!0^=VQO0v#bF5RrV{UsZ^V#v81eO;aoe_jEDsII9qVwT zqfc9DpNO79KE_tZb)M$+@vP7v+NBPPXg)0~36p0T`363GWoBR7fI&f_J$KjbgwPEY zS1dWC)HSgN2wPxJTUorU=zc2(KbY4*R4~SV4jpwZQjB%(xC`NtSguV{Us|994Vb)ncaNPmrO`lp26{~q49&|(|Y+rKR&t=x^$n}N<%!!o= zt^-2l@2p*%K(CMCawVpqXrZR%)igP5lC^9(fyZ`Zx3#W)r0!^DT5&Wl?$qz-o~fSy z)|DZ)Rg(bFlByLdM+Ss?5bPF;#5vv%++f+RXX@8)+jfqdm?%yd?W)x6n*<)2wY%&l z8_bQzYQ(KIx^Lu zWpZK3zo71roy69HDe1~m#7NfFy;xfycVF&uD|8HV&6{iYRqAJ25>X!J(fxZ_m=eyS)q#V{#2IE|9-%1%CoQ@Rn^|N!EOspZa<*p0f z&st-ulnvi919irI>!#Y7Yos*xEp8RRIHhFj1Mn^fnrnT-=m-GsW~z7(dq&-Z|9a|j zd!EICg(kT!iUz7aN`%|{@aO|)6u5y*s-4qOz#o$dlv$2*oJ-QtG*|Vx3X0&>AWsXh z9HB;MxA{r_Je2_EI#XPlt~WVCiaiN~qe(NMe!H<#gh)kkQdVHF^u|%-SY3i8a`UWz z>o4Vz4DHRXyDKrYowDPgz`~BiXOYkjVuw%yxgqpWpVf*ebFbI?IU3>tDx7dgHo1-s zb1oo!;J6-R7FeDK8#$sCukLsT0Xm?m1XI@KW<{~@l7zsOiSqR*8Lr_g0s=eU=giAsor0wMfQe-*5c!GeRf`s zb#b4ZY}hHT_1rDqczJ$i#QSMA>yQ>mJpFKzNcHS$Z=~+5~*8`aCp4dJ?OdMiiz3*oBWq3}0Z$KXChFlS%c5K2I zMSjXD#Fa&ZSlxSqR$2tnYcZhjT6F&@pI8M&SbDnB&A_jFFnGIvrQW2zYPU@mz&jmy z7y;tb((TJ{o+8+JY-AeJ6OzJkqK$cefjruaH>4tJM6qPP>l5O;t~aEn(d_Hq49TCc zr}gKRc^Eo`IDG&xp|P8hJRMebkt90)LwE~Njv=Pbs7^2~kJgK)*4k;VrW zts!(2dnY0RKox}4vQl81(}uW|160juG6)y@_p{)?N%j5uO#M4wokbtZzi-+E{yh*X z`G1$Rl|Hurot#ueN(98|t%vhr=kL-N+SnL2_ZdMG(Nja4Yf2trK}q~ZBbIR?*!z^= zC+9Icg_@af$EL4e$_D0`Drs@dI7p6MQ#XM<7ql9d;0oW{-Zi&%4LUmJd{64U=xR4! zvPQYZx96W8LM@w` zE}!(jvuaX0fcJpz)0mj|gv=&^cVk9TM=Zu=80ZJ^IEO{4dLAjT4ATRi2UJySRufq4 znO5n0ic2z?26IS^ErgXD_)J~hvybNQcmH}lY5-)yUwOFx*QBmG!!lv?2}4CU*`3O> z?vk_HVyXPxw$6mOQKDFG2Qgzzv_Ul#Dx=*Cp9-yh(5J!z!|SttdI(}nOP~9kJ?aR* zScnxR@BxcZEEAhX`^IyJp(9Da}LaK;k7h_cw42yowDTW5b zaAVW7yO8oKZx67_l+n($AM0g=Ip-pcN`i1yByWV1PqEy4FkBDM{f(a95T|owv9n1$ zkoX~m-n~)>0!R+-2(bcG*_2l$$FJYsui`|>$AH3h6bn|Ye6bEu5wh$k(6Kr|Nf2l% zC)fq^lR=0zO|KIPOE^(3nVX_$BAt?Zq4dKTby?HOK?eGa4HOLQ+~qhhRf#jm{mO=` z=}|$WMbT~m+s8EWbzA`PV&2air_d{9flE-J@~JqzNf+C=8Gp}E7Z`qs5GA4u`^(jR z6F883k_p~LG{)^*7grrgeuT4${gEWnG{0Zw)cYeC461yA<#h?`9af}V$J!_sJ}tZ3=0tG+T(TR2sT{p=%&8#wOB*%Su4pVu2G2qnJG>ull=J9?lt9M1Y|5 zJS(W$HMU&K@jTb{77MLLgxJO@ntl50;Tbh}rf--MnS6ZS`>tU}dvfsM){pgYIebf+ zTjS`>6|5p)r#xfZ`fAw9C)J~KXjR8th17a+*4%4MVwJdng>EJf(=xj!#BaiG7O`pLR|{G0n}0kTDS&Dx&GB$GNW-YLn)5+lHfL81d8SFqq*dtkB}e>6|~seGS1b|bePj!%S(p_ zd(V7e=-XO+nC%r|l%-i%={PJgtF5bz4!9BIJvtaCf$eP6c9i(W0jujD!Jlja|6rnW z9-*N*$oJ+Svj$Q$=7wRw=2|PdI@V57BoR0qTbnDI$+X&AtGc8S(G1`Y@AuH}0O7-I9}Z+wxUWKmSkS69u6SHT4}%L2 zb6H2K8fLybLYfDWA@G|bmLNQ*N=Qjh#fDQgTF*CiWRdYpiV?i(M82fAeyHncQUi`N z4L!;QkpeNtthGjkRpklPbYAT)IGb6OcV$QOg3hF#@C~rS(*acsg_K-yG0C5>=1<~G z!mkPgK{ASMP=XPV7F;RZ;wCq$ObJzGTw)D7%4Lj20myhM*Y`X(y)Nz;(wY8w#K7eG;akPFv`8C(J;fV`sIM>l&Y2y%Li@IL1|LW*M7lTdfoqBOZUg#{a>x~ALl3)4<;GucA@3dOQ!M!QL`+MR{i$N=VIH!ML3-fPgJbN;@)hv|E( zGsAz02K3tf`{NcMmcxYjGEqH4egiSR+0!nfv$@oCM&{$NAZ{hqd-y>v>(I*^P`JR< z6=4Fx!q{Yt*z_+i3d3l^6Sy=Do@Qk4G=djx<;%+>cV7v zivm)r5TWpZJLiC)MhRKWA+dFakrR}K5p6JGG?D5Tf`ujnT4~;Vu|PWM$l#G9ij*!| zqIIMXGADlYr{xZ7>0wkCoQ{ib&CM_QKij~@xq^PVAGMCD>CiEL4pd?stC8uy{&?R}Mh!DE(m%x=L zeIdETw|cMbq?=)*h${{eDK-e+3eW5IGOV_{07Bbfhcv48LKNJ@+;w~!vieo-fxbH+ z{<;S)efLMIH8s5OHhQz@P9rO@aF->ihUh?gbc9V~mgDC`yqlm_V`Wjd|I@}hi3$;o(-*(AH_E)OTxhG}OrR;cN zBynheL1qBEFRnB3D#kj2ic7?lQO2%rmuD2|K^qL^?^*XSOoA&R*dCk>Ij^A)0n3?m z&J3`7cw>`}W3Gmce9S3+ysy{D?Wwq|z{4G{rY`2nr9tmDw_2YnJyCaL&>d#P~Wi zJuc4CF3*`sh-;Zh1Eguekvz0JZ)Xzc(HkeVW0;1-BZY_i3pefi>7^2eA%7OM+R9NW zQ*IR+2Jkmfp65hOLVE{`s#aXL)7kYyw>eGAYb&0R1@STY9BIIGF1cBkWn&Uq*kQ)C{&^o3PeksmX zw^S6aV-LYoGf7rp;4Z_Ewx!*LQa`S2dS@@_jPs-dk}k?7O!AL;%sQSvdR z9C1HKcZ!foppzw3kXBiQG%1`Y-Y<++{+ujRUj>{$)JX<7V7LO|oVeSv(ucB44t*;v z+8vE!{4Qlm4|i5ql$bve9-c9c$?T!9y2U5rjsf4=em`2Dy&v($?<)fc<>9R)y=v1n z8+>23v448t-xp9@P;2vu0rqFp2E4{Wjur}s~Xes41T2{{}8=Kcg zs(vjxrFMV^RbNsTIaj6XNWo(aGE$l$N)~zj!Sq>a7nRZn>$o}=+yBi zlIP#_6^;(A-B+D=F;7hN5y>xoCGG-{k10L@IPn`n;e*5ZZnd8T7C(E}IIK>W_yiy@sK=g=XiCO9 zcMkW&VQ(!$a_-;aC1UnC+FQ@TukUstr9!Z=JndLskcCzrm=b5z=-ub=r9xieEGRB- zZNGfvUI|?nHCfuN|0OkOD*Eu%SYwas-smnY-tXE6L-9F@!k? z*Lzkx?rFWPtK(5iLLFX7@5&1Tv4h?s1c0k%aNHKtsK1-8{O7CZx_v_KHO*XtHy`c| zxyjZ8ku6>gMLBuR7!qD)9}nFD|2Wc-+Pr=Thrv2JNL2}@AW0TnJ_>^aUfczllh@W1 z2v%F_E=)L&3eS=pw_^`y+1i5On6PzQLGqUB&N;8*nse#9qxDvhI4KC?w#|oxgTn#T zSlpx{!ldu`l=J;)>#(i#8O-=9msjgaWylhd(aBkM(x^f-KvrvlG5(>PkaK1lftCC` zk#^UKlFG%r<17OXRFC61MKL1rTF>`cGp$BHU4;T-crnXVJeSg&0*LO%=0igKiQ;)j zvbHz%fW@Vws0itIm?u>!e}0`#6EXc!$>Mz|3fgBIfNCYz$zqvG^}UL#H&z*YbjH2d z$`c*c=wLr><=Y^z18}hQSfAo*z~Dhr+mE_yT_%?fsZ6*8-_JMuMiNJuCPgW+d3%6F z>7wsd3J=M+Sabmo40+3LI}%hBI-cU8x?>k}9$vp~* zV4zS+t8JEPO~RB7uhSb+D&C?<8y7Ie^E01aa!MGh>r*QF5PE2F%Q<)P*4FhK_J~TG zP$Nq%b(Yh-5$#;hMG&}&@wr3fQ}&l41EW0f13D=e-%38MV%Si3?#Nc!=o<2EGUWAa zr*qs3j(p&P-=@g&TxoroHl9@zDMq9aV`(q}#Bl`kcGZhqYQ|*I@__ej-N5g4@w;UG zN#&)~>D44W77gv|>R=Iz`EY6&l$#9(l`d2R05Q;9rg9{CZIkcsYNUyoYcf5h_kq4t zM-&`NH7b^JzlZnL#(@bi1*wcFSweZB4rO_v_3z7CdLmHhj#8(u`tLAv>S`0hHno~1 zOqdBo*5&|4rC2Oiyc>7Aa=`FCT0_SKP^T-g1NrAzBwY#8AQBEyG&Fg9P6}^IzvmZ2 z$CI_hFGgZ|aE6 zjWhSrOvo5ACqT-EIgz^w4&aq5)m;#vAvMg-o<%lyoEHOGub$w(pgvlZ;!5e8{rJ&Z zsybE5ikQP_k{GPxTLM#dS8|^V|FY9RDe{aF7)hx7`uV0A9JkS+#(-jGWT~NeYa8aS z{9bu)S$e8cYjL{IXmD)&B8K>jLZ3H%rDCih3jcO%{oVn(iNJ=BSmOpDhtbW^rPf5y zwRGVuY2J~dNzqFzjt)^HEA$}-$?{( z?6_@{owK$~C0djy5-EJ|6aeETpGW?FO*%)j4WxoaC&4V*jbR)C6 zP&%X#MeMw#FwR~2!uWpy^C9&J$b9ifrzXk}r~;GG^y&NYA-UL`n8_7$IhKjVk0hf? zOIJ+=;OEQmwE^fA81~?{7r49z&hf{sDUZM{<6PdF#TMCn8AJ+i-tAR%)gs}nJ=2m; zX=6iRP2Ue`X;nQ^ESk{R!$DW^uAA#&%SK~w8|1cIMro#X+j3avugrw=e$D)l-3LvU zYM}`GF@TI1U!U}5d+K+vBPKjU`HTi|+vI!{Z4bpA2@-bTjt||^bIyHzNmPv@`eB~! z*F~m9IZYA59rwSU_bUGlm7_aphkhX|tLgou3MDg#zOPia5!{O~y#`_SG%<%>r232f zQ?_}1&x}^3g(->;KTWe&8%G;0%1Zlg=r9B_G6GRE&bb8a&H?g+_U}+>!t7d*=xT$G zQLz&{UECReQH&4YSDm!^XDW=KJ|Lh^Vv)|>{J(pdfk@$wf0B5#f6Z|0?>GCxmJv%% z;SKwxW!Atz<@n{A_lICQ)-I`{V(u$1|zNUzCl`NuN3nIemP$=i8LaF#NJ2(9z?< z$oye@$u>qx0qL}H{nX>&cU51@`P#N|$kl1pJWEM?T6re4e^_ywX_z=cxfks_qswej zQ+c;QmFjET#s)&3yjZGCulm0_k2jVxF~Al{9sTMJ4r%awURDoZFe-t}2prGZ;<&D( z8`rD3M~@#EZWQ@}HQFRbmGCxYMnMFDt%l16B_4eK^_bX0PrycjpLKM^bn`P@R`7~$ zfFqSYBs^IbH?yn~$gmXTP}bKEH3mfgmH&_@KaR#(n&jmzb*)DFxfw%^l`n_oA8kL8jFy976l@;V zlaD;L|9C+RJP2Y5As3eZub)FXK9Rt9ML+%jZt+6EX|e#vkJ3xW zMs)&}6ToRi%XK#l7f9J7afptKb-xBA|AX!R2oRaj)-{%NzAA(-fsP9r9u;363;+gY|f++q1M_8vN}+pMD$m>i5MF5O1$aVD-}&Z zG9$^r%moQ^9jPmiT6Stp7P=T5#~9lVwk4^~{(RE*$3X~B<;@+Q>p{o1Zhc=4+HT^9 z&U&W2y{^#%$>gxoEp3^!hIBc6Qh81@f}oaY;!&=8fxU@mmMBJu93V3|_|`XU&&e>V zO_29C?~z=*f3RvJd;EhkL!JdaFAX%~yooUeFD=^@O}XT3}&p;+|Eze~X= zNptqLp$I;Y1*G3Z6{&QPmK_zaaP}L5xFS7yHi?eY*~s4lCz%cWTC_IhMaz!dz+R8f z8E7Kx#M1BOWHL~*jxc&!USz-N{O@d;gtEz{dA(7gtIrmMuO5&>#*H}~B z2VHjl#3dbAHWc<#ra&ST@p-yb9uujfVRTZ}a5`B;#I^pcpn!O?P^c6vM7KPg>Hb}C zB#{8rQ^_4$ej@QeQf>H!Uj2S3J1#h6 z$k1V;X=8m)?R$t3A2V;&Vhu)RZy7&);?xOpP900UmDGEzOQ&$=X=kvSBD~>*)??xapdF&I=r|n;S7ausM^f2NOcOg5emAa{Ce`5~-$1-C zh`mH9{o+s`v!jEL=I1^Rky@el@UT435zywOWd;B`WXM<#;IfiBL?qhT2o?%2heG3H z*8vxHwxr0<`KU%Q_3SX>SS{8P&YF{AY@#W8t@eiNBieOHDGOCrdw*8}m?28OdD<)3 zi9X3hUU3|e=z|`MRM&M-@osl+a%=~i*%vVgUuFKQadG!<*EclL zG`WD#?nB+dQ`SYx(v6?$MzY-ZdQwT*`(lq6W}BvaKx;=y(r@HIJLYp>B1!nfF*Jp8 z{=5TCoP<_yhc%@BzIEa7HHFB`F?8QG%>6t6IN?QbDihvDbcF` ztH<%A-JSM9_D(8~*Q$_4W4ZcDP{%IM{fe}<>XYh$&XEa;T$Ic1GX84zKrOF_nuc0} zX7M!T701G&pO+av8{1Rvq*Qhbil1soZ-s5aiOLaDdlS(C$r3v^EfHe4Q+gP|;vwKt zyvG^~iwV;!xhmIMS@ECGD^8}ed>>s~xvRbcQ1kwX`~7Bw90%ytv0bdNKfUdLlGp6Z zWC(HZN}@Pr2;CmcryioPP5Ac8ZTKl<(N7%LA^lGOy-_m4hIAXZR)0R)ZtV14ULQ62)b}< z#d`E_yMvH^`eyDzxbpWdKk6`Xw*-}VGf^hf1UZ<0@Y z=pne8JOD*Ny1z=^B_&}O{n6M-jP6zqaJVJ{3i)V!5MYcAKKRMAu3op}^neZS?ExMn zVw46S??)EY>vr(7lfHm&#jtQK4(B(O4a%ZbH<(h=wWf!B4{0crF@X?RnWtK!Ge z4y)Md;v%a9phiDPW7&>%_eE<*pS*0zv&p$Wcpo-Z?lDAf25gg)K!;MFyKD5jYAm(+ zG#xtNVRHk%xFTAlTXWgych_i&enT<@iX&h zgER(@!KAizW1|S~r}jXNa?QLH-3jMx?9gMdM*p$->2JKIJuEq%)?WfP$`$jHg!@jf zVHkaVd1Ina*(TAk$|u}Dld8Hw4Z0i1a5t^cJODQ>3>YGGF%K-S87&?y}TJ+xC20Iu%3M>XUa3ToWgS7Y|`@C{+F^BtviT z7L(Sjn4i7um)d_$*Rc~vp99BxDRfWseS}t)A33>{Cn%_torD27qOm$1ZroO*?G);Z zgZIxY~JL$7baB zwLx+qA0Bgo*$1Fan*kySQStn9Um}f@1 z06IRpT(?_^8-1hShjtwJ?G%&kT7NEtK9tey$t2jw$V}ge6pU>;_XZ`x2Q^k{cGq#= zSZEw56c@S>WjR>k`A|l43yge5TculP$Sy3$cl8muim-aRDD*Gq=XunkLctN7&vL2t z3nd!{b$%-eW*(nlXXh)Dp+$R=O`27fCiB{1>U`(8=zg8pwAZuxjg9g}-KiT#p9@F1 zy4)L(AhGFq?oV_-9--?wkJZ4Vll3!PFu);`&zYmok)w1NcDc$xf_P4BTJYn(A?!L= z0a3utC+*araculfJS`o5QMuuxEo|N6f zpNl~*TPVcA82jAYE67iF!1B5wnZ08-4`83_Fe4z3|SS%6B5avy07@#nYeh5d{ zd`KdPAk0AyA~IT}5%Fdfx`eWOY^>;3#&KR39Q-!N_8uqUEi!@^=EA}HX!$J`tNs8i zi3LJ{90fUEuQu8Og+9)$c2o6-UO*>SA0b|=@8U>ifh9Vf5iOl~p2qc7^&1&)y{q}B!izd-=v(SuF) zg@_#FSC1kO`J_X14>J?18hA(K(ua7bC4Wuo$bR}#A zTv8kwQyn13(mP1?L>UHG?`*0(a$ScHC7=1&n+&=$nm?q0Dl$5j9@)xc;+A-nW>J2~ z<6r)Wycmj~YnVm59-xO&@^7DazwPc$3WZ2@lBpYt1w4-1%C0WLGO>1H2scn_G+q~w z%VW`y4Wqk2_h2Yb(k2T3v$h}gDDhTv1J`?-)K?+WpEJzBE5kX54FUxTGFm~6$aky` zL;_2q_9`K`s^Wlb6j9geVVpwVK$mAEvhg)Wn!Gi3{h-0B&xrUMD2Y#Mz@L*}L!>z1 z(!1az)H_gpGe~-w{rNbb*0(?ra6R@&AWXLMRT^xILrn5&FV@xwq9q6i+K2`_$^hXE z9=#B1vs@8cVleD3Wh11csDd_L+#hnKR-2wBSjdUqtY76(Yw4>kx@gFSZyw2%^>woXTCK}Sj$2{Sx!sp-f~d3yp_ zieMrD>eRhw&}RImB4@rEVNNRX{zaUi1_>VcUQCxbyG(e%{-NLpHiu<<}m zV$!zimU$raT!>W@Wj2S7CLd(J;cnQ=^Da6dv#q)tK8_NtZvT29FSw)@-C}Int#`KD zi^1yfJO33V0DaOO`ByiG7hO+ouWdxeI;x)Vqx2?Nk13luF8ri7QW_G7^W>EIa;j|f z>hao$mFj&cTt2b|(m*Kvh0B*>K)IjRc$&N!@>TUf9H#0g8KoedV6En0y=b7cmtqkQ z=MwlxQuPgGLN1xSt4bC~_XKs_dMun#%5B=^S_!D{k{KUR&MeA)RuGP0<)#{s8=v_t zCleO}BpxZHYKBVer-Gu~jXbN0T(tc1xXx*#ofgv~WyPzE#zz}*ux|^#M0+w~ugU%d zx8kk}AI(}hR= z&XNm|Z~$0p5nbxrFHy4*quyY&l`;Hy=dBmSo@Zg@m?(RsAR0jD=*4U=Lfh}q5q#6v z(`Ipw9Sc%dIPIUbXmiFhfScmeZIXfh%z6pMJ>Gv_|M7j}t%vL_mxgM_kQG?HzpGY~ zchU!sNL^qR5B3ueCXt=*f&AtkeC@UxE~8Oevv>*nWVQ966507x{J(lT=$gF^Baqs- z(F)L*6%@v&S*IS4LUvM95_hBKG?eUniBJib!QF<$HhT!Ji46xk!b8vn!@x5|=KTio ze7NfmG1W`H%P=SQ&Y-_VLECen6^pAw6WpaRZp}^0OtuGK!Ar!}0blN`k%1WgZ4Qkn zMu423T8TZYJFGT}Aw%VSu15Xc5pm=Yl`sIxOe;;6_o|w*_9-Uv@_Kq$zxQ36H~llpWJFZ#}rCF?1h6zQiV#K8<=K z54Yc}aGJLoTrze@h!QjOU3^UKsI}nz6<`eU-a}IgWm%V0Ms1`L_m}`pPo$z;CR%7t zJi+uXv3;$Uz?|e|4?582((sYQ(#X)AmJ7>C(b$)oRoq&Fa#A);em7ol7TFudXeQcB zdCxc&w)wa(+6<4Vsbf>--1vXa`+B#T&LU)hHOp-z+|AEBGyV85gGf$(KJUEcub31} zgRcm&WBk|0nj6k^=nBvp`oF{7ulE^m(h160R7YzA|H_nc(*}O;o#%spo=r{9&~k^I zy@r3rjr&#xL2QH4fVu#9sC0PSeRJWx2?@YiH^VtY3YiF4z%)sF>CX!?r)Q9_mGsG&?53tb;3k(*nk=B2AYOEUKhlQL&SGg= zTzS2tnU;cttH5@UX}ce=-MZPZMQE}BOM$$u7?pa3rC*>Xwg2q(j_1}&jBB>Y-u-Ql z4SBK&=S<6$H0!$>-u!%R!g*y{Fr20?cowEx7k;$)BfXwCY5I2w4g=h6Qd>X6!{o3q z?U{mN4f|w)2q)s~Xzv6V8T55Z>ucp7OS_1+w83xarBZg$h#wi0q(~w4QON}P_@)yH z#)=4u>`vt|IPOn!2Cv}StYE@_N9We`bZV{h(B!?AWoJEhNdfTO>lma1LI8AfOc$Vt zAeTc-=_jRA*~aGVVJm~q1c?t=Jc9{0$w3&aOzxW3s-hS$W8&}4G(BWiA;kki`c77?2GNQT)5+N||{Lma2n#9l-aRFF`HxPB8i zZcPSP5kcoBZsHd7y>Ok1Fw)z6jCeDiRc5f*29LaZXNC&%&_-af1yi5LC2wU=ig}i4 zjk|?RQmY?Gyej}*E&}A=yS9v}katzKnsT}7?{#lQZkW$fgEZI#>m9)Pj~fPJlkR`v z-M~vzO5>*2P+uTUV@JZWGLR^7Z}qOBX8Or-rCSM?;rO%2fJ-9#tMT<($RK@)b~}8T zTMm~aj0c2gBgjaMM*h(^WqfPiO_3k4H7;Dl<89a%OdHRWw>eFaIlN zeEsWYbPdh^?ciJDuBVNjAI7~EfwDY|G~$G0E=?n2CeaxOnML-A5Hu5rP31l^8W_%7 zT6BZjM_uv*bkKhEgSa3Ddr0!k7zUf8HGm@$!nZ_fcD*3EPRo!PsCww|RtA(YDVWb^ zUPgT!cot!hd%!}T;t0E_HF%#Pr<1$m#P7lG;5#$0vOvL{o%o(>+MeZ4m*y*~L=Qv> z#1mQc_jO~4BT9VVA$6?PJX|JrI?61pj@Gq(SSD!Avo_RG`fRD6YZ@pMd1U2@qUP7N zeM~0EG}eY9CssDKmHik%I`T+cKWb2`)V9N*G;b3v&WN<;1o5^b1-Elu?QKOK#!cvk z1E+Dbwjgv@@oWWx(MvI`bYadWgZe>(+6|Ep4Mg|OqqHO~=xwu1dw>DK6qtS(^EFU=E^5);^1R*3Ooe&~Kpg`lWJ$`_nWzJ`sgpK7+T7Ab4}Git!I|}{s3siS zQk+67IP&uK%Bw)F^s|%pSY9-PKRxh&FDtMUF)iUnuEpV_mQN$!8Mp6a$oA!6RdKu4 zgEL7Br7*l>iFp+%wTF7&fGqW$+aj;L^89jZN6dqe4zr-6Bi++qEZT2It{Y1$)i?ibL`aKD692 zY0uJ`nz54}HrKid5|9L_Bb7SJ>Vk-sz59~8UFVfp?*I3HZ;m3j=q1Y+7m(b3QrW6l zy5(8~PS==Kv)5V~U+RY0lxlhES+jVnFl5PC2;yT`jYh-joRBSoPu1BjkTQnmTg;vu(>tEe38y(Gb>EObyY?2 zF5^R|;;?g@=!Yr~5)TtQ!omMabHKhsou?b(%HXgG^R4GOhq=-meAppdXshTT#MoBG zTpWb?z10q12Z0V}6_wK#kRh$w`;R6KyjwBW73Ho=%79eap*ELozwT_62Ra|^$M|?w znDaiKoL(7lIu}_HXkdFNjQX1GCC==flh)+VhfQL-R9hVO(e+t8jup;2=lZ$fmTV16 zY@6%7P2DhGrqgK~OuZB0!w~6rjjJTZ`;IuYRniAB!FFFyCSEXcn-E)ORwJ*yhPEMV z)xC_#_zcj?F0wUU@qHtXcE=;p1%xF$5UN&Ud|e3$`}Jv>)|)(-&pD>iSo}R1ySUqb zI~0r4WY}??4U%q6M9d0ilivttW^8|uieGH{MC34H->QMra@1{2! z0TkKbJwfq}s{Wv0N`xR3(T?ZsnzFjjp+eZgtvBIx6sB8M(1mV=AM!E;jMY(+#3lZm z3978}zAw#-FmemafGjwuW2zMCGm^N8lObPWi@jSr>Zb#_L6xW~AqZFee2Te@UT=aN zr}VnKH;vwqgYW@;7u<2kh9kn8x-|I)C$;Ifh^uQ_u4*Fp*Ymup1b~Fy96*vpUzqbD zGl(cc-+=!AR?S7b+uoS+R4TR}=-tUNuNbMSepp_usJVovdQ+MzExt#64Wed-Q>}m( zlV=o`if@W^v2MZ-v^H8-N9|^oyq-YWOC*xYLRsaF`uhvB%|+0oY8<>vjlIn_hN!f6 zL@L>AGqq5mY7qRR4|L*cbIwI0^HUbqYHD%3_Yt+HTFf{gB05Dq|ESZ^)&K5dcg6m1 z*cA&?Zff}FUpU8anh(^beC0xmu|ga|!ZL=bEvJYEO~I_YEO=U8wzVZV9jFVh-Z8@4 zEm&5221P@rU}i3gHrf!IC1bwZnQ(<37+?x}lase-7y%z_%S`StEYorH#r~oc4VQx1 z==)-oTFs4?s1>l0U@5%oKPN0*22)ijV|S7An0{_OwbhK1YA_Y!2G?vN1mS3u6!Wlc zs~6%HPI#o@CYp*AH!3<~uI&~SS(2;FWVRbkKMJNvLS)UmgO3vgidR8UpRiDj*}LJK zWnww&E^_v0o4s~))?BYMFKn^irO}zHDOZ+)=G}_>6r%q6b~|6*#mZVgGAE#^772R{#^i9%$vOsj#E-T&71g8{bm zIuUSfgw*KGWr5VA4Oh?OVbTctLXa2<7`Bck%#*U@5kIvP3M{h=REzeDsUsEnFHNg; z6{8DTL#c#(MqY&}2o9ZW%?w!GGU%Mnlkq^~9W?hq7`R}dm*Sw9VZ)C7aL8veWs?s& zLOXyCSsL^q^t_zN_i<218-Ym)ZqW?n_LTdwIwGbkwE-IB`+88?#pZHUK zwY*~yAry$X(D$;k_8!ki$MWzPB&pN4`Ur%Q7BCVg&v9f?IZXp77&grG*ysBvko-kz zq#v0mT5wW483E@ho@e}-h!M+C?uMkJ^(ozS1@)M4xZ4hFSYD3P5~&zpQe>D9G5kc61yVc7#%`21idd{ zj%Gu93)ykKR;95GVNP#8sV@Jm{#W$Mwp7el@4w%th76BHzNHP`y+G$r&RTRcxTdt^ z;dX15Kn%g)&pQFo6&Fd9)DloJHCo&(I%$1Awo_T5vZ$}6UuyQi*y5mIYV^?VSEu4r zLZ&J01s6WVOswD_jO^?vNr$TXzKqz}nLSQWjes;#@K(3XOA6W<_d1Eu-%64!8~Ttt zY>MC=sfOt3KJ+SGV>_=U0Ut;s6B#+vB1240J}LJy#fZXIRa>uw3d*SovD!VSSXqsOE+;72pX4!!tK=y z%e&W2CUTX!7kSOWYpH9stCPdqlq3M^?lOUY`+(wkHD`%d4>I^+m#NK*e&-Q47RxV;~#&!Lz9RTAU) z6MG(8ytd6L;A+=t^X3H)%>IZI_SfPo_c?Prh?qm$?R5?qM6{2rkm|0Z+7Q+iF;=XN z@+XyP>rZ|^qk|>lvLzmYkk^Fcp1Q~u7#G`PbM!*+6s8zZT^_jq`$1ga$wG5Wf71x5 zd?=CB8lW4fIDTDC%YR5;hR7-MA4^y@ck<&=0C3k+HyM~{9U0ah$@%S=|Cb^+huWu6 zVPy5iGIMXpBL^y%48cX&A7$-~vB!Y?hbYotVdVF=fQ4uH;#Ei^v=RDlNE z52U%{GzCYhFz1yBvx>Acvh6ys(M7tJZlNj?&IKPNp(W$c7Ju4hQG;nWPFTX##~AzO zD*2MJ-MOO(6|SOU9YdQ=e|yXGh|ELKl`$13XKqA}b6DI^SXrP?W~Ikah@;ExN#kg_ zsvfD0qx1W%P%7)3P3B(oX%GH!8^PoxLN1uIk*7V(wv02yO6{2&ZL-@pE8Xc16L~UH znrsjwN`lvC9^eb+wsFs_(01Rssh9Og#f(5AFcUJi5h54v%aXlV7K&e!MSF6pIKvVt zV6q5i11}Y|Ppm^$(CkfZ`!Hs=>55rOmGdD+tF6t%i5+KjO94q6dymERAV)~zCby;# z=jvLXVChzVP9@q9-_A1}slBA`zc99q^yw)dEWNqOvp zw;(@XW399{LN|khTJ537lIm**wTs*gD6nEZip1vB?OQjX#Omppg^6+B8drfM>!)8S zK%1KO4Z>t4cS-iOXE`ya)BAH87EV5F z@D7y?2hL`CV2^sg)iU(^7~V=i$NkAgSHd_b@0D=t$BX?+`9^M0C8ht6Y?ziA@pQcD zRIzQmX8H$_lqO~J^k`)tF8m~;Ik#@34zS&hqfQ}XL)h&QH$SxEbnz|4Q|P4cI|a@R ziU_`aS1JlyM3-C-45`wqwWetN90KDAP`C4`vS0NcTg?A^o6Pe-*L;|tR^Z&NzlKx7 zaVPWttO>6xcC+6>FTJdqlsaSvg>R)%GuOmv>el0^r?~&VdQ8~AIn=&FDAjFwD@!xR zr(++V&7qnyr)3!)o&=Ov&SbFKcc}(purbWz&j=B)#B~vx!bT4j;d!PdjN3gpZv-%4 zqKQkpE1MMd!L@pOUBxExX(8!!iVb4D^|pw3E-+t7p^I zw&_}YzbqTDHv2JsO8+Nmh>jz!w~Z1he7$|vX)9^o&TpcSgRhA<+XKhKKjoMpqX=x9 z0LkCUzV1+CiFPgW)G*0Sco*J8#FouXb}1PT%$}Za4s&JKLaYn);V)l#*7NL&#O8&T ze2*lfCF;lYeJLlOU5>UOrl(s+wy}@|1E*b%_;gpX!t=y9(HmQ;=u~lB*(01LuxC_< zb5A6i-y>|r{&aiO<+D1C)(FFA{pn_x(qEMqh+{s1Y)4=JCx_3agNq}lm0etf{`YVn z4lviZD|`ni#nCFnaSmLg1BQh=`$WT1(QMsF#Wn~0}0TpQ^+k6-;-3Oy28fjHy z%8j|jgacq;u)2dCq;k3!6XEMbgL*FnC;p<&sKU-*WN{pTU$@FX`)Gb@x#DMhWCHlW zX<&$RM?l4n$gE&+v{#?p_Vwsx9~KLMVX=KP!Km(Fx`^7+qWp)NNV8rdx4uBuj|aOg zs+^B-3RCB?`!n0V9qzV@CA>Nyc`n4qvj}{4xk>$9{XWhvKUfL6v2icfe7y)18&#s2 z^kdUSETd{w#OOJTS*w3?c8QHz@Zj@sQ;+3#+%y$En6#*(7E=^h@qr*0)ZqPUS2Ox3LdbK!T*`PYy#g{&S2#k( z`oRe+jZ5n5Zmofa?7$p&kdUMeo>RF3fSzm2zG)S8ty!Jj|@%u^qTcBsp0f zbY?Bu*w`bg7!5R=08?Aly+}K1qKtK$>B$W|@ClCdQ{>-q1 z>8e&yjF&lf1#v_Zf}28}9r8f5!PH`=2*Kl8{C_))BL*&?4)(nkMT(d)nlMHe&WPH~ zpiy*9>MCcc0$d!P;wH|c6IOOjQ#9|MZ>x-_?|@hs{2K4&UR z?;0j$H85ceuAyyoDaVKjGzr)=&*=v-g6#~A1^3PDpxNQK32pqDgP6D_9d6MC9dO`S z+h`lKYZq*#{`qV6aMAm^(9yN&q5~EcmxjkmtNY3jLX05kVS5{DI+Ia2?)XV7#}I_Q z8rCYJm3saZvp^~t5WM#pGBzc{{izoLzu z+YGF(i+jHj#D;a`1%2g<tyJCL$Hm~*~}U% ze*3D=6sr5~h26ILQ>iQVC-PB6hR!#+e|a3v1SiNJ@k!tpto#z`kbz9DH3Ec^lB6G9 zwGJbDsQ-jw*!vAaDmks9-vQ9-Z8QuH`!a^NGobApp$^xlLES>@ShiN565!Co?0T*l zWDt#dsO3A=_k7+aSzVtm(WPN7E+m~=rSs7;;ptPlT8(;*WGpvgTc}s&L1^~$?@j3H zgj+`8S6ihuul#H}o}FS=0a?3qxmWb&AaKRd<~ctN>K=8oP$R&5l?y-yb_RwKsaJ!w zEd$ejD1h#0S4MOdePCcRF<}C!OY}2-(DHGsRI1HqeO9JEUF~5>{pJIQ%4OWG zD|Kt9YdxN7cT3z#VYo>Q=ib<`Lu|X@8sGN8V4QA0$kg9#>^sbSxTes_lR*jx^Ty1M zU^wHjswVCjF$T)-_NT=Is~b z!r=Ke-`u;4jYjKP`|E)@Go&ZyUCn=y?FZH_yK^(&i+g*|2t6bbW(tm4q4Y{#E5rTX z>{h*XYs~dl*ltNWe5W1P*K#s~P#>sIE$@1!$mO5$gcU=? zG(qLdA1OOS&!(B|yAk*i(B^Q%%Z)G2^4l11#^GYB_tkRIg4_XnUBxN!V`sX~)uunL zuDstT!|&9L(A*#;lCmk<0p-<=J#eA&V7s}yC43xIbuk9*IQ{K@I0+xWkBdTrtbT0= znbPBO0EqD;g~wIzL22>ip%#ml@M-;_k?u z6n~*S@xM@JXgRDO+H&A-o4QiOZ+RjGlXs<@l5eUWAy0pXArcrYII8UeIMy~bz7*zn zNJly$)LfV|A|5V3Uq1Ok6AYxKALJ{x=VWP@hhbIg{4NBIW4jIrm(11;o^%8G=-3MN zj%#LZHVB-ShaN@6w>}_xj3Oj4V;|@i1oKO|$Dc}RAN4>3&c1}Zj1NC zDgdt76cWSe#psRtvR8e7&C)qoS*L-b{!M{Oz|m}z8tVYtz~ZLaMki8%gj{clWRz%XvbX( z+b~Y$IL$6{!dhZ^r@To5H+nkaEsk450W06IoR$D`W*`_{NxyxXS+~H#upBo8&so+> zGp&M=*YcZyKB8kfEvrmXaHBs3rqLr%Ok(?k)59Ks6VY;jtNJkEl3i^pPy~n#y1pBbXFs(jtKFFbtIBm8hALT!F z*aidL1qXDorX}E`^!q#3L8f9XjUP^%*?upHrH)$S ztA=7$N9|TH@r=jkV=#IRT*Fp$F#E+MM6wsl)OlS7ODr#3#_1CK2M}ilryqpzL#ibB zKt7rx%lhh!6@J1U^)}P8dql^+4kAMtmL;(pJc+j?EFN=6+5_%#?cDJM zvzN0HlhdxQn5Z?SESX7cX>(ty=r8O=&d{L@@*71 zQWdcWr|unmorAj0T_&Ufv*3(sqiqT(YF}%ulub5y0#LPRR?l6)A-t`mOzDBG>N0#` zz$kYIT*yVy4|e%{4s7cEfYLnBnKT|>Sx@#FjoT4^DwvwI6K!8qSHQrRdmsp;nr5KK zH5eK%^$s|g{g+^KH0!jj19(wSW=bt(m2%myPtRp0Hfp=NzHPa`S=W&Lw{!ChXmf85+mZn;foP~h|fOn(<$Tp3_N5;1;6UqXF&_vzTX}@VL zY;>cZG}T2J6?-iyIsm%W6AWvMM9=d^erHC73nBThBAOW1t>v)$* zhj(r4R@Gqt9sam_+4n1WJN`Cmt#aky1^C3tiL70WN>sJIQ!^nMN<0c`_+zUGbpvV? zxc*B_#o%|Nrq06EMoG-luE_TwF@kT;&xsO_qnb&*lvOJveV`}+5r({%bay*hzD4v_|I`hS?gU_CY?*)4~)D8Peea1R^>sPP;bcS!VX@K zR$X=b{M(PebFP)~ZfSAt>ot#fs)gQxa)V>PO@9kh=Vj>ihB!wu)sNBGR z*)H<%FR+pbA-dpc*B#vz9sNlY)s)qr4pRT>rcULt%)9m9zl1IIO9pRUSi0U1T7(fw zaFvzKxDM~*{s62hewFIR6LW(7M$^#07(~~2Y4X|1KMww8vW6r*6_z2k00Qv5XPN{pTde>rz&r)D#SUZ8mWLPV?xG9z)!iq*9v z3PITJK7RL9j#LRRAHWIwhdalM^>PFMjD1a^mTEuQ79jiXj}P0so)Z`{^3m`Re6at* z?*qv8W)C0uN`ht-rW?ifO_t6Ptn;XK&_So*D5>cuWV3jkD_^cl>AHqWgyCWu#1WKY z{dp-8{IOw2g1f-9{z-*DJK3}8En)rB#EdW*H7*4-4red*Y5om!g_Oa)WmKe;GYM1j zQP~jW2 zS>cNyjn2OU+2L>eQiCU>A8hVReWGT(GsSl)9Q!p6&|~0R3SP;5r!mVp&9PFs+-mF@ zqz{0mg9DBD&iRk#H!}Uy6+O*5x~`ri21xE{@szfLk*kk~p=n-i%#S1Wt2&)7W)riz z@p9chatgNFGx<0dA-FETvi1(YJNjEQi8*j~YPwn|;my>7mzRW$QpPPxEz8P9r|g2U zc%=PY8>1k}J7v_Az0#}nR#750yE}M#8zW;emVet%KJ+th_S_&-Q*k~s0Z8rXLEgo) zHA3qEN0&j2oxZ1|l>2Q<8tCGR64;@9@uOx(qVU<>_^XZh)>|C5uJAvs+q38IH&=nV zxi4}hkItOvy^pxSrIlj)9=u_BClKhS(?gcQlnB}9F3Acwqjy4bj0R^cuDX-_L z@@b7U0(gn>Rc^cweX)ie_*ARE-@)Tf*18P8%I~Glh3-S#aqDyM$H$Y_vY68bht}>I z)?=b;$+~r+k8b&H+$mih7ZoXHYYpV1#ihC!C~|0vam*%fMGIOhZ=G{PCaqZ~`#k|j4qSNYju;%?|urJM${ z?*K{FS>ZF2ZExySm026lRGv33Ww&Naz27V_zx;-^9l>;=O;oN`XUJxPbq$Ls!*0)pg}`e$`^|c}0WMS9`L0G9@n*fNt&b z0K_-IB`5fZkg*pI%5O?ADbuC6wldeo3=i}RIsm|G1I!$u#<`sN>rPj-SN}!INO1vS zJ6nejWms;*M*&^2L-3JlTlykwHjx`uUm3$A+0dY_z7?%ySX-TWD35`IIC5)|c^&j_a2IXs2K6+>5vhy{3mo5NX>Ii9+|?y*3{@G##C~ zc`+^6CCcN(_3UdNWsS2T;&A{doZbA-w;IOljV>(q^reB|)SEl(B@F;ijTjS^+fmVyJ}isF4ZgXbTIltg>qg1|f{He+&l0#MvtJ|UoK%x~h0sC3zdgx)B;Yc>(0 z0YPatdAEf~wz3MaM}n_o6!cFwJO!Kpz&I|ebV;G*5|COXgZl&(?l&YX96t)(|a3>}(cUt;kDgJf@dtIAzlL=SSKv(Plp0sE!oCNE34Vog)|4R8yBfu{t z5K6-H?!{Ydm9IjHtG%;)2Wyxw)+HEVXU*18!fhScU!K#7qP<-k^7KUn$P_-(3ZUG4 ztj&~2!ZO)I!`YI)1X&}3X($+ES@mWvw1>Gj+;K)c#*e{h-ca z13t?KYh2tR8PD2b>O)l4Nf_hClbu!Jk^p;S^6&Aq%2vi(=76*5Lknn3crr`pR^|=C z1rD0RAbx(7{rpMgO|KC!j(#%XPs`-+SLxmC!b~_AT4c--^!xYWIflw0_^%S{XOV3I zweLQEm0vZpxG0Tv&4eT;cpRG2tagjefh=cGA_*#Go;mH5Yxk#lv&*{iq3v z)CKF=x7&6skj>6KPp<$ceDhPN4)?FmK;c2a!R?#YOJM5T6A>7+a;$~I&b^-E4zy** zcN45H_HDTS&sV~AY*n>NK6cw)v(}Wa)D5GO%PY2X@F39}VW-+{(Q$co|CV^BhXa6| zw5+_>RVR-(#=%IaXDUFb3xc$n;{TaT#Oxkx5$NJ#y5iCd{Qrs@vBvZ5r**-+-;G%Q z`H#d@=HqK16Nf!x{yj*{pMKhkigO^a>dJgE4G#H350UTy+{*G_6)dzEWx7*mQg0gn zX9_m%-{9lbV%$5X>w6@L48QWH#p;H1TD9DEt(nVr@XEU%3?C&g9q68A=WlU#GaHs{ zXS=9VXzUv&OdGnLEZ`77)`DdhW+_grFU*$@mtUt&j`u$`uM6YnAL9Bw3Mzi0fpy3C zK$U`zJrQr9gY$DW+r_}~R*gx0?f>Kp_t=Mj+3XXtSMr4jB)B5|Qk(eK5!h)!gb_Lts1&w~ zXAbotp&1e_^W2wJnP|`e?ZJ+jIl=VrTm62XlR5>I(j~;$?^@cnSs&HVkflU>N7Wq*PUOU0RlsV|ec@dw zEmtydjy&Twl}BBk21SPj!zBy1&pYGYLA@yAF$0u-JI&YZ7U0yNjL z3WENGxG&F?ZjHLB*Z_~UFeOgV;n4gOlZdfn$*i5u>_0Rjpd#PSj_Z0} zS^va3MS(EJiyk+Sl1Q9VlKr9vUDViH`uV5zAAfYcoYgjlR*)fIO`Oi(pBR}oo zum5EhgBlkWx61fVR(f`f$yWm3wqOcS39Gj)S8^m6lHH~*SFOZnrb5u?xrES**Ek$f zcwVQzV`-T9Fa`jHcUf;bM(PQ1I59B?a4A^M=eD3ljQY&0JM1zlgZ+!d)thZ?&Mnbm zvfkLYQ6PH~81Or;BD2d_;>FZn4vhh|U0cQr$5QMcEP86lG=2w6&$YV?kD`}*i4-}h z(uIuv@J%Us_=wW=OnC9>Ml!Z2)fYPFk$Z3{6XZeb(BM>I*WIogz)jA=NeId7d`d!E zU;jNfoBTgGi@aa)y0zw8T>(|_Fqac?uGl6_bWA1WMU6B>IlXj zJ(RGZa4m=8x^EGSjIdnO^E2D;!eUFQDhCVN$c&6PYj}p~&vD-}%y;@q4$oRanbhc_7`WMdk zmPM$pQ!NU)y;rX<5rjgJkYTtwX#I(MaJ504W0@1FqUnk7{l*l!z9B^}>3kRrty}1$ zhz<+pdX9{i$j?lI0jIJ^<=IQLyYdUx&RKdY4Lzxx>r1{=`TfYF(|U3L#?*4moSAj4 zsu;K~;EEy7f6Z6?$4|sr8>ESm%glXwJ=qUTOg(85xINy733g+ba#AvETSy`{1YbZGag&JT>NPQwmMyu zW+-B;x!yH&Y*06^x!U^XktNI4CF`Dr_=2y}U!wexo#FGi3zN&*g8wW-WTXu9h<@hg zP)!VpC0B(^0Poah$zC7&R?|3RIvH7i0B#xXz6r}N*2!L}mYY`pZn)J?{`V~b-DYna#2T0rL(bQjZsToQ&Rk>c`*cS~P0ANb6w z<<^k8@huYut%^1(l2^4P4oks+6gdv%?}A?0*I|7yapKIZl3+~r;C~3Sxu4apVfK(3 zl{!RKVoEbvF30U%=^7)7iq%uB%+8|DMAo9`Sh810F)?zR(?F zyPAVDrkWp!w&uk+C<3v<0y*Y245R91q>l56X^{)6b{5&Lz}y@muJaDv@(b3>IVc)D z4R8oFw)TH~s`Xf*MMcr(2 zyJ#^~2ORK^_HDgV?Jn-Z_7z@!NDg>&vQa*O<-h)3x3CH)Nb4WL>ZD)&yjU)4C{2iS zpNo^#VicqgxRo7>F1js+49qxI=+%=MlGYI5CjS2b>O)ieH=$C@T;6;8P(J;5xG*(g z4jbxvr~7 zQ#Q1(>QF+dsQAfv{Br%FNDL>qijGSc&gS?vQ!>W#*_ zu8q;09XUMK(Cd~?(RzpW}+K$3DHHAV?&YnGVgpzFGRhGUYf0}H1MnfwDlLPXf0 za8)4r@)>e~z(4A@yQ7c7APc$C_VUfhY0emkLewdtpCxhnNrafH!Ha^Bma*S)mjGn@ z1|smD6?xiqr)3XJZ5c+ed+=0zd{6QN?PSiyXaa34(){URE`m*d4-i~dKR4&8tS&Kd zdtFnOw&Ve+JdRDxl&R;8m88E212Q-J)^+T;`Ra85TR^10ik79#(oxK!PEEe2PW`$x z{qZ;P``kiY4@~OEA$F!U(soQ{ zcyubWPu?$9;H+b>3u!!zGU*^jNJb(!E<);u+nKRp>8U~ES^N%|)@pTf;Z0JreE;)p zs`U8sSvSw^pMULH@JjN`S>76Z~RFIpzvRZR6h6BjjQjN<^8xQ{Vb9ky*>!Y8#4#c@1cH1+rSY_3ej;m0GFxUSC{Gu-=< zZ6Cj3J2!{W8VG`Hel%y)wTX1D#;5@B_^CYJOKJ^mXT`7V(z9c6o*PEMjyj7}!Ld z_PqYgna%O4hEn|wUNaXWmf|&T*%?8k|s;lhLo0zIs0IGa0iYY}BgZya$=TFjvk}xPtt(hN20dpGN`q8fFDVxIdPHl% z>d$`k7%E8*HZnd*e_>&Wdx2$ftkx5=2K1M}X%9Cuf?q~3IiS!~PIYrGrs8K3$!%HO z&G6Gb-mU6Y>H?cJbE#4=QQ*4gqo+e=*2*<9oX1UlTKWgApA=$br%d9zC>u}x$9ARC zxqFLZskLJiD^`*A=EH48rZguR;AB7GRqdIA%bk7wz=u8Z{N#OZ2pl4ijGSjDWGhD> zCEampFrEP%DW$d!spE>N?l$U-5lqW{?M_XQH+U{J&SiI`VkcmS3@kF2+oB65aH-97 zk^o*r5zZjsIrY{}>%~jdF@8Qf-)5pfXl1MlPhW5%z#>`T%vY$cESwXyd_Nd7AAZrD zy)1)})$tFQbdgiCvx1TK5l81iOOEUTug0F^$lf|+X`Nc5qsRD-7L^C|*VYS`^`ayS z5x@H`vGFphzQEE1L-El)=Lk?-q;-3f>baWVgm%zXY$erntySt%hM^K24qzK{Qc_S} zsLU0BqIU_SsOb|~LvJzeSHQ!e=w{-#m-;_x27jze$MFAkfb0R@fs|qRdQ5x z54Re^U06&(v8>Rw9rS=BeZ8?7D(g176&^s@Dn2cEycd@1xtN(fEO9p9XPjb=P!0Mu zfMcpVKo_CG+L}o$V7P*D=rsj*-NL%{b(RF(>DQ`oO%8go1Uz?E${=f5QxxhJ;jNdL zGhCDk^W%H}C#qwVmZrzK$%zq?7W;m*VN5#85X0V4E2xKW`4=p1N@g>(B5^D-crht% z0{Hp@2C#HO#)NozdBGEu-O$II0fGTwO)<8BrKtIUne+-g1RNdDbH>J|sIog}ELNRU*Vv4bNUp54Ke8)?V28%up$}D&$#!BAu1fiwiCH#Mxo1ZNy3Z`-_X_<%ww9xL>GT9&r&N@xOZ_W1vF)vf-;-^$V23 z@DG&({fuY#U##)&qgJ!*+b~BiX=r;`8(^~H-L*G{D0+0>4aO@l=`!|q(ze504r)o+ zy7d@4viyRI&4hJVApQ@1ewseBle^=)VhPY4EdTsENb}W^t$Dee>ty%O7Sd-w|0+pY zcpq4@WI5vvYxMLQdnUl?-tMzyVM*>phzJ@3p4$CLkdrqNVyKAPu8AUHq{mvBz>DGW z3D7_AgH&m)=x|6sP3u}3y&ht#Nov%Ri)BCm%3L{dZjwA5X+SU#%?78ElChxR{zft! z8bnI(Xe63P<;nb|9!Y130I|R|HSa4V%xW_+;RYTu3u7P=(kFlT)l=j72hOv?$0{4# zz(b;@_90n9#1P>#wGor>GP5ZfoY!-b2!vrAC2(E1l;>fobGA&22C$sa56mHWUxQHm z%}+%F@#&5wge5dXl)#6n7ci{rfpowCE{kd?fzlE}6K0Drf!|NAFxZ&pfe*=8TuPZV zS6=a>#S)Y>%@ZeZpN9fKLkv3e=E)O$CK3Y+|9I{kk=N{>qxu!P$%-BC2 zuXpj^{QlPhNGWuiV4Y=|qqQswbr|&HAhY{4-|arr%$`1>^H^uZS$MYu$5gOdIEampty2v>1wYFEQt2Q=Kg)eM_{XZzlAg~YMYqtJdLdodYjH;wvs zSi%*~5znelL2J%@Byf;Rh!gJE&V&Yy`s5+2v4?le%wERKoHO5MvleLGDUj|isQ^<8V>TQ&qo&r;8{LRJu;`bACSR59GQQ#;-Xs(m3WQTvwTaUIA< zN6~<%B2SkhQet3W(3#bBrW%pwI@N<>T@gl!6}hj|cQYwo#R4%CIBI)FZDM|VfZ=~B z`43py+GE)WAr|ZQ?}pDU;fm9nL`EA8^PaFs5K_rZb3JMm1ky4p}(`lf!Sv`zg{&G4m6X?3C6X=y8!j=HJ??-2O{mf`7q?LeOp2ptcI zvT%OYs5fhUI?hS_P@6@{+FsJN?_f zu3Z#Dq%)=s2xZqLMhGAU#}AbmV;_x!$T@Cd9e~x#8JY<{2LiWjQ;XUat|5Z3kIXlp za#Ga=;JUWwg4*CP_BsaZRx~V}-aGh)M1|BSf%H(mXn~ZeZJi7!5Un@{TJ=5L%@6~~ z!l#ra+?=l4u}cYf(jM})r=5RRRv?Ehmk1LpQLu<$Nz3CCyOCGrSyAmruRXQ*-uB;W zSGcsmIqgaHLM;-|d`LqOUYCOfcx<1Sov9Mh959Gk(Xg;x1*dNTlHWRrD#nH|T#`!b z7`yP6^+S0o>kt7qAv4K4O$x9I7D={2$U>9(qZZv=J9n1%Q9A$f>G9RQgGY}cpMMtk z_EFrJi9E`J*DMq-4ZvsPOFW3&-4XG^)Nj=z)HpZsG8>8*IVJMzJ@RdYet|oZ8SZXd ztS=H?Cfmfa)nFq$o%D!5+KdMO2aU5*z$02!QdCuscT!T^efuKe?2OOVY(nptZpt!l6Kx+p_aH>wg3p4Pd} zOB`EYKC)!V7LTfg+!vl7a`e6)JxN+Ch^#7s|H`L<9bCrdu;)QoRk5x$sBaR7<;KdL@Fz5ZzAV zWFutk4gau8ef`7hTL z3xukBVcy%u3^uSJ=*X&$^E!5R5vGeX`l~w<+wVaiK@)TCMd2E{t#o}4fpm2}P=|R* z>;PWUK|po|_S*YfeL2wbn`~`{*8t}4JEqW1vb3EzgL9;V%rK9@c1pP&2ygcLr#qx& zlh#pN;0DW96HuQEwRulr^@u(yD(1Xcw{E3qw7=YQCIA0SlL$XHza0E9*_-rihpqH5 z>3wQ7NuZQ+zh0e2FT1N&hhXVWd@*hkxAj`Bk8Gr2kI@&O!E=$=_(knhm*Kl9bKgU4 zK(Xu1AC>c`>rX%nq+;rr$E0Z@X2Q(jzp+DWd$>uW+ww@LSq!bVrNCdUVQ2vS_Wt+F ziBhj=e#@&=6n`{wbFnQ8bgek27wMh#dby*0@NdW+I(=}{t)unJNE&7_bl!$S#Z#Ic zd_T^2&gy-=r(WQRF0gUtVxJjlU@Xa&#|&pse)GyU3fz(h>lm zZYe>D8d0Fw;JzG%5&$^Tvaf^~zd4yCVPqyeBb9^jB|kuu`V*CNi7!juBrK*XB!{!~ z3<`?oo-BCvbw=i2^Dn<6A9nMPzmIz3XzssnA9c>~8~TPf0)ZKQOFl;4=yTS{CaFm+ z)N}c)i)t|~w#SP+&H}hGq}OMjOwiq;s^ic9%r8QR58e*SE3{%II?xe zsZ8CUQsD#gbNT4OuFjT6=kCK*cyo{Sxug(SxjLoL{U#8-O#f_f7wz`7BlrTNGaw9m=b?<=P9iJN`JE=~I;m z@-kVbc+fT$!1JcRG_!5)yxtl1k)=NkEU|=BH_bEJteahxVeSd#kf-9m-w4W%B^#p~ z#Tl)Tm9fShW}k%%t}XO5$QSVr&T!kYMnpF2lP^(0(1emN_4;Pu4J&eCEbQS%3 zCw(aYaO@Jheamf$X3{y3s2p_dZSR;+W3F-r<;6h+~ohxD^W#fbM>fg(0*f6i*uNG%JFl|MKF15J$iWemRw_h?uP*Pas|KMwm00k_yY& zotW)uPFN}RDVzm8l3c9%W1ko$mZ87EWAvKjBCxKdegNA5PSR&_R$7GNb9ntf`fNSmM@o z&Z-K6z(AZRt*fohHSP;}Aov^viGqOX-ko(_!zUp~90bh4yTMmgeuKH)ECiQ?XmQVh zd>Vo@g6S@Cl6=GqpC4S?70{~_Rk$;2M4f$W#4wwZyyXZ7pKjFt$S%XK_T}S!9sY-j zLb`G2TwoknE8qtG)s20T=XKu(5H;R||4dL8m?MAO`hEN7lI3d# za0MEP&%OXYdRc{|w%>a6QVhO$IJikYod3K6<-@Ct8n9vxhoomMbP&h}LGW_|VBKn= zgg|x$U*)JHr(pgSz)e95k(LWkFsBx32xJBzy7{V$9vz>`a%ZR8S=q5NDO_*v>$E@E z&RW1EGRL(ofio|iHKht770=D=hq82SJletpVveT5rK2 zKVbV8(}h49+^QVINsTTtI=)NS_Snr!%Orp_h=nv%~!uR{vKTd%#Mp5=!Pc(9A!+d{L0EO5_#TQRO z8xV%b+-*8eMG5#*A1gc!u|lQU9=p)G#!eS)PFVu<12yr18cj{8FCjmF?^eBG(sg`> z#9Vl!x9y$Ficbcwh(#jb`dcZ7WZTK@?w3A(wb@Yvfs5CS1UUJXhneFoVZxW1NBwSL z$&%&H-*CD0+`94Fy7kD@H1_+xksG7Vg7XjjIkdN3U6fuf?)=B~Px`r`w;8~0J%;$3 z=ps$?(&zHbcPD5B#^Vs|M6t(TuYwuvNp6#gJm)rpS?w4n0|9^yC`o|JGHgRYBawpw znMcst{WP`w@(>6bYp~v0Q>gb}0l>7zpVM78%A8<$h1&f;! zY1*tv9E*as_AeVe5(#QRi;9tKWJV$S3{0uErqH=^&RU0tnX}7t?k`8{Fhv%qq3Bf- z?6N5U{5=X(5hMHvH6vQ2*yObujAn&pYScNUdl`u&tCqK{CyoFdG3c!5+nTohw$jJb zX}{V?<}9%%EQ^9h&NacfYFin;^3r(GWc|fAyi&<|b2>SzP1)^~l1^K^>e~JziOs?w_)>YLw&uyUxK0R*IQM0e zVFNT@>7c1LDwA`|w^j^jGJVyH(i(!^1j0YPxMBP|`_P@}dXlCBOp5-3k`cm%l0Usi zpC@8K&~VprAgh__m?5!+yI?Y@qv!+97f#|+{t_=SIFOHK)OEhEl>!1M4Z%~+S&Fg? zyp13!J+A)WcUp2(wFs}NJAF5Z12&8kt*DQ2pIIpaOv3~s4iRG$*)yjLO1(0hEBXFT zJF|KDYV*F_J8{A#``~2o2fgAAECw~$32s=NfZ36Jd$jmiQQnO>OQUjW3n zSY{*gcgz$rL&f$HpZDecO)gRgid;`Og|1IK(RsH~DrNXE{EYsRJJa&apC2%cWqIMl zCJCEJwgNLH)-{afeO;DjR+7u_%V1HOz=g;Dio|$nrcnFz@8%<7q55zj72V*8rRw6wnWMJolhv1QvPRBuKkVho}-78o`q7I;Qod=SfRSr2(*iImOy zfwxq)6v$ul4QR)sOJeXgI<|)&uTmoI`tNS|$?fIxd;AsKOUo2lrYfmslWyz2n7@eM zy4Y-j&Fjd9DD(h_DS8K5wAg@}1PaI=D64u%l}KW_bT2;zr!(98$;LhFL^AUlw=wE9 ztbB1MZngs=cBHzDrFn2;e~dMOpX~m;XDm|teC%vos@#DBGi2@l&5I>Q*`@S+mR=pG zzklhk6TP%xL=;kOV!qGto$0-E{2-G%i&_!W4A~X*F?BH{t*h2}@;`_?FWS3L^#rMk zPioQ+(TgMW!Txt+2?eZ9TyppjnSW_c(gV$_8 zjQNSgA&ccqw@ldC{s`S`-~?*y7rcfceOt;j+I-vJ&|f(SIiBX^uiyj)1=dOwA)|MT zT_h`Xo6mSkHU7W{vBiDON~i`EYZPyhu@AoGPsCYOMkQ$?ii9YB)PlS{vb4F*T&S(F zeG4hmWx5;dA60QWxl5mR<4xr1qtsWg&!^gEw}-8G8YMV(^NB5;7DqqdinJDz6fTjQ zzPaK}^4G~s>^3E|J}jY(`K;jYS?5HRUD z{WA#=;=j8bZN2Vps%1QO{Ofn!u=6!bV7dl``LTdb|{++qqcp1oM)cg+oKBgZ0cqPT%Q8%)nA~Kamx82C^6|7_OOWg7&z- z=_R$46+6$@si&Zj+WX{;6w4#((1rgzSN^77ede9PBoVPl@eulYD_kyqiPA6~Vt=GSL1w zjGBjc`mb@{VpDI;QM0QNAn`6dsGyG1Kd&q3IP&_QMWTfDqK`#3CX z?E%aU+B1UyUm7%M(4aws1`Te=zv6iK6PLnB7+%}XVTZ*r)Z-*1Q4+A@=L;b!i0Pu= z#aI+2CO^PtV99t2ttd-KP?Z2*?gwxEQK?j^L~hxSsH`n0A!<~-0psb4>1-YB&u{>+ zYU)?I-=wY;*7|!`XH&HYKN3;XQ~Z~#Z=@!(NMQ~M(H$=dRFZ7RiqyVsT2`C+)f#>6 zZG1&=Wzn*YATV6}J3uTp>i)Ca(}k5>P&^bN>3v1gbF_o1_FjB8c8j8f6$!W3ca!ka zxchUX>SDi%3eH%>YF&|(-hfErW*cO14Q2R$_Hrerhn&c%3FA;!6mhD5Rao0k82g2q z&;wo;wG2O^IawLAw7-+#JFl@(6>`PWU2zQDWm^h3F%`2?1j?dYLc2U*?C1o4A3@0* zCrx29FYLVsPoRplVsiuq6e$zOC*0eJ%aFoO!*ZV!Ekc4&kN^tzI*h>I6)ctoQIHS{ zcMDx{VN1nU3k#zVg(y(+hcGi)AO#7daGxO%#aQ7AggpnHAh1!2BuL?2L?Ezr#iJ%0 zrbwa`?nU^5XIY-v#{W1MUex+fZir2iiWcEwt68vMg$0YF3(hN0-L;i{RqiTRNYx5f zSg`mg2G9nntURlvj^h-yToAzNRPyL1DVIqog8s_yBz!p%8GqyaR-y zipaA~h-M3E(9VjDyvzqOTE@$-)*mG*p|0Yi;zDkjj{8TIx0m6D)QUu9sI2&?a>mjE z8~5$}7*+yFdB2m7@nPQ8bp6dx_@a#>3e@p<6CZM+1CeCSp1lzYS}QM^L4V~YuJfUqhTzXUa?*ucx=v_LE0fAJ)xm2 z8lSS{+4Sx*F^*f6RRtT@S}0J&cQv3bJOGMot<*>N1xXYd6^c-!QMG9LkzM2j+SX`B zJwV%z$J!~=S@1UMC#~$mb+t|1k`>|dCWWpNj~)2QpMW(-Pq*509|}g+hC!_QN8oxw zyEf%}X3)cN1MJp*u(L8NYenQneb1!*!#V)L(8qYJZ-}^;`8>>?^a8?t0<=1c%%4Gm z)%PREo$r50x-A2*R9VCinrchm?8|6>d7`*qK>TRZS^Q0U2L`6{ZbO`y<#f7q88`n2 z*sNtZnp%pVzxa4T)$isG&I)5J6FE0mg!P#~h`YDtj8w!ulmns1YTNLuND{mSjn#Zv z5ohNg*_)V`x2a}3 zLhRUmW!t3veP2Z0tu?1~=9~nd1up|yo32noRD*#4y>Pi|d*KPl(rf^1w8QTb0KQH zFjx&EEKDEgGYca5D=koFaWU0ZZdX7;{S-?K;}cB0MP%scRgj(rrm!&4+pD!4Iuh=$ zg_-VQnx6Lk3p3+Kz)ug3jSNx^^>1P+Ig4Xn6Ej6?`DHO#_2P3+Z1Pj3T9WIxt0@`D zKQ>;mteyn;cH}zGt*fQfokOntkSB%b(ati&fYint47X`C`z zLQ>Ddq6>Zh1yw)@Yd38AB8!j1_LeC&6o4CBW{Xh(XvPE1l;qV-g@aab@YO#&cbSvA zrG@34{&y$Zo8C@87yh`fyYTv)s_z-#oDglcgj1GYj@gR6+TO9aHhv_tSxhOi@7HT< zo@41!si%iw?v~S>q{UiKZ30T2@_7@eNx|CH4?aH-9cglSDXWEv=gB(}y_GNDn%lJ| z^ad@qxBLufMoq+lA`iRHGMCOvwyFNy089ij%lNDjfy)ujqj6JI9o`U%0Vk$1en^Ov z4CFT;*aiP8a>6)cYEQBEL39*P4_ zEQ>NYj-VvyiX9)!NmQOE=p~xW1Jy-w_p*Oq5+`t^4^kjlnBuKf>41Un&>^iTfmgbe3OU#YSrW-0CbszeNC$m_4PZNCcC(PP6<3Vn8 zUU}uEd;d7+@5ZrF9AkWExb#8>Q4tdyLB}0P8sfCyc2>UF)6WU5F)PBS0l7pPvl%YsYPLt&$OvvhI?eh3LE^!54}!#0ksFEe!8kdr(3Y;C%8hhpoSN0w~ynDHPB z?duZm!L}_Mq5@+&r1k_~RO%M+>XHmi_*Lu`0T2l}OXhK$bB}o-DQOA$num~nYB zBgvBV`z<3x_PBL1J>0ulwnB*)=*Q(@Vp!+DUe^RyRn+z9s5{u7xi2!zN@6>Y#L)y$ z-WvYsj-%4$WLwJ|{ew6l+Jw`f21pX%Y{>F~S0{}ZE8RDBg>=4u%{(Br$sOU5s3Ux} z_UlY8Y05ym%X|HS70h$);9x(3_bpMQ6+_kg!v&M~=NMH(d35de`tzE>_xc=$yI zGIf|QR@Wj_h>wy#s=7an*QS^@u%y%=CI_M3_)uSj5R(diZ5Vpal}J*{D+-QbU^{Xi zrtf(HO(&T5#e-^)xPdOMq_MICkQ4Qis1eg&M?}yt@lZ!Vq9=>j5mYlFMU=7R>j^?A z22s8wT?d#%ey2Qt`V(xreRu{iONz%&MELl850)1=-T#RTUW+NF%It;{g}$~+yb z(XiVEF8X{6DOqtupLMi#CE1e?gQqI+K`&1feYoLbPUWN1NQv*8kZz+>MGZj-v8j_f zRc;xFvN+;|>p0OlfZ>a4E+__E=i2zr(wSHdl(41Wb57vqn{sN%L#S11=Y7@Htmi>6 zQu4cVUpd$+FT(brn(Y8Sf#D92DxfWP&#uAp7807Lvjbwop9i<;&^GI9y7g6*EOqm~ zn17|RPVIbcBJ#&r(c3@y+u^^!My{}TwP9-AA!j0);*Yd~R>dJW8Ua-kJ=l~v+T${5 zB>uW&!&-PXZN?zR^0qJA=Q-u;`u0F&9yD+qr(gcHPR3G5Vm35#Sg#0Gw(>LMlHfOv zh?}~|nNhIdRHliHi>cwyOo9hJ|3CjcjMIIT_WwQw&7OIoj{-NZr%%EDRe0vqx5fV8 z2WC3$Bs%Jc13UMNvbH^iJj;K$&_~W1dpivq8z&HF#x;VHcNL%+_jI9^drG$Ckk0ry zW>l3~55l^~R_Dn2jEpM~e`I~|#4(mMiC zi;xRlVzNfsu9FrMQK|+ta-vvlvJMlAM6)IyGSV77H%MD0?hFn;Qe@s-673;~n{`bC z8;XFgd}W(v+@Y%~MXu2_eVX<+`wkL|rTFLzDE0nfxxwAM*%t_r5QXIrSZzFLF08LI zr#}yB|Kg+hnYKVD+dIG*;3ok#XH8@y+}V}OTd<8UrfE*6G0NPPF+03&hI3#6t*yGQ zl}Yma3`m60+ge8HC-PRyoE@?rgN5ZPRFfrY(`akWsaP{aqi;=R<#&4CK*~6-Wsp^J z8gqtg+lpe=sHAP%t|~P<*B?PRWaK&H#tjk*hePA&z%!YrOnmjV3!466>|Q9l7OO zX(I2pf^wm+>s&44SkZ}@Kt$<#N=~Wb0xnYXNa*O8v!;2V0iGZgB*EduvfwfRR#d^W zOq)y9$7XF!MY?KvzMYc$+a$Nd4?C@!I39u*c`5hXyfszMlat}FtQURvZDZ<86InL3 zlsr(me#M&N0_S+4rQ4R%N8%De5kjl3x*jl0SN&ZmHyUr$EnBdNdfwG#IAyhiq8}~z z--u{w2bGXc&9u-Ps2|NM%L+elWnjLyfgAQ@6Jxv#k6jVC+ceZ7bYXPuoL#&{Ab`9| zbeVY?3S|pQvuiwn@Fb@mp!8EAI0mI?5QDoN0}0^Xbt zRVXxx2$~#wXsRgu+^?AmO3}UhjOWF6UP2)@ygqq^kX+}`?_#$#mUWAHs3xF`if~S; znnhD@fhv+uaLb{8G3gBYdJEa1skZ&CNm9DVjcS{KVABwkG9aA`3|xOQSW&s zRwge3tk@}kzaJ@)ccq@Mzbhm6jiOdeI9vhuN$nX(3TWckugX%9^&M>3TdBYWcVB9O zh1FESwVJ5xX`~#f#efXgX(EN7hHz29zX{iUP?)(|h?9y+a^-AY9@u7=5`aF__2ACJ zJH)MafKP)sO@oU`{h26mFYs}C;W(cI=+xM0gTtP4f?aS(qq01>MRjdY?L*{e;x+Nr z0?HfIC@7z6%Iv3ogjQ3zkrOUCY-|y57r-37T&`q6Nz>0q?9>LUfk?<@ze$mXhO7Mqj5$SOis}7IE zDfZA^5>?w}ptaRqX$T-i<_l~wzz0WL2eZB$SF}XPg_*&xCXg&_;+}_!=(i6K;NB4~ zgHoqGVm$p&AfJvXb(imdwTc7WMyenjFM=390pGi^FHS@IEe4CmP0re$h)n^wP;JQI zl7LuwzzL+HeM&opg41w^qQ^7uN86_VHs`ig!cr{&nV_4Tq45n#LD)q1Y3(?rFEoX4 zUBCY|uAmQI7aOFTGAkGJ#t-dmFOq;(A~k}J!RhVDBMgwJWj33aHv z8eIXLl2$3Jctt(z;MV)c>e*^1Dt{~Jv5oJo>q*+W`N)#xhdO%uwWD+)bALC=KQGo` z7gl@9ukqriZ&>4udy8PlvG6{yWT`wz&sw`HQ@`Bn32&KGdM@~#gFmxMu-?Y-Yfyvd zf+fS$9BH3Yj?@AreEps>S>YP&p`%l~y5@&V{6K)54&tLYYAfI1jJ}-DHRU`Pk?kyIg&nzCXju4}(8wLu>NJMqNPe(T6fw z+i~K5O93V1y_VHJVB_3gJBn8udn(0;E`(U%x}&ED zOUn+utcMV8l#N41FA9H1OXB9rl+Ady0~)UJ=ct^{k7ud#yuUV%UYBBH-gVUy&ZRGO z>+lG2T3m=z0qM`@+oSo(_J2Ow-w#i&@KjX-G#EI-Aw%A?DzaZ#cRU-+%&jerasy!d2xaZf`EdU7P26VD4XO||_Pp6=X{%8++%*d6K0Q{S5MJQ3Q2!DMk zBz!Y}%SWScpL#{oLf?DNETrbkiK-Sh6(pgosEiz$k4<|(^9u37078@W8bVx0bmn=5 zY4CFF00f2w(G`&tQDdAk8bZ=YLS+&I@B+suJKIQ)9e_)~bRbAxND3Y&&$uU4Xt;1?<737FdGgZ); zj$4e2u3UgLt_El#t@T1klPta%Qc>2=g?E?UDX;NwICVRytpm%S-Q*=ylci zneC?oW>iO(@nISqh7f5u0cEfo%-g0^6eE_2V4(uD6?7tSJ&XOv28%u0&PV%D*S;=s z3|NlCScvuzB9aE0?|Bc_!aya)u~!Kcq-m0p@7-J^UFTUH&?K4Ns~4ZY3Bp4XWfU|5 zCl&-vT?ZMV+8J3gK@Vn<^NQM+f~$uYBZ4NN|DYP7qy>0Rvz3lr8s)Jy|?o^m!amNou?ifAcxN2_gD^p0+U!MUUZ|nof>K zSeK?+)o}QjsmJEBlm7j4(e2g{?RUYuYI|w(0b4|jAy6783I zL3xv|iLFDPe_kMQp>BPg$-Gm0p;S(I(c}SSp zxr-3MQbh<4rXiDWT`sDjgcbbdiVKL`S?we&t~0xJsItWm;DiTQyaceofahRU(5ttz zL2Yb+|I|2NO7S$hK3*lJjoy4w#(k^2*Q?gFTisgjK6W&i(;2|%(^9X}9rVbO*e(N$ zH3+8goxse8h&F?qv0}3G+?a+W))O6O!CRsX#?H{g0Wd@cr5-wt04e2;$>#`c8iVt@ zBFD-{N=A%hwrUvVEhs@>K8!gHcd;Oo3ljiKU*H3&Xi)(GfxG}fCWS_{7jy^Iz*vM~ z&s;7AetOCY}Byu=$zE^v+=^dS7p#Psey)n{tiq@AaqMn-^^C1cJIdG zHX2x@{WlJTZ8N<}d89FhrlQv z6gRbzWRW+Lusn+122&xZvrIxlP@2J0mvqM@_U!QGGnGcK-J=msDRyPwYBaL%MH`I+ zn%|Cm*5f;!U3Gf%%*#k2;XS(7iz#l+6oQCO#^s32?mS9_Rst&}#FhpB`P)1GMD|?# zwj!yhlW?y+-c{N3;i>+9cqp3y@5KK+Wu7{3VH+s`VV9|3c+dF9jZ}=qtFyE$yI&;E=yfsUOA5VJ#}^@5hsfn&Hfx z{lpaOd!Php1ir51(gcqKT%ON#Gu(Bo5e?e|U+5i^%TLQrVKp^HohuIHHNF~e4jVHw zGtcrD^z#~XB4dzoc$Lii)h=DIR-4|q>dN*i^FCJjZ8oh$0YksDf+1S1ccnpaNE9*N zI6-nuIWvnEoNGb8j0f>$0xnAn+t1tx8&h)?u_jZ0!D5PFjrRnugC7kc)NzZxk2sRC zQP2@0N=L|5q?R7~(t<$zDJIobeK!kMD6Peu^j&SdC%vl5^g{BkP$Oa3M0je-!mBbV0ZQcGC~0g!cT zp@0|hgH69bXK>Hf4r&?+V$nnmRPWqSA1d<%8XQS)IU~bw&q7s|nP06RW`1lQLQ>-& z$@-y;ynD=vnm(2J=pgSNs0Yb?K2rTW9#|%&`r&`W#Mlwr+|Y7}fHFf~pCF=Rw(u=p zO6R0HZz!fdwkSi)W(GO6Jx7KlFk}viy#^O5PvbX6)`fA$w$w~ok!t*cx@Jm~LjTp>LX&1m*czQ|_GxCk)IQ$MyN zNuv!@Z)Jmt$A<(kMKS9#TD+9NDa*%7mJd>B(W28gQ$Bz9T|q#Ag=g~V@c|Q~boe+a zGlspz!ApO^Lp$uMh;ck&@j?7HYsPbQ{* zw+A_h4sX60s(+`AoqLocyNU7HfIB@7@b@A&;q(|&($N_;aP9#11T>$t3U7sIfm-cLS_HQ ziaJ+7mMeQ=V0caE%D~9aWm3_rv!4Z$QvZ8mKQys@vlpH=#Cfge83FN@Zszk~kpoa%a(P%NMJ(Wv`ztFxmA;sb?UrH=tT5 zR%?(f0nrzypWxa*N$d*VZ(+_rYOtXfv*ixn-4VO*S#!nZ(Mz4^)ac8*AAtU6&?b^D~7;wmqh~b((GUE72-p-fueDN2!i*Ou`T&l^PE!uLgUIm5?cC`jOwwNpZ@Vs}Y=Im5IPkKLlDCQ3MaO2JwA5XU^U0$n9 ze9WbXb8BtOTQ?tBvSj(8orMtv9U`EsEp{T2loJC7Il!NJPpM z0d&Sn>OJJ~cXVzFrB9LgiKiC@_0r&L|Ydx!?9<0x&Y!-6IBgOw#{F(;>rM8pE}ZgS{Cq=+sr6Z8Wj_3)et z^%h|k#GO+_F7OL;Ea)pmE)_%&Ky3lh)v!3DpBmJa@Eog$U|`JxI7UW}7><58fZHj~ z6WRy_aFJ4i;+d}ehiQh940NU5loA#pir|E;sj_<^L{g`&WCPt9p(nH01yN@1BN}Hi z_7fN|>NX+;AirU4z%l2Dc_u-yL?D%J;4pZ!EhwcQ6-J|1))WBBIW6!LHFYY}InW(U zkpRN^*NGrX|JZ|qYq)zh*b$)Ca|G&MGL0*TolZ|6g{uo*xsuRAupyI%gaGrfwqAz0 zLsClX1)pbmHCw4niKJK3PV$Uv1%+^+?qtT$Q*hp#f)E1m;=|I;Xc8zx>}`ku1@@FS zPP;6~I6xw_%Dt0Xr0#KFDxUuN8Ui~vGH+8MB7!R;+x_~CHfjh*m<1s6GFwCtzJaB%8#^Os`3>EAWYXgGZHtmO)jSV10mFC=}u=tz*m3N6hH#0KB~~pUyn5f{w-w>O4xQcDC== zy@CddbG|}E3CMAVG*1U7d1Q|hi~I?72Nep+8nuR5$w6nw5Qa~=b$EDrdT_b+WJWFt zQH^0}`bmPM;!FQW2&-H2>-9cEaiH%PHqO=nOhB{0^}0e<6qgZuv;WSyl9_QaTRCC% zXFYWr&_IBRA0^IhYdGNB?_(i1mgMS`v{LW|Vdy82%C=^0lj#k9`25wDn7_mymKMfO z3Qeyp!PUCYUfvBU@V<`47d2yO!BdD5Xbh6K^L1wa`#D^FI)HP^QJcK4&!A zK%5NhHG=cx=Y!03549!_}easM(4^mI4SS398G$^H*MYOOpUBGiYnhSK`%|QjVO+E0tXraI_LV$Ya zK{nl6gWNe|o10B~gz9+CouKur>C1#lz!=K%58q5CnM2=dsdf+EoF01?WBpU>sviBx z2J2nJAiGK1w9ecl^Ce1M%+kR0n0ik^h#M?*zOcn>=t3PD1Cs^~c;R!5jivi}M$rTQ zMBUu`e!+T$m2Qn0&1c0}ys+4vizkcqy#6e!&<}`@0|*rvAk2VPyfsbZ;e|^Jt=$2!g4;1#EjC1s1_VPE2){I}eIx9k35YDd9lCbCn6gR{MTeZA&P=?we5EBig4U1r4y+lg4 z`Z+ldrC+k;xv#9oOG@0>iBBEaHRJ}Hmaf&baH#)^gmoW-1E(JioRBgFw!@I%fb2J~ zTFcQD!%^_q0gCmT^dbeCIbs?X&hSdZ^Z%hH=mP1YT49&T0Xo#;1XS`a=|wzK4_Ggv*&jrGIw zBhfeJlu{dQvh(H8I=vXgq(OUN=vf^dF1b6ndF-()^kEbCc#Elgo3p8%v-I?$T(A5v?ByB`$IL zLOycwDPV4m*{C5yB4u466fPSiLhy+ea2l`&PVqeD9i@YV{7B-VB#x&|TG#GwSyGJH zvi8~cp-^}Kli7qzEs8CGKr9lGqML#QZ(;j`Z#v7gSdRBACoAy%=daF(zl=u0KBH1= zsYnz!{U+A9`V9;Dk6()xkKP<(OsDtkmzOb2nrl~^GTVWa_^t-(_Rh}xf7aWtRBKl) zC6L9AgS@dE$w6ZgYf_S(M3wP`S_GQU4?uEDZ%f&zMFuaeEWVV&BpOAkx>I{-zGcK` z%C}ymx1ak8FWl@i_{|Ob{`DREB9^Sgc@wX7y(yVLzu^zr#-gCf>1o5D zP%|HeP1lc6>4PLm!e$#D9GRIt40-*s#YAF~G>+w^%r_xQaEyK6 zjg-8EvC4{9u~++16K0h}mar-wydw?>=b;Fb|Ki;Me5i;V?etbbBi4eUZJyyJc5J;z z6hHMleg(D_uLylnS`iAA30-H0LC!Ka?@zx_wDc0-u$60n!@ELgysBvv29zn#jqqqU zaQNdiVAsDTre!esyHcHn9tcw23JZ>KN7V)>EKU8I8oN|UKEF~YS6{6(o z?07#CdO+KF71LCZ)l+cNG6jmVe-_hKSO}bz)b8KRj372fZ2N& z@V!%2tyw2s9BV@r)_NuuXk4KdSOI455+GF!vITrRfDSBJA>#@rF0hdWBCt^U#U8r% zR6Nwpg%*}jGO}PIIvZOcjzBS~#lmCWdQTtwgrZEa39#Wp5cm zG|Ta7=)LS0xyFm~TOKom(}L}r>NVzg1Kf4`Z6H`pgT=_~NJvo6%WoiJV?OZp2x9;o zoFN4-6rO~35&evqM>s~9NNmyfFC~F$VfZBpLaTS|34QbH?*4#1yD@ja`^a{s(i9Kl z2H_UkO2syX0WU;=R)lgrsYN?KBwtvO&nmAfHS^oRw%zU~kwGhdU%*XwARnF8k@7(W zB@U>~I#Rs0-!=@8P)dU1#_>TL4~dpJBCh%F*hv)HiXH9urUL&Gzmb=27MAD27*ebC zj1V%U1xvNhFZ+~!Hln1{R5+ZAmJblnkZLWx4cVXsBguJS{rq>g0&g@%qcOYTl&>4x zhOy78q3t+5F|#8qBaUFL>b^^lLQ&Z#Ixr`;W%vQ}MqU^x`9VMd^+sL?K(F1YY~826 ziAk=IdeZUCi~R288^aO?M~>`{)N|*_e8EYjs8ojdNvnThj+ipd{>ljF_t*f?2 zC*ZNYwb_iQJiNbd#+p43ND`}TF+tX_j=>kd8ZFmH-ZPKO6VpO_!#aL`yXel{UxMA; zp`V^%G#d%28IYCV1~jBr*e*kQ2KBV%nKxy%BWl?1 zl#HZdq$rl*-|PmS(bw{8gRtCz{6lgEg7h@UHIJnDb zCskL0t9Qft;UGntlVdO$3U`|~b0hV>torO(n6eOA+(e^$QYX~5wFHSV8X1^_T=R;x zScs|zv0hr~@8<} zW8X0Lw{B*xU7h$p45$5SEt->PPg&|FWHVYZV|DEmES}|RFJ!jh0h=@v~kKlE^+WVSPmx4s3kjnq%k0Qo@tFz>6Gq+g&r`T$3SOlW(};*# zG-8w3F9(JYY_wFzIaaNHraJfg-fr+b`f~V4QrhKjeagf$SF|xS%oOhP3ABZYxLJt4Kfq z0nf^u)t6b`-cu8h>)7%_pOv%rDrR<_3%>a`n0X;cSIpUAt7Q?{xSQB`|gm0IYtvTwMd~4K`h!+;mxQzUsOszuPi0v@Ra_s4GnhLn=fpr6?$XM)CNs{wbj4EhAm(q!jL14)GIo}g zt@GrcIRtcG==sRyk>&d8wc-IL$MS;ORC;HwXNQgcjxuqh%4f3SGxo?L4-ye z8y3tD0g?!L0STsjQbQ={#Is?K`N1KIKx@OpoUin;SG1Y?kFOz@id&#${hFC}u|Y)9 zjsJqh2+#df%O`?TkF`eBB;}q)By9MT7h03J zU6GEowYqRH3vgsnMi0ulwbvfe$zVRU*r%vl@Ues7K^p&nq09RZ2qUDXuA>dm!RY@| zBL_C4p>|f9qMlf7zpadJkj6emx2hZ3CrGi*hZLD#a*JGBYt|2qaKFb`ZvH1U z-2ngqG!zu-53n|1UQl(0VKTZ$LZfJ*K7tWxD0&_O4fHv}kLdwG-!01jupS}Fv7HB) zib5wN{82Zh-4U#zqfqa`h|r^gIYT>QF7s4*<}Yz5WV5Qqid1+xhiEC=YVMn+XJN^5 zx(mN#Y$RQL5lgwu*|t361$w0ltI73$aF)sX=?BsYiMp3O_g-(rU&}FkG*s6Qr*fX9 z@X27cImoxGUvsDN0dAeCHu~*8vQ=;8!Os3F`fd4LIccT5jO`KDTrTS%hvv`{(HyOX zQ!mY~!IH&xiu=zO!bE_8wk+iQJiuH~1`_=ChzR4YJ#FaMtOoL{K~vR2@pTyP zTXM`2rZZ42@{mxxtDli$!VICZv?S7Dt1dKg5+!j?cbf@K6u6(6ROl^T4bZxGFpuwZ zu`*W_!~d?intuJ~nYtkf@RN{GbmyiW?P#M&44#1wU9z8i0R7;!Is)D0byMJuuVk@f z4u-;F9{OQji==M8;<)QkQLqxmWCQY;8(gs1U-eHTq}n@i(eJ_YEGY(D)yPg^{ z0Z1JI^+6rY75shZ1^IbBtrcDDRvE9;=V>AD3iJ84PMBwdydp7!z-DTZlZi?}5M1Ch zWeCY_HYKmdfpx`yGh%|k!d-?@Q0=PsrGnidfPmElB8zzUZJd9O5r50uD;fqjq(BFz zGs_~gND)~qrMCw8MQHdi5T;5yi_jvqRIFQ+t*-y_;8S9CjU?McA+hvYq;y)el-_6F zmcO@R+N~c!(1v=b&yA=pQnVH$@x>E*)-qVSL0d86#L1zzXl4DXBayMDA>nb)*kl<890yV#V{-#qgf zTBQk+3ug1^I~Q7*aw`r5n@B6Ni`B9#-*f@F(FiuX89)Og-@HEO4H7DTVaWzGuucM5 z;2bquU{#TiaL*uk&8if4VF$34YQd@;TL|ju@6w=eTe#*JFy3q%S#oDU+W(nhM5~&Yu0b&R%6Vr!TTKYxd0%5)x$X9^K9x7<3uPCcXlbEA1YE zt)>;SiJO1}Wn=~kPj7PI;-7yyVfnArs~BwttTg&PN;8#i1L};Q3P2{vCa!?jHfyTx zHVjSiSPMu_wGQHR!v%k+pB*#j@DIC=jo2DuvT04-Hn-|7nl+}9O*HhOmMb2c0_!Vn zvv3*Bk(2jDBz@h0{+odV^Q zkZe7-v<-A_us_3!>kvYyFnW3l%PlVSmJ@HA*bX<)yQ5VInuZWei&d!dkRnuh+DCK2 zH5jw2uF3mC7!4JzbUZ{z|97QalJyX}3+<+=-z0)NR-ge>h>)`MYa@Y(o&BC%3E^-0 zw6U6JAr)!Ru80!Y`UAp7FTE_=|HDZpm%|+@v($+8e}So3{GUa0%SRV60NC?F_D{kl z)S<$9jKcL9_`B+giH&hZDD$#Lw@T2*0__(0Zj0UCEc>x0_K&-v)sOg(65+qI+iFlQ zKmu*7o3=z8nambtb29yL__yeqr%{1W*lxe~Y!d*pKYV%Z`u_+5ATWi$IGLVk5|yKW zTpfw_3F}iz#7=@^99Fc{xRLl|ol~$5C|5+ zsCqTx5n6Z`^gK4Ivgft~#*|;_DJ;ic-OUtos67sxS(ESh!WKFIN(z4G)~iW{>{-Mt ztH4BKnWY285@GP|YAG%{VsmM5R9xxmzCHvlL=X7CKF?EPGpOYFnaojFw&MbAL$r)1 zVN=VNrq5$yI$;_RaKYxxudn?J_TNt@W@Px6tEgo0OL=YvoONGVSU5ll$s%r=pFjKV|^o`L|2XPVmN z7%2*21k(EvypMe{O5{=B3|h+%GrgVgBA5yl;f;vtw^fI9g-8CLZ(^`$&B8S!M$}1c z4Y*ML04)#rFzPmxk-~H2EUzW2W8M~VMKiNnr-%aqb6mEX8Ce`y%X_SjuR}{XgzmGoNrhn zk2KEYl-bzkxbx19eWV%7=Z%?}*+6%j{u>SbFRHIhp!V{C93q||g}^(i1QHha?>W~O z%*@Qp%*;k|=iQU=*}G4-x8c2k7|p}y%sVO{H0^{*sHj99h)k^p7!UNOfoq+Lv=9a; z4rZ)#%p?#3P5F&E;R19(W@ct)W@e)?_dfSzG@&gxWuPZz4c0#2wgl zU=2xx`jP;^zLehCrJ#yOqkJ6XI?9)j?LejbNxe}t`tA2tBW0d7JP8mrz>W-OenE#7 z)-g(V!nU3H=!DJF82y2Z-2z2 zu)D{x0*IT3(~BXr$J{}l*WZ`y+$hR>?4JvS2&`kZQ7_s|kXNNx>~4IaZ=faaG9-$P zieCb8l=dk8{YO>sBwkt?4oneEhMJ}+&e{pslH?=P@o>uF$PzOnsK?<{E+6(IW_1CQ zW6sC?*jk9Ocq4%&+T6fvmMHE-m~xccHQb~_ z6q{9TROpjCHWJjTndvyUNf2&rF4z{H1dKj;v3xKEny+=iz4~2d-WJm!m1U9_ILc^0 zZ9-OSao4-DTr7V;h8G?R=j(!xMNOalf9UPftjUh*32i3@ITqfBTOrjI-=0pe={@{! z`VUoRn88pPtq)j<{j0@FT`QbAHtqG)A2ReCeAFp4-YH1XPRDN{sT@?(JV!W zXzgNA}gOBhIOSC>oQr^X7!H>jfEUFU1I>p z68j@Z8|U!MGb*iThaK<>tG|Wa5EF$l(=x7^B6%x+?-_1xS>tQm8Nrv3_#63AdF5{H z^r!fG#&JPb8Iga!0;3Rn<5)y@@?18SttF=X2&O)!o>D>ajDC7eS?EW0oR)7qW@h7$ zl!Ek;QWm$s!<~ejZD*T-L{xX35w^jJSL#!>h;c-Wyap&iYhMiu12-zeY@#?xgbsZ4 zuo+>YWO;F7mh#*apIom3mNMNQKj;q&yPPjcHcDkFq6X#JuVBJog?Fu?!91L%3Y)Dh zwld2H`l{pS^7!FDRhZZ0aDmELq)>VKS{u4fOaJJ@r|lOB3xu+B6@aY)AcmvD-g-#P zR7;*Xvl`4^4{oc*aafk#dXj|DCfxilfb_Sso49+~Xklj$F*ldfWeU$xCla5mGg7|b zs$7@jRG^_ZnIrAL-&h3bzq?r(VrjhURIm}_>R_cRCd5)v9C4IpPGuc7 zZV)hdF2T*!F!S4bSs-N=swT_PBLm~`Z~y@R^Z`7;L8iU=eG;p>{u%Z%c*S@|8?tYF zU@YHU?j|5-T@h*mM|g7uLiU9gyD;sFm-Yb-FnoNod$n0i*ah zQ2l0q(j~Dx_CHPp1<-$w5y_e)!lZMyP%J&STSLN23s|=v(KiPnjOe3NXQ4=oLNX?1 z=3&a1y*Aa8aegQr!~Sl@a13;$1Z5FY-7*cCT;^5iw4UrVKOMul4#rW%Z!kfD$+wQ%~!fhf%cS z*>(?U2Bc10;p@qb7Sd(aJ3z~kxtZsL)ZmpL%A(<)lgdX*!eYuJTd;<#j1-1F&+`?- zIb2pBo*g@@z+HyL&-U&d#fjX2Wc~cy_)ILZ;cbe>qtO#V=%KLFn0HySEd(%abWib$ zjPjLQF(K24I~7fdkOHM+Yv~^CEUM zK@FDJpehD%faqg{Eu`vsfj)whRAm`8>Jj&C8k|zr8({YM9zxt3Pl@##Vw(FM{`Rbd zX>(CU3YcVujNKBEu?*fyTa}0`4paqIRPodPw~sZPlY=633|YoH2*Xc&yw1JWQiqab z(&{qLj$fr`qu6tdO^_BsyTQz^V}weQF2}BSk5w{26;p2#6w8>R+`i;Vdn_ibN-CJr zIFd3h6lRVgL;rYljeZAr^0#|l0ZH1*srzDhh9><}>~}v_Umc&i=e-z%aU1MA5VQ>6 z*oej$2W5Dv_qOS}vhoh6zF>a3r8fQ;u$U(R4#kE)iQhaMEhlH{DqU zldA~Lu!cT_M= zs+wQY8h282&FT_PPakD6m7wmRtd-q?yqm92^LKP*27jt&FMBf&{{BUc z60gCS__u7DgT1Y}cU|7v%l8(R6B}%bLBMm>bX`94Kc24_nMze-qflq<#{Xz;+}g|2 z%mEA!xTtt5*N&~Vd$e=%{e{$6FVw>FS#ma#6t=6u5;h&cz)!U#jX~he0B$W|);;b) zW-LF-?gqcr&6hSwoUlvmNfa;qmFQqcv+zEYgJ-yPFl}9jg|6)~zcI_i4z!J?hzbF%`En9tRnH0!D1RzZ)jiYfR@D#!&7+Nc z%XTM=QT-=ZYuKD1QTw}Nq2m+c8l;tz;#!qVdMKfa`yI|RGBQj5R|3eNln=z|lmK?b z?An5Zg)wehD=9#zPH-1j0 zdGN0s{F`$^c(2MZ#Y{fBG`<`~$lT<;-i$FU$BCy|<)KfZUjMs@C_$-^mhPNc(@`+Y zCd1nR7C)(K3B1a}+~`sGp=4jq*k2g6$`=VHBjGYEzdwI*xai!EP)83`yJyx_+(f^2@_d z(ifZMZ7Nw21$Abvt%3AOwbIt`Ok1Bhuh6)bUmu|{p^W(A02XqjPX{)XZ2*BSc5Wh5 zi(*;2yo0kRC0|wb7&>t5LHrde9 zH~QBDY&g`*a23R`s)f>^?94{NY-1}hM5_=P^*TE1f^TVPN?NV$cU->a`5lf~zo7EG zYnuuqD|&wx2M?sQHOA{O7Z`j{Y;so7?m1`~Y3SQ#pAZqDARFM}H7o2efK}*Mcm`U8 zPHOlDUZpCaP&g*zI!%8IWlij%>WXT|yGvtMce;y(J5mpz4573e!1)?ub+~t#0%`1> z+>^J~8qUkp5IuTu+48qjsKC)Pd_~Gzj?GHDD9N{cSThnOHtvAVES`dpf&+&9HPt*X zy9Cb=i<@|YVq<0b_EJMz~^uC90{?g4rn@6mMM z6dXlUpS=Co9a*pw7pBs2BRYZl#E{-rB(4T%(?$_(8v3kjTM8i!Qg7=NE=<5COOwax z{It={tN2j-c%vosu4n?C0g4e+lv#-aa1C^=4GEkFD7dga+xABEMO0*qw}2uV0ZD$Q zs+BBBQwv_(Et-NR-{vjH3zDp4^`d}LUJzd-Z4#91psPGt*g>e1j#WAu&jUBa`P=Qw z9|oUJTGvjE<#BI*dUX9Ot21Q|Sd9(qD{ay5XlMX>#1uv4Eys%^eM%6Y!J;#+_f>(u zbzL{6bi}s@7Lo*hyxoA*^(?Wqg~MMa`}EeQR7((9T6buE`zJV8nKuIy2+fg@3X)dd z3Yf=A8&UIpoip=+X;$n~qdXt24p3`u3ES*wN-0SxHOBU7I_&Jy_m{!fA=UfyxBYFENJ_n*!KlryyeZhz zlp#fOkVKLVZE-$Q;z)FYBA!1DGene}R(VG297z>bM~zcP32v;7Ga#hvUGKMrGN2P4 zJ@d($axpU_Q_augBuCx6J5#o4EcJEXC-rP_9nP4HAWOqph?M?$F~K}94h4zXCUSYJ z#BR?RYgZH<&3Ke2ph}p`&YPBP8+2az5w6UOobaS6eX+A_#q+VlW^?gP|63AR?qomZ0R;l4-db;Mnf2Ez6c6suTHx>1=j0d_-$R9&P@^o%`(YbN9 zK_+xn20`Pa@0PDpYZ5Vga9nWUH)^sB&B()V(n3(fU>C1V!PB%JZTYFUo2@suuW+3| zUg-ng!hEv@wMC7M#(b8HVaEvq^~1=z5cz7YVBfZ-Z(A2#aP3e|_`fa_tfGd7z(=L=cB z4?PvDsjzSV^`JXfC6Z?K^BgRHiL0oVTW79y z)^XibCQ-WlD3;KB4QX*Ym}$sKU&+i*Eoegm(WJhuuZKxpJd@v>;0$IMMsEBuQXjzpk^KDfEczgP^JG7JBP{f@RY5U-Y)?f(yx} ztuJei^GN6Y|F#NpeBCp?L4Wof(J}UsF8CZS3Xz)i7dmodAPHqTbn{0Pgu3SnvXP#s zjjNKUJ3{U{If<}T$W|-Y=hYbs8B9fZ1TwJn@IDb;@3wV4-k%w=$Zg3(q3G|pcfnH_ z5WeHC12 zPYUFWkq!?)pA4t5DTAj8{bm+J*T#&lo!zowI=a;2a=RsGwWVLj_Bv;N50<*`8HqO! z@e<3w#H@g{AL#Qr>Lfn+WqPXv)?dvZ!=8y6ALA>_Q~FlROPyGPCh!3yC>Q2JM=;3^(^O zN149~>gqu`e+oyu5B_Z34 z6un2wXmr(;EzL3;@%E}HY}{n=?j$9sb~CstGYggr`nY**OdPH#-noo**T_@s4kxRe z6;>o>M+4f(hnCp^nh{e;!TWQ4$RCI%wfHYT8Foz~8RRiBj;&_IYo>J$_$fz;YNX_0 z7}~Lxi%{DkWoN_jm8n?aQW?aZdM9LcFk4mA#8hTG1FQB$%kXqHmc7u5bBHQ?19+Am zLf5?oWg&uGib2~lnMsUINpN#jluTy$yhgVG=uU>OK|J4tHO6 zF0;qYuIu#PQL>(#nIIWS%hDZn;kZtOLAY=N_C~3bd_F($%A-@vrMDw}WUMP^mH`7D z#8i3Miazi__VHL38+a%eOTu;W+|!b@z6!1+rxJ1I-ROhZ^(|7WhE-}n*E zRYKf1G^o~A7_O)-lI)?Ktkz%)D2*v)8Tb>Tb2^LGm$RS)wCq$qrR~>)j5J35QoJBv zp>37qiqn{<>a|s0btEc9e!#;xE^IOr$y4P(Jm3vppT%)K{CpaVi#@~Vv~$aLv4tO! zNEIsn!AZYrxWqAswDqfD+=BfUe$&0!hR56#4teFal)3ni^YC{2$dHw+hlVg5d__CF zdN<<|r3{_`en(cq7(J|HY4X;!%n!k&D|nBaufe=^jSEsJT>lj=M(_@uR6=oIv57U6 zK6Fu}GsC{?ot|f`2Z6~xO}?w@@gGmZMpjTyijemu*ovDeGt{Ge;aIL?J_@UoL2jOR z8GL5lgj)7r;UA~;Z=s1Ig#5{=0;qX}ThahPLdjC#KE0nKg1{w)DZ-Q%ikIVuRDvnx z#BJolR_FjO`@r1l%`7O!=-YURB#ILZ;9dI2$@;w%GaoXzF4=1%Gr9KTDsu&8hgKF| zSfmqB%x(C{ZJ|v#k~_;`lIIslmfUz~Hiew)8?ZQv}|2pA{L3p$dCr=gBk_i)R+EA~Ytq#1CWH>2`N z$GdUc`boFfkr$treNwp45>IY0gIUAJ&5cJ=qudS{^y#C1$6r;XipG{9K3mppXh`~; zuWQ@pC|XA|Q+-DBuRHcWy`#XD^?381&Nm%AZ@%#(H?%7u+9KT^&+6izG2j37444F; z^tqc6BHAD$v64~5|57xB-9aG$1}lyi+)Ei+3zmZz(^zvhC6QVvM^-!rv$(+^q$!K? zwN?EV9_|&ATewfdO5Tu-aJqV=nwuZ6M&;dtZsJDAliXl{jib(A#`YunP*aw>tFHgG z-+dHwMH5!dDqYs1Uwt(HAn0PV;zMW{Ln}=kG!EnY)aKjL5$(GC5QAx-KzV3jS(8(U zSk+>0ZXp@-x_Cye>ZtN-Gr)Oq;Aknk;>4@eB(4dkg>nk}VoddK8&$Ktidz=fm;~W> zjJ=JW1Vb+~fM`XqoE8oW=JFOZgai?i zr{3dS|FPER6Mt`3Ulte7uQ0IwKd9?IQ-ae3#u=NV_;H|W z`)pqrih&_kuqm~(5k$5iTLY7)XvZo@jwQFE1$h{y)bwqf3h${NhvOQYAbwSpt}>&r z(P`FNqjgn8n>{z%^wSpm%rWiu zfhJsqAqA;o6O8nOE?r(l$P#Om`7fdIY4Q$M^;n(tY>PS%)1&torSxXF(_5ZHu7p#EeLeV)M%n|-qFev%72V~(0pb{65a$IC z)Xe;s**Eb}8Zwz&TDf)Vj1=Bhda)EVnSObr?6E7T*``cU@c-4YqIkjqOrD8OT5uMw zF5pctQKwfPIml1plhl_4$6n`v5>5FCgTb4t<;^NV4k)Fgvh8|x(fdN|#iQjNTrEG8 zrHXHtrjDmz`Y%lS0H167anL&=suLA`)Aq8B^`}E>tO!E7K0Evi1;Ya88(VZ!C1JlT z0q6bI^0ptLHAr=o`L?;JC8+>U7>`RIs%dz=%Na1pYVD!r{RDq(A$;V_`%D{2TMf zoz-(WycU!&6>k`mR8nedq58?%(p@2hsDc|Mggyt>X-qAgA+`pT)$+LGzDT=l3DwaS z_&Y{`jNhCi@Fbu#xlUJkPZ^nPQ3^Qow(~2X^G7+lHD~heJQ29dValBViDzbMT^?tt z35K>Q&hKuVFHTI$BJ{*@Hay@@aW+62t%Tuu;aG64WsvV-nvDhrR?Ck#JJRJcTM}*5 z7WmJM2zmTw%@I%N9Q@fKiDCi?Sjl-2*B@5sXN!d11o*{Nv8iaN2h$h*lroK{zJI!N zI9313Y#&MqImw%U_}*r|kEGdFjUH4VeTJ3{Q<3Ss^S}riQPk;`$y+5xIqQ95`1}*7 zj@wr-CaTuzfSOu^njd&(Flzehw&h9)dUOSJY}JjTl?xVS*dgJCaXUjWRUB&Ck0*r! z2)?)8(;EUz4}|Nm?OA%y#(`{mQS^zZ5atnhCx4kgJNBA$@rlQLkDo}D(OX}Q9&xcc zmX90(dzOdM-Jbh)*oYqstj3v`kwV5d@r0FI$u4F8+rDd^i?gsSaF)+J`?{!o#F0Wa z*8vJ``45=ux?b<)_dwH_LI^6Hf1Ntp;z86hrPlxZ$8XY5I=QBC(1OIz%BKL)k2pq< z&BdUVTmBtZ-r|_yr5N{Mon7MPi#lVq^^KAFuQJCq;~4Xb#)vWyTRWTJE#>fK#wc(EOf9yxe{lxJhaTI9H zoTGG{o)y_2eBF9%J_k-q81H~idIVAw#sAPJmU-tNq$lTw%DZWhG2CsA9lo%BsZ}A@ z)TQ&^p-ep0Zp6LCsi;NilpPqo%ZSU@Pj*&(h8GJAz`(T8zVGSYQW55=Atpskars+4j3 z*Kojouy0^|0U~Tn2%=@yOw#uZrE|{kqKk^j*&Am}p5L^XTS@&E%$(*?Y(k60Yb z%;+mtT^0YHVz!h^5d1fAU>iX=A-EtDyCoHn^{Tciw&ql?#8@R2**CMPez`y7+X=|O zhJh@kO`0KIfb?x}NO2&=oNW99h|@jWmr#xxjxQ)DHZlAitdc+%Qr5a#Zi3h0JAyl#S&) zZ^$7(q0E5?7%ABC01n`ElGZdS4-7(2td(NrQo`6}Ti)Hzk_aC$qpUH*CqHk6>oGF< zap`USmhZUvLt4r*95_AtTp&jN+G>%q$%J6<2C}xYt>LJ=9cvxS=~@Jk6@+xSp@TZ$pd*N9QSl{m_^k1M@^Nezr3$TM z?(v9!vu}6df%1I`@9_z9lVy9rvTkkgr$9E~-Squ2`g9@80!7nV*U)h4k$GDoIKeg! zk;%(ESAG@IvN0xO$#pt8b^tG!qe65aYtWww3VT{%bMRPjdAc&@_g=Pto#2xMuhSFn z1qObXd_&r6=r5eQ@5|}3xHOuv;iG!J4A}|iRdihw#TbojCf`QfP7$uzP>x9wd2!b9 zVWGdd@7%kED9qnZjaa@@FHbt7A6Sr!p{NV#H+#AZ@NJIL9`7c+TJY9&VGAvaNi6;c z0O3hs^8CQh_Fzb!XMgXm&$&z^wCl=9Q5!n=J+k1`L=#c1Fza|Td)n#gY0v0F$Gt;> zTAMqveIbB>k8RHqNsy?Sqq^RVq^}rW7FCLbTPIQSVF-d1~6Tggj|0 zFO?;KCpin@$;v8h9j51lu>@mVf?^r*?*{d>;Pno{_Kym%4WhGchUF@mKsoquwS2fh zK~{MVDYHFK#*fP!eZjWN#JEyvA-yg36zAV(RCRDs!im6J|7^YHr8k*dAQ}f28^NJ&UneMNBR9Rp_n@eG?Wroiu1T_- z=jcS(G%UD}(1P&i%^g%T)GNuwuIBo~Xn?kWUof2J)g+=tD8&{K)E>4Rkbzl=6%WZi zK%2*j^ADq!A9-$>q3u@#{D7`V4;S@p zmo{6@YJnW)W>;epatrm9c~=tTY4JPOr9#o8Z>_wgOQj4(8ykGET0WRTO0~%(lGnN5 z$4$RKV|p^ZK@*uCXaJsO4@YHx<;o4m*;J^s;bWFKQ@9~5U3sMQ1y2RWGz8ligL zD{8?6KUBKr|-aH2-MzHHj=? zCX!+yDl&cQMN}8ASyMoTj(BN4yW&XFvU4}ijkSC9fojd~;x+bupR48GPE-YxzbHdj z=BT=QxI&86>3NDnXgk*!g^(Hwpw*2Y1j{5}6qV!4)$&E|9V~P*jIKss$COj(xDKSj zFBkHshDh`ID!b?BWUuEdl1sYJ$^E2rx=90_L=lspxweG|j4*%31cxejRhFmJ z>_c%=FmOCO^%;390LVA;s{}PKgA8yt!Da_2u4S>u+^G{#U#-Y=?#qlm%B9=*z663>Y7IF9XO;_j zXU*=Mooqcc5aF)5S46U6!DbJFiwu{dviipuel4ZN!o9K29S8!L)wq`Ww?hr)22z$M z+7&?+ziv7*G~RXgX~}wc2*SZvtK|Wm6&PBPEph*$$Sv4pnjpsj2a#uU?eNEIP$PAw zsR(~v5TYGylFdNGA1k)q-fmmlDyx(}-+@u1l-qIs?_m5$hZs+G?&S+sdI;Hc=rRPdbxyv!Xq0MT%)o7V8SmIvE) zG3{@GkqWA`gg7QdPzoy}gv7w2$ogwrwV_*;@0AODdbJ?eXd7?|Z?0D;-&AMadrTs3 zN{4bMKng2M`4An2P2zNor?0VMHewAQyPyvr=;trfM3v(nW> zXNatj9(f=#Vkl1?>w##CFB4t_Fqz1<-SMIMVi+EqW?s%JN3tXl6bw&uqX-fF#G7Ke zQeGO-9K!9hmK*2#ao_Myxbblj0m;-KioM6?iB#sWW@#X(x67GX*(W4i21fFfS6I=I z*6Pj=iZN|>6M`{AEf$(Wwbc_E`a->8_o9qLLdJ?CYt0BmcS%5^Mi@s~Q@B#K^tyYg{YruJ^~kPT_ycZF$0 z<)F=r-M_usbYU3vY3$4^l->`)oG8ZI)e#{PE%28#Z(h@iWW&^FN%knfTJdQ(KvsDd$uJA@_OIdR~y)iLa`f(V#+X8=eqUbX|}Ij3rbiF@nTFs~Sl4 z^V3!@+EDfsBP^DL>^Oef92`h|`UT=V8uN()loa5lkr0kc~>52~- zm;UpIduRaW~k@ zcyY=X?St515YLN#jH(Plwxr?W1(sN}JHNU2?go}x#N^+&qx+OM4!1&tt$OVNGa%6w z(lHkaEshIB9K-XrWgS23oR6xuiut&6YAU_$$8F{Ejc*<>JMAF5?DsJ5tu)EQ*MzsN z`MHLJ!Q2)2U?0P?vmezRJ;)Fqb!eIFE8?h->>^o>$xK$YjH_QZBiX$V;~FqK{oLkN z_MqfNF18%2yvm%#lZ|I8<qb*j*vg&pi_ zoX9II>2%Z&XBNFKY-un6lW6yB8toIyE_F4>U3rzI5?I5!rvfjZ!iK50sAr9NSlX~NnoxM>;`3RF-4qZBcds-`ztRA*KK z`8Mp2TP+NKZ?Z1;Cn+LqwS)MA%7w(aTzMBzzJfv;)$%e~r+e!Pt$MCh3#BN#_ilea zcf5KzdQ%kfDiY>&RM*>dz+}0-UW)R(1ig*!(5D4kJ}T{2V`>5v2w|>ADc>fViz?Cr z?quofA`PJSxM)_8rPqpTr0PxEaX%~z#X$kQWrcO)t}I7y+zKuts5=;vpt2;nUR5yN z+o#n6)B2Fj{_fb~VY*E<)Sa2opG4aNw^A~5I#qPL6qO)Uko>_c!F9znEK6TeQS6k; zCG?z|Bu4x8XzJ;0?ssE@dB_d#@K?zLoBko4beD9$FDt=*e5$!81^wRkTroOrBIA%| zdAImdzHta|*F9v5jYU6%wOD7!aDTi#@@J^weBxQ-qnSC_-Ps?bkTBeG#vM!+zSM8L z1?y)Ii7zWLBnZwMcB?J6a4v=`XZtz5BZY6#SvXvR1*7CS>{o0SXxtix!@kGbO;x2B z$)VhgAFATf@!s7p4w2q;#z6?jjCByEV@RF9x4$fFhRSViRcknT4*M007Y=0e9v^}5 zm#oALi6*i>LS1v|H7qoyw*j_6S&wrX~{NwSBGYQ*& z8*ZwDEJY+zJKwU{KBXz3sfcIU8vIKov0STj!R}K6Cy;LD+g?MbajP?`5dLv79{AYJ z{#+`-+AF$JN#(w7hQv$?gcT&f%bkVLYrF-bt5)%1nNnbHZVaPgj_1UiV!Bdxh~i}{ z`cw6iO5t+0(3TPHc6)2$@z+Hi-g4G7) z%%~XAiXtT_^ivvSW?i!8XurWZ?JiZh>N9KWfR72|egNnoMJ;}^xiz8CxK(q9p> z-9-;LwpXJWV5890PJp_A{Vro@ii%Mzeo=x^(}IkJ%rfs}FaV@&i0OlPAL3>AcHh5$ zd12=Yd{%$-QM|~kd~iE4I4U~JEIE%5w<=mTtDO9dHtCLSMV^*o$YeY7Lka^XS_7fl zPo5)Euo6_VnCh;hi*#D@qsx&clE6q&Ur(y0K4n&$A)yf=C`?zC3Za|85arAUoO?A2 zF?3|}27+Za2QE7st{N4{DH%pLAAKZ_nrCkwGIi;PG*Fq}6l>8*_0H-56HGIKw&9wl zFq?G|HPnVYvvZf#=;HHMXI_(~4oJ&6$tVDX-k@uPByan=dBZl;&XOccS|}tk99;x^ z6Q_7^?M<&?OlhsS&XN>!ELD(?O*aAxDosF8uwWO)bot7{Vr1GrY@gI@&vFc;RTQS+ zxHn7rT`d(Gce6Raf=!$*tK|T*E*HBW4x40ZN0oDoHg;~G!F#@`O*rr#oAH7Rs%?)+wVpH?=J8;hP`y?*_fAWkEV=}&@{u4l=vme|95Nb35raT6O{wps# z9$;zHs#m8+DriKOc2*B!*P<&*ST*8Ap-yd)BpH_}>1aV?my$Z>3}lB~0&@SN@Dw(O zbpVq#H6=|`)d5;-8zutL*68W(W=HRreB_c3>w8w-x=p zSnVDH-luyvx8Ea|@17-0wh8AR#MQTBZ{Bv<^H6R-2U7JHV^84NuIo6r87Q|_-zw3t z58|(L7)^pC_YpIy^Rseb_>HQ}Z~ll%m4`ZS$;MI_-O-!`4N?T!IRfQ5Hide%uRz<3@^$OXr%~&6b;qAowRvYKXNOIYK ziR1|PLU3qgr(Q3!Mm@gV7SGXC*ETHk=DGTI(L&2otbztsF!*Sd6>hfJdq{`z&-Xsj z%Kd>wzeTJ1Z)&3-hd3cyck}GRmWP4|+*T?4RNc1q-_%Xus|>{{*}50CFwu!qyv$-* z09FBj$N&EuJPMe8T2&i3!$yB$==vieos<~~%(2Ur6v#)>3NA$} zSc>>54ATs(D`1sreo-{;1Y1~3Sfpaxog?moBY#}VqkLR?An#vKV<77&vkUBzmtIfP=#EWvkY_o!`899XI&GoISdO@#tA1v%6r<_pif$ zTRsVxuVbf9eDqlfRh@6$HaPN!ZQtb)T;nc|C`U8|WJ#zmXuKIs-zxVJaxC3=aN`GV zs%u{rQ~?%r)tu+L?&05amYxz#Xpt}=uLt4+V50wmS8w^m&%|acGTtmr>?4#Barbcc z{BX0@eJ$yLYq}H{0MnNG1>y<%;Z6IugM5!e9iXhqNv1U=#OWMER3_!?a9+8>ZX&09Vd41-s;ZN=zWLtjZW8< zWIU)i_LEyWnhFVIzxQ~}h52V?YQOB-F26Z(3l$J*Dv$dUCpWF@$!wt#@J~p+QXpRu zFrfzTdvOL(m8&$3)&#$CM5BB=*89*l6=6ePL}o$|-|VkfxX1+*d=3p^1)@IbKcw~| zaDbO?JOeFLr|qytV&?g$_rhO`tUaC)`RRvSpv9td>V|U@))-&@r#g}~@@Y)09#fnX zR7}u-paz!5DyV0(Nf2U0g^0UUz`HToT@5eZDU#mRXlq{13<`3G!21rIDTQ;Yf?^Za zLiCjbe_f6{P8RmugBNbWiTArElF-&voueZ3z<)Gvn_g1*o=<1m{r#ZBDrm82P{<%- zoXn!GlnXffQgNYJ!LAnuBKY zfSVTosYA8HGBo{%wXTZ>CYcEf3zcxvR#3!jltddBtDQw75TO*&xE*icWJJ3KTJ#kW zs|X0Z0Qc$tBBA{_jf>e_2l*y~gml;Y8Z~h%6Z?J^M7)?;DXa~9HCLcI7zO#!HAt6e z+@C;@#Npn$s$$hk+onoduOx0Euz%Vrs6=#4X$kMoJ8J^z5)c0gxbk3ye{2}>?AcUF z>y^YU1P)MJs7lyon6jof!^|1}4^mc=B9XYh%yax*eo2^j{-OR!o2>;HxsB9%;O5xK zc_fq2SC6LkffZT5xjFk^&!yZlz7va1(xNT09y->DhSPLMU{+WR;ex}Wq)(r_kM8>c zpMLvkJG-*n%PPK@AF6wt92T;k*gTOjGT9whIKns3zDOw(Edho75e9ZRBq&imEPPRAeofBgo5Lj!_YasDsyk zF@O9bqsVqqqcWJph??U+R_HEdRE$ZK*KwyEM)Ymoe5bv%aEt0sxQ#>9bC6w*?_xbIbQ;B$mR$%B-ttCMuR5(?*+$cE6D?Ph*;fb^1W&sj>*t4r z_fwO%m_LuGpkT(wE_4U?JP)(FT)gV#x;>4Zlywo)hrY}>a!C)j5co-LeNQeOj9WI& zLFBssm?6%S4R6byWds{WSq--kSW0bO(ueDIZmNupQRU57ROGH(cvw?1ThmPDC7kW# zx|xQYYLiiNRh=>dMPQ8KlxKt--%Lre)?YJOvrD8p|FvN?@%kPUdvH>m=*PVDYmN2N z*6@0L(K6D-IJR08nd_c?_H$C{&gzQ7EU$nl#*q$HQKhn1Nb^tn+L z)O;DHPONq}BZ*%oi~O=kMZUhM`0{ktj|DNC`0-(YCtiCd=2HH(%;FT8?qAqi`~F~e zEQL@y&4(~A0qdj;t}|Bvu^(+W9Qwx2O7W#>JBYoTJ?Xx;zE5tD64qQzT`jI-9pJD+ z2ef6ylA_c}2XyJ^x+7VdrA#v$+jgdkse3=3y!z{X>MJ=dv9LB`4blrCfsp2JUWq$J z*$e*z`Qj=-Eml6{p+IBIZs5M+d^H*sJ?&gTjSuPQj+Z93GF5}tyMcqrc?e9v$%6+t z7(%nXZ4+a26ov6GRYiu}nQd;Q$(L*V8_S1!+y2v+b5%l;OQL}r@6gdh9?3Ba4Dcp1 zCX6x_cOidK@@d!ki+$E1=z^p8dxXCP#gM#oh%vwf?OHleG{;~@%$_&!sz6HSOEN{p zR8B^@hBz~LPOb>pqVhr2|Q5(Ysr`2 zDw$ma4yy3w?5-1(6dGvC&Y$mr}-e#Tb|yVN~LRHiGo9|@Bz z84mH=w0k&~vcv&ztdpNG(?!7dE?6QCp$7v6TAk@cn`;5qz-(0#W#FoBLS&%n8|w(% z4V6YYu(ZSSc~dB)_{pj&0g7+VB9CJ1&;=R}Y#$#MfX-l4-iB=#lTDrRlFttey8aF!6i^K1Z9Q=+2EH5QsVXv|DxQ(}vgQTV{ek%($7{p8LsHy=wS(P8=e&~A+J3L>w zCT|^a-;^O`QIx*M*@}WXOAS_j)@+c>Ibf*Aa~q@ddkzr<5>dhS?aaW0fklYpu#P_0 z#6c}ALOvl#qY!I3Ya%7mF<2DTn!YchQ+KM|mt;)ak2m*e^DY20ksak9puZdn&)#F< z;9GvY0QCgMDaiKMp|F)d=E{e1R%Wx(`8mf))nIn>B^MhaC)qUNa;c_*U4^!@nOA(T zhVMHd<5t?Y?R}4Zq=?s_;d>i3YTV$D+p1$xQ9o-{HiK!z>vSx-~Gb&*`6wOBAc*!rEE?bPdOh6n5&D5SbH&E~O zNVv~aoL^!nhGGu*1~H$JmAzBF^rOezN_fvVIntOOw)m~VS*)k|p)v;~+L(|KNqQ!_ zU^uf420zwJls7`Z;%M4N4P_~ESWFbEw)o(!5xcL?P%hu={8&0xEps2|1h07smWvDp z1clU@zLT`=y{g!v41*7WXhX1LyF9MJUML#;uJEJs8o^P(?P)nB+ZOx(vS$Xl_}h@ zu;bihhScQ==EegzVvG^>&=C9e4Ks>0}gM6Ov=rPjHW&7T+2bvcqP!#czsp!gv^Xuq(<|ifD_C979w5lwVjs zj&z#l-l1SD?N#?kmL7sqMN7Ed16yCV;^66|P9Dr<7j!B%+TS0zw%6cHZ}SS%s!%TG z=b}7drJK5md05X*Xi<0_=B@q{D2cgXdQ*p7nx^z!!|Ef&QqvT}15YF@wBk;{;XY)~ zl;yr|birSCYH-xzMaEh3wmrCFE3%`#89z{ORgSd8G&D7HqpWQ8?GlhL|7?%MqmiOt z!*GZ1Ny%VS61zBT1amlhTj1K>{WEE60Ox;~F=<+6^tmWZ^Ia-2evfOi7~qvF1$Wlu zl9A0l`jF_G)0vHvwqFSTjmu)qMzK(V-Z%mfOSi&CJn&6>Jd5L?M_&uL)*pM+lhN0f z_0?tzhP#aXVRvnY(a(m@yQn_p3ABul-+qgek4c|93j_w51K8OGqz6$Q?qo+)e>gZe z*mC}_*tk~PBRG~i;Msk8-zc{$={sN60=p}u%`hY=k2U0^zOF3Se0o_-20scR(5gF|4P6WTLTdaL-1Stg44(a^j458PVIviZ$e{SQk+W z(kRy&*{E53b1R!l;oT^)J9Rj-B)16?kWy+C^Rl@)x(DsIg+TIh$&bzEaI3sYvh=Md z9SXg!eSP)VOj*_?$Sc`Y+2rdlPSmmZ(8@Wh2pSx*o@mid0vvrdM`3d*AVeUuz>8NA zG!9FSXYy3HG_kTN;1aDC=7FnJs^VTrmG~kWcKqv-`dFk&rkJ%^P5%wF9;ze>3!#- z*S$=;)eT=M*+fS5@h1nqy7kLuyg>xUDOjn!Hloqv^M#969Hr~#GPk6d|Gu(*uY%hn z*3zY{R?GRkQ4;OV_-r_MmyRA|k??`gly8N*05Q3&tA!u*KU(6$V5j!mQ9ubE$iPyV z_&f;ytIU(^Q{Ms@-$NNlZ&_M?+O!%878BJpA5yEdG%d%nPg9Ey-lL<(648KT2&s$J}sMQh;8 zl_f!L9)XIYMdKU(!54TLMs3KYetIL?I16}!#BkIMUD!rD@Kpq6P`Ru@TKhANzMU8QQwbBCRH zpN?KtI246$;2S#aAp&dKjE^Xww&cTcKsa|47dJH^RLM0P84Yz*NpOXSrJ!y1*9ExC zYSgTky~=?s^-+pRCOKb8Rau@$=flApbaY{G$x>+=xb?c*9(h!&lwV(uKu^r( zL<2zY$W%h~4CJt-VO~73Qr{Hh>9)$Wc~NXxNdAOQggOV(m|7F;iZCTY&xC-N7TQz~ z3sjE6HL^y^E2`}$PRogH znLVo5j33|kVyes)c;9vQic->^B)%_(X35@GUt{_lP+G7O#*N!A+<9K4dAgeGWbiH> zT~}y~H>D@dVR9mvX9b}MH7?y+~x~0j24**6p zCxQWVn<68jn1b!r5c%?fpL8~mP}wlPTFPYE;%zF)ZWg7$tSCC!3CLreWdbibLt9p}Z*rX z>v~%j5F3Fdpagp-e)L<5=|}4tIAyY_w&vYl%Z!(%w8mdKZ4LcU*_GHdqZE!8%f|3*H9fW1g^z@7}n?Loidnt3z2Pu8< zRe0g#;}DA!tpiq)MC*xZbZi~xz|k%Cp`>Gj&2O-!*!Er~R< zy6r%b1AOZat3;e$0tM`2r1{@{^S{0tt($}6C=Y@0Ob(<3E4p`ek{mK{bY)o5@>*7Tyzo99eWQ6e>wJ>5%t#J^3K{2IcpX^6 z_QG0l8MGX=vxl~|)yR@g(A7pgh`Jy*$oOI+P<2Z|aDM0OXxs9dLFy`kAT|s_--(~S z$|>|i;d!D)s-2G|tUmQc?^^g!sMMeYU0A5$Q(~bIaZZ3Q@$$=B z3bA3Gy;sLL$T;v8{PQaKJo0XcqJM}}SvcvFDTmbQgRZlUQ%r^6i0YMMPyy2<@+@te9kC5!1JHf0*E@i%d*-f=;RKlj3+C%dyR&GBNDo!W@P zg$%Uby7Fr*8>y?mzUZ~NFSxSRy}}Q3#PM)NslGJFn^j8M4>YcLEw01Ya~Ty2U?q9Q z{xVnUI+_Jhm{Qs#pXR^IWMJEOJa#Hq($8NWtTz1I!q-r}HgXy%x^? z^R3ueiS3K)RR^=jB1x`m?>z6@i<<-hS+zg5x~dJ1SWtAij??*ZdCn0{s1Esi7rAFz zmlsm=h?2My@RY-s4xk?{IgV`~{Gn|ECK>y%GyO`vlpH|nGp9_S+Q`$>)F!~@(evGW zm0DxR1#!kyrZkFpGolj@f>MSCJkagR*|snmD~{8a=m#j%z~aqSS~?wp+jfa#SW01t zPc>kr;OybxTBSDxTcE?IKjRk8U$eH@#rh9+u{_P#Xd#A{tZ zHOOobBTjCnwa7Ze3!LMDYd3-eVnhgyhNl7Q3gJBe#EiMs)zc=m+Lg|W;xBpcLw+3!WiU0aIDhRmhFy z$&z$~hsgISI7Uo^R;)b3OBZrFU38h$Zeot-pR;feB3Th#d0@Eshy?|jME-K?admYI zk`;d78LhxORZF?FT+j3V5Iq6b71()xi!}h!dugu)x&3Abf6ynoIfZbb-UifX<*-43 zu7{ysDH&k}Hkdy_tOkiM1`8ohJRiTE8;n@fgPRi$-$}cPCtlcJ zxTt64`ZR*1h)x9}(E@0?A0u@JwCSU!RWQhK#KN3fmeFQ##I?5R|2L3qAo87*Ar2st zed0IAtpsTNLDe?&5hczywT`H9AC>GSuLe-mg5gU9u>)8>*PHQz*o#qk$Q&L7_sAis za?jLj)r;MT&UO)~biV@(?y#-NJlsFBEgwFqeDG;){%7iA22ifK10d5iAl}VH9u;G{ zgH)5KIV&|%jnp&0m~a9n1kEpSb`$Vq)qmsxg#NY6vCid%QyQNdMHhs4602|?tTgwm zlytoz$qoBrge29O3@C~i^yW%wg8@^rvun`N!P4YJWKkzGvwnu)zVUiJ4oS!7p3Ek4 z?6R85teSQJA>FI-lX@q~jcsyam0UQdIQUyxw6OGr8P4ohrm zJWothhybm0am^{)terh_6ILyf?7@c_Xg@QRG@9NklyWoEG+n3`ka+?)6grfuTCiD0n)%smt;{r+!9k1D5!{u=5aNRk;UTHD z(~)Mk%BvWqZ5*mS)gND5f)U{SM!YNXzvS+}f)+!#I-_Up2RNZ3U+JcPiBIW3bRF#x zplW(u{;t)CA`S~+--Yv@ve0@<1=+fMO?7Leu9omP&!=4mtj`?!7ZGwHBnI5r+kl&8 z10YFToys<)WWKVEjl!U=;oQ{dq4Ql{XvrP!W_e#uFrKe`qoK>yccrP$@X8_JE9IXq~cLjbLr@QWp2)->QM25n&fiNnt5; z;mN0yxIv)}T&X{_QmnenAI-t9?)2gp%b!6bQCej@1)o@nJNQ5WgP{u{?PDuww*evq z^G$%U=lzi7+z5rG42v{?-Swt!4Yd;F*T!(DIE!&pm~Ck5VI6i3$TsvvT@UJ3TYUt@ znV)`oG4@AP?p#PA>B>kO*;m=>Y6WaQR9|(@IMOeh+peWELBr5abkeW9FEP}DW9l*l zJfuW4J9$lrKN;9>`+n<`AQ)fZ*gTH^_ zjkS3rpT*XauBTd8)3+`$bx!!n4yLoS)bN57n$aqKmICwDadV51E*jm;1iWSw0IV z&+5hA1HSs@LfvlGF&jCkV3{8gvd@UNMOjtWGW$6p9X^pf{yX>PXYCOa2?5#TK5@^< z>&pM*;(9ck`v|TenDfZ#gq6{6gn+99^v;lAQ1qn&8xvh)SmOo&YMG$r5HiIqZcfDKS%5H)<6UDvB+<8)e z4TBn$pDN|3XW3L*7N5+{3SsZ+VW1Bw*4+W9SQlRT%swQuU1|E#P6M(I`_>J7UAFY% z{pin=e!D3Q`NYzr`>nYv|Igd%(oi|UO2yh8(Ov|z9s4+cIA~mu3Qx8*eaG5vJdR9s zEb4p^+xbQ%EYc~cqk&HHNFB;D7J=mS;tKkVU}Gz`(ZZk3dI!MxQP`c5JNPEyL$$8M zxBWj)pEbmBr}rf{@aDkJbRZmYv^w%;V!hyOmxh*U+CRUR}emLbVq*GXq zz6>l)WN!q^bOmkQQiBJaXzgvc%cHrV?mPWu^54EplwhRAMI&7JExOPjUo%>!Y7^SV zS03l)`1>qlx#-&FRn}Pf;dx4bU3Phh%t#hZqQ5LSXY{g!bC5&9RO(DmQ#JI+YUZ{% zj1g*(sKG^Nn~|c_m;@80e62y6XM>jOSo5|}M>u@FiuRQ_23|VmX?2{oMWoTwJ-b|( z(*L}Lo?k3SU%B{u-)4mpNl<6=Yxmxa>P+doz({xCQw3#3T3QkH2hIpVS?z(U#|P=K z@4j6}w>j^euoMQ(dEhp}9G2u7I?<~plUG7=uGYi2c*#;Pp0tj=njTr?wue-|ay1b1AQST_joJrD%#nsj`AWLalQ*%AaW=d-lr5tkta=y7>dDD-fGtG}o zoYG`&n`GnoVY{Rvlq+3W%XQrTPYPHi#30bXJXvx-V$9)%U8AEDI%pC>iG-31bdiWm z^dHsI8JH@;6dbFdK4f0+^Dq;9UlpJbX_y z-Q;)KY8Z??PbzoDi{pbjwzMMK%vzSgA@=M`RlGHN#dquDdG$fXuSU;@{(@Akig=b8 z>0z`?vIk@>HU-c#rUqS|^S{Kn{cyNFvjSl_po;q&b=B@;RLZ8)Rn&D205e$bFMJ4Z z)d&>`FnZ$b0l?^fQveMQ6l8XG+pRxj1GywfzvOu?QI5Xe8Z*~`y>fQ3?9Btmmk#p% z(-Aovl`H_@b%ANI1=RwK6kx;G3oFGYAf!@l1?!YELYJ0_a|;JA(a|#RDDV@nc-jyg zM{uVU&W#u>APxjBiJBw#bL}Ydb*SMEPy(Ntz_DAGJ?(tylnx$9$9Ur#YCRP~{Q1P1 zt4GQXBj+c(@jcGd82H`lE95dbSCcAzfJohP@B=*h*!`xIxbx1+%P^-e=hvO>FVbbXv>F%tKy8bG@G~`Ct1+dC;@ApfG-KTU zGBkeK)5{l4<2|tYs@W}Eel0pmIMm*5_tS#+MaeLp)_>jaAJqo{l(A5ZWu=FPwNi}p z$7VWjHVrmWu_{}Mn+tBPfi@%?euDr5U zuCsi!1+B%=h?DHjxJyjntk^abb-$j@UvT8}K&4yGg>Sw=038h2I0b*NjtBoNSH7U= zfmKy@Y6IF?*MDJ8=jQ2E`NqfP%8_`F1DJ{JHdnof+7c1+pAaG{tep+y5GcZ3ng*DB zCC?R5Hs-Y7V|tw(4WF4MI$Gwf?~x=ZWrq7GbocCLhxvr5K$A=@ zZs_y%J*vXoZdL9s0jCWxRo@mRiwdDEx4I0h@{Wp*PM~6oH?vyPMvuZ}vcS^ReU|k7 z1I`O@)R}WN?dLRHRetaT9qmD0unA*X@Q@BR3PnD<#bG{VDqK)@C6rVl=hng1MhiEU z=FJ9%*S(c2U1iEa$XR&@-rF~78rvZwd-7=uh0W_8{m%eqK5_0bSasEn$5u7Zy>@SNX~F@u8K&8y@YqC3$g((4&;noP%Efm~1+(br z2c34f6|98T)>pqn>-PnKO1N^lkjH5kAwuBq(<6HEdn`SVRQbg~eUj*!Y6-Kp+fW|- zQ&{|S0==&;YM(vpVgT4nLZQG$yb)i|t|M>_qq?s7WL~R58AhmZMK|T3;)oN8@Gk8b zjX_e>7DUURe7HG85wJH4f>+R5CnEJmggr^*r;csA?Zy@xDb5RraA!L^+(!m6MKMtX z865~&d&HwPwsLzuXErWVvz8xec1^+D=F7*W01$I*S=hEQg|2CtmSrBVLU^NW_Ys1Z zPGW=E*^ScEuX@H@*QZNS{A(cFvMI^YAIuCCK_g_mq0tPYm=G-bZ?;YfE{y(H*l_@+ zjRXXuTbnof+#J8Tv(n$Xep~MK=-4)#Jc+iWZR8^X4YG>MYY*<4F88tTKkYnIDT58} z(gM{uFL}(cLY84hwtYaa%|g4F^RDQaFg3}``6G;9=AQ{r_%$U8yc^qES`cWdNCGnt zcdy4VOqJPyMb&(=$fUCilw=0ZB$wJl#N~Sln{Q zsjS5HIGbXX$cNhDChZ}Z#P|53iBx&mu1qa0J{lK zhT(CT!>TX^Q4&uQuKVvVC5F8D3Rq* z0!tk8^Gb?NC$CINyQbkAF0D~xj#QNsYf{Er$<<7gnr3(rU}6yrwRWnf`AwMtHv#F} zVP(~pxPxLt*lRDpXuXC;6>kt%x|sh{DDSMyqBz4C*MDe7EG$VJ43@Valokvj&;c#g z?x7}dD68rU_dIFVC`g+Gm>p?MM&Z#YEg@Bi$u8)$DqdJ^!vs28FgI9plL$W0@$;4LpMzwp&)pUV;5FQr8X}; zAymWcJ;6hl=%rz9G2MxBbm1;ZA4DWT2_Yi;5>E7$-_@s@R3WcqHXNOSNm5RO`v9K? zTqU>1t5m;hAYqboD})J;uVUkjT?wY_*tM7Q1cJtciZZQMX!_u0?SGIuAHr3OqVlev z;SHK#pV|kNg(umlZ|uBD zvIb1E!>UCJgLlY>SQ^+>?HUA%enuP%qh?R%nav3P3CUG)lCPpj%N>GSAK^y|8Q zf#h~rmUviRm5VhoH6^&>3Jk+C+!m|`8nrZNcnrRrItQMS<+iq`Phjo;WDUz_?c#Uc?4!8cO!5SBF8V~%)taW;zW z&}<;KBQ5Wa7q~NrN=g=!3R)7Z#N*&}f6HjjokQQKao7W#04*Ue@|Zu;!vlLgne|$( zaj`O_y`bp`HPBxfQ`>`%=Xo?B`YezTiIklYJUG=EXOW^1b9>@)f=SCI_Tqj56ZHHsq@uDOb?%k|%^*8mhy&5`mZzMoH3P6eGv4Ai5uE4xW zE*8BzCS?6ZKAD>B@Acq&ZtD@(2dLGl@_CW(>(=~E*nmBV zAcTaQ;h$7qt)zq#F)Z2qde|x-qs_USH;#H#d~0q0>8o z3aw~|{e9t=1XH7}HM|M#Z09uYV8Acdv8l-r&|29KhJMK!gY?`W1Nel4xpAOu(}yks zq=&TbDpd>OHl$7JU&T_UXo3>Y;~C`0^Rxlu9cAo~U6qGnS@JFOx*2BP71jXkgw4ms z?>r%g_4jY>b|sTTW6;x}+N(T}NMdEdfJnuUFrfEmekr%WfJZ$K7nTb2VE;VHHdj}Y z9mZshL0YP_dZvkFtSP%<-W^iKU|g^#&W!0;T-f$PvtHp5?8vhtsiYJRB)X=s+UhsO z6Jv3fD_|UMLaHsempB?O*ea996O+d20McWAEWxw?i z%05aJ3~~k88^>W?Zgm-SpsmL8Qr( zJnLWGLQ49ctW!&kk^$!>rj4A#rJ#zfmO3)iAMzo%txUz>WY*C9NC5fOU?gg)$iM2h zy>~dbK@;P=vF{rCrU$k?YiARh#zFceZKMtsB|Q!WaAXLza$cV?{EXMu|0Uijr+T zkeelH954g)Z<^mj2Ws304v}R)U7iQc-&GZnuh6^l5FWyN5Lz zS=aV)3hVRk4Q*Eth8ST!woqQ4)shlSFg)BGVzsp!Y$L{3Nl}CIMt*tZc`J2uI+atT zBwJT6sBexD(i^8kl$kMdp%q2JDF}yauNp#Y%6No;t^sA2WW6U7QChM%6_V&`A_}bS zr!3r5Xp93E-6T#>k%p|x)oa*7=kbA_cs_X2nZ&N&=lZ|jyXbw|JOiapadV0~cvkow zJb$u_@a_s&x=qu}GZ*yq7MGkFqCA7O6gQ4}T8SfV)j0DEkLa*H+@ecm!=^zlaSaEl z2Mf{@4p)2plMj<8ZI~sP<7#7WI&lie8mGHRHqpWZF8|SGBYR$?a*}NQeiS9h-G@?7(@#XXcH zgm-Gq(H<?0j z?Bf(ir39=Y0_tyX$j`ZA8uEi?1;Xuod_GO>W5WecWK5mwMDsu+tm=uY?Q&y;j#%2V)k8wq+%W#khfiZrWauB*CTL6}Kr0?~9IiyX+R!5_5P^jaPsW z&qdYI+y-2#)J1Wb$qjt6jtL~e^RqiJc%z~wVW@4iOpOa&v@`=KrIS$dM6Aj^599Sk zq+&vYXowj{2tp7BWyp8H{YxkYLyNLlntyIjmYITLqbFlfL|a@HXJ?EwOGF;K6<|4+ zz*~j<*AHZiU{i1nEW&AYX>1(0fhU*W$P3zRSgkBQ%+5}GK;N8%LKJ2h35p`eF)2;r zAbE_wgLrn5WOkS`=HRvdit;(f@W`R-YhDl+-RJpK**p*O-nOa@0ndEK>!PI;yp&?Gjkk3?bwzXYQDC-W+r?88ic_X^3$%4RPeIXrZ}m zB;|vtK}vEQyD|}6l3EG=-txG10Poq_v0~+@HV%G=!a4#01lMgUV-w*-H0F1;gMY{( zy)A&|d-_VOI~>mIYvq(c8n;GAlLc2MB`33GLSD^;+B~X%E@vm(G7+y3-$TM3Ghv#m zan-stCERXXBG1_M;e@3+>3I$LFoP{#-YvnZN*54i{IUbvHf-!l5LR(ox_}vKz;)?E z6f7p&yaM$)s(7fwAax=Ep=(}>1JCSk}k8nuA zLxaxRY^c0Jmg$(875Qu`HP)2;a3Y%kI<<$Q#hG0NZ_$EP3;k`DV##n=13j~`$5wI@ zDhX<9ffE|F3E|;Nv@)IJ68Mo%`FNjLeDGT99?5{W&r+c-c*@jeppo4*cy`o^?6?>Y zCWy}uijI2qdtlm)eQy^h3?q22AXi>{RX>|ful51jfwhF%I%C0EGd-f@5){Qgd>Mrl~b`jgLo;W9t=kc2#pQ*=F?7!tHNp+YH5lY=LVA$N$ZXp0j_A`P@<%KST4+`Pc0zK`x0+6^_%Kup&~;f zb0mo2rF~0xI3?rE92q7glJDTi#O38gX^2^x0U~6Wx1P5gFSpH&d!TDJ6NZ4WAx;H%bJ#qsy+| zp{pboRO_%)|K6tUwB(seBO#4e6mbtg$|UXiB|})6 z5(ZUb@Jw})@i0)}Y=UjvxZL?6(bnq?hNK(T| zJlhDKo)x2JICf-jI?+Ej1kzKNFlA9Vgu$^0%%-+mFU#q`x5YBhM5Mx`lpN-qLs;61 z2elQ>bsrz{0N`~bdd~$-TlyMQfDj2+)J1o)6uFUy9oPaI>N(%Qfmx!fnv9Ag2b-%F zR4ao&p1k<*Zuq;&b7o~mWNFg3QDsE3;_Uxhtg}+>w?kuD9Wv$&Nqd3#gpJazm$((( znhXPm+Vp2@XD1d`mB1O1S*dj4@*ep>pK{A&!bK!2;Sy3Rbu(%hx`~h}7&ZIDWpa3x zSQweFzlyaS<wt(5+M&Ecn;Ye%GZ!bTZ2n zwyuMBLr#C9`iykUdUY^6mB?CTHS#igycKbRC7}KiAY97M)8fJ<1qEl6!ioPpHW0@4 z%6Ohiv-oWFM{CN$ZgJsf4)>7`5T(MZAd&Zd)t6c2h1TBPm)^!-{rqg8BY)xwF>l@c z;dle!DoTagXj5Nil{d(h8vC~=H=$kuBJWnOT!Bzs^CtN;&r6nQ@otSD!+zrWwTQKO z7|H$!P14PKo~0+-yOV3@9h3;)yF|AyH@m^XBhvv#9ExO{cIJVLC3xiD*(Uir-*1@K z-lU9!{jiJXPOZe%wU|N+I^hxW1g#aqdb}`k@(&wzS!2zIQBEti!dHfAcCk@>FBD=igZU>V}WM<=m>~N}3cO9Vm?@Gz+Z2w(N);4NseJR;FKr3bo&RC|B7T@OL~I9#yFVsx8zB|7e|zKndC5xANk1_s8;>AW)V6lTmO!Ki6`)ON z8Fxyuu-%f4SIwNou3)Z$Km*`P90}s9Z14AoDFpgz1O~=xc~O!*x4IOJ`)%$f;4Su> zx~qS5JOIEKhkrXXgEJ*YrOta3#4HFf8`1uM2gggh2K#CW#-FxjO4S9Y^fLpj4Up#K zJ+x;-L`4mMEA~x&U5{!U4$`-b(wsh-O(a)Fg|w)kM##1~z0fNQ-fv2zF~02DP-s1b zDQ(8{7TYSR#qW~Vwz@p5iz`xRC_L>YTl3|Jme@SQ$+GadKN$$D@MgADKQZFh$=q40 zZgigt1^#%xW(K4^yCG&7w;(g+s-`L z#4DF%@Q!LDri4nZ3gXN3=r>Gs&8SN`!t&Tb(8#Lehj1~2vO6HdT!l|G_VQcdtV93Y zsPMR$PgM4p>`2>2c}^n%q$hF5c>6_Re`| zX$?IjK||l;z^M?6WT*s;45t>qWAU`sb~InqdL-f~wnSCe?9lnp#|jZJS{IjRtdp#a z)pR8xmO>Tz0m`tW6`<#934;mPu2~|V%N3!isH(T-Jlt6?yJg&xk!YEz2$nsnaxrQ~ zbQ%N`SVR)%FhyEXYztYM!m8XAr^`F9I)2!RElX!!v=|j3H4HfU{}vlBtXp-gAGqq0 zc*{s;YzZc5xu#tf!;+dvokyB|;wGX4G@>IqVs>mZ-AoZ; zE4@=nYS-2c$whRQ2@d_Aeg219V4`%k-@|9~{F)aQ^N#T9K=E z>2}95@oQ)Wiw}=}u|&TK$9T~t9ZxfHtfL-O?@d1V%698!jBH;B8^6>O1OOro9(ukR zU!GPdp^R8ca$&wQ)|H}|pJly%SwT-YP=mMqirj4YKq z7(D10y2KggJCe8c+n}O}d`id^N`JAWqYF7Nn!#WAI^|q_^G@9!z|cK}Ov>nDY)Y=G z*=6+5GZd|ElDG36V_9f3I2&dMj=#T6t-X!SB6-W?#%Ef;OR`V#4;CJ}|L^Q4G}+nY zB{Xe^>kNQZZlyQ4@#b8#X}}tDO(ynX-)B;|zWG0=|n_$6!BJf*`p35WJ=evH_bs?=FlyPx&s;w#BwS4zE z_I)|#iF;9(aQB2&qEiZRH*)!Jha*ceOLi4ndN94Pm?cKbL>Nhlh-*1RtSBg@?*WLA zZARqyeD(6B!rrA&Id@h>QVdUn!5*9Cp`a4+iG01?&r>;`X%4yVcOQ`xa+9s+7IkpOvFZocak3m|ALMnXs5 zs4u-5@BkNfnosVO3s#!W;%|#%x$LIX8$1`|T!$E<$K{wPSZ2#SS+qIO%kpI!P#1vI zGCjrTOQ?K4-Zmg>P%Y|&7$9}tjuUSal(ZUsQ_(fUfZ^D{Czu#>u{3qnp9|L(lC~%c z>c^rnv$Vysw`<*1)0@OyZW)$G7+4@^Ej+51RZY+d)R~qD0GT_bD3*>RnHL-C$1FhB z_%R}3y0~f*dau!80ByWLvSvZ{{ObnI3RtSfO+>hun#RGpkvnYgJ{>(07!VqX4S7rVcBNNRW|OIeJzvdpJ!2tTfJLAG zJ{DTmtUP+7A0Td#|D&$;C~+T#;Qy-Y|9m2jynGp6yHRnHDZ?MHuoRh}}-0E~E zOLja7(s}|wOg|RUS}_?>;|)&F`*r{(H*v=3u<|r$PVq$%rlD!Bgg2d;M#@e;XDDY1 zb6zuR7>*AI3RYz=cPFwRuGDq56{3VZMT~Zx-dD~(j})JJqK3P^%@tW1S-Vj$Z$^x= z1<37at=5qee1#QD@$1psJJ|QO|FO4}3Mo(GRaAx@c~KX&Y8v19c(+;3B=OH*#m1JY zoh+xUnHWXk%a@)!QE-$xl}k?Ev_^#GNkwke;--O+=iMn-cQiTiRdq$kQZ)DBJ%x}a zxJ_j+3Tm@)dtjSif90&oB&^I!H;DO0gz&ArE(k$$=^PO0C`f32UA8KB`8=xv>_4zA zYg=KkoU;*Is_bO%ky$r6?~57<78Yx=(OhI!jZjb3*!fUUs z@UG*X75b|OKs}z?QROq${=-SM9rz=7uScj$qj+Pd_@vepMR{#!$EOJ5%OBB2+AsUS zJ|myeWcPRE15qwW&Z25hH~KIvq1fURdO1L)`wT>h87rL#l!K$In@t%D8S?0{!VK=} za*G8uHn4^8v#B&;d((^Ms6t=G(=FTayVVR&c9nuUyt)O%lsrev=gHWxd}j7M&4x}{ zl0Il3){=}RD|0_w-|TcJ@3<32w;P#sF9CcQ5sOMPK>Nw%nVQ~U;AbvbqGFr3!E`a5 zz|zz@m!z&^bf<|b`3XsT)mWo>ZE8Z|zI?HSK~B`>SJI*YEhFs92bDF~u7(7C+oCHL zLmOOFwKt^hfl(f^#4?>!aNK^E?WyFpsckbb_4}1HRuwY7?>WKC^-R{!R^1Vv0DQo* zST|0yl&EQQt@v5nOhx%4S!TGjwe=k}om|n|RO_Z$>vKf*Krm9SeVAqmnqiVMca9vh zn2}o9dIGQ8iwe9vFU2YMzmQd=xa#}Hg@qTB1G=pEITfL#TE=3NZ6ZK=x&{|L3d#UC zJXNHYC4h`U#8V(7qEp9bYg!QALibPA`QuuIfpJw@dqvHUK3jhE%A-@5H+5U!`JYiXP}!N|Zef4&PCE97Z0`>$Ae3NqHVwY*U(?l?F58N2sxE#${N9*= z^eA^DM8?fNERh2M_s2)hud7LFQ!pZ z@c!#dw%61-)=hZZCBm0-@`4Ik8H2qt0U2ScAJGFMdzPmh@)jN4 z26EsTi}K;B$bKErhGTc~!QeYhYU?-l-8mEqyfxhK-7hjd_^`_F&QSmxf$s%Eh$YSB z-6l2s1XchZ2p@q>)w{Z)QUBz&oHaG(xwl z{~#T%Xo>R+H@&d6_>e7vIXZp9&K}Z(nrl_5{{!&&-LPaoX`>pcVXae}FF;WI&*zwd z_)qilFE!Q|U|cGdNPM}T<2sghjFQstkg8ITR!bCXEC3&v84Sk1gjx?6(?5Dhl^|V- zL*AmJd#ntM-WM=cno3`Hi1mCnc8{7?yT5o8rw`RivsDX7}22b~Cd zztYfDVeO$T*8l`ahPjt9Mj#QV_R(3nhJLj7k!$%QxGvHX4(@$AK4BRhPg$B1qPWkfY)d%FZ*XCr;_he5$>z7qM0y9^ei ziA4Y!yAq+Vlr25Fy1)@QfbqQfJYxnGc@JjRi?Ilp;eMoFNiUk>uD`0ZqM-~7X$J!O zL?QGDu(~qV&UZ**(QkYn+N8l1GoS#kZGR+~c6X$s=nf|+7}llA>vVLTrYZ@_BE?JG zA0S1oh7z%JE;5Oq5T_}am(hWLE!5l^wi%e(78hky$J<-#(U(;krC(I}b~S>JDB<*n zEV~TxARMrnE4b7)HSMa#!Om-EnTK?O!okOMbU!L9Gb$vGbm07@^HW zSwh;i(YiYQ5-8W2Fzx=Bh-!|zZ&{d zsisoBFNESFZa6?{kz|az`!!wMnWCL=|^J$G^hUg}nYnmH&W}0%<6B>h!=zNB=|*!ky3F^gaS7 zY)S&#eZ`Fx`d28ZE)(N;ClU3%34*ZEkm)vxQMFwXF|Fb_C1D&DKfbsN&Bh_OYNGo| zl_k~nh+F@R<#{qiZQlMStLQP+V>|m3E`%Kt-?wabMZDc(Le;U9`7PP%t(Y)5vEyUt zEPhR0?@85)I%}!S#OYUA9)`9*RryGDK1ic1EH~-GQk-!`F@gO%VL-w;hh9~!pLtlG zaX)BMh>|ipfpd-9n@-NRPH^t^SceG{nBbjIrUgmS39xpX)&9F_eC*)}g=wg${ZU`R zr6LKj?dPb!u!|qTx)df7=`3Z~o6u8f+)WD2-vV^^&&A#D(6sv0GhAA9~u56v#EMzKm z;Rk)zE47?10OQEWs$U5Ke9M})fl`EKUBH%J0NXuaaFQ|_8dIwxm=uX2*J;}*Za?FN zjNL_7ei9-g3AKo{njW}H23;3hE};Z}9)21*VReVAyBPcS=-AOeyUFB1vioz8T?+Jw z7^rztKO$hIcB8RWr~;Q!5QoHG4Pp8ti_1X9=?wv)?*xQcFgTeQA*FI)s;a78If#8- zNXI985bJfK!75gP89@MP3RrZUxjA(5ee!topEkcnrTYLCK#mjLGTW}5=gz!CIoEP@ zLrOBJZ?{=Ff-TR8$g8lmgw|DXwTx0Hz@?ZbgWmd1-g%2!ERQ-HO!ik*AIbmoI(2z|55i=vU{Py+=u)ayz1XPUaYva_P4SAokxxE81SMii1=ZLme+RdpX51xW}YI7?1f;q(H12$ZwI3YVb1 z3Iw+MI96?f5KiY50njM`Ca(7==^EHwLIlZFG9o(+!QVX+9SwR6zJg0t$)ZEK85gS& z%d=uA6BA)dEq*OkX{PSu2D{$ny9!C)uu}-^Dqu?`>&qt%SuHLDv}*I;H^^1&AQ+36 z-i(9=U^#3+V5{(FdDkV(HL6Cwm4h+hq~0Q>T7TU%BZSAF2L$A*_*(SU>mTM(4I=Cre%UDvi%FRD_>sFbk?h}2q2V|w- zVS#1{^<3DIo1BMqO;(K-}7Fe^=Vo8@K#SkuEp!(TQV3Xo7^+;9wSmJ7G$c3Tnl|ZUsI%5=W1kW z#uP6WW-->YCh0U|{Oy8khHG^?E8B%{3WN>?a@bjpvDF_~$6zZx+tYjhW{IHZdsAg- z5*t{E#ghSZ@FYTOKa`8`+B-m6khZwKI%3BTuZ(@1fdGL&3f5r1Ygv}d0_iTBLU`|B zr|^oX_;-^mscK|`pI(s``mqzI>s)jM-IkS#$Av-&fHIX>k}5_!SsN3h<}uH zB|_Fk(d>l_kgpfm4IX>u-dE3~x$0Sv)^j>r#`EyoR)cSbQr|Th$~fDi-MApDUYTuL0)=4L8V&qX-q;GI|$KSE~0-??)KxdcqmKj zz?lPu#ThyzU&IEZLBFYKN@z|o*yAW|sEQ8Pw0y#T{mu2b1WqEN=XDvGHT6#ORdf^l z=DNIl2k52m&zP9k9-S$6QZZI#t^1#!*USs-xy^q1$JyI6?^u(VUvzF3bB0^1eBCdW zNA-9Gu;`Ah83k*j6akH4czv5&>!V9EIgk0>3~N1f=@U+dEYGKmj9t1%eB(SL@(=!W z=INv7hiUxYi$rUy7!aC`Uq)+To`^2r^|L}d=eeemaK`K@Hfk`B>=4ld>~{%;5OUa5 zer5-}w#8`Tz=B>tAY1+h1tVd&)p;=ZQx+Wv<Yt* zJ*KiBqi~_zhVdK&Cn)0!HjbhaF4#T-2v$fJzLsO)_g?=-U>sFXw zE0eNAgeh*|xVttLcS)A{L~+++ngX2PVu4#D#S~`HK`1)URoEQkKH+7$x(>BTgPd%2 zh;l?;+TUV8q3a(F@wJ|ktN;hj@luF;(-6X``fsA0ns5En=flH;gOGj#=zwc&+yJDi zb%q@w{niTdb=EAk7|-c8Gm9}W6mgfiMQzzrR^T+#sC=1@UC6sE>reSUy6a^K%oHS; z%qs2#0Sy9o>rw(M3Z)Ok&Z|AZ!I7uYoT#G~ZyHT2SOUQajskBaaxEM0@W!7TEVM2N zyv9Hn%81{~0kJUANz08H{}VEs_m%hwnVowc)?BarMCG_pDO}gxtZRb0!bP!-Q#fX^ zlRZQn^MN^5-eAO@$(`6}ycIST5#Yv9))=|4h0+wK3M5XD*wB0=dzgq^rK)J@g(ce>JsV*&5*@mya`~ zwT^sSIzXaEv7lhcCOx#ptYi`qRO+?ufFfI~gAduK!|%=fvhnDgu-Hc}RhdK6#w;vzH7tg5viZ5XHgs!cz?gwpsn;Xaz2l(4 z_H*$!Ir`T+&S>rC#zD;6UPu1q>pnR<>%LL-*_F2np_M6oB}{-Ne2~tM)i)#ANRP0j zaMNZq_DMY6M7`?G5|uIK@Uuw@xE4d-9i-@w!tFL+iKQRQQ3f=EIZ>4J8R6Lz>jn+ge(S5D zt*U1vs4#XVYTL-t?QtU4z!3&VjsX}T8(6gTVBxaAcflBYx&mps0PQRf>IETlSw?(p z(g>x5uUd^q_h8kHj3td)CT?Zm*DQ3o!I#JB-eQFtnEY?GRYF*t@-Q}qp|(T2bA1aK zc?a^WFcq_a)@EVeN==jBI!#xk9ktH*VW00UeL4VtVdltw^=zCm(|ur)6aVpCz7F$5 zZF^E48*Dz!=^mp@I`=Ms0+UGY$kX3CWj2R+up>F^ot(Dca%UhdgTGx$d(|LCht+j_ zc8g%GGl_DV!=MQ?2YpvH#n~01prV`sjW_A&9s!a|ydm={rwI(v$owZ#mNo&Dt08HH zBu2d`r2q4>9W(I$<;yhN9y0 z^mOk3)4w*YDP8pGauj~8P9?AJ9)KO)z)$J?Y^}}BMB5E&&5ye70e@4I5Dae4KBLopD~rg@)c;aZ&-~b(B?J7(10;gETJ1X1kM!I z7y(HwFh5Y{5emt*7)THDP0mC?wY&l8Swc&#TvF?8-cTa0lY4>Ui%u0dbvv_?pm_)n zor5d%V9h`;97c~7J4?mXQd;KPqF_ML?lVx5(WeGdTUETkGg2cnr$)qSb64OJ&^ZUh zZxz}Z7#yIZ? z=EPOavl**nwZ4f=S&r7np)8hG*=T_px#$(734_%%TGj6)p4iT6m26iQsf`?4Mjt~( z9%j)Ly%aW0yD!)5WuLZ*%W`G*b`cR^@h~Y;Q)2z4a`jHhFshF|?~e^f1$l&jI{x9< zE$uM##2J&;Mvtt~6cPVsDb<(SgiF~iTNo|DFIPQD zzBe?7FZJN1Z5de+4&e)LO^AW1Y%7|plB&i5baaywJxz%dxFR(dC8_F%dIIAwP@%O- zY%KW8AS^SCFls4Z@SHz0gNt*j_Q7zRjdQ8cei&oCTHS=gscz^65`dDyr73@&VW zJ88H;Mrt;|7At9VrtIch(9uho(E`mhNC8CQyqHlR zUIqzeRrw&D{$<)+GKlP${MxoB1NH(0dTXpzPH3@oA(|Fy?(;ybI$>SS%5=>HZMYLQ z#;ghA9_!HkEaF=4CUd7mG?Z1U<3FIU6TlCUdvX zOdb|uMF0*_A9}v0H6s*JYn*);RGwbKjvccu%#Lr72R}*~CFm13YAzBd^Ppl%JCnCX zoE5aD*vXUupWkMbg;!g485D`VN@!ZpJ^*JG`d+o-rEhAFBGR&cty%HlA&(MH*Am4030nmHhzPUBpUNxWCjyt30Sp{>#Gob~Gj}2R1 z1FO;^m5-{a>qwm&DphYICOTK)W=nY12JIT zR1kiFhCw#!H!~(e6FK*$>N(k8YVx^om_AkEOrYLSyVsJ}hNO-)+ad^Lr6D~aT6itB zD>YCb^t%HcJ@A&P4JSA=7kl(DXQ0^(2B5IExF;2y<92x-@)Xi8kz~3Ip-)R!Z443W zEy7&eW6*G}f!%7L6qh^j83&Yohw4VO2d79?dD-f8K%f*)Gr~0prJ3H{5IZawk1gRV zPm)WERdV6;?Gfq86orxeUv;hXi2sCbKyYYZZys>6$WP5l(L$VAZ1)ZfyRY4W*kQ{; zTepDV^e8gd7(IV)7)bT_B*_p>}7{nWsfrqxE8i%bb(}M z7%^#B&LFeZ=VOb?Kz1d#Z(IGvp0+R2cN*v=5bL77Vv<)_^s%w5yIBQx1gphvvSPB% zVpljfX(mUuxLB}js3V?u*=^~OBK6RoSbr~`D`(zLQ6QPy>ZfOy9255XCu zqB@y$Qz^oy-VgdC&Hn3TbGoD?w$n*c#N9pcYtqwHZMQSM_h+1!#UeorPrDB&k)%w2 zC>Ag%gy|w8xLuQY^)fpQ55E+FVnO}3%B&YwPW`(|%G=3QTtp;gJnl5;owo7oNGL$fl9>_%US%Pu0 z(@WF!1*`(c#1ZY597HRE`CkD1?`F3KR4SV_sB!|G;ppsS-PgZ68cG*kzZ%87y>JNc z($Q6sgvxXV#Lxhw%rtxu7)`~)Jp0wr9uZ69Y*5kUstjksuIfN&=44kuyG9bna$>k7 zJJc{92L+H+;iw;zEwuZ7`QL8>T18G)AsU)}!k9p=`qepKwx-jP0y?@fa{Xvwd7?Pj zA~#AYU`TZL+6Dz0DY!+b*|5nP{$T#fh2!%c<%84WZ&py-kuJ=;{kkj5F)OWez9@Q} z*GS8EXPlAJ0<95Wwad`PBHAHf>7|B-ynz!Wdz`RDld$n^S*c{hmXK|j|V)$BJ??gK^ zr6jb&JKG{4jnWp3f=}q^o`XqdIe=$MrSlTp7gT~zRWozATYa3S4{=UN{RY z^?P>RYz6p@XI&!yNRT+$kMDA#BuwRUwCTr7*^ai z4YaGM=}XVwSvkCxa?1A-E0|!x0|F067pMv=0bmfGZm58ma$rGWQY2^DiD2}hq2*C@ zBS4Z%BSO4QX9j#?a@%Tz$Gkm6!C+68K0;8Oxxvi$z#&6N*K8Zn+4riJU|}ZSkcYH- z)@t;1&(D(898h_x*01LZD3#=?OC2Rvy*aB09DI^Ir229I(ss9kHr9dB-AxYE9(kuc z5IH8FNbiipI7UHO57}p7w5%ZrwyFb{Q^oBL zoclMBh5nOq@t;6N4WD>K7WtA~j+9BD(Mz8f>g#oU%_7#~H*)nx9LlX)MDkkToodp} z>0M3)YgP4d7gxJ}-(g2cenFAaB2LmC#zNBSN!=71IAf_}qUUDr&&L`dVOknjQ{)v- znOQ2IfsQT+vZU8D8;7qiXoRI7MiE-PQwxP14Hi_JtSPnM=7{gq*(m4qA891TREGhm z<3@nA(F4uqms7-0*=>pM2~Yft_s<3&fHD=K4kYTDyDSC9sn;7P=j~JEFXU^9)-Y{` zhkY#3um~drMhe0S>zMO8ZM7H@&%P7G4S59Wl1P^Y6*(3KDno|k0H-%g!Il$ z@#`L@#&5-N>1veGN-b8o-{`VbUyHMz?KkISuxsPi&ztUui(W}88yG;iwWTK`%JyPA zTK`^0w*4ZV2lJkhPY^Q9o|`sBE-?m)0o0I)!6X)1lZ@oGugmpd7L0VE6q^@$Fb(6X z7G8DVO>}ul!NQHS6$K0t&LkiXkUKi$nK}_-!#89Q)`S`1!|qr>@$ckV(5dl4-G%o7 zkx@nxU;a2gf$gp%4Txl-xwRE-ffp^_czo+!okpAJWn5$}Hd>`^q>Ki6g-(Nfy2%%= zxzJL6&O97vh{(nXM*fm22dQ(z%O5imZ{! zLtqq<4NjIhcBY&rVjdyjW9w(k$JH7hPAM~PM}?Tf`xwhkMrj$B$bN-Sy)!aD*ScBh zM=ZPr;#3Oks%{u&BRX>gTB<9ATbwbNiU@f+;%^oq-$>q`yc}!h-R3{8{_+#426N`$ zBU+igyH*iGLTgK3EtqCgsACu?T=>0GL~K}7=Mf&?w3wLC7*heWmDw@R=vx)|Nv(BH zx%dJmGu-!naQceZ)^4)G)fX&OLy~m>bNx(ET#or2l@_y^_20|z_|;h9YseYBx4$wesN1fn+XRkx|6IIu$OoCzyYgX5% zSql#66sFmlzi$}-<0_W2m_UN6@Svmh}%eKm*0x36b$|#nyAD?rh1W8C*K+VbdVN#UjL&dJ!&54hQEMCu50ev_TI| z-P%yWABB2FmL#16N6TZaLv=`Q5=rtK1%$7cp|t&i(}fCGGwF;61bOHi%UQoRIGQOP zvV>;b>=iR44kcqhaRX@E&uTX;A=jP#;)VN~@x_&;-t24_E)qt<4G;0ugN74ge3eF} zh>ema?KGlb&Lb3;y_t?v@b!%7r`;Ne)Kwv=8j0K-8J!?lk%-vjw&af(#6Za(`*9qz zE?{?P|E>g*q85343#Rdp)0f1fELh3@?GS=UfFwL*C>G?Oxgy4`SS;fB2ixe3f~l*| zMDH~Ai7mO@?Pp~{T@$H^8W3K~A)<-G*=f2PqWkHvi`LTrB&^%6xAZMD^o#OQ&GcK2 z@T$~(cA*|3_r>}3ix0qLxF%g07^E%enG2%>fi)}9rTuen2+@-W_3;b4LAvl>S|gaOy*Z(-KZL$aZ<%(rt#)hK%&F#u4)zQBl4F}0F5ei{P{}q1J#t8=T=hrH zfcr!0#tBWNIktfFQ{d_+CPXKC0CPGf3s$&SNf*(SNZ^gO+}DkI#kTKs>hDli%^LAZ zGEJ?0L|=&S6KhV4OH&JNCbCn{cq-oB!rWhJ=MayjS_9o1@ zZS|t2dE2UC_NgcnBVUle24V;_Eg^Lb2>RQhj8l@67{G``0QM`4N zhoKLIZPOfX^|Sd-NaVwxN<$l&*}NyeUlUCQIa5iE8HubU%`HJXcFa$5#cQ#BQauJW zxiy;tiuVp-T0e#4-PLKfAQShj&kv5Ml|V&5sj>Zh;g^Ioz0VqpQQTd2M`KhIr7i2N z9i()Oi(%G3vDPU#+-2DY!Gi7^xGaeSj4m)Ic+O*!3CgR`==!D+?LR#+CA}j_ zBfMU0VNq#@_bQOCG(_E;!JI}G50cM4`pX4$+*FYI$ zm?-c72WA$S(d7A5BX8|hPkQQW-!j-avoFhiZYQ}4CK2En@;O~uK_Iys0Ulc~lc_;N z?U~*8BGtF6wRiW8+>k5ktTl^uXPa-iO)aQMvIKp+A1n;wqpqpSmX2&Hfm~a*7r}d6 zjBJTRa%aW|IgTWI8hu+{8GXZR_wB!bX^^#5%dof_sGdwH4>1|R>edvbspc{l%B5G4 zm+ zo-rmd>m?hjYhje7t!--w6&Hqy|CL1#ingqyygMrS1%Y3n_D4Ae@g21MiXBtr{}Nf9GH%%<9dix^z)Kk z)X<&cDP(^@cL;u5pzhGmrMOUWN@NQmB7!&|87U!JOF<@`u~LB(0%ocS&Xn*!SwA|J zl#lNm-dTPWhHat@MypqJ4{r#6{#2RiWB1f4hXoSrB9p1$gQ^u6ijG5vNd z9Gj^POkXI?D5sZr0BX4CSOQc0G__y5wutUc-?DE3CCbx`o6xmI79 z_Xu#%(}oC6O3D>Sn3!fg0XqaC#^m=QCT)l)6Kt0%Ga1xZ{r4dBP%HGO%^c3vVZtup zk1XBPYO8BFJdg5KR#h!4&!)moGuzH}=$Fo^9tJJ0gMdx{cb$}9@lTQ^x?s48dk&Ybq>0Oj_ioUyi3pMb4%ihx>vL59#}2w@i@IWn0CjK zRhaFCMuRCKpGB^X3aLT!u?_(wiTnB&t6yaZz

    Si}fN`9Cfchlp}DV;4uk+G3eo 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 z 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

    )HQ~N6xl(4oadf&`H^)O~_M(V1wJw?*6sIB^ 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 z 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�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

    #2ox<_CEQsnKmf?l4!!uQ`y)##OFr2vm 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 Date: Tue, 5 Nov 2024 13:05:24 +0900 Subject: [PATCH 026/648] =?UTF-8?q?design:=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공통 스타일 추가 및 root 레이아웃에 적용 --- app/layout.tsx | 2 ++ shared/styles/base/_variables.scss | 15 +++++++++++++++ shared/styles/global.scss | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 shared/styles/global.scss diff --git a/app/layout.tsx b/app/layout.tsx index 735755b1..921f8a08 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,7 @@ import type { Metadata } from 'next' +import '@/shared/styles/global.scss' + import { pretendard } from './fonts' export const metadata: Metadata = { diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index 8ed366a2..dad922cd 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -32,6 +32,16 @@ $color-salmon-200: #fed8d7; $color-salmon-100: #ffeceb; /* Typography */ +$font-family-base: + var(--font-pretendard), + -apple-system, + BlinkMacSystemFont, + system-ui, + Roboto, + 'Helvetica Neue', + sans-serif; + +// Size $text-h1: 54px; $text-h2: 36px; $text-h3: 30px; @@ -42,6 +52,11 @@ $text-b3: 14px; $text-c1: 12px; $text-c2: 10px; +// Weight +$text-bold: 700; +$text-semibold: 600; +$text-medium: 500; + /* Breakpoints */ $breakpoint-sm: 576px; $breakpoint-md: 768px; diff --git a/shared/styles/global.scss b/shared/styles/global.scss new file mode 100644 index 00000000..48aca0d4 --- /dev/null +++ b/shared/styles/global.scss @@ -0,0 +1,19 @@ +@import './base/variables'; +@import './base/reset'; + +body { + font-family: $font-family-base; + font-weight: $text-medium; + font-size: $text-b2; + letter-spacing: -0.02em; + line-height: 132%; + color: $color-black; + background-color: $color-white; +} + +input, +textarea { + &::placeholder { + opacity: 1; /* Firefox */ + } +} From c0d1a9f319e6be37e36fb3ed39636e0bdf10cd42 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 6 Nov 2024 14:03:11 +0900 Subject: [PATCH 027/648] =?UTF-8?q?rename:=20=EA=B8=B0=EC=A1=B4=20TanStack?= =?UTF-8?q?=20Query=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=EB=A5=BC=20a?= =?UTF-8?q?pp=20=EC=97=90=EC=84=9C=20shared=20=ED=95=98=EC=9C=84=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99=20(#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 4 ++-- shared/providers/index.ts | 1 + .../providers/react-query/query-client.ts | 4 +++- .../providers/react-query/query-provider.tsx | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 shared/providers/index.ts rename app/get-query-client.ts => shared/providers/react-query/query-client.ts (90%) rename app/providers.tsx => shared/providers/react-query/query-provider.tsx (65%) diff --git a/app/layout.tsx b/app/layout.tsx index f53605a5..1c30a56e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,10 +1,10 @@ -import { Providers } from './providers' +import { QueryProvider } from '@/shared/providers' const RootLayout = ({ children }: { children: React.ReactNode }) => { return ( - {children} + {children} ) diff --git a/shared/providers/index.ts b/shared/providers/index.ts new file mode 100644 index 00000000..ac2203f8 --- /dev/null +++ b/shared/providers/index.ts @@ -0,0 +1 @@ +export { QueryProvider } from './react-query/query-provider' diff --git a/app/get-query-client.ts b/shared/providers/react-query/query-client.ts similarity index 90% rename from app/get-query-client.ts rename to shared/providers/react-query/query-client.ts index 38f081b4..2ad2ce08 100644 --- a/app/get-query-client.ts +++ b/shared/providers/react-query/query-client.ts @@ -1,10 +1,12 @@ import { isServer, QueryClient, defaultShouldDehydrateQuery } from '@tanstack/react-query' +const STALE_SECONDS = 60 * 1000 + const makeQueryClient = () => { return new QueryClient({ defaultOptions: { queries: { - staleTime: 60 * 1000, + staleTime: STALE_SECONDS, }, dehydrate: { shouldDehydrateQuery: (query) => diff --git a/app/providers.tsx b/shared/providers/react-query/query-provider.tsx similarity index 65% rename from app/providers.tsx rename to shared/providers/react-query/query-provider.tsx index 02a6d086..94e1d873 100644 --- a/app/providers.tsx +++ b/shared/providers/react-query/query-provider.tsx @@ -2,9 +2,9 @@ import { QueryClientProvider } from '@tanstack/react-query' import { ReactNode } from 'react' -import { getQueryClient } from './get-query-client' +import { getQueryClient } from './query-client' -export const Providers = ({ children }: { children: ReactNode }) => { +export const QueryProvider = ({ children }: { children: ReactNode }) => { const queryClient = getQueryClient() return {children} From 9f6c3ccca5b904e471c083edd6f3934f438d3589 Mon Sep 17 00:00:00 2001 From: deun Date: Thu, 7 Nov 2024 10:11:24 +0900 Subject: [PATCH 028/648] =?UTF-8?q?design:=20=EC=88=98=EC=A0=95=EB=90=9C?= =?UTF-8?q?=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=8B=9C=EC=8A=A4=ED=85=9C(co?= =?UTF-8?q?lor)=20=EB=B0=98=EC=98=81=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_variables.scss | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index dad922cd..497c80f9 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -12,24 +12,18 @@ $color-gray-100: #fafafa; $color-white: #ffffff; // Primary -$color-indigo-800: #0f21a9; -$color-indigo-700: #1228ce; -$color-indigo-600: #1e37eb; -$color-indigo-500: #4533eb; -$color-indigo-400: #5668f0; -$color-indigo-300: #7c8af4; -$color-indigo-200: #a1abf7; -$color-indigo-100: #d9ddfc; +$color-orange-800: #f53500; +$color-orange-700: #ff4f1f; +$color-orange-600: #ff5f33; +$color-orange-500: #ff7752; +$color-orange-400: #ff8f70; +$color-orange-300: #ffaf99; +$color-orange-200: #ffbfad; +$color-orange-100: #ffdfd6; -// Secondary -$color-salmon-800: #fb2a23; -$color-salmon-700: #fb514b; -$color-salmon-600: #fc6e69; -$color-salmon-500: #fd8b87; -$color-salmon-400: #fd9e9b; -$color-salmon-300: #fdb2af; -$color-salmon-200: #fed8d7; -$color-salmon-100: #ffeceb; +// Feedback +$color-indigo: #6877ff; +$color-yellow: #ffe070; /* Typography */ $font-family-base: From d3cb88bf6f50fa9efb2d2a5c3125ff4b260de623 Mon Sep 17 00:00:00 2001 From: deun Date: Thu, 7 Nov 2024 10:53:41 +0900 Subject: [PATCH 029/648] =?UTF-8?q?settings:=20.storybook=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EC=84=A4=EC=A0=95=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WARN 해결을 위해 version 업데이트 --- .storybook/main.ts | 18 + .storybook/preview.ts | 14 + package-lock.json | 1970 +++++++++++++++++++++-------------------- package.json | 11 +- 4 files changed, 1045 insertions(+), 968 deletions(-) create mode 100644 .storybook/main.ts create mode 100644 .storybook/preview.ts diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 00000000..fe874bb5 --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,18 @@ +import type { StorybookConfig } from '@storybook/nextjs' + +const config: StorybookConfig = { + stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + '@storybook/addon-onboarding', + '@storybook/addon-essentials', + '@chromatic-com/storybook', + '@storybook/addon-interactions', + '@storybook/addon-docs', + ], + framework: { + name: '@storybook/nextjs', + options: {}, + }, +} + +export default config diff --git a/.storybook/preview.ts b/.storybook/preview.ts new file mode 100644 index 00000000..25953420 --- /dev/null +++ b/.storybook/preview.ts @@ -0,0 +1,14 @@ +import type { Preview } from '@storybook/react' + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +} + +export default preview diff --git a/package-lock.json b/package-lock.json index 0f8e701f..f14beaad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,13 +19,14 @@ "devDependencies": { "@chromatic-com/storybook": "^3.2.2", "@next/eslint-plugin-next": "^15.0.2", - "@storybook/addon-essentials": "^8.4.0", - "@storybook/addon-interactions": "^8.4.0", + "@storybook/addon-docs": "^8.4.2", + "@storybook/addon-essentials": "^8.4.2", + "@storybook/addon-interactions": "^8.4.2", "@storybook/addon-onboarding": "^8.4.0", "@storybook/blocks": "^8.4.0", - "@storybook/nextjs": "^8.4.0", - "@storybook/react": "^8.4.0", - "@storybook/test": "^8.4.0", + "@storybook/nextjs": "^8.4.2", + "@storybook/react": "^8.4.2", + "@storybook/test": "^8.4.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^20", "@types/react": "^18", @@ -120,18 +121,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -198,15 +187,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -852,15 +832,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", @@ -1821,15 +1792,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", @@ -1863,33 +1825,6 @@ "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, - "node_modules/@chromatic-com/storybook/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@chromatic-com/storybook/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -2334,6 +2269,55 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", @@ -2358,6 +2342,28 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2756,33 +2762,6 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -2872,34 +2851,6 @@ "fast-glob": "3.3.1" } }, - "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, - "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.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@next/eslint-plugin-next/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, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@next/swc-darwin-arm64": { "version": "14.2.16", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.16.tgz", @@ -3080,10 +3031,12 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", - "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", - "devOptional": true, + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -3098,24 +3051,25 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.4.1", - "@parcel/watcher-darwin-arm64": "2.4.1", - "@parcel/watcher-darwin-x64": "2.4.1", - "@parcel/watcher-freebsd-x64": "2.4.1", - "@parcel/watcher-linux-arm-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-musl": "2.4.1", - "@parcel/watcher-linux-x64-glibc": "2.4.1", - "@parcel/watcher-linux-x64-musl": "2.4.1", - "@parcel/watcher-win32-arm64": "2.4.1", - "@parcel/watcher-win32-ia32": "2.4.1", - "@parcel/watcher-win32-x64": "2.4.1" + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", - "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", "cpu": [ "arm64" ], @@ -3133,9 +3087,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", - "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", "cpu": [ "arm64" ], @@ -3153,9 +3107,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", - "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", "cpu": [ "x64" ], @@ -3173,9 +3127,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", - "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", "cpu": [ "x64" ], @@ -3193,9 +3147,29 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", - "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", "cpu": [ "arm" ], @@ -3213,9 +3187,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", - "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", "cpu": [ "arm64" ], @@ -3233,9 +3207,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", - "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", "cpu": [ "arm64" ], @@ -3253,9 +3227,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", - "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", "cpu": [ "x64" ], @@ -3273,9 +3247,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", - "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", "cpu": [ "x64" ], @@ -3293,9 +3267,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", - "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", "cpu": [ "arm64" ], @@ -3313,9 +3287,9 @@ } }, "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", - "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", "cpu": [ "ia32" ], @@ -3333,9 +3307,9 @@ } }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", - "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", "cpu": [ "x64" ], @@ -3422,18 +3396,6 @@ } } }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/loader-utils": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", @@ -3461,9 +3423,9 @@ "dev": true }, "node_modules/@storybook/addon-actions": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.0.tgz", - "integrity": "sha512-xQ84mDIl+jyDpjt8SnCfhqVECQu7k1dLyhiAi983Tp5nyW8KRJa/tEATDLOCpz1eL9AMf2WjAypi+vIiNIul8w==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.2.tgz", + "integrity": "sha512-+hA200XN5aeA4T3jq8IifQq6Y+9FyNQ0Q+blM1L0Tl7WLzBc7B1kHQnKvhSj5pvMSBWc/Q/kY7Ev5t9gdOu13g==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -3477,13 +3439,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-backgrounds": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.0.tgz", - "integrity": "sha512-2LpA7Ja7s76rFjSQHTPhbfmwsCmAuyU5k05CIbbUxM+iBVOaBXUYLaoi8dl448W/o/rmNHeW5YCtxzmMPlScrQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.2.tgz", + "integrity": "sha512-s4uag5VKuk8q2MSnuNS7Sv+v1/mykzGPXe/zZRW2ammtkdHp8Uy78eQS2G0aiG02chXCX+qQgWMyy5QItDcTFQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -3495,13 +3457,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-controls": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.0.tgz", - "integrity": "sha512-KoqwWHi6cUv1WXcANH4l175kNkuFPVhexP/8F9tE9uhv2xHNx5cTefmB174dWpfOO2H3IdUk0RuMWjOZFpztqQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.2.tgz", + "integrity": "sha512-raCbHEj1xl4F3wKH6IdfEXNRaxKpY4QGhjSTE8Pte5iJSVhKG86taLqqRr+4dC7H1/LVMPU1XCGV4mkgDGtyxQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -3513,19 +3475,19 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-docs": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.0.tgz", - "integrity": "sha512-n/tAu8xmfdxTkr7ooDM3h+QwDyP9eoKoKuaKXfiPPevrFk0FXRw5KzNhTHTlHniJ2LD+gyaomPGV6D2oBl1KIg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.2.tgz", + "integrity": "sha512-jIpykha7hv2Inlrq31ZoYg2QhuCuvcO+Q+uvhT45RDTB+2US/fg3rJINKlw2Djq8RPPOXvty5W0yvE6CrWKhnQ==", "dev": true, "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.4.0", - "@storybook/csf-plugin": "8.4.0", - "@storybook/react-dom-shim": "8.4.0", + "@storybook/blocks": "8.4.2", + "@storybook/csf-plugin": "8.4.2", + "@storybook/react-dom-shim": "8.4.2", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "ts-dedent": "^2.0.0" @@ -3535,24 +3497,24 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-essentials": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.0.tgz", - "integrity": "sha512-45CI0LpNr8ASHEckxbW/osgnsFMWl847S9rALNQUAN3VaqlDQeF/VIDt1s9vtV9ZYNHASxPFmW4qjgylxv8HpQ==", - "dev": true, - "dependencies": { - "@storybook/addon-actions": "8.4.0", - "@storybook/addon-backgrounds": "8.4.0", - "@storybook/addon-controls": "8.4.0", - "@storybook/addon-docs": "8.4.0", - "@storybook/addon-highlight": "8.4.0", - "@storybook/addon-measure": "8.4.0", - "@storybook/addon-outline": "8.4.0", - "@storybook/addon-toolbars": "8.4.0", - "@storybook/addon-viewport": "8.4.0", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.2.tgz", + "integrity": "sha512-+/vfPrXM/GWU3Kbrg92PepwAZr7lOeulTTYF4THK0CL3DfUUlkGNpBPLP5PtjCuIkVrTCjXiIEdVWk47d5m2+w==", + "dev": true, + "dependencies": { + "@storybook/addon-actions": "8.4.2", + "@storybook/addon-backgrounds": "8.4.2", + "@storybook/addon-controls": "8.4.2", + "@storybook/addon-docs": "8.4.2", + "@storybook/addon-highlight": "8.4.2", + "@storybook/addon-measure": "8.4.2", + "@storybook/addon-outline": "8.4.2", + "@storybook/addon-toolbars": "8.4.2", + "@storybook/addon-viewport": "8.4.2", "ts-dedent": "^2.0.0" }, "funding": { @@ -3560,13 +3522,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-highlight": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.0.tgz", - "integrity": "sha512-tshX/2HnPzGQ9Kza2DARNfirBRhE/Ts7bldbhMiJu20YhJD1jQzXSDEX1cCgHsDc8HKYOsV/Kuu5WDzp/1i97w==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.2.tgz", + "integrity": "sha512-vTtwp7nyJ09SXrsMnH+pukCjHjRMjQXgHZHxvbrv09uoH8ldQMv9B7u+X+9Wcy/jYSKFz/ng7pWo4b4a2oXHkg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -3576,18 +3538,18 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-interactions": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.0.tgz", - "integrity": "sha512-yXPAyGRjElYZ0ObUo7Ipww4CwgScc2FXMxeQHKSZ+9wuDOU8uSaWpINB++8nS6yPZyhHeUqgzGCF/w3ZusNvzA==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.2.tgz", + "integrity": "sha512-+/NTENTApeOcONgFNQ6Olbk0GH3pTDG3w0eh00slCB+2agD1BcVKg8SSlHQV0lQF1cK3vWL/X3jeaxdFLYOjjg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.4.0", - "@storybook/test": "8.4.0", + "@storybook/instrumenter": "8.4.2", + "@storybook/test": "8.4.2", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, @@ -3596,13 +3558,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-measure": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.0.tgz", - "integrity": "sha512-Zews/03IL/UUJMaheduGxJKG1mEwfpGq7SP1RtK0kK3l/yh6kVcKG63RXw5zVEoDwG4wzuuH9vi06Mlzhu8/rA==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.2.tgz", + "integrity": "sha512-z+j6xQwcUBSpgzl1XDU+xU4YYgLraLMljECW7NvRNyJ/PYixvol8R3wtzWbr+CBpxmvbXjEJCPlF+EjF9/mBWQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -3613,13 +3575,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-onboarding": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.4.0.tgz", - "integrity": "sha512-q9nvMFxvjwDvkumIO0VEa2RhIfxwU8YiCCLVXrGw73XtWs2UViXBLaVl/W2USDGDq27T6VFTh6KzZVMikPJrUQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.4.2.tgz", + "integrity": "sha512-zWzOyRASnIPt2AcaEl1KhI+aOaKDuoIcNB7u1GoABj0YM+V9d6o3lvcsmOAQG5pgwgFyqyOnLwpTfvRSEyzGFA==", "dev": true, "dependencies": { "react-confetti": "^6.1.0" @@ -3629,13 +3591,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-outline": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.0.tgz", - "integrity": "sha512-qZdHaWq/DXoVycKzcynvVxg3MNzavsGCuq9HUl2X/oBKNii00NEZgYVLo4dQ8iDNlmykuJ9ReyXKBOKF7AU+9w==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.2.tgz", + "integrity": "sha512-oTMlPEyT4CBqzcQbfemoJzJ6yzeRAmvrAx9ssaBcnQQRsKxo0D2Ri/Jmm6SNcR0yBHxYRkvIH+2phLw8aiflCQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -3646,26 +3608,26 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-toolbars": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.0.tgz", - "integrity": "sha512-fXDeLsAweC1/roe5qNys+pBrjf1Mxof/7O/dZtQZJtcKox4WwzgirxexFFAZLfXOE9awm5svzo0YWYxWk+Lfwg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.2.tgz", + "integrity": "sha512-DidzW/NQS224niMJIjcJI2ls83emqygUcS9GYNGgdc5Xwro/TPgGYOXP2qnXgYUxXQTHbrxmIbHdEehxC7CcYQ==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/addon-viewport": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.0.tgz", - "integrity": "sha512-hbHJzz7PcZ/bazUH3nAdG9yP3CUfF+wPdDwzcqSEVBRjdWSLZ4DHAtB0wajqhUoCsiRehg9avft1NokAc+KOgg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.2.tgz", + "integrity": "sha512-qVQ2UaxCNsUSFHnAAAizNPIJ/QwfMg7p5bBdpYROTZXJe+bxVp0rFzZmQgHZ3/sn+lzE4ItM4QEfxkfQUWi1ag==", "dev": true, "dependencies": { "memoizerific": "^1.11.3" @@ -3675,13 +3637,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/blocks": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.0.tgz", - "integrity": "sha512-LeXsZLTNcmKtgt0ZRdgzBa2Z8A5CH3gGyjG7QT3M+3yH9fVAXB2XplKOIejDsvR9jSBww3mKXyabX12NVZKz0A==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.2.tgz", + "integrity": "sha512-yAAvmOWaD8gIrepOxCh/RxQqd/1xZIwd/V+gsvAhW/thawN+SpI+zK63gmcqAPLX84hJ3Dh5pegRk0SoHNuDVA==", "dev": true, "dependencies": { "@storybook/csf": "^0.1.11", @@ -3695,7 +3657,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.0" + "storybook": "^8.4.2" }, "peerDependenciesMeta": { "react": { @@ -3707,12 +3669,12 @@ } }, "node_modules/@storybook/builder-webpack5": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.0.tgz", - "integrity": "sha512-NVPEB31x1LU73ghgPaynY603Pi0MKPlM/YovevlwZtTIU9st+DSEss1qSjC0As2Lq/bHZTJu+jhTCIB76MK7wQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.2.tgz", + "integrity": "sha512-Pqa0/sqqEujzcvs+/Cwf/5qRLC+atmceROCFokMOgpIaorTXlbmiQdJ2dBhMFNugLvXfL7dVQBjBfiuzhsQ57g==", "dev": true, "dependencies": { - "@storybook/core-webpack": "8.4.0", + "@storybook/core-webpack": "8.4.2", "@types/node": "^22.0.0", "@types/semver": "^7.3.4", "browser-assert": "^1.2.1", @@ -3743,7 +3705,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" }, "peerDependenciesMeta": { "typescript": { @@ -3752,31 +3714,31 @@ } }, "node_modules/@storybook/builder-webpack5/node_modules/@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, "dependencies": { "undici-types": "~6.19.8" } }, "node_modules/@storybook/components": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.0.tgz", - "integrity": "sha512-o2jPW05YN2rbSLNMzPV769c4zCy3Vn0DhJbIQZsxUmUXAMX/n1+V1jlV3kbY0kCjiI6i/PH7i6PJnxICdJ35mQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.2.tgz", + "integrity": "sha512-+W59oF7D73LAxLNmCfFrfs98cH9pyNHK9HlJoO5/lKbK4IdWhhOoqUR/AJ3ueksoLuetFat4DxyE8SN1H4Bvrg==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "node_modules/@storybook/core": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.0.tgz", - "integrity": "sha512-RlvkBNPPLbHtJQ5M3SKfLLtn5GssRBOLBbJLJf8HjraeDI+YRt+J9FVXqNa9aHhOGoxam+hFinmuy9gyMbPW1A==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.2.tgz", + "integrity": "sha512-hF8GWoUZTjwwuV5j4OLhMHZtZQL/NYcVUBReC2Ba06c8PkFIKqKZwATr1zKd301gQ5Qwcn9WgmZxJTMgdKQtOg==", "dev": true, "dependencies": { "@storybook/csf": "^0.1.11", @@ -3805,9 +3767,9 @@ } }, "node_modules/@storybook/core-webpack": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.0.tgz", - "integrity": "sha512-14UnJ7zFSLEyaBvYe7+K1t/TWJc41KxstMHgVxHyE6TDy9MGi+GLfmq2xB5OIVE4nxtjSon3tIOf/hVBrtbt0A==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.2.tgz", + "integrity": "sha512-bzGvzrLK/oDE9YlKayDEplcECURSa1oRkvV7rxI2sOTNfwuoxHJapvxFxazEKAHMVeSwfWDf4uKK0XeG2R/arA==", "dev": true, "dependencies": { "@types/node": "^22.0.0", @@ -3818,13 +3780,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/core-webpack/node_modules/@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, "dependencies": { "undici-types": "~6.19.8" @@ -3840,9 +3802,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.0.tgz", - "integrity": "sha512-l4vD1XboHh3nFOvcCIjoTED6bQZtRx+T/CUFfuZu3KEA7uJnXt/kUCXair9+Cgky9XvSEMvBPhoqa2dRx9ibBQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.2.tgz", + "integrity": "sha512-1f0t6W5xbC1sSAHHs3uXYPIQs2NXAEtIGqn6X9i3xbbub6hDS8PF8BIm7dOjQ8dZOPp7d9ltR64V5CoLlsOigA==", "dev": true, "dependencies": { "unplugin": "^1.3.1" @@ -3852,19 +3814,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" - } - }, - "node_modules/@storybook/csf/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "storybook": "^8.4.2" } }, "node_modules/@storybook/global": { @@ -3887,9 +3837,9 @@ } }, "node_modules/@storybook/instrumenter": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.0.tgz", - "integrity": "sha512-iqQdH2lhyRVcCBnVOmjn/r/pFwIJ5X1isUkvyavwPf0KOB2bz+QuXXkvKdzirwQFu9jSLOEdu0v3Fr+PHUbIfA==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.2.tgz", + "integrity": "sha512-gPYCZ/0O6gRLI3zmenu2N6QtKzxDZFdT2xf4RWcNUSZyp28RZkRCIgKFMt3fTmvE0yMzAjQyRSkBdrONjQ44HA==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0", @@ -3900,26 +3850,26 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/manager-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.0.tgz", - "integrity": "sha512-duYoAtx3VkTHpoXd+NaMqBQNqIovmbTN7w/244O0LWyhF6AmQXnrY1Z72rjvvpxY6c1boRs6YdDLXPKxGVeRxw==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.2.tgz", + "integrity": "sha512-rhPc4cgQDKDH8NUyRh/ZaJW7QIhR/PO5MNX4xc+vz71sM2nO7ONA/FrgLtCuu4SULdwilEPvGefYvLK0dE+Caw==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "node_modules/@storybook/nextjs": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.4.0.tgz", - "integrity": "sha512-izOpE8m8TjPFhkGPB+4Lzt5gPzDxiu0v+sdnKmi41BvY4XGSXFqhBhLC5+M200VDqolJJzqZ9WCXp21pscyf+g==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.4.2.tgz", + "integrity": "sha512-HySwS9zfenurk+O+SX9gKskotkHo8mFRBKAIlEROIWi7iipp5GCVPyqb8gFWjvN81dKfEIAZs+fB/7ySulJ4rg==", "dev": true, "dependencies": { "@babel/core": "^7.24.4", @@ -3936,10 +3886,10 @@ "@babel/preset-typescript": "^7.24.1", "@babel/runtime": "^7.24.4", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@storybook/builder-webpack5": "8.4.0", - "@storybook/preset-react-webpack": "8.4.0", - "@storybook/react": "8.4.0", - "@storybook/test": "8.4.0", + "@storybook/builder-webpack5": "8.4.2", + "@storybook/preset-react-webpack": "8.4.2", + "@storybook/react": "8.4.2", + "@storybook/test": "8.4.2", "@types/node": "^22.0.0", "@types/semver": "^7.3.4", "babel-loader": "^9.1.3", @@ -3975,7 +3925,7 @@ "next": "^13.5.0 || ^14.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.0", + "storybook": "^8.4.2", "webpack": "^5.0.0" }, "peerDependenciesMeta": { @@ -3988,99 +3938,22 @@ } }, "node_modules/@storybook/nextjs/node_modules/@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, "dependencies": { "undici-types": "~6.19.8" } }, - "node_modules/@storybook/nextjs/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@storybook/nextjs/node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/@storybook/nextjs/node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "dev": true, - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/@storybook/nextjs/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@storybook/preset-react-webpack": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.0.tgz", - "integrity": "sha512-me5gqQqfU/jxQMJJljdID3lbKH2RGvdgxVwLhvrUSmEhimcuWXgJxvxE4hHGbUiYcwiM/xmQLrf286/B3agN7w==", + "node_modules/@storybook/preset-react-webpack": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.2.tgz", + "integrity": "sha512-Gt9hQRo1ythGFzATNV4WgQDlMDzBgiq7ks+YkW2/Xu5ZkrRrM/gK75fhmbICrknZl2pPPfNFXlECPWKAeTmwFA==", "dev": true, "dependencies": { - "@storybook/core-webpack": "8.4.0", - "@storybook/react": "8.4.0", + "@storybook/core-webpack": "8.4.2", + "@storybook/react": "8.4.2", "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", "@types/node": "^22.0.0", "@types/semver": "^7.3.4", @@ -4102,7 +3975,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.0" + "storybook": "^8.4.2" }, "peerDependenciesMeta": { "typescript": { @@ -4111,65 +3984,39 @@ } }, "node_modules/@storybook/preset-react-webpack/node_modules/@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, "dependencies": { "undici-types": "~6.19.8" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@storybook/preset-react-webpack/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@storybook/preview-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.0.tgz", - "integrity": "sha512-Z9yduQRqzqeV85GEFyaTKtRtg/QYCb89bKhi4xcxY9l7DMAr7/lqpUxqngW5ogiNslusQzct3zI7os6INBlMFg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.2.tgz", + "integrity": "sha512-5X/xvIvDPaWJKUBCo5zVeBbbjkhnwcI2KPkuOgrHVRRhuQ5WqD0RYxVtOOFNyQXme7g0nNl5RFNgvT7qv9qGeg==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "node_modules/@storybook/react": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.4.0.tgz", - "integrity": "sha512-jB7SNGdxFHFR9GgAPjrUUigE0pgOy3Bv3MaR9VdSGOZOnP+mjvZAO+ItPeKWHcQ7JnNujjtmMa2A80YcBfqBzQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.4.2.tgz", + "integrity": "sha512-rO5/aVKBVhIKENcL7G8ud4QKC5OyWBPCkJIvY6XUHIuhErJy9/4pP+sZ85jypVwx5kq+EqCPF8AEOWjIxB/4/Q==", "dev": true, "dependencies": { - "@storybook/components": "^8.4.0", + "@storybook/components": "8.4.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "^8.4.0", - "@storybook/preview-api": "^8.4.0", - "@storybook/react-dom-shim": "8.4.0", - "@storybook/theming": "^8.4.0" + "@storybook/manager-api": "8.4.2", + "@storybook/preview-api": "8.4.2", + "@storybook/react-dom-shim": "8.4.2", + "@storybook/theming": "8.4.2" }, "engines": { "node": ">=18.0.0" @@ -4179,10 +4026,10 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/test": "8.4.0", + "@storybook/test": "8.4.2", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.0", + "storybook": "^8.4.2", "typescript": ">= 4.2.x" }, "peerDependenciesMeta": { @@ -4214,9 +4061,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.0.tgz", - "integrity": "sha512-PYYZVdQ6/ts6hBMAwMEu4hfbyHFPzUYmVsZNtF2egaVJQ44xM4i1Zt+RJuo2NOt5VyBCfXJOs+lSIdmSBY2arw==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.2.tgz", + "integrity": "sha512-FZVTM1f34FpGnf6e3MDIKkz05gmn8H9wEccvQAgr8pEFe8VWfrpVWeUrmatSAfgrCMNXYC1avDend8UX6IM8Fg==", "dev": true, "funding": { "type": "opencollective", @@ -4225,18 +4072,18 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/test": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.0.tgz", - "integrity": "sha512-uHZ6+8RfEauwxi7Zy/LijfyIXrjCD7iTHmnTdT3BdP+2c/lDFAKXzHmbQJitefDFEgz1eHx/MArHZ8V3qu1ogg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.2.tgz", + "integrity": "sha512-MipTdboStv0hsqF2Sw8TZgP0YnxCcDYwxkTOd4hmRzev/7Brtvpi4pqjqh8k98ZCvhrCPAPVIoX5drk+oi3YUA==", "dev": true, "dependencies": { "@storybook/csf": "^0.1.11", "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.4.0", + "@storybook/instrumenter": "8.4.2", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.5.0", "@testing-library/user-event": "14.5.2", @@ -4248,20 +4095,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.4.2" } }, "node_modules/@storybook/theming": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.0.tgz", - "integrity": "sha512-S7Iv5HMiYEJZlkQM0K9bxACLN7s8lCSG3M2CN6A82LSoXayFauuaPpn3LrNE2BvkTpdu17w19YiGbVYhPtRqsg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.2.tgz", + "integrity": "sha512-9j4fnu5LcV+qSs1rdwf61Bt14lms0T1LOZkHxGNcS1c1oH+cPS+sxECh2lxtni+mvOAHUlBs9pKhVZzRPdWpvg==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.0" + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, "node_modules/@swc/counter": { @@ -4279,20 +4126,20 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.59.16", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.16.tgz", - "integrity": "sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==", + "version": "5.59.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.20.tgz", + "integrity": "sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "5.59.16", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.16.tgz", - "integrity": "sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==", + "version": "5.59.20", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.20.tgz", + "integrity": "sha512-Zly0egsK0tFdfSbh5/mapSa+Zfc3Et0Zkar7Wo5sQkFzWyB3p3uZWOHR2wrlAEEV2L953eLuDBtbgFvMYiLvUw==", "dependencies": { - "@tanstack/query-core": "5.59.16" + "@tanstack/query-core": "5.59.20" }, "funding": { "type": "github", @@ -4321,15 +4168,6 @@ "node": ">=18" } }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/@testing-library/jest-dom": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", @@ -4494,15 +4332,6 @@ "node": ">=6.9.0" } }, - "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -4577,6 +4406,26 @@ "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", "dev": true }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -4608,9 +4457,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.17.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.4.tgz", - "integrity": "sha512-Fi1Bj8qTJr4f1FDdHFR7oMlOawEYSzkHNdBJK+aRjcDDNHwEV3jPPjuZP2Lh2QNgXeqzM8Y+U6b6urKAog2rZw==", + "version": "20.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", + "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", "dev": true, "dependencies": { "undici-types": "~6.19.2" @@ -4666,16 +4515,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", - "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", + "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/type-utils": "8.12.2", - "@typescript-eslint/utils": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/type-utils": "8.13.0", + "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -4699,15 +4548,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", - "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", + "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", "debug": "^4.3.4" }, "engines": { @@ -4727,13 +4576,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", - "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", + "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2" + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4744,13 +4593,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", - "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", + "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.12.2", - "@typescript-eslint/utils": "8.12.2", + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/utils": "8.13.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -4768,9 +4617,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", - "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", + "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4781,13 +4630,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", - "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", + "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/visitor-keys": "8.12.2", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4808,40 +4657,44 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.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==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 6" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", - "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", + "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.12.2", - "@typescript-eslint/types": "8.12.2", - "@typescript-eslint/typescript-estree": "8.12.2" + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4855,12 +4708,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.12.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", - "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", + "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/types": "8.13.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -4958,148 +4811,148 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -5161,18 +5014,6 @@ "node": ">=8.9" } }, - "node_modules/adjust-sourcemap-loader/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", @@ -5319,12 +5160,12 @@ "dev": true }, "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "dequal": "^2.0.3" } }, "node_modules/array-buffer-byte-length": { @@ -5817,20 +5658,19 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "devOptional": true, + "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -6097,9 +5937,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001676", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", - "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==", + "version": "1.0.30001677", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", + "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", "funding": [ { "type": "opencollective", @@ -6166,24 +6006,45 @@ } }, "node_modules/chokidar": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", - "devOptional": true, + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "dependencies": { - "readdirp": "^4.0.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 8.10.0" }, "funding": { "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/chromatic": { - "version": "11.16.3", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.16.3.tgz", - "integrity": "sha512-bckarRbZ3M1BvsmhLqEMschuQPk2FlSD9cvy8383JwoVvaIqLr0dv1tI/DPM4LMuXOjTjeBSZZINVH9r3RMiiA==", + "version": "11.16.5", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.16.5.tgz", + "integrity": "sha512-wUEKXyu3GYmUg6Jq13uyRE9iC8ph5gbfDHdyHH0vQathkGQrcjHHdoxI/GXKIjU6d+xupLon8sxRV9NuZKTWbA==", "dev": true, "bin": { "chroma": "dist/bin.js", @@ -6517,34 +6378,6 @@ } } }, - "node_modules/css-loader/node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/css-select": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", @@ -6767,7 +6600,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "devOptional": true, + "dev": true, + "optional": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -6903,9 +6737,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.50", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", - "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", + "version": "1.5.52", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz", + "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==", "dev": true }, "node_modules/elliptic": { @@ -7086,9 +6920,9 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", - "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", + "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", "dev": true, "dependencies": { "call-bind": "^1.0.7", @@ -7099,6 +6933,7 @@ "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "globalthis": "^1.0.4", + "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", @@ -7332,11 +7167,33 @@ "glob": "10.3.10" } }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, + "node_modules/eslint-config-next/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7399,6 +7256,34 @@ } } }, + "node_modules/eslint-import-resolver-typescript/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "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.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/eslint-import-resolver-typescript/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/eslint-module-utils": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", @@ -7473,6 +7358,16 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, + "node_modules/eslint-plugin-import/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -7494,6 +7389,30 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/eslint-plugin-import/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -7503,6 +7422,18 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -7532,6 +7463,37 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eslint-plugin-jsx-a11y/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", @@ -7606,6 +7568,16 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, + "node_modules/eslint-plugin-react/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -7618,6 +7590,18 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-react/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -7645,9 +7629,9 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.10.1.tgz", - "integrity": "sha512-YpxkdqyiKpMIrRquuvBaCinsqmZJ86JvXRX/gtRa4Qctpk0ipFt2cWqEjkB1HHWWG0DVRXlUBKHjRogC2Ig1fg==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.10.2.tgz", + "integrity": "sha512-iOrFJfyLgRLIbWDLbbs3J4yrknvIB+uiZ8KGqGOEXTL7/BGuBMZLiIU9Q4Pm/VYJrHN4JqmMtwCSrAemHL2nFg==", "dev": true, "dependencies": { "@storybook/csf": "^0.1.11", @@ -7689,6 +7673,67 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -7811,9 +7856,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -7896,7 +7941,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "devOptional": true, + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -8019,52 +8064,26 @@ "webpack": "^5.11.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", + "node_modules/fork-ts-checker-webpack-plugin/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, "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/fork-ts-checker-webpack-plugin/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, "dependencies": { - "picomatch": "^2.2.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8.10.0" + "node": "*" } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { @@ -8219,22 +8238,21 @@ } }, "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -8259,42 +8277,34 @@ "dev": true }, "node_modules/glob/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==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/globalthis": { @@ -8864,7 +8874,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8909,7 +8919,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -8961,7 +8971,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.12.0" } @@ -9283,15 +9293,15 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsonfile": { @@ -9621,10 +9631,13 @@ } }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } }, "node_modules/lz-string": { "version": "1.5.0", @@ -9725,7 +9738,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "devOptional": true, + "dev": true, "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -9796,15 +9809,18 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -9909,6 +9925,55 @@ } } }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/next/node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -9929,7 +9994,8 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "devOptional": true + "dev": true, + "optional": true }, "node_modules/node-polyfill-webpack-plugin": { "version": "2.0.1", @@ -9970,18 +10036,6 @@ "webpack": ">=5" } }, - "node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -10358,6 +10412,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -10401,7 +10461,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, @@ -10507,9 +10567,10 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -10525,9 +10586,9 @@ } ], "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -10740,12 +10801,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -10772,6 +10827,12 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -10951,9 +11012,9 @@ } }, "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, "node_modules/react-refresh": { @@ -10982,16 +11043,15 @@ } }, "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", - "devOptional": true, - "engines": { - "node": ">= 14.16.0" + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=8.10.0" } }, "node_modules/recast": { @@ -11179,6 +11239,18 @@ "strip-ansi": "^6.0.1" } }, + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -11245,18 +11317,6 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "node_modules/resolve-url-loader/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/resolve-url-loader/node_modules/loader-utils": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", @@ -11306,27 +11366,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -11416,12 +11455,11 @@ } }, "node_modules/sass": { - "version": "1.80.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", - "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", + "version": "1.80.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz", + "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==", "devOptional": true, "dependencies": { - "@parcel/watcher": "^2.4.1", "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" @@ -11431,6 +11469,9 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, "node_modules/sass-loader": { @@ -11470,6 +11511,34 @@ } } }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "devOptional": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "devOptional": true, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -11764,12 +11833,12 @@ "dev": true }, "node_modules/storybook": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.0.tgz", - "integrity": "sha512-hLfXPtqfoQUMKVortxXdnQoUwDwtH85eSj9LbqGT/z1f/gLLYGNG3Mv3QbsRjHXhn+EfYffh7wuLpAn+Cicijw==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.2.tgz", + "integrity": "sha512-GMCgyAulmLNrkUtDkCpFO4SB77YrpiIxq6e5tzaQdXEuaDu1mdNwOuP3VG7nE2FzxmqDvagSgriM68YW9iFaZA==", "dev": true, "dependencies": { - "@storybook/core": "8.4.0" + "@storybook/core": "8.4.2" }, "bin": { "getstorybook": "bin/index.cjs", @@ -11894,31 +11963,16 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/string.prototype.includes": { @@ -12021,15 +12075,18 @@ } }, "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-ansi-cjs": { @@ -12045,6 +12102,18 @@ "node": ">=8" } }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -12098,9 +12167,10 @@ } }, "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "dev": true, "dependencies": { "client-only": "0.0.1" }, @@ -12108,7 +12178,7 @@ "node": ">= 12.0.0" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { @@ -12299,7 +12369,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -12343,15 +12413,17 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", + "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/tsconfig-paths-webpack-plugin": { @@ -12368,36 +12440,10 @@ "node": ">=10.13.0" } }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tty-browserify": { "version": "0.0.1", @@ -12424,12 +12470,12 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -12728,11 +12774,12 @@ } }, "node_modules/webpack": { - "version": "5.96.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.0.tgz", - "integrity": "sha512-gvn84AfQ4f6vUeNWmFuRp3vGERyxK4epADKTaAo60K0EQbY/YBNQbXH3Ji/ZRK5M25O/XneAOuChF4xQZjQ4xA==", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dev": true, "dependencies": { + "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -12811,6 +12858,18 @@ "strip-ansi": "^6.0.0" } }, + "node_modules/webpack-hot-middleware/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", @@ -13024,16 +13083,16 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { @@ -13048,21 +13107,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 9054323e..94af56f2 100644 --- a/package.json +++ b/package.json @@ -24,13 +24,14 @@ "devDependencies": { "@chromatic-com/storybook": "^3.2.2", "@next/eslint-plugin-next": "^15.0.2", - "@storybook/addon-essentials": "^8.4.0", - "@storybook/addon-interactions": "^8.4.0", + "@storybook/addon-essentials": "^8.4.2", + "@storybook/addon-interactions": "^8.4.2", "@storybook/addon-onboarding": "^8.4.0", + "@storybook/addon-docs": "^8.4.2", "@storybook/blocks": "^8.4.0", - "@storybook/nextjs": "^8.4.0", - "@storybook/react": "^8.4.0", - "@storybook/test": "^8.4.0", + "@storybook/nextjs": "^8.4.2", + "@storybook/react": "^8.4.2", + "@storybook/test": "^8.4.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^20", "@types/react": "^18", From 5aae36cfd4a58bef294eae729700bc2341fa703c Mon Sep 17 00:00:00 2001 From: deun Date: Thu, 7 Nov 2024 10:57:31 +0900 Subject: [PATCH 030/648] =?UTF-8?q?feat:=20colors=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/stories/colors.stories.tsx | 94 ++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 shared/styles/stories/colors.stories.tsx diff --git a/shared/styles/stories/colors.stories.tsx b/shared/styles/stories/colors.stories.tsx new file mode 100644 index 00000000..a4628118 --- /dev/null +++ b/shared/styles/stories/colors.stories.tsx @@ -0,0 +1,94 @@ +import type { Meta, StoryObj as Story } from '@storybook/react' + +const meta = { + title: 'Design System/Colors', + parameters: { + layout: 'centered', + }, +} satisfies Meta + +interface ColorBoxProps { + shade: string + hex: string +} + +const ColorBox = ({ shade, hex }: ColorBoxProps) => ( +

    +
    +
    {shade}
    +
    + {hex.toUpperCase()} +
    +
    +) + +interface ColorSectionProps { + title: string + colors: Array<{ shade: string; hex: string }> +} + +const ColorSection = ({ title, colors }: ColorSectionProps) => ( +
    +

    {title}

    +
    + {colors.map(({ shade, hex }) => ( + + ))} +
    +
    +) + +export const Colors: Story = { + render: () => ( + <> +

    Colors

    + + + + + + ), +} + +export default meta From 28fa9bce61701bbccf678ec3b6a9be459e52ad68 Mon Sep 17 00:00:00 2001 From: deun Date: Thu, 7 Nov 2024 11:17:02 +0900 Subject: [PATCH 031/648] =?UTF-8?q?feat:=20typography=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/stories/fonts.stories.tsx | 97 +++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 shared/styles/stories/fonts.stories.tsx diff --git a/shared/styles/stories/fonts.stories.tsx b/shared/styles/stories/fonts.stories.tsx new file mode 100644 index 00000000..39b8c7fd --- /dev/null +++ b/shared/styles/stories/fonts.stories.tsx @@ -0,0 +1,97 @@ +import type { Meta, StoryObj as Story } from '@storybook/react' + +const meta = { + title: 'Design System/Typography', + parameters: { + layout: 'centered', + }, +} satisfies Meta + +const TYPOGRAPHY_EXAMPLE = '인베스트메틱 Investmetic 0123' as const + +interface FontStyleModel { + size: string + weight: number + label: string +} + +interface FontPreviewProps extends FontStyleModel { + example?: string +} + +const FontPreview = ({ size, weight, label, example = TYPOGRAPHY_EXAMPLE }: FontPreviewProps) => ( +
    +
    +
    {label}
    +
    + {size} / {weight} +
    +
    +
    + {example} +
    +
    +) + +interface TypographySectionProps { + title: string + fonts: Array +} + +const TypographySection = ({ title, fonts }: TypographySectionProps) => ( +
    +

    {title}

    + {fonts.map((font) => ( + + ))} +
    +) + +export const Typography: Story = { + render: () => ( + <> +

    Typography

    + + + + + + + ), +} + +export default meta From 51d9b567bee7f50b36899257fb07f4acafdfbef3 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Thu, 7 Nov 2024 18:09:48 +0900 Subject: [PATCH 032/648] =?UTF-8?q?setting:=20route=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/{(home)/ui => (dashboard)}/.gitkeep | 0 app/{(home)/page.module.css => (dashboard)/admin/.gitkeep} | 0 app/(dashboard)/admin/category/.gitkeep | 0 app/(dashboard)/admin/notices/.gitkeep | 0 app/(dashboard)/admin/users/.gitkeep | 0 app/(dashboard)/my/.gitkeep | 0 app/(dashboard)/my/favorites/.gitkeep | 0 app/(dashboard)/my/questions/.gitkeep | 0 app/(dashboard)/my/strategies/.gitkeep | 0 app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep | 0 app/(dashboard)/strategies/.gitkeep | 0 app/(dashboard)/strategies/[strategyId]/.gitkeep | 0 app/(dashboard)/traders/.gitkeep | 0 app/(dashboard)/traders/[traderId]/.gitkeep | 0 app/(landing)/(home)/page.module.css | 0 app/{ => (landing)}/(home)/page.tsx | 0 app/(landing)/(home)/ui/.gitkeep | 0 app/{(home) => (landing)}/layout.tsx | 0 app/(landing)/signin/.gitkeep | 0 app/(landing)/signup/.gitkeep | 0 20 files changed, 0 insertions(+), 0 deletions(-) rename app/{(home)/ui => (dashboard)}/.gitkeep (100%) rename app/{(home)/page.module.css => (dashboard)/admin/.gitkeep} (100%) create mode 100644 app/(dashboard)/admin/category/.gitkeep create mode 100644 app/(dashboard)/admin/notices/.gitkeep create mode 100644 app/(dashboard)/admin/users/.gitkeep create mode 100644 app/(dashboard)/my/.gitkeep create mode 100644 app/(dashboard)/my/favorites/.gitkeep create mode 100644 app/(dashboard)/my/questions/.gitkeep create mode 100644 app/(dashboard)/my/strategies/.gitkeep create mode 100644 app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep create mode 100644 app/(dashboard)/strategies/.gitkeep create mode 100644 app/(dashboard)/strategies/[strategyId]/.gitkeep create mode 100644 app/(dashboard)/traders/.gitkeep create mode 100644 app/(dashboard)/traders/[traderId]/.gitkeep create mode 100644 app/(landing)/(home)/page.module.css rename app/{ => (landing)}/(home)/page.tsx (100%) create mode 100644 app/(landing)/(home)/ui/.gitkeep rename app/{(home) => (landing)}/layout.tsx (100%) create mode 100644 app/(landing)/signin/.gitkeep create mode 100644 app/(landing)/signup/.gitkeep diff --git a/app/(home)/ui/.gitkeep b/app/(dashboard)/.gitkeep similarity index 100% rename from app/(home)/ui/.gitkeep rename to app/(dashboard)/.gitkeep diff --git a/app/(home)/page.module.css b/app/(dashboard)/admin/.gitkeep similarity index 100% rename from app/(home)/page.module.css rename to app/(dashboard)/admin/.gitkeep diff --git a/app/(dashboard)/admin/category/.gitkeep b/app/(dashboard)/admin/category/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/admin/notices/.gitkeep b/app/(dashboard)/admin/notices/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/admin/users/.gitkeep b/app/(dashboard)/admin/users/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/my/.gitkeep b/app/(dashboard)/my/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/my/favorites/.gitkeep b/app/(dashboard)/my/favorites/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/my/questions/.gitkeep b/app/(dashboard)/my/questions/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/my/strategies/.gitkeep b/app/(dashboard)/my/strategies/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep b/app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/strategies/.gitkeep b/app/(dashboard)/strategies/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/strategies/[strategyId]/.gitkeep b/app/(dashboard)/strategies/[strategyId]/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/traders/.gitkeep b/app/(dashboard)/traders/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(dashboard)/traders/[traderId]/.gitkeep b/app/(dashboard)/traders/[traderId]/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(landing)/(home)/page.module.css b/app/(landing)/(home)/page.module.css new file mode 100644 index 00000000..e69de29b diff --git a/app/(home)/page.tsx b/app/(landing)/(home)/page.tsx similarity index 100% rename from app/(home)/page.tsx rename to app/(landing)/(home)/page.tsx diff --git a/app/(landing)/(home)/ui/.gitkeep b/app/(landing)/(home)/ui/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(home)/layout.tsx b/app/(landing)/layout.tsx similarity index 100% rename from app/(home)/layout.tsx rename to app/(landing)/layout.tsx diff --git a/app/(landing)/signin/.gitkeep b/app/(landing)/signin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/(landing)/signup/.gitkeep b/app/(landing)/signup/.gitkeep new file mode 100644 index 00000000..e69de29b From 42b018b0bece3d0464dc6206473ae0883845be39 Mon Sep 17 00:00:00 2001 From: hoyoen Date: Fri, 8 Nov 2024 19:05:49 +0900 Subject: [PATCH 033/648] =?UTF-8?q?settings:=20api=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=8B=9C=20=EA=B2=BD=EB=A1=9C=20rewiter=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/next.config.mjs b/next.config.mjs index d97a24a4..a02c122f 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -8,6 +8,14 @@ const nextConfig = { @import "@/shared/styles/base/functions"; `, }, + async rewrites() { + return [ + { + source: '/api/:path*', + destination: `${process.env.API_HOST}/api/:path*`, + }, + ] + }, } export default nextConfig From c440f4d5a71a9dc0c199d3c999ccc85795f859c2 Mon Sep 17 00:00:00 2001 From: hoyoen Date: Fri, 8 Nov 2024 20:27:48 +0900 Subject: [PATCH 034/648] =?UTF-8?q?settings:=20msw=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 2 +- app/layout.tsx | 7 + mocks/browser.ts | 5 + mocks/handlers.ts | 15 + mocks/index.ts | 11 + mocks/server.ts | 5 + package-lock.json | 563 ++++++++++++++++++++++++++++++++++++ package.json | 6 + public/mockServiceWorker.js | 290 +++++++++++++++++++ shared/constants/env.ts | 4 + 10 files changed, 907 insertions(+), 1 deletion(-) create mode 100644 mocks/browser.ts create mode 100644 mocks/handlers.ts create mode 100644 mocks/index.ts create mode 100644 mocks/server.ts create mode 100644 public/mockServiceWorker.js create mode 100644 shared/constants/env.ts diff --git a/.eslintrc.js b/.eslintrc.js index a62490e4..89b5764e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -126,5 +126,5 @@ module.exports = { }, }, }, - ignorePatterns: ['dist', '.eslintrc.cjs'], + ignorePatterns: ['dist', '.eslintrc.js', 'public/mockServiceWorker.js'], } diff --git a/app/layout.tsx b/app/layout.tsx index 75b512e6..e6a50f0b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,8 @@ import type { Metadata } from 'next' +import initMSW from '@/mocks' + +import { ENV } from '@/shared/constants/env' import { QueryProvider } from '@/shared/providers' import '@/shared/styles/global.scss' @@ -10,6 +13,10 @@ export const metadata: Metadata = { description: '', } +if (process.env.NODE_ENV === ENV.DEVELOPMENT) { + initMSW() +} + const RootLayout = ({ children }: { children: React.ReactNode }) => { return ( diff --git a/mocks/browser.ts b/mocks/browser.ts new file mode 100644 index 00000000..d3824e65 --- /dev/null +++ b/mocks/browser.ts @@ -0,0 +1,5 @@ +import { setupWorker } from 'msw/browser' + +import { handlers } from './handlers' + +export const worker = setupWorker(...handlers) diff --git a/mocks/handlers.ts b/mocks/handlers.ts new file mode 100644 index 00000000..0e68c0c2 --- /dev/null +++ b/mocks/handlers.ts @@ -0,0 +1,15 @@ +import { HttpResponse, http } from 'msw' + +const { API_HOST } = process.env + +const handlers = [ + http.get(`${API_HOST}/api/users`, () => { + return HttpResponse.json({ + id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', + firstName: 'John', + lastName: 'Maverick', + }) + }), +] + +export { handlers } diff --git a/mocks/index.ts b/mocks/index.ts new file mode 100644 index 00000000..00005180 --- /dev/null +++ b/mocks/index.ts @@ -0,0 +1,11 @@ +async function initMSW() { + if (typeof window === 'undefined') { + const { server } = await import('./server') + server.listen() + } else { + const { worker } = await import('./browser') + worker.start() + } +} + +export default initMSW diff --git a/mocks/server.ts b/mocks/server.ts new file mode 100644 index 00000000..e5e2e69c --- /dev/null +++ b/mocks/server.ts @@ -0,0 +1,5 @@ +import { setupServer } from 'msw/node' + +import { handlers } from './handlers' + +export const server = setupServer(...handlers) diff --git a/package-lock.json b/package-lock.json index e37098ff..b283b26f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-storybook": "^0.10.1", "lefthook": "^1.8.2", + "msw": "^2.6.2", "prettier": "^3.3.3", "sass": "^1.80.5", "storybook": "^8.4.0", @@ -1805,6 +1806,34 @@ "node": ">=6.9.0" } }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", + "dev": true, + "dependencies": { + "cookie": "^0.7.2" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dev": true, + "dependencies": { + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" + } + }, "node_modules/@chromatic-com/storybook": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.2.tgz", @@ -2745,6 +2774,109 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@inquirer/confirm": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.1.tgz", + "integrity": "sha512-6ycMm7k7NUApiMGfVc32yIPp28iPKxhGRMqoNDiUjq2RyTAkbs5Fx0TdzBqhabcKvniDdAAvHCmsRjnNfTsogw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.0.1", + "@inquirer/type": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.0.1.tgz", + "integrity": "sha512-KKTgjViBQUi3AAssqjUFMnMO3CM3qwCHvePV9EW+zTKGKafFGFF01sc1yOIYjLJ7QU52G/FbzKc+c01WLzXmVQ==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.7", + "@inquirer/type": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", + "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", + "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", + "dev": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2837,6 +2969,23 @@ "react": ">=16" } }, + "node_modules/@mswjs/interceptors": { + "version": "0.36.10", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.36.10.tgz", + "integrity": "sha512-GXrJgakgJW3DWKueebkvtYgGKkxA7s0u5B0P5syJM5rvQUnrpLPigvci8Hukl7yEM+sU06l+er2Fgvx/gmiRgg==", + "dev": true, + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@next/env": { "version": "14.2.16", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.16.tgz", @@ -3030,6 +3179,28 @@ "node": ">=12.4.0" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "node_modules/@parcel/watcher": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", @@ -4400,6 +4571,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "node_modules/@types/doctrine": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", @@ -4508,6 +4685,18 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -5092,6 +5281,33 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-html": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", @@ -6115,11 +6331,83 @@ "node": ">=0.10.0" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -6214,6 +6502,15 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -8189,6 +8486,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -8346,6 +8652,15 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -8459,6 +8774,12 @@ "he": "bin/he" } }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true + }, "node_modules/highcharts": { "version": "11.4.8", "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-11.4.8.tgz", @@ -8967,6 +9288,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9847,6 +10174,71 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/msw": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.2.tgz", + "integrity": "sha512-RdRgPvjfuzMIACkWv7VOVAeSRYMU3ofokLv1w0RsbFX960qnj/tFEyOFXY0G2GTUd9trA6rHuHciM/FKpBp6/A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.36.5", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.26.1", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -10245,6 +10637,12 @@ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -10418,6 +10816,12 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -10833,6 +11237,15 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/psl": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", + "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + } + }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -10886,6 +11299,12 @@ "node": ">=0.4.x" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", @@ -11251,6 +11670,15 @@ "node": ">=8" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -11260,6 +11688,12 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -11832,6 +12266,15 @@ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "dev": true }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/storybook": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.2.tgz", @@ -11916,6 +12359,12 @@ "node": ">=10.0.0" } }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -12377,6 +12826,30 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/ts-api-utils": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", @@ -12710,6 +13183,16 @@ "node": ">= 0.4" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/url/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -13143,6 +13626,15 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -13158,6 +13650,65 @@ "node": ">= 6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -13170,6 +13721,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zustand": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz", diff --git a/package.json b/package.json index 92d6d827..efb3d344 100644 --- a/package.json +++ b/package.json @@ -48,9 +48,15 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-storybook": "^0.10.1", "lefthook": "^1.8.2", + "msw": "^2.6.2", "prettier": "^3.3.3", "sass": "^1.80.5", "storybook": "^8.4.0", "typescript": "^5" + }, + "msw": { + "workerDirectory": [ + "public" + ] } } diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js new file mode 100644 index 00000000..1e368468 --- /dev/null +++ b/public/mockServiceWorker.js @@ -0,0 +1,290 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const PACKAGE_VERSION = '2.6.2' +const INTEGRITY_CHECKSUM = '07a8241b182f8a246a7cd39894799a9e' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, + }, + [responseClone.body] + ) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone() + + function passthrough() { + const headers = Object.fromEntries(requestClone.headers.entries()) + + // Remove internal MSW request header so the passthrough request + // complies with any potential CORS preflight checks on the server. + // Some servers forbid unknown request headers. + delete headers['x-msw-intention'] + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const requestBuffer = await request.arrayBuffer() + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, + }, + [requestBuffer] + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean))) + }) +} + +async function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} diff --git a/shared/constants/env.ts b/shared/constants/env.ts new file mode 100644 index 00000000..2f9768ef --- /dev/null +++ b/shared/constants/env.ts @@ -0,0 +1,4 @@ +export const ENV = { + PRODUCTION: 'production', + DEVELOPMENT: 'development', +} as const From 04036105257583d66fa35274ca3548e800da2d17 Mon Sep 17 00:00:00 2001 From: hoyoen Date: Mon, 11 Nov 2024 16:18:56 +0900 Subject: [PATCH 035/648] =?UTF-8?q?setting=20test:=20msw=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20=EA=B3=BC=EC=A0=95=EC=97=90=EC=84=9C=EC=9D=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=EB=93=A4=20(#1?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/test/client/page.tsx | 36 +++++++++++++ app/test/server/page.tsx | 34 ++++++++++++ app/test/ui/email-check-button/index.tsx | 25 +++++++++ app/test/ui/signup-button/index.tsx | 29 ++++++++++ app/test/ui/styels.ts | 7 +++ app/test/ui/withdraw-button/index.tsx | 27 ++++++++++ mocks/handlers.ts | 14 +---- mocks/handlers/index.ts | 2 + mocks/handlers/post.ts | 12 +++++ mocks/handlers/user.ts | 68 ++++++++++++++++++++++++ 10 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 app/test/client/page.tsx create mode 100644 app/test/server/page.tsx create mode 100644 app/test/ui/email-check-button/index.tsx create mode 100644 app/test/ui/signup-button/index.tsx create mode 100644 app/test/ui/styels.ts create mode 100644 app/test/ui/withdraw-button/index.tsx create mode 100644 mocks/handlers/index.ts create mode 100644 mocks/handlers/post.ts create mode 100644 mocks/handlers/user.ts diff --git a/app/test/client/page.tsx b/app/test/client/page.tsx new file mode 100644 index 00000000..ee9310b6 --- /dev/null +++ b/app/test/client/page.tsx @@ -0,0 +1,36 @@ +'use client' + +// 클라이언트 컴포넌트 test +import { useEffect, useState } from 'react' + +import EmailCheckButton from '../ui/email-check-button' +import SignupButton from '../ui/signup-button' +import WithdrawButton from '../ui/withdraw-button' + +const Sub = () => { + const [user, setUser] = useState<{ title: string }>() + + useEffect(() => { + const fetchUsers = async () => { + const res = await fetch('/api/posts') + if (res.ok) { + return res.json() + } + return null + } + + fetchUsers().then((user) => setUser(user)) + }, []) + + return ( +
    + Title : {user?.title} +
    + + + +
    + ) +} + +export default Sub diff --git a/app/test/server/page.tsx b/app/test/server/page.tsx new file mode 100644 index 00000000..da63cea8 --- /dev/null +++ b/app/test/server/page.tsx @@ -0,0 +1,34 @@ +// 서버 컴포넌트 test +import EmailCheckButton from '../ui/email-check-button' +import SignupButton from '../ui/signup-button' +import WithdrawButton from '../ui/withdraw-button' + +const { API_HOST } = process.env + +const Home = async () => { + const fetchPost = async () => { + try { + const res = await fetch(`${API_HOST}/api/posts`) + if (res.ok) { + return res.json() + } + return [] + } catch (e) { + console.log('error', e) + } + } + + const user = await fetchPost() + + return ( +
    + Title : {user?.title} +
    + + + +
    + ) +} + +export default Home diff --git a/app/test/ui/email-check-button/index.tsx b/app/test/ui/email-check-button/index.tsx new file mode 100644 index 00000000..f5a2a292 --- /dev/null +++ b/app/test/ui/email-check-button/index.tsx @@ -0,0 +1,25 @@ +'use client' + +import { styles } from '../styels' + +const EmailCheckButton = () => { + const checkEmail = async (email: string) => { + const res = await fetch(`/api/users/check/email?email=${email}`) + if (res.ok) { + const result = await res.json() + alert(result.message) + return console.log(result) + } + + console.log('not ok') + return null + } + + return ( + + ) +} + +export default EmailCheckButton diff --git a/app/test/ui/signup-button/index.tsx b/app/test/ui/signup-button/index.tsx new file mode 100644 index 00000000..81f43bc3 --- /dev/null +++ b/app/test/ui/signup-button/index.tsx @@ -0,0 +1,29 @@ +'use client' + +import { styles } from '../styels' + +const SignupButton = () => { + const signup = async (userName: string, password: string) => { + const res = await fetch(`/api/users/signup`, { + method: 'POST', + body: JSON.stringify({ userName, password }), + }) + + if (res.ok) { + const result = await res.json() + alert(result.message) + return console.log(result) + } + + console.log('not ok') + return null + } + + return ( + + ) +} + +export default SignupButton diff --git a/app/test/ui/styels.ts b/app/test/ui/styels.ts new file mode 100644 index 00000000..af4f0ed2 --- /dev/null +++ b/app/test/ui/styels.ts @@ -0,0 +1,7 @@ +import { CSSProperties } from 'react' + +export const styles: CSSProperties = { + padding: '6px 12px', + border: '1px solid black', + margin: '12px', +} diff --git a/app/test/ui/withdraw-button/index.tsx b/app/test/ui/withdraw-button/index.tsx new file mode 100644 index 00000000..453fd747 --- /dev/null +++ b/app/test/ui/withdraw-button/index.tsx @@ -0,0 +1,27 @@ +'use client' + +import { styles } from '../styels' + +const WithdrawButton = () => { + const 탈퇴 = async (userId: string) => { + const res = await fetch(`/api/users/${userId}`, { + method: 'DELETE', + }) + + if (res.ok) { + const result = await res.json() + alert(result.message) + return console.log(result) + } + + console.log('not ok') + } + + return ( + + ) +} + +export default WithdrawButton diff --git a/mocks/handlers.ts b/mocks/handlers.ts index 0e68c0c2..4af399c8 100644 --- a/mocks/handlers.ts +++ b/mocks/handlers.ts @@ -1,15 +1,5 @@ -import { HttpResponse, http } from 'msw' +import { postHandlers, userHandlers } from './handlers/' -const { API_HOST } = process.env - -const handlers = [ - http.get(`${API_HOST}/api/users`, () => { - return HttpResponse.json({ - id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', - firstName: 'John', - lastName: 'Maverick', - }) - }), -] +const handlers = [...userHandlers, ...postHandlers] export { handlers } diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts new file mode 100644 index 00000000..3f70ad6f --- /dev/null +++ b/mocks/handlers/index.ts @@ -0,0 +1,2 @@ +export { postHandlers } from './post' +export { userHandlers } from './user' diff --git a/mocks/handlers/post.ts b/mocks/handlers/post.ts new file mode 100644 index 00000000..5fab54b9 --- /dev/null +++ b/mocks/handlers/post.ts @@ -0,0 +1,12 @@ +import { HttpResponse, http } from 'msw' + +const allPosts = new Map() +allPosts.set(123, { title: 'How to be with X-mas with girl-friend' }) + +const { API_HOST } = process.env + +export const postHandlers = [ + http.get(`${API_HOST}/api/posts`, () => { + return HttpResponse.json({ title: 'How to be friend with you' }) + }), +] diff --git a/mocks/handlers/user.ts b/mocks/handlers/user.ts new file mode 100644 index 00000000..ccf3f1c3 --- /dev/null +++ b/mocks/handlers/user.ts @@ -0,0 +1,68 @@ +import { HttpResponse, http } from 'msw' + +const { API_HOST } = process.env + +export const userHandlers = [ + http.get(`${API_HOST}/api/users/check/email`, ({ request }) => { + const url = new URL(request.url) + const email = url.searchParams.get('email') + + const emailList = new Set(['kbhoon@gmail.com']) + + if (!email || emailList.has(email)) { + return HttpResponse.json({ + isSuccess: false, + message: '사용할 수 없는 이메일입니다.', + code: 2204, + }) + } + + return HttpResponse.json({ + isSuccess: true, + message: '사용 가능한 이메일입니다.', + }) + }), + http.post(`${API_HOST}/api/users/signup`, async ({ request }) => { + const newUser = (await request.json()) as { userName: string; password: string } + const { userName, password } = newUser + + if (!userName || !password) + return HttpResponse.json( + { + isSuccess: false, + message: '회원가입에 실패하였습니다.', + code: 2003, + }, + { status: 400 } + ) + + return HttpResponse.json( + { + isSuccess: true, + message: '회원가입이 완료되었습니다. 로그인 화면으로 이동합니다.', + }, + { status: 201 } + ) + }), + http.delete(`${API_HOST}/api/users/:userId`, async ({ params }) => { + const { userId } = params + + if (!userId) + return HttpResponse.json( + { + isSuccess: false, + message: '회원 탈퇴에 실패하였습니다.', + code: 2004, + }, + { status: 400 } + ) + + return HttpResponse.json( + { + isSuccess: true, + message: '탈퇴가 완료되었습니다. 그 동안 서비스를 이용해주셔서 감사합니다.', + }, + { status: 201 } + ) + }), +] From 25a181e4d91f0ce579c2927b231f01037f320403 Mon Sep 17 00:00:00 2001 From: hoyoen Date: Mon, 11 Nov 2024 16:36:05 +0900 Subject: [PATCH 036/648] =?UTF-8?q?setting=20test:=20msw=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20=EA=B3=BC=EC=A0=95=EC=97=90=EC=84=9C=EC=9D=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/test/client/page.tsx | 3 +++ app/test/server/page.tsx | 4 ++++ app/test/ui/title/index.tsx | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 app/test/ui/title/index.tsx diff --git a/app/test/client/page.tsx b/app/test/client/page.tsx index ee9310b6..6784bef4 100644 --- a/app/test/client/page.tsx +++ b/app/test/client/page.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from 'react' import EmailCheckButton from '../ui/email-check-button' import SignupButton from '../ui/signup-button' +import Title from '../ui/title' import WithdrawButton from '../ui/withdraw-button' const Sub = () => { @@ -25,6 +26,8 @@ const Sub = () => { return (
    Title : {user?.title} + {/* 서버 컴포넌트 in 클라이언트 주석 풀면 콘솔 난리남 */} + {/* */} <br /> <EmailCheckButton /> <SignupButton /> diff --git a/app/test/server/page.tsx b/app/test/server/page.tsx index da63cea8..c23cde09 100644 --- a/app/test/server/page.tsx +++ b/app/test/server/page.tsx @@ -1,6 +1,7 @@ // 서버 컴포넌트 test import EmailCheckButton from '../ui/email-check-button' import SignupButton from '../ui/signup-button' +import Title from '../ui/title' import WithdrawButton from '../ui/withdraw-button' const { API_HOST } = process.env @@ -23,7 +24,10 @@ const Home = async () => { return ( <div> Title : {user?.title} + {/* 서버 컴포넌트 in 서버 컴포넌트 문제 없음 */} + <Title /> <br /> + {/* 클라이언트 컴포넌트 in 서버 컴포넌트 문제 없음 */} <EmailCheckButton /> <SignupButton /> <WithdrawButton /> diff --git a/app/test/ui/title/index.tsx b/app/test/ui/title/index.tsx new file mode 100644 index 00000000..a3cf934a --- /dev/null +++ b/app/test/ui/title/index.tsx @@ -0,0 +1,22 @@ +// 서버 컴포넌트 + +const { API_HOST } = process.env + +const Title = async () => { + const fetchPost = async () => { + try { + const res = await fetch(`${API_HOST}/api/posts`) + if (res.ok) { + return res.json() + } + return [] + } catch (e) { + console.log('error', e) + } + } + + const post = await fetchPost() + return <h1>{post.title}</h1> +} + +export default Title From 4551b854a12f4672226c6addb3aa92ccb8540ed5 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Tue, 12 Nov 2024 16:01:38 +0900 Subject: [PATCH 037/648] =?UTF-8?q?feat:=20msw=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 14 +- app/test/client/page.tsx | 29 +- app/test/server/page.tsx | 18 +- app/test/ui/title/index.tsx | 14 +- mocks/handlers/post.ts | 13 +- mocks/handlers/user.ts | 17 +- mocks/index.ts | 13 +- mocks/server.ts | 1 + next.config.mjs | 29 +- package-lock.json | 1814 ++++++++++++++++++++------ public/mockServiceWorker.js | 18 +- shared/providers/index.ts | 2 + shared/providers/msw-Initializer.tsx | 31 + shared/stores/msw.ts | 13 + shared/utils/env.ts | 3 + 15 files changed, 1541 insertions(+), 488 deletions(-) create mode 100644 shared/providers/msw-Initializer.tsx create mode 100644 shared/stores/msw.ts create mode 100644 shared/utils/env.ts diff --git a/app/layout.tsx b/app/layout.tsx index e6a50f0b..922c9aac 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,9 +1,6 @@ import type { Metadata } from 'next' -import initMSW from '@/mocks' - -import { ENV } from '@/shared/constants/env' -import { QueryProvider } from '@/shared/providers' +import { MSWInitializer, QueryProvider } from '@/shared/providers' import '@/shared/styles/global.scss' import { pretendard } from './fonts' @@ -13,15 +10,14 @@ export const metadata: Metadata = { description: '', } -if (process.env.NODE_ENV === ENV.DEVELOPMENT) { - initMSW() -} - const RootLayout = ({ children }: { children: React.ReactNode }) => { return ( <html lang="ko" className={pretendard.variable}> <body> - <QueryProvider>{children}</QueryProvider> + <QueryProvider> + <MSWInitializer /> + {children} + </QueryProvider> </body> </html> ) diff --git a/app/test/client/page.tsx b/app/test/client/page.tsx index 6784bef4..8c522409 100644 --- a/app/test/client/page.tsx +++ b/app/test/client/page.tsx @@ -1,33 +1,42 @@ 'use client' -// 클라이언트 컴포넌트 test import { useEffect, useState } from 'react' +import { useMSWStore } from '@/shared/stores/msw' + import EmailCheckButton from '../ui/email-check-button' import SignupButton from '../ui/signup-button' -import Title from '../ui/title' import WithdrawButton from '../ui/withdraw-button' const Sub = () => { const [user, setUser] = useState<{ title: string }>() + const isReady = useMSWStore((state) => state.isReady) useEffect(() => { const fetchUsers = async () => { - const res = await fetch('/api/posts') - if (res.ok) { - return res.json() + if (!isReady) return + + try { + const res = await fetch('/api/posts') + if (res.ok) { + const data = await res.json() + setUser(data) + } + } catch (error) { + console.error('Fetch error:', error) } - return null } - fetchUsers().then((user) => setUser(user)) - }, []) + fetchUsers() + }, [isReady]) + + if (!isReady) { + return <div>로딩 중..</div> + } return ( <div> Title : {user?.title} - {/* 서버 컴포넌트 in 클라이언트 주석 풀면 콘솔 난리남 */} - {/* <Title /> */} <br /> <EmailCheckButton /> <SignupButton /> diff --git a/app/test/server/page.tsx b/app/test/server/page.tsx index c23cde09..3474246f 100644 --- a/app/test/server/page.tsx +++ b/app/test/server/page.tsx @@ -1,33 +1,31 @@ // 서버 컴포넌트 test +import '@/mocks/server' + import EmailCheckButton from '../ui/email-check-button' import SignupButton from '../ui/signup-button' import Title from '../ui/title' import WithdrawButton from '../ui/withdraw-button' -const { API_HOST } = process.env - const Home = async () => { const fetchPost = async () => { try { - const res = await fetch(`${API_HOST}/api/posts`) - if (res.ok) { - return res.json() + const res = await fetch(`http://localhost:3000/api/posts`) + if (!res.ok) { + throw new Error('Failed to fetch post') } - return [] + return res.json() } catch (e) { console.log('error', e) + return { title: '잘못된 제목이라니까' } } } const user = await fetchPost() - return ( <div> - Title : {user?.title} - {/* 서버 컴포넌트 in 서버 컴포넌트 문제 없음 */} + Title : {user.title} <Title /> <br /> - {/* 클라이언트 컴포넌트 in 서버 컴포넌트 문제 없음 */} <EmailCheckButton /> <SignupButton /> <WithdrawButton /> diff --git a/app/test/ui/title/index.tsx b/app/test/ui/title/index.tsx index a3cf934a..dccd784a 100644 --- a/app/test/ui/title/index.tsx +++ b/app/test/ui/title/index.tsx @@ -1,17 +1,13 @@ // 서버 컴포넌트 - -const { API_HOST } = process.env - const Title = async () => { const fetchPost = async () => { try { - const res = await fetch(`${API_HOST}/api/posts`) - if (res.ok) { - return res.json() - } - return [] + const res = await fetch('http://localhost:3000/api/posts') + if (!res.ok) throw new Error('Fetch 요청 실패~') + return res.json() } catch (e) { - console.log('error', e) + console.error(e) + return { title: '잘못된 제목입니다' } } } diff --git a/mocks/handlers/post.ts b/mocks/handlers/post.ts index 5fab54b9..af7d95f5 100644 --- a/mocks/handlers/post.ts +++ b/mocks/handlers/post.ts @@ -3,10 +3,17 @@ import { HttpResponse, http } from 'msw' const allPosts = new Map() allPosts.set(123, { title: 'How to be with X-mas with girl-friend' }) -const { API_HOST } = process.env +// const { API_HOST } = process.env export const postHandlers = [ - http.get(`${API_HOST}/api/posts`, () => { - return HttpResponse.json({ title: 'How to be friend with you' }) + http.get('/api/posts', () => { + return HttpResponse.json({ + title: '오늘 점심은 뭐가 좋을까', + }) + }), + http.get('http://localhost:3000/api/posts', () => { + return HttpResponse.json({ + title: '역시 제육볶음이지ㅋ', + }) }), ] diff --git a/mocks/handlers/user.ts b/mocks/handlers/user.ts index ccf3f1c3..645c5005 100644 --- a/mocks/handlers/user.ts +++ b/mocks/handlers/user.ts @@ -1,9 +1,8 @@ import { HttpResponse, http } from 'msw' -const { API_HOST } = process.env - +// const { API_HOST } = process.env export const userHandlers = [ - http.get(`${API_HOST}/api/users/check/email`, ({ request }) => { + http.get('/api/users/check/email', ({ request }) => { const url = new URL(request.url) const email = url.searchParams.get('email') @@ -22,11 +21,12 @@ export const userHandlers = [ message: '사용 가능한 이메일입니다.', }) }), - http.post(`${API_HOST}/api/users/signup`, async ({ request }) => { + + http.post('/api/users/signup', async ({ request }) => { const newUser = (await request.json()) as { userName: string; password: string } const { userName, password } = newUser - if (!userName || !password) + if (!userName || !password) { return HttpResponse.json( { isSuccess: false, @@ -35,6 +35,7 @@ export const userHandlers = [ }, { status: 400 } ) + } return HttpResponse.json( { @@ -44,10 +45,11 @@ export const userHandlers = [ { status: 201 } ) }), - http.delete(`${API_HOST}/api/users/:userId`, async ({ params }) => { + + http.delete('/api/users/:userId', ({ params }) => { const { userId } = params - if (!userId) + if (!userId) { return HttpResponse.json( { isSuccess: false, @@ -56,6 +58,7 @@ export const userHandlers = [ }, { status: 400 } ) + } return HttpResponse.json( { diff --git a/mocks/index.ts b/mocks/index.ts index 00005180..31d1add1 100644 --- a/mocks/index.ts +++ b/mocks/index.ts @@ -1,11 +1,10 @@ async function initMSW() { - if (typeof window === 'undefined') { - const { server } = await import('./server') - server.listen() - } else { - const { worker } = await import('./browser') - worker.start() - } + const { worker } = await import('./browser') + await worker.start({ + serviceWorker: { + url: '/mockServiceWorker.js', + }, + }) } export default initMSW diff --git a/mocks/server.ts b/mocks/server.ts index e5e2e69c..45deee17 100644 --- a/mocks/server.ts +++ b/mocks/server.ts @@ -3,3 +3,4 @@ import { setupServer } from 'msw/node' import { handlers } from './handlers' export const server = setupServer(...handlers) +server.listen() diff --git a/next.config.mjs b/next.config.mjs index a02c122f..a9274889 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,14 +1,35 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + transpilePackages: ['msw'], + webpack: (config, { isServer }) => { + if (isServer) { + if (Array.isArray(config.resolve.alias)) { + config.resolve.alias.push({ name: 'msw/browser', alias: false }) + } else { + config.resolve.alias['msw/browser'] = false + } + } else { + if (Array.isArray(config.resolve.alias)) { + config.resolve.alias.push({ name: 'msw/node', alias: false }) + } else { + config.resolve.alias['msw/node'] = false + } + } + return config + }, sassOptions: { includePaths: ['./shared/styles'], prependData: ` - @import "@/shared/styles/base/variables"; - @import "@/shared/styles/base/mixins"; - @import "@/shared/styles/base/functions"; - `, + @import "@/shared/styles/base/variables"; + @import "@/shared/styles/base/mixins"; + @import "@/shared/styles/base/functions"; + `, }, async rewrites() { + if (process.env.NODE_ENV === 'development') { + return [] + } + return [ { source: '/api/:path*', diff --git a/package-lock.json b/package-lock.json index b283b26f..6db08f93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,13 +54,15 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -74,6 +76,7 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -88,6 +91,7 @@ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -97,6 +101,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -127,6 +132,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -136,6 +142,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.26.2", "@babel/types": "^7.26.0", @@ -152,6 +159,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -164,6 +172,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -177,6 +186,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -193,6 +203,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -202,6 +213,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", @@ -223,6 +235,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -232,6 +245,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.1.1", @@ -249,15 +263,17 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -274,6 +290,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -286,6 +303,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.24.7", "@babel/types": "^7.24.7" @@ -299,6 +317,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -311,6 +330,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -324,6 +344,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -337,6 +358,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -354,6 +376,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.25.9" }, @@ -366,6 +389,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -375,6 +399,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", @@ -392,6 +417,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", @@ -409,6 +435,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -422,6 +449,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -435,6 +463,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -447,6 +476,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -456,6 +486,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -465,6 +496,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -474,6 +506,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", @@ -488,6 +521,7 @@ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.0" @@ -501,6 +535,7 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.26.0" }, @@ -516,6 +551,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -532,6 +568,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -547,6 +584,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -562,6 +600,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", @@ -579,6 +618,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -595,6 +635,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -607,6 +648,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -619,6 +661,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -631,6 +674,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -646,6 +690,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -661,6 +706,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -676,6 +722,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -691,6 +738,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -707,6 +755,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -722,6 +771,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9", @@ -739,6 +789,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -756,6 +807,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -771,6 +823,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -786,6 +839,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -802,6 +856,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -818,6 +873,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", @@ -838,6 +894,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" @@ -854,6 +911,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -869,6 +927,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -885,6 +944,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -900,6 +960,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -916,6 +977,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -931,6 +993,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -947,6 +1010,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -962,6 +1026,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -978,6 +1043,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -995,6 +1061,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1010,6 +1077,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1025,6 +1093,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1040,6 +1109,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1055,6 +1125,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1071,6 +1142,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1088,6 +1160,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1106,6 +1179,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1122,6 +1196,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1138,6 +1213,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1153,6 +1229,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1168,6 +1245,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1183,6 +1261,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1200,6 +1279,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" @@ -1216,6 +1296,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1231,6 +1312,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1247,6 +1329,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1262,6 +1345,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1278,6 +1362,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1295,6 +1380,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1310,6 +1396,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1325,6 +1412,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -1344,6 +1432,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, @@ -1359,6 +1448,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1375,6 +1465,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" @@ -1391,6 +1482,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1407,6 +1499,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1422,6 +1515,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1442,6 +1536,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -1451,6 +1546,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1466,6 +1562,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1482,6 +1579,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1497,6 +1595,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1512,6 +1611,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1527,6 +1627,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1546,6 +1647,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1561,6 +1663,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1577,6 +1680,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1593,6 +1697,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1609,6 +1714,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.26.0", "@babel/helper-compilation-targets": "^7.25.9", @@ -1692,6 +1798,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -1701,6 +1808,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -1715,6 +1823,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -1735,6 +1844,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -1754,6 +1864,7 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dev": true, + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1766,6 +1877,7 @@ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", @@ -1780,6 +1892,7 @@ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/generator": "^7.25.9", @@ -1798,6 +1911,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -1811,6 +1925,7 @@ "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", "dev": true, + "license": "ISC", "dependencies": { "cookie": "^0.7.2" } @@ -1820,6 +1935,7 @@ "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", "dev": true, + "license": "ISC", "dependencies": { "statuses": "^2.0.1" } @@ -1829,6 +1945,7 @@ "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", "dev": true, + "license": "ISC", "dependencies": { "@types/tough-cookie": "^4.0.5", "tough-cookie": "^4.1.4" @@ -1839,6 +1956,7 @@ "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.2.tgz", "integrity": "sha512-xmXt/GW0hAPbzNTrxYuVo43Adrtjue4DeVrsoIIEeJdGaPNNeNf+DHMlJKOBdlHmCnFUoe9R/0mLM9zUp5bKWw==", "dev": true, + "license": "MIT", "dependencies": { "chromatic": "^11.15.0", "filesize": "^10.0.12", @@ -1859,6 +1977,7 @@ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -1872,6 +1991,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -1888,6 +2008,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1904,6 +2025,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1920,6 +2042,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1936,6 +2059,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1952,6 +2076,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1968,6 +2093,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1984,6 +2110,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -2000,6 +2127,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2016,6 +2144,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2032,6 +2161,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2048,6 +2178,7 @@ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2064,6 +2195,7 @@ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2080,6 +2212,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2096,6 +2229,7 @@ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2112,6 +2246,7 @@ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2128,6 +2263,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2144,6 +2280,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -2160,6 +2297,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -2176,6 +2314,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -2192,6 +2331,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -2208,6 +2348,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -2224,6 +2365,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -2240,6 +2382,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -2253,6 +2396,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -2271,6 +2415,7 @@ "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" } @@ -2280,6 +2425,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2303,6 +2449,7 @@ "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" @@ -2313,6 +2460,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -2328,6 +2476,7 @@ "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" }, @@ -2340,6 +2489,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -2352,6 +2502,7 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2362,6 +2513,7 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -2376,6 +2528,7 @@ "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" @@ -2386,6 +2539,7 @@ "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" }, @@ -2398,6 +2552,7 @@ "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" }, @@ -2411,7 +2566,8 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", @@ -2421,6 +2577,7 @@ "arm64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -2443,6 +2600,7 @@ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -2465,6 +2623,7 @@ "arm64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" @@ -2481,6 +2640,7 @@ "x64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" @@ -2497,6 +2657,7 @@ "arm" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2513,6 +2674,7 @@ "arm64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2529,6 +2691,7 @@ "s390x" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2545,6 +2708,7 @@ "x64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2561,6 +2725,7 @@ "arm64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2577,6 +2742,7 @@ "x64" ], "dev": true, + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -2593,6 +2759,7 @@ "arm" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2615,6 +2782,7 @@ "arm64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2637,6 +2805,7 @@ "s390x" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2659,6 +2828,7 @@ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2681,6 +2851,7 @@ "arm64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2703,6 +2874,7 @@ "x64" ], "dev": true, + "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -2725,6 +2897,7 @@ "wasm32" ], "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { "@emnapi/runtime": "^1.2.0" @@ -2744,6 +2917,7 @@ "ia32" ], "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" @@ -2763,6 +2937,7 @@ "x64" ], "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" @@ -2775,13 +2950,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.1.tgz", - "integrity": "sha512-6ycMm7k7NUApiMGfVc32yIPp28iPKxhGRMqoNDiUjq2RyTAkbs5Fx0TdzBqhabcKvniDdAAvHCmsRjnNfTsogw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.2.tgz", + "integrity": "sha512-KJLUHOaKnNCYzwVbryj3TNBxyZIrr56fR5N45v6K9IPrbT6B7DcudBMfylkV1A8PUdJE15mybkEQyp2/ZUpxUA==", "dev": true, + "license": "MIT", "dependencies": { - "@inquirer/core": "^10.0.1", - "@inquirer/type": "^3.0.0" + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1" }, "engines": { "node": ">=18" @@ -2791,13 +2967,14 @@ } }, "node_modules/@inquirer/core": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.0.1.tgz", - "integrity": "sha512-KKTgjViBQUi3AAssqjUFMnMO3CM3qwCHvePV9EW+zTKGKafFGFF01sc1yOIYjLJ7QU52G/FbzKc+c01WLzXmVQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.0.tgz", + "integrity": "sha512-I+ETk2AL+yAVbvuKx5AJpQmoaWhpiTFOg/UJb7ZkMAK4blmtG8ATh5ct+T/8xNld0CZG/2UhtkdMwpgvld92XQ==", "dev": true, + "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.7", - "@inquirer/type": "^3.0.0", + "@inquirer/figures": "^1.0.8", + "@inquirer/type": "^3.0.1", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -2810,31 +2987,12 @@ "node": ">=18" } }, - "node_modules/@inquirer/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@inquirer/core/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2842,34 +3000,22 @@ "node": ">=8" } }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@inquirer/figures": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", - "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.8.tgz", + "integrity": "sha512-tKd+jsmhq21AP1LhexC0pPwsCxEhGgAkg28byjJAd+xhmIs8LUX8JbUc3vBf3PhLxWiB5EvyBE5X7JSPAqMAqg==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@inquirer/type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.0.tgz", - "integrity": "sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.1.tgz", + "integrity": "sha512-+ksJMIy92sOAiAccGpcKZUc3bYO07cADnscIxHBknEm3uNts3movSmBofc1908BNy5edKscxYeAdaX1NXkHS6A==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2882,6 +3028,7 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -2894,11 +3041,61 @@ "node": ">=12" } }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2913,6 +3110,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -2922,6 +3120,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -2931,6 +3130,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -2940,13 +3140,15 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2957,6 +3159,7 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/mdx": "^2.0.0" }, @@ -2974,6 +3177,7 @@ "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.36.10.tgz", "integrity": "sha512-GXrJgakgJW3DWKueebkvtYgGKkxA7s0u5B0P5syJM5rvQUnrpLPigvci8Hukl7yEM+sU06l+er2Fgvx/gmiRgg==", "dev": true, + "license": "MIT", "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", @@ -2989,13 +3193,15 @@ "node_modules/@next/env": { "version": "14.2.16", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.16.tgz", - "integrity": "sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==" + "integrity": "sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==", + "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.2.tgz", - "integrity": "sha512-R9Jc7T6Ge0txjmqpPwqD8vx6onQjynO9JT73ArCYiYPvSrwYXepH/UY/WdKDY8JPWJl72sAE4iGMHPeQ5xdEWg==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.3.tgz", + "integrity": "sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "3.3.1" } @@ -3007,6 +3213,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3022,6 +3229,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3037,6 +3245,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3052,6 +3261,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3067,6 +3277,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3082,6 +3293,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -3097,6 +3309,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -3112,6 +3325,7 @@ "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -3127,6 +3341,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -3140,6 +3355,7 @@ "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" @@ -3153,6 +3369,7 @@ "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" } @@ -3162,6 +3379,7 @@ "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" @@ -3175,6 +3393,7 @@ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.4.0" } @@ -3183,13 +3402,15 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@open-draft/logger": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", "dev": true, + "license": "MIT", "dependencies": { "is-node-process": "^1.2.0", "outvariant": "^1.4.0" @@ -3199,7 +3420,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@parcel/watcher": { "version": "2.5.0", @@ -3207,6 +3429,7 @@ "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { "detect-libc": "^1.0.3", @@ -3245,6 +3468,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -3265,6 +3489,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3285,6 +3510,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3305,6 +3531,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -3325,6 +3552,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3345,6 +3573,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3365,6 +3594,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3385,6 +3615,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3405,6 +3636,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3425,6 +3657,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3445,6 +3678,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3465,6 +3699,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3485,6 +3720,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3502,6 +3738,7 @@ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -3512,6 +3749,7 @@ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, @@ -3524,6 +3762,7 @@ "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-html": "^0.0.9", "core-js-pure": "^3.23.3", @@ -3572,6 +3811,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -3585,19 +3825,22 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@rushstack/eslint-patch": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@storybook/addon-actions": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.2.tgz", "integrity": "sha512-+hA200XN5aeA4T3jq8IifQq6Y+9FyNQ0Q+blM1L0Tl7WLzBc7B1kHQnKvhSj5pvMSBWc/Q/kY7Ev5t9gdOu13g==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "@types/uuid": "^9.0.1", @@ -3618,6 +3861,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.2.tgz", "integrity": "sha512-s4uag5VKuk8q2MSnuNS7Sv+v1/mykzGPXe/zZRW2ammtkdHp8Uy78eQS2G0aiG02chXCX+qQgWMyy5QItDcTFQ==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3", @@ -3636,6 +3880,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.2.tgz", "integrity": "sha512-raCbHEj1xl4F3wKH6IdfEXNRaxKpY4QGhjSTE8Pte5iJSVhKG86taLqqRr+4dC7H1/LVMPU1XCGV4mkgDGtyxQ==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "dequal": "^2.0.2", @@ -3654,6 +3899,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.2.tgz", "integrity": "sha512-jIpykha7hv2Inlrq31ZoYg2QhuCuvcO+Q+uvhT45RDTB+2US/fg3rJINKlw2Djq8RPPOXvty5W0yvE6CrWKhnQ==", "dev": true, + "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/blocks": "8.4.2", @@ -3676,6 +3922,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.2.tgz", "integrity": "sha512-+/vfPrXM/GWU3Kbrg92PepwAZr7lOeulTTYF4THK0CL3DfUUlkGNpBPLP5PtjCuIkVrTCjXiIEdVWk47d5m2+w==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/addon-actions": "8.4.2", "@storybook/addon-backgrounds": "8.4.2", @@ -3701,6 +3948,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.2.tgz", "integrity": "sha512-vTtwp7nyJ09SXrsMnH+pukCjHjRMjQXgHZHxvbrv09uoH8ldQMv9B7u+X+9Wcy/jYSKFz/ng7pWo4b4a2oXHkg==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0" }, @@ -3717,6 +3965,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.2.tgz", "integrity": "sha512-+/NTENTApeOcONgFNQ6Olbk0GH3pTDG3w0eh00slCB+2agD1BcVKg8SSlHQV0lQF1cK3vWL/X3jeaxdFLYOjjg==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "@storybook/instrumenter": "8.4.2", @@ -3737,6 +3986,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.2.tgz", "integrity": "sha512-z+j6xQwcUBSpgzl1XDU+xU4YYgLraLMljECW7NvRNyJ/PYixvol8R3wtzWbr+CBpxmvbXjEJCPlF+EjF9/mBWQ==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "tiny-invariant": "^1.3.1" @@ -3754,6 +4004,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.4.2.tgz", "integrity": "sha512-zWzOyRASnIPt2AcaEl1KhI+aOaKDuoIcNB7u1GoABj0YM+V9d6o3lvcsmOAQG5pgwgFyqyOnLwpTfvRSEyzGFA==", "dev": true, + "license": "MIT", "dependencies": { "react-confetti": "^6.1.0" }, @@ -3770,6 +4021,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.2.tgz", "integrity": "sha512-oTMlPEyT4CBqzcQbfemoJzJ6yzeRAmvrAx9ssaBcnQQRsKxo0D2Ri/Jmm6SNcR0yBHxYRkvIH+2phLw8aiflCQ==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" @@ -3787,6 +4039,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.2.tgz", "integrity": "sha512-DidzW/NQS224niMJIjcJI2ls83emqygUcS9GYNGgdc5Xwro/TPgGYOXP2qnXgYUxXQTHbrxmIbHdEehxC7CcYQ==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" @@ -3800,6 +4053,7 @@ "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.2.tgz", "integrity": "sha512-qVQ2UaxCNsUSFHnAAAizNPIJ/QwfMg7p5bBdpYROTZXJe+bxVp0rFzZmQgHZ3/sn+lzE4ItM4QEfxkfQUWi1ag==", "dev": true, + "license": "MIT", "dependencies": { "memoizerific": "^1.11.3" }, @@ -3816,6 +4070,7 @@ "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.2.tgz", "integrity": "sha512-yAAvmOWaD8gIrepOxCh/RxQqd/1xZIwd/V+gsvAhW/thawN+SpI+zK63gmcqAPLX84hJ3Dh5pegRk0SoHNuDVA==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/csf": "^0.1.11", "@storybook/icons": "^1.2.12", @@ -3844,6 +4099,7 @@ "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.2.tgz", "integrity": "sha512-Pqa0/sqqEujzcvs+/Cwf/5qRLC+atmceROCFokMOgpIaorTXlbmiQdJ2dBhMFNugLvXfL7dVQBjBfiuzhsQ57g==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/core-webpack": "8.4.2", "@types/node": "^22.0.0", @@ -3889,6 +4145,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.19.8" } @@ -3898,6 +4155,7 @@ "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.2.tgz", "integrity": "sha512-+W59oF7D73LAxLNmCfFrfs98cH9pyNHK9HlJoO5/lKbK4IdWhhOoqUR/AJ3ueksoLuetFat4DxyE8SN1H4Bvrg==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" @@ -3911,6 +4169,7 @@ "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.2.tgz", "integrity": "sha512-hF8GWoUZTjwwuV5j4OLhMHZtZQL/NYcVUBReC2Ba06c8PkFIKqKZwATr1zKd301gQ5Qwcn9WgmZxJTMgdKQtOg==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/csf": "^0.1.11", "better-opn": "^3.0.2", @@ -3942,6 +4201,7 @@ "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.2.tgz", "integrity": "sha512-bzGvzrLK/oDE9YlKayDEplcECURSa1oRkvV7rxI2sOTNfwuoxHJapvxFxazEKAHMVeSwfWDf4uKK0XeG2R/arA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.0.0", "ts-dedent": "^2.0.0" @@ -3959,6 +4219,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.19.8" } @@ -3968,6 +4229,7 @@ "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.11.tgz", "integrity": "sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^2.19.0" } @@ -3977,6 +4239,7 @@ "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.2.tgz", "integrity": "sha512-1f0t6W5xbC1sSAHHs3uXYPIQs2NXAEtIGqn6X9i3xbbub6hDS8PF8BIm7dOjQ8dZOPp7d9ltR64V5CoLlsOigA==", "dev": true, + "license": "MIT", "dependencies": { "unplugin": "^1.3.1" }, @@ -3992,13 +4255,15 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@storybook/icons": { "version": "1.2.12", "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.12.tgz", "integrity": "sha512-UxgyK5W3/UV4VrI3dl6ajGfHM4aOqMAkFLWe2KibeQudLf6NJpDrDMSHwZj+3iKC4jFU7dkKbbtH2h/al4sW3Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" }, @@ -4012,6 +4277,7 @@ "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.2.tgz", "integrity": "sha512-gPYCZ/0O6gRLI3zmenu2N6QtKzxDZFdT2xf4RWcNUSZyp28RZkRCIgKFMt3fTmvE0yMzAjQyRSkBdrONjQ44HA==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "@vitest/utils": "^2.1.1" @@ -4029,6 +4295,7 @@ "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.2.tgz", "integrity": "sha512-rhPc4cgQDKDH8NUyRh/ZaJW7QIhR/PO5MNX4xc+vz71sM2nO7ONA/FrgLtCuu4SULdwilEPvGefYvLK0dE+Caw==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" @@ -4042,6 +4309,7 @@ "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.4.2.tgz", "integrity": "sha512-HySwS9zfenurk+O+SX9gKskotkHo8mFRBKAIlEROIWi7iipp5GCVPyqb8gFWjvN81dKfEIAZs+fB/7ySulJ4rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.24.4", "@babel/plugin-syntax-bigint": "^7.8.3", @@ -4113,6 +4381,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.19.8" } @@ -4122,6 +4391,7 @@ "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.2.tgz", "integrity": "sha512-Gt9hQRo1ythGFzATNV4WgQDlMDzBgiq7ks+YkW2/Xu5ZkrRrM/gK75fhmbICrknZl2pPPfNFXlECPWKAeTmwFA==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/core-webpack": "8.4.2", "@storybook/react": "8.4.2", @@ -4159,6 +4429,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.19.8" } @@ -4168,6 +4439,7 @@ "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.2.tgz", "integrity": "sha512-5X/xvIvDPaWJKUBCo5zVeBbbjkhnwcI2KPkuOgrHVRRhuQ5WqD0RYxVtOOFNyQXme7g0nNl5RFNgvT7qv9qGeg==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" @@ -4181,6 +4453,7 @@ "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.4.2.tgz", "integrity": "sha512-rO5/aVKBVhIKENcL7G8ud4QKC5OyWBPCkJIvY6XUHIuhErJy9/4pP+sZ85jypVwx5kq+EqCPF8AEOWjIxB/4/Q==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/components": "8.4.2", "@storybook/global": "^5.0.0", @@ -4217,6 +4490,7 @@ "resolved": "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz", "integrity": "sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", "endent": "^2.0.1", @@ -4236,6 +4510,7 @@ "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.2.tgz", "integrity": "sha512-FZVTM1f34FpGnf6e3MDIKkz05gmn8H9wEccvQAgr8pEFe8VWfrpVWeUrmatSAfgrCMNXYC1avDend8UX6IM8Fg==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" @@ -4251,6 +4526,7 @@ "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.2.tgz", "integrity": "sha512-MipTdboStv0hsqF2Sw8TZgP0YnxCcDYwxkTOd4hmRzev/7Brtvpi4pqjqh8k98ZCvhrCPAPVIoX5drk+oi3YUA==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/csf": "^0.1.11", "@storybook/global": "^5.0.0", @@ -4274,6 +4550,7 @@ "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.2.tgz", "integrity": "sha512-9j4fnu5LcV+qSs1rdwf61Bt14lms0T1LOZkHxGNcS1c1oH+cPS+sxECh2lxtni+mvOAHUlBs9pKhVZzRPdWpvg==", "dev": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" @@ -4285,12 +4562,14 @@ "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" }, "node_modules/@swc/helpers": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", "tslib": "^2.4.0" @@ -4300,6 +4579,7 @@ "version": "5.59.20", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.20.tgz", "integrity": "sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -4309,6 +4589,7 @@ "version": "5.59.20", "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.20.tgz", "integrity": "sha512-Zly0egsK0tFdfSbh5/mapSa+Zfc3Et0Zkar7Wo5sQkFzWyB3p3uZWOHR2wrlAEEV2L953eLuDBtbgFvMYiLvUw==", + "license": "MIT", "dependencies": { "@tanstack/query-core": "5.59.20" }, @@ -4325,6 +4606,7 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4344,6 +4626,7 @@ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", "dev": true, + "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", @@ -4364,6 +4647,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4376,13 +4660,15 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@testing-library/user-event": { "version": "14.5.2", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12", "npm": ">=6" @@ -4396,6 +4682,7 @@ "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@babel/generator": "7.17.7", "@babel/parser": "^7.20.5", @@ -4419,6 +4706,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.17.0", "jsesc": "^2.5.1", @@ -4433,6 +4721,7 @@ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/generator": "^7.23.0", @@ -4454,6 +4743,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.26.2", "@babel/types": "^7.26.0", @@ -4470,6 +4760,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -4483,6 +4774,7 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -4495,6 +4787,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" @@ -4508,6 +4801,7 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -4520,6 +4814,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -4528,13 +4823,15 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -4548,6 +4845,7 @@ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } @@ -4557,6 +4855,7 @@ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -4567,6 +4866,7 @@ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.20.7" } @@ -4575,19 +4875,22 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/doctrine": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4598,6 +4901,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -4607,37 +4911,43 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true + "dev": true, + "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/mdx": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "20.17.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.19.2" } @@ -4646,19 +4956,22 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", "devOptional": true, + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4669,6 +4982,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } @@ -4677,43 +4991,49 @@ "version": "1.20.6", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", "integrity": "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/statuses": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", - "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz", + "integrity": "sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/type-utils": "8.13.0", - "@typescript-eslint/utils": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/type-utils": "8.14.0", + "@typescript-eslint/utils": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -4737,15 +5057,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", - "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.14.0.tgz", + "integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", "debug": "^4.3.4" }, "engines": { @@ -4765,13 +5086,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", - "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz", + "integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0" + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4782,13 +5104,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", - "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz", + "integrity": "sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/typescript-estree": "8.14.0", + "@typescript-eslint/utils": "8.14.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -4806,10 +5129,11 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", - "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz", + "integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4819,13 +5143,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", - "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz", + "integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4851,6 +5176,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -4867,6 +5193,7 @@ "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" }, @@ -4875,15 +5202,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", - "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz", + "integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0" + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4897,12 +5225,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", - "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz", + "integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/types": "8.14.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -4917,13 +5246,15 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@vitest/expect": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/spy": "2.0.5", "@vitest/utils": "2.0.5", @@ -4939,6 +5270,7 @@ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", "dev": true, + "license": "MIT", "dependencies": { "tinyrainbow": "^1.2.0" }, @@ -4951,6 +5283,7 @@ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/pretty-format": "2.0.5", "estree-walker": "^3.0.3", @@ -4966,6 +5299,7 @@ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", "dev": true, + "license": "MIT", "dependencies": { "tinyrainbow": "^1.2.0" }, @@ -4978,6 +5312,7 @@ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", "dev": true, + "license": "MIT", "dependencies": { "tinyspy": "^3.0.0" }, @@ -4990,6 +5325,7 @@ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/pretty-format": "2.1.4", "loupe": "^3.1.2", @@ -5004,6 +5340,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -5013,25 +5350,29 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -5042,13 +5383,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -5061,6 +5404,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -5070,6 +5414,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } @@ -5078,13 +5423,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -5101,6 +5448,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -5114,6 +5462,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -5126,6 +5475,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -5140,6 +5490,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -5149,19 +5500,22 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "dev": true, + "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" }, @@ -5174,6 +5528,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -5186,6 +5541,7 @@ "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" } @@ -5195,6 +5551,7 @@ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, + "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "regex-parser": "^2.2.11" @@ -5208,6 +5565,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -5222,6 +5580,7 @@ "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", @@ -5238,6 +5597,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -5255,6 +5615,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5270,13 +5631,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -5286,6 +5649,7 @@ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -5301,6 +5665,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -5316,6 +5681,7 @@ "engines": [ "node >= 0.8.0" ], + "license": "Apache-2.0", "bin": { "ansi-html": "bin/ansi-html" } @@ -5328,6 +5694,7 @@ "engines": [ "node >= 0.8.0" ], + "license": "Apache-2.0", "bin": { "ansi-html": "bin/ansi-html" } @@ -5337,6 +5704,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5346,6 +5714,7 @@ "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" }, @@ -5361,6 +5730,7 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -5373,13 +5743,15 @@ "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 + "dev": true, + "license": "Python-2.0" }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, + "license": "Apache-2.0", "dependencies": { "dequal": "^2.0.3" } @@ -5389,6 +5761,7 @@ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" @@ -5405,6 +5778,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5425,6 +5799,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5445,6 +5820,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5465,6 +5841,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -5483,6 +5860,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -5501,6 +5879,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5517,6 +5896,7 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.5", @@ -5539,6 +5919,7 @@ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -5546,16 +5927,18 @@ } }, "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", @@ -5569,6 +5952,7 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" } @@ -5578,6 +5962,7 @@ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -5589,13 +5974,15 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -5611,6 +5998,7 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", "dev": true, + "license": "MPL-2.0", "engines": { "node": ">=4" } @@ -5620,6 +6008,7 @@ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -5629,6 +6018,7 @@ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", "dev": true, + "license": "MIT", "dependencies": { "find-cache-dir": "^4.0.0", "schema-utils": "^4.0.0" @@ -5646,6 +6036,7 @@ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "dev": true, + "license": "MIT", "dependencies": { "common-path-prefix": "^3.0.0", "pkg-dir": "^7.0.0" @@ -5662,6 +6053,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" @@ -5678,6 +6070,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^6.0.0" }, @@ -5693,6 +6086,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^1.0.0" }, @@ -5708,6 +6102,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^4.0.0" }, @@ -5723,6 +6118,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } @@ -5732,6 +6128,7 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^6.3.0" }, @@ -5747,6 +6144,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.20" }, @@ -5755,13 +6153,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -5773,6 +6172,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -5782,6 +6182,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2", "core-js-compat": "^3.38.0" @@ -5791,12 +6192,13 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5806,7 +6208,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -5826,13 +6229,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/better-opn": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", "dev": true, + "license": "MIT", "dependencies": { "open": "^8.0.4" }, @@ -5845,6 +6250,7 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -5854,6 +6260,7 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -5865,19 +6272,22 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "dev": true, + "license": "ISC" }, "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" } @@ -5887,6 +6297,7 @@ "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" }, @@ -5898,7 +6309,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/browser-assert": { "version": "1.2.1", @@ -5911,6 +6323,7 @@ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, + "license": "MIT", "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -5925,6 +6338,7 @@ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, + "license": "MIT", "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -5936,6 +6350,7 @@ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, + "license": "MIT", "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -5948,6 +6363,7 @@ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^5.2.1", "randombytes": "^2.1.0", @@ -5962,6 +6378,7 @@ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", "dev": true, + "license": "ISC", "dependencies": { "bn.js": "^5.2.1", "browserify-rsa": "^4.1.0", @@ -5982,13 +6399,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/browserify-sign/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6003,13 +6422,15 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/browserify-sign/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } @@ -6018,13 +6439,15 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, + "license": "MIT", "dependencies": { "pako": "~1.0.5" } @@ -6048,6 +6471,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -6080,6 +6504,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -6089,19 +6514,22 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/busboy": { "version": "1.6.0", @@ -6119,6 +6547,7 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -6138,6 +6567,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6147,15 +6577,16 @@ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dev": true, + "license": "MIT", "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001677", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", - "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "funding": [ { "type": "opencollective", @@ -6169,13 +6600,15 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -6185,6 +6618,7 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", "dev": true, + "license": "MIT", "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", @@ -6201,6 +6635,7 @@ "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" @@ -6217,6 +6652,7 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 16" } @@ -6226,6 +6662,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -6250,6 +6687,7 @@ "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" }, @@ -6258,10 +6696,11 @@ } }, "node_modules/chromatic": { - "version": "11.16.5", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.16.5.tgz", - "integrity": "sha512-wUEKXyu3GYmUg6Jq13uyRE9iC8ph5gbfDHdyHH0vQathkGQrcjHHdoxI/GXKIjU6d+xupLon8sxRV9NuZKTWbA==", + "version": "11.18.0", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.18.0.tgz", + "integrity": "sha512-3o9Frn1oIS1hFLsJxVH9yVJ1O7+TCYoyL7OZzUorL/DCYduhXr5LDSBfpUsp7EdCPb64ufkbyFzSRNbt/xy9kg==", "dev": true, + "license": "MIT", "bin": { "chroma": "dist/bin.js", "chromatic": "dist/bin.js", @@ -6285,6 +6724,7 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0" } @@ -6294,6 +6734,7 @@ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -6303,18 +6744,21 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", "dev": true, + "license": "MIT", "dependencies": { "source-map": "~0.6.0" }, @@ -6327,6 +6771,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -6336,6 +6781,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "license": "ISC", "engines": { "node": ">= 12" } @@ -6343,13 +6789,15 @@ "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -6359,31 +6807,12 @@ "node": ">=12" } }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6396,6 +6825,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6413,6 +6843,7 @@ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "color-convert": "^2.0.1", @@ -6427,6 +6858,7 @@ "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" }, @@ -6438,13 +6870,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "color-name": "^1.0.0", @@ -6455,13 +6889,15 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12" } @@ -6470,19 +6906,22 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/console-browserify": { "version": "1.2.0", @@ -6494,19 +6933,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6516,6 +6958,7 @@ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.24.2" }, @@ -6530,6 +6973,7 @@ "integrity": "sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -6539,13 +6983,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, + "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -6562,22 +7008,25 @@ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" } }, "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, + "license": "MIT", "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -6591,6 +7040,7 @@ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, + "license": "MIT", "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -6601,10 +7051,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6619,6 +7070,7 @@ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", "dev": true, + "license": "MIT", "dependencies": { "browserify-cipher": "^1.0.1", "browserify-sign": "^4.2.3", @@ -6645,6 +7097,7 @@ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -6680,6 +7133,7 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -6696,6 +7150,7 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -6707,13 +7162,15 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, + "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -6725,19 +7182,22 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -6755,6 +7215,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -6772,6 +7233,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -6789,6 +7251,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -6805,13 +7268,15 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6820,13 +7285,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6836,6 +7303,7 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -6853,6 +7321,7 @@ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6862,6 +7331,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -6879,6 +7349,7 @@ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6888,6 +7359,7 @@ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -6898,6 +7370,7 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, + "license": "Apache-2.0", "optional": true, "bin": { "detect-libc": "bin/detect-libc.js" @@ -6911,6 +7384,7 @@ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -6918,16 +7392,18 @@ } }, "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -6939,13 +7415,15 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", "dev": true, + "license": "MIT", "dependencies": { "utila": "~0.4" } @@ -6955,6 +7433,7 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, + "license": "MIT", "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -6969,6 +7448,7 @@ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.23.0.tgz", "integrity": "sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==", "dev": true, + "license": "Artistic-2.0", "engines": { "node": ">=10" }, @@ -6986,13 +7466,15 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.2.0" }, @@ -7008,6 +7490,7 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -7022,6 +7505,7 @@ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "dev": true, + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -7031,19 +7515,22 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.52", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz", - "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==", - "dev": true + "version": "1.5.56", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.56.tgz", + "integrity": "sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw==", + "dev": true, + "license": "ISC" }, "node_modules/elliptic": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -7055,22 +7542,25 @@ } }, "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -7080,6 +7570,7 @@ "resolved": "https://registry.npmjs.org/endent/-/endent-2.1.0.tgz", "integrity": "sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==", "dev": true, + "license": "MIT", "dependencies": { "dedent": "^0.7.0", "fast-json-parse": "^1.0.3", @@ -7091,6 +7582,7 @@ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -7104,6 +7596,7 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "dev": true, + "license": "BSD-2-Clause", "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -7113,6 +7606,7 @@ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7122,6 +7616,7 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -7131,6 +7626,7 @@ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", "dev": true, + "license": "MIT", "dependencies": { "stackframe": "^1.3.4" } @@ -7140,6 +7636,7 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -7200,6 +7697,7 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -7212,6 +7710,7 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -7221,6 +7720,7 @@ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7246,13 +7746,15 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -7265,6 +7767,7 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4", "has-tostringtag": "^1.0.2", @@ -7279,6 +7782,7 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.0" } @@ -7288,6 +7792,7 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -7306,6 +7811,7 @@ "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -7344,6 +7850,7 @@ "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -7356,6 +7863,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7365,6 +7873,7 @@ "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" }, @@ -7378,6 +7887,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -7433,6 +7943,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.16.tgz", "integrity": "sha512-HOcnCJsyLXR7B8wmjaCgkTSpz+ijgOyAkP8OlvANvciP8PspBYFEBTmakNMxOf71fY0aKOm/blFIiKnrM4K03Q==", "dev": true, + "license": "MIT", "dependencies": { "@next/eslint-plugin-next": "14.2.16", "@rushstack/eslint-patch": "^1.3.3", @@ -7460,6 +7971,7 @@ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.16.tgz", "integrity": "sha512-noORwKUMkKc96MWjTOwrsUCjky0oFegHbeJ1yEnQBGbMHAaTEIgLZIIfsYF0x3a06PiS+2TXppfifR+O6VWslg==", "dev": true, + "license": "MIT", "dependencies": { "glob": "10.3.10" } @@ -7469,6 +7981,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", @@ -7491,6 +8004,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, + "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7503,6 +8017,7 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -7514,6 +8029,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -7523,6 +8039,7 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz", "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==", "dev": true, + "license": "ISC", "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.3.5", @@ -7558,6 +8075,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -7574,6 +8092,7 @@ "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" }, @@ -7586,6 +8105,7 @@ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -7603,6 +8123,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -7612,6 +8133,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz", "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==", "dev": true, + "license": "MIT", "dependencies": { "lodash.camelcase": "4.3.0", "lodash.kebabcase": "4.1.1", @@ -7627,6 +8149,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, + "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -7660,6 +8183,7 @@ "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" @@ -7670,6 +8194,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -7679,6 +8204,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -7691,6 +8217,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.0" }, @@ -7703,6 +8230,7 @@ "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" }, @@ -7715,6 +8243,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -7724,6 +8253,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -7736,6 +8266,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, + "license": "MIT", "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -7765,6 +8296,7 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -7774,6 +8306,7 @@ "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" @@ -7784,6 +8317,7 @@ "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" }, @@ -7796,6 +8330,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, + "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.9.1" @@ -7826,6 +8361,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -7858,6 +8394,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7870,6 +8407,7 @@ "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" @@ -7880,6 +8418,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -7892,6 +8431,7 @@ "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" }, @@ -7904,6 +8444,7 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -7921,6 +8462,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -7930,6 +8472,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.10.2.tgz", "integrity": "sha512-iOrFJfyLgRLIbWDLbbs3J4yrknvIB+uiZ8KGqGOEXTL7/BGuBMZLiIU9Q4Pm/VYJrHN4JqmMtwCSrAemHL2nFg==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/csf": "^0.1.11", "@typescript-eslint/utils": "^8.8.1", @@ -7947,6 +8490,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -7963,6 +8507,7 @@ "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" }, @@ -7975,6 +8520,7 @@ "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" @@ -7985,6 +8531,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -8000,6 +8547,7 @@ "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" }, @@ -8012,6 +8560,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8024,6 +8573,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -8036,6 +8586,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -8053,6 +8604,7 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -8066,6 +8618,7 @@ "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" }, @@ -8078,6 +8631,7 @@ "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" }, @@ -8090,6 +8644,7 @@ "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" } @@ -8099,6 +8654,7 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } @@ -8108,6 +8664,7 @@ "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" } @@ -8117,6 +8674,7 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -8126,6 +8684,7 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } @@ -8135,6 +8694,7 @@ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, + "license": "MIT", "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -8144,19 +8704,22 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/fast-diff": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -8173,6 +8736,7 @@ "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" }, @@ -8184,31 +8748,36 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", - "dev": true + "dev": true, + "license": "MIT" }, "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 + "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 + "dev": true, + "license": "MIT" }, "node_modules/fast-uri": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -8218,6 +8787,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -8230,6 +8800,7 @@ "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", "integrity": "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">= 10.4.0" } @@ -8239,6 +8810,7 @@ "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" }, @@ -8251,6 +8823,7 @@ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.2.tgz", "integrity": "sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8260,6 +8833,7 @@ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, + "license": "MIT", "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -8277,6 +8851,7 @@ "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" @@ -8293,6 +8868,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -8306,13 +8882,15 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } @@ -8322,6 +8900,7 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -8338,6 +8917,7 @@ "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", "chalk": "^4.1.2", @@ -8366,6 +8946,7 @@ "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" @@ -8376,6 +8957,7 @@ "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" }, @@ -8388,6 +8970,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -8406,6 +8989,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -8419,13 +9003,15 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "dev": true + "dev": true, + "license": "Unlicense" }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -8433,6 +9019,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -8446,6 +9033,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8455,6 +9043,7 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -8473,6 +9062,7 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8482,6 +9072,7 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -8491,6 +9082,7 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -8500,6 +9092,7 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -8519,6 +9112,7 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "es-errors": "^1.3.0", @@ -8536,6 +9130,7 @@ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, + "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -8549,6 +9144,7 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8569,6 +9165,7 @@ "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" }, @@ -8580,13 +9177,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/glob/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" @@ -8597,6 +9196,7 @@ "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" }, @@ -8609,6 +9209,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -8618,6 +9219,7 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -8634,6 +9236,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -8644,19 +9247,22 @@ "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==" + "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 + "dev": true, + "license": "MIT" }, "node_modules/graphql": { "version": "16.9.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -8666,6 +9272,7 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8675,6 +9282,7 @@ "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" } @@ -8684,6 +9292,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -8696,6 +9305,7 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8708,6 +9318,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8720,6 +9331,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -8735,6 +9347,7 @@ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -8748,6 +9361,7 @@ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -8758,6 +9372,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -8770,6 +9385,7 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } @@ -8778,18 +9394,21 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/highcharts": { "version": "11.4.8", "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-11.4.8.tgz", - "integrity": "sha512-5Tke9LuzZszC4osaFisxLIcw7xgNGz4Sy3Jc9pRMV+ydm6sYqsPYdU8ELOgpzGNrbrRNDRBtveoR5xS3SzneEA==" + "integrity": "sha512-5Tke9LuzZszC4osaFisxLIcw7xgNGz4Sy3Jc9pRMV+ydm6sYqsPYdU8ELOgpzGNrbrRNDRBtveoR5xS3SzneEA==", + "license": "https://www.highcharts.com/license" }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dev": true, + "license": "MIT", "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -8810,13 +9429,15 @@ "type": "patreon", "url": "https://patreon.com/mdevils" } - ] + ], + "license": "MIT" }, "node_modules/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dev": true, + "license": "MIT", "dependencies": { "camel-case": "^4.1.2", "clean-css": "^5.2.2", @@ -8838,6 +9459,7 @@ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", "dev": true, + "license": "MIT", "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", @@ -8877,6 +9499,7 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", @@ -8888,13 +9511,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -8920,13 +9545,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "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" } @@ -8936,6 +9563,7 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", "dev": true, + "license": "MIT", "dependencies": { "queue": "6.0.2" }, @@ -8950,13 +9578,15 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -8973,6 +9603,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -8982,6 +9613,7 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8992,6 +9624,7 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -9001,13 +9634,15 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.0", @@ -9022,6 +9657,7 @@ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -9038,6 +9674,7 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1" @@ -9053,13 +9690,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -9075,6 +9714,7 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" }, @@ -9087,6 +9727,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -9099,6 +9740,7 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -9115,6 +9757,7 @@ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz", "integrity": "sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.6.3" } @@ -9124,6 +9767,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9136,6 +9780,7 @@ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, + "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -9151,6 +9796,7 @@ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, + "license": "MIT", "dependencies": { "is-typed-array": "^1.1.13" }, @@ -9166,6 +9812,7 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -9181,6 +9828,7 @@ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, + "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -9196,6 +9844,7 @@ "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" } @@ -9205,6 +9854,7 @@ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -9217,6 +9867,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -9226,6 +9877,7 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -9241,6 +9893,7 @@ "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" }, @@ -9253,6 +9906,7 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9265,6 +9919,7 @@ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" @@ -9281,6 +9936,7 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9292,13 +9948,15 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", - "dev": true + "dev": true, + "license": "MIT" }, "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" } @@ -9308,6 +9966,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -9323,6 +9982,7 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -9332,6 +9992,7 @@ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -9348,6 +10009,7 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9360,6 +10022,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7" }, @@ -9375,6 +10038,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -9390,6 +10054,7 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" }, @@ -9405,6 +10070,7 @@ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" }, @@ -9420,6 +10086,7 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9432,6 +10099,7 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -9444,6 +10112,7 @@ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4" @@ -9460,6 +10129,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "license": "MIT", "dependencies": { "is-docker": "^2.0.0" }, @@ -9471,19 +10141,22 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/iterator.prototype": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "get-intrinsic": "^1.2.1", @@ -9500,6 +10173,7 @@ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -9517,13 +10191,15 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -9538,6 +10214,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9553,6 +10230,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, + "license": "MIT", "bin": { "jiti": "bin/jiti.js" } @@ -9560,13 +10238,15 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -9579,6 +10259,7 @@ "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0.0" } @@ -9588,6 +10269,7 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -9599,31 +10281,36 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "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 + "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 + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -9636,6 +10323,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -9648,6 +10336,7 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -9663,6 +10352,7 @@ "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" } @@ -9671,13 +10361,15 @@ "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/language-tags": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, + "license": "MIT", "dependencies": { "language-subtag-registry": "^0.3.20" }, @@ -9691,6 +10383,7 @@ "integrity": "sha512-lMXbcFHNDr+gzy/7ghuJDVB/Yyycj+ZL/7pN3Gm/s5Xqrc9+5sj3IrDAPylcEJ1cKCbUnXbwESrhhqpcYv4d4g==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "lefthook": "bin/index.js" }, @@ -9715,6 +10408,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -9728,6 +10422,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -9741,6 +10436,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -9754,6 +10450,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -9767,6 +10464,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -9780,6 +10478,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -9793,6 +10492,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -9806,6 +10506,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -9819,6 +10520,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -9832,6 +10534,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -9842,6 +10545,7 @@ "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" @@ -9854,13 +10558,15 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.11.5" } @@ -9870,6 +10576,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12.13.0" } @@ -9879,6 +10586,7 @@ "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" }, @@ -9893,48 +10601,56 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true + "dev": true, + "license": "MIT" }, "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 + "dev": true, + "license": "MIT" }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.upperfirst": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -9946,13 +10662,15 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.0.3" } @@ -9962,6 +10680,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -9971,6 +10690,7 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, + "license": "MIT", "bin": { "lz-string": "bin/bin.js" } @@ -9980,6 +10700,7 @@ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } @@ -9989,6 +10710,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^6.0.0" }, @@ -10004,6 +10726,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -10012,13 +10735,15 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, + "license": "MIT", "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -10030,6 +10755,7 @@ "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, + "license": "Unlicense", "dependencies": { "fs-monkey": "^1.0.4" }, @@ -10042,6 +10768,7 @@ "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", "dev": true, + "license": "MIT", "dependencies": { "map-or-similar": "^1.5.0" } @@ -10050,13 +10777,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "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" } @@ -10066,6 +10795,7 @@ "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" @@ -10079,6 +10809,7 @@ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -10088,16 +10819,18 @@ } }, "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" }, "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==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10107,6 +10840,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -10119,6 +10853,7 @@ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -10127,19 +10862,22 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true + "dev": true, + "license": "MIT" }, "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" }, @@ -10155,6 +10893,7 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10164,6 +10903,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -10172,14 +10912,16 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/msw": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.2.tgz", - "integrity": "sha512-RdRgPvjfuzMIACkWv7VOVAeSRYMU3ofokLv1w0RsbFX960qnj/tFEyOFXY0G2GTUd9trA6rHuHciM/FKpBp6/A==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.4.tgz", + "integrity": "sha512-Pm4LmWQeytDsNCR+A7gt39XAdtH6zQb6jnIKRig0FlvYOn8eksn3s1nXxUfz5KYUjbckof7Z4p2ewzgffPoCbg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@bundled-es-modules/cookie": "^2.0.1", "@bundled-es-modules/statuses": "^1.0.1", @@ -10223,6 +10965,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" }, @@ -10235,6 +10978,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", "dev": true, + "license": "ISC", "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -10249,6 +10993,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -10260,18 +11005,21 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/next": { "version": "14.2.16", "resolved": "https://registry.npmjs.org/next/-/next-14.2.16.tgz", "integrity": "sha512-LcO7WnFu6lYSvCzZoo1dB+IO0xXz5uEv52HF1IUN0IqVTUIZGHuuR10I5efiLadGt+4oZqTcNZyVVEem/TM5nA==", + "license": "MIT", "dependencies": { "@next/env": "14.2.16", "@swc/helpers": "0.5.5", @@ -10335,6 +11083,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -10348,6 +11097,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", "dependencies": { "client-only": "0.0.1" }, @@ -10371,6 +11121,7 @@ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, + "license": "MIT", "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -10380,13 +11131,15 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/node-polyfill-webpack-plugin": { @@ -10394,6 +11147,7 @@ "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-2.0.1.tgz", "integrity": "sha512-ZUMiCnZkP1LF0Th2caY6J/eKKoA0TefpoVa68m/LQU1I/mE8rGt4fNYGgNuCcK+aG8P8P43nbeJ2RqJMOL/Y1A==", "dev": true, + "license": "MIT", "dependencies": { "assert": "^2.0.0", "browserify-zlib": "^0.2.0", @@ -10432,13 +11186,15 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10448,6 +11204,7 @@ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -10460,15 +11217,17 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10481,6 +11240,7 @@ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" @@ -10497,6 +11257,7 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -10506,6 +11267,7 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "define-properties": "^1.2.1", @@ -10524,6 +11286,7 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10538,6 +11301,7 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10556,6 +11320,7 @@ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10570,6 +11335,7 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10586,13 +11352,15 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.5.tgz", "integrity": "sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -10602,6 +11370,7 @@ "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dev": true, + "license": "MIT", "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -10619,6 +11388,7 @@ "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", @@ -10635,19 +11405,22 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/outvariant": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", - "dev": true + "dev": true, + "license": "MIT" }, "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" }, @@ -10663,6 +11436,7 @@ "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" }, @@ -10678,6 +11452,7 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -10686,13 +11461,15 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "dev": true, + "license": "(MIT AND Zlib)" }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -10703,6 +11480,7 @@ "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" }, @@ -10715,6 +11493,7 @@ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", "dev": true, + "license": "ISC", "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", @@ -10732,6 +11511,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -10750,6 +11530,7 @@ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", "dev": true, + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -10759,13 +11540,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "dev": true, + "license": "MIT" }, "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" } @@ -10775,6 +11558,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10784,6 +11568,7 @@ "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" } @@ -10792,13 +11577,15 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -10814,19 +11601,22 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -10836,6 +11626,7 @@ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14.16" } @@ -10845,6 +11636,7 @@ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, + "license": "MIT", "dependencies": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -10859,13 +11651,15 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "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" }, @@ -10878,6 +11672,7 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -10890,6 +11685,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -10903,6 +11699,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -10915,6 +11712,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -10930,6 +11728,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -10942,6 +11741,7 @@ "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.7.0.tgz", "integrity": "sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==", "dev": true, + "license": "MIT", "dependencies": { "ts-pnp": "^1.1.6" }, @@ -10954,6 +11754,7 @@ "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.17.8" }, @@ -10966,14 +11767,15 @@ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -10989,9 +11791,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -11003,6 +11806,7 @@ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "dev": true, + "license": "MIT", "dependencies": { "cosmiconfig": "^9.0.0", "jiti": "^1.20.0", @@ -11034,6 +11838,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, + "license": "MIT", "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -11060,6 +11865,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -11068,13 +11874,14 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz", + "integrity": "sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "engines": { @@ -11085,12 +11892,13 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, + "license": "ISC", "dependencies": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": "^10 || ^12 || >= 14" @@ -11104,6 +11912,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, + "license": "ISC", "dependencies": { "icss-utils": "^5.0.0" }, @@ -11115,10 +11924,11 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -11131,13 +11941,15 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "dev": true, + "license": "MIT" }, "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" } @@ -11147,6 +11959,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -11162,6 +11975,7 @@ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, + "license": "MIT", "dependencies": { "fast-diff": "^1.1.2" }, @@ -11174,6 +11988,7 @@ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.20", "renderkid": "^3.0.0" @@ -11184,6 +11999,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -11198,6 +12014,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -11210,6 +12027,7 @@ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6.0" } @@ -11218,13 +12036,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11235,13 +12055,15 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/psl": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", "dev": true, + "license": "MIT", "dependencies": { "punycode": "^2.3.1" } @@ -11251,6 +12073,7 @@ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -11261,16 +12084,18 @@ } }, "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "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" } @@ -11280,6 +12105,7 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -11303,13 +12129,15 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/queue": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "~2.0.3" } @@ -11332,13 +12160,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } @@ -11348,6 +12178,7 @@ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, + "license": "MIT", "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -11358,6 +12189,7 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -11366,6 +12198,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -11378,6 +12211,7 @@ "resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.1.0.tgz", "integrity": "sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==", "dev": true, + "license": "MIT", "dependencies": { "tween-functions": "^1.2.0" }, @@ -11393,6 +12227,7 @@ "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.0.tgz", "integrity": "sha512-APPU8HB2uZnpl6Vt/+0AFoVYgSRtfiP6FLrZgPPTDmqSb2R4qZRbgd0A3VzIFxDt5e+Fozjx79WjLWnF69DK8g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.18.9", "@babel/traverse": "^7.18.9", @@ -11414,6 +12249,7 @@ "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", "dev": true, + "license": "MIT", "peerDependencies": { "typescript": ">= 4.3.x" } @@ -11422,6 +12258,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -11434,13 +12271,15 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11450,6 +12289,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "dev": true, + "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -11466,6 +12306,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -11478,6 +12319,7 @@ "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.9.tgz", "integrity": "sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==", "dev": true, + "license": "MIT", "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", @@ -11494,6 +12336,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -11503,6 +12346,7 @@ "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, + "license": "MIT", "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" @@ -11516,6 +12360,7 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, + "license": "MIT", "dependencies": { "min-indent": "^1.0.0" }, @@ -11528,6 +12373,7 @@ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -11548,13 +12394,15 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -11566,13 +12414,15 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.4" } @@ -11581,13 +12431,15 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -11606,6 +12458,7 @@ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", "dev": true, + "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", @@ -11622,13 +12475,15 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regjsparser": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.0.2" }, @@ -11641,6 +12496,7 @@ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -11650,6 +12506,7 @@ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "dev": true, + "license": "MIT", "dependencies": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", @@ -11663,6 +12520,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -11675,6 +12533,7 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11684,6 +12543,7 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11692,13 +12552,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -11716,6 +12578,7 @@ "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" } @@ -11725,6 +12588,7 @@ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -11734,6 +12598,7 @@ "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, + "license": "MIT", "dependencies": { "adjust-sourcemap-loader": "^4.0.0", "convert-source-map": "^1.7.0", @@ -11749,13 +12614,15 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/resolve-url-loader/node_modules/loader-utils": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -11770,6 +12637,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -11779,6 +12647,7 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -11790,6 +12659,7 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -11805,6 +12675,7 @@ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, + "license": "MIT", "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -11829,6 +12700,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -11838,6 +12710,7 @@ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4", @@ -11869,13 +12742,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -11893,6 +12768,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz", "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==", "devOptional": true, + "license": "MIT", "dependencies": { "chokidar": "^4.0.0", "immutable": "^4.0.0", @@ -11913,6 +12789,7 @@ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.3.3.tgz", "integrity": "sha512-mt5YN2F1MOZr3d/wBRcZxeFgwgkH44wVc2zohO2YF6JiOMkiXe4BYRZpSu2sO1g71mo/j16txzUhsKZlqjVGzA==", "dev": true, + "license": "MIT", "dependencies": { "neo-async": "^2.6.2" }, @@ -11950,6 +12827,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "devOptional": true, + "license": "MIT", "dependencies": { "readdirp": "^4.0.1" }, @@ -11965,6 +12843,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">= 14.16.0" }, @@ -11977,6 +12856,7 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -11986,6 +12866,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -12005,6 +12886,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12021,6 +12903,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -12032,13 +12915,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -12051,6 +12936,7 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } @@ -12060,6 +12946,7 @@ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -12077,6 +12964,7 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -12091,13 +12979,15 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, + "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -12112,6 +13002,7 @@ "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", "dev": true, "hasInstallScript": true, + "license": "Apache-2.0", "optional": true, "dependencies": { "color": "^4.2.3", @@ -12151,6 +13042,7 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, + "license": "Apache-2.0", "optional": true, "engines": { "node": ">=8" @@ -12161,6 +13053,7 @@ "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" }, @@ -12173,6 +13066,7 @@ "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" } @@ -12182,6 +13076,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -12200,6 +13095,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -12212,6 +13108,7 @@ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "is-arrayish": "^0.3.1" @@ -12222,6 +13119,7 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/source-map": { @@ -12229,6 +13127,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">= 8" } @@ -12237,6 +13136,7 @@ "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" } @@ -12246,6 +13146,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -12256,6 +13157,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -12264,13 +13166,15 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -12280,6 +13184,7 @@ "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.2.tgz", "integrity": "sha512-GMCgyAulmLNrkUtDkCpFO4SB77YrpiIxq6e5tzaQdXEuaDu1mdNwOuP3VG7nE2FzxmqDvagSgriM68YW9iFaZA==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/core": "8.4.2" }, @@ -12306,6 +13211,7 @@ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -12316,6 +13222,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -12330,6 +13237,7 @@ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", "dev": true, + "license": "MIT", "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", @@ -12342,6 +13250,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -12363,32 +13272,32 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/string-width-cjs": { @@ -12397,6 +13306,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12410,13 +13320,35 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -12429,6 +13361,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -12443,6 +13376,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -12469,6 +13403,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -12479,6 +13414,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -12497,6 +13433,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -12511,6 +13448,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -12528,6 +13466,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -12544,6 +13483,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -12556,6 +13496,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -12568,6 +13509,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -12577,6 +13519,7 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", "dev": true, + "license": "MIT", "dependencies": { "min-indent": "^1.0.1" }, @@ -12592,6 +13535,7 @@ "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" }, @@ -12604,6 +13548,7 @@ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12.13.0" }, @@ -12620,6 +13565,7 @@ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "dev": true, + "license": "MIT", "dependencies": { "client-only": "0.0.1" }, @@ -12643,6 +13589,7 @@ "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" }, @@ -12655,6 +13602,7 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -12667,6 +13615,7 @@ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, + "license": "MIT", "dependencies": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" @@ -12683,6 +13632,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -12692,6 +13642,7 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -12710,6 +13661,7 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", @@ -12744,6 +13696,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -12761,19 +13714,22 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", "dev": true, + "license": "MIT", "dependencies": { "setimmediate": "^1.0.4" }, @@ -12785,13 +13741,15 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/tinyrainbow": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -12801,6 +13759,7 @@ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -12810,6 +13769,7 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -12819,6 +13779,7 @@ "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" }, @@ -12831,6 +13792,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -12846,6 +13808,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -12855,6 +13818,7 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -12867,6 +13831,7 @@ "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.10" } @@ -12876,6 +13841,7 @@ "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -12890,6 +13856,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, + "license": "MIT", "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", @@ -12904,6 +13871,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", @@ -12916,25 +13884,29 @@ "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/tween-functions": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz", "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==", - "dev": true + "dev": true, + "license": "BSD" }, "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" }, @@ -12947,6 +13919,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" }, @@ -12959,6 +13932,7 @@ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -12973,6 +13947,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -12992,6 +13967,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -13012,6 +13988,7 @@ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -13032,6 +14009,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13045,6 +14023,7 @@ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -13059,13 +14038,15 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -13075,6 +14056,7 @@ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -13088,6 +14070,7 @@ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -13097,6 +14080,7 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -13106,6 +14090,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -13115,6 +14100,7 @@ "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.15.0.tgz", "integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==", "dev": true, + "license": "MIT", "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" @@ -13150,6 +14136,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" @@ -13166,6 +14153,7 @@ "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" } @@ -13175,6 +14163,7 @@ "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", "dev": true, + "license": "MIT", "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" @@ -13188,6 +14177,7 @@ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, + "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -13197,13 +14187,15 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -13216,13 +14208,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/uuid": { "version": "9.0.1", @@ -13233,6 +14227,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -13241,13 +14236,15 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -13261,6 +14258,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -13307,6 +14305,7 @@ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.3.tgz", "integrity": "sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==", "dev": true, + "license": "MIT", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.12", @@ -13335,6 +14334,7 @@ "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz", "integrity": "sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-html-community": "0.0.8", "html-entities": "^2.1.0", @@ -13346,6 +14346,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -13358,6 +14359,7 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -13366,13 +14368,15 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -13386,6 +14390,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -13395,6 +14400,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -13413,6 +14419,7 @@ "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" }, @@ -13428,6 +14435,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -13444,6 +14452,7 @@ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", "dev": true, + "license": "MIT", "dependencies": { "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", @@ -13470,6 +14479,7 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -13488,6 +14498,7 @@ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -13507,25 +14518,24 @@ "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/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/wrap-ansi-cjs": { @@ -13534,6 +14544,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -13546,31 +14557,25 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -13578,29 +14583,19 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -13622,6 +14617,7 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4" } @@ -13631,6 +14627,7 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -13639,13 +14636,15 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true, + "license": "ISC", "engines": { "node": ">= 6" } @@ -13655,6 +14654,7 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -13673,47 +14673,17 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "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" }, @@ -13726,6 +14696,7 @@ "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -13737,6 +14708,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz", "integrity": "sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w==", + "license": "MIT", "engines": { "node": ">=12.20.0" }, diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index 1e368468..742f3083 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -8,8 +8,8 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.6.2' -const INTEGRITY_CHECKSUM = '07a8241b182f8a246a7cd39894799a9e' +const PACKAGE_VERSION = '2.6.4' +const INTEGRITY_CHECKSUM = 'ca7800994cc8bfb5eb961e037c877074' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() @@ -192,12 +192,14 @@ async function getResponse(event, client, requestId) { const requestClone = request.clone() function passthrough() { - const headers = Object.fromEntries(requestClone.headers.entries()) - - // Remove internal MSW request header so the passthrough request - // complies with any potential CORS preflight checks on the server. - // Some servers forbid unknown request headers. - delete headers['x-msw-intention'] + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + headers.delete('accept', 'msw/passthrough') return fetch(requestClone, { headers }) } diff --git a/shared/providers/index.ts b/shared/providers/index.ts index ac2203f8..25979743 100644 --- a/shared/providers/index.ts +++ b/shared/providers/index.ts @@ -1 +1,3 @@ export { QueryProvider } from './react-query/query-provider' + +export { MSWInitializer } from './msw-Initializer' diff --git a/shared/providers/msw-Initializer.tsx b/shared/providers/msw-Initializer.tsx new file mode 100644 index 00000000..6adbbd69 --- /dev/null +++ b/shared/providers/msw-Initializer.tsx @@ -0,0 +1,31 @@ +'use client' + +import { useEffect } from 'react' + +import { useMSWStore } from '@/shared/stores/msw' +import { isDevelopment } from '@/shared/utils/env' + +export const MSWInitializer = () => { + const setReady = useMSWStore((state) => state.setReady) + + useEffect(() => { + const initMSW = async () => { + if (!isDevelopment) { + setReady(true) + return + } + + try { + const { default: init } = await import('@/mocks') + await init() + setReady(true) + } catch (error) { + console.error('MSW initialization failed:', error) + } + } + + initMSW() + }, [setReady]) + + return null +} diff --git a/shared/stores/msw.ts b/shared/stores/msw.ts new file mode 100644 index 00000000..34904acd --- /dev/null +++ b/shared/stores/msw.ts @@ -0,0 +1,13 @@ +import { create } from 'zustand' + +import { isDevelopment } from '@/shared/utils/env' + +interface Props { + isReady: boolean + setReady: (ready: boolean) => void +} + +export const useMSWStore = create<Props>((set) => ({ + isReady: !isDevelopment, + setReady: (ready) => set({ isReady: ready }), +})) diff --git a/shared/utils/env.ts b/shared/utils/env.ts new file mode 100644 index 00000000..c00e3400 --- /dev/null +++ b/shared/utils/env.ts @@ -0,0 +1,3 @@ +import { ENV } from '@/shared/constants/env' + +export const isDevelopment = process.env.NODE_ENV === ENV.DEVELOPMENT From 95c79c656750572533325c867127cba20bf6a803 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 12 Nov 2024 22:31:24 +0900 Subject: [PATCH 038/648] =?UTF-8?q?feat:=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=83=81=EC=88=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 shared/constants/path.ts diff --git a/shared/constants/path.ts b/shared/constants/path.ts new file mode 100644 index 00000000..43957502 --- /dev/null +++ b/shared/constants/path.ts @@ -0,0 +1,24 @@ +export const PATH = { + // Auth + SIGN_IN: '/signin', + SIGN_UP: '/signup', + + // Main + HOME: '/', + STRATEGIES: '/strategies', + TRADERS: '/traders', + + // My + FAVORITES: '/my/favorites', + MY_STRATEGIES: '/my/strategies', + STRATEGIES_MANAGE: '/my/strategies/manage', + MY_QUESTIONS: 'my/questions', + + // Admin + ADMIN: '/admin', + ADMIN_CATEGORY: '/admin/category', + ADMIN_USERS: '/admin/users', + ADMIN_NOTICES: '/admin/notices', + ADMIN_STRATEGIES: '/admin/strategies', + ADMIN_QUESTIONS: '/admin/questions', +} as const From 578ed909df1a2b3a04341f4e06fcfd8a373293f3 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 12 Nov 2024 22:33:12 +0900 Subject: [PATCH 039/648] =?UTF-8?q?chore:=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A1=9C=EA=B3=A0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/category.svg | 6 ++++++ public/icons/change.svg | 4 ++++ public/icons/favorite.svg | 3 +++ public/icons/logo.svg | 5 +++++ public/icons/notice.svg | 3 +++ public/icons/profile.svg | 4 ++++ public/icons/question.svg | 3 +++ public/icons/signout.svg | 3 +++ public/icons/strategy-ranking.svg | 3 +++ public/icons/strategy.svg | 5 +++++ public/icons/text-logo.svg | 13 +++++++++++++ public/icons/traders.svg | 6 ++++++ public/images/logo.svg | 5 +++++ public/images/text-logo.svg | 13 +++++++++++++ 14 files changed, 76 insertions(+) create mode 100644 public/icons/category.svg create mode 100644 public/icons/change.svg create mode 100644 public/icons/favorite.svg create mode 100644 public/icons/logo.svg create mode 100644 public/icons/notice.svg create mode 100644 public/icons/profile.svg create mode 100644 public/icons/question.svg create mode 100644 public/icons/signout.svg create mode 100644 public/icons/strategy-ranking.svg create mode 100644 public/icons/strategy.svg create mode 100644 public/icons/text-logo.svg create mode 100644 public/icons/traders.svg create mode 100644 public/images/logo.svg create mode 100644 public/images/text-logo.svg diff --git a/public/icons/category.svg b/public/icons/category.svg new file mode 100644 index 00000000..a3bf1f01 --- /dev/null +++ b/public/icons/category.svg @@ -0,0 +1,6 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="3" y="3" width="8.1" height="8.1" rx="1" fill="#797979"/> +<rect x="3" y="12.8999" width="8.1" height="8.1" rx="1" fill="#797979"/> +<rect x="12.9004" y="3" width="8.1" height="8.1" rx="1" fill="#797979"/> +<rect x="12.9004" y="12.8999" width="8.1" height="8.1" rx="1" fill="#797979"/> +</svg> diff --git a/public/icons/change.svg b/public/icons/change.svg new file mode 100644 index 00000000..6e6ac5a7 --- /dev/null +++ b/public/icons/change.svg @@ -0,0 +1,4 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M17.7071 5L22.2071 9.5C22.4931 9.786 22.5787 10.2161 22.4239 10.5898C22.2691 10.9635 21.9045 11.2071 21.5 11.2071H6V9.20711H19.0858L16.2929 6.41421L17.7071 5Z" fill="#797979"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M6.79289 19.4141L2.29289 14.9141C2.0069 14.6281 1.92134 14.1979 2.07612 13.8243C2.2309 13.4506 2.59554 13.207 3 13.207H18.5V15.207H5.41421L8.20711 17.9998L6.79289 19.4141Z" fill="#797979"/> +</svg> diff --git a/public/icons/favorite.svg b/public/icons/favorite.svg new file mode 100644 index 00000000..8e891487 --- /dev/null +++ b/public/icons/favorite.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4C6.44772 4 6 4.44772 6 5V19.5603L12 16.5L18 19.4994V5C18 4.44772 17.5523 4 17 4H7Z" fill="#797979"/> +</svg> diff --git a/public/icons/logo.svg b/public/icons/logo.svg new file mode 100644 index 00000000..c5ff8c35 --- /dev/null +++ b/public/icons/logo.svg @@ -0,0 +1,5 @@ +<svg width="40" height="24" viewBox="0 0 40 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M11.75 1H20.421L27.1169 15.5503V23.5994H18.2051L11.75 9.31447V1Z" fill="#FF7752"/> +<path d="M23.9297 1H32.657L39.3964 15.5503V23.5994H30.4267L23.9297 9.31447V1Z" fill="#FF7752"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M7.49668 1H1V9.23233L1.05415 9.41406H5.51337V23.5988H5.28057L5.28075 23.5994H14.1926V15.5503L7.49668 1Z" fill="#FF7752"/> +</svg> diff --git a/public/icons/notice.svg b/public/icons/notice.svg new file mode 100644 index 00000000..ce40bfd2 --- /dev/null +++ b/public/icons/notice.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M21 12C21 16.9688 16.9688 21 12 21C7.03125 21 3 16.9688 3 12C3 7.03125 7.03125 3 12 3C16.9688 3 21 7.03125 21 12ZM10.5 7L11 13H13L13.5 7H10.5ZM11 17V15H13V17H11Z" fill="#797979"/> +</svg> diff --git a/public/icons/profile.svg b/public/icons/profile.svg new file mode 100644 index 00000000..bd5258fd --- /dev/null +++ b/public/icons/profile.svg @@ -0,0 +1,4 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="12" cy="9" r="4" fill="#797979"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M19 20H5V18C5 15.7909 6.79086 14 9 14H15C17.2091 14 19 15.7909 19 18V20Z" fill="#797979"/> +</svg> diff --git a/public/icons/question.svg b/public/icons/question.svg new file mode 100644 index 00000000..77d39635 --- /dev/null +++ b/public/icons/question.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3 5C3 4.44772 3.44772 4 4 4H20C20.5523 4 21 4.44772 21 5V18C21 18.5523 20.5523 19 20 19H5.54545L3 21V5ZM17 8H7V9H17V8ZM7 11H17V12H7V11ZM13 14H7V15H13V14Z" fill="#797979"/> +</svg> diff --git a/public/icons/signout.svg b/public/icons/signout.svg new file mode 100644 index 00000000..412e14fe --- /dev/null +++ b/public/icons/signout.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3 5C3 4.44772 3.44772 4 4 4H13V6H5V19H13V21H4C3.44772 21 3 20.5523 3 20V5ZM17.0936 11L15.8007 9.70711L17.2149 8.29289L20.922 12L17.2149 15.7071L15.8007 14.2929L17.0936 13H9V11H17.0936Z" fill="#797979"/> +</svg> diff --git a/public/icons/strategy-ranking.svg b/public/icons/strategy-ranking.svg new file mode 100644 index 00000000..35fac62a --- /dev/null +++ b/public/icons/strategy-ranking.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M5 4C3.89543 4 3 4.89543 3 6V10.3H8.07692C8.36343 10.3 8.58296 10.4553 8.68754 10.5377C8.82455 10.6456 8.96228 10.7909 9.1014 10.9688C9.38084 11.3261 9.71474 11.8782 10.0971 12.7067L10.1315 12.7813L10.1401 12.7998L10.1444 12.809L10.1486 12.8183L10.1571 12.8367L10.1656 12.8551L10.2327 13.0004L10.2656 13.0718L10.2697 13.0807L10.2738 13.0895L10.2779 13.0984L10.282 13.1072L10.2901 13.1248L10.2942 13.1336L10.2982 13.1424L10.3623 13.2813L10.3663 13.2898L10.3702 13.2984L10.3742 13.3069L10.3781 13.3155L10.382 13.324L10.386 13.3325L10.3899 13.341L10.3938 13.3495L10.3977 13.3579L10.4016 13.3664L10.4055 13.3748L10.4094 13.3833L10.4133 13.3917L10.4171 13.4001L10.421 13.4085L10.4249 13.4168L10.4287 13.4252L10.4326 13.4335L10.4364 13.4419L10.4403 13.4502L10.4556 13.4834L10.4594 13.4916L10.4632 13.4999L10.467 13.5081L10.4708 13.5163L10.4859 13.5491L10.5009 13.5816L10.5159 13.614L10.5233 13.6301L10.527 13.6381L10.5307 13.6461L10.5454 13.678L10.5601 13.7097L10.5746 13.7412L10.6034 13.8035L10.6105 13.819L10.6176 13.8344L10.6211 13.8421L10.6247 13.8497L10.6317 13.865L10.6388 13.8803L10.6458 13.8954L10.6493 13.903L10.6528 13.9106L10.6597 13.9256L10.7145 14.0443L10.8192 14.2711L10.8255 14.2848L10.8318 14.2985L10.8349 14.3053L10.8381 14.3121L10.8412 14.3188L10.8443 14.3256L10.8462 14.3296L13.9029 7.70666C14.0245 7.44328 14.2959 7.28216 14.5853 7.30157C14.8747 7.32097 15.1222 7.51687 15.2075 7.7941L15.2075 7.79414L15.2075 7.79417L15.2077 7.79474L15.2085 7.79747L15.2122 7.80919L15.2269 7.85628C15.24 7.89772 15.2592 7.95852 15.2839 8.0348C15.3332 8.18749 15.4038 8.40153 15.4884 8.64602C15.6595 9.14015 15.8812 9.73886 16.0971 10.2067C16.2196 10.4719 16.2866 10.6145 16.3462 10.7113C16.3619 10.7369 16.3733 10.7526 16.3804 10.7617C16.4007 10.7674 16.451 10.7785 16.554 10.7865C16.7226 10.7995 16.9502 10.8 17.3077 10.8H17.3207H17.3336H17.3465H17.3594H17.3723H17.3851H17.398H17.4108H17.4236H17.4363H17.449H17.4617H17.4744H17.4871H17.4997H17.5123H17.5249H17.5375H17.55H17.5625H17.575H17.5875H17.5999H17.6124H17.6248H17.6371H17.6495H17.6618H17.6741H17.6864H17.6986H17.7109H17.7231H17.7353H17.7474H17.7595H17.7717H17.7837H17.7958H17.8079H17.8199H17.8319H17.8438H17.8558H17.8677H17.8796H17.8915H17.9033H17.9152H17.927H17.9388H17.9505H17.9622H17.974H17.9856H17.9973H18.009H18.0206H18.0322H18.0437H18.0553H18.0668H18.0783H18.0898H18.1012H18.1127H18.1241H18.1355H18.1468H18.1582H18.1695H18.1808H18.192H18.2033H18.2145H18.2257H18.2369H18.248H18.2591H18.2702H18.2813H18.2924H18.3034H18.3144H18.3254H18.3364H18.3473H18.3582H18.3691H18.38H18.3908H18.4017H18.4125H18.4232H18.434H18.4447H18.4554H18.4661H18.4768H18.4874H18.498H18.5086H18.5192H18.5297H18.5403H18.5508H18.5612H18.5717H18.5821H18.5925H18.6029H18.6133H18.6236H18.6339H18.6442H18.6545H18.6648H18.675H18.6852H18.6954H18.7055H18.7156H18.7258H18.7358H18.7459H18.7559H18.766H18.776H18.7859H18.7959H18.8058H18.8157H18.8256H18.8355H18.8453H18.8551H18.8649H18.8747H18.8844H18.8941H18.9038H18.9135H18.9231H18.9328H18.9424H18.952H18.9615H18.9711H18.9806H18.9901H18.9995H19.009H19.0184H19.0278H19.0372H19.0466H19.0559H19.0652H19.0745H19.0838H19.093H19.1022H19.1114H19.1206H19.1298H19.1389H19.148H19.1571H19.1661H19.1752H19.1842H19.1932H19.2022H19.2111H19.22H19.229H19.2378H19.2467H19.2555H19.2644H19.2731H19.2819H19.2907H19.2994H19.3081H19.3168H19.3254H19.3341H19.3427H19.3513H19.3598H19.3684H19.3769H19.3854H19.3939H19.4024H19.4108H19.4192H19.4276H19.436H19.4443H19.4526H19.4609H19.4692H19.4775H19.4857H19.4939H19.5021H19.5103H19.5184H19.5265H19.5346H19.5427H19.5508H19.5588H19.5668H19.5748H19.5828H19.5907H19.5986H19.6066H19.6144H19.6223H19.6301H19.6379H19.6457H19.6535H19.6612H19.669H19.6767H19.6844H19.692H19.6996H19.7073H19.7149H19.7224H19.73H19.7375H19.745H19.7525H19.7599H19.7674H19.7748H19.7822H19.7896H19.7969H19.8043H19.8116H19.8188H19.8261H19.8334H19.8406H19.8478H19.8549H19.8621H19.8692H19.8763H19.8834H19.8905H19.8975H19.9046H19.9116H19.9185H19.9255H19.9324H19.9394H19.9462H19.9531H19.96H19.9668H19.9736H19.9804H19.9871H19.9939H20.0006H20.0073H20.014H20.0206H20.0273H20.0339H20.0405H20.047H20.0536H20.0601H20.0666H20.0731H20.0795H20.086H20.0924H20.0988H20.1052H20.1115H20.1178H20.1242H20.1304H20.1367H20.143H20.1492H20.1554H20.1616H20.1677H20.1739H20.18H20.1861H20.1921H20.1982H20.2042H20.2102H20.2162H20.2222H20.2281H20.234H20.2399H20.2458H20.2517H20.2575H20.2633H20.2691H20.2749H20.2806H20.2864H20.2921H20.2978H20.3034H20.3091H20.3147H20.3203H20.3259H20.3315H20.337H20.3425H20.348H20.3535H20.3589H20.3644H20.3698H20.3752H20.3805H20.3859H20.3912H20.3965H20.4018H20.4071H20.4123H20.4175H20.4227H20.4279H20.4331H20.4382H20.4433H20.4484H20.4535H20.4586H20.4636H20.4686H20.4736H20.4786H20.4835H20.4884H20.4933H20.4982H20.5031H20.5079H20.5128H20.5176H20.5223H20.5271H20.5318H20.5365H20.5412H20.5459H20.5506H20.5552H20.5598H20.5644H20.569H20.5735H20.5781H20.5826H20.5871H20.5915H20.596H20.6004H20.6048H20.6092H20.6136H20.6179H20.6222H20.6265H20.6308H20.6351H20.6393H20.6435H20.6477H20.6519H20.656H20.6602H20.6643H20.6684H20.6725H20.6765H20.6806H20.6846H20.6886H20.6925H20.6965H20.7004H20.7043H20.7082H20.7121H20.7159H20.7197H20.7235H20.7273H20.7311H20.7348H20.7386H20.7423H20.746H20.7496H20.7533H20.7569H20.7605H20.7641H20.7676H20.7712H20.7747H20.7782H20.7817H20.7851H20.7885H20.792H20.7954H20.7987H20.8021H20.8054H20.8087H20.812H20.8153H20.8186H20.8218H20.825H20.8282H20.8314H20.8345H20.8377H20.8408H20.8439H20.8469H20.85H20.853H20.856H20.859H20.862H20.8649H20.8679H20.8708H20.8737H20.8765H20.8794H20.8822H20.885H20.8878H20.8906H20.8933H20.8961H20.8988H20.9015H20.9041H20.9068H20.9094H20.912H20.9146H20.9172H20.9197H20.9223H20.9248H20.9272H20.9297H20.9322H20.9346H20.937H20.9394H20.9418H20.9441H20.9464H20.9487H20.951H20.9533H20.9555H20.9578H20.96H20.9622H20.9643H20.9665H20.9686H20.9707H20.9728H20.9749H20.9769H20.979H20.981H20.983H20.9849H20.9869H20.9888H20.9907H20.9926H20.9945H20.9964H20.9982H21V6C21 4.89543 20.1046 4 19 4H5ZM21 12.2H20.9982H20.9964H20.9945H20.9926H20.9907H20.9888H20.9869H20.9849H20.983H20.981H20.979H20.9769H20.9749H20.9728H20.9707H20.9686H20.9665H20.9643H20.9622H20.96H20.9578H20.9555H20.9533H20.951H20.9487H20.9464H20.9441H20.9418H20.9394H20.937H20.9346H20.9322H20.9297H20.9272H20.9248H20.9223H20.9197H20.9172H20.9146H20.912H20.9094H20.9068H20.9041H20.9015H20.8988H20.8961H20.8933H20.8906H20.8878H20.885H20.8822H20.8794H20.8765H20.8737H20.8708H20.8679H20.8649H20.862H20.859H20.856H20.853H20.85H20.8469H20.8439H20.8408H20.8377H20.8345H20.8314H20.8282H20.825H20.8218H20.8186H20.8153H20.812H20.8087H20.8054H20.8021H20.7987H20.7954H20.792H20.7885H20.7851H20.7817H20.7782H20.7747H20.7712H20.7676H20.7641H20.7605H20.7569H20.7533H20.7496H20.746H20.7423H20.7386H20.7348H20.7311H20.7273H20.7235H20.7197H20.7159H20.7121H20.7082H20.7043H20.7004H20.6965H20.6925H20.6886H20.6846H20.6806H20.6765H20.6725H20.6684H20.6643H20.6602H20.656H20.6519H20.6477H20.6435H20.6393H20.6351H20.6308H20.6265H20.6222H20.6179H20.6136H20.6092H20.6048H20.6004H20.596H20.5915H20.5871H20.5826H20.5781H20.5735H20.569H20.5644H20.5598H20.5552H20.5506H20.5459H20.5412H20.5365H20.5318H20.5271H20.5223H20.5176H20.5128H20.5079H20.5031H20.4982H20.4933H20.4884H20.4835H20.4786H20.4736H20.4686H20.4636H20.4586H20.4535H20.4484H20.4433H20.4382H20.4331H20.4279H20.4227H20.4175H20.4123H20.4071H20.4018H20.3965H20.3912H20.3859H20.3805H20.3752H20.3698H20.3644H20.3589H20.3535H20.348H20.3425H20.337H20.3315H20.3259H20.3203H20.3147H20.3091H20.3034H20.2978H20.2921H20.2864H20.2806H20.2749H20.2691H20.2633H20.2575H20.2517H20.2458H20.2399H20.234H20.2281H20.2222H20.2162H20.2102H20.2042H20.1982H20.1921H20.1861H20.18H20.1739H20.1677H20.1616H20.1554H20.1492H20.143H20.1367H20.1304H20.1242H20.1178H20.1115H20.1052H20.0988H20.0924H20.086H20.0795H20.0731H20.0666H20.0601H20.0536H20.047H20.0405H20.0339H20.0273H20.0206H20.014H20.0073H20.0006H19.9939H19.9871H19.9804H19.9736H19.9668H19.96H19.9531H19.9462H19.9394H19.9324H19.9255H19.9185H19.9116H19.9046H19.8975H19.8905H19.8834H19.8763H19.8692H19.8621H19.8549H19.8478H19.8406H19.8334H19.8261H19.8188H19.8116H19.8043H19.7969H19.7896H19.7822H19.7748H19.7674H19.7599H19.7525H19.745H19.7375H19.73H19.7224H19.7149H19.7073H19.6996H19.692H19.6844H19.6767H19.669H19.6612H19.6535H19.6457H19.6379H19.6301H19.6223H19.6144H19.6066H19.5986H19.5907H19.5828H19.5748H19.5668H19.5588H19.5508H19.5427H19.5346H19.5265H19.5184H19.5103H19.5021H19.4939H19.4857H19.4775H19.4692H19.4609H19.4526H19.4443H19.436H19.4276H19.4192H19.4108H19.4024H19.3939H19.3854H19.3769H19.3684H19.3598H19.3513H19.3427H19.3341H19.3254H19.3168H19.3081H19.2994H19.2907H19.2819H19.2731H19.2644H19.2555H19.2467H19.2378H19.229H19.22H19.2111H19.2022H19.1932H19.1842H19.1752H19.1661H19.1571H19.148H19.1389H19.1298H19.1206H19.1114H19.1022H19.093H19.0838H19.0745H19.0652H19.0559H19.0466H19.0372H19.0278H19.0184H19.009H18.9995H18.9901H18.9806H18.9711H18.9615H18.952H18.9424H18.9328H18.9231H18.9135H18.9038H18.8941H18.8844H18.8747H18.8649H18.8551H18.8453H18.8355H18.8256H18.8157H18.8058H18.7959H18.7859H18.776H18.766H18.7559H18.7459H18.7358H18.7258H18.7156H18.7055H18.6954H18.6852H18.675H18.6648H18.6545H18.6442H18.6339H18.6236H18.6133H18.6029H18.5925H18.5821H18.5717H18.5612H18.5508H18.5403H18.5297H18.5192H18.5086H18.498H18.4874H18.4768H18.4661H18.4554H18.4447H18.434H18.4232H18.4125H18.4017H18.3908H18.38H18.3691H18.3582H18.3473H18.3364H18.3254H18.3144H18.3034H18.2924H18.2813H18.2702H18.2591H18.248H18.2369H18.2257H18.2145H18.2033H18.192H18.1808H18.1695H18.1582H18.1468H18.1355H18.1241H18.1127H18.1012H18.0898H18.0783H18.0668H18.0553H18.0437H18.0322H18.0206H18.009H17.9973H17.9856H17.974H17.9622H17.9505H17.9388H17.927H17.9152H17.9033H17.8915H17.8796H17.8677H17.8558H17.8438H17.8319H17.8199H17.8079H17.7958H17.7837H17.7717H17.7595H17.7474H17.7353H17.7231H17.7109H17.6986H17.6864H17.6741H17.6618H17.6495H17.6371H17.6248H17.6124H17.5999H17.5875H17.575H17.5625H17.55H17.5375H17.5249H17.5123H17.4997H17.4871H17.4744H17.4617H17.449H17.4363H17.4236H17.4108H17.398H17.3851H17.3723H17.3594H17.3465H17.3336H17.3207H17.3077L17.2818 12.2C16.9578 12.2 16.6752 12.2 16.446 12.1823C16.2097 12.164 15.9492 12.1228 15.7051 11.9905C15.4447 11.8495 15.2782 11.647 15.1538 11.445C15.045 11.2681 14.9431 11.0472 14.8394 10.8225L14.826 10.7933C14.6941 10.5076 14.5639 10.1875 14.4445 9.87405L11.4817 16.2933C11.3673 16.5412 11.1192 16.7 10.8462 16.7C10.5731 16.7 10.325 16.5412 10.2106 16.2933L10.2031 16.2772L10.2012 16.273L10.2002 16.2708L10.1992 16.2687L10.1972 16.2644L10.1962 16.2622L10.1952 16.2599L10.1867 16.2417L10.1778 16.2223L10.1585 16.1804L10.1481 16.1579L10.1372 16.1344L10.114 16.0841L10.1079 16.0709L10.1017 16.0575L10.089 16.0298L10.062 15.9714L10.0585 15.9638L10.055 15.9562L10.0514 15.9485L10.0478 15.9407L10.0332 15.909L10.0025 15.8425L9.99456 15.8253L9.99054 15.8166L9.98649 15.8078L9.97001 15.7721L9.9616 15.7539L9.95308 15.7354L9.9357 15.6978L9.93128 15.6882L9.92906 15.6834L9.92684 15.6786L9.92236 15.6689L9.92012 15.664L9.91786 15.6591L9.89958 15.6195L9.88086 15.5789L9.87133 15.5583L9.86653 15.5479L9.86169 15.5374L9.82204 15.4515L9.80155 15.4071L9.79637 15.3959L9.79376 15.3902L9.79115 15.3846L9.78064 15.3618L9.77799 15.3561L9.77534 15.3503L9.77268 15.3446L9.77002 15.3388L9.75929 15.3155L9.73751 15.2683L9.64612 15.0703L9.5979 14.9659L9.57317 14.9123L9.56065 14.8851L9.5575 14.8783L9.55435 14.8715L9.54802 14.8578L9.54485 14.8509L9.54167 14.844L9.53529 14.8302L9.52889 14.8163L9.52246 14.8024L9.4965 14.7462L9.44334 14.631L9.43658 14.6163L9.4332 14.609L9.4298 14.6016L9.41616 14.5721L9.40242 14.5423L9.39551 14.5274L9.39205 14.5198L9.38858 14.5123L9.3606 14.4517L9.34646 14.4211L9.33222 14.3902L9.30344 14.3279L9.28891 14.2964L9.27428 14.2647L9.27061 14.2567L9.26693 14.2488L9.25955 14.2328L9.24472 14.2006L9.21478 14.1358L9.18445 14.0701L9.16914 14.0369L9.15374 14.0035L9.12265 13.9361L9.11873 13.9277L9.11481 13.9192L9.10696 13.9021L9.09117 13.868L9.05932 13.7989L9.05531 13.7903L9.0513 13.7816L9.04728 13.7728L9.04325 13.7641L9.02709 13.7291L8.99449 13.6585L8.99039 13.6496L8.98628 13.6407L8.98217 13.6318L8.97805 13.6229L8.9698 13.605L8.96152 13.587L8.82597 13.2933C8.46988 12.5218 8.18839 12.0739 7.9986 11.8312C7.95341 11.7734 7.91609 11.7308 7.88702 11.7H3V18C3 19.1046 3.89543 20 5 20H19C20.1046 20 21 19.1046 21 18V12.2ZM16.3896 10.7722C16.3896 10.7723 16.3889 10.7717 16.3874 10.7701C16.3889 10.7714 16.3896 10.7722 16.3896 10.7722ZM7.80666 11.6272C7.80657 11.6268 7.81122 11.6296 7.82089 11.6372C7.81157 11.6315 7.80674 11.6277 7.80666 11.6272Z" fill="#797979"/> +</svg> diff --git a/public/icons/strategy.svg b/public/icons/strategy.svg new file mode 100644 index 00000000..1cdde736 --- /dev/null +++ b/public/icons/strategy.svg @@ -0,0 +1,5 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="2" y="11" width="5" height="11" rx="1" fill="#797979"/> +<rect x="9" y="2" width="5" height="20" rx="1" fill="#797979"/> +<rect x="16" y="8" width="5" height="14" rx="1" fill="#797979"/> +</svg> diff --git a/public/icons/text-logo.svg b/public/icons/text-logo.svg new file mode 100644 index 00000000..36d7c26b --- /dev/null +++ b/public/icons/text-logo.svg @@ -0,0 +1,13 @@ +<svg width="142" height="24" viewBox="0 0 142 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M0.132269 22.3365V8.07017H2.88346V22.3365H0.132269ZM0 5.27286V2H3.04219V5.27286H0Z" fill="#0A0A0A"/> +<path d="M6.74572 22.3365V8.07017H9.36464V11.427H9.49691V22.3365H6.74572ZM16.322 22.3365V13.1613C16.322 12.1729 16.0839 11.4363 15.6077 10.9514C15.1492 10.4665 14.4614 10.2241 13.5443 10.2241C12.7507 10.2241 12.0453 10.4106 11.428 10.7836C10.8284 11.1565 10.3522 11.6694 9.99953 12.3221C9.66445 12.9748 9.49691 13.7487 9.49691 14.6439L9.15301 11.2311C9.59391 10.1682 10.2729 9.32897 11.19 8.71356C12.107 8.09815 13.174 7.79044 14.3909 7.79044C15.837 7.79044 16.9745 8.21936 17.8034 9.07721C18.6323 9.93505 19.0467 11.0726 19.0467 12.4899V22.3365H16.322Z" fill="#0A0A0A"/> +<path d="M26.1082 22.3365L20.6058 8.07017H23.5422L28.0394 21.0217H26.7431L31.1874 8.07017H34.0179L28.542 22.3365H26.1082Z" fill="#0A0A0A"/> +<path d="M41.7319 22.6162C40.3739 22.6162 39.1747 22.2992 38.1342 21.6651C37.1113 21.0311 36.3089 20.1546 35.7269 19.0356C35.1449 17.9167 34.8539 16.63 34.8539 15.1754C34.8539 13.7021 35.1361 12.4153 35.7004 11.3151C36.2824 10.1961 37.0937 9.32897 38.1342 8.71356C39.1747 8.09815 40.3651 7.79044 41.7054 7.79044C43.0105 7.79044 44.1392 8.08882 45.0915 8.68558C46.0615 9.26369 46.8022 10.0749 47.3136 11.1192C47.8427 12.1636 48.1072 13.4037 48.1072 14.8397C48.1072 15.0635 48.0984 15.2779 48.0808 15.4831C48.0808 15.6695 48.0632 15.8654 48.0279 16.0705H36.7057V13.8886H46.0968L45.409 14.7558C45.409 13.2266 45.0827 12.061 44.4302 11.2591C43.7776 10.4572 42.8694 10.0563 41.7054 10.0563C40.4533 10.0563 39.4569 10.5132 38.7162 11.427C37.9931 12.3221 37.6315 13.5716 37.6315 15.1754C37.6315 16.7978 37.9931 18.0659 38.7162 18.9797C39.4569 19.8935 40.4797 20.3504 41.7848 20.3504C42.5784 20.3504 43.2662 20.1825 43.8482 19.8469C44.4302 19.4925 44.8623 18.9797 45.1444 18.3083H47.7369C47.296 19.6511 46.5465 20.7047 45.4883 21.4693C44.4478 22.2339 43.1957 22.6162 41.7319 22.6162Z" fill="#0A0A0A"/> +<path d="M55.8406 22.6162C54.0771 22.6162 52.675 22.2059 51.6345 21.3854C50.594 20.5648 50.0032 19.4086 49.8621 17.9167H52.4016C52.5251 18.7373 52.8778 19.3713 53.4598 19.8189C54.0594 20.2478 54.8707 20.4623 55.8935 20.4623C56.793 20.4623 57.4808 20.2944 57.9569 19.9588C58.4331 19.6231 58.6712 19.1475 58.6712 18.5321C58.6712 18.0846 58.5301 17.7116 58.2479 17.4132C57.9834 17.0962 57.4543 16.8351 56.6607 16.63L54.4915 16.0985C53.1335 15.7441 52.1195 15.222 51.4493 14.532C50.7968 13.8233 50.4705 12.9561 50.4705 11.9305C50.4705 10.6437 50.9202 9.63667 51.8197 8.90937C52.7367 8.16342 53.9889 7.79044 55.5761 7.79044C57.1457 7.79044 58.4067 8.16342 59.359 8.90937C60.329 9.65532 60.8757 10.6996 60.9991 12.0424H58.4596C58.3538 11.371 58.0451 10.8582 57.5337 10.5038C57.0399 10.1309 56.3609 9.94437 55.4967 9.94437C54.6855 9.94437 54.0594 10.1029 53.6185 10.4199C53.1953 10.7183 52.9836 11.1379 52.9836 11.6787C52.9836 12.1263 53.1424 12.4993 53.4598 12.7976C53.7949 13.096 54.3416 13.3478 55.0999 13.5529L57.322 14.1403C58.6095 14.476 59.5794 15.0262 60.232 15.7908C60.8845 16.5367 61.2108 17.4412 61.2108 18.5042C61.2108 19.7909 60.7346 20.798 59.7823 21.5253C58.8476 22.2526 57.5337 22.6162 55.8406 22.6162Z" fill="#0A0A0A"/> +<path d="M69.4755 22.6162C67.9059 22.6162 66.7508 22.2339 66.0101 21.4693C65.287 20.6861 64.9255 19.5392 64.9255 18.0286V4.79732L67.6502 3.70636V18.0566C67.6502 18.8025 67.8354 19.3527 68.2057 19.707C68.5761 20.0613 69.1757 20.2385 70.0046 20.2385C70.322 20.2385 70.6042 20.2105 70.8511 20.1546C71.098 20.0986 71.3096 20.0334 71.486 19.9588V22.3085C71.292 22.4017 71.0186 22.4763 70.6659 22.5323C70.3132 22.5882 69.9164 22.6162 69.4755 22.6162ZM62.2272 10.3919V8.07017H71.486V10.3919H62.2272Z" fill="#0A0A0A"/> +<path d="M74.0944 22.3365V8.07017H76.7133V11.427H76.8456V22.3365H74.0944ZM83.1945 22.3365V13.0214C83.1945 12.0517 82.9828 11.343 82.5596 10.8955C82.1363 10.4479 81.5279 10.2241 80.7343 10.2241C79.9936 10.2241 79.3234 10.4106 78.7238 10.7836C78.1418 11.1379 77.6833 11.6414 77.3482 12.2941C77.0131 12.9282 76.8456 13.6835 76.8456 14.5599L76.5017 11.2311C76.9426 10.1682 77.6039 9.32897 78.4857 8.71356C79.3851 8.09815 80.3904 7.79044 81.5014 7.79044C82.8418 7.79044 83.9087 8.20071 84.7023 9.02126C85.5136 9.84181 85.9192 10.9234 85.9192 12.2661V22.3365H83.1945ZM92.2946 22.3365V13.0214C92.2946 12.0517 92.0741 11.343 91.6332 10.8955C91.21 10.4479 90.6015 10.2241 89.8079 10.2241C89.0849 10.2241 88.4235 10.4106 87.8239 10.7836C87.2419 11.1379 86.7746 11.6414 86.4218 12.2941C86.0868 12.9282 85.9192 13.6835 85.9192 14.5599L85.3637 11.2311C85.8222 10.1682 86.51 9.32897 87.4271 8.71356C88.3618 8.09815 89.4023 7.79044 90.5486 7.79044C91.9066 7.79044 92.9912 8.21004 93.8024 9.04923C94.6137 9.86978 95.0193 10.9701 95.0193 12.3501V22.3365H92.2946Z" fill="#0A0A0A"/> +<path d="M104.534 22.6162C103.176 22.6162 101.977 22.2992 100.936 21.6651C99.9132 21.0311 99.1108 20.1546 98.5288 19.0356C97.9468 17.9167 97.6558 16.63 97.6558 15.1754C97.6558 13.7021 97.938 12.4153 98.5023 11.3151C99.0843 10.1961 99.8956 9.32897 100.936 8.71356C101.977 8.09815 103.167 7.79044 104.507 7.79044C105.812 7.79044 106.941 8.08882 107.893 8.68558C108.863 9.26369 109.604 10.0749 110.116 11.1192C110.645 12.1636 110.909 13.4037 110.909 14.8397C110.909 15.0635 110.9 15.2779 110.883 15.4831C110.883 15.6695 110.865 15.8654 110.83 16.0705H99.5076V13.8886H108.899L108.211 14.7558C108.211 13.2266 107.885 12.061 107.232 11.2591C106.58 10.4572 105.671 10.0563 104.507 10.0563C103.255 10.0563 102.259 10.5132 101.518 11.427C100.795 12.3221 100.433 13.5716 100.433 15.1754C100.433 16.7978 100.795 18.0659 101.518 18.9797C102.259 19.8935 103.282 20.3504 104.587 20.3504C105.38 20.3504 106.068 20.1825 106.65 19.8469C107.232 19.4925 107.664 18.9797 107.946 18.3083H110.539C110.098 19.6511 109.348 20.7047 108.29 21.4693C107.25 22.2339 105.998 22.6162 104.534 22.6162Z" fill="#0A0A0A"/> +<path d="M119.231 22.6162C117.662 22.6162 116.507 22.2339 115.766 21.4693C115.043 20.6861 114.681 19.5392 114.681 18.0286V4.79732L117.406 3.70636V18.0566C117.406 18.8025 117.591 19.3527 117.962 19.707C118.332 20.0613 118.932 20.2385 119.76 20.2385C120.078 20.2385 120.36 20.2105 120.607 20.1546C120.854 20.0986 121.065 20.0334 121.242 19.9588V22.3085C121.048 22.4017 120.774 22.4763 120.422 22.5323C120.069 22.5882 119.672 22.6162 119.231 22.6162ZM111.983 10.3919V8.07017H121.242V10.3919H111.983Z" fill="#0A0A0A"/> +<path d="M123.799 22.3365V8.07017H126.55V22.3365H123.799ZM123.666 5.27286V2H126.708V5.27286H123.666Z" fill="#0A0A0A"/> +<path d="M135.969 22.6162C134.681 22.6162 133.535 22.2992 132.53 21.6651C131.524 21.0124 130.731 20.1266 130.149 19.0077C129.584 17.8887 129.302 16.6113 129.302 15.1754C129.302 13.7394 129.584 12.4713 130.149 11.371C130.731 10.2521 131.524 9.37559 132.53 8.74153C133.535 8.10747 134.672 7.79044 135.942 7.79044C137.018 7.79044 137.979 8.0049 138.826 8.43382C139.672 8.86275 140.369 9.45951 140.915 10.2241C141.462 10.9887 141.815 11.8932 141.974 12.9375H139.381C139.205 12.0983 138.817 11.427 138.217 10.9234C137.635 10.4199 136.894 10.1682 135.995 10.1682C135.219 10.1682 134.54 10.3733 133.958 10.7836C133.376 11.1938 132.926 11.772 132.609 12.5179C132.291 13.2639 132.133 14.1497 132.133 15.1754C132.133 16.1824 132.291 17.0682 132.609 17.8328C132.926 18.5974 133.376 19.1942 133.958 19.6231C134.54 20.0334 135.228 20.2385 136.021 20.2385C136.886 20.2385 137.617 19.9961 138.217 19.5112C138.834 19.0077 139.231 18.3363 139.408 17.4971H142C141.824 18.5228 141.453 19.4179 140.889 20.1825C140.342 20.9471 139.646 21.5439 138.799 21.9728C137.953 22.4017 137.009 22.6162 135.969 22.6162Z" fill="#0A0A0A"/> +</svg> diff --git a/public/icons/traders.svg b/public/icons/traders.svg new file mode 100644 index 00000000..c43591e9 --- /dev/null +++ b/public/icons/traders.svg @@ -0,0 +1,6 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="8.08594" cy="9" r="4" fill="#797979"/> +<circle cx="17.0859" cy="11" r="3" fill="#797979"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M14 20H1V18C1 15.7909 2.79086 14 5 14H10C12.2091 14 14 15.7909 14 18V20Z" fill="#797979"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5 20C15.3516 18.4189 16 16 14.1172 15H18.999C21.208 15 22.999 16.7908 22.999 19V19.3333C22.999 19.5605 22.9805 19.7832 22.9434 20H15.5Z" fill="#797979"/> +</svg> diff --git a/public/images/logo.svg b/public/images/logo.svg new file mode 100644 index 00000000..c5ff8c35 --- /dev/null +++ b/public/images/logo.svg @@ -0,0 +1,5 @@ +<svg width="40" height="24" viewBox="0 0 40 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M11.75 1H20.421L27.1169 15.5503V23.5994H18.2051L11.75 9.31447V1Z" fill="#FF7752"/> +<path d="M23.9297 1H32.657L39.3964 15.5503V23.5994H30.4267L23.9297 9.31447V1Z" fill="#FF7752"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M7.49668 1H1V9.23233L1.05415 9.41406H5.51337V23.5988H5.28057L5.28075 23.5994H14.1926V15.5503L7.49668 1Z" fill="#FF7752"/> +</svg> diff --git a/public/images/text-logo.svg b/public/images/text-logo.svg new file mode 100644 index 00000000..36d7c26b --- /dev/null +++ b/public/images/text-logo.svg @@ -0,0 +1,13 @@ +<svg width="142" height="24" viewBox="0 0 142 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M0.132269 22.3365V8.07017H2.88346V22.3365H0.132269ZM0 5.27286V2H3.04219V5.27286H0Z" fill="#0A0A0A"/> +<path d="M6.74572 22.3365V8.07017H9.36464V11.427H9.49691V22.3365H6.74572ZM16.322 22.3365V13.1613C16.322 12.1729 16.0839 11.4363 15.6077 10.9514C15.1492 10.4665 14.4614 10.2241 13.5443 10.2241C12.7507 10.2241 12.0453 10.4106 11.428 10.7836C10.8284 11.1565 10.3522 11.6694 9.99953 12.3221C9.66445 12.9748 9.49691 13.7487 9.49691 14.6439L9.15301 11.2311C9.59391 10.1682 10.2729 9.32897 11.19 8.71356C12.107 8.09815 13.174 7.79044 14.3909 7.79044C15.837 7.79044 16.9745 8.21936 17.8034 9.07721C18.6323 9.93505 19.0467 11.0726 19.0467 12.4899V22.3365H16.322Z" fill="#0A0A0A"/> +<path d="M26.1082 22.3365L20.6058 8.07017H23.5422L28.0394 21.0217H26.7431L31.1874 8.07017H34.0179L28.542 22.3365H26.1082Z" fill="#0A0A0A"/> +<path d="M41.7319 22.6162C40.3739 22.6162 39.1747 22.2992 38.1342 21.6651C37.1113 21.0311 36.3089 20.1546 35.7269 19.0356C35.1449 17.9167 34.8539 16.63 34.8539 15.1754C34.8539 13.7021 35.1361 12.4153 35.7004 11.3151C36.2824 10.1961 37.0937 9.32897 38.1342 8.71356C39.1747 8.09815 40.3651 7.79044 41.7054 7.79044C43.0105 7.79044 44.1392 8.08882 45.0915 8.68558C46.0615 9.26369 46.8022 10.0749 47.3136 11.1192C47.8427 12.1636 48.1072 13.4037 48.1072 14.8397C48.1072 15.0635 48.0984 15.2779 48.0808 15.4831C48.0808 15.6695 48.0632 15.8654 48.0279 16.0705H36.7057V13.8886H46.0968L45.409 14.7558C45.409 13.2266 45.0827 12.061 44.4302 11.2591C43.7776 10.4572 42.8694 10.0563 41.7054 10.0563C40.4533 10.0563 39.4569 10.5132 38.7162 11.427C37.9931 12.3221 37.6315 13.5716 37.6315 15.1754C37.6315 16.7978 37.9931 18.0659 38.7162 18.9797C39.4569 19.8935 40.4797 20.3504 41.7848 20.3504C42.5784 20.3504 43.2662 20.1825 43.8482 19.8469C44.4302 19.4925 44.8623 18.9797 45.1444 18.3083H47.7369C47.296 19.6511 46.5465 20.7047 45.4883 21.4693C44.4478 22.2339 43.1957 22.6162 41.7319 22.6162Z" fill="#0A0A0A"/> +<path d="M55.8406 22.6162C54.0771 22.6162 52.675 22.2059 51.6345 21.3854C50.594 20.5648 50.0032 19.4086 49.8621 17.9167H52.4016C52.5251 18.7373 52.8778 19.3713 53.4598 19.8189C54.0594 20.2478 54.8707 20.4623 55.8935 20.4623C56.793 20.4623 57.4808 20.2944 57.9569 19.9588C58.4331 19.6231 58.6712 19.1475 58.6712 18.5321C58.6712 18.0846 58.5301 17.7116 58.2479 17.4132C57.9834 17.0962 57.4543 16.8351 56.6607 16.63L54.4915 16.0985C53.1335 15.7441 52.1195 15.222 51.4493 14.532C50.7968 13.8233 50.4705 12.9561 50.4705 11.9305C50.4705 10.6437 50.9202 9.63667 51.8197 8.90937C52.7367 8.16342 53.9889 7.79044 55.5761 7.79044C57.1457 7.79044 58.4067 8.16342 59.359 8.90937C60.329 9.65532 60.8757 10.6996 60.9991 12.0424H58.4596C58.3538 11.371 58.0451 10.8582 57.5337 10.5038C57.0399 10.1309 56.3609 9.94437 55.4967 9.94437C54.6855 9.94437 54.0594 10.1029 53.6185 10.4199C53.1953 10.7183 52.9836 11.1379 52.9836 11.6787C52.9836 12.1263 53.1424 12.4993 53.4598 12.7976C53.7949 13.096 54.3416 13.3478 55.0999 13.5529L57.322 14.1403C58.6095 14.476 59.5794 15.0262 60.232 15.7908C60.8845 16.5367 61.2108 17.4412 61.2108 18.5042C61.2108 19.7909 60.7346 20.798 59.7823 21.5253C58.8476 22.2526 57.5337 22.6162 55.8406 22.6162Z" fill="#0A0A0A"/> +<path d="M69.4755 22.6162C67.9059 22.6162 66.7508 22.2339 66.0101 21.4693C65.287 20.6861 64.9255 19.5392 64.9255 18.0286V4.79732L67.6502 3.70636V18.0566C67.6502 18.8025 67.8354 19.3527 68.2057 19.707C68.5761 20.0613 69.1757 20.2385 70.0046 20.2385C70.322 20.2385 70.6042 20.2105 70.8511 20.1546C71.098 20.0986 71.3096 20.0334 71.486 19.9588V22.3085C71.292 22.4017 71.0186 22.4763 70.6659 22.5323C70.3132 22.5882 69.9164 22.6162 69.4755 22.6162ZM62.2272 10.3919V8.07017H71.486V10.3919H62.2272Z" fill="#0A0A0A"/> +<path d="M74.0944 22.3365V8.07017H76.7133V11.427H76.8456V22.3365H74.0944ZM83.1945 22.3365V13.0214C83.1945 12.0517 82.9828 11.343 82.5596 10.8955C82.1363 10.4479 81.5279 10.2241 80.7343 10.2241C79.9936 10.2241 79.3234 10.4106 78.7238 10.7836C78.1418 11.1379 77.6833 11.6414 77.3482 12.2941C77.0131 12.9282 76.8456 13.6835 76.8456 14.5599L76.5017 11.2311C76.9426 10.1682 77.6039 9.32897 78.4857 8.71356C79.3851 8.09815 80.3904 7.79044 81.5014 7.79044C82.8418 7.79044 83.9087 8.20071 84.7023 9.02126C85.5136 9.84181 85.9192 10.9234 85.9192 12.2661V22.3365H83.1945ZM92.2946 22.3365V13.0214C92.2946 12.0517 92.0741 11.343 91.6332 10.8955C91.21 10.4479 90.6015 10.2241 89.8079 10.2241C89.0849 10.2241 88.4235 10.4106 87.8239 10.7836C87.2419 11.1379 86.7746 11.6414 86.4218 12.2941C86.0868 12.9282 85.9192 13.6835 85.9192 14.5599L85.3637 11.2311C85.8222 10.1682 86.51 9.32897 87.4271 8.71356C88.3618 8.09815 89.4023 7.79044 90.5486 7.79044C91.9066 7.79044 92.9912 8.21004 93.8024 9.04923C94.6137 9.86978 95.0193 10.9701 95.0193 12.3501V22.3365H92.2946Z" fill="#0A0A0A"/> +<path d="M104.534 22.6162C103.176 22.6162 101.977 22.2992 100.936 21.6651C99.9132 21.0311 99.1108 20.1546 98.5288 19.0356C97.9468 17.9167 97.6558 16.63 97.6558 15.1754C97.6558 13.7021 97.938 12.4153 98.5023 11.3151C99.0843 10.1961 99.8956 9.32897 100.936 8.71356C101.977 8.09815 103.167 7.79044 104.507 7.79044C105.812 7.79044 106.941 8.08882 107.893 8.68558C108.863 9.26369 109.604 10.0749 110.116 11.1192C110.645 12.1636 110.909 13.4037 110.909 14.8397C110.909 15.0635 110.9 15.2779 110.883 15.4831C110.883 15.6695 110.865 15.8654 110.83 16.0705H99.5076V13.8886H108.899L108.211 14.7558C108.211 13.2266 107.885 12.061 107.232 11.2591C106.58 10.4572 105.671 10.0563 104.507 10.0563C103.255 10.0563 102.259 10.5132 101.518 11.427C100.795 12.3221 100.433 13.5716 100.433 15.1754C100.433 16.7978 100.795 18.0659 101.518 18.9797C102.259 19.8935 103.282 20.3504 104.587 20.3504C105.38 20.3504 106.068 20.1825 106.65 19.8469C107.232 19.4925 107.664 18.9797 107.946 18.3083H110.539C110.098 19.6511 109.348 20.7047 108.29 21.4693C107.25 22.2339 105.998 22.6162 104.534 22.6162Z" fill="#0A0A0A"/> +<path d="M119.231 22.6162C117.662 22.6162 116.507 22.2339 115.766 21.4693C115.043 20.6861 114.681 19.5392 114.681 18.0286V4.79732L117.406 3.70636V18.0566C117.406 18.8025 117.591 19.3527 117.962 19.707C118.332 20.0613 118.932 20.2385 119.76 20.2385C120.078 20.2385 120.36 20.2105 120.607 20.1546C120.854 20.0986 121.065 20.0334 121.242 19.9588V22.3085C121.048 22.4017 120.774 22.4763 120.422 22.5323C120.069 22.5882 119.672 22.6162 119.231 22.6162ZM111.983 10.3919V8.07017H121.242V10.3919H111.983Z" fill="#0A0A0A"/> +<path d="M123.799 22.3365V8.07017H126.55V22.3365H123.799ZM123.666 5.27286V2H126.708V5.27286H123.666Z" fill="#0A0A0A"/> +<path d="M135.969 22.6162C134.681 22.6162 133.535 22.2992 132.53 21.6651C131.524 21.0124 130.731 20.1266 130.149 19.0077C129.584 17.8887 129.302 16.6113 129.302 15.1754C129.302 13.7394 129.584 12.4713 130.149 11.371C130.731 10.2521 131.524 9.37559 132.53 8.74153C133.535 8.10747 134.672 7.79044 135.942 7.79044C137.018 7.79044 137.979 8.0049 138.826 8.43382C139.672 8.86275 140.369 9.45951 140.915 10.2241C141.462 10.9887 141.815 11.8932 141.974 12.9375H139.381C139.205 12.0983 138.817 11.427 138.217 10.9234C137.635 10.4199 136.894 10.1682 135.995 10.1682C135.219 10.1682 134.54 10.3733 133.958 10.7836C133.376 11.1938 132.926 11.772 132.609 12.5179C132.291 13.2639 132.133 14.1497 132.133 15.1754C132.133 16.1824 132.291 17.0682 132.609 17.8328C132.926 18.5974 133.376 19.1942 133.958 19.6231C134.54 20.0334 135.228 20.2385 136.021 20.2385C136.886 20.2385 137.617 19.9961 138.217 19.5112C138.834 19.0077 139.231 18.3363 139.408 17.4971H142C141.824 18.5228 141.453 19.4179 140.889 20.1825C140.342 20.9471 139.646 21.5439 138.799 21.9728C137.953 22.4017 137.009 22.6162 135.969 22.6162Z" fill="#0A0A0A"/> +</svg> From 1a4cd7b14258acf223845c7998824d21aa657f9a Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 12 Nov 2024 22:37:34 +0900 Subject: [PATCH 040/648] =?UTF-8?q?rename:=20admin=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8A=B8=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/admin/users/.gitkeep | 0 app/{(dashboard)/admin => admin/category}/.gitkeep | 0 app/{(dashboard)/admin/category => admin/notices}/.gitkeep | 0 app/admin/page.tsx | 5 +++++ app/{(dashboard)/admin/notices => admin/users}/.gitkeep | 0 5 files changed, 5 insertions(+) delete mode 100644 app/(dashboard)/admin/users/.gitkeep rename app/{(dashboard)/admin => admin/category}/.gitkeep (100%) rename app/{(dashboard)/admin/category => admin/notices}/.gitkeep (100%) create mode 100644 app/admin/page.tsx rename app/{(dashboard)/admin/notices => admin/users}/.gitkeep (100%) diff --git a/app/(dashboard)/admin/users/.gitkeep b/app/(dashboard)/admin/users/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/admin/.gitkeep b/app/admin/category/.gitkeep similarity index 100% rename from app/(dashboard)/admin/.gitkeep rename to app/admin/category/.gitkeep diff --git a/app/(dashboard)/admin/category/.gitkeep b/app/admin/notices/.gitkeep similarity index 100% rename from app/(dashboard)/admin/category/.gitkeep rename to app/admin/notices/.gitkeep diff --git a/app/admin/page.tsx b/app/admin/page.tsx new file mode 100644 index 00000000..a9e1c38e --- /dev/null +++ b/app/admin/page.tsx @@ -0,0 +1,5 @@ +const AdminPage = () => { + return <>AdminPage</> +} + +export default AdminPage diff --git a/app/(dashboard)/admin/notices/.gitkeep b/app/admin/users/.gitkeep similarity index 100% rename from app/(dashboard)/admin/notices/.gitkeep rename to app/admin/users/.gitkeep From ce453fa26da0bc7f07ea5d8c7c0480e353d5d070 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 12:51:25 +0900 Subject: [PATCH 041/648] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/button/button.module.scss | 49 +++++++++++++++++++++++++++++ shared/ui/button/index.tsx | 34 ++++++++++++++++++++ shared/ui/button/styles.module.scss | 0 3 files changed, 83 insertions(+) create mode 100644 shared/ui/button/button.module.scss delete mode 100644 shared/ui/button/styles.module.scss diff --git a/shared/ui/button/button.module.scss b/shared/ui/button/button.module.scss new file mode 100644 index 00000000..0e96536c --- /dev/null +++ b/shared/ui/button/button.module.scss @@ -0,0 +1,49 @@ +.button { + display: inline-flex; + justify-content: center; + align-items: center; + gap: 10px; + border: 1px solid $color-gray-800; + color: $color-gray-800; + line-height: 132%; + + &.small { + padding: 12px 24px; + border-radius: 19.5px; + border-width: 1.5px; + font-size: $text-c1; + font-weight: $text-medium; + letter-spacing: -0.24px; + } + + &.medium { + padding: 12px 24px; + border-radius: 25px; + font-size: $text-b2; + font-weight: $text-semibold; + letter-spacing: -0.36px; + } + + &.large { + width: 400px; + padding: 18px 36px; + border-radius: 60px; + border-width: 1.2px; + font-size: $text-b1; + font-weight: $text-semibold; + } + + &.outline { + background: transparent; + } + + &.filled { + background: $color-gray-800; + color: $color-white; + } + + &.disabled { + background: $color-gray-600; + cursor: not-allowed; + } +} diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index e69de29b..3dd50e6e 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -0,0 +1,34 @@ +'use client' + +import { ComponentProps } from 'react' + +import classNames from 'classnames/bind' + +import styles from './button.module.scss' + +const cx = classNames.bind(styles) + +type ButtonSizeType = 'small' | 'medium' | 'large' +type ButtonVariantType = 'outline' | 'filled' + +interface ButtonProps extends ComponentProps<'button'> { + size: ButtonSizeType + variant?: ButtonVariantType +} + +export const Button = ({ + children, + size, + variant = 'outline', + disabled = false, + className, + ...props +}: ButtonProps) => ( + <button + className={cx('button', size, variant, { disabled }, className)} + disabled={disabled} + {...props} + > + {children} + </button> +) diff --git a/shared/ui/button/styles.module.scss b/shared/ui/button/styles.module.scss deleted file mode 100644 index e69de29b..00000000 From 443ad6a8db0503a03fd27b24fcf1c80b0d62ef0b Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 13:45:40 +0900 Subject: [PATCH 042/648] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/main.ts | 36 +++++++++++ shared/ui/button/button.module.scss | 5 +- shared/ui/button/button.stories.tsx | 99 +++++++++++++++++++++++++++++ shared/ui/button/index.tsx | 4 +- 4 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 shared/ui/button/button.stories.tsx diff --git a/.storybook/main.ts b/.storybook/main.ts index fe874bb5..b69a6279 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,4 +1,5 @@ import type { StorybookConfig } from '@storybook/nextjs' +import path from 'path' const config: StorybookConfig = { stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|mjs|ts|tsx)'], @@ -13,6 +14,41 @@ const config: StorybookConfig = { name: '@storybook/nextjs', options: {}, }, + webpackFinal: async (config) => { + if (config.module?.rules) { + const rules = config.module.rules as Array<any> + const scssRule = rules.find((rule) => rule.test && rule.test.toString().includes('scss')) + + if (scssRule) { + const sassLoader = scssRule.use.find( + (loader: any) => loader && loader.loader && loader.loader.includes('sass-loader') + ) + + if (sassLoader) { + sassLoader.options = { + ...sassLoader.options, + additionalData: ` + @import "@/shared/styles/base/variables"; + @import "@/shared/styles/base/mixins"; + @import "@/shared/styles/base/functions"; + `, + sassOptions: { + includePaths: [path.resolve(__dirname, '..')], + }, + } + } + } + } + + if (config.resolve) { + config.resolve.alias = { + ...config.resolve.alias, + '@': path.resolve(__dirname, '..'), + } + } + + return config + }, } export default config diff --git a/shared/ui/button/button.module.scss b/shared/ui/button/button.module.scss index 0e96536c..752ddf45 100644 --- a/shared/ui/button/button.module.scss +++ b/shared/ui/button/button.module.scss @@ -35,13 +35,16 @@ &.outline { background: transparent; + + &.disabled { + background: $color-gray-200; + } } &.filled { background: $color-gray-800; color: $color-white; } - &.disabled { background: $color-gray-600; cursor: not-allowed; diff --git a/shared/ui/button/button.stories.tsx b/shared/ui/button/button.stories.tsx new file mode 100644 index 00000000..70ba74e2 --- /dev/null +++ b/shared/ui/button/button.stories.tsx @@ -0,0 +1,99 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import { Button } from './index' + +const meta = { + title: 'Components/Button', + component: Button, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + children: { control: 'text' }, + size: { + control: 'select', + options: ['small', 'medium', 'large'], + }, + variant: { + control: 'select', + options: ['outline', 'filled'], + }, + disabled: { + control: 'boolean', + }, + onClick: { action: 'clicked' }, + }, + args: { + children: 'contents', + size: 'medium', + variant: 'outline', + disabled: false, + }, +} satisfies Meta<typeof Button> + +export default meta +type StoryType = StoryObj<typeof Button> + +export const Default: StoryType = { + args: {}, +} + +export const Small: StoryType = { + args: { + children: 'small', + size: 'small', + }, +} + +export const Medium: StoryType = { + args: { + children: 'meduim', + size: 'medium', + }, +} + +export const Large: StoryType = { + args: { + children: 'large', + size: 'large', + }, +} + +export const Outline: StoryType = { + args: { + children: 'outline', + size: 'medium', + variant: 'outline', + }, +} + +export const Filled: StoryType = { + args: { + children: 'filled', + size: 'medium', + variant: 'filled', + }, +} + +export const Disabled: StoryType = { + args: { + children: 'disabled', + size: 'medium', + variant: 'filled', + disabled: true, + }, +} + +export const GroupExample: StoryType = { + render: () => ( + <div style={{ display: 'flex', gap: '8px' }}> + <Button size="medium" variant="outline"> + 취소 + </Button> + <Button size="medium" variant="filled"> + 확인 + </Button> + </div> + ), +} diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index 3dd50e6e..29aceb61 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -12,13 +12,13 @@ type ButtonSizeType = 'small' | 'medium' | 'large' type ButtonVariantType = 'outline' | 'filled' interface ButtonProps extends ComponentProps<'button'> { - size: ButtonSizeType + size?: ButtonSizeType variant?: ButtonVariantType } export const Button = ({ children, - size, + size = 'medium', variant = 'outline', disabled = false, className, From cc938ba87e5a3d7d1f58619310bb56f44f2f8552 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 15:32:12 +0900 Subject: [PATCH 043/648] =?UTF-8?q?feat:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=EC=97=90=20reset=20=EC=A0=81=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EA=B3=A0=20Pretendard=20=ED=8F=B0=ED=8A=B8=EA=B0=80=20?= =?UTF-8?q?=EC=A0=95=EC=83=81=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=82=98?= =?UTF-8?q?=EC=98=A4=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview-head.html | 13 +++++++++++++ .storybook/preview.ts | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 .storybook/preview-head.html diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 00000000..11e11cb8 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,13 @@ +<style> + @font-face { + font-family: 'Pretendard'; + src: url('/fonts/PretendardVariable.woff2') format('woff2'); + font-weight: 500 700; + font-style: normal; + font-display: swap; + } + + :root { + --font-pretendard: 'Pretendard'; + } +</style> diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 25953420..23e07eb9 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,5 +1,7 @@ import type { Preview } from '@storybook/react' +import '@/shared/styles/global.scss' + const preview: Preview = { parameters: { controls: { From d8af21cb8ccc86b54b35c8337512074fd933310d Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 15:51:17 +0900 Subject: [PATCH 044/648] =?UTF-8?q?feat:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=EC=97=90=EC=84=9C=EB=8F=84=20public=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EC=9D=98=20=ED=8F=B0=ED=8A=B8=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=97=90=20=EC=A0=91=EA=B7=BC=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=ED=95=A8=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/.storybook/main.ts b/.storybook/main.ts index b69a6279..4a04c546 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -3,6 +3,7 @@ import path from 'path' const config: StorybookConfig = { stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|mjs|ts|tsx)'], + staticDirs: ['../public'], addons: [ '@storybook/addon-onboarding', '@storybook/addon-essentials', From ccf2df412e2a566a4d194df403d63844c6dbda88 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 16:25:47 +0900 Subject: [PATCH 045/648] =?UTF-8?q?feat:=20=EB=A7=81=ED=81=AC=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/button/index.tsx | 4 +- shared/ui/link-button/index.tsx | 32 +++++++ shared/ui/link-button/link-button.module.scss | 44 ++++++++++ shared/ui/link-button/link-button.stories.tsx | 88 +++++++++++++++++++ 4 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 shared/ui/link-button/index.tsx create mode 100644 shared/ui/link-button/link-button.module.scss create mode 100644 shared/ui/link-button/link-button.stories.tsx diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index 29aceb61..3248fabf 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -8,8 +8,8 @@ import styles from './button.module.scss' const cx = classNames.bind(styles) -type ButtonSizeType = 'small' | 'medium' | 'large' -type ButtonVariantType = 'outline' | 'filled' +export type ButtonSizeType = 'small' | 'medium' | 'large' +export type ButtonVariantType = 'outline' | 'filled' interface ButtonProps extends ComponentProps<'button'> { size?: ButtonSizeType diff --git a/shared/ui/link-button/index.tsx b/shared/ui/link-button/index.tsx new file mode 100644 index 00000000..c57915dc --- /dev/null +++ b/shared/ui/link-button/index.tsx @@ -0,0 +1,32 @@ +'use client' + +import { ComponentProps } from 'react' + +import { Url } from 'next/dist/shared/lib/router/router' +import Link from 'next/link' + +import classNames from 'classnames/bind' + +import { ButtonSizeType, ButtonVariantType } from '../button' +import styles from './link-button.module.scss' + +const cx = classNames.bind(styles) + +interface Props extends Omit<ComponentProps<typeof Link>, 'href'> { + size?: ButtonSizeType + variant?: ButtonVariantType + href: Url +} + +export const LinkButton = ({ + children, + size = 'medium', + variant = 'outline', + className, + href, + ...props +}: Props) => ( + <Link href={href} className={cx('button', size, variant, className)} {...props}> + {children} + </Link> +) diff --git a/shared/ui/link-button/link-button.module.scss b/shared/ui/link-button/link-button.module.scss new file mode 100644 index 00000000..9f7431d1 --- /dev/null +++ b/shared/ui/link-button/link-button.module.scss @@ -0,0 +1,44 @@ +.button { + display: inline-flex; + justify-content: center; + align-items: center; + gap: 10px; + border: 1px solid $color-gray-800; + color: $color-gray-800; + line-height: 132%; + + &.small { + padding: 12px 24px; + border-radius: 19.5px; + border-width: 1.5px; + font-size: $text-c1; + font-weight: $text-medium; + letter-spacing: -0.24px; + } + + &.medium { + padding: 12px 24px; + border-radius: 25px; + font-size: $text-b2; + font-weight: $text-semibold; + letter-spacing: -0.36px; + } + + &.large { + width: 400px; + padding: 18px 36px; + border-radius: 60px; + border-width: 1.2px; + font-size: $text-b1; + font-weight: $text-semibold; + } + + &.outline { + background: transparent; + } + + &.filled { + background: $color-gray-800; + color: $color-white; + } +} diff --git a/shared/ui/link-button/link-button.stories.tsx b/shared/ui/link-button/link-button.stories.tsx new file mode 100644 index 00000000..0895d836 --- /dev/null +++ b/shared/ui/link-button/link-button.stories.tsx @@ -0,0 +1,88 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import { LinkButton } from './index' + +const meta = { + title: 'Components/LinkButton', + component: LinkButton, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + children: { control: 'text' }, + size: { + control: 'select', + options: ['small', 'medium', 'large'], + }, + variant: { + control: 'select', + options: ['outline', 'filled'], + }, + href: { control: 'text' }, + onClick: { action: 'clicked' }, + }, + args: { + children: 'contents', + size: 'medium', + variant: 'outline', + href: '/example', + }, +} satisfies Meta<typeof LinkButton> + +export default meta +type StoryType = StoryObj<typeof LinkButton> + +export const Default: StoryType = { + args: {}, +} + +export const Small: StoryType = { + args: { + children: 'small', + size: 'small', + }, +} + +export const Medium: StoryType = { + args: { + children: 'meduim', + size: 'medium', + }, +} + +export const Large: StoryType = { + args: { + children: 'large', + size: 'large', + }, +} + +export const Outline: StoryType = { + args: { + children: 'outline', + size: 'medium', + variant: 'outline', + }, +} + +export const Filled: StoryType = { + args: { + children: 'filled', + size: 'medium', + variant: 'filled', + }, +} + +export const GroupExample: StoryType = { + render: () => ( + <div style={{ display: 'flex', gap: '8px' }}> + <LinkButton size="medium" variant="outline" href="/cancel"> + 로그인 + </LinkButton> + <LinkButton size="medium" variant="filled" href="/confirm"> + 회원가입 + </LinkButton> + </div> + ), +} From c10868830c4897c912f86700d51129251d766bd2 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 16:29:19 +0900 Subject: [PATCH 046/648] =?UTF-8?q?fix:=20ButtonProps=EB=A5=BC=20Props?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/button/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index 3248fabf..8f2637ff 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -11,7 +11,7 @@ const cx = classNames.bind(styles) export type ButtonSizeType = 'small' | 'medium' | 'large' export type ButtonVariantType = 'outline' | 'filled' -interface ButtonProps extends ComponentProps<'button'> { +interface Props extends ComponentProps<'button'> { size?: ButtonSizeType variant?: ButtonVariantType } @@ -23,7 +23,7 @@ export const Button = ({ disabled = false, className, ...props -}: ButtonProps) => ( +}: Props) => ( <button className={cx('button', size, variant, { disabled }, className)} disabled={disabled} From acb93d9606d081d3b5f49a8cdca47df6d8f063df Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 16:36:21 +0900 Subject: [PATCH 047/648] =?UTF-8?q?feat:=20onClick=EC=9D=84=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=ED=95=84=EC=88=98=20=EC=86=8D=EC=84=B1=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=AA=85=EC=8B=9C=EC=A0=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/button/button.stories.tsx | 4 ++-- shared/ui/button/index.tsx | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/shared/ui/button/button.stories.tsx b/shared/ui/button/button.stories.tsx index 70ba74e2..9a71d7b5 100644 --- a/shared/ui/button/button.stories.tsx +++ b/shared/ui/button/button.stories.tsx @@ -88,10 +88,10 @@ export const Disabled: StoryType = { export const GroupExample: StoryType = { render: () => ( <div style={{ display: 'flex', gap: '8px' }}> - <Button size="medium" variant="outline"> + <Button size="medium" variant="outline" onClick={() => {}}> 취소 </Button> - <Button size="medium" variant="filled"> + <Button size="medium" variant="filled" onClick={() => {}}> 확인 </Button> </div> diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index 8f2637ff..5baf0728 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -14,6 +14,7 @@ export type ButtonVariantType = 'outline' | 'filled' interface Props extends ComponentProps<'button'> { size?: ButtonSizeType variant?: ButtonVariantType + onClick: () => void } export const Button = ({ @@ -21,11 +22,13 @@ export const Button = ({ size = 'medium', variant = 'outline', disabled = false, + onClick, className, ...props }: Props) => ( <button className={cx('button', size, variant, { disabled }, className)} + onClick={onClick} disabled={disabled} {...props} > From 3101dc5dee8146f08163093ee59e35c814410d14 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 13 Nov 2024 17:08:29 +0900 Subject: [PATCH 048/648] =?UTF-8?q?chore:=20=EB=AA=A8=EB=93=A0=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20svg=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/bars.svg | 5 +++++ public/icons/category.svg | 10 +++++----- public/icons/change.svg | 6 +++--- public/icons/check.svg | 3 +++ public/icons/checkbox.svg | 11 +++++++++++ public/icons/chevron-down.svg | 3 +++ public/icons/chevron-left.svg | 3 +++ public/icons/chevron-right.svg | 3 +++ public/icons/chevron-up.svg | 3 +++ public/icons/daily-graph.svg | 4 ++++ public/icons/favorite.svg | 4 ++-- public/icons/file.svg | 5 +++++ public/icons/logo.svg | 5 ----- public/icons/money.svg | 3 +++ public/icons/monthly-graph.svg | 5 +++++ public/icons/notice.svg | 4 ++-- public/icons/open.svg | 11 +++++++++++ public/icons/pencil.svg | 4 ++++ public/icons/profile.svg | 6 +++--- public/icons/question.svg | 4 ++-- public/icons/search.svg | 4 ++++ public/icons/signout.svg | 4 ++-- public/icons/statistics.svg | 14 ++++++++++++++ public/icons/strategy-ranking.svg | 4 ++-- public/icons/strategy.svg | 8 ++++---- public/icons/text-logo.svg | 13 ------------- public/icons/traders.svg | 10 +++++----- 27 files changed, 111 insertions(+), 48 deletions(-) create mode 100644 public/icons/bars.svg create mode 100644 public/icons/check.svg create mode 100644 public/icons/checkbox.svg create mode 100644 public/icons/chevron-down.svg create mode 100644 public/icons/chevron-left.svg create mode 100644 public/icons/chevron-right.svg create mode 100644 public/icons/chevron-up.svg create mode 100644 public/icons/daily-graph.svg create mode 100644 public/icons/file.svg delete mode 100644 public/icons/logo.svg create mode 100644 public/icons/money.svg create mode 100644 public/icons/monthly-graph.svg create mode 100644 public/icons/open.svg create mode 100644 public/icons/pencil.svg create mode 100644 public/icons/search.svg create mode 100644 public/icons/statistics.svg delete mode 100644 public/icons/text-logo.svg diff --git a/public/icons/bars.svg b/public/icons/bars.svg new file mode 100644 index 00000000..9b277458 --- /dev/null +++ b/public/icons/bars.svg @@ -0,0 +1,5 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<rect x="3" y="6" width="18" height="2" rx="1" fill="currentColor"/> +<rect x="3" y="11" width="13" height="2" rx="1" fill="currentColor"/> +<rect x="3" y="16" width="7" height="2" rx="1" fill="currentColor"/> +</svg> diff --git a/public/icons/category.svg b/public/icons/category.svg index a3bf1f01..4b89fd49 100644 --- a/public/icons/category.svg +++ b/public/icons/category.svg @@ -1,6 +1,6 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<rect x="3" y="3" width="8.1" height="8.1" rx="1" fill="#797979"/> -<rect x="3" y="12.8999" width="8.1" height="8.1" rx="1" fill="#797979"/> -<rect x="12.9004" y="3" width="8.1" height="8.1" rx="1" fill="#797979"/> -<rect x="12.9004" y="12.8999" width="8.1" height="8.1" rx="1" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<rect x="3" y="3" width="8.1" height="8.1" rx="1" fill="currentColor"/> +<rect x="3" y="12.8999" width="8.1" height="8.1" rx="1" fill="currentColor"/> +<rect x="12.9004" y="3" width="8.1" height="8.1" rx="1" fill="currentColor"/> +<rect x="12.9004" y="12.8999" width="8.1" height="8.1" rx="1" fill="currentColor"/> </svg> diff --git a/public/icons/change.svg b/public/icons/change.svg index 6e6ac5a7..bc6704dc 100644 --- a/public/icons/change.svg +++ b/public/icons/change.svg @@ -1,4 +1,4 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M17.7071 5L22.2071 9.5C22.4931 9.786 22.5787 10.2161 22.4239 10.5898C22.2691 10.9635 21.9045 11.2071 21.5 11.2071H6V9.20711H19.0858L16.2929 6.41421L17.7071 5Z" fill="#797979"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M6.79289 19.4141L2.29289 14.9141C2.0069 14.6281 1.92134 14.1979 2.07612 13.8243C2.2309 13.4506 2.59554 13.207 3 13.207H18.5V15.207H5.41421L8.20711 17.9998L6.79289 19.4141Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M17.7071 5L22.2071 9.5C22.4931 9.786 22.5787 10.2161 22.4239 10.5898C22.2691 10.9635 21.9045 11.2071 21.5 11.2071H6V9.20711H19.0858L16.2929 6.41421L17.7071 5Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M6.79289 19.4141L2.29289 14.9141C2.0069 14.6281 1.92134 14.1979 2.07612 13.8243C2.2309 13.4506 2.59554 13.207 3 13.207H18.5V15.207H5.41421L8.20711 17.9998L6.79289 19.4141Z" fill="currentColor"/> </svg> diff --git a/public/icons/check.svg b/public/icons/check.svg new file mode 100644 index 00000000..7484da4d --- /dev/null +++ b/public/icons/check.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M20.9305 6.38095L9.72374 18.1213L4 12.3976L5.41421 10.9834L9.69047 15.2596L19.4838 5L20.9305 6.38095Z" fill="currentColor"/> +</svg> diff --git a/public/icons/checkbox.svg b/public/icons/checkbox.svg new file mode 100644 index 00000000..10a3bae5 --- /dev/null +++ b/public/icons/checkbox.svg @@ -0,0 +1,11 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0_1175_4989)"> +<rect x="5" y="5" width="15" height="15" rx="2" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M17 9.73671L11.0427 16L8 12.9465L8.75178 12.192L11.025 14.4733L16.2309 9L17 9.73671Z" fill="white"/> +</g> +<defs> +<clipPath id="clip0_1175_4989"> +<rect width="24" height="24" fill="white" transform="matrix(0 -1 1 0 0 24)"/> +</clipPath> +</defs> +</svg> diff --git a/public/icons/chevron-down.svg b/public/icons/chevron-down.svg new file mode 100644 index 00000000..15c83bac --- /dev/null +++ b/public/icons/chevron-down.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M11.6812 17.832C12.0793 18.4 12.9207 18.4 13.3188 17.832L18.7577 10.0741C19.2223 9.41134 18.7482 8.5 17.9389 8.5H7.06112C6.25177 8.5 5.77769 9.41134 6.2423 10.074L11.6812 17.832Z" fill="currentColor"/> +</svg> diff --git a/public/icons/chevron-left.svg b/public/icons/chevron-left.svg new file mode 100644 index 00000000..3e0fd795 --- /dev/null +++ b/public/icons/chevron-left.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M6.16796 11.6812C5.60003 12.0793 5.60003 12.9207 6.16796 13.3188L13.9259 18.7577C14.5887 19.2223 15.5 18.7482 15.5 17.9389V7.06112C15.5 6.25177 14.5887 5.77769 13.926 6.2423L6.16796 11.6812Z" fill="currentColor"/> +</svg> diff --git a/public/icons/chevron-right.svg b/public/icons/chevron-right.svg new file mode 100644 index 00000000..93789dc2 --- /dev/null +++ b/public/icons/chevron-right.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M17.832 11.6812C18.4 12.0793 18.4 12.9207 17.832 13.3188L10.0741 18.7577C9.41134 19.2223 8.5 18.7482 8.5 17.9389V7.06112C8.5 6.25177 9.41134 5.77769 10.074 6.2423L17.832 11.6812Z" fill="currentColor"/> +</svg> diff --git a/public/icons/chevron-up.svg b/public/icons/chevron-up.svg new file mode 100644 index 00000000..93e8ca08 --- /dev/null +++ b/public/icons/chevron-up.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M12.3188 6.16796C11.9207 5.60003 11.0793 5.60003 10.6812 6.16796L5.2423 13.9259C4.77769 14.5887 5.25177 15.5 6.06112 15.5H16.9389C17.7482 15.5 18.2223 14.5887 17.7577 13.926L12.3188 6.16796Z" fill="currentColor"/> +</svg> diff --git a/public/icons/daily-graph.svg b/public/icons/daily-graph.svg new file mode 100644 index 00000000..b918c604 --- /dev/null +++ b/public/icons/daily-graph.svg @@ -0,0 +1,4 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M4 4V20.5H20.5" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> +<path d="M4 20.5L10.5 9L16 12.5L20 6" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> +</svg> diff --git a/public/icons/favorite.svg b/public/icons/favorite.svg index 8e891487..68b247bd 100644 --- a/public/icons/favorite.svg +++ b/public/icons/favorite.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4C6.44772 4 6 4.44772 6 5V19.5603L12 16.5L18 19.4994V5C18 4.44772 17.5523 4 17 4H7Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4C6.44772 4 6 4.44772 6 5V19.5603L12 16.5L18 19.4994V5C18 4.44772 17.5523 4 17 4H7Z" fill="currentColor"/> </svg> diff --git a/public/icons/file.svg b/public/icons/file.svg new file mode 100644 index 00000000..f71ccaf4 --- /dev/null +++ b/public/icons/file.svg @@ -0,0 +1,5 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M3 9.26172H20.3478V18.0008C20.3478 18.5531 19.9001 19.0008 19.3478 19.0008H4C3.44771 19.0008 3 18.5531 3 18.0008V9.26172Z" fill="currentColor"/> +<rect x="3" y="6.21875" width="17.3478" height="1.82609" fill="currentColor"/> +<rect x="3" y="5" width="6.08696" height="3.04348" fill="currentColor"/> +</svg> diff --git a/public/icons/logo.svg b/public/icons/logo.svg deleted file mode 100644 index c5ff8c35..00000000 --- a/public/icons/logo.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg width="40" height="24" viewBox="0 0 40 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M11.75 1H20.421L27.1169 15.5503V23.5994H18.2051L11.75 9.31447V1Z" fill="#FF7752"/> -<path d="M23.9297 1H32.657L39.3964 15.5503V23.5994H30.4267L23.9297 9.31447V1Z" fill="#FF7752"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M7.49668 1H1V9.23233L1.05415 9.41406H5.51337V23.5988H5.28057L5.28075 23.5994H14.1926V15.5503L7.49668 1Z" fill="#FF7752"/> -</svg> diff --git a/public/icons/money.svg b/public/icons/money.svg new file mode 100644 index 00000000..f45d2f57 --- /dev/null +++ b/public/icons/money.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5138 3.48828H12.5817V5.40136C14.5888 5.5962 15.9229 7.04084 15.9503 9.02808H14.1044C14.0084 7.93682 13.2331 7.30825 12.0812 7.31698C10.8629 7.30825 10.1393 8.00666 10.1319 8.95824C10.1245 10.0233 11.077 10.4598 11.9631 10.7043L12.9673 11.0185C14.5622 11.455 16.098 12.4503 16.098 14.6502C16.098 16.71 14.8174 18.1672 12.5817 18.364V20.5117H11.5138V18.3636C9.33742 18.1712 7.97718 16.7875 7.90198 14.4756H9.79224C9.88084 15.7939 10.8186 16.4399 12.0369 16.4486C13.2996 16.4399 14.1708 15.7153 14.1782 14.6328C14.1708 13.6376 13.4029 13.2185 12.2437 12.8693L11.0327 12.5026C9.27537 11.9614 8.18256 10.9138 8.18256 9.09792C8.18256 7.06545 9.5688 5.65392 11.5138 5.41489V3.48828Z" fill="currentColor"/> +</svg> diff --git a/public/icons/monthly-graph.svg b/public/icons/monthly-graph.svg new file mode 100644 index 00000000..bc3d6caf --- /dev/null +++ b/public/icons/monthly-graph.svg @@ -0,0 +1,5 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M3.75 3.5V20H20.25" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> +<path d="M3.75 20L10.25 8.5L15.75 12L19.75 5.5" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> +<path d="M7.25098 20L11.1729 13.5L17.5496 17.1267L19.5068 13.9643V20" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> +</svg> diff --git a/public/icons/notice.svg b/public/icons/notice.svg index ce40bfd2..0c004f7c 100644 --- a/public/icons/notice.svg +++ b/public/icons/notice.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M21 12C21 16.9688 16.9688 21 12 21C7.03125 21 3 16.9688 3 12C3 7.03125 7.03125 3 12 3C16.9688 3 21 7.03125 21 12ZM10.5 7L11 13H13L13.5 7H10.5ZM11 17V15H13V17H11Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M21 12C21 16.9688 16.9688 21 12 21C7.03125 21 3 16.9688 3 12C3 7.03125 7.03125 3 12 3C16.9688 3 21 7.03125 21 12ZM10.5 7L11 13H13L13.5 7H10.5ZM11 17V15H13V17H11Z" fill="currentColor"/> </svg> diff --git a/public/icons/open.svg b/public/icons/open.svg new file mode 100644 index 00000000..0dd2fa8d --- /dev/null +++ b/public/icons/open.svg @@ -0,0 +1,11 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0_1175_5001)"> +<path d="M19 7L12 16L5 7" stroke="currentColor" stroke-width="1.5"/> +<path d="M19 7L12 16L9 12L5.5 7" stroke="currentColor"/> +</g> +<defs> +<clipPath id="clip0_1175_5001"> +<rect width="24" height="24" fill="white" transform="matrix(0 1 -1 0 24 0)"/> +</clipPath> +</defs> +</svg> diff --git a/public/icons/pencil.svg b/public/icons/pencil.svg new file mode 100644 index 00000000..b905b734 --- /dev/null +++ b/public/icons/pencil.svg @@ -0,0 +1,4 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M19.6777 8.36328L16.1421 4.82775L18.2635 2.70643C18.654 2.3159 19.2871 2.3159 19.6777 2.70643L21.799 4.82775C22.1895 5.21827 22.1895 5.85144 21.799 6.24196L19.6777 8.36328Z" fill="currentColor"/> +<path d="M15.435 5.53516L18.9706 9.07069L7.35109 20.6902L3.76777 20.738L3.81555 17.1546L15.435 5.53516Z" fill="currentColor"/> +</svg> diff --git a/public/icons/profile.svg b/public/icons/profile.svg index bd5258fd..10ec4f74 100644 --- a/public/icons/profile.svg +++ b/public/icons/profile.svg @@ -1,4 +1,4 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<circle cx="12" cy="9" r="4" fill="#797979"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M19 20H5V18C5 15.7909 6.79086 14 9 14H15C17.2091 14 19 15.7909 19 18V20Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<circle cx="12" cy="9" r="4" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M19 20H5V18C5 15.7909 6.79086 14 9 14H15C17.2091 14 19 15.7909 19 18V20Z" fill="currentColor"/> </svg> diff --git a/public/icons/question.svg b/public/icons/question.svg index 77d39635..e5afbc18 100644 --- a/public/icons/question.svg +++ b/public/icons/question.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M3 5C3 4.44772 3.44772 4 4 4H20C20.5523 4 21 4.44772 21 5V18C21 18.5523 20.5523 19 20 19H5.54545L3 21V5ZM17 8H7V9H17V8ZM7 11H17V12H7V11ZM13 14H7V15H13V14Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3 5C3 4.44772 3.44772 4 4 4H20C20.5523 4 21 4.44772 21 5V18C21 18.5523 20.5523 19 20 19H5.54545L3 21V5ZM17 8H7V9H17V8ZM7 11H17V12H7V11ZM13 14H7V15H13V14Z" fill="currentColor"/> </svg> diff --git a/public/icons/search.svg b/public/icons/search.svg new file mode 100644 index 00000000..0012695c --- /dev/null +++ b/public/icons/search.svg @@ -0,0 +1,4 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M11.3333 16.6667C14.2789 16.6667 16.6667 14.2789 16.6667 11.3333C16.6667 8.38781 14.2789 6 11.3333 6C8.38781 6 6 8.38781 6 11.3333C6 14.2789 8.38781 16.6667 11.3333 16.6667Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M17.9996 18.0016L15.0996 15.1016" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/public/icons/signout.svg b/public/icons/signout.svg index 412e14fe..80b62a14 100644 --- a/public/icons/signout.svg +++ b/public/icons/signout.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M3 5C3 4.44772 3.44772 4 4 4H13V6H5V19H13V21H4C3.44772 21 3 20.5523 3 20V5ZM17.0936 11L15.8007 9.70711L17.2149 8.29289L20.922 12L17.2149 15.7071L15.8007 14.2929L17.0936 13H9V11H17.0936Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3 5C3 4.44772 3.44772 4 4 4H13V6H5V19H13V21H4C3.44772 21 3 20.5523 3 20V5ZM17.0936 11L15.8007 9.70711L17.2149 8.29289L20.922 12L17.2149 15.7071L15.8007 14.2929L17.0936 13H9V11H17.0936Z" fill="currentColor"/> </svg> diff --git a/public/icons/statistics.svg b/public/icons/statistics.svg new file mode 100644 index 00000000..9315fb69 --- /dev/null +++ b/public/icons/statistics.svg @@ -0,0 +1,14 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<mask id="path-1-inside-1_1175_5005" fill="white"> +<rect x="12" y="3" width="5" height="11" rx="1" transform="rotate(90 12 3)"/> +</mask> +<rect x="12" y="3" width="5" height="11" rx="1" transform="rotate(90 12 3)" stroke="currentColor" stroke-width="3" mask="url(#path-1-inside-1_1175_5005)"/> +<mask id="path-2-inside-2_1175_5005" fill="white"> +<rect x="21" y="10" width="5" height="20" rx="1" transform="rotate(90 21 10)"/> +</mask> +<rect x="21" y="10" width="5" height="20" rx="1" transform="rotate(90 21 10)" stroke="currentColor" stroke-width="3" mask="url(#path-2-inside-2_1175_5005)"/> +<mask id="path-3-inside-3_1175_5005" fill="white"> +<rect x="15" y="17" width="5" height="14" rx="1" transform="rotate(90 15 17)"/> +</mask> +<rect x="15" y="17" width="5" height="14" rx="1" transform="rotate(90 15 17)" stroke="currentColor" stroke-width="3" mask="url(#path-3-inside-3_1175_5005)"/> +</svg> diff --git a/public/icons/strategy-ranking.svg b/public/icons/strategy-ranking.svg index 35fac62a..3a8bf9cb 100644 --- a/public/icons/strategy-ranking.svg +++ b/public/icons/strategy-ranking.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M5 4C3.89543 4 3 4.89543 3 6V10.3H8.07692C8.36343 10.3 8.58296 10.4553 8.68754 10.5377C8.82455 10.6456 8.96228 10.7909 9.1014 10.9688C9.38084 11.3261 9.71474 11.8782 10.0971 12.7067L10.1315 12.7813L10.1401 12.7998L10.1444 12.809L10.1486 12.8183L10.1571 12.8367L10.1656 12.8551L10.2327 13.0004L10.2656 13.0718L10.2697 13.0807L10.2738 13.0895L10.2779 13.0984L10.282 13.1072L10.2901 13.1248L10.2942 13.1336L10.2982 13.1424L10.3623 13.2813L10.3663 13.2898L10.3702 13.2984L10.3742 13.3069L10.3781 13.3155L10.382 13.324L10.386 13.3325L10.3899 13.341L10.3938 13.3495L10.3977 13.3579L10.4016 13.3664L10.4055 13.3748L10.4094 13.3833L10.4133 13.3917L10.4171 13.4001L10.421 13.4085L10.4249 13.4168L10.4287 13.4252L10.4326 13.4335L10.4364 13.4419L10.4403 13.4502L10.4556 13.4834L10.4594 13.4916L10.4632 13.4999L10.467 13.5081L10.4708 13.5163L10.4859 13.5491L10.5009 13.5816L10.5159 13.614L10.5233 13.6301L10.527 13.6381L10.5307 13.6461L10.5454 13.678L10.5601 13.7097L10.5746 13.7412L10.6034 13.8035L10.6105 13.819L10.6176 13.8344L10.6211 13.8421L10.6247 13.8497L10.6317 13.865L10.6388 13.8803L10.6458 13.8954L10.6493 13.903L10.6528 13.9106L10.6597 13.9256L10.7145 14.0443L10.8192 14.2711L10.8255 14.2848L10.8318 14.2985L10.8349 14.3053L10.8381 14.3121L10.8412 14.3188L10.8443 14.3256L10.8462 14.3296L13.9029 7.70666C14.0245 7.44328 14.2959 7.28216 14.5853 7.30157C14.8747 7.32097 15.1222 7.51687 15.2075 7.7941L15.2075 7.79414L15.2075 7.79417L15.2077 7.79474L15.2085 7.79747L15.2122 7.80919L15.2269 7.85628C15.24 7.89772 15.2592 7.95852 15.2839 8.0348C15.3332 8.18749 15.4038 8.40153 15.4884 8.64602C15.6595 9.14015 15.8812 9.73886 16.0971 10.2067C16.2196 10.4719 16.2866 10.6145 16.3462 10.7113C16.3619 10.7369 16.3733 10.7526 16.3804 10.7617C16.4007 10.7674 16.451 10.7785 16.554 10.7865C16.7226 10.7995 16.9502 10.8 17.3077 10.8H17.3207H17.3336H17.3465H17.3594H17.3723H17.3851H17.398H17.4108H17.4236H17.4363H17.449H17.4617H17.4744H17.4871H17.4997H17.5123H17.5249H17.5375H17.55H17.5625H17.575H17.5875H17.5999H17.6124H17.6248H17.6371H17.6495H17.6618H17.6741H17.6864H17.6986H17.7109H17.7231H17.7353H17.7474H17.7595H17.7717H17.7837H17.7958H17.8079H17.8199H17.8319H17.8438H17.8558H17.8677H17.8796H17.8915H17.9033H17.9152H17.927H17.9388H17.9505H17.9622H17.974H17.9856H17.9973H18.009H18.0206H18.0322H18.0437H18.0553H18.0668H18.0783H18.0898H18.1012H18.1127H18.1241H18.1355H18.1468H18.1582H18.1695H18.1808H18.192H18.2033H18.2145H18.2257H18.2369H18.248H18.2591H18.2702H18.2813H18.2924H18.3034H18.3144H18.3254H18.3364H18.3473H18.3582H18.3691H18.38H18.3908H18.4017H18.4125H18.4232H18.434H18.4447H18.4554H18.4661H18.4768H18.4874H18.498H18.5086H18.5192H18.5297H18.5403H18.5508H18.5612H18.5717H18.5821H18.5925H18.6029H18.6133H18.6236H18.6339H18.6442H18.6545H18.6648H18.675H18.6852H18.6954H18.7055H18.7156H18.7258H18.7358H18.7459H18.7559H18.766H18.776H18.7859H18.7959H18.8058H18.8157H18.8256H18.8355H18.8453H18.8551H18.8649H18.8747H18.8844H18.8941H18.9038H18.9135H18.9231H18.9328H18.9424H18.952H18.9615H18.9711H18.9806H18.9901H18.9995H19.009H19.0184H19.0278H19.0372H19.0466H19.0559H19.0652H19.0745H19.0838H19.093H19.1022H19.1114H19.1206H19.1298H19.1389H19.148H19.1571H19.1661H19.1752H19.1842H19.1932H19.2022H19.2111H19.22H19.229H19.2378H19.2467H19.2555H19.2644H19.2731H19.2819H19.2907H19.2994H19.3081H19.3168H19.3254H19.3341H19.3427H19.3513H19.3598H19.3684H19.3769H19.3854H19.3939H19.4024H19.4108H19.4192H19.4276H19.436H19.4443H19.4526H19.4609H19.4692H19.4775H19.4857H19.4939H19.5021H19.5103H19.5184H19.5265H19.5346H19.5427H19.5508H19.5588H19.5668H19.5748H19.5828H19.5907H19.5986H19.6066H19.6144H19.6223H19.6301H19.6379H19.6457H19.6535H19.6612H19.669H19.6767H19.6844H19.692H19.6996H19.7073H19.7149H19.7224H19.73H19.7375H19.745H19.7525H19.7599H19.7674H19.7748H19.7822H19.7896H19.7969H19.8043H19.8116H19.8188H19.8261H19.8334H19.8406H19.8478H19.8549H19.8621H19.8692H19.8763H19.8834H19.8905H19.8975H19.9046H19.9116H19.9185H19.9255H19.9324H19.9394H19.9462H19.9531H19.96H19.9668H19.9736H19.9804H19.9871H19.9939H20.0006H20.0073H20.014H20.0206H20.0273H20.0339H20.0405H20.047H20.0536H20.0601H20.0666H20.0731H20.0795H20.086H20.0924H20.0988H20.1052H20.1115H20.1178H20.1242H20.1304H20.1367H20.143H20.1492H20.1554H20.1616H20.1677H20.1739H20.18H20.1861H20.1921H20.1982H20.2042H20.2102H20.2162H20.2222H20.2281H20.234H20.2399H20.2458H20.2517H20.2575H20.2633H20.2691H20.2749H20.2806H20.2864H20.2921H20.2978H20.3034H20.3091H20.3147H20.3203H20.3259H20.3315H20.337H20.3425H20.348H20.3535H20.3589H20.3644H20.3698H20.3752H20.3805H20.3859H20.3912H20.3965H20.4018H20.4071H20.4123H20.4175H20.4227H20.4279H20.4331H20.4382H20.4433H20.4484H20.4535H20.4586H20.4636H20.4686H20.4736H20.4786H20.4835H20.4884H20.4933H20.4982H20.5031H20.5079H20.5128H20.5176H20.5223H20.5271H20.5318H20.5365H20.5412H20.5459H20.5506H20.5552H20.5598H20.5644H20.569H20.5735H20.5781H20.5826H20.5871H20.5915H20.596H20.6004H20.6048H20.6092H20.6136H20.6179H20.6222H20.6265H20.6308H20.6351H20.6393H20.6435H20.6477H20.6519H20.656H20.6602H20.6643H20.6684H20.6725H20.6765H20.6806H20.6846H20.6886H20.6925H20.6965H20.7004H20.7043H20.7082H20.7121H20.7159H20.7197H20.7235H20.7273H20.7311H20.7348H20.7386H20.7423H20.746H20.7496H20.7533H20.7569H20.7605H20.7641H20.7676H20.7712H20.7747H20.7782H20.7817H20.7851H20.7885H20.792H20.7954H20.7987H20.8021H20.8054H20.8087H20.812H20.8153H20.8186H20.8218H20.825H20.8282H20.8314H20.8345H20.8377H20.8408H20.8439H20.8469H20.85H20.853H20.856H20.859H20.862H20.8649H20.8679H20.8708H20.8737H20.8765H20.8794H20.8822H20.885H20.8878H20.8906H20.8933H20.8961H20.8988H20.9015H20.9041H20.9068H20.9094H20.912H20.9146H20.9172H20.9197H20.9223H20.9248H20.9272H20.9297H20.9322H20.9346H20.937H20.9394H20.9418H20.9441H20.9464H20.9487H20.951H20.9533H20.9555H20.9578H20.96H20.9622H20.9643H20.9665H20.9686H20.9707H20.9728H20.9749H20.9769H20.979H20.981H20.983H20.9849H20.9869H20.9888H20.9907H20.9926H20.9945H20.9964H20.9982H21V6C21 4.89543 20.1046 4 19 4H5ZM21 12.2H20.9982H20.9964H20.9945H20.9926H20.9907H20.9888H20.9869H20.9849H20.983H20.981H20.979H20.9769H20.9749H20.9728H20.9707H20.9686H20.9665H20.9643H20.9622H20.96H20.9578H20.9555H20.9533H20.951H20.9487H20.9464H20.9441H20.9418H20.9394H20.937H20.9346H20.9322H20.9297H20.9272H20.9248H20.9223H20.9197H20.9172H20.9146H20.912H20.9094H20.9068H20.9041H20.9015H20.8988H20.8961H20.8933H20.8906H20.8878H20.885H20.8822H20.8794H20.8765H20.8737H20.8708H20.8679H20.8649H20.862H20.859H20.856H20.853H20.85H20.8469H20.8439H20.8408H20.8377H20.8345H20.8314H20.8282H20.825H20.8218H20.8186H20.8153H20.812H20.8087H20.8054H20.8021H20.7987H20.7954H20.792H20.7885H20.7851H20.7817H20.7782H20.7747H20.7712H20.7676H20.7641H20.7605H20.7569H20.7533H20.7496H20.746H20.7423H20.7386H20.7348H20.7311H20.7273H20.7235H20.7197H20.7159H20.7121H20.7082H20.7043H20.7004H20.6965H20.6925H20.6886H20.6846H20.6806H20.6765H20.6725H20.6684H20.6643H20.6602H20.656H20.6519H20.6477H20.6435H20.6393H20.6351H20.6308H20.6265H20.6222H20.6179H20.6136H20.6092H20.6048H20.6004H20.596H20.5915H20.5871H20.5826H20.5781H20.5735H20.569H20.5644H20.5598H20.5552H20.5506H20.5459H20.5412H20.5365H20.5318H20.5271H20.5223H20.5176H20.5128H20.5079H20.5031H20.4982H20.4933H20.4884H20.4835H20.4786H20.4736H20.4686H20.4636H20.4586H20.4535H20.4484H20.4433H20.4382H20.4331H20.4279H20.4227H20.4175H20.4123H20.4071H20.4018H20.3965H20.3912H20.3859H20.3805H20.3752H20.3698H20.3644H20.3589H20.3535H20.348H20.3425H20.337H20.3315H20.3259H20.3203H20.3147H20.3091H20.3034H20.2978H20.2921H20.2864H20.2806H20.2749H20.2691H20.2633H20.2575H20.2517H20.2458H20.2399H20.234H20.2281H20.2222H20.2162H20.2102H20.2042H20.1982H20.1921H20.1861H20.18H20.1739H20.1677H20.1616H20.1554H20.1492H20.143H20.1367H20.1304H20.1242H20.1178H20.1115H20.1052H20.0988H20.0924H20.086H20.0795H20.0731H20.0666H20.0601H20.0536H20.047H20.0405H20.0339H20.0273H20.0206H20.014H20.0073H20.0006H19.9939H19.9871H19.9804H19.9736H19.9668H19.96H19.9531H19.9462H19.9394H19.9324H19.9255H19.9185H19.9116H19.9046H19.8975H19.8905H19.8834H19.8763H19.8692H19.8621H19.8549H19.8478H19.8406H19.8334H19.8261H19.8188H19.8116H19.8043H19.7969H19.7896H19.7822H19.7748H19.7674H19.7599H19.7525H19.745H19.7375H19.73H19.7224H19.7149H19.7073H19.6996H19.692H19.6844H19.6767H19.669H19.6612H19.6535H19.6457H19.6379H19.6301H19.6223H19.6144H19.6066H19.5986H19.5907H19.5828H19.5748H19.5668H19.5588H19.5508H19.5427H19.5346H19.5265H19.5184H19.5103H19.5021H19.4939H19.4857H19.4775H19.4692H19.4609H19.4526H19.4443H19.436H19.4276H19.4192H19.4108H19.4024H19.3939H19.3854H19.3769H19.3684H19.3598H19.3513H19.3427H19.3341H19.3254H19.3168H19.3081H19.2994H19.2907H19.2819H19.2731H19.2644H19.2555H19.2467H19.2378H19.229H19.22H19.2111H19.2022H19.1932H19.1842H19.1752H19.1661H19.1571H19.148H19.1389H19.1298H19.1206H19.1114H19.1022H19.093H19.0838H19.0745H19.0652H19.0559H19.0466H19.0372H19.0278H19.0184H19.009H18.9995H18.9901H18.9806H18.9711H18.9615H18.952H18.9424H18.9328H18.9231H18.9135H18.9038H18.8941H18.8844H18.8747H18.8649H18.8551H18.8453H18.8355H18.8256H18.8157H18.8058H18.7959H18.7859H18.776H18.766H18.7559H18.7459H18.7358H18.7258H18.7156H18.7055H18.6954H18.6852H18.675H18.6648H18.6545H18.6442H18.6339H18.6236H18.6133H18.6029H18.5925H18.5821H18.5717H18.5612H18.5508H18.5403H18.5297H18.5192H18.5086H18.498H18.4874H18.4768H18.4661H18.4554H18.4447H18.434H18.4232H18.4125H18.4017H18.3908H18.38H18.3691H18.3582H18.3473H18.3364H18.3254H18.3144H18.3034H18.2924H18.2813H18.2702H18.2591H18.248H18.2369H18.2257H18.2145H18.2033H18.192H18.1808H18.1695H18.1582H18.1468H18.1355H18.1241H18.1127H18.1012H18.0898H18.0783H18.0668H18.0553H18.0437H18.0322H18.0206H18.009H17.9973H17.9856H17.974H17.9622H17.9505H17.9388H17.927H17.9152H17.9033H17.8915H17.8796H17.8677H17.8558H17.8438H17.8319H17.8199H17.8079H17.7958H17.7837H17.7717H17.7595H17.7474H17.7353H17.7231H17.7109H17.6986H17.6864H17.6741H17.6618H17.6495H17.6371H17.6248H17.6124H17.5999H17.5875H17.575H17.5625H17.55H17.5375H17.5249H17.5123H17.4997H17.4871H17.4744H17.4617H17.449H17.4363H17.4236H17.4108H17.398H17.3851H17.3723H17.3594H17.3465H17.3336H17.3207H17.3077L17.2818 12.2C16.9578 12.2 16.6752 12.2 16.446 12.1823C16.2097 12.164 15.9492 12.1228 15.7051 11.9905C15.4447 11.8495 15.2782 11.647 15.1538 11.445C15.045 11.2681 14.9431 11.0472 14.8394 10.8225L14.826 10.7933C14.6941 10.5076 14.5639 10.1875 14.4445 9.87405L11.4817 16.2933C11.3673 16.5412 11.1192 16.7 10.8462 16.7C10.5731 16.7 10.325 16.5412 10.2106 16.2933L10.2031 16.2772L10.2012 16.273L10.2002 16.2708L10.1992 16.2687L10.1972 16.2644L10.1962 16.2622L10.1952 16.2599L10.1867 16.2417L10.1778 16.2223L10.1585 16.1804L10.1481 16.1579L10.1372 16.1344L10.114 16.0841L10.1079 16.0709L10.1017 16.0575L10.089 16.0298L10.062 15.9714L10.0585 15.9638L10.055 15.9562L10.0514 15.9485L10.0478 15.9407L10.0332 15.909L10.0025 15.8425L9.99456 15.8253L9.99054 15.8166L9.98649 15.8078L9.97001 15.7721L9.9616 15.7539L9.95308 15.7354L9.9357 15.6978L9.93128 15.6882L9.92906 15.6834L9.92684 15.6786L9.92236 15.6689L9.92012 15.664L9.91786 15.6591L9.89958 15.6195L9.88086 15.5789L9.87133 15.5583L9.86653 15.5479L9.86169 15.5374L9.82204 15.4515L9.80155 15.4071L9.79637 15.3959L9.79376 15.3902L9.79115 15.3846L9.78064 15.3618L9.77799 15.3561L9.77534 15.3503L9.77268 15.3446L9.77002 15.3388L9.75929 15.3155L9.73751 15.2683L9.64612 15.0703L9.5979 14.9659L9.57317 14.9123L9.56065 14.8851L9.5575 14.8783L9.55435 14.8715L9.54802 14.8578L9.54485 14.8509L9.54167 14.844L9.53529 14.8302L9.52889 14.8163L9.52246 14.8024L9.4965 14.7462L9.44334 14.631L9.43658 14.6163L9.4332 14.609L9.4298 14.6016L9.41616 14.5721L9.40242 14.5423L9.39551 14.5274L9.39205 14.5198L9.38858 14.5123L9.3606 14.4517L9.34646 14.4211L9.33222 14.3902L9.30344 14.3279L9.28891 14.2964L9.27428 14.2647L9.27061 14.2567L9.26693 14.2488L9.25955 14.2328L9.24472 14.2006L9.21478 14.1358L9.18445 14.0701L9.16914 14.0369L9.15374 14.0035L9.12265 13.9361L9.11873 13.9277L9.11481 13.9192L9.10696 13.9021L9.09117 13.868L9.05932 13.7989L9.05531 13.7903L9.0513 13.7816L9.04728 13.7728L9.04325 13.7641L9.02709 13.7291L8.99449 13.6585L8.99039 13.6496L8.98628 13.6407L8.98217 13.6318L8.97805 13.6229L8.9698 13.605L8.96152 13.587L8.82597 13.2933C8.46988 12.5218 8.18839 12.0739 7.9986 11.8312C7.95341 11.7734 7.91609 11.7308 7.88702 11.7H3V18C3 19.1046 3.89543 20 5 20H19C20.1046 20 21 19.1046 21 18V12.2ZM16.3896 10.7722C16.3896 10.7723 16.3889 10.7717 16.3874 10.7701C16.3889 10.7714 16.3896 10.7722 16.3896 10.7722ZM7.80666 11.6272C7.80657 11.6268 7.81122 11.6296 7.82089 11.6372C7.81157 11.6315 7.80674 11.6277 7.80666 11.6272Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M5 4C3.89543 4 3 4.89543 3 6V10.3H8.07692C8.36343 10.3 8.58296 10.4553 8.68754 10.5377C8.82455 10.6456 8.96228 10.7909 9.1014 10.9688C9.38084 11.3261 9.71474 11.8782 10.0971 12.7067L10.1315 12.7813L10.1401 12.7998L10.1444 12.809L10.1486 12.8183L10.1571 12.8367L10.1656 12.8551L10.2327 13.0004L10.2656 13.0718L10.2697 13.0807L10.2738 13.0895L10.2779 13.0984L10.282 13.1072L10.2901 13.1248L10.2942 13.1336L10.2982 13.1424L10.3623 13.2813L10.3663 13.2898L10.3702 13.2984L10.3742 13.3069L10.3781 13.3155L10.382 13.324L10.386 13.3325L10.3899 13.341L10.3938 13.3495L10.3977 13.3579L10.4016 13.3664L10.4055 13.3748L10.4094 13.3833L10.4133 13.3917L10.4171 13.4001L10.421 13.4085L10.4249 13.4168L10.4287 13.4252L10.4326 13.4335L10.4364 13.4419L10.4403 13.4502L10.4556 13.4834L10.4594 13.4916L10.4632 13.4999L10.467 13.5081L10.4708 13.5163L10.4859 13.5491L10.5009 13.5816L10.5159 13.614L10.5233 13.6301L10.527 13.6381L10.5307 13.6461L10.5454 13.678L10.5601 13.7097L10.5746 13.7412L10.6034 13.8035L10.6105 13.819L10.6176 13.8344L10.6211 13.8421L10.6247 13.8497L10.6317 13.865L10.6388 13.8803L10.6458 13.8954L10.6493 13.903L10.6528 13.9106L10.6597 13.9256L10.7145 14.0443L10.8192 14.2711L10.8255 14.2848L10.8318 14.2985L10.8349 14.3053L10.8381 14.3121L10.8412 14.3188L10.8443 14.3256L10.8462 14.3296L13.9029 7.70666C14.0245 7.44328 14.2959 7.28216 14.5853 7.30157C14.8747 7.32097 15.1222 7.51687 15.2075 7.7941L15.2075 7.79414L15.2075 7.79417L15.2077 7.79474L15.2085 7.79747L15.2122 7.80919L15.2269 7.85628C15.24 7.89772 15.2592 7.95852 15.2839 8.0348C15.3332 8.18749 15.4038 8.40153 15.4884 8.64602C15.6595 9.14015 15.8812 9.73886 16.0971 10.2067C16.2196 10.4719 16.2866 10.6145 16.3462 10.7113C16.3619 10.7369 16.3733 10.7526 16.3804 10.7617C16.4007 10.7674 16.451 10.7785 16.554 10.7865C16.7226 10.7995 16.9502 10.8 17.3077 10.8H17.3207H17.3336H17.3465H17.3594H17.3723H17.3851H17.398H17.4108H17.4236H17.4363H17.449H17.4617H17.4744H17.4871H17.4997H17.5123H17.5249H17.5375H17.55H17.5625H17.575H17.5875H17.5999H17.6124H17.6248H17.6371H17.6495H17.6618H17.6741H17.6864H17.6986H17.7109H17.7231H17.7353H17.7474H17.7595H17.7717H17.7837H17.7958H17.8079H17.8199H17.8319H17.8438H17.8558H17.8677H17.8796H17.8915H17.9033H17.9152H17.927H17.9388H17.9505H17.9622H17.974H17.9856H17.9973H18.009H18.0206H18.0322H18.0437H18.0553H18.0668H18.0783H18.0898H18.1012H18.1127H18.1241H18.1355H18.1468H18.1582H18.1695H18.1808H18.192H18.2033H18.2145H18.2257H18.2369H18.248H18.2591H18.2702H18.2813H18.2924H18.3034H18.3144H18.3254H18.3364H18.3473H18.3582H18.3691H18.38H18.3908H18.4017H18.4125H18.4232H18.434H18.4447H18.4554H18.4661H18.4768H18.4874H18.498H18.5086H18.5192H18.5297H18.5403H18.5508H18.5612H18.5717H18.5821H18.5925H18.6029H18.6133H18.6236H18.6339H18.6442H18.6545H18.6648H18.675H18.6852H18.6954H18.7055H18.7156H18.7258H18.7358H18.7459H18.7559H18.766H18.776H18.7859H18.7959H18.8058H18.8157H18.8256H18.8355H18.8453H18.8551H18.8649H18.8747H18.8844H18.8941H18.9038H18.9135H18.9231H18.9328H18.9424H18.952H18.9615H18.9711H18.9806H18.9901H18.9995H19.009H19.0184H19.0278H19.0372H19.0466H19.0559H19.0652H19.0745H19.0838H19.093H19.1022H19.1114H19.1206H19.1298H19.1389H19.148H19.1571H19.1661H19.1752H19.1842H19.1932H19.2022H19.2111H19.22H19.229H19.2378H19.2467H19.2555H19.2644H19.2731H19.2819H19.2907H19.2994H19.3081H19.3168H19.3254H19.3341H19.3427H19.3513H19.3598H19.3684H19.3769H19.3854H19.3939H19.4024H19.4108H19.4192H19.4276H19.436H19.4443H19.4526H19.4609H19.4692H19.4775H19.4857H19.4939H19.5021H19.5103H19.5184H19.5265H19.5346H19.5427H19.5508H19.5588H19.5668H19.5748H19.5828H19.5907H19.5986H19.6066H19.6144H19.6223H19.6301H19.6379H19.6457H19.6535H19.6612H19.669H19.6767H19.6844H19.692H19.6996H19.7073H19.7149H19.7224H19.73H19.7375H19.745H19.7525H19.7599H19.7674H19.7748H19.7822H19.7896H19.7969H19.8043H19.8116H19.8188H19.8261H19.8334H19.8406H19.8478H19.8549H19.8621H19.8692H19.8763H19.8834H19.8905H19.8975H19.9046H19.9116H19.9185H19.9255H19.9324H19.9394H19.9462H19.9531H19.96H19.9668H19.9736H19.9804H19.9871H19.9939H20.0006H20.0073H20.014H20.0206H20.0273H20.0339H20.0405H20.047H20.0536H20.0601H20.0666H20.0731H20.0795H20.086H20.0924H20.0988H20.1052H20.1115H20.1178H20.1242H20.1304H20.1367H20.143H20.1492H20.1554H20.1616H20.1677H20.1739H20.18H20.1861H20.1921H20.1982H20.2042H20.2102H20.2162H20.2222H20.2281H20.234H20.2399H20.2458H20.2517H20.2575H20.2633H20.2691H20.2749H20.2806H20.2864H20.2921H20.2978H20.3034H20.3091H20.3147H20.3203H20.3259H20.3315H20.337H20.3425H20.348H20.3535H20.3589H20.3644H20.3698H20.3752H20.3805H20.3859H20.3912H20.3965H20.4018H20.4071H20.4123H20.4175H20.4227H20.4279H20.4331H20.4382H20.4433H20.4484H20.4535H20.4586H20.4636H20.4686H20.4736H20.4786H20.4835H20.4884H20.4933H20.4982H20.5031H20.5079H20.5128H20.5176H20.5223H20.5271H20.5318H20.5365H20.5412H20.5459H20.5506H20.5552H20.5598H20.5644H20.569H20.5735H20.5781H20.5826H20.5871H20.5915H20.596H20.6004H20.6048H20.6092H20.6136H20.6179H20.6222H20.6265H20.6308H20.6351H20.6393H20.6435H20.6477H20.6519H20.656H20.6602H20.6643H20.6684H20.6725H20.6765H20.6806H20.6846H20.6886H20.6925H20.6965H20.7004H20.7043H20.7082H20.7121H20.7159H20.7197H20.7235H20.7273H20.7311H20.7348H20.7386H20.7423H20.746H20.7496H20.7533H20.7569H20.7605H20.7641H20.7676H20.7712H20.7747H20.7782H20.7817H20.7851H20.7885H20.792H20.7954H20.7987H20.8021H20.8054H20.8087H20.812H20.8153H20.8186H20.8218H20.825H20.8282H20.8314H20.8345H20.8377H20.8408H20.8439H20.8469H20.85H20.853H20.856H20.859H20.862H20.8649H20.8679H20.8708H20.8737H20.8765H20.8794H20.8822H20.885H20.8878H20.8906H20.8933H20.8961H20.8988H20.9015H20.9041H20.9068H20.9094H20.912H20.9146H20.9172H20.9197H20.9223H20.9248H20.9272H20.9297H20.9322H20.9346H20.937H20.9394H20.9418H20.9441H20.9464H20.9487H20.951H20.9533H20.9555H20.9578H20.96H20.9622H20.9643H20.9665H20.9686H20.9707H20.9728H20.9749H20.9769H20.979H20.981H20.983H20.9849H20.9869H20.9888H20.9907H20.9926H20.9945H20.9964H20.9982H21V6C21 4.89543 20.1046 4 19 4H5ZM21 12.2H20.9982H20.9964H20.9945H20.9926H20.9907H20.9888H20.9869H20.9849H20.983H20.981H20.979H20.9769H20.9749H20.9728H20.9707H20.9686H20.9665H20.9643H20.9622H20.96H20.9578H20.9555H20.9533H20.951H20.9487H20.9464H20.9441H20.9418H20.9394H20.937H20.9346H20.9322H20.9297H20.9272H20.9248H20.9223H20.9197H20.9172H20.9146H20.912H20.9094H20.9068H20.9041H20.9015H20.8988H20.8961H20.8933H20.8906H20.8878H20.885H20.8822H20.8794H20.8765H20.8737H20.8708H20.8679H20.8649H20.862H20.859H20.856H20.853H20.85H20.8469H20.8439H20.8408H20.8377H20.8345H20.8314H20.8282H20.825H20.8218H20.8186H20.8153H20.812H20.8087H20.8054H20.8021H20.7987H20.7954H20.792H20.7885H20.7851H20.7817H20.7782H20.7747H20.7712H20.7676H20.7641H20.7605H20.7569H20.7533H20.7496H20.746H20.7423H20.7386H20.7348H20.7311H20.7273H20.7235H20.7197H20.7159H20.7121H20.7082H20.7043H20.7004H20.6965H20.6925H20.6886H20.6846H20.6806H20.6765H20.6725H20.6684H20.6643H20.6602H20.656H20.6519H20.6477H20.6435H20.6393H20.6351H20.6308H20.6265H20.6222H20.6179H20.6136H20.6092H20.6048H20.6004H20.596H20.5915H20.5871H20.5826H20.5781H20.5735H20.569H20.5644H20.5598H20.5552H20.5506H20.5459H20.5412H20.5365H20.5318H20.5271H20.5223H20.5176H20.5128H20.5079H20.5031H20.4982H20.4933H20.4884H20.4835H20.4786H20.4736H20.4686H20.4636H20.4586H20.4535H20.4484H20.4433H20.4382H20.4331H20.4279H20.4227H20.4175H20.4123H20.4071H20.4018H20.3965H20.3912H20.3859H20.3805H20.3752H20.3698H20.3644H20.3589H20.3535H20.348H20.3425H20.337H20.3315H20.3259H20.3203H20.3147H20.3091H20.3034H20.2978H20.2921H20.2864H20.2806H20.2749H20.2691H20.2633H20.2575H20.2517H20.2458H20.2399H20.234H20.2281H20.2222H20.2162H20.2102H20.2042H20.1982H20.1921H20.1861H20.18H20.1739H20.1677H20.1616H20.1554H20.1492H20.143H20.1367H20.1304H20.1242H20.1178H20.1115H20.1052H20.0988H20.0924H20.086H20.0795H20.0731H20.0666H20.0601H20.0536H20.047H20.0405H20.0339H20.0273H20.0206H20.014H20.0073H20.0006H19.9939H19.9871H19.9804H19.9736H19.9668H19.96H19.9531H19.9462H19.9394H19.9324H19.9255H19.9185H19.9116H19.9046H19.8975H19.8905H19.8834H19.8763H19.8692H19.8621H19.8549H19.8478H19.8406H19.8334H19.8261H19.8188H19.8116H19.8043H19.7969H19.7896H19.7822H19.7748H19.7674H19.7599H19.7525H19.745H19.7375H19.73H19.7224H19.7149H19.7073H19.6996H19.692H19.6844H19.6767H19.669H19.6612H19.6535H19.6457H19.6379H19.6301H19.6223H19.6144H19.6066H19.5986H19.5907H19.5828H19.5748H19.5668H19.5588H19.5508H19.5427H19.5346H19.5265H19.5184H19.5103H19.5021H19.4939H19.4857H19.4775H19.4692H19.4609H19.4526H19.4443H19.436H19.4276H19.4192H19.4108H19.4024H19.3939H19.3854H19.3769H19.3684H19.3598H19.3513H19.3427H19.3341H19.3254H19.3168H19.3081H19.2994H19.2907H19.2819H19.2731H19.2644H19.2555H19.2467H19.2378H19.229H19.22H19.2111H19.2022H19.1932H19.1842H19.1752H19.1661H19.1571H19.148H19.1389H19.1298H19.1206H19.1114H19.1022H19.093H19.0838H19.0745H19.0652H19.0559H19.0466H19.0372H19.0278H19.0184H19.009H18.9995H18.9901H18.9806H18.9711H18.9615H18.952H18.9424H18.9328H18.9231H18.9135H18.9038H18.8941H18.8844H18.8747H18.8649H18.8551H18.8453H18.8355H18.8256H18.8157H18.8058H18.7959H18.7859H18.776H18.766H18.7559H18.7459H18.7358H18.7258H18.7156H18.7055H18.6954H18.6852H18.675H18.6648H18.6545H18.6442H18.6339H18.6236H18.6133H18.6029H18.5925H18.5821H18.5717H18.5612H18.5508H18.5403H18.5297H18.5192H18.5086H18.498H18.4874H18.4768H18.4661H18.4554H18.4447H18.434H18.4232H18.4125H18.4017H18.3908H18.38H18.3691H18.3582H18.3473H18.3364H18.3254H18.3144H18.3034H18.2924H18.2813H18.2702H18.2591H18.248H18.2369H18.2257H18.2145H18.2033H18.192H18.1808H18.1695H18.1582H18.1468H18.1355H18.1241H18.1127H18.1012H18.0898H18.0783H18.0668H18.0553H18.0437H18.0322H18.0206H18.009H17.9973H17.9856H17.974H17.9622H17.9505H17.9388H17.927H17.9152H17.9033H17.8915H17.8796H17.8677H17.8558H17.8438H17.8319H17.8199H17.8079H17.7958H17.7837H17.7717H17.7595H17.7474H17.7353H17.7231H17.7109H17.6986H17.6864H17.6741H17.6618H17.6495H17.6371H17.6248H17.6124H17.5999H17.5875H17.575H17.5625H17.55H17.5375H17.5249H17.5123H17.4997H17.4871H17.4744H17.4617H17.449H17.4363H17.4236H17.4108H17.398H17.3851H17.3723H17.3594H17.3465H17.3336H17.3207H17.3077L17.2818 12.2C16.9578 12.2 16.6752 12.2 16.446 12.1823C16.2097 12.164 15.9492 12.1228 15.7051 11.9905C15.4447 11.8495 15.2782 11.647 15.1538 11.445C15.045 11.2681 14.9431 11.0472 14.8394 10.8225L14.826 10.7933C14.6941 10.5076 14.5639 10.1875 14.4445 9.87405L11.4817 16.2933C11.3673 16.5412 11.1192 16.7 10.8462 16.7C10.5731 16.7 10.325 16.5412 10.2106 16.2933L10.2031 16.2772L10.2012 16.273L10.2002 16.2708L10.1992 16.2687L10.1972 16.2644L10.1962 16.2622L10.1952 16.2599L10.1867 16.2417L10.1778 16.2223L10.1585 16.1804L10.1481 16.1579L10.1372 16.1344L10.114 16.0841L10.1079 16.0709L10.1017 16.0575L10.089 16.0298L10.062 15.9714L10.0585 15.9638L10.055 15.9562L10.0514 15.9485L10.0478 15.9407L10.0332 15.909L10.0025 15.8425L9.99456 15.8253L9.99054 15.8166L9.98649 15.8078L9.97001 15.7721L9.9616 15.7539L9.95308 15.7354L9.9357 15.6978L9.93128 15.6882L9.92906 15.6834L9.92684 15.6786L9.92236 15.6689L9.92012 15.664L9.91786 15.6591L9.89958 15.6195L9.88086 15.5789L9.87133 15.5583L9.86653 15.5479L9.86169 15.5374L9.82204 15.4515L9.80155 15.4071L9.79637 15.3959L9.79376 15.3902L9.79115 15.3846L9.78064 15.3618L9.77799 15.3561L9.77534 15.3503L9.77268 15.3446L9.77002 15.3388L9.75929 15.3155L9.73751 15.2683L9.64612 15.0703L9.5979 14.9659L9.57317 14.9123L9.56065 14.8851L9.5575 14.8783L9.55435 14.8715L9.54802 14.8578L9.54485 14.8509L9.54167 14.844L9.53529 14.8302L9.52889 14.8163L9.52246 14.8024L9.4965 14.7462L9.44334 14.631L9.43658 14.6163L9.4332 14.609L9.4298 14.6016L9.41616 14.5721L9.40242 14.5423L9.39551 14.5274L9.39205 14.5198L9.38858 14.5123L9.3606 14.4517L9.34646 14.4211L9.33222 14.3902L9.30344 14.3279L9.28891 14.2964L9.27428 14.2647L9.27061 14.2567L9.26693 14.2488L9.25955 14.2328L9.24472 14.2006L9.21478 14.1358L9.18445 14.0701L9.16914 14.0369L9.15374 14.0035L9.12265 13.9361L9.11873 13.9277L9.11481 13.9192L9.10696 13.9021L9.09117 13.868L9.05932 13.7989L9.05531 13.7903L9.0513 13.7816L9.04728 13.7728L9.04325 13.7641L9.02709 13.7291L8.99449 13.6585L8.99039 13.6496L8.98628 13.6407L8.98217 13.6318L8.97805 13.6229L8.9698 13.605L8.96152 13.587L8.82597 13.2933C8.46988 12.5218 8.18839 12.0739 7.9986 11.8312C7.95341 11.7734 7.91609 11.7308 7.88702 11.7H3V18C3 19.1046 3.89543 20 5 20H19C20.1046 20 21 19.1046 21 18V12.2ZM16.3896 10.7722C16.3896 10.7723 16.3889 10.7717 16.3874 10.7701C16.3889 10.7714 16.3896 10.7722 16.3896 10.7722ZM7.80666 11.6272C7.80657 11.6268 7.81122 11.6296 7.82089 11.6372C7.81157 11.6315 7.80674 11.6277 7.80666 11.6272Z" fill="currentColor"/> </svg> diff --git a/public/icons/strategy.svg b/public/icons/strategy.svg index 1cdde736..6ae2e2fd 100644 --- a/public/icons/strategy.svg +++ b/public/icons/strategy.svg @@ -1,5 +1,5 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<rect x="2" y="11" width="5" height="11" rx="1" fill="#797979"/> -<rect x="9" y="2" width="5" height="20" rx="1" fill="#797979"/> -<rect x="16" y="8" width="5" height="14" rx="1" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<rect x="2" y="11" width="5" height="11" rx="1" fill="currentColor"/> +<rect x="9" y="2" width="5" height="20" rx="1" fill="currentColor"/> +<rect x="16" y="8" width="5" height="14" rx="1" fill="currentColor"/> </svg> diff --git a/public/icons/text-logo.svg b/public/icons/text-logo.svg deleted file mode 100644 index 36d7c26b..00000000 --- a/public/icons/text-logo.svg +++ /dev/null @@ -1,13 +0,0 @@ -<svg width="142" height="24" viewBox="0 0 142 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M0.132269 22.3365V8.07017H2.88346V22.3365H0.132269ZM0 5.27286V2H3.04219V5.27286H0Z" fill="#0A0A0A"/> -<path d="M6.74572 22.3365V8.07017H9.36464V11.427H9.49691V22.3365H6.74572ZM16.322 22.3365V13.1613C16.322 12.1729 16.0839 11.4363 15.6077 10.9514C15.1492 10.4665 14.4614 10.2241 13.5443 10.2241C12.7507 10.2241 12.0453 10.4106 11.428 10.7836C10.8284 11.1565 10.3522 11.6694 9.99953 12.3221C9.66445 12.9748 9.49691 13.7487 9.49691 14.6439L9.15301 11.2311C9.59391 10.1682 10.2729 9.32897 11.19 8.71356C12.107 8.09815 13.174 7.79044 14.3909 7.79044C15.837 7.79044 16.9745 8.21936 17.8034 9.07721C18.6323 9.93505 19.0467 11.0726 19.0467 12.4899V22.3365H16.322Z" fill="#0A0A0A"/> -<path d="M26.1082 22.3365L20.6058 8.07017H23.5422L28.0394 21.0217H26.7431L31.1874 8.07017H34.0179L28.542 22.3365H26.1082Z" fill="#0A0A0A"/> -<path d="M41.7319 22.6162C40.3739 22.6162 39.1747 22.2992 38.1342 21.6651C37.1113 21.0311 36.3089 20.1546 35.7269 19.0356C35.1449 17.9167 34.8539 16.63 34.8539 15.1754C34.8539 13.7021 35.1361 12.4153 35.7004 11.3151C36.2824 10.1961 37.0937 9.32897 38.1342 8.71356C39.1747 8.09815 40.3651 7.79044 41.7054 7.79044C43.0105 7.79044 44.1392 8.08882 45.0915 8.68558C46.0615 9.26369 46.8022 10.0749 47.3136 11.1192C47.8427 12.1636 48.1072 13.4037 48.1072 14.8397C48.1072 15.0635 48.0984 15.2779 48.0808 15.4831C48.0808 15.6695 48.0632 15.8654 48.0279 16.0705H36.7057V13.8886H46.0968L45.409 14.7558C45.409 13.2266 45.0827 12.061 44.4302 11.2591C43.7776 10.4572 42.8694 10.0563 41.7054 10.0563C40.4533 10.0563 39.4569 10.5132 38.7162 11.427C37.9931 12.3221 37.6315 13.5716 37.6315 15.1754C37.6315 16.7978 37.9931 18.0659 38.7162 18.9797C39.4569 19.8935 40.4797 20.3504 41.7848 20.3504C42.5784 20.3504 43.2662 20.1825 43.8482 19.8469C44.4302 19.4925 44.8623 18.9797 45.1444 18.3083H47.7369C47.296 19.6511 46.5465 20.7047 45.4883 21.4693C44.4478 22.2339 43.1957 22.6162 41.7319 22.6162Z" fill="#0A0A0A"/> -<path d="M55.8406 22.6162C54.0771 22.6162 52.675 22.2059 51.6345 21.3854C50.594 20.5648 50.0032 19.4086 49.8621 17.9167H52.4016C52.5251 18.7373 52.8778 19.3713 53.4598 19.8189C54.0594 20.2478 54.8707 20.4623 55.8935 20.4623C56.793 20.4623 57.4808 20.2944 57.9569 19.9588C58.4331 19.6231 58.6712 19.1475 58.6712 18.5321C58.6712 18.0846 58.5301 17.7116 58.2479 17.4132C57.9834 17.0962 57.4543 16.8351 56.6607 16.63L54.4915 16.0985C53.1335 15.7441 52.1195 15.222 51.4493 14.532C50.7968 13.8233 50.4705 12.9561 50.4705 11.9305C50.4705 10.6437 50.9202 9.63667 51.8197 8.90937C52.7367 8.16342 53.9889 7.79044 55.5761 7.79044C57.1457 7.79044 58.4067 8.16342 59.359 8.90937C60.329 9.65532 60.8757 10.6996 60.9991 12.0424H58.4596C58.3538 11.371 58.0451 10.8582 57.5337 10.5038C57.0399 10.1309 56.3609 9.94437 55.4967 9.94437C54.6855 9.94437 54.0594 10.1029 53.6185 10.4199C53.1953 10.7183 52.9836 11.1379 52.9836 11.6787C52.9836 12.1263 53.1424 12.4993 53.4598 12.7976C53.7949 13.096 54.3416 13.3478 55.0999 13.5529L57.322 14.1403C58.6095 14.476 59.5794 15.0262 60.232 15.7908C60.8845 16.5367 61.2108 17.4412 61.2108 18.5042C61.2108 19.7909 60.7346 20.798 59.7823 21.5253C58.8476 22.2526 57.5337 22.6162 55.8406 22.6162Z" fill="#0A0A0A"/> -<path d="M69.4755 22.6162C67.9059 22.6162 66.7508 22.2339 66.0101 21.4693C65.287 20.6861 64.9255 19.5392 64.9255 18.0286V4.79732L67.6502 3.70636V18.0566C67.6502 18.8025 67.8354 19.3527 68.2057 19.707C68.5761 20.0613 69.1757 20.2385 70.0046 20.2385C70.322 20.2385 70.6042 20.2105 70.8511 20.1546C71.098 20.0986 71.3096 20.0334 71.486 19.9588V22.3085C71.292 22.4017 71.0186 22.4763 70.6659 22.5323C70.3132 22.5882 69.9164 22.6162 69.4755 22.6162ZM62.2272 10.3919V8.07017H71.486V10.3919H62.2272Z" fill="#0A0A0A"/> -<path d="M74.0944 22.3365V8.07017H76.7133V11.427H76.8456V22.3365H74.0944ZM83.1945 22.3365V13.0214C83.1945 12.0517 82.9828 11.343 82.5596 10.8955C82.1363 10.4479 81.5279 10.2241 80.7343 10.2241C79.9936 10.2241 79.3234 10.4106 78.7238 10.7836C78.1418 11.1379 77.6833 11.6414 77.3482 12.2941C77.0131 12.9282 76.8456 13.6835 76.8456 14.5599L76.5017 11.2311C76.9426 10.1682 77.6039 9.32897 78.4857 8.71356C79.3851 8.09815 80.3904 7.79044 81.5014 7.79044C82.8418 7.79044 83.9087 8.20071 84.7023 9.02126C85.5136 9.84181 85.9192 10.9234 85.9192 12.2661V22.3365H83.1945ZM92.2946 22.3365V13.0214C92.2946 12.0517 92.0741 11.343 91.6332 10.8955C91.21 10.4479 90.6015 10.2241 89.8079 10.2241C89.0849 10.2241 88.4235 10.4106 87.8239 10.7836C87.2419 11.1379 86.7746 11.6414 86.4218 12.2941C86.0868 12.9282 85.9192 13.6835 85.9192 14.5599L85.3637 11.2311C85.8222 10.1682 86.51 9.32897 87.4271 8.71356C88.3618 8.09815 89.4023 7.79044 90.5486 7.79044C91.9066 7.79044 92.9912 8.21004 93.8024 9.04923C94.6137 9.86978 95.0193 10.9701 95.0193 12.3501V22.3365H92.2946Z" fill="#0A0A0A"/> -<path d="M104.534 22.6162C103.176 22.6162 101.977 22.2992 100.936 21.6651C99.9132 21.0311 99.1108 20.1546 98.5288 19.0356C97.9468 17.9167 97.6558 16.63 97.6558 15.1754C97.6558 13.7021 97.938 12.4153 98.5023 11.3151C99.0843 10.1961 99.8956 9.32897 100.936 8.71356C101.977 8.09815 103.167 7.79044 104.507 7.79044C105.812 7.79044 106.941 8.08882 107.893 8.68558C108.863 9.26369 109.604 10.0749 110.116 11.1192C110.645 12.1636 110.909 13.4037 110.909 14.8397C110.909 15.0635 110.9 15.2779 110.883 15.4831C110.883 15.6695 110.865 15.8654 110.83 16.0705H99.5076V13.8886H108.899L108.211 14.7558C108.211 13.2266 107.885 12.061 107.232 11.2591C106.58 10.4572 105.671 10.0563 104.507 10.0563C103.255 10.0563 102.259 10.5132 101.518 11.427C100.795 12.3221 100.433 13.5716 100.433 15.1754C100.433 16.7978 100.795 18.0659 101.518 18.9797C102.259 19.8935 103.282 20.3504 104.587 20.3504C105.38 20.3504 106.068 20.1825 106.65 19.8469C107.232 19.4925 107.664 18.9797 107.946 18.3083H110.539C110.098 19.6511 109.348 20.7047 108.29 21.4693C107.25 22.2339 105.998 22.6162 104.534 22.6162Z" fill="#0A0A0A"/> -<path d="M119.231 22.6162C117.662 22.6162 116.507 22.2339 115.766 21.4693C115.043 20.6861 114.681 19.5392 114.681 18.0286V4.79732L117.406 3.70636V18.0566C117.406 18.8025 117.591 19.3527 117.962 19.707C118.332 20.0613 118.932 20.2385 119.76 20.2385C120.078 20.2385 120.36 20.2105 120.607 20.1546C120.854 20.0986 121.065 20.0334 121.242 19.9588V22.3085C121.048 22.4017 120.774 22.4763 120.422 22.5323C120.069 22.5882 119.672 22.6162 119.231 22.6162ZM111.983 10.3919V8.07017H121.242V10.3919H111.983Z" fill="#0A0A0A"/> -<path d="M123.799 22.3365V8.07017H126.55V22.3365H123.799ZM123.666 5.27286V2H126.708V5.27286H123.666Z" fill="#0A0A0A"/> -<path d="M135.969 22.6162C134.681 22.6162 133.535 22.2992 132.53 21.6651C131.524 21.0124 130.731 20.1266 130.149 19.0077C129.584 17.8887 129.302 16.6113 129.302 15.1754C129.302 13.7394 129.584 12.4713 130.149 11.371C130.731 10.2521 131.524 9.37559 132.53 8.74153C133.535 8.10747 134.672 7.79044 135.942 7.79044C137.018 7.79044 137.979 8.0049 138.826 8.43382C139.672 8.86275 140.369 9.45951 140.915 10.2241C141.462 10.9887 141.815 11.8932 141.974 12.9375H139.381C139.205 12.0983 138.817 11.427 138.217 10.9234C137.635 10.4199 136.894 10.1682 135.995 10.1682C135.219 10.1682 134.54 10.3733 133.958 10.7836C133.376 11.1938 132.926 11.772 132.609 12.5179C132.291 13.2639 132.133 14.1497 132.133 15.1754C132.133 16.1824 132.291 17.0682 132.609 17.8328C132.926 18.5974 133.376 19.1942 133.958 19.6231C134.54 20.0334 135.228 20.2385 136.021 20.2385C136.886 20.2385 137.617 19.9961 138.217 19.5112C138.834 19.0077 139.231 18.3363 139.408 17.4971H142C141.824 18.5228 141.453 19.4179 140.889 20.1825C140.342 20.9471 139.646 21.5439 138.799 21.9728C137.953 22.4017 137.009 22.6162 135.969 22.6162Z" fill="#0A0A0A"/> -</svg> diff --git a/public/icons/traders.svg b/public/icons/traders.svg index c43591e9..bd827810 100644 --- a/public/icons/traders.svg +++ b/public/icons/traders.svg @@ -1,6 +1,6 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<circle cx="8.08594" cy="9" r="4" fill="#797979"/> -<circle cx="17.0859" cy="11" r="3" fill="#797979"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M14 20H1V18C1 15.7909 2.79086 14 5 14H10C12.2091 14 14 15.7909 14 18V20Z" fill="#797979"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5 20C15.3516 18.4189 16 16 14.1172 15H18.999C21.208 15 22.999 16.7908 22.999 19V19.3333C22.999 19.5605 22.9805 19.7832 22.9434 20H15.5Z" fill="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<circle cx="8.08594" cy="9" r="4" fill="currentColor"/> +<circle cx="17.0859" cy="11" r="3" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M14 20H1V18C1 15.7909 2.79086 14 5 14H10C12.2091 14 14 15.7909 14 18V20Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5 20C15.3516 18.4189 16 16 14.1172 15H18.999C21.208 15 22.999 16.7908 22.999 19V19.3333C22.999 19.5605 22.9805 19.7832 22.9434 20H15.5Z" fill="currentColor"/> </svg> From 37782ffb6f758d73487097e1113cdab241f9bcb6 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 13 Nov 2024 17:09:45 +0900 Subject: [PATCH 049/648] =?UTF-8?q?settings:=20svg=EB=A5=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20svgr=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 8 + package-lock.json | 699 ++++++++++++++++++++++++++++++++++------------ package.json | 3 +- 3 files changed, 524 insertions(+), 186 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index a02c122f..b21c8353 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -16,6 +16,14 @@ const nextConfig = { }, ] }, + webpack(config) { + config.module.rules.push({ + test: /\.svg$/, + use: ['@svgr/webpack'], + }) + + return config + }, } export default nextConfig diff --git a/package-lock.json b/package-lock.json index e37098ff..46cddc71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "investmetic", "version": "0.1.0", "dependencies": { + "@svgr/webpack": "^8.1.0", "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", @@ -59,7 +60,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -72,7 +72,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -86,7 +85,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -95,7 +93,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", - "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -125,7 +122,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -134,7 +130,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", - "dev": true, "dependencies": { "@babel/parser": "^7.26.2", "@babel/types": "^7.26.0", @@ -150,7 +145,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, "dependencies": { "@babel/types": "^7.25.9" }, @@ -162,7 +156,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -175,7 +168,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", - "dev": true, "dependencies": { "@babel/compat-data": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -191,7 +183,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -200,7 +191,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", @@ -221,7 +211,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -230,7 +219,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.1.1", @@ -247,7 +235,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -256,7 +243,6 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", - "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -309,7 +295,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", - "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -322,7 +307,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -335,7 +319,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -352,7 +335,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", - "dev": true, "dependencies": { "@babel/types": "^7.25.9" }, @@ -364,7 +346,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -373,7 +354,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", @@ -390,7 +370,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", - "dev": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", @@ -407,7 +386,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -420,7 +398,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", - "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -445,7 +422,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -454,7 +430,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -463,7 +438,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -472,7 +446,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", - "dev": true, "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", @@ -486,7 +459,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", - "dev": true, "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.0" @@ -499,7 +471,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "dev": true, "dependencies": { "@babel/types": "^7.26.0" }, @@ -514,7 +485,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -530,7 +500,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -545,7 +514,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -560,7 +528,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", @@ -577,7 +544,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -593,7 +559,6 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, "engines": { "node": ">=6.9.0" }, @@ -629,7 +594,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -644,7 +608,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -659,7 +622,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -674,7 +636,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -689,7 +650,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -705,7 +665,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -720,7 +679,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9", @@ -737,7 +695,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", - "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -754,7 +711,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -769,7 +725,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -784,7 +739,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", - "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -800,7 +754,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", - "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -816,7 +769,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", @@ -836,7 +788,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" @@ -852,7 +803,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -867,7 +817,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -883,7 +832,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -898,7 +846,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -914,7 +861,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -929,7 +875,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", - "dev": true, "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -945,7 +890,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -960,7 +904,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -976,7 +919,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", - "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -993,7 +935,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1008,7 +949,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1023,7 +963,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1038,7 +977,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1053,7 +991,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", - "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1069,7 +1006,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", - "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1086,7 +1022,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", - "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1104,7 +1039,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", - "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1120,7 +1054,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1136,7 +1069,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1151,7 +1083,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1166,7 +1097,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1181,7 +1111,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", - "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1198,7 +1127,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" @@ -1214,7 +1142,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1229,7 +1156,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1245,7 +1171,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1260,7 +1185,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", - "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1276,7 +1200,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1293,7 +1216,20 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", - "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", + "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1308,7 +1244,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1323,7 +1258,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -1342,7 +1276,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", - "dev": true, "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, @@ -1357,7 +1290,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1373,7 +1305,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" @@ -1389,7 +1320,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1405,7 +1335,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1449,7 +1378,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1464,7 +1392,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1480,7 +1407,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1495,7 +1421,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1510,7 +1435,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1525,7 +1449,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1544,7 +1467,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1559,7 +1481,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1575,7 +1496,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1591,7 +1511,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", - "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1607,7 +1526,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", - "dev": true, "dependencies": { "@babel/compat-data": "^7.26.0", "@babel/helper-compilation-targets": "^7.25.9", @@ -1690,7 +1608,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -1699,7 +1616,6 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -1713,7 +1629,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -1733,7 +1648,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", - "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -1752,7 +1666,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1764,7 +1677,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", @@ -1778,7 +1690,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/generator": "^7.25.9", @@ -1796,7 +1707,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -2766,7 +2676,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2780,7 +2689,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -2789,7 +2697,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -2807,14 +2714,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -4111,6 +4016,310 @@ "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" } }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dependencies": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -4353,6 +4562,14 @@ "node": ">=0.10.0" } }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -5156,8 +5373,7 @@ "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 + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-query": { "version": "5.3.0", @@ -5542,7 +5758,6 @@ "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", - "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.2", @@ -5556,7 +5771,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -5565,7 +5779,6 @@ "version": "0.10.6", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", - "dev": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2", "core-js-compat": "^3.38.0" @@ -5578,7 +5791,6 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dev": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2" }, @@ -5654,8 +5866,7 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -5817,7 +6028,6 @@ "version": "4.24.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5921,7 +6131,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -5936,6 +6145,17 @@ "tslib": "^2.0.3" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001677", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", @@ -6211,14 +6431,12 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", - "dev": true, "dependencies": { "browserslist": "^4.24.2" }, @@ -6394,11 +6612,22 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, "engines": { "node": ">= 6" }, @@ -6424,6 +6653,36 @@ "node": ">=4" } }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -6491,7 +6750,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -6529,7 +6787,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6683,7 +6940,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, "funding": [ { "type": "github", @@ -6724,7 +6980,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -6739,8 +6994,7 @@ "node_modules/electron-to-chromium": { "version": "1.5.52", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz", - "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==", - "dev": true + "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==" }, "node_modules/elliptic": { "version": "6.6.0", @@ -6824,7 +7078,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -7058,7 +7311,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "engines": { "node": ">=6" } @@ -7810,7 +8062,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8148,7 +8399,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8184,7 +8434,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -8302,7 +8551,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "engines": { "node": ">=4" } @@ -8442,7 +8690,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -8635,7 +8882,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -8731,8 +8977,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-async-function": { "version": "2.0.0", @@ -8814,7 +9059,6 @@ "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -9239,7 +9483,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -9260,7 +9503,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -9277,8 +9519,7 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -9296,7 +9537,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -9526,8 +9766,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/loader-runner": { "version": "4.3.0", @@ -9577,8 +9816,7 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "node_modules/lodash.kebabcase": { "version": "4.1.1", @@ -9625,7 +9863,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -9634,7 +9871,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "dependencies": { "yallist": "^3.0.2" } @@ -9698,6 +9934,11 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, "node_modules/memfs": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", @@ -9844,8 +10085,7 @@ "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 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { "version": "3.3.7", @@ -9978,7 +10218,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -10039,8 +10278,7 @@ "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -10055,7 +10293,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -10304,7 +10541,6 @@ "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, "dependencies": { "callsites": "^3.0.0" }, @@ -10333,7 +10569,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -10393,8 +10628,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", @@ -10422,7 +10656,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -11128,14 +11361,12 @@ "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, "dependencies": { "regenerate": "^1.4.2" }, @@ -11146,14 +11377,12 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" } @@ -11186,7 +11415,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", - "dev": true, "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", @@ -11202,14 +11430,12 @@ "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" }, "node_modules/regjsparser": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", - "dev": true, "dependencies": { "jsesc": "~3.0.2" }, @@ -11264,7 +11490,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -11281,7 +11506,6 @@ "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, "engines": { "node": ">=4" } @@ -11790,6 +12014,15 @@ "dev": true, "optional": true }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -12205,7 +12438,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -12213,6 +12445,109 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/svgo/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/svgo/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/synckit": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", @@ -12558,7 +12893,7 @@ "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12592,7 +12927,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true, "engines": { "node": ">=4" } @@ -12601,7 +12935,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -12614,7 +12947,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true, "engines": { "node": ">=4" } @@ -12623,7 +12955,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, "engines": { "node": ">=4" } @@ -12662,7 +12993,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -13146,8 +13476,7 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 92d6d827..00e28ffc 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "@svgr/webpack": "^8.1.0", "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", @@ -24,10 +25,10 @@ "devDependencies": { "@chromatic-com/storybook": "^3.2.2", "@next/eslint-plugin-next": "^15.0.2", + "@storybook/addon-docs": "^8.4.2", "@storybook/addon-essentials": "^8.4.2", "@storybook/addon-interactions": "^8.4.2", "@storybook/addon-onboarding": "^8.4.0", - "@storybook/addon-docs": "^8.4.2", "@storybook/blocks": "^8.4.0", "@storybook/nextjs": "^8.4.2", "@storybook/react": "^8.4.2", From 8738401bdc0d68084b0046ec0b382183a9f7c066 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 13 Nov 2024 18:03:13 +0900 Subject: [PATCH 050/648] =?UTF-8?q?feat:=20SVG=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=A0=20index=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SVG 아이콘 컴포넌트 통합 export 설정 - 아이콘 import 경로 단순화 및 자동완성 지원 --- public/icons/index.tsx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 public/icons/index.tsx diff --git a/public/icons/index.tsx b/public/icons/index.tsx new file mode 100644 index 00000000..aa0ba1e3 --- /dev/null +++ b/public/icons/index.tsx @@ -0,0 +1,25 @@ +export { default as BarsIcon } from './bars.svg' +export { default as CategoryIcon } from './category.svg' +export { default as ChangeIcon } from './change.svg' +export { default as CheckIcon } from './check.svg' +export { default as CheckboxIcon } from './checkbox.svg' +export { default as ChevronDownIcon } from './chevron-down.svg' +export { default as ChevronLeftIcon } from './chevron-left.svg' +export { default as ChevronRightIcon } from './chevron-right.svg' +export { default as ChevronUpIcon } from './chevron-up.svg' +export { default as DailyGraphIcon } from './daily-graph.svg' +export { default as FavoriteIcon } from './favorite.svg' +export { default as FileIcon } from './file.svg' +export { default as MoneyIcon } from './money.svg' +export { default as MonthlyGraphIcon } from './monthly-graph.svg' +export { default as NoticeIcon } from './notice.svg' +export { default as OpenIcon } from './open.svg' +export { default as PencilIcon } from './pencil.svg' +export { default as ProfileIcon } from './profile.svg' +export { default as QuestionIcon } from './question.svg' +export { default as SearchIcon } from './search.svg' +export { default as SignOutIcon } from './signout.svg' +export { default as StatisticsIcon } from './statistics.svg' +export { default as StrategyRankingIcon } from './strategy-ranking.svg' +export { default as StrategyIcon } from './strategy.svg' +export { default as TradersIcon } from './traders.svg' From 3687312ebf1eec9e52f8501bfcd9eaa90c724666 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 13 Nov 2024 18:41:45 +0900 Subject: [PATCH 051/648] =?UTF-8?q?feat:=20=EB=A7=81=ED=81=AC=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=84=9C=EB=B2=84=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EC=A0=84=ED=99=98(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/link-button/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/shared/ui/link-button/index.tsx b/shared/ui/link-button/index.tsx index c57915dc..114b6882 100644 --- a/shared/ui/link-button/index.tsx +++ b/shared/ui/link-button/index.tsx @@ -1,5 +1,3 @@ -'use client' - import { ComponentProps } from 'react' import { Url } from 'next/dist/shared/lib/router/router' From e7ec2abc4e005cac447b980d3ffcb4a53203cffe Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 12 Nov 2024 16:41:00 +0900 Subject: [PATCH 052/648] =?UTF-8?q?feat:=EA=B3=B5=ED=86=B5=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20=ED=86=B5=EA=B3=84=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B0=9C=EB=B0=9C=20(#2?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 1 + app/(dashboard)/strategies/page.tsx | 88 +++++++++++++++++++ shared/types/strategy-details-data.ts | 20 +++++ shared/ui/table/ vertical/index.tsx | 44 ++++++++++ .../ui/table/ vertical/vertical.module.scss | 19 ++++ shared/ui/table/statistics/inKorean.ts | 32 +++++++ shared/ui/table/statistics/index.tsx | 51 +++++++++++ .../table/statistics/statistics.module.scss | 21 +++++ 8 files changed, 276 insertions(+) create mode 100644 .env create mode 100644 app/(dashboard)/strategies/page.tsx create mode 100644 shared/types/strategy-details-data.ts create mode 100644 shared/ui/table/ vertical/index.tsx create mode 100644 shared/ui/table/ vertical/vertical.module.scss create mode 100644 shared/ui/table/statistics/inKorean.ts create mode 100644 shared/ui/table/statistics/index.tsx create mode 100644 shared/ui/table/statistics/statistics.module.scss diff --git a/.env b/.env new file mode 100644 index 00000000..b9074b9b --- /dev/null +++ b/.env @@ -0,0 +1 @@ +API_HOST=http://localhost:8080 \ No newline at end of file diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx new file mode 100644 index 00000000..1c83e69f --- /dev/null +++ b/app/(dashboard)/strategies/page.tsx @@ -0,0 +1,88 @@ +import VerticalTable from '@/shared/ui/table/ vertical' +import StatisticsTable from '@/shared/ui/table/statistics' + +const Strategies = () => { + const head = ['날짜', '원금', '입출금', '일손익', '일손익률', '누적손익', '누적수익률'] + const body = [ + { + date: '2015-03-12', // 날짜 + principal: 100000000, // 원금 + transaction: 0, // 입출금 + dailyProfitLoss: 332410, // 일 손익 + dailyProfitLossRate: 0.33, // 일 수익률 + cumulativeProfitLoss: 302280, // 누적 손익 + cumulativeProfitLossRate: 0.3, // 누적 수익률 + }, + { + date: '2015-03-13', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + { + date: '2015-03-14', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + ] + // 자산 및 운영 정보 + const assetManagementData = { + balance: 896217437, // 잔고 + cumulativeTransactionAmount: 896217437, // 누적 거래 금액 + principal: 238704360, // 원금 + operationPeriod: '2년 4월', // 운용 기간 + startDate: '2012-10-11', // 시작 일자 + endDate: '2015-03-11', // 종료 일자 (endDate) + daysSincePeakUpdate: 513, // 고점 갱신 후 경과일 + } + // 손익률 관련 정보 + const profitLoss = { + cumulativeProfitAmount: 247525031, // 누적 수익 금액 + cumulativeProfitRate: 49.24, // 누적 수익률 + maxCumulativeProfitAmount: 247525031, // 최대 누적 수익 금액 + maxCumulativeProfitRate: 49.24, // 최대 누적 수익률 + averageProfitLossAmount: 336311, // 평균 손익 금액 + averageProfitLossRate: 6, // 평균 손익률 + maxDailyProfitAmount: 25257250, // 최대 일 수익 금액 + maxDailyProfitRate: 3.985, // 최대 일 수익률 + maxDailyLossAmount: -17465050, // 최대 일 손실 금액 + maxDailyLossRate: -3.95, // 최대 일 손실률 + roa: 453, // 자산 수익률 (Return on Assets) + profitFactor: 1.48, // Profit Factor + } + // DD&MDD 정보 + const ddMddInfo = { + currentDrawdown: 0, // 현재 자본 인하 금액 + currentDrawdownRate: 0, // 현재 자본 인하율 + maxDrawdown: -54832778, // 최대 자본 인하 금액 + maxDrawdownRate: -13.98, // 최대 자본 인하율 + } + // 거래 관련 정보 + const tradingInfo = { + totalTradeDays: 736, // 총 거래 일수 + totalProfitableDays: 508, // 총 이익 일수 + totalLossDays: 228, // 총 손실 일수 + currentConsecutiveLossDays: 6, // 현재 연속 손실 일수 + maxConsecutiveProfitDays: 22, // 최대 연속 이익 일수 + maxConsecutiveLossDays: 8, // 최대 연속 손실 일수 + winRate: 69, // 승률 + } + return ( + <> + <VerticalTable tableHead={head} tableBody={body} countPerPage={2} currentPage={1} /> + <StatisticsTable statisticsData={assetManagementData} /> + <StatisticsTable statisticsData={profitLoss} /> + <StatisticsTable statisticsData={ddMddInfo} /> + <StatisticsTable statisticsData={tradingInfo} /> + </> + ) +} + +export default Strategies diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts new file mode 100644 index 00000000..dd8b001b --- /dev/null +++ b/shared/types/strategy-details-data.ts @@ -0,0 +1,20 @@ +// API Request Data +export interface DailyAnalysisModel { + date: string + principal: number + transaction: number + dailyProfitLoss: number + dailyProfitLossRate: number + cumulativeProfitLoss: number + cumulativeProfitLossRate: number +} + +export interface MonthlyAnalysisModel { + month: string + monthlyAveragePrincipal: number + depositsWithdrawals: number + monthlyProfitLoss: number + monthlyProfitLossRate: number + cumulativeProfitLoss: number + cumulativeProfitLossRate: number +} diff --git a/shared/ui/table/ vertical/index.tsx b/shared/ui/table/ vertical/index.tsx new file mode 100644 index 00000000..1aae8d59 --- /dev/null +++ b/shared/ui/table/ vertical/index.tsx @@ -0,0 +1,44 @@ +import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' + +import styles from './vertical.module.scss' + +type TableBodyDataType = DailyAnalysisModel | MonthlyAnalysisModel + +interface Props { + tableHead: string[] + tableBody: TableBodyDataType[] + countPerPage: number + currentPage: number +} + +const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Props) => { + const croppedTableBody = tableBody.slice( + countPerPage * (currentPage - 1), + countPerPage * (currentPage - 1) + countPerPage + ) + + return ( + <div className={styles.container}> + <table> + <thead> + <tr> + {tableHead.map((head) => ( + <td key={head}>{head}</td> + ))} + </tr> + </thead> + <tbody> + {croppedTableBody.map((row) => ( + <tr key={Object.values(row)[0]}> + {Object.entries(row).map((rowData, index) => ( + <td key={index}>{rowData[1]}</td> + ))} + </tr> + ))} + </tbody> + </table> + </div> + ) +} + +export default VerticalTable diff --git a/shared/ui/table/ vertical/vertical.module.scss b/shared/ui/table/ vertical/vertical.module.scss new file mode 100644 index 00000000..cbc413ca --- /dev/null +++ b/shared/ui/table/ vertical/vertical.module.scss @@ -0,0 +1,19 @@ +.container { + width: 100%; + padding: 20px; + table { + width: 100%; + font-size: $text-c1; + thead { + background-color: $color-gray-100; + } + td { + padding: 0 20px; + height: 40px; + text-align: start; + vertical-align: middle; + border-top: 1px solid $color-gray-200; + border-bottom: 1px solid $color-gray-200; + } + } +} diff --git a/shared/ui/table/statistics/inKorean.ts b/shared/ui/table/statistics/inKorean.ts new file mode 100644 index 00000000..094c509d --- /dev/null +++ b/shared/ui/table/statistics/inKorean.ts @@ -0,0 +1,32 @@ +export const inKoreanData = { + balance: '잔고', + principal: '원금', + cumulativeTransactionAmount: '누적 거래 금액', + operationPeriod: '운용 기간', + startDate: '시작 일자', + endDate: '종료 일자', + daysSincePeakUpdate: '고점 갱신 후 경과일', + cumulativeProfitAmount: '누적 수익 금액', + cumulativeProfitRate: '누적 수익률', + maxCumulativeProfitAmount: '최대 누적 수익 금액', + maxCumulativeProfitRate: '최대 누적 수익률', + averageProfitLossAmount: '평균 손익 금액', + averageProfitLossRate: '평균 손익률', + maxDailyProfitAmount: '최대 일 수익 금액', + maxDailyProfitRate: '최대 일 수익률', + maxDailyLossAmount: '최대 일 손실 금액', + maxDailyLossRate: '최대 일 손실률', + roa: '자산 수익률', + profitFactor: 'Profit Factor', + currentDrawdown: '현재 자본 인하 금액', + currentDrawdownRate: '현재 자본 인하율', + maxDrawdown: '최대 자본 인하 금액', + maxDrawdownRate: '최대 자본 인하율', + totalTradeDays: '총 거래 일수', + totalProfitableDays: '총 이익 일수', + totalLossDays: '총 손실 일수', + currentConsecutiveLossDays: '현재 연속 손실 일수', + maxConsecutiveProfitDays: '최대 연속 이익 일수', + maxConsecutiveLossDays: '최대 연속 손실 일수', + winRate: '승률', +} diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx new file mode 100644 index 00000000..43f2c3a0 --- /dev/null +++ b/shared/ui/table/statistics/index.tsx @@ -0,0 +1,51 @@ +import { inKoreanData } from './inKorean' +import styles from './statistics.module.scss' + +interface Props { + statisticsData: { [key: string]: string | number } +} + +const StatisticsTable = ({ statisticsData }: Props) => { + const inKoreanDataToArray: string[][] = Object.entries(inKoreanData) + + const mappedDataInKorean: { [key: string]: string | number } = {} + for (const [key, value] of inKoreanDataToArray) { + if (statisticsData[key] !== undefined) { + mappedDataInKorean[value] = statisticsData[key] + } + } + + const groupedData = [] + const mappedDataInKoreanToArray = Object.entries(mappedDataInKorean) + for (let i = 0; i < mappedDataInKoreanToArray.length; i += 2) { + groupedData.push([ + mappedDataInKoreanToArray[i][0], + mappedDataInKoreanToArray[i][1], + mappedDataInKoreanToArray[i + 1]?.[0], + mappedDataInKoreanToArray[i + 1]?.[1], + ]) + } + + return ( + <div className={styles.container}> + <table> + <tbody> + {groupedData.map((row, index) => ( + <tr key={index}> + <td>{row[0]}</td> + <td>{row[1]}</td> + {row[2] && ( + <> + <td>{row[2]}</td> + <td>{row[3]}</td> + </> + )} + </tr> + ))} + </tbody> + </table> + </div> + ) +} + +export default StatisticsTable diff --git a/shared/ui/table/statistics/statistics.module.scss b/shared/ui/table/statistics/statistics.module.scss new file mode 100644 index 00000000..dc4b01e8 --- /dev/null +++ b/shared/ui/table/statistics/statistics.module.scss @@ -0,0 +1,21 @@ +.container { + width: 100%; + padding: 20px; + table { + width: 100%; + font-size: $text-c1; + td:nth-child(2n + 1) { + font-weight: $text-semibold; + color: $color-gray-600; + background-color: $color-gray-100; + } + td { + padding: 0 20px; + width: 120px; + height: 32px; + text-align: start; + vertical-align: middle; + border: 1px solid $color-white; + } + } +} From acaacc64c6cabd3d7e07adb163c992cb208b83a1 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 12 Nov 2024 16:56:12 +0900 Subject: [PATCH 053/648] =?UTF-8?q?chore:=20.env=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a5abcf01..b77597cf 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ yarn-debug.log* yarn-error.log* # local env files +.env .env*.local # vercel From 138f3d7a0419440abdc4bcd9f2b1a2b6e1a2b2f2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 12 Nov 2024 17:00:00 +0900 Subject: [PATCH 054/648] =?UTF-8?q?remove:=20git=20=ED=8A=B8=EB=9E=99?= =?UTF-8?q?=ED=82=B9=EC=97=90=EC=84=9C=20.env=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index b9074b9b..00000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -API_HOST=http://localhost:8080 \ No newline at end of file From 9e49f84f2f9c967c52c703076e529ea4bb2d8586 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 12 Nov 2024 17:08:07 +0900 Subject: [PATCH 055/648] =?UTF-8?q?feat:=20=ED=86=B5=EA=B3=84=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20title=20props=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/page.tsx | 8 ++++---- shared/ui/table/statistics/index.tsx | 4 +++- shared/ui/table/statistics/statistics.module.scss | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 1c83e69f..427c5a86 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -77,10 +77,10 @@ const Strategies = () => { return ( <> <VerticalTable tableHead={head} tableBody={body} countPerPage={2} currentPage={1} /> - <StatisticsTable statisticsData={assetManagementData} /> - <StatisticsTable statisticsData={profitLoss} /> - <StatisticsTable statisticsData={ddMddInfo} /> - <StatisticsTable statisticsData={tradingInfo} /> + <StatisticsTable title={'자산 및 운영 정보'} statisticsData={assetManagementData} /> + <StatisticsTable title={'손익률 관련 정보'} statisticsData={profitLoss} /> + <StatisticsTable title={'DD & MDD 정보'} statisticsData={ddMddInfo} /> + <StatisticsTable title={'거래 관련 정보'} statisticsData={tradingInfo} /> </> ) } diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index 43f2c3a0..50e881f0 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -2,10 +2,11 @@ import { inKoreanData } from './inKorean' import styles from './statistics.module.scss' interface Props { + title: string statisticsData: { [key: string]: string | number } } -const StatisticsTable = ({ statisticsData }: Props) => { +const StatisticsTable = ({ title, statisticsData }: Props) => { const inKoreanDataToArray: string[][] = Object.entries(inKoreanData) const mappedDataInKorean: { [key: string]: string | number } = {} @@ -28,6 +29,7 @@ const StatisticsTable = ({ statisticsData }: Props) => { return ( <div className={styles.container}> + <p>{title}</p> <table> <tbody> {groupedData.map((row, index) => ( diff --git a/shared/ui/table/statistics/statistics.module.scss b/shared/ui/table/statistics/statistics.module.scss index dc4b01e8..a4b15362 100644 --- a/shared/ui/table/statistics/statistics.module.scss +++ b/shared/ui/table/statistics/statistics.module.scss @@ -3,6 +3,7 @@ padding: 20px; table { width: 100%; + margin-top: 10px; font-size: $text-c1; td:nth-child(2n + 1) { font-weight: $text-semibold; From 54d46fab709baaca5ece8de27d11da08886ea7cc Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 13 Nov 2024 10:29:57 +0900 Subject: [PATCH 056/648] =?UTF-8?q?rename:=20vertical=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EC=97=90=20=EA=B3=B5=EB=B0=B1=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/page.tsx | 2 +- shared/ui/table/{ vertical => vertical}/index.tsx | 0 shared/ui/table/{ vertical => vertical}/vertical.module.scss | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename shared/ui/table/{ vertical => vertical}/index.tsx (100%) rename shared/ui/table/{ vertical => vertical}/vertical.module.scss (100%) diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 427c5a86..99aa3a69 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -1,5 +1,5 @@ -import VerticalTable from '@/shared/ui/table/ vertical' import StatisticsTable from '@/shared/ui/table/statistics' +import VerticalTable from '@/shared/ui/table/vertical' const Strategies = () => { const head = ['날짜', '원금', '입출금', '일손익', '일손익률', '누적손익', '누적수익률'] diff --git a/shared/ui/table/ vertical/index.tsx b/shared/ui/table/vertical/index.tsx similarity index 100% rename from shared/ui/table/ vertical/index.tsx rename to shared/ui/table/vertical/index.tsx diff --git a/shared/ui/table/ vertical/vertical.module.scss b/shared/ui/table/vertical/vertical.module.scss similarity index 100% rename from shared/ui/table/ vertical/vertical.module.scss rename to shared/ui/table/vertical/vertical.module.scss From 3d440e7cf836dcbb94702dc2f3a36bf5a9948adf Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 13 Nov 2024 14:13:45 +0900 Subject: [PATCH 057/648] =?UTF-8?q?feat:=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table/vertical/stories/table.stories.tsx | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 shared/ui/table/vertical/stories/table.stories.tsx diff --git a/shared/ui/table/vertical/stories/table.stories.tsx b/shared/ui/table/vertical/stories/table.stories.tsx new file mode 100644 index 00000000..9819e6f6 --- /dev/null +++ b/shared/ui/table/vertical/stories/table.stories.tsx @@ -0,0 +1,96 @@ +import type { Meta, StoryFn } from '@storybook/react' + +import VerticalTable from '../index' + +const meta: Meta = { + title: 'Shared/UI/Table', + component: VerticalTable, + tags: ['autodocs'], +} + +const tableHead = ['날짜', '원금', '입출금', '일손익', '일손익률', '누적손익', '누적수익률'] +const tableBody = [ + { + date: '2015-03-12', // 날짜 + principal: 100000000, // 원금 + transaction: 0, // 입출금 + dailyProfitLoss: 332410, // 일 손익 + dailyProfitLossRate: 0.33, // 일 수익률 + cumulativeProfitLoss: 302280, // 누적 손익 + cumulativeProfitLossRate: 0.3, // 누적 수익률 + }, + { + date: '2015-03-13', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + { + date: '2015-03-14', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + { + date: '2015-03-15', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + { + date: '2015-03-16', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + { + date: '2015-03-17', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + { + date: '2015-03-18', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, + { + date: '2015-03-19', + principal: 100000000, + transaction: 0, + dailyProfitLoss: 332410, + dailyProfitLossRate: 0.33, + cumulativeProfitLoss: 302280, + cumulativeProfitLossRate: 0.3, + }, +] + +const Table: StoryFn<{ countPerPage: number }> = (args) => ( + <VerticalTable tableHead={tableHead} tableBody={tableBody} currentPage={1} {...args} /> +) + +export const Primary = Table.bind({}) +Primary.args = { + countPerPage: 7, +} + +export default meta From 6c0eed189d1578b9e537ac8b14984430f8554bbc Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 13 Nov 2024 14:20:49 +0900 Subject: [PATCH 058/648] =?UTF-8?q?feat:=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=98=88?= =?UTF-8?q?=EC=8B=9C=20=EC=82=AD=EC=A0=9C=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/page.tsx | 32 ----------------------------- 1 file changed, 32 deletions(-) diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 99aa3a69..32b5c475 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -1,37 +1,6 @@ import StatisticsTable from '@/shared/ui/table/statistics' -import VerticalTable from '@/shared/ui/table/vertical' const Strategies = () => { - const head = ['날짜', '원금', '입출금', '일손익', '일손익률', '누적손익', '누적수익률'] - const body = [ - { - date: '2015-03-12', // 날짜 - principal: 100000000, // 원금 - transaction: 0, // 입출금 - dailyProfitLoss: 332410, // 일 손익 - dailyProfitLossRate: 0.33, // 일 수익률 - cumulativeProfitLoss: 302280, // 누적 손익 - cumulativeProfitLossRate: 0.3, // 누적 수익률 - }, - { - date: '2015-03-13', - principal: 100000000, - transaction: 0, - dailyProfitLoss: 332410, - dailyProfitLossRate: 0.33, - cumulativeProfitLoss: 302280, - cumulativeProfitLossRate: 0.3, - }, - { - date: '2015-03-14', - principal: 100000000, - transaction: 0, - dailyProfitLoss: 332410, - dailyProfitLossRate: 0.33, - cumulativeProfitLoss: 302280, - cumulativeProfitLossRate: 0.3, - }, - ] // 자산 및 운영 정보 const assetManagementData = { balance: 896217437, // 잔고 @@ -76,7 +45,6 @@ const Strategies = () => { } return ( <> - <VerticalTable tableHead={head} tableBody={body} countPerPage={2} currentPage={1} /> <StatisticsTable title={'자산 및 운영 정보'} statisticsData={assetManagementData} /> <StatisticsTable title={'손익률 관련 정보'} statisticsData={profitLoss} /> <StatisticsTable title={'DD & MDD 정보'} statisticsData={ddMddInfo} /> From 2452be707e72aee1651ff3a309c8728f902d8f40 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 13 Nov 2024 15:13:05 +0900 Subject: [PATCH 059/648] =?UTF-8?q?feat:=20=ED=86=B5=EA=B3=84=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#2?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/page.tsx | 53 +----------- .../stories/statistics-table.stories.tsx | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 52 deletions(-) create mode 100644 shared/ui/table/statistics/stories/statistics-table.stories.tsx diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 32b5c475..bf212969 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -1,56 +1,5 @@ -import StatisticsTable from '@/shared/ui/table/statistics' - const Strategies = () => { - // 자산 및 운영 정보 - const assetManagementData = { - balance: 896217437, // 잔고 - cumulativeTransactionAmount: 896217437, // 누적 거래 금액 - principal: 238704360, // 원금 - operationPeriod: '2년 4월', // 운용 기간 - startDate: '2012-10-11', // 시작 일자 - endDate: '2015-03-11', // 종료 일자 (endDate) - daysSincePeakUpdate: 513, // 고점 갱신 후 경과일 - } - // 손익률 관련 정보 - const profitLoss = { - cumulativeProfitAmount: 247525031, // 누적 수익 금액 - cumulativeProfitRate: 49.24, // 누적 수익률 - maxCumulativeProfitAmount: 247525031, // 최대 누적 수익 금액 - maxCumulativeProfitRate: 49.24, // 최대 누적 수익률 - averageProfitLossAmount: 336311, // 평균 손익 금액 - averageProfitLossRate: 6, // 평균 손익률 - maxDailyProfitAmount: 25257250, // 최대 일 수익 금액 - maxDailyProfitRate: 3.985, // 최대 일 수익률 - maxDailyLossAmount: -17465050, // 최대 일 손실 금액 - maxDailyLossRate: -3.95, // 최대 일 손실률 - roa: 453, // 자산 수익률 (Return on Assets) - profitFactor: 1.48, // Profit Factor - } - // DD&MDD 정보 - const ddMddInfo = { - currentDrawdown: 0, // 현재 자본 인하 금액 - currentDrawdownRate: 0, // 현재 자본 인하율 - maxDrawdown: -54832778, // 최대 자본 인하 금액 - maxDrawdownRate: -13.98, // 최대 자본 인하율 - } - // 거래 관련 정보 - const tradingInfo = { - totalTradeDays: 736, // 총 거래 일수 - totalProfitableDays: 508, // 총 이익 일수 - totalLossDays: 228, // 총 손실 일수 - currentConsecutiveLossDays: 6, // 현재 연속 손실 일수 - maxConsecutiveProfitDays: 22, // 최대 연속 이익 일수 - maxConsecutiveLossDays: 8, // 최대 연속 손실 일수 - winRate: 69, // 승률 - } - return ( - <> - <StatisticsTable title={'자산 및 운영 정보'} statisticsData={assetManagementData} /> - <StatisticsTable title={'손익률 관련 정보'} statisticsData={profitLoss} /> - <StatisticsTable title={'DD & MDD 정보'} statisticsData={ddMddInfo} /> - <StatisticsTable title={'거래 관련 정보'} statisticsData={tradingInfo} /> - </> - ) + return <></> } export default Strategies diff --git a/shared/ui/table/statistics/stories/statistics-table.stories.tsx b/shared/ui/table/statistics/stories/statistics-table.stories.tsx new file mode 100644 index 00000000..dfb8d3a9 --- /dev/null +++ b/shared/ui/table/statistics/stories/statistics-table.stories.tsx @@ -0,0 +1,80 @@ +import type { Meta, StoryFn } from '@storybook/react' + +import StatisticsTable from '../index' + +const meta: Meta = { + title: 'Shared/UI/Statistics-Table', + component: StatisticsTable, + tags: ['autodocs'], +} + +const assetManagementData = { + balance: 896217437, // 잔고 + cumulativeTransactionAmount: 896217437, // 누적 거래 금액 + principal: 238704360, // 원금 + operationPeriod: '2년 4월', // 운용 기간 + startDate: '2012-10-11', // 시작 일자 + endDate: '2015-03-11', // 종료 일자 (endDate) + daysSincePeakUpdate: 513, // 고점 갱신 후 경과일 +} + +const profitLoss = { + cumulativeProfitAmount: 247525031, // 누적 수익 금액 + cumulativeProfitRate: 49.24, // 누적 수익률 + maxCumulativeProfitAmount: 247525031, // 최대 누적 수익 금액 + maxCumulativeProfitRate: 49.24, // 최대 누적 수익률 + averageProfitLossAmount: 336311, // 평균 손익 금액 + averageProfitLossRate: 6, // 평균 손익률 + maxDailyProfitAmount: 25257250, // 최대 일 수익 금액 + maxDailyProfitRate: 3.985, // 최대 일 수익률 + maxDailyLossAmount: -17465050, // 최대 일 손실 금액 + maxDailyLossRate: -3.95, // 최대 일 손실률 + roa: 453, // 자산 수익률 (Return on Assets) + profitFactor: 1.48, // Profit Factor +} + +const ddMddInfo = { + currentDrawdown: 0, // 현재 자본 인하 금액 + currentDrawdownRate: 0, // 현재 자본 인하율 + maxDrawdown: -54832778, // 최대 자본 인하 금액 + maxDrawdownRate: -13.98, // 최대 자본 인하율 +} + +const tradingInfo = { + totalTradeDays: 736, // 총 거래 일수 + totalProfitableDays: 508, // 총 이익 일수 + totalLossDays: 228, // 총 손실 일수 + currentConsecutiveLossDays: 6, // 현재 연속 손실 일수 + maxConsecutiveProfitDays: 22, // 최대 연속 이익 일수 + maxConsecutiveLossDays: 8, // 최대 연속 손실 일수 + winRate: 69, // 승률 +} +const Table: StoryFn<{ title: string; statisticsData: { [key: string]: string | number } }> = ( + args +) => <StatisticsTable {...args} /> + +export const Primary = Table.bind({}) +Primary.args = { + title: '자산 및 운영 정보', + statisticsData: assetManagementData, +} + +export const ProfitLoss = Table.bind({}) +ProfitLoss.args = { + title: '손익률 관련 정보', + statisticsData: profitLoss, +} + +export const DDMddInfo = Table.bind({}) +DDMddInfo.args = { + title: 'DD & MDD 정보', + statisticsData: ddMddInfo, +} + +export const TradingInfo = Table.bind({}) +TradingInfo.args = { + title: '거래 관련 정보', + statisticsData: tradingInfo, +} + +export default meta From 81a18c5cf95b2d1cd43896a7135b4c175654054e Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 13 Nov 2024 16:00:55 +0900 Subject: [PATCH 060/648] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/page.tsx | 4 ++-- shared/ui/table/statistics/index.tsx | 11 +++++++++-- shared/ui/table/vertical/index.tsx | 6 +++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index bf212969..95276f9b 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -1,5 +1,5 @@ -const Strategies = () => { +const StrategiesPage = () => { return <></> } -export default Strategies +export default StrategiesPage diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index 50e881f0..6280efda 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -1,9 +1,16 @@ +import classNames from 'classnames/bind' + import { inKoreanData } from './inKorean' import styles from './statistics.module.scss' +const cx = classNames.bind(styles) + +interface StatisticsDataModel { + [key: string]: string | number +} interface Props { title: string - statisticsData: { [key: string]: string | number } + statisticsData: StatisticsDataModel } const StatisticsTable = ({ title, statisticsData }: Props) => { @@ -28,7 +35,7 @@ const StatisticsTable = ({ title, statisticsData }: Props) => { } return ( - <div className={styles.container}> + <div className={cx('container')}> <p>{title}</p> <table> <tbody> diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 1aae8d59..c9afdc00 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -1,7 +1,11 @@ +import classNames from 'classnames/bind' + import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' import styles from './vertical.module.scss' +const cx = classNames.bind(styles) + type TableBodyDataType = DailyAnalysisModel | MonthlyAnalysisModel interface Props { @@ -18,7 +22,7 @@ const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Prop ) return ( - <div className={styles.container}> + <div className={cx('container')}> <table> <thead> <tr> From 441095f9524fcd9f944e4df6d5cad0c876769ee5 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 13 Nov 2024 16:22:43 +0900 Subject: [PATCH 061/648] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/table/statistics/index.tsx | 28 +++++++++++++++------------- shared/ui/table/vertical/index.tsx | 4 ++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index 6280efda..8c03cfac 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -15,7 +15,6 @@ interface Props { const StatisticsTable = ({ title, statisticsData }: Props) => { const inKoreanDataToArray: string[][] = Object.entries(inKoreanData) - const mappedDataInKorean: { [key: string]: string | number } = {} for (const [key, value] of inKoreanDataToArray) { if (statisticsData[key] !== undefined) { @@ -23,24 +22,27 @@ const StatisticsTable = ({ title, statisticsData }: Props) => { } } - const groupedData = [] - const mappedDataInKoreanToArray = Object.entries(mappedDataInKorean) - for (let i = 0; i < mappedDataInKoreanToArray.length; i += 2) { - groupedData.push([ - mappedDataInKoreanToArray[i][0], - mappedDataInKoreanToArray[i][1], - mappedDataInKoreanToArray[i + 1]?.[0], - mappedDataInKoreanToArray[i + 1]?.[1], - ]) - } + const groupedData = Array.from( + { length: Math.ceil(Object.entries(mappedDataInKorean).length / 2) }, + (_, idx) => { + const index = idx * 2 + const mappedDataInKoreanToArray = Object.entries(mappedDataInKorean) + return [ + mappedDataInKoreanToArray[index][0], + mappedDataInKoreanToArray[index][1], + mappedDataInKoreanToArray[index + 1]?.[0], + mappedDataInKoreanToArray[index + 1]?.[1], + ] + } + ) return ( <div className={cx('container')}> <p>{title}</p> <table> <tbody> - {groupedData.map((row, index) => ( - <tr key={index}> + {groupedData.map((row, idx) => ( + <tr key={idx}> <td>{row[0]}</td> <td>{row[1]}</td> {row[2] && ( diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index c9afdc00..44df9d10 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -34,8 +34,8 @@ const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Prop <tbody> {croppedTableBody.map((row) => ( <tr key={Object.values(row)[0]}> - {Object.entries(row).map((rowData, index) => ( - <td key={index}>{rowData[1]}</td> + {Object.entries(row).map((rowData, idx) => ( + <td key={idx}>{rowData[1]}</td> ))} </tr> ))} From 1ee456c833b9b9b4786db7df43b108f2191ed7cf Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 13 Nov 2024 19:26:23 +0900 Subject: [PATCH 062/648] =?UTF-8?q?feat:=20SideNavigation=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/user.ts | 13 ++ shared/types/user.ts | 9 ++ shared/ui/side-navigation/button-item.tsx | 26 ++++ shared/ui/side-navigation/index.tsx | 39 ++++++ shared/ui/side-navigation/link-item.tsx | 33 +++++ shared/ui/side-navigation/styles.module.scss | 113 ++++++++++++++++++ shared/ui/side-navigation/user-navigation.tsx | 46 +++++++ 7 files changed, 279 insertions(+) create mode 100644 shared/api/user.ts create mode 100644 shared/types/user.ts create mode 100644 shared/ui/side-navigation/button-item.tsx create mode 100644 shared/ui/side-navigation/index.tsx create mode 100644 shared/ui/side-navigation/link-item.tsx create mode 100644 shared/ui/side-navigation/styles.module.scss create mode 100644 shared/ui/side-navigation/user-navigation.tsx diff --git a/shared/api/user.ts b/shared/api/user.ts new file mode 100644 index 00000000..51399bb9 --- /dev/null +++ b/shared/api/user.ts @@ -0,0 +1,13 @@ +import { UserModel } from '@/shared/types/user' + +export const fetchUser = () => { + const user: UserModel = { + id: '123', + imageUrl: 'S3 링크', + nickname: '김아무개씨', + email: 'kimamugae@naver.com', + role: 'trader_admin', + } + + return user +} diff --git a/shared/types/user.ts b/shared/types/user.ts new file mode 100644 index 00000000..7af7ea12 --- /dev/null +++ b/shared/types/user.ts @@ -0,0 +1,9 @@ +export interface UserModel { + id: string + imageUrl: string + nickname: string + email: string + role: RoleType +} + +export type RoleType = 'trader' | 'investor' | 'trader_admin' | 'investor_admin' diff --git a/shared/ui/side-navigation/button-item.tsx b/shared/ui/side-navigation/button-item.tsx new file mode 100644 index 00000000..e8e05c0c --- /dev/null +++ b/shared/ui/side-navigation/button-item.tsx @@ -0,0 +1,26 @@ +'use client' + +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + icon: React.ElementType + onClick: React.MouseEventHandler<HTMLButtonElement> + children: React.ReactNode +} + +const ButtonItem = ({ icon: Icon, onClick, children }: Props) => { + return ( + <li className={cx('navigation-item')}> + <button type="button" onClick={onClick} className={cx('button')}> + <Icon className={cx('icon')} /> + <span className={cx('text')}>{children}</span> + </button> + </li> + ) +} + +export default ButtonItem diff --git a/shared/ui/side-navigation/index.tsx b/shared/ui/side-navigation/index.tsx new file mode 100644 index 00000000..e1807bdd --- /dev/null +++ b/shared/ui/side-navigation/index.tsx @@ -0,0 +1,39 @@ +import Link from 'next/link' + +import Logo from '@/public/images/logo.svg' +import TextLogo from '@/public/images/text-logo.svg' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import UserNavigation from '@/shared/ui/side-navigation/user-navigation' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + children: React.ReactNode +} + +const SideNavigation = ({ children }: Props) => { + return ( + <aside className={cx('aside')}> + <header> + <h1> + <Link href={PATH.HOME} className={cx('logo')}> + <Logo /> + <TextLogo className={cx('text')} /> + </Link> + </h1> + </header> + + <nav className={cx('main-navigation')} aria-label="메인 메뉴"> + <ul>{children}</ul> + </nav> + + <UserNavigation /> + </aside> + ) +} + +export default SideNavigation diff --git a/shared/ui/side-navigation/link-item.tsx b/shared/ui/side-navigation/link-item.tsx new file mode 100644 index 00000000..bf3b449d --- /dev/null +++ b/shared/ui/side-navigation/link-item.tsx @@ -0,0 +1,33 @@ +'use client' + +import Link from 'next/link' +import { usePathname } from 'next/navigation' + +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + href: string + icon: React.ElementType + children: React.ReactNode + textClassName?: keyof typeof styles +} + +const LinkItem = ({ href, icon: Icon, textClassName, children }: Props) => { + const path = usePathname() + const isActive = path.startsWith(href) + + return ( + <li className={cx('navigation-item')}> + <Link href={href} className={cx('link', isActive && 'active')}> + <Icon className={cx('icon')} /> + <span className={cx('text', textClassName)}>{children}</span> + </Link> + </li> + ) +} + +export default LinkItem diff --git a/shared/ui/side-navigation/styles.module.scss b/shared/ui/side-navigation/styles.module.scss new file mode 100644 index 00000000..9b703502 --- /dev/null +++ b/shared/ui/side-navigation/styles.module.scss @@ -0,0 +1,113 @@ +.aside { + position: fixed; + top: 0; + left: 0; + bottom: 0; + display: flex; + flex-direction: column; + width: 240px; + border-right: 1px solid $color-gray-200; + background-color: $color-gray-100; + transition: width 0.2s ease; + cursor: pointer; + + &:not(:hover) { + width: 90px; + + .text { + opacity: 0; + transform: translateX(10px); + } + } +} + +.logo { + display: flex; + align-items: center; + gap: 12px; + padding: 24px 0 0 24px; + + svg { + flex-shrink: 0; + } +} + +.main-navigation { + flex: 1; + padding: 16px; + margin-top: 48px; +} + +.user-navigation { + padding: 24px 0; + margin: 0 16px; + border-top: 1px solid $color-gray-200; +} + +.navigation-item { + margin: 0 4px 8px; + + &:last-child { + margin-bottom: 0; + } +} + +.button, +.link { + display: flex; + align-items: center; + gap: 24px; + width: 100%; + padding: 10px 12px; + border: none; + border-radius: 4px; + text-align: left; + font-weight: $text-semibold; + white-space: nowrap; + background: none; + cursor: pointer; + + .icon { + width: 24px; + height: 24px; + min-width: 24px; + display: flex; + align-items: center; + justify-content: center; + fill: $color-gray-500; + } + + .text { + color: $color-gray-500; + line-height: normal; + } + + &.active { + background-color: $color-orange-100; + + svg.icon { + fill: $color-orange-500; + } + + .text { + color: $color-orange-500; + } + } + + &:hover:not(.active) { + background-color: $color-gray-200; + } +} + +.user { + display: flex; + flex-direction: column; + + .nickname { + color: $color-gray-800; + } + + .email { + font-size: $text-c2; + } +} diff --git a/shared/ui/side-navigation/user-navigation.tsx b/shared/ui/side-navigation/user-navigation.tsx new file mode 100644 index 00000000..f88f97d0 --- /dev/null +++ b/shared/ui/side-navigation/user-navigation.tsx @@ -0,0 +1,46 @@ +'use client' + +import { usePathname } from 'next/navigation' + +import { ChangeIcon, ProfileIcon, SignOutIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { fetchUser } from '@/shared/api/user' +import { PATH } from '@/shared/constants/path' +import ButtonItem from '@/shared/ui/side-navigation/button-item' +import LinkItem from '@/shared/ui/side-navigation/link-item' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const UserNavigation = () => { + const path = usePathname() + const isAdminPage = path.startsWith(PATH.ADMIN) + + const user = fetchUser() + const isAdmin = user.role.includes('admin') + + return ( + <nav className={cx('user-navigation')} aria-label="사용자 메뉴"> + <ul> + <LinkItem href={'/profile'} icon={ProfileIcon} textClassName="user"> + <span className={cx('nickname')}>{user.nickname}</span> + <span className={cx('email')}>{user.email}</span> + </LinkItem> + + {isAdmin && ( + <LinkItem href={isAdminPage ? PATH.STRATEGIES : PATH.ADMIN_USERS} icon={ChangeIcon}> + {isAdminPage ? '메인 대시보드' : '관리자 대시보드'} + </LinkItem> + )} + + <ButtonItem icon={SignOutIcon} onClick={() => {}}> + 로그아웃 + </ButtonItem> + </ul> + </nav> + ) +} + +export default UserNavigation From 4331ed98bcf5b05c5298be55093d95b73ffda441 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 13 Nov 2024 19:29:52 +0900 Subject: [PATCH 063/648] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=20=EB=8C=80?= =?UTF-8?q?=EC=8B=9C=EB=B3=B4=EB=93=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20=EC=B6=94=EA=B0=80=20(#2?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/navigation.tsx | 43 +++++++++++++++++++++++++++++ app/(dashboard)/layout.tsx | 12 ++++++++ app/(dashboard)/strategies/.gitkeep | 0 app/(dashboard)/strategies/page.tsx | 5 ++++ 4 files changed, 60 insertions(+) create mode 100644 app/(dashboard)/_ui/navigation.tsx create mode 100644 app/(dashboard)/layout.tsx delete mode 100644 app/(dashboard)/strategies/.gitkeep create mode 100644 app/(dashboard)/strategies/page.tsx diff --git a/app/(dashboard)/_ui/navigation.tsx b/app/(dashboard)/_ui/navigation.tsx new file mode 100644 index 00000000..c1c40903 --- /dev/null +++ b/app/(dashboard)/_ui/navigation.tsx @@ -0,0 +1,43 @@ +'use client' + +import { + FavoriteIcon, + QuestionIcon, + StrategyIcon, + StrategyRankingIcon, + TradersIcon, +} from '@/public/icons' + +import { fetchUser } from '@/shared/api/user' +import { PATH } from '@/shared/constants/path' +import SideNavigation from '@/shared/ui/side-navigation' +import LinkItem from '@/shared/ui/side-navigation/link-item' + +const DashboardNavigation = () => { + const user = fetchUser() + const isTrader = user.role.includes('trader') + + return ( + <SideNavigation> + <LinkItem href={PATH.STRATEGIES} icon={StrategyRankingIcon}> + 전략 랭킹 + </LinkItem> + <LinkItem href={PATH.TRADERS} icon={TradersIcon}> + 트레이더 목록 + </LinkItem> + {isTrader && ( + <LinkItem href={PATH.MY_STRATEGIES} icon={StrategyIcon}> + 나의 전략 + </LinkItem> + )} + <LinkItem href={PATH.FAVORITES} icon={FavoriteIcon}> + 구독한 전략 + </LinkItem> + <LinkItem href={PATH.MY_QUESTIONS} icon={QuestionIcon}> + 문의 내역 + </LinkItem> + </SideNavigation> + ) +} + +export default DashboardNavigation diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx new file mode 100644 index 00000000..48d78914 --- /dev/null +++ b/app/(dashboard)/layout.tsx @@ -0,0 +1,12 @@ +import DashboardNavigation from './_ui/navigation' + +const DashboardLayout = ({ children }: { children: React.ReactNode }) => { + return ( + <> + <DashboardNavigation /> + <main>{children}</main> + </> + ) +} + +export default DashboardLayout diff --git a/app/(dashboard)/strategies/.gitkeep b/app/(dashboard)/strategies/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx new file mode 100644 index 00000000..95276f9b --- /dev/null +++ b/app/(dashboard)/strategies/page.tsx @@ -0,0 +1,5 @@ +const StrategiesPage = () => { + return <></> +} + +export default StrategiesPage From e55a5a4a40a42bcedd916cea924960625404a04b Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 13 Nov 2024 19:30:28 +0900 Subject: [PATCH 064/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/_ui/navigation.tsx | 43 ++++++++++++++++++++++++++++++++++++ app/admin/layout.tsx | 12 ++++++++++ app/admin/page.tsx | 5 ----- app/admin/users/.gitkeep | 0 app/admin/users/page.tsx | 5 +++++ 5 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 app/admin/_ui/navigation.tsx create mode 100644 app/admin/layout.tsx delete mode 100644 app/admin/page.tsx delete mode 100644 app/admin/users/.gitkeep create mode 100644 app/admin/users/page.tsx diff --git a/app/admin/_ui/navigation.tsx b/app/admin/_ui/navigation.tsx new file mode 100644 index 00000000..8d8845ed --- /dev/null +++ b/app/admin/_ui/navigation.tsx @@ -0,0 +1,43 @@ +'use client' + +import { + CategoryIcon, + NoticeIcon, + QuestionIcon, + StrategyRankingIcon, + TradersIcon, +} from '@/public/icons' + +import { fetchUser } from '@/shared/api/user' +import { PATH } from '@/shared/constants/path' +import SideNavigation from '@/shared/ui/side-navigation' +import LinkItem from '@/shared/ui/side-navigation/link-item' + +const AdminNavigation = () => { + const user = fetchUser() + const isTrader = user.role.includes('trader') + + return ( + <SideNavigation> + <LinkItem href={PATH.ADMIN_USERS} icon={TradersIcon}> + 회원 관리 + </LinkItem> + <LinkItem href={PATH.ADMIN_NOTICES} icon={NoticeIcon}> + 공지사항 + </LinkItem> + {isTrader && ( + <LinkItem href={PATH.ADMIN_CATEGORY} icon={CategoryIcon}> + 종목 및 매매 유형 + </LinkItem> + )} + <LinkItem href={PATH.ADMIN_STRATEGIES} icon={StrategyRankingIcon}> + 전략 관리 + </LinkItem> + <LinkItem href={PATH.ADMIN_QUESTIONS} icon={QuestionIcon}> + 문의 내역 + </LinkItem> + </SideNavigation> + ) +} + +export default AdminNavigation diff --git a/app/admin/layout.tsx b/app/admin/layout.tsx new file mode 100644 index 00000000..b9eade5e --- /dev/null +++ b/app/admin/layout.tsx @@ -0,0 +1,12 @@ +import AdminNavigation from './_ui/navigation' + +const DashboardLayout = ({ children }: { children: React.ReactNode }) => { + return ( + <> + <AdminNavigation /> + <main>{children}</main> + </> + ) +} + +export default DashboardLayout diff --git a/app/admin/page.tsx b/app/admin/page.tsx deleted file mode 100644 index a9e1c38e..00000000 --- a/app/admin/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const AdminPage = () => { - return <>AdminPage</> -} - -export default AdminPage diff --git a/app/admin/users/.gitkeep b/app/admin/users/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/admin/users/page.tsx b/app/admin/users/page.tsx new file mode 100644 index 00000000..024603db --- /dev/null +++ b/app/admin/users/page.tsx @@ -0,0 +1,5 @@ +const AdminUsersPage = () => { + return <></> +} + +export default AdminUsersPage From eb4b929dd5156e3caf4d9423c098f436bd817cc5 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 13 Nov 2024 19:42:11 +0900 Subject: [PATCH 065/648] =?UTF-8?q?remove:table=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=82=B4=EC=9D=98=20stories=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/{stories => }/statistics-table.stories.tsx | 4 ++-- shared/ui/table/vertical/{stories => }/table.stories.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename shared/ui/table/statistics/{stories => }/statistics-table.stories.tsx (96%) rename shared/ui/table/vertical/{stories => }/table.stories.tsx (97%) diff --git a/shared/ui/table/statistics/stories/statistics-table.stories.tsx b/shared/ui/table/statistics/statistics-table.stories.tsx similarity index 96% rename from shared/ui/table/statistics/stories/statistics-table.stories.tsx rename to shared/ui/table/statistics/statistics-table.stories.tsx index dfb8d3a9..227f2f75 100644 --- a/shared/ui/table/statistics/stories/statistics-table.stories.tsx +++ b/shared/ui/table/statistics/statistics-table.stories.tsx @@ -1,9 +1,9 @@ import type { Meta, StoryFn } from '@storybook/react' -import StatisticsTable from '../index' +import StatisticsTable from './index' const meta: Meta = { - title: 'Shared/UI/Statistics-Table', + title: 'component/Statistics-Table', component: StatisticsTable, tags: ['autodocs'], } diff --git a/shared/ui/table/vertical/stories/table.stories.tsx b/shared/ui/table/vertical/table.stories.tsx similarity index 97% rename from shared/ui/table/vertical/stories/table.stories.tsx rename to shared/ui/table/vertical/table.stories.tsx index 9819e6f6..4e2bd5ef 100644 --- a/shared/ui/table/vertical/stories/table.stories.tsx +++ b/shared/ui/table/vertical/table.stories.tsx @@ -1,9 +1,9 @@ import type { Meta, StoryFn } from '@storybook/react' -import VerticalTable from '../index' +import VerticalTable from './index' const meta: Meta = { - title: 'Shared/UI/Table', + title: 'component/Table', component: VerticalTable, tags: ['autodocs'], } From 3d8b1b2796a14c83b4e04407495eab0493fccc0f Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 13 Nov 2024 21:27:01 +0900 Subject: [PATCH 066/648] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B3=A0=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=9B=B9=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=84=B1=20=ED=96=A5=EC=83=81=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 웹 접근성 향상을 위해 SVG 로고에 aria-label 추가 --- shared/ui/side-navigation/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/side-navigation/index.tsx b/shared/ui/side-navigation/index.tsx index e1807bdd..b03434e5 100644 --- a/shared/ui/side-navigation/index.tsx +++ b/shared/ui/side-navigation/index.tsx @@ -22,7 +22,7 @@ const SideNavigation = ({ children }: Props) => { <h1> <Link href={PATH.HOME} className={cx('logo')}> <Logo /> - <TextLogo className={cx('text')} /> + <TextLogo className={cx('text')} aria-label="인베스트메틱" /> </Link> </h1> </header> From 7d07564bb77e6247d6c31d8bfa4242041d7e3147 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 14 Nov 2024 11:51:14 +0900 Subject: [PATCH 067/648] =?UTF-8?q?feat:=20PATH=EC=97=90=20profile=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 1 + shared/ui/side-navigation/user-navigation.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/shared/constants/path.ts b/shared/constants/path.ts index 43957502..91bd0fd8 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -9,6 +9,7 @@ export const PATH = { TRADERS: '/traders', // My + PROFILE: 'my/profile', FAVORITES: '/my/favorites', MY_STRATEGIES: '/my/strategies', STRATEGIES_MANAGE: '/my/strategies/manage', diff --git a/shared/ui/side-navigation/user-navigation.tsx b/shared/ui/side-navigation/user-navigation.tsx index f88f97d0..9899e4c9 100644 --- a/shared/ui/side-navigation/user-navigation.tsx +++ b/shared/ui/side-navigation/user-navigation.tsx @@ -24,7 +24,7 @@ const UserNavigation = () => { return ( <nav className={cx('user-navigation')} aria-label="사용자 메뉴"> <ul> - <LinkItem href={'/profile'} icon={ProfileIcon} textClassName="user"> + <LinkItem href={PATH.PROFILE} icon={ProfileIcon} textClassName="user"> <span className={cx('nickname')}>{user.nickname}</span> <span className={cx('email')}>{user.email}</span> </LinkItem> From 4cd3861880668e7c8349cde8a61f76f5e44bf6b0 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 14 Nov 2024 11:55:45 +0900 Subject: [PATCH 068/648] =?UTF-8?q?rename:=20Navigation=EC=9D=98=20LinkIte?= =?UTF-8?q?m=EA=B3=BC=20ButtonItem=20=EC=9D=B4=EB=A6=84=20=EA=B5=AC?= =?UTF-8?q?=EC=B2=B4=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#2?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/navigation.tsx | 22 +++++++++---------- app/admin/_ui/navigation.tsx | 22 +++++++++---------- .../{button-item.tsx => nav-button-item.tsx} | 4 ++-- .../{link-item.tsx => nav-link-item.tsx} | 4 ++-- shared/ui/side-navigation/user-navigation.tsx | 16 +++++++------- 5 files changed, 34 insertions(+), 34 deletions(-) rename shared/ui/side-navigation/{button-item.tsx => nav-button-item.tsx} (83%) rename shared/ui/side-navigation/{link-item.tsx => nav-link-item.tsx} (86%) diff --git a/app/(dashboard)/_ui/navigation.tsx b/app/(dashboard)/_ui/navigation.tsx index c1c40903..da21fa54 100644 --- a/app/(dashboard)/_ui/navigation.tsx +++ b/app/(dashboard)/_ui/navigation.tsx @@ -11,7 +11,7 @@ import { import { fetchUser } from '@/shared/api/user' import { PATH } from '@/shared/constants/path' import SideNavigation from '@/shared/ui/side-navigation' -import LinkItem from '@/shared/ui/side-navigation/link-item' +import NavLinkItem from '@/shared/ui/side-navigation/nav-link-item' const DashboardNavigation = () => { const user = fetchUser() @@ -19,23 +19,23 @@ const DashboardNavigation = () => { return ( <SideNavigation> - <LinkItem href={PATH.STRATEGIES} icon={StrategyRankingIcon}> + <NavLinkItem href={PATH.STRATEGIES} icon={StrategyRankingIcon}> 전략 랭킹 - </LinkItem> - <LinkItem href={PATH.TRADERS} icon={TradersIcon}> + </NavLinkItem> + <NavLinkItem href={PATH.TRADERS} icon={TradersIcon}> 트레이더 목록 - </LinkItem> + </NavLinkItem> {isTrader && ( - <LinkItem href={PATH.MY_STRATEGIES} icon={StrategyIcon}> + <NavLinkItem href={PATH.MY_STRATEGIES} icon={StrategyIcon}> 나의 전략 - </LinkItem> + </NavLinkItem> )} - <LinkItem href={PATH.FAVORITES} icon={FavoriteIcon}> + <NavLinkItem href={PATH.FAVORITES} icon={FavoriteIcon}> 구독한 전략 - </LinkItem> - <LinkItem href={PATH.MY_QUESTIONS} icon={QuestionIcon}> + </NavLinkItem> + <NavLinkItem href={PATH.MY_QUESTIONS} icon={QuestionIcon}> 문의 내역 - </LinkItem> + </NavLinkItem> </SideNavigation> ) } diff --git a/app/admin/_ui/navigation.tsx b/app/admin/_ui/navigation.tsx index 8d8845ed..6f062a8a 100644 --- a/app/admin/_ui/navigation.tsx +++ b/app/admin/_ui/navigation.tsx @@ -11,7 +11,7 @@ import { import { fetchUser } from '@/shared/api/user' import { PATH } from '@/shared/constants/path' import SideNavigation from '@/shared/ui/side-navigation' -import LinkItem from '@/shared/ui/side-navigation/link-item' +import NavLinkItem from '@/shared/ui/side-navigation/nav-link-item' const AdminNavigation = () => { const user = fetchUser() @@ -19,23 +19,23 @@ const AdminNavigation = () => { return ( <SideNavigation> - <LinkItem href={PATH.ADMIN_USERS} icon={TradersIcon}> + <NavLinkItem href={PATH.ADMIN_USERS} icon={TradersIcon}> 회원 관리 - </LinkItem> - <LinkItem href={PATH.ADMIN_NOTICES} icon={NoticeIcon}> + </NavLinkItem> + <NavLinkItem href={PATH.ADMIN_NOTICES} icon={NoticeIcon}> 공지사항 - </LinkItem> + </NavLinkItem> {isTrader && ( - <LinkItem href={PATH.ADMIN_CATEGORY} icon={CategoryIcon}> + <NavLinkItem href={PATH.ADMIN_CATEGORY} icon={CategoryIcon}> 종목 및 매매 유형 - </LinkItem> + </NavLinkItem> )} - <LinkItem href={PATH.ADMIN_STRATEGIES} icon={StrategyRankingIcon}> + <NavLinkItem href={PATH.ADMIN_STRATEGIES} icon={StrategyRankingIcon}> 전략 관리 - </LinkItem> - <LinkItem href={PATH.ADMIN_QUESTIONS} icon={QuestionIcon}> + </NavLinkItem> + <NavLinkItem href={PATH.ADMIN_QUESTIONS} icon={QuestionIcon}> 문의 내역 - </LinkItem> + </NavLinkItem> </SideNavigation> ) } diff --git a/shared/ui/side-navigation/button-item.tsx b/shared/ui/side-navigation/nav-button-item.tsx similarity index 83% rename from shared/ui/side-navigation/button-item.tsx rename to shared/ui/side-navigation/nav-button-item.tsx index e8e05c0c..3c2ce0a3 100644 --- a/shared/ui/side-navigation/button-item.tsx +++ b/shared/ui/side-navigation/nav-button-item.tsx @@ -12,7 +12,7 @@ interface Props { children: React.ReactNode } -const ButtonItem = ({ icon: Icon, onClick, children }: Props) => { +const NavButtonItem = ({ icon: Icon, onClick, children }: Props) => { return ( <li className={cx('navigation-item')}> <button type="button" onClick={onClick} className={cx('button')}> @@ -23,4 +23,4 @@ const ButtonItem = ({ icon: Icon, onClick, children }: Props) => { ) } -export default ButtonItem +export default NavButtonItem diff --git a/shared/ui/side-navigation/link-item.tsx b/shared/ui/side-navigation/nav-link-item.tsx similarity index 86% rename from shared/ui/side-navigation/link-item.tsx rename to shared/ui/side-navigation/nav-link-item.tsx index bf3b449d..783cecf4 100644 --- a/shared/ui/side-navigation/link-item.tsx +++ b/shared/ui/side-navigation/nav-link-item.tsx @@ -16,7 +16,7 @@ interface Props { textClassName?: keyof typeof styles } -const LinkItem = ({ href, icon: Icon, textClassName, children }: Props) => { +const NavLinkItem = ({ href, icon: Icon, textClassName, children }: Props) => { const path = usePathname() const isActive = path.startsWith(href) @@ -30,4 +30,4 @@ const LinkItem = ({ href, icon: Icon, textClassName, children }: Props) => { ) } -export default LinkItem +export default NavLinkItem diff --git a/shared/ui/side-navigation/user-navigation.tsx b/shared/ui/side-navigation/user-navigation.tsx index 9899e4c9..c680bb66 100644 --- a/shared/ui/side-navigation/user-navigation.tsx +++ b/shared/ui/side-navigation/user-navigation.tsx @@ -7,8 +7,8 @@ import classNames from 'classnames/bind' import { fetchUser } from '@/shared/api/user' import { PATH } from '@/shared/constants/path' -import ButtonItem from '@/shared/ui/side-navigation/button-item' -import LinkItem from '@/shared/ui/side-navigation/link-item' +import NavButtonItem from '@/shared/ui/side-navigation/nav-button-item' +import NavLinkItem from '@/shared/ui/side-navigation/nav-link-item' import styles from './styles.module.scss' @@ -24,20 +24,20 @@ const UserNavigation = () => { return ( <nav className={cx('user-navigation')} aria-label="사용자 메뉴"> <ul> - <LinkItem href={PATH.PROFILE} icon={ProfileIcon} textClassName="user"> + <NavLinkItem href={PATH.PROFILE} icon={ProfileIcon} textClassName="user"> <span className={cx('nickname')}>{user.nickname}</span> <span className={cx('email')}>{user.email}</span> - </LinkItem> + </NavLinkItem> {isAdmin && ( - <LinkItem href={isAdminPage ? PATH.STRATEGIES : PATH.ADMIN_USERS} icon={ChangeIcon}> + <NavLinkItem href={isAdminPage ? PATH.STRATEGIES : PATH.ADMIN_USERS} icon={ChangeIcon}> {isAdminPage ? '메인 대시보드' : '관리자 대시보드'} - </LinkItem> + </NavLinkItem> )} - <ButtonItem icon={SignOutIcon} onClick={() => {}}> + <NavButtonItem icon={SignOutIcon} onClick={() => {}}> 로그아웃 - </ButtonItem> + </NavButtonItem> </ul> </nav> ) From 099a2b9780fcdb28b784786ff6e6a36fef4fee5c Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 14 Nov 2024 11:58:14 +0900 Subject: [PATCH 069/648] =?UTF-8?q?feat:=20Admin=20Navigation=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A1=B0=EA=B1=B4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/_ui/navigation.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app/admin/_ui/navigation.tsx b/app/admin/_ui/navigation.tsx index 6f062a8a..2bcf0455 100644 --- a/app/admin/_ui/navigation.tsx +++ b/app/admin/_ui/navigation.tsx @@ -8,15 +8,11 @@ import { TradersIcon, } from '@/public/icons' -import { fetchUser } from '@/shared/api/user' import { PATH } from '@/shared/constants/path' import SideNavigation from '@/shared/ui/side-navigation' import NavLinkItem from '@/shared/ui/side-navigation/nav-link-item' const AdminNavigation = () => { - const user = fetchUser() - const isTrader = user.role.includes('trader') - return ( <SideNavigation> <NavLinkItem href={PATH.ADMIN_USERS} icon={TradersIcon}> @@ -25,11 +21,9 @@ const AdminNavigation = () => { <NavLinkItem href={PATH.ADMIN_NOTICES} icon={NoticeIcon}> 공지사항 </NavLinkItem> - {isTrader && ( - <NavLinkItem href={PATH.ADMIN_CATEGORY} icon={CategoryIcon}> - 종목 및 매매 유형 - </NavLinkItem> - )} + <NavLinkItem href={PATH.ADMIN_CATEGORY} icon={CategoryIcon}> + 종목 및 매매 유형 + </NavLinkItem> <NavLinkItem href={PATH.ADMIN_STRATEGIES} icon={StrategyRankingIcon}> 전략 관리 </NavLinkItem> From f36171f0b6eb69344a2846447e1371cf3970add0 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 14 Nov 2024 12:04:51 +0900 Subject: [PATCH 070/648] =?UTF-8?q?refactor:=20NavHeader=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC=20(#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/side-navigation/index.tsx | 15 ++------------ shared/ui/side-navigation/nav-header.tsx | 26 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 shared/ui/side-navigation/nav-header.tsx diff --git a/shared/ui/side-navigation/index.tsx b/shared/ui/side-navigation/index.tsx index b03434e5..f5b87480 100644 --- a/shared/ui/side-navigation/index.tsx +++ b/shared/ui/side-navigation/index.tsx @@ -1,10 +1,6 @@ -import Link from 'next/link' - -import Logo from '@/public/images/logo.svg' -import TextLogo from '@/public/images/text-logo.svg' import classNames from 'classnames/bind' -import { PATH } from '@/shared/constants/path' +import NavHeader from '@/shared/ui/side-navigation/nav-header' import UserNavigation from '@/shared/ui/side-navigation/user-navigation' import styles from './styles.module.scss' @@ -18,14 +14,7 @@ interface Props { const SideNavigation = ({ children }: Props) => { return ( <aside className={cx('aside')}> - <header> - <h1> - <Link href={PATH.HOME} className={cx('logo')}> - <Logo /> - <TextLogo className={cx('text')} aria-label="인베스트메틱" /> - </Link> - </h1> - </header> + <NavHeader /> <nav className={cx('main-navigation')} aria-label="메인 메뉴"> <ul>{children}</ul> diff --git a/shared/ui/side-navigation/nav-header.tsx b/shared/ui/side-navigation/nav-header.tsx new file mode 100644 index 00000000..64e247d3 --- /dev/null +++ b/shared/ui/side-navigation/nav-header.tsx @@ -0,0 +1,26 @@ +import Link from 'next/link' + +import Logo from '@/public/images/logo.svg' +import TextLogo from '@/public/images/text-logo.svg' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const NavHeader = () => { + return ( + <header> + <h1> + <Link href={PATH.HOME} className={cx('logo')}> + <Logo /> + <TextLogo className={cx('text')} aria-label="인베스트메틱" /> + </Link> + </h1> + </header> + ) +} + +export default NavHeader From 62815b1ab8e69d11b98007953a24b2062a00a4b4 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 14 Nov 2024 13:07:09 +0900 Subject: [PATCH 071/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=95=98=EC=9C=84=20=EB=9D=BC=EC=9A=B0=ED=8A=B8?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=83=81=EC=88=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shared/constants/path.ts b/shared/constants/path.ts index 91bd0fd8..552fb193 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -2,6 +2,10 @@ export const PATH = { // Auth SIGN_IN: '/signin', SIGN_UP: '/signup', + SIGN_UP_USER_TYPE: '/signup/user-type', + SIGN_UP_TERMS_OF_USE: '/signup/terms-of-use', + SIGN_UP_INFORMATION: '/signup/information', + SIGN_UP_COMPLETE: '/signup/complete', // Main HOME: '/', From 3798ea1e0c751f7c99bde1813c5abac8f9d03ab4 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 14 Nov 2024 13:09:19 +0900 Subject: [PATCH 072/648] =?UTF-8?q?design:=20=EA=B8=80=EB=A1=9C=EB=B2=8C?= =?UTF-8?q?=20background=20color=20=EC=88=98=EC=A0=95=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/global.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/styles/global.scss b/shared/styles/global.scss index 48aca0d4..0d3439cf 100644 --- a/shared/styles/global.scss +++ b/shared/styles/global.scss @@ -8,7 +8,7 @@ body { letter-spacing: -0.02em; line-height: 132%; color: $color-black; - background-color: $color-white; + background-color: $color-gray-100; } input, From c2086d0f56ecbf9beaf28b844d1672ab9be69893 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 14 Nov 2024 13:10:38 +0900 Subject: [PATCH 073/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20step=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/index.tsx | 35 ++++++++++++ app/(landing)/signup/_ui/step/step-item.tsx | 54 +++++++++++++++++++ .../signup/_ui/step/style.module.scss | 45 ++++++++++++++++ app/(landing)/signup/complete/page.tsx | 16 ++++++ app/(landing)/signup/information/page.tsx | 17 ++++++ app/(landing)/signup/terms-of-use/page.tsx | 17 ++++++ app/(landing)/signup/user-type/page.tsx | 16 ++++++ 7 files changed, 200 insertions(+) create mode 100644 app/(landing)/signup/_ui/step/index.tsx create mode 100644 app/(landing)/signup/_ui/step/step-item.tsx create mode 100644 app/(landing)/signup/_ui/step/style.module.scss create mode 100644 app/(landing)/signup/complete/page.tsx create mode 100644 app/(landing)/signup/information/page.tsx create mode 100644 app/(landing)/signup/terms-of-use/page.tsx create mode 100644 app/(landing)/signup/user-type/page.tsx diff --git a/app/(landing)/signup/_ui/step/index.tsx b/app/(landing)/signup/_ui/step/index.tsx new file mode 100644 index 00000000..dd2f53ab --- /dev/null +++ b/app/(landing)/signup/_ui/step/index.tsx @@ -0,0 +1,35 @@ +'use client' + +import StepItem from '@/app/(landing)/signup/_ui/step/step-item' +import { BarsIcon, CheckIcon, PencilIcon, ProfileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' + +import styles from './style.module.scss' + +const cx = classNames.bind(styles) + +const Step = () => { + return ( + <div className={cx('step')}> + <StepItem step={1} pathname={PATH.SIGN_UP_USER_TYPE} icon={ProfileIcon}> + 회원 선택 + </StepItem> + <div className={cx('bar')}> </div> + <StepItem step={2} pathname={PATH.SIGN_UP_TERMS_OF_USE} icon={BarsIcon}> + 약관 동의 + </StepItem> + <div className={cx('bar')}> </div> + <StepItem step={3} pathname={PATH.SIGN_UP_INFORMATION} icon={PencilIcon}> + 정보 입력 + </StepItem> + <div className={cx('bar')}> </div> + <StepItem step={4} pathname={PATH.SIGN_UP_COMPLETE} icon={CheckIcon}> + 가입 완료 + </StepItem> + </div> + ) +} + +export default Step diff --git a/app/(landing)/signup/_ui/step/step-item.tsx b/app/(landing)/signup/_ui/step/step-item.tsx new file mode 100644 index 00000000..b2639fd8 --- /dev/null +++ b/app/(landing)/signup/_ui/step/step-item.tsx @@ -0,0 +1,54 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { useEffect, useState } from 'react' + +import { usePathname } from 'next/navigation' + +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' + +import styles from './style.module.scss' + +const cx = classNames.bind(styles) + +const STEP_HISTORY = [ + PATH.SIGN_UP_USER_TYPE, + PATH.SIGN_UP_TERMS_OF_USE, + PATH.SIGN_UP_INFORMATION, + PATH.SIGN_UP_COMPLETE, +] + +interface Props { + children: React.ReactNode + step: number + pathname: string + icon: React.ElementType +} + +const StepItem = ({ children, step, pathname, icon: Icon }: Props) => { + const [isDone, setIsDone] = useState(false) + const currentPath = usePathname() + + useEffect(() => { + validateIsDoneStatus() + }, [currentPath]) + + const isCurrentStep = currentPath === pathname + + const validateIsDoneStatus = () => { + const currentPathIdx = STEP_HISTORY.findIndex((path) => path === currentPath) + if (currentPathIdx > step - 1) { + setIsDone(true) + } + } + return ( + <div className={cx('step-item')}> + <div className={cx('circle', isCurrentStep && 'current', isDone && 'done')}> + {isCurrentStep || isDone ? <Icon className={cx('icon')} /> : step} + </div> + <p className={cx(isCurrentStep && 'current', isDone && 'done')}>{children}</p> + </div> + ) +} + +export default StepItem diff --git a/app/(landing)/signup/_ui/step/style.module.scss b/app/(landing)/signup/_ui/step/style.module.scss new file mode 100644 index 00000000..b6a1edf7 --- /dev/null +++ b/app/(landing)/signup/_ui/step/style.module.scss @@ -0,0 +1,45 @@ +.step { + display: flex; + align-items: top; + justify-content: center; + margin: 20px 0; + .bar { + width: 60px; + height: 3px; + margin-top: 25px; + background-color: $color-white; + } +} +.step-item { + color: $color-gray-400; + font-weight: $text-semibold; + .circle { + width: 55px; + height: 50px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 25px; + background-color: $color-white; + font-size: $text-h4; + &.current { + background-color: $color-orange-400; + } + &.done { + background-color: $color-orange-200; + } + .icon { + color: $color-gray-700; + } + } + + p { + margin-top: 20px; + &.current { + color: $color-gray-700; + } + &.done { + color: $color-gray-700; + } + } +} diff --git a/app/(landing)/signup/complete/page.tsx b/app/(landing)/signup/complete/page.tsx new file mode 100644 index 00000000..a878f448 --- /dev/null +++ b/app/(landing)/signup/complete/page.tsx @@ -0,0 +1,16 @@ +import Link from 'next/link' + +import Step from '@/app/(landing)/signup/_ui/step' + +import { PATH } from '@/shared/constants/path' + +const CompletePage = () => { + return ( + <> + <Step /> + <Link href={PATH.SIGN_UP_INFORMATION}>이전</Link> + </> + ) +} + +export default CompletePage diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx new file mode 100644 index 00000000..063e24d2 --- /dev/null +++ b/app/(landing)/signup/information/page.tsx @@ -0,0 +1,17 @@ +import Link from 'next/link' + +import Step from '@/app/(landing)/signup/_ui/step' + +import { PATH } from '@/shared/constants/path' + +const InformationPage = () => { + return ( + <> + <Step /> + <Link href={PATH.SIGN_UP_COMPLETE}>다음</Link> + <Link href={PATH.SIGN_UP_TERMS_OF_USE}>이전</Link> + </> + ) +} + +export default InformationPage diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx new file mode 100644 index 00000000..1ffa2064 --- /dev/null +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -0,0 +1,17 @@ +import Link from 'next/link' + +import Step from '@/app/(landing)/signup/_ui/step' + +import { PATH } from '@/shared/constants/path' + +const TermsOfUsePage = () => { + return ( + <> + <Step /> + <Link href={PATH.SIGN_UP_INFORMATION}>다음</Link> + <Link href={PATH.SIGN_UP_USER_TYPE}>이전</Link> + </> + ) +} + +export default TermsOfUsePage diff --git a/app/(landing)/signup/user-type/page.tsx b/app/(landing)/signup/user-type/page.tsx new file mode 100644 index 00000000..874a912c --- /dev/null +++ b/app/(landing)/signup/user-type/page.tsx @@ -0,0 +1,16 @@ +import Link from 'next/link' + +import Step from '@/app/(landing)/signup/_ui/step' + +import { PATH } from '@/shared/constants/path' + +const UserTypePage = () => { + return ( + <> + <Step /> + <Link href={PATH.SIGN_UP_TERMS_OF_USE}>다음</Link> + </> + ) +} + +export default UserTypePage From 07d89d46fc707c015b36cbc3d34a48c8004ec940 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 14 Nov 2024 13:33:13 +0900 Subject: [PATCH 074/648] =?UTF-8?q?feat:=20=EA=B3=B5=ED=86=B5=EB=90=9C=20c?= =?UTF-8?q?lassName=20=EB=B3=80=EC=88=98=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20(?= =?UTF-8?q?#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/step-item.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/(landing)/signup/_ui/step/step-item.tsx b/app/(landing)/signup/_ui/step/step-item.tsx index b2639fd8..3bb14cc2 100644 --- a/app/(landing)/signup/_ui/step/step-item.tsx +++ b/app/(landing)/signup/_ui/step/step-item.tsx @@ -33,20 +33,26 @@ const StepItem = ({ children, step, pathname, icon: Icon }: Props) => { validateIsDoneStatus() }, [currentPath]) - const isCurrentStep = currentPath === pathname - const validateIsDoneStatus = () => { const currentPathIdx = STEP_HISTORY.findIndex((path) => path === currentPath) if (currentPathIdx > step - 1) { setIsDone(true) } } + + const isCurrentStep = currentPath === pathname + + const stepCondition = { + current: isCurrentStep, + done: isDone, + } + return ( <div className={cx('step-item')}> - <div className={cx('circle', isCurrentStep && 'current', isDone && 'done')}> + <div className={cx('circle', stepCondition)}> {isCurrentStep || isDone ? <Icon className={cx('icon')} /> : step} </div> - <p className={cx(isCurrentStep && 'current', isDone && 'done')}>{children}</p> + <p className={cx(stepCondition)}>{children}</p> </div> ) } From 815d333bb429e2b8985501561b3a6ca7a05dbe3f Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 14 Nov 2024 13:55:44 +0900 Subject: [PATCH 075/648] =?UTF-8?q?design:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20style=20=EC=A0=9C=EA=B1=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/style.module.scss | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/(landing)/signup/_ui/step/style.module.scss b/app/(landing)/signup/_ui/step/style.module.scss index b6a1edf7..3e93e351 100644 --- a/app/(landing)/signup/_ui/step/style.module.scss +++ b/app/(landing)/signup/_ui/step/style.module.scss @@ -1,6 +1,5 @@ .step { display: flex; - align-items: top; justify-content: center; margin: 20px 0; .bar { @@ -35,9 +34,7 @@ p { margin-top: 20px; - &.current { - color: $color-gray-700; - } + &.current, &.done { color: $color-gray-700; } From 50e6de71c7716b5e92bb8d2c8d06110159abc537 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 15 Nov 2024 05:33:29 +0900 Subject: [PATCH 076/648] =?UTF-8?q?settings:=20PR=EC=9D=B4=20=EC=97=B4?= =?UTF-8?q?=EB=A6=B4=20=EB=95=8C=20=20storybook=EC=9D=B4=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EB=90=98=EB=8A=94=20workflow=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/chromatic.yml | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/chromatic.yml diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 00000000..fce223c4 --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,38 @@ +name: 'Chromatic' +on: + pull_request: +jobs: + chromatic: + name: Run Chromatic + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: cache dependencies + id: cache + uses: actions/cache@v4 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-storybook + + - name: Install dependencies + run: npm ci + + - name: Publish to Chromatic + id: chromatic + uses: chromaui/action@latest + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + + - name: comment on PR + uses: thollander/actions-comment-pull-request@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + message: '🚀 Storybook이 배포 됐어요! 확인하러 가기 => ${{ steps.chromatic.outputs.storybookUrl }}' From a81e6c5d0da666a7ea56ab8f66586b7d0c66162c Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 15 Nov 2024 06:15:42 +0900 Subject: [PATCH 077/648] =?UTF-8?q?settings:=20typescale=20mixin=EB=93=A4?= =?UTF-8?q?=EC=9D=84=20=EC=B6=94=EA=B0=80=20(#37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_mixins.scss | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index 9cca6daf..0f256fd3 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -27,3 +27,78 @@ @content; } } + +// typescales +// header +%base-headline { + font-style: normal; + line-height: normal; +} + +@mixin typo-h1 { + @extend %base-headline; + font-size: $text-h1; + font-weight: $text-bold; +} + +@mixin typo-h2 { + @extend %base-headline; + font-size: $text-h2; + font-weight: $text-bold; +} + +@mixin typo-h3 { + @extend %base-headline; + font-size: $text-h3; + font-weight: $text-semibold; +} + +@mixin typo-h4 { + @extend %base-headline; + font-size: $text-h4; + font-weight: $text-semibold; +} + +// body +%base-body { + font-style: normal; + font-weight: $text-semibold; + line-height: 132%; +} + +@mixin typo-b1 { + @extend %base-body; + font-size: $text-b1; + letter-spacing: -0.4px; +} + +@mixin typo-b2 { + @extend %base-body; + font-size: $text-b2; + letter-spacing: -0.32px; +} + +@mixin typo-b3 { + @extend %base-body; + font-size: $text-b3; + letter-spacing: -0.28px; +} + +// caption +%base-caption { + font-style: normal; + font-weight: $text-medium; + line-height: 132%; +} + +@mixin typo-c1 { + @extend %base-caption; + font-size: $text-c1; + letter-spacing: -0.24px; +} + +@mixin typo-c2 { + @extend %base-caption; + font-size: $text-c2; + letter-spacing: -0.2px; +} From ce7e501c43821d89534f4a21f17f96e9a8104949 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 15 Nov 2024 10:41:48 +0900 Subject: [PATCH 078/648] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/pagination/index.tsx | 84 +++++++++++++++++++++ shared/ui/pagination/pagination.module.scss | 40 ++++++++++ shared/ui/pagination/pagination.stories.tsx | 57 ++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 shared/ui/pagination/index.tsx create mode 100644 shared/ui/pagination/pagination.module.scss create mode 100644 shared/ui/pagination/pagination.stories.tsx diff --git a/shared/ui/pagination/index.tsx b/shared/ui/pagination/index.tsx new file mode 100644 index 00000000..3b597c00 --- /dev/null +++ b/shared/ui/pagination/index.tsx @@ -0,0 +1,84 @@ +'use client' + +import { useCallback, useEffect, useState } from 'react' + +import { ChevronLeftIcon, ChevronRightIcon } from '@/public/icons' +import cx from 'classnames' + +import styles from './pagination.module.scss' + +const DOTS = '...' +const PAGES_TO_SHOW = 7 +const PAGE_THRESHOLD = 10 +const BOUNDARY_PAGES = 4 +type PageType = number | string + +interface Props { + currentPage: number + maxPage: number + onPageChange: (page: number) => void +} + +const Pagination = ({ currentPage, maxPage, onPageChange }: Props) => { + const [pages, setPages] = useState<PageType[]>([]) + + const getSequentialPages = useCallback((start: number, length: number) => { + return Array.from({ length }, (_, i) => start + i) + }, []) + + const calculatePages = useCallback(() => { + if (maxPage < PAGE_THRESHOLD) { + return getSequentialPages(1, maxPage) + } + + if (currentPage < BOUNDARY_PAGES + 1) { + return [...getSequentialPages(1, PAGES_TO_SHOW), DOTS, maxPage] + } + + if (currentPage <= maxPage - BOUNDARY_PAGES) { + return [1, DOTS, ...getSequentialPages(currentPage - 2, 5), DOTS, maxPage] + } + + return [1, DOTS, ...getSequentialPages(maxPage - 6, PAGES_TO_SHOW)] + }, [currentPage, maxPage, getSequentialPages]) + + useEffect(() => { + setPages(calculatePages()) + }, [calculatePages]) + + const handleLeft = () => onPageChange(Math.max(1, currentPage - 1)) + const handleRight = () => onPageChange(Math.min(maxPage, currentPage + 1)) + const handlePageClick = (page: PageType) => { + if (typeof page === 'number') onPageChange(page) + } + + return ( + <div className={styles.paginationContainer}> + {currentPage !== 1 && ( + <button className={styles.left} onClick={handleLeft}> + <ChevronLeftIcon /> + </button> + )} + {pages.map((page, index) => ( + <button + key={`${page}-${index}`} + className={cx(styles.page, { + [styles.active]: page === currentPage, + })} + disabled={page === DOTS} + style={page === DOTS ? { cursor: 'default', border: 0 } : {}} + onClick={() => handlePageClick(page)} + > + {page} + </button> + ))} + {currentPage !== maxPage && ( + <button className={styles.right} onClick={handleRight}> + <ChevronRightIcon /> + </button> + )} + </div> + ) +} + +export default Pagination diff --git a/shared/ui/pagination/pagination.module.scss b/shared/ui/pagination/pagination.module.scss new file mode 100644 index 00000000..b5ac5a0f --- /dev/null +++ b/shared/ui/pagination/pagination.module.scss @@ -0,0 +1,40 @@ +.paginationContainer { + display: flex; + justify-content: center; + gap: 10px; + + button { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 4px; + border: 1px solid $color-gray-200; + padding: 9px 11px; + color: $color-gray-400; + font-size: $text-b3; + background-color: $color-white; + } + + .page { + &:hover { + border: 1px solid $color-orange-200; + scale: 1.1; + transition: 0.3s; + } + + &.active { + background: $color-orange-400; + color: $color-white; + transition: 0.3s; + } + } + + .left, + .right { + border: 0; + margin: 0 -10px; + padding: 2px; + } +} diff --git a/shared/ui/pagination/pagination.stories.tsx b/shared/ui/pagination/pagination.stories.tsx new file mode 100644 index 00000000..ad4297b5 --- /dev/null +++ b/shared/ui/pagination/pagination.stories.tsx @@ -0,0 +1,57 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import Pagination from './index' + +const meta = { + title: 'Components/Pagination', + component: Pagination, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + currentPage: { control: 'number' }, + maxPage: { control: 'number' }, + onPageChange: { action: 'page changed' }, + }, + args: { + currentPage: 1, + maxPage: 10, + onPageChange: (page: number) => console.log(`Page changed to ${page}`), + }, +} satisfies Meta<typeof Pagination> + +export default meta +type StoryType = StoryObj<typeof Pagination> + +export const Default: StoryType = { + args: {}, +} + +export const StartPage: StoryType = { + args: { + currentPage: 1, + maxPage: 20, + }, +} + +export const MiddlePage: StoryType = { + args: { + currentPage: 10, + maxPage: 20, + }, +} + +export const LastPage: StoryType = { + args: { + currentPage: 20, + maxPage: 20, + }, +} + +export const FewPages: StoryType = { + args: { + currentPage: 3, + maxPage: 5, + }, +} From 7e85dc7d1269ec2380d632e69b40737ebaaac000 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 15 Nov 2024 10:42:23 +0900 Subject: [PATCH 079/648] =?UTF-8?q?chore:=20Svgr=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=84=A4=EC=B9=98=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 245 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 2 +- 2 files changed, 225 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 46cddc71..f872ce8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "name": "investmetic", "version": "0.1.0", "dependencies": { - "@svgr/webpack": "^8.1.0", "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", @@ -28,6 +27,7 @@ "@storybook/nextjs": "^8.4.2", "@storybook/react": "^8.4.2", "@storybook/test": "^8.4.2", + "@svgr/webpack": "^8.1.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^20", "@types/react": "^18", @@ -60,6 +60,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -72,6 +73,7 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -85,6 +87,7 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -93,6 +96,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -122,6 +126,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -130,6 +135,7 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, "dependencies": { "@babel/parser": "^7.26.2", "@babel/types": "^7.26.0", @@ -145,6 +151,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, "dependencies": { "@babel/types": "^7.25.9" }, @@ -156,6 +163,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", + "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -168,6 +176,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, "dependencies": { "@babel/compat-data": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -183,6 +192,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -191,6 +201,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", @@ -211,6 +222,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -219,6 +231,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.1.1", @@ -235,6 +248,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -243,6 +257,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -295,6 +310,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -307,6 +323,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -319,6 +336,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", @@ -335,6 +353,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, "dependencies": { "@babel/types": "^7.25.9" }, @@ -346,6 +365,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -354,6 +374,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", @@ -370,6 +391,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "dev": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", @@ -386,6 +408,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -398,6 +421,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -422,6 +446,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -430,6 +455,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -438,6 +464,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -446,6 +473,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", @@ -459,6 +487,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.0" @@ -471,6 +500,7 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, "dependencies": { "@babel/types": "^7.26.0" }, @@ -485,6 +515,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -500,6 +531,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -514,6 +546,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -528,6 +561,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", @@ -544,6 +578,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -559,6 +594,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, "engines": { "node": ">=6.9.0" }, @@ -594,6 +630,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -608,6 +645,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -622,6 +660,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -636,6 +675,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -650,6 +690,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -665,6 +706,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -679,6 +721,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9", @@ -695,6 +738,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -711,6 +755,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -725,6 +770,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -739,6 +785,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -754,6 +801,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -769,6 +817,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", @@ -788,6 +837,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" @@ -803,6 +853,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -817,6 +868,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -832,6 +884,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -846,6 +899,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -861,6 +915,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -875,6 +930,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "dev": true, "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -890,6 +946,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -904,6 +961,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -919,6 +977,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -935,6 +994,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -949,6 +1009,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -963,6 +1024,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -977,6 +1039,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -991,6 +1054,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1006,6 +1070,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1022,6 +1087,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1039,6 +1105,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1054,6 +1121,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1069,6 +1137,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1083,6 +1152,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1097,6 +1167,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1111,6 +1182,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -1127,6 +1199,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" @@ -1142,6 +1215,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1156,6 +1230,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1171,6 +1246,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1185,6 +1261,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1200,6 +1277,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1216,6 +1294,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1230,6 +1309,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1244,6 +1324,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1258,6 +1339,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -1276,6 +1358,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "dev": true, "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, @@ -1290,6 +1373,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1305,6 +1389,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" @@ -1320,6 +1405,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1335,6 +1421,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1378,6 +1465,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1392,6 +1480,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -1407,6 +1496,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1421,6 +1511,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1435,6 +1526,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1449,6 +1541,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1467,6 +1560,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1481,6 +1575,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1496,6 +1591,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1511,6 +1607,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1526,6 +1623,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dev": true, "dependencies": { "@babel/compat-data": "^7.26.0", "@babel/helper-compilation-targets": "^7.25.9", @@ -1608,6 +1706,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -1616,6 +1715,7 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -1629,6 +1729,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -1648,6 +1749,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", @@ -1666,6 +1768,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1677,6 +1780,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", @@ -1690,6 +1794,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/generator": "^7.25.9", @@ -1707,6 +1812,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -2676,6 +2782,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2689,6 +2796,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -2697,6 +2805,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -2714,12 +2823,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -4020,6 +4131,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, "engines": { "node": ">=14" }, @@ -4035,6 +4147,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, "engines": { "node": ">=14" }, @@ -4050,6 +4163,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, "engines": { "node": ">=14" }, @@ -4065,6 +4179,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, "engines": { "node": ">=14" }, @@ -4080,6 +4195,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, "engines": { "node": ">=14" }, @@ -4095,6 +4211,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, "engines": { "node": ">=14" }, @@ -4110,6 +4227,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, "engines": { "node": ">=14" }, @@ -4125,6 +4243,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, "engines": { "node": ">=12" }, @@ -4140,6 +4259,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, "dependencies": { "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", @@ -4165,6 +4285,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -4184,6 +4305,7 @@ "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -4209,6 +4331,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, "dependencies": { "@babel/types": "^7.21.3", "entities": "^4.4.0" @@ -4225,6 +4348,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "engines": { "node": ">=0.12" }, @@ -4236,6 +4360,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -4257,6 +4382,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dev": true, "dependencies": { "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", @@ -4277,6 +4403,7 @@ "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -4302,6 +4429,8 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-transform-react-constant-elements": "^7.21.3", @@ -4566,6 +4695,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, "engines": { "node": ">=10.13.0" } @@ -5373,7 +5503,8 @@ "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==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/aria-query": { "version": "5.3.0", @@ -5758,6 +5889,7 @@ "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.2", @@ -5771,6 +5903,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -5779,6 +5912,7 @@ "version": "0.10.6", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2", "core-js-compat": "^3.38.0" @@ -5791,6 +5925,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "dev": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2" }, @@ -5866,7 +6001,8 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -6028,6 +6164,7 @@ "version": "4.24.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6131,6 +6268,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -6149,6 +6287,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, "engines": { "node": ">=10" }, @@ -6431,12 +6570,14 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "dev": true, "dependencies": { "browserslist": "^4.24.2" }, @@ -6616,6 +6757,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -6628,6 +6770,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, "engines": { "node": ">= 6" }, @@ -6657,6 +6800,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, "dependencies": { "css-tree": "~2.2.0" }, @@ -6669,6 +6813,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -6681,7 +6826,8 @@ "node_modules/csso/node_modules/mdn-data": { "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true }, "node_modules/csstype": { "version": "3.1.3", @@ -6750,6 +6896,7 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -6787,6 +6934,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6940,6 +7088,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, "funding": [ { "type": "github", @@ -6980,6 +7129,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -6994,7 +7144,8 @@ "node_modules/electron-to-chromium": { "version": "1.5.52", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz", - "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==" + "integrity": "sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==", + "dev": true }, "node_modules/elliptic": { "version": "6.6.0", @@ -7078,6 +7229,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -7311,6 +7463,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "engines": { "node": ">=6" } @@ -8062,6 +8215,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8399,6 +8553,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8434,6 +8589,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -8551,6 +8707,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, "engines": { "node": ">=4" } @@ -8690,6 +8847,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -8882,6 +9040,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -8977,7 +9136,8 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-async-function": { "version": "2.0.0", @@ -9059,6 +9219,7 @@ "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -9483,6 +9644,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -9503,6 +9665,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -9519,7 +9682,8 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -9537,6 +9701,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -9766,7 +9931,8 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, "node_modules/loader-runner": { "version": "4.3.0", @@ -9816,7 +9982,8 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true }, "node_modules/lodash.kebabcase": { "version": "4.1.1", @@ -9863,6 +10030,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -9871,6 +10039,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "dependencies": { "yallist": "^3.0.2" } @@ -9937,7 +10106,8 @@ "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true }, "node_modules/memfs": { "version": "3.5.3", @@ -10085,7 +10255,8 @@ "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==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/nanoid": { "version": "3.3.7", @@ -10218,6 +10389,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -10278,7 +10450,8 @@ "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", @@ -10293,6 +10466,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -10541,6 +10715,7 @@ "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, "dependencies": { "callsites": "^3.0.0" }, @@ -10569,6 +10744,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -10628,7 +10804,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-scurry": { "version": "1.11.1", @@ -10656,6 +10833,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -11361,12 +11539,14 @@ "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, "dependencies": { "regenerate": "^1.4.2" }, @@ -11377,12 +11557,14 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" } @@ -11415,6 +11597,7 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "dev": true, "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", @@ -11430,12 +11613,14 @@ "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true }, "node_modules/regjsparser": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "dev": true, "dependencies": { "jsesc": "~3.0.2" }, @@ -11490,6 +11675,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -11506,6 +11692,7 @@ "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, "engines": { "node": ">=4" } @@ -12018,6 +12205,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -12438,6 +12626,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -12448,12 +12637,14 @@ "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true }, "node_modules/svgo": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dev": true, "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -12478,6 +12669,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "engines": { "node": ">= 10" } @@ -12486,6 +12678,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -12501,6 +12694,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -12514,6 +12708,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -12528,6 +12723,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -12541,6 +12737,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "engines": { "node": ">=0.12" }, @@ -12893,7 +13090,7 @@ "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "devOptional": true, + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12927,6 +13124,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, "engines": { "node": ">=4" } @@ -12935,6 +13133,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -12947,6 +13146,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, "engines": { "node": ">=4" } @@ -12955,6 +13155,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, "engines": { "node": ">=4" } @@ -12993,6 +13194,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -13476,7 +13678,8 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 00e28ffc..6b1b9e2d 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "build-storybook": "storybook build" }, "dependencies": { - "@svgr/webpack": "^8.1.0", "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", @@ -33,6 +32,7 @@ "@storybook/nextjs": "^8.4.2", "@storybook/react": "^8.4.2", "@storybook/test": "^8.4.2", + "@svgr/webpack": "^8.1.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^20", "@types/react": "^18", From 6d4822f11bfc247cb7d9ff8cf5971923e7969f6f Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 15 Nov 2024 10:44:06 +0900 Subject: [PATCH 080/648] =?UTF-8?q?feat:=20svg=EB=A5=BC=20=EB=A6=AC?= =?UTF-8?q?=EC=95=A1=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=EC=A7=80=EC=A0=95=20=EB=B0=8F=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/main.ts | 16 ++++++++++++++++ shared/types/svg.d.ts | 4 ++++ 2 files changed, 20 insertions(+) create mode 100644 shared/types/svg.d.ts diff --git a/.storybook/main.ts b/.storybook/main.ts index 4a04c546..94e78745 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -19,6 +19,22 @@ const config: StorybookConfig = { if (config.module?.rules) { const rules = config.module.rules as Array<any> const scssRule = rules.find((rule) => rule.test && rule.test.toString().includes('scss')) + const imageRule = config.module?.rules?.find((rule) => { + const test = (rule as { test: RegExp }).test + + if (!test) { + return false + } + + return test.test('.svg') + }) as { [key: string]: any } + + imageRule.exclude = /\.svg$/ + + config.module?.rules?.push({ + test: /\.svg$/, + use: ['@svgr/webpack'], + }) if (scssRule) { const sassLoader = scssRule.use.find( diff --git a/shared/types/svg.d.ts b/shared/types/svg.d.ts new file mode 100644 index 00000000..50267039 --- /dev/null +++ b/shared/types/svg.d.ts @@ -0,0 +1,4 @@ +declare module '@/public/icons/*.svg' { + const content: React.FunctionComponent<React.SVGAttributes<SVGElement>> + export default content +} From fc1e6abb2859bf81cc2268322001f37fc4fab02a Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 15 Nov 2024 10:45:48 +0900 Subject: [PATCH 081/648] =?UTF-8?q?settings:=20NextJS=EC=9D=98=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=84=EC=B9=AD(@)=EA=B3=BC=EC=9D=98=20=ED=98=B8?= =?UTF-8?q?=ED=99=98=EC=84=B1=20=EC=9C=A0=EC=A7=80=20=EB=B0=8F=20SVG=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=ED=83=80=EC=9E=85=20=EC=A7=80=EC=9B=90=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 12 ++++++++---- tsconfig.json | 12 ++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index b21c8353..74121b9d 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,12 +1,15 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + experimental: { + appDir: true, + }, sassOptions: { includePaths: ['./shared/styles'], prependData: ` - @import "@/shared/styles/base/variables"; - @import "@/shared/styles/base/mixins"; - @import "@/shared/styles/base/functions"; - `, + @import "@/shared/styles/base/variables"; + @import "@/shared/styles/base/mixins"; + @import "@/shared/styles/base/functions"; + `, }, async rewrites() { return [ @@ -19,6 +22,7 @@ const nextConfig = { webpack(config) { config.module.rules.push({ test: /\.svg$/, + issuer: /\.[jt]sx?$/, use: ['@svgr/webpack'], }) diff --git a/tsconfig.json b/tsconfig.json index 6430397f..c21c4d22 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,9 +19,17 @@ ], "baseUrl": ".", "paths": { - "@/*": ["./*"] + "@/*": ["./*"], + "*.svg": ["./shared/types/svg.d.ts"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "next.config.mjs"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "next.config.mjs", + "shared/types/**/*.d.ts" + ], "exclude": ["node_modules"] } From 07027fb91faa728ad140f1cb398a7da42ab48f05 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 11:25:38 +0900 Subject: [PATCH 082/648] =?UTF-8?q?feat:=20step=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20gaugeBar=20=ED=9A=A8=EA=B3=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/index.tsx | 50 +++++++++++-- app/(landing)/signup/_ui/step/step-item.tsx | 66 +++++++++++------ .../signup/_ui/step/style.module.scss | 70 ++++++++++++++++--- shared/stores/use-step-history-store.tsx | 18 +++++ 4 files changed, 165 insertions(+), 39 deletions(-) create mode 100644 shared/stores/use-step-history-store.tsx diff --git a/app/(landing)/signup/_ui/step/index.tsx b/app/(landing)/signup/_ui/step/index.tsx index dd2f53ab..e8f664be 100644 --- a/app/(landing)/signup/_ui/step/index.tsx +++ b/app/(landing)/signup/_ui/step/index.tsx @@ -1,31 +1,67 @@ 'use client' +import { useEffect, useRef } from 'react' + +import { usePathname } from 'next/navigation' + import StepItem from '@/app/(landing)/signup/_ui/step/step-item' import { BarsIcon, CheckIcon, PencilIcon, ProfileIcon } from '@/public/icons' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' +import useStepHistoryStore from '@/shared/stores/use-step-history-store' import styles from './style.module.scss' +/* eslint-disable react-hooks/exhaustive-deps */ + const cx = classNames.bind(styles) +export const STEP_OF_PATH: { [key: string]: number } = { + [PATH.SIGN_UP_USER_TYPE]: 1, + [PATH.SIGN_UP_TERMS_OF_USE]: 2, + [PATH.SIGN_UP_INFORMATION]: 3, + [PATH.SIGN_UP_COMPLETE]: 4, +} const Step = () => { + const stepHistory = useStepHistoryStore((state) => state.stepHistory) + const addStep = useStepHistoryStore((state) => state.addStep) + const removeStep = useStepHistoryStore((state) => state.removeStep) + const initialRender = useRef(true) + const currentPath = usePathname() + + useEffect(() => { + if (initialRender.current) { + initialRender.current = false + addHistory() + } + }, [currentPath]) + + const addHistory = () => { + addStep(currentPath) + if (stepHistory.length > 1) { + removeStep() + } + } + + let prevStep = STEP_OF_PATH[stepHistory[0]] + const currentStep = STEP_OF_PATH[stepHistory[1]] + if (currentStep === 1) { + prevStep = 0 + } + return ( <div className={cx('step')}> - <StepItem step={1} pathname={PATH.SIGN_UP_USER_TYPE} icon={ProfileIcon}> + <StepItem step={1} pathname={PATH.SIGN_UP_USER_TYPE} icon={ProfileIcon} prevStep={prevStep}> 회원 선택 </StepItem> - <div className={cx('bar')}> </div> - <StepItem step={2} pathname={PATH.SIGN_UP_TERMS_OF_USE} icon={BarsIcon}> + <StepItem step={2} pathname={PATH.SIGN_UP_TERMS_OF_USE} icon={BarsIcon} prevStep={prevStep}> 약관 동의 </StepItem> - <div className={cx('bar')}> </div> - <StepItem step={3} pathname={PATH.SIGN_UP_INFORMATION} icon={PencilIcon}> + <StepItem step={3} pathname={PATH.SIGN_UP_INFORMATION} icon={PencilIcon} prevStep={prevStep}> 정보 입력 </StepItem> - <div className={cx('bar')}> </div> - <StepItem step={4} pathname={PATH.SIGN_UP_COMPLETE} icon={CheckIcon}> + <StepItem step={4} pathname={PATH.SIGN_UP_COMPLETE} icon={CheckIcon} prevStep={prevStep}> 가입 완료 </StepItem> </div> diff --git a/app/(landing)/signup/_ui/step/step-item.tsx b/app/(landing)/signup/_ui/step/step-item.tsx index 3bb14cc2..59006b2e 100644 --- a/app/(landing)/signup/_ui/step/step-item.tsx +++ b/app/(landing)/signup/_ui/step/step-item.tsx @@ -1,42 +1,61 @@ -/* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState } from 'react' import { usePathname } from 'next/navigation' +import { STEP_OF_PATH } from '@/app/(landing)/signup/_ui/step' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' +import useStepHistoryStore from '@/shared/stores/use-step-history-store' import styles from './style.module.scss' -const cx = classNames.bind(styles) +/* eslint-disable react-hooks/exhaustive-deps */ -const STEP_HISTORY = [ - PATH.SIGN_UP_USER_TYPE, - PATH.SIGN_UP_TERMS_OF_USE, - PATH.SIGN_UP_INFORMATION, - PATH.SIGN_UP_COMPLETE, -] +const cx = classNames.bind(styles) interface Props { children: React.ReactNode step: number pathname: string icon: React.ElementType + prevStep: number } -const StepItem = ({ children, step, pathname, icon: Icon }: Props) => { - const [isDone, setIsDone] = useState(false) +const StepItem = ({ children, step, pathname, icon: Icon, prevStep }: Props) => { + const stepHistory = useStepHistoryStore((state) => state.stepHistory) + const [isNextStep, setIsNextStep] = useState(false) + const [isPrevStep, setIsPrev] = useState(false) + const [isFix, setIsFix] = useState(false) const currentPath = usePathname() useEffect(() => { - validateIsDoneStatus() + if (currentPathIdx - (prevStep - 1) > 0) { + handleNextStep() + } else if (stepHistory.length !== 0) { + handlePrevStep() + } }, [currentPath]) - const validateIsDoneStatus = () => { - const currentPathIdx = STEP_HISTORY.findIndex((path) => path === currentPath) - if (currentPathIdx > step - 1) { - setIsDone(true) + const currentPathIdx = Object.keys(STEP_OF_PATH).findIndex((path) => path === currentPath) + const stepIdx = step - 1 + + const handleNextStep = () => { + if (currentPathIdx > stepIdx) { + setIsNextStep(true) + if (currentPathIdx - step > 0) { + setIsFix(true) + } + } + } + + const handlePrevStep = () => { + if (currentPathIdx === stepIdx) { + setIsPrev(true) + } + if (currentPathIdx - stepIdx > 0) { + setIsNextStep(true) + setIsFix(true) } } @@ -44,16 +63,21 @@ const StepItem = ({ children, step, pathname, icon: Icon }: Props) => { const stepCondition = { current: isCurrentStep, - done: isDone, + next: isNextStep, + prev: isPrevStep, + fix: isFix, } return ( - <div className={cx('step-item')}> - <div className={cx('circle', stepCondition)}> - {isCurrentStep || isDone ? <Icon className={cx('icon')} /> : step} + <> + <div className={cx('step-item')}> + <div className={cx('circle', stepCondition)}> + {isCurrentStep || isNextStep ? <Icon className={cx('icon')} /> : step} + </div> + <p className={cx(stepCondition)}>{children}</p> </div> - <p className={cx(stepCondition)}>{children}</p> - </div> + {pathname !== PATH.SIGN_UP_COMPLETE && <div className={cx('bar', stepCondition)}> </div>} + </> ) } diff --git a/app/(landing)/signup/_ui/step/style.module.scss b/app/(landing)/signup/_ui/step/style.module.scss index 3e93e351..b55d211d 100644 --- a/app/(landing)/signup/_ui/step/style.module.scss +++ b/app/(landing)/signup/_ui/step/style.module.scss @@ -2,31 +2,79 @@ display: flex; justify-content: center; margin: 20px 0; - .bar { - width: 60px; - height: 3px; - margin-top: 25px; - background-color: $color-white; +} + +.bar { + width: 60px; + height: 4px; + margin-top: 24px; + background-color: $color-white; + position: relative; + &.next { + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 0; + height: 100%; + background-color: $color-orange-200; + animation: gaugeUp 0.5s linear forwards; + } + } + &.fix { + background-color: $color-orange-200; + animation: none; + } + &.prev { + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 0; + height: 100%; + background-color: $color-orange-200; + animation: gaugeDown 0.5s linear forwards; + } } } + +@keyframes gaugeUp { + from { + width: 0; + } + to { + width: 100%; + } +} +@keyframes gaugeDown { + to { + width: 0; + } + from { + width: 100%; + } +} + .step-item { color: $color-gray-400; font-weight: $text-semibold; .circle { - width: 55px; - height: 50px; + width: 58px; + height: 52px; display: flex; align-items: center; justify-content: center; border-radius: 25px; background-color: $color-white; font-size: $text-h4; + &.next { + background-color: $color-orange-200; + } &.current { background-color: $color-orange-400; } - &.done { - background-color: $color-orange-200; - } .icon { color: $color-gray-700; } @@ -35,7 +83,7 @@ p { margin-top: 20px; &.current, - &.done { + &.next { color: $color-gray-700; } } diff --git a/shared/stores/use-step-history-store.tsx b/shared/stores/use-step-history-store.tsx new file mode 100644 index 00000000..b4cb3c54 --- /dev/null +++ b/shared/stores/use-step-history-store.tsx @@ -0,0 +1,18 @@ +import { create } from 'zustand' + +interface StateModel { + stepHistory: string[] +} + +interface ActionModel { + addStep: (step: string) => void + removeStep: () => void +} + +const useStepHistoryStore = create<StateModel & ActionModel>((set) => ({ + stepHistory: [], + addStep: (step) => set((state) => ({ ...state, stepHistory: [...state.stepHistory, step] })), + removeStep: () => set((state) => ({ ...state, stepHistory: state.stepHistory.slice(1) })), +})) + +export default useStepHistoryStore From 6826df3d47d60ad6a37b41ef709201b3ce18b9f7 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 11:29:29 +0900 Subject: [PATCH 083/648] =?UTF-8?q?design:=20=EC=A4=91=EB=B3=B5=EB=90=9C?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_ui/step/style.module.scss | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/app/(landing)/signup/_ui/step/style.module.scss b/app/(landing)/signup/_ui/step/style.module.scss index b55d211d..9cae82d1 100644 --- a/app/(landing)/signup/_ui/step/style.module.scss +++ b/app/(landing)/signup/_ui/step/style.module.scss @@ -10,7 +10,8 @@ margin-top: 24px; background-color: $color-white; position: relative; - &.next { + &.next, + &.prev { &::after { content: ''; position: absolute; @@ -19,25 +20,19 @@ width: 0; height: 100%; background-color: $color-orange-200; - animation: gaugeUp 0.5s linear forwards; } } + &.next::after { + animation: gaugeUp 0.5s linear forwards; + } + + &.prev::after { + animation: gaugeDown 0.5s linear forwards; + } &.fix { background-color: $color-orange-200; animation: none; } - &.prev { - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 0; - height: 100%; - background-color: $color-orange-200; - animation: gaugeDown 0.5s linear forwards; - } - } } @keyframes gaugeUp { From b2cfff08c4a7383580c279f00c2495b4ceb7fb6f Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 11:37:51 +0900 Subject: [PATCH 084/648] =?UTF-8?q?rename:=20step=20history=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=ED=95=98=EB=8A=94=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=AA=85=ED=99=95=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(landing)/signup/_ui/step/index.tsx b/app/(landing)/signup/_ui/step/index.tsx index e8f664be..d86483a7 100644 --- a/app/(landing)/signup/_ui/step/index.tsx +++ b/app/(landing)/signup/_ui/step/index.tsx @@ -33,11 +33,11 @@ const Step = () => { useEffect(() => { if (initialRender.current) { initialRender.current = false - addHistory() + handleStepHistoryControl() } }, [currentPath]) - const addHistory = () => { + const handleStepHistoryControl = () => { addStep(currentPath) if (stepHistory.length > 1) { removeStep() From 5f72147526e9385f69c1ed060325b7fb82d26c34 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 11:43:37 +0900 Subject: [PATCH 085/648] =?UTF-8?q?chore:=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/bookmark-outline.svg | 3 +++ public/icons/{favorite.svg => bookmark.svg} | 0 public/icons/daily-graph.svg | 4 ++-- public/icons/index.tsx | 3 ++- public/icons/monthly-graph.svg | 6 +++--- public/icons/open.svg | 10 +--------- public/icons/search.svg | 4 ++-- public/icons/statistics.svg | 15 +++------------ 8 files changed, 16 insertions(+), 29 deletions(-) create mode 100644 public/icons/bookmark-outline.svg rename public/icons/{favorite.svg => bookmark.svg} (100%) diff --git a/public/icons/bookmark-outline.svg b/public/icons/bookmark-outline.svg new file mode 100644 index 00000000..72705823 --- /dev/null +++ b/public/icons/bookmark-outline.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9937 14.9316L16.6 17.2343V5.4H7.4V17.2746L11.9937 14.9316ZM6 19.5603V5C6 4.44772 6.44772 4 7 4H17C17.5523 4 18 4.44772 18 5V19.4994L12 16.5L6 19.5603Z" fill="currentColor"/> +</svg> diff --git a/public/icons/favorite.svg b/public/icons/bookmark.svg similarity index 100% rename from public/icons/favorite.svg rename to public/icons/bookmark.svg diff --git a/public/icons/daily-graph.svg b/public/icons/daily-graph.svg index b918c604..3aa7b95e 100644 --- a/public/icons/daily-graph.svg +++ b/public/icons/daily-graph.svg @@ -1,4 +1,4 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path d="M4 4V20.5H20.5" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> -<path d="M4 20.5L10.5 9L16 12.5L20 6" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3.25 20.5V4H4.75V19.75H20.5V21.25H4C3.58579 21.25 3.25 20.9142 3.25 20.5Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M20.6393 6.39357L16.6393 12.8936C16.534 13.0646 16.3645 13.1863 16.1688 13.2314C15.973 13.2764 15.7674 13.2411 15.5979 13.1332L10.7646 10.0575L4.6535 20.8695L3.34766 20.1315L9.84766 8.63145C9.94888 8.45236 10.1192 8.32272 10.3188 8.27286C10.5184 8.223 10.7297 8.25731 10.9032 8.36775L15.7609 11.459L19.3618 5.60742L20.6393 6.39357Z" fill="currentColor"/> </svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index aa0ba1e3..810e3ab5 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -8,7 +8,8 @@ export { default as ChevronLeftIcon } from './chevron-left.svg' export { default as ChevronRightIcon } from './chevron-right.svg' export { default as ChevronUpIcon } from './chevron-up.svg' export { default as DailyGraphIcon } from './daily-graph.svg' -export { default as FavoriteIcon } from './favorite.svg' +export { default as BookmarkIcon } from './bookmark.svg' +export { default as BookmarkOutlineIcon } from './bookmark-outline.svg' export { default as FileIcon } from './file.svg' export { default as MoneyIcon } from './money.svg' export { default as MonthlyGraphIcon } from './monthly-graph.svg' diff --git a/public/icons/monthly-graph.svg b/public/icons/monthly-graph.svg index bc3d6caf..c96a2210 100644 --- a/public/icons/monthly-graph.svg +++ b/public/icons/monthly-graph.svg @@ -1,5 +1,5 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path d="M3.75 3.5V20H20.25" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> -<path d="M3.75 20L10.25 8.5L15.75 12L19.75 5.5" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> -<path d="M7.25098 20L11.1729 13.5L17.5496 17.1267L19.5068 13.9643V20" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3 20V3.5H4.5V19.25H20.25V20.75H3.75C3.33579 20.75 3 20.4142 3 20Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M20.3893 5.89357L16.3893 12.3936C16.284 12.5646 16.1145 12.6863 15.9188 12.7314C15.723 12.7764 15.5174 12.7411 15.3479 12.6332L10.5146 9.55752L4.4035 20.3695L3.09766 19.6315L9.59766 8.13145C9.69888 7.95236 9.86922 7.82272 10.0688 7.77286C10.2684 7.723 10.4797 7.75731 10.6532 7.86775L15.5109 10.959L19.1118 5.10742L20.3893 5.89357Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5312 13.1127C10.7413 12.7644 11.1907 12.6471 11.5442 12.8482L17.2927 16.1176L18.8697 13.5697C19.0461 13.2847 19.3902 13.1513 19.7126 13.243C20.035 13.3347 20.2574 13.6292 20.2574 13.9644V20.0001H18.7574V16.6013L18.1879 17.5215C17.9756 17.8645 17.53 17.9781 17.1794 17.7787L11.438 14.5134L7.8937 20.3876L6.60938 19.6127L10.5312 13.1127Z" fill="currentColor"/> </svg> diff --git a/public/icons/open.svg b/public/icons/open.svg index 0dd2fa8d..1e60bbe1 100644 --- a/public/icons/open.svg +++ b/public/icons/open.svg @@ -1,11 +1,3 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<g clip-path="url(#clip0_1175_5001)"> -<path d="M19 7L12 16L5 7" stroke="currentColor" stroke-width="1.5"/> -<path d="M19 7L12 16L9 12L5.5 7" stroke="currentColor"/> -</g> -<defs> -<clipPath id="clip0_1175_5001"> -<rect width="24" height="24" fill="white" transform="matrix(0 1 -1 0 24 0)"/> -</clipPath> -</defs> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7689 14.9019L17.9645 7.54883L19.0352 8.45092L11.7493 17.0979L8.31954 12.8766L4.94209 8.42284L6.05761 7.57691L9.4209 12.012L11.7689 14.9019Z" fill="currentColor"/> </svg> diff --git a/public/icons/search.svg b/public/icons/search.svg index 0012695c..109d2771 100644 --- a/public/icons/search.svg +++ b/public/icons/search.svg @@ -1,4 +1,4 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path d="M11.3333 16.6667C14.2789 16.6667 16.6667 14.2789 16.6667 11.3333C16.6667 8.38781 14.2789 6 11.3333 6C8.38781 6 6 8.38781 6 11.3333C6 14.2789 8.38781 16.6667 11.3333 16.6667Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> -<path d="M17.9996 18.0016L15.0996 15.1016" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.334 6.66732C8.75666 6.66732 6.66732 8.75666 6.66732 11.334C6.66732 13.9113 8.75666 16.0007 11.334 16.0007C13.9113 16.0007 16.0007 13.9113 16.0007 11.334C16.0007 8.75666 13.9113 6.66732 11.334 6.66732ZM5.33398 11.334C5.33398 8.02028 8.02028 5.33398 11.334 5.33398C14.6477 5.33398 17.334 8.02028 17.334 11.334C17.334 14.6477 14.6477 17.334 11.334 17.334C8.02028 17.334 5.33398 14.6477 5.33398 11.334Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M14.6289 14.6308C14.8892 14.3705 15.3113 14.3705 15.5717 14.6308L18.4717 17.5308C18.732 17.7912 18.732 18.2133 18.4717 18.4736C18.2113 18.734 17.7892 18.734 17.5289 18.4736L14.6289 15.5736C14.3685 15.3133 14.3685 14.8912 14.6289 14.6308Z" fill="currentColor"/> </svg> diff --git a/public/icons/statistics.svg b/public/icons/statistics.svg index 9315fb69..47164662 100644 --- a/public/icons/statistics.svg +++ b/public/icons/statistics.svg @@ -1,14 +1,5 @@ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<mask id="path-1-inside-1_1175_5005" fill="white"> -<rect x="12" y="3" width="5" height="11" rx="1" transform="rotate(90 12 3)"/> -</mask> -<rect x="12" y="3" width="5" height="11" rx="1" transform="rotate(90 12 3)" stroke="currentColor" stroke-width="3" mask="url(#path-1-inside-1_1175_5005)"/> -<mask id="path-2-inside-2_1175_5005" fill="white"> -<rect x="21" y="10" width="5" height="20" rx="1" transform="rotate(90 21 10)"/> -</mask> -<rect x="21" y="10" width="5" height="20" rx="1" transform="rotate(90 21 10)" stroke="currentColor" stroke-width="3" mask="url(#path-2-inside-2_1175_5005)"/> -<mask id="path-3-inside-3_1175_5005" fill="white"> -<rect x="15" y="17" width="5" height="14" rx="1" transform="rotate(90 15 17)"/> -</mask> -<rect x="15" y="17" width="5" height="14" rx="1" transform="rotate(90 15 17)" stroke="currentColor" stroke-width="3" mask="url(#path-3-inside-3_1175_5005)"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 4.5H2.5V6.5H10.5V4.5ZM12 4C12 3.44772 11.5523 3 11 3H2C1.44772 3 1 3.44772 1 4V7C1 7.55228 1.44771 8 2 8H11C11.5523 8 12 7.55228 12 7V4Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M19.5 11.5H2.5V13.5H19.5V11.5ZM21 11C21 10.4477 20.5523 10 20 10H2C1.44772 10 1 10.4477 1 11V14C1 14.5523 1.44772 15 2 15H20C20.5523 15 21 14.5523 21 14V11Z" fill="currentColor"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 18.5H2.5V20.5H13.5V18.5ZM15 18C15 17.4477 14.5523 17 14 17H2C1.44772 17 1 17.4477 1 18V21C1 21.5523 1.44772 22 2 22H14C14.5523 22 15 21.5523 15 21V18Z" fill="currentColor"/> </svg> From 779cb72262dfbe6a1eb9e390a013c4ee9d3a2159 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 11:45:04 +0900 Subject: [PATCH 086/648] =?UTF-8?q?fix:=20=EB=B0=94=EB=80=90=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/navigation.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/_ui/navigation.tsx b/app/(dashboard)/_ui/navigation.tsx index da21fa54..d2a76c5c 100644 --- a/app/(dashboard)/_ui/navigation.tsx +++ b/app/(dashboard)/_ui/navigation.tsx @@ -1,7 +1,7 @@ 'use client' import { - FavoriteIcon, + BookmarkIcon, QuestionIcon, StrategyIcon, StrategyRankingIcon, @@ -30,7 +30,7 @@ const DashboardNavigation = () => { 나의 전략 </NavLinkItem> )} - <NavLinkItem href={PATH.FAVORITES} icon={FavoriteIcon}> + <NavLinkItem href={PATH.FAVORITES} icon={BookmarkIcon}> 구독한 전략 </NavLinkItem> <NavLinkItem href={PATH.MY_QUESTIONS} icon={QuestionIcon}> From 95f4966c098bb0ce950b453f6385dc1bd1084740 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 11:45:56 +0900 Subject: [PATCH 087/648] =?UTF-8?q?feat:=20Icons=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/stories/icons.stories.tsx | 96 +++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 shared/styles/stories/icons.stories.tsx diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx new file mode 100644 index 00000000..4b56f7d3 --- /dev/null +++ b/shared/styles/stories/icons.stories.tsx @@ -0,0 +1,96 @@ +import { + BarsIcon, + BookmarkIcon, + BookmarkOutlineIcon, + CategoryIcon, + ChangeIcon, + CheckIcon, + CheckboxIcon, + ChevronDownIcon, + ChevronLeftIcon, + ChevronRightIcon, + ChevronUpIcon, + DailyGraphIcon, + FileIcon, + MoneyIcon, + MonthlyGraphIcon, + NoticeIcon, + OpenIcon, + PencilIcon, + ProfileIcon, + QuestionIcon, + SearchIcon, + SignOutIcon, + StatisticsIcon, + StrategyIcon, + StrategyRankingIcon, + TradersIcon, +} from '@/public/icons' +import type { Meta, StoryObj as Story } from '@storybook/react' + +const meta = { + title: 'Design System/Icons', +} satisfies Meta + +const icons = [ + { name: 'BarsIcon', icon: BarsIcon }, + { name: 'CategoryIcon', icon: CategoryIcon }, + { name: 'ChangeIcon', icon: ChangeIcon }, + { name: 'CheckIcon', icon: CheckIcon }, + { name: 'CheckboxIcon', icon: CheckboxIcon }, + { name: 'ChevronDownIcon', icon: ChevronDownIcon }, + { name: 'ChevronLeftIcon', icon: ChevronLeftIcon }, + { name: 'ChevronRightIcon', icon: ChevronRightIcon }, + { name: 'ChevronUpIcon', icon: ChevronUpIcon }, + { name: 'DailyGraphIcon', icon: DailyGraphIcon }, + { name: 'BookmarkIcon', icon: BookmarkIcon }, + { name: 'BookmarkOutlineIcon', icon: BookmarkOutlineIcon }, + { name: 'FileIcon', icon: FileIcon }, + { name: 'MoneyIcon', icon: MoneyIcon }, + { name: 'MonthlyGraphIcon', icon: MonthlyGraphIcon }, + { name: 'NoticeIcon', icon: NoticeIcon }, + { name: 'OpenIcon', icon: OpenIcon }, + { name: 'PencilIcon', icon: PencilIcon }, + { name: 'ProfileIcon', icon: ProfileIcon }, + { name: 'QuestionIcon', icon: QuestionIcon }, + { name: 'SearchIcon', icon: SearchIcon }, + { name: 'SignOutIcon', icon: SignOutIcon }, + { name: 'StatisticsIcon', icon: StatisticsIcon }, + { name: 'StrategyRankingIcon', icon: StrategyRankingIcon }, + { name: 'StrategyIcon', icon: StrategyIcon }, + { name: 'TradersIcon', icon: TradersIcon }, +] + +export const Icons: Story = { + render: () => ( + <div style={{ padding: '24px' }}> + <h1 style={{ marginBottom: '24px', fontSize: '32px', fontWeight: 'bold' }}>Icons</h1> + <div + style={{ + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', + gap: '16px', + }} + > + {icons.map(({ name, icon: Icon }) => ( + <div + key={name} + style={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '8px', + padding: '16px', + cursor: 'pointer', + }} + > + <Icon style={{ width: '24px', height: '24px', fill: '#797979' }} /> + <span style={{ fontSize: '12px', color: '#797979' }}>{name}</span> + </div> + ))} + </div> + </div> + ), +} + +export default meta From 979401d0a925f0bde2394455c406aafa40578e65 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 11:48:28 +0900 Subject: [PATCH 088/648] =?UTF-8?q?design:=20Colors,=20Typography=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20h1=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=ED=86=B5=EC=9D=BC=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/stories/colors.stories.tsx | 2 +- shared/styles/stories/fonts.stories.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/styles/stories/colors.stories.tsx b/shared/styles/stories/colors.stories.tsx index a4628118..23e78618 100644 --- a/shared/styles/stories/colors.stories.tsx +++ b/shared/styles/stories/colors.stories.tsx @@ -50,7 +50,7 @@ const ColorSection = ({ title, colors }: ColorSectionProps) => ( export const Colors: Story = { render: () => ( <> - <h1>Colors</h1> + <h1 style={{ margin: '24px 0', fontSize: '32px', fontWeight: 'bold' }}>Colors</h1> <ColorSection title="Grayscale" diff --git a/shared/styles/stories/fonts.stories.tsx b/shared/styles/stories/fonts.stories.tsx index 39b8c7fd..dfe424aa 100644 --- a/shared/styles/stories/fonts.stories.tsx +++ b/shared/styles/stories/fonts.stories.tsx @@ -56,7 +56,7 @@ const TypographySection = ({ title, fonts }: TypographySectionProps) => ( export const Typography: Story = { render: () => ( <> - <h1>Typography</h1> + <h1 style={{ margin: '24px 0', fontSize: '32px', fontWeight: 'bold' }}>Typography</h1> <TypographySection title="Headings" From 6fa725e2943392a9ec1e4c086038dc75d3b054cb Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 11:59:04 +0900 Subject: [PATCH 089/648] =?UTF-8?q?feat:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20effect=EC=9D=98=20=EC=9D=98=EC=A1=B4=EC=84=B1,=20re?= =?UTF-8?q?f=EC=A0=9C=EA=B1=B0=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/index.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/(landing)/signup/_ui/step/index.tsx b/app/(landing)/signup/_ui/step/index.tsx index d86483a7..3c9b27aa 100644 --- a/app/(landing)/signup/_ui/step/index.tsx +++ b/app/(landing)/signup/_ui/step/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect, useRef } from 'react' +import { useEffect } from 'react' import { usePathname } from 'next/navigation' @@ -27,15 +27,11 @@ const Step = () => { const stepHistory = useStepHistoryStore((state) => state.stepHistory) const addStep = useStepHistoryStore((state) => state.addStep) const removeStep = useStepHistoryStore((state) => state.removeStep) - const initialRender = useRef(true) const currentPath = usePathname() useEffect(() => { - if (initialRender.current) { - initialRender.current = false - handleStepHistoryControl() - } - }, [currentPath]) + handleStepHistoryControl() + }, []) const handleStepHistoryControl = () => { addStep(currentPath) From dc516531842671e5843d94ba2d8ea5c243e33511 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 15 Nov 2024 13:41:43 +0900 Subject: [PATCH 090/648] =?UTF-8?q?settings:=20letter-spacing=20em?= =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=B4=20base=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20(#37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_mixins.scss | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index 0f256fd3..d20d9670 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -64,24 +64,22 @@ font-style: normal; font-weight: $text-semibold; line-height: 132%; + letter-spacing: -0.02em; } @mixin typo-b1 { @extend %base-body; font-size: $text-b1; - letter-spacing: -0.4px; } @mixin typo-b2 { @extend %base-body; font-size: $text-b2; - letter-spacing: -0.32px; } @mixin typo-b3 { @extend %base-body; font-size: $text-b3; - letter-spacing: -0.28px; } // caption @@ -89,16 +87,15 @@ font-style: normal; font-weight: $text-medium; line-height: 132%; + letter-spacing: -0.02em; } @mixin typo-c1 { @extend %base-caption; font-size: $text-c1; - letter-spacing: -0.24px; } @mixin typo-c2 { @extend %base-caption; font-size: $text-c2; - letter-spacing: -0.2px; } From 66e0fd001016ad0370c0c0cfd1e0352bc53554a4 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 14:18:18 +0900 Subject: [PATCH 091/648] =?UTF-8?q?feat:=20Link=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EA=B3=B5=ED=86=B5LinkButton=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/complete/page.tsx | 5 ++--- app/(landing)/signup/information/page.tsx | 7 +++---- app/(landing)/signup/terms-of-use/page.tsx | 7 +++---- app/(landing)/signup/user-type/page.tsx | 5 ++--- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/(landing)/signup/complete/page.tsx b/app/(landing)/signup/complete/page.tsx index a878f448..b12ab20e 100644 --- a/app/(landing)/signup/complete/page.tsx +++ b/app/(landing)/signup/complete/page.tsx @@ -1,14 +1,13 @@ -import Link from 'next/link' - import Step from '@/app/(landing)/signup/_ui/step' import { PATH } from '@/shared/constants/path' +import { LinkButton } from '@/shared/ui/link-button' const CompletePage = () => { return ( <> <Step /> - <Link href={PATH.SIGN_UP_INFORMATION}>이전</Link> + <LinkButton href={PATH.SIGN_UP_INFORMATION}>이전</LinkButton> </> ) } diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index 063e24d2..166540c5 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -1,15 +1,14 @@ -import Link from 'next/link' - import Step from '@/app/(landing)/signup/_ui/step' import { PATH } from '@/shared/constants/path' +import { LinkButton } from '@/shared/ui/link-button' const InformationPage = () => { return ( <> <Step /> - <Link href={PATH.SIGN_UP_COMPLETE}>다음</Link> - <Link href={PATH.SIGN_UP_TERMS_OF_USE}>이전</Link> + <LinkButton href={PATH.SIGN_UP_COMPLETE}>다음</LinkButton> + <LinkButton href={PATH.SIGN_UP_TERMS_OF_USE}>이전</LinkButton> </> ) } diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx index 1ffa2064..8f528aa6 100644 --- a/app/(landing)/signup/terms-of-use/page.tsx +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -1,15 +1,14 @@ -import Link from 'next/link' - import Step from '@/app/(landing)/signup/_ui/step' import { PATH } from '@/shared/constants/path' +import { LinkButton } from '@/shared/ui/link-button' const TermsOfUsePage = () => { return ( <> <Step /> - <Link href={PATH.SIGN_UP_INFORMATION}>다음</Link> - <Link href={PATH.SIGN_UP_USER_TYPE}>이전</Link> + <LinkButton href={PATH.SIGN_UP_INFORMATION}>다음</LinkButton> + <LinkButton href={PATH.SIGN_UP_USER_TYPE}>이전</LinkButton> </> ) } diff --git a/app/(landing)/signup/user-type/page.tsx b/app/(landing)/signup/user-type/page.tsx index 874a912c..b5b679f1 100644 --- a/app/(landing)/signup/user-type/page.tsx +++ b/app/(landing)/signup/user-type/page.tsx @@ -1,14 +1,13 @@ -import Link from 'next/link' - import Step from '@/app/(landing)/signup/_ui/step' import { PATH } from '@/shared/constants/path' +import { LinkButton } from '@/shared/ui/link-button' const UserTypePage = () => { return ( <> <Step /> - <Link href={PATH.SIGN_UP_TERMS_OF_USE}>다음</Link> + <LinkButton href={PATH.SIGN_UP_TERMS_OF_USE}>다음</LinkButton> </> ) } From 371aa90f0cb703b5ff31637a22d6272cef434d50 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 14:19:36 +0900 Subject: [PATCH 092/648] =?UTF-8?q?rename:=20styles=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/index.tsx | 2 +- app/(landing)/signup/_ui/step/step-item.tsx | 2 +- .../signup/_ui/step/{style.module.scss => styles.module.scss} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename app/(landing)/signup/_ui/step/{style.module.scss => styles.module.scss} (100%) diff --git a/app/(landing)/signup/_ui/step/index.tsx b/app/(landing)/signup/_ui/step/index.tsx index 3c9b27aa..ea9ff3c4 100644 --- a/app/(landing)/signup/_ui/step/index.tsx +++ b/app/(landing)/signup/_ui/step/index.tsx @@ -11,7 +11,7 @@ import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import useStepHistoryStore from '@/shared/stores/use-step-history-store' -import styles from './style.module.scss' +import styles from './styles.module.scss' /* eslint-disable react-hooks/exhaustive-deps */ diff --git a/app/(landing)/signup/_ui/step/step-item.tsx b/app/(landing)/signup/_ui/step/step-item.tsx index 59006b2e..b81f369e 100644 --- a/app/(landing)/signup/_ui/step/step-item.tsx +++ b/app/(landing)/signup/_ui/step/step-item.tsx @@ -8,7 +8,7 @@ import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import useStepHistoryStore from '@/shared/stores/use-step-history-store' -import styles from './style.module.scss' +import styles from './styles.module.scss' /* eslint-disable react-hooks/exhaustive-deps */ diff --git a/app/(landing)/signup/_ui/step/style.module.scss b/app/(landing)/signup/_ui/step/styles.module.scss similarity index 100% rename from app/(landing)/signup/_ui/step/style.module.scss rename to app/(landing)/signup/_ui/step/styles.module.scss From 476a7e3c3b4ca4486e8cae3cd0bd03336d0ad76e Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 14:39:02 +0900 Subject: [PATCH 093/648] =?UTF-8?q?feat:=20StarIcon=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/index.tsx | 1 + public/icons/star.svg | 3 +++ shared/styles/stories/icons.stories.tsx | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 public/icons/star.svg diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 810e3ab5..9b314739 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -24,3 +24,4 @@ export { default as StatisticsIcon } from './statistics.svg' export { default as StrategyRankingIcon } from './strategy-ranking.svg' export { default as StrategyIcon } from './strategy.svg' export { default as TradersIcon } from './traders.svg' +export { default as StarIcon } from './star.svg' diff --git a/public/icons/star.svg b/public/icons/star.svg new file mode 100644 index 00000000..4f3d2734 --- /dev/null +++ b/public/icons/star.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M12.0007 4.91797L14.1892 9.35173L19.0833 10.0671L15.542 13.5163L16.3777 18.3892L12.0007 16.0874L7.62355 18.3892L8.45931 13.5163L4.91797 10.0671L9.8121 9.35173L12.0007 4.91797Z" fill="#797979" stroke="#797979" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index 4b56f7d3..8e9d5a93 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -21,6 +21,7 @@ import { QuestionIcon, SearchIcon, SignOutIcon, + StarIcon, StatisticsIcon, StrategyIcon, StrategyRankingIcon, @@ -59,6 +60,7 @@ const icons = [ { name: 'StrategyRankingIcon', icon: StrategyRankingIcon }, { name: 'StrategyIcon', icon: StrategyIcon }, { name: 'TradersIcon', icon: TradersIcon }, + { name: 'StarIcon', icon: StarIcon }, ] export const Icons: Story = { From cb3bde0ecf28173d01394b33d3ee2932302126e1 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 15 Nov 2024 14:46:07 +0900 Subject: [PATCH 094/648] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/pagination/index.tsx | 10 +++++----- shared/ui/pagination/pagination.module.scss | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/shared/ui/pagination/index.tsx b/shared/ui/pagination/index.tsx index 3b597c00..14134405 100644 --- a/shared/ui/pagination/index.tsx +++ b/shared/ui/pagination/index.tsx @@ -23,7 +23,7 @@ const Pagination = ({ currentPage, maxPage, onPageChange }: Props) => { const [pages, setPages] = useState<PageType[]>([]) const getSequentialPages = useCallback((start: number, length: number) => { - return Array.from({ length }, (_, i) => start + i) + return Array.from({ length }, (_, idx) => start + idx) }, []) const calculatePages = useCallback(() => { @@ -53,20 +53,20 @@ const Pagination = ({ currentPage, maxPage, onPageChange }: Props) => { } return ( - <div className={styles.paginationContainer}> + <div className={styles.pagination}> {currentPage !== 1 && ( <button className={styles.left} onClick={handleLeft}> <ChevronLeftIcon /> </button> )} - {pages.map((page, index) => ( + {pages.map((page, idx) => ( <button - key={`${page}-${index}`} + key={`${page}-${idx}`} className={cx(styles.page, { [styles.active]: page === currentPage, })} disabled={page === DOTS} - style={page === DOTS ? { cursor: 'default', border: 0 } : {}} + style={page === DOTS ? { cursor: 'default' } : {}} onClick={() => handlePageClick(page)} > {page} diff --git a/shared/ui/pagination/pagination.module.scss b/shared/ui/pagination/pagination.module.scss index b5ac5a0f..8063d902 100644 --- a/shared/ui/pagination/pagination.module.scss +++ b/shared/ui/pagination/pagination.module.scss @@ -1,4 +1,4 @@ -.paginationContainer { +.pagination { display: flex; justify-content: center; gap: 10px; @@ -20,12 +20,13 @@ .page { &:hover { border: 1px solid $color-orange-200; - scale: 1.1; + background-color: $color-gray-100; transition: 0.3s; } &.active { background: $color-orange-400; + border: 1px solid $color-orange-400; color: $color-white; transition: 0.3s; } From ff80ad876d686f7f6843bd3b35bfbeb315b671d9 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 14:48:14 +0900 Subject: [PATCH 095/648] =?UTF-8?q?chore:=20StarIcon=20stroke=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B2=84=EC=A0=84=EC=9C=BC=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/star.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/icons/star.svg b/public/icons/star.svg index 4f3d2734..bf9e7ea1 100644 --- a/public/icons/star.svg +++ b/public/icons/star.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M12.0007 4.91797L14.1892 9.35173L19.0833 10.0671L15.542 13.5163L16.3777 18.3892L12.0007 16.0874L7.62355 18.3892L8.45931 13.5163L4.91797 10.0671L9.8121 9.35173L12.0007 4.91797Z" fill="#797979" stroke="#797979" stroke-linecap="round" stroke-linejoin="round"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M12.449 4.69666C12.3648 4.526 12.191 4.41797 12.0007 4.41797C11.8104 4.41797 11.6365 4.526 11.5523 4.69666L9.47999 8.89496L4.84567 9.57234C4.65739 9.59986 4.50105 9.73186 4.44236 9.91286C4.38367 10.0939 4.43281 10.2925 4.56912 10.4253L7.92206 13.691L7.13076 18.3047C7.09859 18.4923 7.17571 18.6819 7.3297 18.7938C7.48369 18.9056 7.68784 18.9204 7.85629 18.8318L12.0007 16.6523L16.145 18.8318C16.3135 18.9204 16.5176 18.9056 16.6716 18.7938C16.8256 18.6819 16.9027 18.4923 16.8706 18.3047L16.0793 13.691L19.4322 10.4253C19.5685 10.2925 19.6177 10.0939 19.559 9.91286C19.5003 9.73186 19.3439 9.59986 19.1557 9.57234L14.5213 8.89496L12.449 4.69666Z" fill="currentColor"/> </svg> From d09aac4d86f58b2d5ca410b28b4c1ce9c5128107 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 15:23:06 +0900 Subject: [PATCH 096/648] =?UTF-8?q?rename:=20table=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20tit?= =?UTF-8?q?le=20=EC=88=98=EC=A0=95=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/table/statistics/statistics-table.stories.tsx | 2 +- shared/ui/table/vertical/table.stories.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/ui/table/statistics/statistics-table.stories.tsx b/shared/ui/table/statistics/statistics-table.stories.tsx index 227f2f75..c38ae5de 100644 --- a/shared/ui/table/statistics/statistics-table.stories.tsx +++ b/shared/ui/table/statistics/statistics-table.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryFn } from '@storybook/react' import StatisticsTable from './index' const meta: Meta = { - title: 'component/Statistics-Table', + title: 'components/Statistics-Table', component: StatisticsTable, tags: ['autodocs'], } diff --git a/shared/ui/table/vertical/table.stories.tsx b/shared/ui/table/vertical/table.stories.tsx index 4e2bd5ef..1097dfe4 100644 --- a/shared/ui/table/vertical/table.stories.tsx +++ b/shared/ui/table/vertical/table.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryFn } from '@storybook/react' import VerticalTable from './index' const meta: Meta = { - title: 'component/Table', + title: 'components/Table', component: VerticalTable, tags: ['autodocs'], } From f31af75cee5768287b1f21264683ff9fe31e79d2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 15:25:40 +0900 Subject: [PATCH 097/648] =?UTF-8?q?rename:=20table=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EB=AA=85=20=EC=88=98=EC=A0=95=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/table/statistics/index.tsx | 2 +- .../statistics/{statistics.module.scss => styles.module.scss} | 0 shared/ui/table/vertical/index.tsx | 2 +- .../table/vertical/{vertical.module.scss => styles.module.scss} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename shared/ui/table/statistics/{statistics.module.scss => styles.module.scss} (100%) rename shared/ui/table/vertical/{vertical.module.scss => styles.module.scss} (100%) diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index 8c03cfac..ea289d4c 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames/bind' import { inKoreanData } from './inKorean' -import styles from './statistics.module.scss' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/shared/ui/table/statistics/statistics.module.scss b/shared/ui/table/statistics/styles.module.scss similarity index 100% rename from shared/ui/table/statistics/statistics.module.scss rename to shared/ui/table/statistics/styles.module.scss diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 44df9d10..580adee3 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames/bind' import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' -import styles from './vertical.module.scss' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/shared/ui/table/vertical/vertical.module.scss b/shared/ui/table/vertical/styles.module.scss similarity index 100% rename from shared/ui/table/vertical/vertical.module.scss rename to shared/ui/table/vertical/styles.module.scss From c23093ea1a91ff5d9849489c6ed2b12722a0f7a7 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 10:55:39 +0900 Subject: [PATCH 098/648] =?UTF-8?q?feat:=20Tabs=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/tabs/index.tsx | 40 +++++++++++++++++++++++++++++++ shared/ui/tabs/styles.module.scss | 40 +++++++++++++++++++++++++++++++ shared/ui/tabs/tab-button.tsx | 26 ++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 shared/ui/tabs/index.tsx create mode 100644 shared/ui/tabs/styles.module.scss create mode 100644 shared/ui/tabs/tab-button.tsx diff --git a/shared/ui/tabs/index.tsx b/shared/ui/tabs/index.tsx new file mode 100644 index 00000000..daad5c13 --- /dev/null +++ b/shared/ui/tabs/index.tsx @@ -0,0 +1,40 @@ +import classNames from 'classnames/bind' + +import TabButton from '@/shared/ui/tabs/tab-button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export interface TabItemModel { + id: string + label: string + icon?: React.FC<React.SVGProps<SVGSVGElement>> + content: React.ReactElement +} + +interface Props { + tabs: TabItemModel[] + activeTab: string + onTabChange: (id: string) => void +} + +const Tabs = ({ tabs, activeTab, onTabChange }: Props) => { + return ( + <> + <ul className={cx('tab-list')}> + {tabs.map(({ id, label, icon: Icon }) => ( + <li key={id}> + <TabButton isActive={id === activeTab} onClick={() => onTabChange(id)}> + {Icon && <Icon className={cx('icon')} />} + {label} + </TabButton> + </li> + ))} + </ul> + <div>{tabs.find((tab) => tab.id === activeTab)?.content}</div> + </> + ) +} + +export default Tabs diff --git a/shared/ui/tabs/styles.module.scss b/shared/ui/tabs/styles.module.scss new file mode 100644 index 00000000..7aad32df --- /dev/null +++ b/shared/ui/tabs/styles.module.scss @@ -0,0 +1,40 @@ +.tab-list { + display: flex; + gap: 20px; + border-bottom: 2px solid $color-gray-300; +} + +.tab-button { + position: relative; + display: flex; + align-items: center; + padding-bottom: 20px; + font-size: $text-b1; + font-weight: $text-semibold; + color: $color-gray-400; + line-height: normal; + background-color: transparent; + + .icon { + margin-right: 8px; + fill: $color-gray-400; + } + + &.active { + color: $color-orange-500; + + .icon { + fill: $color-orange-500; + } + + &::after { + content: ''; + position: absolute; + bottom: -3px; + left: 0; + width: 100%; + height: 4px; + background-color: $color-orange-500; + } + } +} diff --git a/shared/ui/tabs/tab-button.tsx b/shared/ui/tabs/tab-button.tsx new file mode 100644 index 00000000..c86b6831 --- /dev/null +++ b/shared/ui/tabs/tab-button.tsx @@ -0,0 +1,26 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isActive: boolean + onClick: React.MouseEventHandler<HTMLButtonElement> + children: React.ReactNode +} + +const TabButton = ({ isActive, onClick, children }: Props) => { + return ( + <button + type="button" + role="tab" + className={cx('tab-button', isActive && 'active')} + onClick={onClick} + > + {children} + </button> + ) +} + +export default TabButton From d18a97f7fe05cea7e80dae51d310a08cf097408d Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 10:56:20 +0900 Subject: [PATCH 099/648] =?UTF-8?q?feat:=20Tabs=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/tabs/tabs.stories.tsx | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 shared/ui/tabs/tabs.stories.tsx diff --git a/shared/ui/tabs/tabs.stories.tsx b/shared/ui/tabs/tabs.stories.tsx new file mode 100644 index 00000000..83592f70 --- /dev/null +++ b/shared/ui/tabs/tabs.stories.tsx @@ -0,0 +1,60 @@ +import { useState } from 'react' + +import { DailyGraphIcon, MoneyIcon, MonthlyGraphIcon, StatisticsIcon } from '@/public/icons' +import type { Meta, StoryFn } from '@storybook/react' + +import Tabs, { TabItemModel } from './index' + +const meta: Meta = { + title: 'components/Tabs', + component: Tabs, + tags: ['autodocs'], +} + +const tabsData: TabItemModel[] = [ + { id: 'all', label: '모든 회원', content: <div style={{ marginTop: 30 }}>모든회원</div> }, + { id: 'investor', label: '일반', content: <div style={{ marginTop: 30 }}>일반</div> }, + { id: 'trader', label: '트레이더', content: <div style={{ marginTop: 30 }}>트레이더</div> }, + { id: 'admin', label: '관리자', content: <div style={{ marginTop: 30 }}>관리자</div> }, +] + +const iconTabsData: TabItemModel[] = [ + { + id: 'statistics', + label: '통계', + content: <div style={{ marginTop: 30 }}>통계</div>, + icon: StatisticsIcon, + }, + { + id: 'daily', + label: '일간분석', + content: <div style={{ marginTop: 30 }}>일간분석</div>, + icon: DailyGraphIcon, + }, + { + id: 'monthly', + label: '월간분석', + content: <div style={{ marginTop: 30 }}>월간분석</div>, + icon: MonthlyGraphIcon, + }, + { + id: 'account', + label: '실거래계좌', + content: <div style={{ marginTop: 30 }}>실거래계좌</div>, + icon: MoneyIcon, + }, +] + +export const Default: StoryFn = () => { + const [activeTab, setActiveTab] = useState(tabsData[0].id) + + return <Tabs tabs={tabsData} activeTab={activeTab} onTabChange={setActiveTab} /> +} + +export const WithIcons: StoryFn = () => { + const [activeTab, setActiveTab] = useState(iconTabsData[0].id) + + return <Tabs tabs={iconTabsData} activeTab={activeTab} onTabChange={setActiveTab} /> +} + +export default meta From cc92c7f83e3ae2bd2d3c2487ed7e929bfc5d7152 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 16:34:15 +0900 Subject: [PATCH 100/648] =?UTF-8?q?feat:=20=EA=B0=81=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8A=B8=20=ED=8F=B4=EB=8D=94=EC=97=90=20page.tsx=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80=20(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/favorites/.gitkeep | 0 app/(dashboard)/my/favorites/page.tsx | 5 +++++ app/(dashboard)/my/profile/page.tsx | 5 +++++ app/(dashboard)/my/questions/.gitkeep | 0 app/(dashboard)/my/questions/page.tsx | 5 +++++ app/(dashboard)/my/strategies/.gitkeep | 0 app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep | 0 app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx | 5 +++++ app/(dashboard)/my/strategies/page.tsx | 5 +++++ app/(dashboard)/strategies/[strategyId]/.gitkeep | 0 app/(dashboard)/strategies/[strategyId]/page.tsx | 5 +++++ app/(dashboard)/traders/.gitkeep | 0 app/(dashboard)/traders/[traderId]/.gitkeep | 0 app/(dashboard)/traders/[traderId]/page.tsx | 5 +++++ app/(dashboard)/traders/page.tsx | 5 +++++ app/{(dashboard) => (landing)/(home)/_ui}/.gitkeep | 0 app/(landing)/(home)/page.module.css | 0 .../my/.gitkeep => (landing)/(home)/page.module.scss} | 0 app/(landing)/(home)/page.tsx | 6 +++--- app/(landing)/(home)/ui/.gitkeep | 0 app/(landing)/signin/.gitkeep | 0 app/(landing)/signin/page.tsx | 5 +++++ app/admin/category/.gitkeep | 0 app/admin/category/page.tsx | 5 +++++ app/admin/notices/.gitkeep | 0 app/admin/notices/page.tsx | 5 +++++ app/admin/questions/page.tsx | 5 +++++ app/admin/strategies/page.tsx | 5 +++++ 28 files changed, 68 insertions(+), 3 deletions(-) delete mode 100644 app/(dashboard)/my/favorites/.gitkeep create mode 100644 app/(dashboard)/my/favorites/page.tsx create mode 100644 app/(dashboard)/my/profile/page.tsx delete mode 100644 app/(dashboard)/my/questions/.gitkeep create mode 100644 app/(dashboard)/my/questions/page.tsx delete mode 100644 app/(dashboard)/my/strategies/.gitkeep delete mode 100644 app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep create mode 100644 app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx create mode 100644 app/(dashboard)/my/strategies/page.tsx delete mode 100644 app/(dashboard)/strategies/[strategyId]/.gitkeep create mode 100644 app/(dashboard)/strategies/[strategyId]/page.tsx delete mode 100644 app/(dashboard)/traders/.gitkeep delete mode 100644 app/(dashboard)/traders/[traderId]/.gitkeep create mode 100644 app/(dashboard)/traders/[traderId]/page.tsx create mode 100644 app/(dashboard)/traders/page.tsx rename app/{(dashboard) => (landing)/(home)/_ui}/.gitkeep (100%) delete mode 100644 app/(landing)/(home)/page.module.css rename app/{(dashboard)/my/.gitkeep => (landing)/(home)/page.module.scss} (100%) delete mode 100644 app/(landing)/(home)/ui/.gitkeep delete mode 100644 app/(landing)/signin/.gitkeep create mode 100644 app/(landing)/signin/page.tsx delete mode 100644 app/admin/category/.gitkeep create mode 100644 app/admin/category/page.tsx delete mode 100644 app/admin/notices/.gitkeep create mode 100644 app/admin/notices/page.tsx create mode 100644 app/admin/questions/page.tsx create mode 100644 app/admin/strategies/page.tsx diff --git a/app/(dashboard)/my/favorites/.gitkeep b/app/(dashboard)/my/favorites/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/my/favorites/page.tsx b/app/(dashboard)/my/favorites/page.tsx new file mode 100644 index 00000000..6d98eaf1 --- /dev/null +++ b/app/(dashboard)/my/favorites/page.tsx @@ -0,0 +1,5 @@ +const MyFavoritesPage = () => { + return <></> +} + +export default MyFavoritesPage diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx new file mode 100644 index 00000000..c0cd893f --- /dev/null +++ b/app/(dashboard)/my/profile/page.tsx @@ -0,0 +1,5 @@ +const MyProfilePage = () => { + return <></> +} + +export default MyProfilePage diff --git a/app/(dashboard)/my/questions/.gitkeep b/app/(dashboard)/my/questions/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/my/questions/page.tsx b/app/(dashboard)/my/questions/page.tsx new file mode 100644 index 00000000..5f164eef --- /dev/null +++ b/app/(dashboard)/my/questions/page.tsx @@ -0,0 +1,5 @@ +const MyQuestionsPage = () => { + return <></> +} + +export default MyQuestionsPage diff --git a/app/(dashboard)/my/strategies/.gitkeep b/app/(dashboard)/my/strategies/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep b/app/(dashboard)/my/strategies/manage/[strategyId]/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx new file mode 100644 index 00000000..f3022b99 --- /dev/null +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -0,0 +1,5 @@ +const StrategyManagePage = () => { + return <></> +} + +export default StrategyManagePage diff --git a/app/(dashboard)/my/strategies/page.tsx b/app/(dashboard)/my/strategies/page.tsx new file mode 100644 index 00000000..6f7e77b5 --- /dev/null +++ b/app/(dashboard)/my/strategies/page.tsx @@ -0,0 +1,5 @@ +const MyStrategiesPage = () => { + return <></> +} + +export default MyStrategiesPage diff --git a/app/(dashboard)/strategies/[strategyId]/.gitkeep b/app/(dashboard)/strategies/[strategyId]/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx new file mode 100644 index 00000000..1fffbb1f --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -0,0 +1,5 @@ +const StrategyDetailPage = () => { + return <></> +} + +export default StrategyDetailPage diff --git a/app/(dashboard)/traders/.gitkeep b/app/(dashboard)/traders/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/traders/[traderId]/.gitkeep b/app/(dashboard)/traders/[traderId]/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/traders/[traderId]/page.tsx b/app/(dashboard)/traders/[traderId]/page.tsx new file mode 100644 index 00000000..66a91ecf --- /dev/null +++ b/app/(dashboard)/traders/[traderId]/page.tsx @@ -0,0 +1,5 @@ +const TraderDetailPage = () => { + return <></> +} + +export default TraderDetailPage diff --git a/app/(dashboard)/traders/page.tsx b/app/(dashboard)/traders/page.tsx new file mode 100644 index 00000000..77fbb01d --- /dev/null +++ b/app/(dashboard)/traders/page.tsx @@ -0,0 +1,5 @@ +const TradersPage = () => { + return <></> +} + +export default TradersPage diff --git a/app/(dashboard)/.gitkeep b/app/(landing)/(home)/_ui/.gitkeep similarity index 100% rename from app/(dashboard)/.gitkeep rename to app/(landing)/(home)/_ui/.gitkeep diff --git a/app/(landing)/(home)/page.module.css b/app/(landing)/(home)/page.module.css deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(dashboard)/my/.gitkeep b/app/(landing)/(home)/page.module.scss similarity index 100% rename from app/(dashboard)/my/.gitkeep rename to app/(landing)/(home)/page.module.scss diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index 83ca4582..ae3650fe 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,5 +1,5 @@ -const Home = () => { - return <div>Home</div> +const HomePage = () => { + return <>Home</> } -export default Home +export default HomePage diff --git a/app/(landing)/(home)/ui/.gitkeep b/app/(landing)/(home)/ui/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(landing)/signin/.gitkeep b/app/(landing)/signin/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx new file mode 100644 index 00000000..eb8da8c5 --- /dev/null +++ b/app/(landing)/signin/page.tsx @@ -0,0 +1,5 @@ +const SignInPage = () => { + return <></> +} + +export default SignInPage diff --git a/app/admin/category/.gitkeep b/app/admin/category/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/admin/category/page.tsx b/app/admin/category/page.tsx new file mode 100644 index 00000000..6c9b9f84 --- /dev/null +++ b/app/admin/category/page.tsx @@ -0,0 +1,5 @@ +const AdminCategoryPage = () => { + return <></> +} + +export default AdminCategoryPage diff --git a/app/admin/notices/.gitkeep b/app/admin/notices/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/admin/notices/page.tsx b/app/admin/notices/page.tsx new file mode 100644 index 00000000..e1304ad7 --- /dev/null +++ b/app/admin/notices/page.tsx @@ -0,0 +1,5 @@ +const AdminNoticesPage = () => { + return <></> +} + +export default AdminNoticesPage diff --git a/app/admin/questions/page.tsx b/app/admin/questions/page.tsx new file mode 100644 index 00000000..7fbeedf7 --- /dev/null +++ b/app/admin/questions/page.tsx @@ -0,0 +1,5 @@ +const AdminQuestionsPage = () => { + return <></> +} + +export default AdminQuestionsPage diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx new file mode 100644 index 00000000..fca16be7 --- /dev/null +++ b/app/admin/strategies/page.tsx @@ -0,0 +1,5 @@ +const AdminStrategiesPage = () => { + return <></> +} + +export default AdminStrategiesPage From be6dac64e33761ad9f403f5b78c5e0cc14a5d568 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 16:41:07 +0900 Subject: [PATCH 101/648] =?UTF-8?q?fix:=20my/profile,=20my/questions=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/constants/path.ts b/shared/constants/path.ts index 91bd0fd8..da671cef 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -9,11 +9,11 @@ export const PATH = { TRADERS: '/traders', // My - PROFILE: 'my/profile', + PROFILE: '/my/profile', FAVORITES: '/my/favorites', MY_STRATEGIES: '/my/strategies', STRATEGIES_MANAGE: '/my/strategies/manage', - MY_QUESTIONS: 'my/questions', + MY_QUESTIONS: '/my/questions', // Admin ADMIN: '/admin', From 0ab372436dbd0ec286b9d1808a19d15251b3154e Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 16:45:10 +0900 Subject: [PATCH 102/648] =?UTF-8?q?fix:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=EC=9D=98=20=EA=B0=80=EB=A0=A4=EC=A7=84=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=EC=97=90=EC=84=9C=EB=8F=84=20hover=20?= =?UTF-8?q?=ED=9A=A8=EA=B3=BC=EA=B0=80=20=EB=82=98=ED=83=80=EB=82=98?= =?UTF-8?q?=EB=8A=94=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/side-navigation/styles.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/ui/side-navigation/styles.module.scss b/shared/ui/side-navigation/styles.module.scss index 9b703502..01dd42e7 100644 --- a/shared/ui/side-navigation/styles.module.scss +++ b/shared/ui/side-navigation/styles.module.scss @@ -13,6 +13,7 @@ &:not(:hover) { width: 90px; + overflow: hidden; .text { opacity: 0; From 18580353705ee567ea1a404eff3ae38c3fb976de Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 15 Nov 2024 16:48:24 +0900 Subject: [PATCH 103/648] =?UTF-8?q?fix:=20profile=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EC=97=90=EC=84=9C=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=EC=9D=98=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=EC=9D=B4=20active=20=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/side-navigation/nav-link-item.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/ui/side-navigation/nav-link-item.tsx b/shared/ui/side-navigation/nav-link-item.tsx index 783cecf4..53408ed6 100644 --- a/shared/ui/side-navigation/nav-link-item.tsx +++ b/shared/ui/side-navigation/nav-link-item.tsx @@ -5,6 +5,8 @@ import { usePathname } from 'next/navigation' import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' + import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -18,7 +20,7 @@ interface Props { const NavLinkItem = ({ href, icon: Icon, textClassName, children }: Props) => { const path = usePathname() - const isActive = path.startsWith(href) + const isActive = path.startsWith(href) && href !== PATH.PROFILE return ( <li className={cx('navigation-item')}> From 1a35d4098617f3283ace60369c83f3d8ff6c6ad0 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 19:42:05 +0900 Subject: [PATCH 104/648] =?UTF-8?q?feat:=20=EC=B4=9D=EB=B3=84=EC=A0=90=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#4?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/star.svg | 2 +- shared/ui/total-star/index.tsx | 30 ++++++++++++++++++ shared/ui/total-star/star-icon.tsx | 24 +++++++++++++++ shared/ui/total-star/styles.module.scss | 34 +++++++++++++++++++++ shared/ui/total-star/total-star.stories.tsx | 33 ++++++++++++++++++++ 5 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 shared/ui/total-star/index.tsx create mode 100644 shared/ui/total-star/star-icon.tsx create mode 100644 shared/ui/total-star/styles.module.scss create mode 100644 shared/ui/total-star/total-star.stories.tsx diff --git a/public/icons/star.svg b/public/icons/star.svg index bf9e7ea1..b1cbc594 100644 --- a/public/icons/star.svg +++ b/public/icons/star.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<svg width="100%" height="100%" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M12.449 4.69666C12.3648 4.526 12.191 4.41797 12.0007 4.41797C11.8104 4.41797 11.6365 4.526 11.5523 4.69666L9.47999 8.89496L4.84567 9.57234C4.65739 9.59986 4.50105 9.73186 4.44236 9.91286C4.38367 10.0939 4.43281 10.2925 4.56912 10.4253L7.92206 13.691L7.13076 18.3047C7.09859 18.4923 7.17571 18.6819 7.3297 18.7938C7.48369 18.9056 7.68784 18.9204 7.85629 18.8318L12.0007 16.6523L16.145 18.8318C16.3135 18.9204 16.5176 18.9056 16.6716 18.7938C16.8256 18.6819 16.9027 18.4923 16.8706 18.3047L16.0793 13.691L19.4322 10.4253C19.5685 10.2925 19.6177 10.0939 19.559 9.91286C19.5003 9.73186 19.3439 9.59986 19.1557 9.57234L14.5213 8.89496L12.449 4.69666Z" fill="currentColor"/> </svg> diff --git a/shared/ui/total-star/index.tsx b/shared/ui/total-star/index.tsx new file mode 100644 index 00000000..74273cef --- /dev/null +++ b/shared/ui/total-star/index.tsx @@ -0,0 +1,30 @@ +import classNames from 'classnames/bind' + +import Star from '@/shared/ui/total-star/star-icon' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export type SizeType = 'small' | 'medium' +export type ColorType = 'black' | 'gray' + +interface Props { + averageRating: number + totalElements: number + size?: SizeType + color?: ColorType +} +const TotalStar = ({ averageRating, totalElements, size = 'small', color = 'gray' }: Props) => { + return ( + <div className={cx('container', size, color)}> + <div className={cx('icon')}> + <Star size={size} /> + </div> + <p>{averageRating}</p> + <p>({totalElements})</p> + </div> + ) +} + +export default TotalStar diff --git a/shared/ui/total-star/star-icon.tsx b/shared/ui/total-star/star-icon.tsx new file mode 100644 index 00000000..78a4d64e --- /dev/null +++ b/shared/ui/total-star/star-icon.tsx @@ -0,0 +1,24 @@ +'use client' + +import { StarIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { SizeType } from '@/shared/ui/total-star' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + size?: SizeType +} + +const Star = ({ size }: Props) => { + return ( + <div className={cx('icon-wrapper', size)}> + <StarIcon /> + </div> + ) +} + +export default Star diff --git a/shared/ui/total-star/styles.module.scss b/shared/ui/total-star/styles.module.scss new file mode 100644 index 00000000..034e2209 --- /dev/null +++ b/shared/ui/total-star/styles.module.scss @@ -0,0 +1,34 @@ +.container { + display: flex; + align-items: center; + .icon { + color: $color-yellow; + } + &.small { + @include typo-c1; + } + &.medium { + @include typo-b2; + } + &.black { + color: $color-gray-800; + } + &.gray { + color: $color-gray-400; + } + p { + padding-top: 2px; + margin-left: 2px; + } +} +.icon-wrapper { + display: block; + &.small { + width: 22px; + height: 22px; + } + &.medium { + width: 28px; + height: 28px; + } +} diff --git a/shared/ui/total-star/total-star.stories.tsx b/shared/ui/total-star/total-star.stories.tsx new file mode 100644 index 00000000..778e3d50 --- /dev/null +++ b/shared/ui/total-star/total-star.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, StoryFn } from '@storybook/react' + +import TotalStar, { ColorType, SizeType } from './index' + +const meta: Meta<typeof TotalStar> = { + title: 'components/TotalStar', + component: TotalStar, + tags: ['autodocs'], +} + +const star: StoryFn<{ size: SizeType; color: ColorType }> = (args) => ( + <TotalStar averageRating={4.9} totalElements={62} {...args} /> +) + +export const Primary = star.bind({}) +Primary.args = { + size: 'small', + color: 'gray', +} + +export const ForStrategiesPage = star.bind({}) +Primary.args = { + size: 'small', + color: 'black', +} + +export const ForReviewPage = star.bind({}) +ForReviewPage.args = { + size: 'medium', + color: 'gray', +} + +export default meta From fecc249e3a687b09b9bd50f2579a673fe9fad23d Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 15 Nov 2024 19:58:32 +0900 Subject: [PATCH 105/648] =?UTF-8?q?design:=20current=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_ui/step/styles.module.scss | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/app/(landing)/signup/_ui/step/styles.module.scss b/app/(landing)/signup/_ui/step/styles.module.scss index 9cae82d1..ce2fea03 100644 --- a/app/(landing)/signup/_ui/step/styles.module.scss +++ b/app/(landing)/signup/_ui/step/styles.module.scss @@ -10,6 +10,7 @@ margin-top: 24px; background-color: $color-white; position: relative; + &.next, &.prev { &::after { @@ -22,13 +23,15 @@ background-color: $color-orange-200; } } + &.next::after { - animation: gaugeUp 0.5s linear forwards; + animation: gaugeUp 0.3s linear forwards; } &.prev::after { animation: gaugeDown 0.5s linear forwards; } + &.fix { background-color: $color-orange-200; animation: none; @@ -43,6 +46,7 @@ width: 100%; } } + @keyframes gaugeDown { to { width: 0; @@ -55,6 +59,7 @@ .step-item { color: $color-gray-400; font-weight: $text-semibold; + .circle { width: 58px; height: 52px; @@ -64,14 +69,38 @@ border-radius: 25px; background-color: $color-white; font-size: $text-h4; + position: relative; + overflow: hidden; + &.next { background-color: $color-orange-200; } + &.current { - background-color: $color-orange-400; + position: relative; + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 0; + width: 0; + height: 100%; + background-color: $color-orange-400; + transform: translateY(-50%); + animation: fillCircle 0.3s 0.3s forwards; + z-index: 1; + } + + &.prev { + background-color: $color-orange-400; + animation: none; + } } + .icon { color: $color-gray-700; + z-index: 2; } } @@ -83,3 +112,12 @@ } } } + +@keyframes fillCircle { + from { + width: 0%; + } + to { + width: 100%; + } +} From ba736cdc0bea8ffdbde655d801aa22fa9b18ea5a Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 16 Nov 2024 12:50:35 +0900 Subject: [PATCH 106/648] =?UTF-8?q?feat:=20useStepHistoryStore=20Actions?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=ED=99=94=20=EC=B2=98=EB=A6=AC=20=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/index.tsx | 3 +-- shared/stores/use-step-history-store.tsx | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/(landing)/signup/_ui/step/index.tsx b/app/(landing)/signup/_ui/step/index.tsx index ea9ff3c4..9394159b 100644 --- a/app/(landing)/signup/_ui/step/index.tsx +++ b/app/(landing)/signup/_ui/step/index.tsx @@ -25,8 +25,7 @@ export const STEP_OF_PATH: { [key: string]: number } = { } const Step = () => { const stepHistory = useStepHistoryStore((state) => state.stepHistory) - const addStep = useStepHistoryStore((state) => state.addStep) - const removeStep = useStepHistoryStore((state) => state.removeStep) + const { addStep, removeStep } = useStepHistoryStore((state) => state.actions) const currentPath = usePathname() useEffect(() => { diff --git a/shared/stores/use-step-history-store.tsx b/shared/stores/use-step-history-store.tsx index b4cb3c54..3524fc30 100644 --- a/shared/stores/use-step-history-store.tsx +++ b/shared/stores/use-step-history-store.tsx @@ -8,11 +8,16 @@ interface ActionModel { addStep: (step: string) => void removeStep: () => void } +interface ActionsModel { + actions: ActionModel +} -const useStepHistoryStore = create<StateModel & ActionModel>((set) => ({ +const useStepHistoryStore = create<StateModel & ActionsModel>((set) => ({ stepHistory: [], - addStep: (step) => set((state) => ({ ...state, stepHistory: [...state.stepHistory, step] })), - removeStep: () => set((state) => ({ ...state, stepHistory: state.stepHistory.slice(1) })), + actions: { + addStep: (step) => set((state) => ({ ...state, stepHistory: [...state.stepHistory, step] })), + removeStep: () => set((state) => ({ ...state, stepHistory: state.stepHistory.slice(1) })), + }, })) export default useStepHistoryStore From 398665fb71050bb8ab0e52911b48513c120a8625 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 16 Nov 2024 13:09:10 +0900 Subject: [PATCH 107/648] =?UTF-8?q?feat:=20=EA=B0=81=20=EC=8A=A4=ED=85=9D?= =?UTF-8?q?=EC=9D=98=20path,=20icon,=20step,=20label=EC=9D=84=20=EB=B0=B0?= =?UTF-8?q?=EC=97=B4=EB=A1=9C=20=EA=B4=80=EB=A6=AC=ED=95=A0=EC=88=98?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EC=88=98=EC=A0=95=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/index.tsx | 40 ++++++++++----------- app/(landing)/signup/_ui/step/step-item.tsx | 4 +-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/(landing)/signup/_ui/step/index.tsx b/app/(landing)/signup/_ui/step/index.tsx index 9394159b..8dc13370 100644 --- a/app/(landing)/signup/_ui/step/index.tsx +++ b/app/(landing)/signup/_ui/step/index.tsx @@ -17,12 +17,13 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -export const STEP_OF_PATH: { [key: string]: number } = { - [PATH.SIGN_UP_USER_TYPE]: 1, - [PATH.SIGN_UP_TERMS_OF_USE]: 2, - [PATH.SIGN_UP_INFORMATION]: 3, - [PATH.SIGN_UP_COMPLETE]: 4, -} +export const STEPS = [ + { path: PATH.SIGN_UP_USER_TYPE, icon: ProfileIcon, step: 1, label: '회원 선택' }, + { path: PATH.SIGN_UP_TERMS_OF_USE, icon: BarsIcon, step: 2, label: '약관 동의' }, + { path: PATH.SIGN_UP_INFORMATION, icon: PencilIcon, step: 3, label: '정보 입력' }, + { path: PATH.SIGN_UP_COMPLETE, icon: CheckIcon, step: 4, label: '가입 완료' }, +] + const Step = () => { const stepHistory = useStepHistoryStore((state) => state.stepHistory) const { addStep, removeStep } = useStepHistoryStore((state) => state.actions) @@ -39,26 +40,25 @@ const Step = () => { } } - let prevStep = STEP_OF_PATH[stepHistory[0]] - const currentStep = STEP_OF_PATH[stepHistory[1]] + let prevStep = STEPS[STEPS.findIndex((step) => step.path === stepHistory[0])]?.step + const currentStep = STEPS[STEPS.findIndex((step) => step.path === stepHistory[1])]?.step if (currentStep === 1) { prevStep = 0 } return ( <div className={cx('step')}> - <StepItem step={1} pathname={PATH.SIGN_UP_USER_TYPE} icon={ProfileIcon} prevStep={prevStep}> - 회원 선택 - </StepItem> - <StepItem step={2} pathname={PATH.SIGN_UP_TERMS_OF_USE} icon={BarsIcon} prevStep={prevStep}> - 약관 동의 - </StepItem> - <StepItem step={3} pathname={PATH.SIGN_UP_INFORMATION} icon={PencilIcon} prevStep={prevStep}> - 정보 입력 - </StepItem> - <StepItem step={4} pathname={PATH.SIGN_UP_COMPLETE} icon={CheckIcon} prevStep={prevStep}> - 가입 완료 - </StepItem> + {STEPS.map((step) => ( + <StepItem + key={step.path} + step={step.step} + pathname={step.path} + icon={step.icon} + prevStep={prevStep} + > + {step.label} + </StepItem> + ))} </div> ) } diff --git a/app/(landing)/signup/_ui/step/step-item.tsx b/app/(landing)/signup/_ui/step/step-item.tsx index b81f369e..2ba80a0d 100644 --- a/app/(landing)/signup/_ui/step/step-item.tsx +++ b/app/(landing)/signup/_ui/step/step-item.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { usePathname } from 'next/navigation' -import { STEP_OF_PATH } from '@/app/(landing)/signup/_ui/step' +import { STEPS } from '@/app/(landing)/signup/_ui/step' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -37,7 +37,7 @@ const StepItem = ({ children, step, pathname, icon: Icon, prevStep }: Props) => } }, [currentPath]) - const currentPathIdx = Object.keys(STEP_OF_PATH).findIndex((path) => path === currentPath) + const currentPathIdx = STEPS.findIndex((step) => step.path === currentPath) const stepIdx = step - 1 const handleNextStep = () => { From cd47824452a692c9e1f49a783316b3be2355bfdf Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 16 Nov 2024 13:12:47 +0900 Subject: [PATCH 108/648] =?UTF-8?q?rename:=20color=20props=EB=AA=85=20text?= =?UTF-8?q?Color=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#45)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/total-star/index.tsx | 8 ++++---- shared/ui/total-star/total-star.stories.tsx | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/shared/ui/total-star/index.tsx b/shared/ui/total-star/index.tsx index 74273cef..f2c3bda1 100644 --- a/shared/ui/total-star/index.tsx +++ b/shared/ui/total-star/index.tsx @@ -7,17 +7,17 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) export type SizeType = 'small' | 'medium' -export type ColorType = 'black' | 'gray' +export type TextColorType = 'black' | 'gray' interface Props { averageRating: number totalElements: number size?: SizeType - color?: ColorType + textColor?: TextColorType } -const TotalStar = ({ averageRating, totalElements, size = 'small', color = 'gray' }: Props) => { +const TotalStar = ({ averageRating, totalElements, size = 'small', textColor = 'gray' }: Props) => { return ( - <div className={cx('container', size, color)}> + <div className={cx('container', size, textColor)}> <div className={cx('icon')}> <Star size={size} /> </div> diff --git a/shared/ui/total-star/total-star.stories.tsx b/shared/ui/total-star/total-star.stories.tsx index 778e3d50..be34d499 100644 --- a/shared/ui/total-star/total-star.stories.tsx +++ b/shared/ui/total-star/total-star.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryFn } from '@storybook/react' -import TotalStar, { ColorType, SizeType } from './index' +import TotalStar, { SizeType, TextColorType } from './index' const meta: Meta<typeof TotalStar> = { title: 'components/TotalStar', @@ -8,26 +8,26 @@ const meta: Meta<typeof TotalStar> = { tags: ['autodocs'], } -const star: StoryFn<{ size: SizeType; color: ColorType }> = (args) => ( +const star: StoryFn<{ size: SizeType; textColor: TextColorType }> = (args) => ( <TotalStar averageRating={4.9} totalElements={62} {...args} /> ) export const Primary = star.bind({}) Primary.args = { size: 'small', - color: 'gray', + textColor: 'gray', } export const ForStrategiesPage = star.bind({}) Primary.args = { size: 'small', - color: 'black', + textColor: 'black', } export const ForReviewPage = star.bind({}) ForReviewPage.args = { size: 'medium', - color: 'gray', + textColor: 'gray', } export default meta From 2e4c76ee74a5470c2e486cd0cdb8a4733227f825 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 16 Nov 2024 14:20:24 +0900 Subject: [PATCH 109/648] =?UTF-8?q?feat:=20=EC=95=84=EB=B0=94=ED=83=80=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#5?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/profile.svg | 2 +- shared/ui/avatar/index.tsx | 30 +++++++++++++++ shared/ui/avatar/styles.module.scss | 58 +++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 shared/ui/avatar/index.tsx create mode 100644 shared/ui/avatar/styles.module.scss diff --git a/public/icons/profile.svg b/public/icons/profile.svg index 10ec4f74..aa36a8f1 100644 --- a/public/icons/profile.svg +++ b/public/icons/profile.svg @@ -1,4 +1,4 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <circle cx="12" cy="9" r="4" fill="currentColor"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M19 20H5V18C5 15.7909 6.79086 14 9 14H15C17.2091 14 19 15.7909 19 18V20Z" fill="currentColor"/> </svg> diff --git a/shared/ui/avatar/index.tsx b/shared/ui/avatar/index.tsx new file mode 100644 index 00000000..67a58f78 --- /dev/null +++ b/shared/ui/avatar/index.tsx @@ -0,0 +1,30 @@ +'use client' + +import { CSSProperties } from 'react' + +import Image, { StaticImageData } from 'next/image' + +import { ProfileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +type AvatarSizeType = 'small' | 'medium' | 'large' + +interface Props { + src?: string | StaticImageData + size?: AvatarSizeType + avatarStyle?: CSSProperties +} + +const Avatar = ({ src, size = 'small', avatarStyle }: Props) => { + return ( + <div className={cx('avatar', size, avatarStyle)}> + {src ? <Image src={src} alt="프로필" fill /> : <ProfileIcon />} + </div> + ) +} + +export default Avatar diff --git a/shared/ui/avatar/styles.module.scss b/shared/ui/avatar/styles.module.scss new file mode 100644 index 00000000..5c069d98 --- /dev/null +++ b/shared/ui/avatar/styles.module.scss @@ -0,0 +1,58 @@ +$avatar-small: 24px; +$avatar-medium: 32px; +$avatar-large: 72px; + +$svg-small: 20px; +$svg-medium: 24px; +$svg-large: 54px; + +.avatar { + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + background-color: $color-gray-200; + border-radius: 50%; + overflow: hidden; + + &.small { + width: $avatar-small; + height: $avatar-small; + + svg { + width: $svg-small; + height: $svg-small; + } + } + + &.medium { + width: $avatar-medium; + height: $avatar-medium; + + svg { + width: $svg-medium; + height: $svg-medium; + } + } + + &.large { + width: $avatar-large; + height: $avatar-large; + + svg { + width: $svg-large; + height: $svg-large; + margin-bottom: 4px; + } + } + + svg { + margin-bottom: 2px; + fill: $color-gray-500; + } + + img { + object-fit: cover; + } +} From 8f741e70da2c7ab269e8bab6757cfc2e4971e61c Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 16 Nov 2024 14:27:39 +0900 Subject: [PATCH 110/648] =?UTF-8?q?feat:=20=EC=BB=A8=ED=94=8C=EB=A6=AD?= =?UTF-8?q?=ED=8A=B8=20=ED=95=B4=EA=B2=B0=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index a9274889..72c24c44 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,35 +1,21 @@ /** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ['msw'], - webpack: (config, { isServer }) => { - if (isServer) { - if (Array.isArray(config.resolve.alias)) { - config.resolve.alias.push({ name: 'msw/browser', alias: false }) - } else { - config.resolve.alias['msw/browser'] = false - } - } else { - if (Array.isArray(config.resolve.alias)) { - config.resolve.alias.push({ name: 'msw/node', alias: false }) - } else { - config.resolve.alias['msw/node'] = false - } - } - return config + experimental: { + appDir: true, }, sassOptions: { includePaths: ['./shared/styles'], prependData: ` - @import "@/shared/styles/base/variables"; - @import "@/shared/styles/base/mixins"; - @import "@/shared/styles/base/functions"; - `, + @import "@/shared/styles/base/variables"; + @import "@/shared/styles/base/mixins"; + @import "@/shared/styles/base/functions"; + `, }, async rewrites() { if (process.env.NODE_ENV === 'development') { return [] } - return [ { source: '/api/:path*', @@ -37,6 +23,29 @@ const nextConfig = { }, ] }, + webpack: (config, { isServer }) => { + if (isServer) { + if (Array.isArray(config.resolve.alias)) { + config.resolve.alias.push({ name: 'msw/browser', alias: false }) + } else { + config.resolve.alias['msw/browser'] = false + } + } else { + if (Array.isArray(config.resolve.alias)) { + config.resolve.alias.push({ name: 'msw/node', alias: false }) + } else { + config.resolve.alias['msw/node'] = false + } + } + + config.module.rules.push({ + test: /\.svg$/, + issuer: /\.[jt]sx?$/, + use: ['@svgr/webpack'], + }) + + return config + }, } export default nextConfig From 43e1ca458a2dc5c74552335249aa28bb97435dc0 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 16 Nov 2024 14:33:37 +0900 Subject: [PATCH 111/648] =?UTF-8?q?feat:=20=EC=95=84=EB=B0=94=ED=83=80=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#5?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/avatar/avatar.stories.tsx | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 shared/ui/avatar/avatar.stories.tsx diff --git a/shared/ui/avatar/avatar.stories.tsx b/shared/ui/avatar/avatar.stories.tsx new file mode 100644 index 00000000..9bb902eb --- /dev/null +++ b/shared/ui/avatar/avatar.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import Avatar from './index' + +const meta = { + title: 'components/Avatar', + component: Avatar, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + size: { + control: 'radio', + options: ['small', 'medium', 'large'], + defaultValue: 'small', + }, + src: { + control: 'text', + }, + }, +} satisfies Meta<typeof Avatar> + +type StoryType = StoryObj<typeof Avatar> + +const AVATAR = 'https://avatars.githubusercontent.com/u/108856689?v=4' + +export const Default: StoryType = { + args: { src: AVATAR }, +} + +export const NoImage: StoryType = { + args: {}, +} + +export const Sizes: StoryType = { + render: () => ( + <div style={{ display: 'flex', gap: 20, alignItems: 'center' }}> + <Avatar size="small" /> + <Avatar size="medium" /> + <Avatar size="large" /> + </div> + ), +} + +export default meta From 562ed31b8cf70315c35b01b8f78e27d01a7c5bb6 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 16 Nov 2024 14:34:17 +0900 Subject: [PATCH 112/648] =?UTF-8?q?fix:=20=EC=98=AC=EB=B0=94=EB=A5=B4?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?url=EC=9D=BC=20=EB=95=8C=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#55)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/avatar/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/shared/ui/avatar/index.tsx b/shared/ui/avatar/index.tsx index 67a58f78..7cc8d830 100644 --- a/shared/ui/avatar/index.tsx +++ b/shared/ui/avatar/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { CSSProperties } from 'react' +import { CSSProperties, useState } from 'react' import Image, { StaticImageData } from 'next/image' @@ -20,9 +20,15 @@ interface Props { } const Avatar = ({ src, size = 'small', avatarStyle }: Props) => { + const [isValidImage, setIsValidImage] = useState(true) + return ( <div className={cx('avatar', size, avatarStyle)}> - {src ? <Image src={src} alt="프로필" fill /> : <ProfileIcon />} + {src && isValidImage ? ( + <Image src={src} alt="프로필" fill onError={() => setIsValidImage(false)} /> + ) : ( + <ProfileIcon /> + )} </div> ) } From 300e5d9f59fe068b9b57c5aee0b3792d1f4481aa Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 17:36:13 +0900 Subject: [PATCH 113/648] =?UTF-8?q?feat:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20SCSS=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9,=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/page.tsx | 2 +- shared/constants/errorMessages.ts | 6 +++ shared/ui/input/index.tsx | 77 ++++++++++++++++++++++++++++++ shared/ui/input/styles.module.scss | 44 +++++++++++++++++ shared/utils/validation.ts | 28 +++++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 shared/constants/errorMessages.ts create mode 100644 shared/ui/input/index.tsx create mode 100644 shared/ui/input/styles.module.scss create mode 100644 shared/utils/validation.ts diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index ae3650fe..3635a839 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,5 +1,5 @@ const HomePage = () => { - return <>Home</> + return <div></div> } export default HomePage diff --git a/shared/constants/errorMessages.ts b/shared/constants/errorMessages.ts new file mode 100644 index 00000000..163220d4 --- /dev/null +++ b/shared/constants/errorMessages.ts @@ -0,0 +1,6 @@ +export const ERROR_MESSAGES = { + email: '이메일 형식이 잘못되었습니다.', + password: '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', + phone: '전화번호는 10자리 이상이어야 합니다.', + required: '필수 입력란입니다.', +} diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx new file mode 100644 index 00000000..7ae94505 --- /dev/null +++ b/shared/ui/input/index.tsx @@ -0,0 +1,77 @@ +'use client' + +import { ComponentProps, useEffect, useState } from 'react' + +import classNames from 'classnames/bind' + +import { validateInput } from '@/shared/utils/validation' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export type InputSizeType = 'small' | 'medium' | 'large' +export type InputVariantType = 'default' | 'error' +export type InputType = + | 'name' + | 'nickname' + | 'email' + | 'verificationCode' + | 'password' + | 'confirmPassword' + | 'phone' + | 'text' + +interface Props extends ComponentProps<'input'> { + inputSize?: InputSizeType + variant?: InputVariantType + type?: InputType + setIsInvalid?: (isValid: boolean) => void +} + +export const Input = ({ + inputSize = 'small', + variant = 'default', + type = 'text', + setIsInvalid, + className, + ...props +}: Props) => { + const [value, setValue] = useState<string>('') + const [errorMessage, setErrorMessage] = useState<string>('') + + useEffect(() => { + if (setIsInvalid) { + setIsInvalid(!!errorMessage) + } + }, [errorMessage, setIsInvalid]) + + const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { + const newValue = event.target.value + setValue(newValue) + + if (variant === 'error') { + const message = validateInput(type, newValue) + setErrorMessage(message) + } + } + + return ( + <div> + <input + value={value} + onChange={handleInputChange} + className={cx( + 'input', + inputSize, + { + error: !!errorMessage, + }, + className + )} + {...props} + /> + {errorMessage && <p className={cx('error-message')}>{errorMessage}</p>} + </div> + ) +} diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss new file mode 100644 index 00000000..81796b67 --- /dev/null +++ b/shared/ui/input/styles.module.scss @@ -0,0 +1,44 @@ +.input { + color: $color-gray-400; + border: 1px solid $color-gray-300; + border-radius: 2px; + height: 48px; + font-size: $text-b3; + padding: 14px 24px; + display: inline-block; + + &:focus { + color: $color-gray-500; + border-color: $color-gray-500; + } + + &.error { + border-color: $color-orange-500; + } + + &.small { + width: 132px; + } + &.medium { + width: 300px; + } + &.large { + width: 400px; + } + &:disabled { + color: $color-gray-200; + border-color: $color-gray-200; + background-color: $color-gray-100; + cursor: not-allowed; + } + &:required { + border-color: $color-indigo; + } +} + +.error-message { + color: $color-orange-500; + font-size: $text-b3; + margin-top: 10px; + margin-bottom: 10px; +} diff --git a/shared/utils/validation.ts b/shared/utils/validation.ts new file mode 100644 index 00000000..c168027f --- /dev/null +++ b/shared/utils/validation.ts @@ -0,0 +1,28 @@ +import { ERROR_MESSAGES } from '@/shared/constants/errorMessages' + +export const validateInput = (type: string, value: string): string => { + switch (type) { + case 'email': { + const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ + return emailPattern.test(value) ? '' : ERROR_MESSAGES.email + } + case 'password': { + const passwordPattern = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/ + return passwordPattern.test(value) ? '' : ERROR_MESSAGES.password + } + case 'phone': { + const phonePattern = /^\d{10,11}$/ + return phonePattern.test(value) ? '' : ERROR_MESSAGES.phone + } + case 'name': + case 'nickname': + case 'verificationCode': { + return value.trim() === '' ? ERROR_MESSAGES.required : '' + } + case 'text': { + return '' + } + default: + return '' + } +} From f5da74707e5f9c5079a3b8b1f80bbfb6577e31b1 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 18:44:47 +0900 Subject: [PATCH 114/648] =?UTF-8?q?feat:=20search=20input=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20SCSS=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 1 - shared/ui/search-input/index.tsx | 23 ++++++++++++++++ shared/ui/search-input/styles.module.scss | 32 +++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 shared/ui/search-input/index.tsx create mode 100644 shared/ui/search-input/styles.module.scss diff --git a/package-lock.json b/package-lock.json index f872ce8e..1991b5b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4430,7 +4430,6 @@ "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-transform-react-constant-elements": "^7.21.3", diff --git a/shared/ui/search-input/index.tsx b/shared/ui/search-input/index.tsx new file mode 100644 index 00000000..c9ede261 --- /dev/null +++ b/shared/ui/search-input/index.tsx @@ -0,0 +1,23 @@ +'use client' + +import { ComponentProps } from 'react' + +import { SearchIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props extends ComponentProps<'input'> { + placeholder?: string +} + +export const SearchInput = ({ placeholder, ...props }: Props) => { + return ( + <div className={cx('search-input-wrapper')}> + <input className={cx('search-input')} placeholder={placeholder} {...props} /> + <SearchIcon className={cx('search-icon')} /> + </div> + ) +} diff --git a/shared/ui/search-input/styles.module.scss b/shared/ui/search-input/styles.module.scss new file mode 100644 index 00000000..bffb072d --- /dev/null +++ b/shared/ui/search-input/styles.module.scss @@ -0,0 +1,32 @@ +.search-input-wrapper { + position: relative; + display: inline-block; + width: 240px; + height: 30px; +} + +.search-input { + padding: 8px 12px; + padding-right: 30px; + border-radius: 4px; + border: 1px solid $color-gray-300; + color: $color-gray-400; + width: 100%; + height: 100%; + font-size: $text-c1; + display: inline-block; + + &:focus { + color: $color-gray-500; + border-color: $color-gray-500; + } +} + +.search-icon { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + color: $color-gray-500; + cursor: pointer; +} From 0d99b65d439712f8f00916f738c3e172ac8987d6 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 21:21:35 +0900 Subject: [PATCH 115/648] =?UTF-8?q?feat:=20textarea=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20input=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/index.tsx | 12 ++----- shared/ui/search-input/index.tsx | 21 ++++++++++--- shared/ui/textarea/index.tsx | 45 +++++++++++++++++++++++++++ shared/ui/textarea/styles.module.scss | 19 +++++++++++ 4 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 shared/ui/textarea/index.tsx create mode 100644 shared/ui/textarea/styles.module.scss diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index 7ae94505..5fac84d7 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -34,7 +34,6 @@ export const Input = ({ variant = 'default', type = 'text', setIsInvalid, - className, ...props }: Props) => { const [value, setValue] = useState<string>('') @@ -61,14 +60,9 @@ export const Input = ({ <input value={value} onChange={handleInputChange} - className={cx( - 'input', - inputSize, - { - error: !!errorMessage, - }, - className - )} + className={cx('input', inputSize, { + error: !!errorMessage, + })} {...props} /> {errorMessage && <p className={cx('error-message')}>{errorMessage}</p>} diff --git a/shared/ui/search-input/index.tsx b/shared/ui/search-input/index.tsx index c9ede261..fe8af54c 100644 --- a/shared/ui/search-input/index.tsx +++ b/shared/ui/search-input/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { ComponentProps } from 'react' +import { ComponentProps, useState } from 'react' import { SearchIcon } from '@/public/icons' import classNames from 'classnames/bind' @@ -11,13 +11,26 @@ const cx = classNames.bind(styles) interface Props extends ComponentProps<'input'> { placeholder?: string + handleSearchIconClick?: () => void } -export const SearchInput = ({ placeholder, ...props }: Props) => { +export const SearchInput = ({ placeholder = '', handleSearchIconClick, ...props }: Props) => { + const [value, setValue] = useState('') + + const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { + setValue(e.target.value) + } + return ( <div className={cx('search-input-wrapper')}> - <input className={cx('search-input')} placeholder={placeholder} {...props} /> - <SearchIcon className={cx('search-icon')} /> + <input + value={value} + onChange={handleInputChange} + placeholder={placeholder} + className={cx('search-input')} + {...props} + /> + <SearchIcon className={cx('search-icon')} onClick={handleSearchIconClick} /> </div> ) } diff --git a/shared/ui/textarea/index.tsx b/shared/ui/textarea/index.tsx new file mode 100644 index 00000000..18a433a1 --- /dev/null +++ b/shared/ui/textarea/index.tsx @@ -0,0 +1,45 @@ +'use client' + +import { ComponentProps, useEffect, useState } from 'react' + +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props extends ComponentProps<'textarea'> { + rows?: number +} + +export const Textarea = ({ rows = 5, className, ...props }: Props) => { + const [value, setValue] = useState('') + const [isFocused, setIsFocused] = useState(false) + + useEffect(() => { + if (value.length === 0) { + setIsFocused(false) + } + }, [value]) + + const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { + setValue(e.target.value) + } + + const handleFocus = () => { + setIsFocused(true) + } + + return ( + <div> + <textarea + value={value} + onChange={handleInputChange} + rows={rows} + onFocus={handleFocus} + className={cx('textarea', { focused: isFocused })} + {...props} + /> + </div> + ) +} diff --git a/shared/ui/textarea/styles.module.scss b/shared/ui/textarea/styles.module.scss new file mode 100644 index 00000000..da0d6920 --- /dev/null +++ b/shared/ui/textarea/styles.module.scss @@ -0,0 +1,19 @@ +.textarea { + padding: 8px 12px; + border-radius: 2px; + border: 1px solid $color-gray-300; + color: $color-gray-400; + width: 100%; + height: 100%; + font-size: $text-b3; + + &:focus { + color: $color-gray-500; + border-color: $color-gray-500; + } + + &.focused { + color: $color-gray-500; + border-color: $color-gray-500; + } +} From 927dfc5046e1e6f2eded43d8b523bcc5acb1ee0d Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 21:46:04 +0900 Subject: [PATCH 116/648] =?UTF-8?q?feat:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/index.tsx | 4 +- shared/ui/input/input.stories.tsx | 78 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 shared/ui/input/input.stories.tsx diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index 5fac84d7..3e03dc01 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -27,6 +27,7 @@ interface Props extends ComponentProps<'input'> { variant?: InputVariantType type?: InputType setIsInvalid?: (isValid: boolean) => void + className?: string } export const Input = ({ @@ -34,6 +35,7 @@ export const Input = ({ variant = 'default', type = 'text', setIsInvalid, + className, ...props }: Props) => { const [value, setValue] = useState<string>('') @@ -60,7 +62,7 @@ export const Input = ({ <input value={value} onChange={handleInputChange} - className={cx('input', inputSize, { + className={cx('input', inputSize, className, { error: !!errorMessage, })} {...props} diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx new file mode 100644 index 00000000..431764e4 --- /dev/null +++ b/shared/ui/input/input.stories.tsx @@ -0,0 +1,78 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { Input } from './index' + +const meta: Meta<typeof Input> = { + title: 'Components/Input', + component: Input, + args: { + inputSize: 'small', + variant: 'default', + placeholder: 'Enter text', + type: 'text', + }, + argTypes: { + inputSize: { + control: { type: 'radio' }, + options: ['small', 'medium', 'large'], + }, + variant: { + control: { type: 'radio' }, + options: ['default', 'error'], + }, + type: { + control: { type: 'select' }, + options: [ + 'name', + 'nickname', + 'email', + 'verificationCode', + 'password', + 'confirmPassword', + 'phone', + 'text', + ], + }, + }, +} + +export default meta +type StoryType = StoryObj<typeof Input> + +export const Default: StoryType = {} + +export const Error: StoryType = { + args: { + variant: 'error', + value: 'Invalid Input', + placeholder: 'Invalid value', + }, +} + +export const Small: StoryType = { + args: { + inputSize: 'small', + placeholder: 'Small input', + }, +} + +export const Medium: StoryType = { + args: { + inputSize: 'medium', + placeholder: 'Medium input', + }, +} + +export const Large: StoryType = { + args: { + inputSize: 'large', + placeholder: 'Large input', + }, +} + +export const Disabled: StoryType = { + args: { + disabled: true, + placeholder: 'Disabled input', + }, +} From 01b80d26e31507331c250f2526f12df47f632065 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 21:53:19 +0900 Subject: [PATCH 117/648] =?UTF-8?q?refactor:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/input.stories.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx index 431764e4..7ada030e 100644 --- a/shared/ui/input/input.stories.tsx +++ b/shared/ui/input/input.stories.tsx @@ -70,9 +70,14 @@ export const Large: StoryType = { }, } -export const Disabled: StoryType = { +export const WithValidation: StoryType = { args: { - disabled: true, - placeholder: 'Disabled input', + type: 'email', + placeholder: 'Enter a valid email', + }, + play: async ({ canvasElement }) => { + const input = canvasElement.querySelector('input') as HTMLInputElement + input.value = 'invalid email' + input.dispatchEvent(new Event('input', { bubbles: true })) }, } From 07a5774e710fffc529ee6b77e1980442fed814d2 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 22:04:08 +0900 Subject: [PATCH 118/648] =?UTF-8?q?feat:=20search=20input=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/search-input/search-input.stories.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 shared/ui/search-input/search-input.stories.tsx diff --git a/shared/ui/search-input/search-input.stories.tsx b/shared/ui/search-input/search-input.stories.tsx new file mode 100644 index 00000000..6e58a430 --- /dev/null +++ b/shared/ui/search-input/search-input.stories.tsx @@ -0,0 +1,34 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { SearchInput } from './index' + +const meta: Meta<typeof SearchInput> = { + title: 'Components/SearchInput', + component: SearchInput, + args: { + placeholder: 'Search...', + }, + argTypes: { + handleSearchIconClick: { action: 'clicked' }, + }, +} + +export default meta +type StoryType = StoryObj<typeof SearchInput> + +export const Default: StoryType = {} + +export const WithPlaceholder: StoryType = { + args: { + placeholder: 'Type to search...', + }, +} + +export const SearchExample: StoryType = { + render: () => ( + <div style={{ display: 'flex', gap: '10px' }}> + <SearchInput placeholder="Search products" /> + <SearchInput placeholder="Search users" /> + </div> + ), +} From 60dce8afe397e5215d55a3eabf92c29411854abd Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 22:09:49 +0900 Subject: [PATCH 119/648] =?UTF-8?q?feat:=20textarea=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/textarea/index.tsx | 2 +- shared/ui/textarea/textarea.stories.tsx | 54 +++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 shared/ui/textarea/textarea.stories.tsx diff --git a/shared/ui/textarea/index.tsx b/shared/ui/textarea/index.tsx index 18a433a1..b17aa4a5 100644 --- a/shared/ui/textarea/index.tsx +++ b/shared/ui/textarea/index.tsx @@ -37,7 +37,7 @@ export const Textarea = ({ rows = 5, className, ...props }: Props) => { onChange={handleInputChange} rows={rows} onFocus={handleFocus} - className={cx('textarea', { focused: isFocused })} + className={cx('textarea', className, { focused: isFocused })} {...props} /> </div> diff --git a/shared/ui/textarea/textarea.stories.tsx b/shared/ui/textarea/textarea.stories.tsx new file mode 100644 index 00000000..4ba5cd14 --- /dev/null +++ b/shared/ui/textarea/textarea.stories.tsx @@ -0,0 +1,54 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { Textarea } from './index' + +const meta: Meta<typeof Textarea> = { + title: 'Components/Textarea', + component: Textarea, + args: { + rows: 5, + placeholder: 'Enter text...', + }, + argTypes: { + rows: { + control: { type: 'number' }, + defaultValue: 5, + }, + }, +} + +export default meta +type StoryType = StoryObj<typeof Textarea> + +export const Default: StoryType = {} + +export const Focused: StoryType = { + args: { + placeholder: 'Focused textarea', + }, + play: async ({ canvasElement }) => { + const textarea = canvasElement.querySelector('textarea') as HTMLTextAreaElement + textarea.focus() + }, +} + +export const CustomClass: StoryType = { + args: { + className: 'custom-textarea', + placeholder: 'Textarea with custom class', + }, +} + +export const Disabled: StoryType = { + args: { + disabled: true, + placeholder: 'Disabled textarea', + }, +} + +export const WithLongText: StoryType = { + args: { + value: 'Hello \n\n Banana \n\n Banana', + placeholder: 'Textarea with long text', + }, +} From e55b6561c1a8ccc359b2db4c124ead57540c66e0 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 16 Nov 2024 23:34:03 +0900 Subject: [PATCH 120/648] =?UTF-8?q?refactor:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/page.tsx | 2 +- shared/ui/input/index.tsx | 1 + shared/ui/input/input.stories.tsx | 3 ++- shared/ui/input/styles.module.scss | 2 +- shared/ui/search-input/search-input.stories.tsx | 3 ++- shared/ui/search-input/styles.module.scss | 2 +- shared/ui/textarea/styles.module.scss | 2 +- shared/ui/textarea/textarea.stories.tsx | 3 ++- 8 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index 3635a839..ae3650fe 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,5 +1,5 @@ const HomePage = () => { - return <div></div> + return <>Home</> } export default HomePage diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index 3e03dc01..16a69d85 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -61,6 +61,7 @@ export const Input = ({ <div> <input value={value} + type={type} onChange={handleInputChange} className={cx('input', inputSize, className, { error: !!errorMessage, diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx index 7ada030e..abd97245 100644 --- a/shared/ui/input/input.stories.tsx +++ b/shared/ui/input/input.stories.tsx @@ -36,7 +36,6 @@ const meta: Meta<typeof Input> = { }, } -export default meta type StoryType = StoryObj<typeof Input> export const Default: StoryType = {} @@ -81,3 +80,5 @@ export const WithValidation: StoryType = { input.dispatchEvent(new Event('input', { bubbles: true })) }, } + +export default meta diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss index 81796b67..faa75eca 100644 --- a/shared/ui/input/styles.module.scss +++ b/shared/ui/input/styles.module.scss @@ -3,7 +3,7 @@ border: 1px solid $color-gray-300; border-radius: 2px; height: 48px; - font-size: $text-b3; + @include typo-body3; padding: 14px 24px; display: inline-block; diff --git a/shared/ui/search-input/search-input.stories.tsx b/shared/ui/search-input/search-input.stories.tsx index 6e58a430..6e6b3aa3 100644 --- a/shared/ui/search-input/search-input.stories.tsx +++ b/shared/ui/search-input/search-input.stories.tsx @@ -13,7 +13,6 @@ const meta: Meta<typeof SearchInput> = { }, } -export default meta type StoryType = StoryObj<typeof SearchInput> export const Default: StoryType = {} @@ -32,3 +31,5 @@ export const SearchExample: StoryType = { </div> ), } + +export default meta diff --git a/shared/ui/search-input/styles.module.scss b/shared/ui/search-input/styles.module.scss index bffb072d..cc8431d5 100644 --- a/shared/ui/search-input/styles.module.scss +++ b/shared/ui/search-input/styles.module.scss @@ -13,8 +13,8 @@ color: $color-gray-400; width: 100%; height: 100%; - font-size: $text-c1; display: inline-block; + @include typo-caption3; &:focus { color: $color-gray-500; diff --git a/shared/ui/textarea/styles.module.scss b/shared/ui/textarea/styles.module.scss index da0d6920..d9035c5a 100644 --- a/shared/ui/textarea/styles.module.scss +++ b/shared/ui/textarea/styles.module.scss @@ -5,7 +5,7 @@ color: $color-gray-400; width: 100%; height: 100%; - font-size: $text-b3; + @include typo-body3; &:focus { color: $color-gray-500; diff --git a/shared/ui/textarea/textarea.stories.tsx b/shared/ui/textarea/textarea.stories.tsx index 4ba5cd14..439f3793 100644 --- a/shared/ui/textarea/textarea.stories.tsx +++ b/shared/ui/textarea/textarea.stories.tsx @@ -17,7 +17,6 @@ const meta: Meta<typeof Textarea> = { }, } -export default meta type StoryType = StoryObj<typeof Textarea> export const Default: StoryType = {} @@ -52,3 +51,5 @@ export const WithLongText: StoryType = { placeholder: 'Textarea with long text', }, } + +export default meta From b20d4ff829f107e8547b96df62cf28c522aef6e5 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 17 Nov 2024 04:17:27 +0900 Subject: [PATCH 121/648] =?UTF-8?q?refactor:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{errorMessages.ts => error-messages.ts} | 0 shared/ui/input/index.tsx | 44 ++----------------- shared/ui/input/styles.module.scss | 3 +- shared/ui/search-input/index.tsx | 18 ++------ shared/ui/search-input/styles.module.scss | 4 +- shared/ui/textarea/index.tsx | 28 +----------- shared/ui/textarea/styles.module.scss | 2 +- shared/utils/validation.ts | 42 +++++++----------- 8 files changed, 30 insertions(+), 111 deletions(-) rename shared/constants/{errorMessages.ts => error-messages.ts} (100%) diff --git a/shared/constants/errorMessages.ts b/shared/constants/error-messages.ts similarity index 100% rename from shared/constants/errorMessages.ts rename to shared/constants/error-messages.ts diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index 16a69d85..a40db237 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -1,70 +1,34 @@ 'use client' -import { ComponentProps, useEffect, useState } from 'react' +import { ComponentProps } from 'react' import classNames from 'classnames/bind' -import { validateInput } from '@/shared/utils/validation' - import styles from './styles.module.scss' const cx = classNames.bind(styles) export type InputSizeType = 'small' | 'medium' | 'large' export type InputVariantType = 'default' | 'error' -export type InputType = - | 'name' - | 'nickname' - | 'email' - | 'verificationCode' - | 'password' - | 'confirmPassword' - | 'phone' - | 'text' interface Props extends ComponentProps<'input'> { inputSize?: InputSizeType variant?: InputVariantType - type?: InputType - setIsInvalid?: (isValid: boolean) => void - className?: string + errorMessage?: string } export const Input = ({ inputSize = 'small', variant = 'default', - type = 'text', - setIsInvalid, + errorMessage, className, ...props }: Props) => { - const [value, setValue] = useState<string>('') - const [errorMessage, setErrorMessage] = useState<string>('') - - useEffect(() => { - if (setIsInvalid) { - setIsInvalid(!!errorMessage) - } - }, [errorMessage, setIsInvalid]) - - const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { - const newValue = event.target.value - setValue(newValue) - - if (variant === 'error') { - const message = validateInput(type, newValue) - setErrorMessage(message) - } - } - return ( <div> <input - value={value} - type={type} - onChange={handleInputChange} className={cx('input', inputSize, className, { - error: !!errorMessage, + error: variant === 'error' && !!errorMessage, })} {...props} /> diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss index faa75eca..932cf270 100644 --- a/shared/ui/input/styles.module.scss +++ b/shared/ui/input/styles.module.scss @@ -3,9 +3,9 @@ border: 1px solid $color-gray-300; border-radius: 2px; height: 48px; - @include typo-body3; padding: 14px 24px; display: inline-block; + @include typo-b3; &:focus { color: $color-gray-500; @@ -38,6 +38,7 @@ .error-message { color: $color-orange-500; + font-size: $text-b3; margin-top: 10px; margin-bottom: 10px; diff --git a/shared/ui/search-input/index.tsx b/shared/ui/search-input/index.tsx index fe8af54c..555fb67b 100644 --- a/shared/ui/search-input/index.tsx +++ b/shared/ui/search-input/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { ComponentProps, useState } from 'react' +import { ComponentProps } from 'react' import { SearchIcon } from '@/public/icons' import classNames from 'classnames/bind' @@ -15,21 +15,9 @@ interface Props extends ComponentProps<'input'> { } export const SearchInput = ({ placeholder = '', handleSearchIconClick, ...props }: Props) => { - const [value, setValue] = useState('') - - const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValue(e.target.value) - } - return ( - <div className={cx('search-input-wrapper')}> - <input - value={value} - onChange={handleInputChange} - placeholder={placeholder} - className={cx('search-input')} - {...props} - /> + <div className={cx('search-input-container')}> + <input placeholder={placeholder} className={cx('search-input')} {...props} /> <SearchIcon className={cx('search-icon')} onClick={handleSearchIconClick} /> </div> ) diff --git a/shared/ui/search-input/styles.module.scss b/shared/ui/search-input/styles.module.scss index cc8431d5..9f632026 100644 --- a/shared/ui/search-input/styles.module.scss +++ b/shared/ui/search-input/styles.module.scss @@ -1,4 +1,4 @@ -.search-input-wrapper { +.search-input-container { position: relative; display: inline-block; width: 240px; @@ -14,7 +14,7 @@ width: 100%; height: 100%; display: inline-block; - @include typo-caption3; + @include typo-c1; &:focus { color: $color-gray-500; diff --git a/shared/ui/textarea/index.tsx b/shared/ui/textarea/index.tsx index b17aa4a5..dad3080f 100644 --- a/shared/ui/textarea/index.tsx +++ b/shared/ui/textarea/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { ComponentProps, useEffect, useState } from 'react' +import { ComponentProps } from 'react' import classNames from 'classnames/bind' @@ -13,33 +13,9 @@ interface Props extends ComponentProps<'textarea'> { } export const Textarea = ({ rows = 5, className, ...props }: Props) => { - const [value, setValue] = useState('') - const [isFocused, setIsFocused] = useState(false) - - useEffect(() => { - if (value.length === 0) { - setIsFocused(false) - } - }, [value]) - - const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { - setValue(e.target.value) - } - - const handleFocus = () => { - setIsFocused(true) - } - return ( <div> - <textarea - value={value} - onChange={handleInputChange} - rows={rows} - onFocus={handleFocus} - className={cx('textarea', className, { focused: isFocused })} - {...props} - /> + <textarea rows={rows} className={cx('textarea', className)} {...props} /> </div> ) } diff --git a/shared/ui/textarea/styles.module.scss b/shared/ui/textarea/styles.module.scss index d9035c5a..1e2d8037 100644 --- a/shared/ui/textarea/styles.module.scss +++ b/shared/ui/textarea/styles.module.scss @@ -5,7 +5,7 @@ color: $color-gray-400; width: 100%; height: 100%; - @include typo-body3; + @include typo-b3; &:focus { color: $color-gray-500; diff --git a/shared/utils/validation.ts b/shared/utils/validation.ts index c168027f..378c8f1d 100644 --- a/shared/utils/validation.ts +++ b/shared/utils/validation.ts @@ -1,28 +1,18 @@ -import { ERROR_MESSAGES } from '@/shared/constants/errorMessages' +export const isValidateEmail = (email: string): boolean => { + const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ + return emailPattern.test(email) +} + +export const isValidatePassword = (password: string): boolean => { + const passwordPattern = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/ + return passwordPattern.test(password) +} + +export const isValidatePhone = (phone: string): boolean => { + const phonePattern = /^\d{10,11}$/ + return phonePattern.test(phone) +} -export const validateInput = (type: string, value: string): string => { - switch (type) { - case 'email': { - const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ - return emailPattern.test(value) ? '' : ERROR_MESSAGES.email - } - case 'password': { - const passwordPattern = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/ - return passwordPattern.test(value) ? '' : ERROR_MESSAGES.password - } - case 'phone': { - const phonePattern = /^\d{10,11}$/ - return phonePattern.test(value) ? '' : ERROR_MESSAGES.phone - } - case 'name': - case 'nickname': - case 'verificationCode': { - return value.trim() === '' ? ERROR_MESSAGES.required : '' - } - case 'text': { - return '' - } - default: - return '' - } +export const isRequired = (value: string): boolean => { + return value.trim() !== '' } From 693923846cb23043620f873f84f903b4191349da Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 13:42:00 +0900 Subject: [PATCH 122/648] =?UTF-8?q?feat:=20UserTypeCard=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_ui/user-type-card/index.tsx | 40 +++++++++++++++++++ .../_ui/user-type-card/styles.module.scss | 26 ++++++++++++ shared/types/user.ts | 2 + 3 files changed, 68 insertions(+) create mode 100644 app/(landing)/signup/_ui/user-type-card/index.tsx create mode 100644 app/(landing)/signup/_ui/user-type-card/styles.module.scss diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx new file mode 100644 index 00000000..dc18df8b --- /dev/null +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -0,0 +1,40 @@ +'use client' + +import { useRouter } from 'next/navigation' + +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import { RoleSelectType } from '@/shared/types/user' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + role: RoleSelectType + title: string + highlight: string +} + +const UserTypeCard = ({ role, title, highlight }: Props) => { + const router = useRouter() + + const handleTypeSelect = () => { + // TODO: role 저장 + router.push(PATH.SIGN_UP_TERMS_OF_USE) + } + + return ( + <button className={cx('card')} onClick={handleTypeSelect}> + <h2 className={cx('title')}>{title}</h2> + <hr className={cx('line')} /> + <p className={cx('contents')}> + 인베스트메틱을 통해 <br /> + <span className={cx('highlight')}>{highlight}</span>해보세요! + </p> + </button> + ) +} + +export default UserTypeCard diff --git a/app/(landing)/signup/_ui/user-type-card/styles.module.scss b/app/(landing)/signup/_ui/user-type-card/styles.module.scss new file mode 100644 index 00000000..bc3eb058 --- /dev/null +++ b/app/(landing)/signup/_ui/user-type-card/styles.module.scss @@ -0,0 +1,26 @@ +.card { + width: 100%; + padding: 43px 44px 53px; + text-align: left; + background-color: $color-white; +} + +.title { + @include typo-h2; +} + +.line { + width: 100%; + margin: 30px 0; + border: 1px solid $color-gray-200; +} + +.contents { + @include typo-h4; + color: $color-gray-500; + line-height: 140%; +} + +.highlight { + color: $color-orange-600; +} diff --git a/shared/types/user.ts b/shared/types/user.ts index 7af7ea12..978ef47d 100644 --- a/shared/types/user.ts +++ b/shared/types/user.ts @@ -7,3 +7,5 @@ export interface UserModel { } export type RoleType = 'trader' | 'investor' | 'trader_admin' | 'investor_admin' + +export type RoleSelectType = 'trader' | 'investor' From d0f9204ed2f26ff0b7f448c0309c012c3299665c Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 13:47:51 +0900 Subject: [PATCH 123/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=9C=A0=ED=98=95=20=EC=84=A0=ED=83=9D=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#5?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/styles.module.scss | 2 +- app/(landing)/signup/_ui/user-type-card/index.tsx | 4 ++-- app/(landing)/signup/user-type/page.module.scss | 7 +++++++ app/(landing)/signup/user-type/page.tsx | 13 ++++++++++--- shared/styles/global.scss | 12 ++++++++++++ 5 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 app/(landing)/signup/user-type/page.module.scss diff --git a/app/(landing)/signup/_ui/step/styles.module.scss b/app/(landing)/signup/_ui/step/styles.module.scss index ce2fea03..5197438f 100644 --- a/app/(landing)/signup/_ui/step/styles.module.scss +++ b/app/(landing)/signup/_ui/step/styles.module.scss @@ -1,7 +1,7 @@ .step { display: flex; justify-content: center; - margin: 20px 0; + margin: 135px 0 0; } .bar { diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx index dc18df8b..e5a7c569 100644 --- a/app/(landing)/signup/_ui/user-type-card/index.tsx +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -12,12 +12,12 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - role: RoleSelectType + userType: RoleSelectType title: string highlight: string } -const UserTypeCard = ({ role, title, highlight }: Props) => { +const UserTypeCard = ({ userType, title, highlight }: Props) => { const router = useRouter() const handleTypeSelect = () => { diff --git a/app/(landing)/signup/user-type/page.module.scss b/app/(landing)/signup/user-type/page.module.scss new file mode 100644 index 00000000..986a3f5b --- /dev/null +++ b/app/(landing)/signup/user-type/page.module.scss @@ -0,0 +1,7 @@ +.card-container { + display: flex; + gap: 32px; + width: 100%; + max-width: 952px; + margin: 120px auto 0; +} diff --git a/app/(landing)/signup/user-type/page.tsx b/app/(landing)/signup/user-type/page.tsx index b5b679f1..325380b8 100644 --- a/app/(landing)/signup/user-type/page.tsx +++ b/app/(landing)/signup/user-type/page.tsx @@ -1,13 +1,20 @@ import Step from '@/app/(landing)/signup/_ui/step' +import UserTypeCard from '@/app/(landing)/signup/_ui/user-type-card' +import classNames from 'classnames/bind' -import { PATH } from '@/shared/constants/path' -import { LinkButton } from '@/shared/ui/link-button' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) const UserTypePage = () => { return ( <> <Step /> - <LinkButton href={PATH.SIGN_UP_TERMS_OF_USE}>다음</LinkButton> + <section className={cx('card-container')}> + <h2 className="visually-hidden">회원 유형 선택</h2> + <UserTypeCard userType="investor" title="투자자" highlight="다양한 투자 전략을 구독" /> + <UserTypeCard userType="trader" title="트레이더" highlight="나만의 투자 전략을 공유" /> + </section> </> ) } diff --git a/shared/styles/global.scss b/shared/styles/global.scss index 0d3439cf..e936e215 100644 --- a/shared/styles/global.scss +++ b/shared/styles/global.scss @@ -17,3 +17,15 @@ textarea { opacity: 1; /* Firefox */ } } + +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + border: 0; + clip: rect(0, 0, 0, 0); + overflow: hidden; + white-space: nowrap; +} From 7e70ece65f1d7437f8868d392a9f208522d824e2 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 13:54:14 +0900 Subject: [PATCH 124/648] =?UTF-8?q?feat:=20=EC=84=A0=ED=83=9D=ED=95=9C=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EC=9C=A0=ED=98=95=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_stores/use-signup-store.ts | 28 +++++++++++++++++++ .../signup/_ui/user-type-card/index.tsx | 4 ++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 app/(landing)/signup/_stores/use-signup-store.ts diff --git a/app/(landing)/signup/_stores/use-signup-store.ts b/app/(landing)/signup/_stores/use-signup-store.ts new file mode 100644 index 00000000..26ac99cb --- /dev/null +++ b/app/(landing)/signup/_stores/use-signup-store.ts @@ -0,0 +1,28 @@ +import { create } from 'zustand' + +import { RoleSelectType } from '@/shared/types/user' + +interface StateModel { + userType: RoleSelectType | null +} + +interface ActionModel { + setUserType: (userType: RoleSelectType) => void +} + +interface ActionsModel { + actions: ActionModel +} + +const initialState = { + userType: null, +} as const + +const useSignupStore = create<StateModel & ActionsModel>((set) => ({ + ...initialState, + actions: { + setUserType: (userType) => set((state) => ({ ...state, userType })), + }, +})) + +export default useSignupStore diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx index e5a7c569..cd5a72ea 100644 --- a/app/(landing)/signup/_ui/user-type-card/index.tsx +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -2,6 +2,7 @@ import { useRouter } from 'next/navigation' +import useSignupStore from '@/app/(landing)/signup/_stores/use-signup-store' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -19,9 +20,10 @@ interface Props { const UserTypeCard = ({ userType, title, highlight }: Props) => { const router = useRouter() + const { setUserType } = useSignupStore((state) => state.actions) const handleTypeSelect = () => { - // TODO: role 저장 + setUserType(userType) router.push(PATH.SIGN_UP_TERMS_OF_USE) } From b65f9d59ddb41240bb1f11dfe27fa47d076c83bb Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 17 Nov 2024 15:49:59 +0900 Subject: [PATCH 125/648] =?UTF-8?q?refactor:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=A0=84=EC=B2=B4=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/page.tsx | 2 +- shared/constants/error-messages.ts | 10 +++--- shared/types/error-message.ts | 3 ++ shared/ui/input/index.tsx | 18 ++++------- shared/ui/input/input.stories.tsx | 40 ++++------------------- shared/ui/input/styles.module.scss | 7 ++-- shared/ui/textarea/textarea.stories.tsx | 21 ++---------- shared/utils/validation.ts | 43 ++++++++++++++++++------- 8 files changed, 61 insertions(+), 83 deletions(-) create mode 100644 shared/types/error-message.ts diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index ae3650fe..6eae6a74 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,5 +1,5 @@ const HomePage = () => { - return <>Home</> + return <></> } export default HomePage diff --git a/shared/constants/error-messages.ts b/shared/constants/error-messages.ts index 163220d4..494a61c4 100644 --- a/shared/constants/error-messages.ts +++ b/shared/constants/error-messages.ts @@ -1,6 +1,6 @@ export const ERROR_MESSAGES = { - email: '이메일 형식이 잘못되었습니다.', - password: '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', - phone: '전화번호는 10자리 이상이어야 합니다.', - required: '필수 입력란입니다.', -} + EMAIL: '이메일 형식이 잘못되었습니다.', + PASSWORD: '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', + PHONE: '전화번호는 10자리 이상이어야 합니다.', + REQUIRED: '필수 입력란입니다.', +} as const diff --git a/shared/types/error-message.ts b/shared/types/error-message.ts new file mode 100644 index 00000000..2d9c43c2 --- /dev/null +++ b/shared/types/error-message.ts @@ -0,0 +1,3 @@ +import { ERROR_MESSAGES } from '../constants/error-messages' + +export type ErrorMessageType = (typeof ERROR_MESSAGES)[keyof typeof ERROR_MESSAGES] diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index a40db237..bd5be9af 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -4,31 +4,25 @@ import { ComponentProps } from 'react' import classNames from 'classnames/bind' +import { ErrorMessageType } from '@/shared/types/error-message' + import styles from './styles.module.scss' const cx = classNames.bind(styles) -export type InputSizeType = 'small' | 'medium' | 'large' -export type InputVariantType = 'default' | 'error' +export type InputSizeType = 'small' | 'medium' | 'large' | 'full' interface Props extends ComponentProps<'input'> { inputSize?: InputSizeType - variant?: InputVariantType - errorMessage?: string + errorMessage?: ErrorMessageType | null } -export const Input = ({ - inputSize = 'small', - variant = 'default', - errorMessage, - className, - ...props -}: Props) => { +export const Input = ({ inputSize = 'medium', errorMessage, className, ...props }: Props) => { return ( <div> <input className={cx('input', inputSize, className, { - error: variant === 'error' && !!errorMessage, + error: !!errorMessage, })} {...props} /> diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx index abd97245..4d374aff 100644 --- a/shared/ui/input/input.stories.tsx +++ b/shared/ui/input/input.stories.tsx @@ -6,32 +6,19 @@ const meta: Meta<typeof Input> = { title: 'Components/Input', component: Input, args: { - inputSize: 'small', - variant: 'default', + inputSize: 'medium', + errorMessage: null, placeholder: 'Enter text', type: 'text', }, argTypes: { inputSize: { control: { type: 'radio' }, - options: ['small', 'medium', 'large'], - }, - variant: { - control: { type: 'radio' }, - options: ['default', 'error'], + options: ['small', 'medium', 'large', 'full'], }, type: { control: { type: 'select' }, - options: [ - 'name', - 'nickname', - 'email', - 'verificationCode', - 'password', - 'confirmPassword', - 'phone', - 'text', - ], + options: ['email', 'password', 'phone', 'text'], }, }, } @@ -40,14 +27,6 @@ type StoryType = StoryObj<typeof Input> export const Default: StoryType = {} -export const Error: StoryType = { - args: { - variant: 'error', - value: 'Invalid Input', - placeholder: 'Invalid value', - }, -} - export const Small: StoryType = { args: { inputSize: 'small', @@ -69,15 +48,10 @@ export const Large: StoryType = { }, } -export const WithValidation: StoryType = { +export const Full: StoryType = { args: { - type: 'email', - placeholder: 'Enter a valid email', - }, - play: async ({ canvasElement }) => { - const input = canvasElement.querySelector('input') as HTMLInputElement - input.value = 'invalid email' - input.dispatchEvent(new Event('input', { bubbles: true })) + inputSize: 'full', + placeholder: 'full input', }, } diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss index 932cf270..13358628 100644 --- a/shared/ui/input/styles.module.scss +++ b/shared/ui/input/styles.module.scss @@ -25,6 +25,10 @@ &.large { width: 400px; } + &.full { + width: 100%; + height: 100%; + } &:disabled { color: $color-gray-200; border-color: $color-gray-200; @@ -38,8 +42,7 @@ .error-message { color: $color-orange-500; - - font-size: $text-b3; margin-top: 10px; margin-bottom: 10px; + @include typo-b3; } diff --git a/shared/ui/textarea/textarea.stories.tsx b/shared/ui/textarea/textarea.stories.tsx index 439f3793..87b4a63e 100644 --- a/shared/ui/textarea/textarea.stories.tsx +++ b/shared/ui/textarea/textarea.stories.tsx @@ -21,23 +21,6 @@ type StoryType = StoryObj<typeof Textarea> export const Default: StoryType = {} -export const Focused: StoryType = { - args: { - placeholder: 'Focused textarea', - }, - play: async ({ canvasElement }) => { - const textarea = canvasElement.querySelector('textarea') as HTMLTextAreaElement - textarea.focus() - }, -} - -export const CustomClass: StoryType = { - args: { - className: 'custom-textarea', - placeholder: 'Textarea with custom class', - }, -} - export const Disabled: StoryType = { args: { disabled: true, @@ -45,9 +28,9 @@ export const Disabled: StoryType = { }, } -export const WithLongText: StoryType = { +export const Scroll: StoryType = { args: { - value: 'Hello \n\n Banana \n\n Banana', + value: 'Hello \n\n Banana \n\n Banana \n\n Banana \n\n Banana \n\n Banana', placeholder: 'Textarea with long text', }, } diff --git a/shared/utils/validation.ts b/shared/utils/validation.ts index 378c8f1d..69bc9cd2 100644 --- a/shared/utils/validation.ts +++ b/shared/utils/validation.ts @@ -1,18 +1,39 @@ -export const isValidateEmail = (email: string): boolean => { - const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ - return emailPattern.test(email) +import { ERROR_MESSAGES } from '@/shared/constants/error-messages' + +import { ErrorMessageType } from '../types/error-message' + +const PATTERNS = { + EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, + PASSWORD: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/, + PHONE: /^\d{10,11}$/, +} as const + +const isValidEmail = (email: string): boolean => { + return PATTERNS.EMAIL.test(email) } -export const isValidatePassword = (password: string): boolean => { - const passwordPattern = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/ - return passwordPattern.test(password) +const isValidPassword = (password: string): boolean => { + return PATTERNS.PASSWORD.test(password) } -export const isValidatePhone = (phone: string): boolean => { - const phonePattern = /^\d{10,11}$/ - return phonePattern.test(phone) +const isValidPhone = (phone: string): boolean => { + return PATTERNS.PHONE.test(phone) } -export const isRequired = (value: string): boolean => { - return value.trim() !== '' +const validators = { + EMAIL: isValidEmail, + PASSWORD: isValidPassword, + PHONE: isValidPhone, +} as const + +export const validate = (name: keyof typeof validators, value: string): ErrorMessageType | null => { + if (!value.trim()) { + return ERROR_MESSAGES.REQUIRED + } + + if (!validators[name](value)) { + return ERROR_MESSAGES[name] + } + + return null } From d3f1c3112973c9db7c43a9d8a8062ed5ba655bd5 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 15:53:25 +0900 Subject: [PATCH 126/648] =?UTF-8?q?refactor:=20RoleSelectType=EC=9D=84=20?= =?UTF-8?q?=EC=9E=AC=EC=82=AC=EC=9A=A9=20=EA=B0=80=EB=8A=A5=ED=95=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#5?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_stores/use-signup-store.ts | 6 +++--- app/(landing)/signup/_ui/user-type-card/index.tsx | 4 ++-- shared/types/user.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/(landing)/signup/_stores/use-signup-store.ts b/app/(landing)/signup/_stores/use-signup-store.ts index 26ac99cb..d4776f0d 100644 --- a/app/(landing)/signup/_stores/use-signup-store.ts +++ b/app/(landing)/signup/_stores/use-signup-store.ts @@ -1,13 +1,13 @@ import { create } from 'zustand' -import { RoleSelectType } from '@/shared/types/user' +import { UserType } from '@/shared/types/user' interface StateModel { - userType: RoleSelectType | null + userType: UserType | null } interface ActionModel { - setUserType: (userType: RoleSelectType) => void + setUserType: (userType: UserType) => void } interface ActionsModel { diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx index cd5a72ea..600a1b01 100644 --- a/app/(landing)/signup/_ui/user-type-card/index.tsx +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -6,14 +6,14 @@ import useSignupStore from '@/app/(landing)/signup/_stores/use-signup-store' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' -import { RoleSelectType } from '@/shared/types/user' +import { UserType } from '@/shared/types/user' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - userType: RoleSelectType + userType: UserType title: string highlight: string } diff --git a/shared/types/user.ts b/shared/types/user.ts index 978ef47d..cb0d7f84 100644 --- a/shared/types/user.ts +++ b/shared/types/user.ts @@ -6,6 +6,6 @@ export interface UserModel { role: RoleType } -export type RoleType = 'trader' | 'investor' | 'trader_admin' | 'investor_admin' +export type UserType = 'trader' | 'investor' -export type RoleSelectType = 'trader' | 'investor' +export type RoleType = UserType | `${UserType}_admin` From b538f228335677b6a3985cbd2ffb067733cef528 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 15:58:41 +0900 Subject: [PATCH 127/648] =?UTF-8?q?design:=20UserTypeCard=20hover=20?= =?UTF-8?q?=ED=9A=A8=EA=B3=BC=20=EC=B6=94=EA=B0=80=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/user-type-card/styles.module.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/(landing)/signup/_ui/user-type-card/styles.module.scss b/app/(landing)/signup/_ui/user-type-card/styles.module.scss index bc3eb058..76321cb9 100644 --- a/app/(landing)/signup/_ui/user-type-card/styles.module.scss +++ b/app/(landing)/signup/_ui/user-type-card/styles.module.scss @@ -3,6 +3,12 @@ padding: 43px 44px 53px; text-align: left; background-color: $color-white; + border: 2px solid $color-white; + transition: 0.2s ease-in-out; + + &:hover { + border: 2px solid $color-orange-400; + } } .title { From d9d017a2e7603cd23b627ac4f78903addacbf54f Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 17 Nov 2024 16:05:09 +0900 Subject: [PATCH 128/648] =?UTF-8?q?refactor:=20=ED=99=88=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20Home=20=EC=9B=90=EC=83=81=EB=B3=B5=EA=B5=AC=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index 6eae6a74..ae3650fe 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,5 +1,5 @@ const HomePage = () => { - return <></> + return <>Home</> } export default HomePage From 81a96dc7536e76259abde755c2d3216000f2407d Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 17 Nov 2024 16:58:07 +0900 Subject: [PATCH 129/648] =?UTF-8?q?refactor:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20aut?= =?UTF-8?q?odocs=20=EC=B6=94=EA=B0=80=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/input.stories.tsx | 11 +++++++++++ shared/ui/search-input/search-input.stories.tsx | 1 + shared/ui/textarea/textarea.stories.tsx | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx index 4d374aff..8c53a5d5 100644 --- a/shared/ui/input/input.stories.tsx +++ b/shared/ui/input/input.stories.tsx @@ -20,7 +20,18 @@ const meta: Meta<typeof Input> = { control: { type: 'select' }, options: ['email', 'password', 'phone', 'text'], }, + errorMessage: { + control: { type: 'select' }, + options: [ + null, + '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', + '이메일 형식이 잘못되었습니다.', + '전화번호는 10자리 이상이어야 합니다.', + '필수 입력란입니다.', + ], + }, }, + tags: ['autodocs'], } type StoryType = StoryObj<typeof Input> diff --git a/shared/ui/search-input/search-input.stories.tsx b/shared/ui/search-input/search-input.stories.tsx index 6e6b3aa3..ced99a3e 100644 --- a/shared/ui/search-input/search-input.stories.tsx +++ b/shared/ui/search-input/search-input.stories.tsx @@ -11,6 +11,7 @@ const meta: Meta<typeof SearchInput> = { argTypes: { handleSearchIconClick: { action: 'clicked' }, }, + tags: ['autodocs'], } type StoryType = StoryObj<typeof SearchInput> diff --git a/shared/ui/textarea/textarea.stories.tsx b/shared/ui/textarea/textarea.stories.tsx index 87b4a63e..b80f2a6d 100644 --- a/shared/ui/textarea/textarea.stories.tsx +++ b/shared/ui/textarea/textarea.stories.tsx @@ -15,6 +15,7 @@ const meta: Meta<typeof Textarea> = { defaultValue: 5, }, }, + tags: ['autodocs'], } type StoryType = StoryObj<typeof Textarea> @@ -31,7 +32,7 @@ export const Disabled: StoryType = { export const Scroll: StoryType = { args: { value: 'Hello \n\n Banana \n\n Banana \n\n Banana \n\n Banana \n\n Banana', - placeholder: 'Textarea with long text', + placeholder: 'Textarea with scroll', }, } From f4a04a1943b03bee6ff0db02adc3e05a1883249a Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sun, 17 Nov 2024 16:59:19 +0900 Subject: [PATCH 130/648] =?UTF-8?q?chore:=20=ED=95=98=EC=9D=B4=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 11 +++++++++++ package.json | 1 + 2 files changed, 12 insertions(+) diff --git a/package-lock.json b/package-lock.json index 395a3cd0..38bf3b72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", + "highcharts-react-official": "^3.2.1", "next": "14.2.16", "react": "^18", "react-dom": "^18", @@ -9808,6 +9809,16 @@ "integrity": "sha512-5Tke9LuzZszC4osaFisxLIcw7xgNGz4Sy3Jc9pRMV+ydm6sYqsPYdU8ELOgpzGNrbrRNDRBtveoR5xS3SzneEA==", "license": "https://www.highcharts.com/license" }, + "node_modules/highcharts-react-official": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/highcharts-react-official/-/highcharts-react-official-3.2.1.tgz", + "integrity": "sha512-hyQTX7ezCxl7JqumaWiGsroGWalzh24GedQIgO3vJbkGOZ6ySRAltIYjfxhrq4HszJOySZegotEF7v+haQ75UA==", + "license": "MIT", + "peerDependencies": { + "highcharts": ">=6.0.0", + "react": ">=16.8.0" + } + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", diff --git a/package.json b/package.json index d545dbbb..c563d596 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@tanstack/react-query": "^5.59.19", "classnames": "^2.5.1", "highcharts": "^11.4.8", + "highcharts-react-official": "^3.2.1", "next": "14.2.16", "react": "^18", "react-dom": "^18", From 739746f486d3e9d7280229f4d14d64a9ecff9ddc Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 17 Nov 2024 17:02:23 +0900 Subject: [PATCH 131/648] =?UTF-8?q?feat:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/input.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx index 8c53a5d5..7d5aab05 100644 --- a/shared/ui/input/input.stories.tsx +++ b/shared/ui/input/input.stories.tsx @@ -18,7 +18,7 @@ const meta: Meta<typeof Input> = { }, type: { control: { type: 'select' }, - options: ['email', 'password', 'phone', 'text'], + options: ['email', 'password', 'tel', 'text'], }, errorMessage: { control: { type: 'select' }, From 16917a1cf3c6f463690f9d88cfab3ed3827ca831 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sun, 17 Nov 2024 17:02:37 +0900 Subject: [PATCH 132/648] =?UTF-8?q?feat:=20=EB=9D=BC=EC=9D=B8=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/line-chart/index.tsx | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 shared/ui/line-chart/index.tsx diff --git a/shared/ui/line-chart/index.tsx b/shared/ui/line-chart/index.tsx new file mode 100644 index 00000000..1d57bfde --- /dev/null +++ b/shared/ui/line-chart/index.tsx @@ -0,0 +1,68 @@ +'use client' + +import dynamic from 'next/dynamic' + +import Highcharts from 'highcharts' + +import { CardSizeType } from '../sm-score-card/index' + +const HighchartsReact = dynamic(() => import('highcharts-react-official'), { + ssr: false, +}) + +interface ChartProps { + data: number[] + isNegative?: boolean + size?: CardSizeType +} + +const getChartDimensions = (size: CardSizeType) => ({ + height: size === 'small' ? 55 : 165, + width: size === 'small' ? 90 : 185, +}) + +const Chart = ({ data, isNegative = false, size = 'small' }: ChartProps) => { + const dimensions = getChartDimensions(size) + + const chartOptions: Highcharts.Options = { + chart: { + type: 'line', + height: dimensions.height, + width: dimensions.width, + backgroundColor: 'transparent', + margin: [0, 0, 0, 0], + }, + title: { text: undefined }, + xAxis: { + visible: false, + }, + yAxis: { + visible: false, + min: Math.min(...data) * 0.95, + max: Math.max(...data) * 1.05, + }, + legend: { enabled: false }, + plotOptions: { + series: { + animation: false, + lineWidth: 2, + marker: { enabled: false }, + states: { hover: { enabled: false } }, + enableMouseTracking: false, + }, + }, + series: [ + { + type: 'line', + data, + color: isNegative ? '#6877FF' : '#FF7752', + }, + ], + credits: { enabled: false }, + tooltip: { enabled: false }, + } + + return <HighchartsReact highcharts={Highcharts} options={chartOptions} /> +} + +export default Chart From 13b2f93bdf1822582f2d645cff4e996cd3ce742a Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sun, 17 Nov 2024 17:05:31 +0900 Subject: [PATCH 133/648] =?UTF-8?q?feat:=20sm-score-card=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#52)=20-=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80(=EB=B0=B0=EA=B2=BD=EC=9D=80=20=EA=B2=80=EC=9D=80?= =?UTF-8?q?=EC=83=89=EC=9C=BC=EB=A1=9C=20=EC=84=A4=EC=A0=95=ED=95=B4?= =?UTF-8?q?=EB=86=A8=EC=8A=B5=EB=8B=88=EB=8B=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/sm-score-card/index.tsx | 69 +++++++++ .../sm-score-card/sm-score-card.stories.tsx | 131 ++++++++++++++++++ shared/ui/sm-score-card/styles.module.scss | 107 ++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 shared/ui/sm-score-card/index.tsx create mode 100644 shared/ui/sm-score-card/sm-score-card.stories.tsx create mode 100644 shared/ui/sm-score-card/styles.module.scss diff --git a/shared/ui/sm-score-card/index.tsx b/shared/ui/sm-score-card/index.tsx new file mode 100644 index 00000000..c9684b47 --- /dev/null +++ b/shared/ui/sm-score-card/index.tsx @@ -0,0 +1,69 @@ +'use client' + +import classNames from 'classnames/bind' + +import Avatar from '@/shared/ui/avatar' + +import Chart from '../line-chart' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export type CardSizeType = 'small' | 'large' + +export interface ScoreCardProps { + name: string + title: string + score: number + percentageChange: number + chartData: number[] + ranking: number + profileImage?: string + size?: CardSizeType +} + +const ScoreCard = ({ + name, + title, + score, + percentageChange, + chartData, + ranking, + profileImage, + size = 'small', +}: ScoreCardProps) => { + const isNegative = percentageChange < 0 + + return ( + <div className={cx('card-wrapper', size)}> + <div className={cx('top-frame', size)}> + <div className={cx('content-area')}> + <div className={cx('rank')}>Top {ranking}</div> + <div className={cx('profile')}> + <Avatar src={profileImage} size="small" /> + <span className={cx('profile-name')}>{name}</span> + </div> + <h3 className={cx('title')}>{title}</h3> + </div> + <div className={cx('chart-area', size)}> + <Chart data={chartData} isNegative={isNegative} size={size} /> + </div> + </div> + <div className={cx('bottom-frame')}> + <div className={cx('sm-score')}> + <span className={cx('label')}>SM SCORE</span> + <span className={cx('value', 'score')}>{score.toFixed(2)}</span> + </div> + <div className={cx('profit')}> + <span className={cx('label')}>누적수익률</span> + <span className={cx('value', { negative: isNegative })}> + {isNegative ? '' : '+'} + {percentageChange}% + </span> + </div> + </div> + </div> + ) +} + +export default ScoreCard diff --git a/shared/ui/sm-score-card/sm-score-card.stories.tsx b/shared/ui/sm-score-card/sm-score-card.stories.tsx new file mode 100644 index 00000000..f8d51e08 --- /dev/null +++ b/shared/ui/sm-score-card/sm-score-card.stories.tsx @@ -0,0 +1,131 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ScoreCard from './index' + +const meta = { + title: 'Components/ScoreCard', + component: ScoreCard, + parameters: { + layout: 'centered', + backgrounds: { + default: 'dark', + values: [ + { + name: 'dark', + value: '#000000', + }, + ], + }, + }, + tags: ['autodocs'], + argTypes: { + size: { + control: 'radio', + options: ['small', 'large'], + }, + }, + decorators: [ + (Story) => ( + <div style={{ padding: '20px' }}> + <Story /> + </div> + ), + ], +} satisfies Meta<typeof ScoreCard> + +export default meta +type StoryType = StoryObj<typeof meta> + +const mockChartData = [8, 15, 10, 17, 15, 19, 25, 20] + +export const Default: StoryType = { + args: { + name: '내 이름은 김다은', + title: '내 전략은 엄청나', + score: 60.63, + percentageChange: 37, + chartData: mockChartData, + ranking: 1, + size: 'small', + profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', + }, +} + +export const LargeCard: StoryType = { + args: { + ...Default.args, + size: 'large', + }, +} + +export const NegativeChange: StoryType = { + args: { + name: '나 김다은 아니다', + title: '내 전략은 엄청나ㅋ', + score: 50.63, + percentageChange: -12, + chartData: [20, 19, 17, 18, 15, 13, 11, 8], + ranking: 4, + profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', + }, +} + +export const LayoutExample: StoryType = { + args: { + name: '내 이름은 김다은', + title: '내 전략은 엄청나', + score: 60.63, + percentageChange: 37, + chartData: [8, 15, 10, 17, 15, 19, 25, 20], + ranking: 1, + size: 'large', + profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', + }, + decorators: [ + () => ( + <div style={{ width: '900px' }}> + <div + style={{ + display: 'flex', + gap: '20px', + width: '100%', + }} + > + <div style={{ flex: '0 0 300px' }}> + <ScoreCard + name="내 이름은 김다은" + title="내 전략은 엄청나" + score={60.63} + percentageChange={37} + chartData={[8, 15, 10, 17, 15, 19, 25, 20]} + ranking={1} + size="large" + profileImage="https://lh3.googleusercontent.com/a/your-image-id" + /> + </div> + <div + style={{ + flex: 1, + display: 'grid', + gridTemplateColumns: 'repeat(2, 1fr)', + gap: '20px', + }} + > + {[2, 3, 4, 5].map((ranking) => ( + <ScoreCard + key={ranking} + name="엄청난 트레이더" + title="내 전략은 엄청나" + score={60.63} + percentageChange={37} + chartData={[10, 12, 15, 14, 16, 18, 20, 18]} + ranking={ranking} + profileImage="https://lh3.googleusercontent.com/a/your-image-id" + /> + ))} + </div> + </div> + </div> + ), + ], +} diff --git a/shared/ui/sm-score-card/styles.module.scss b/shared/ui/sm-score-card/styles.module.scss new file mode 100644 index 00000000..9874f0ba --- /dev/null +++ b/shared/ui/sm-score-card/styles.module.scss @@ -0,0 +1,107 @@ +.card-wrapper { + background-color: $color-white; + border-radius: 5px; + padding: 14px 18px; + width: 300px; + min-width: 300px; + display: flex; + flex-direction: column; + + &.small { + height: 160px; + } + + &.large { + height: 340px; + } +} + +.top-frame { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + flex: 1; + + &.large { + flex-direction: column; + align-items: flex-start; + } +} + +.content-area { + display: flex; + flex-direction: column; + gap: 14px; +} + +.rank { + font-size: 20px; + font-weight: 700; + color: $color-black; +} + +.profile { + display: flex; + align-items: center; + + .profile-name { + font-size: 12px; + color: $color-gray-500; + font-weight: 500; + margin-left: 0.5rem; + } +} + +.title { + font-size: 16px; + font-weight: 600; + color: $color-black; + margin-top: -0.5rem; +} + +.chart-area { + display: flex; + align-items: center; + flex: 1; + justify-content: center; + + &.large { + width: 100%; + } + + &.small { + margin-left: 2rem; + } +} + +.bottom-frame { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 14px; +} + +.sm-score, +.profit { + display: flex; + gap: 0.5rem; + align-items: center; + font-size: 16px; + font-weight: 600; + margin-bottom: 7px; + + .label { + color: $color-gray-700; + } + + .value { + &.negative { + color: $color-indigo; + } + + &:not(.negative) { + color: $color-orange-800; + } + } +} From 0d9e8160b6651d2558b0e5203ce7b37af67c84ae Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sun, 17 Nov 2024 17:59:51 +0900 Subject: [PATCH 134/648] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#52)=20-=20=EC=BB=AC=EB=9F=AC?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EC=82=AC=EC=9A=A9=20-=20Export=20?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20Props=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/line-chart/index.tsx | 6 +++--- shared/ui/sm-score-card/index.tsx | 8 ++++---- shared/ui/sm-score-card/styles.module.scss | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/shared/ui/line-chart/index.tsx b/shared/ui/line-chart/index.tsx index 1d57bfde..7740671c 100644 --- a/shared/ui/line-chart/index.tsx +++ b/shared/ui/line-chart/index.tsx @@ -10,7 +10,7 @@ const HighchartsReact = dynamic(() => import('highcharts-react-official'), { ssr: false, }) -interface ChartProps { +interface Props { data: number[] isNegative?: boolean size?: CardSizeType @@ -21,7 +21,7 @@ const getChartDimensions = (size: CardSizeType) => ({ width: size === 'small' ? 90 : 185, }) -const Chart = ({ data, isNegative = false, size = 'small' }: ChartProps) => { +const LineChart = ({ data, isNegative = false, size = 'small' }: Props) => { const dimensions = getChartDimensions(size) const chartOptions: Highcharts.Options = { @@ -65,4 +65,4 @@ const Chart = ({ data, isNegative = false, size = 'small' }: ChartProps) => { return <HighchartsReact highcharts={Highcharts} options={chartOptions} /> } -export default Chart +export default LineChart diff --git a/shared/ui/sm-score-card/index.tsx b/shared/ui/sm-score-card/index.tsx index c9684b47..15f35322 100644 --- a/shared/ui/sm-score-card/index.tsx +++ b/shared/ui/sm-score-card/index.tsx @@ -4,14 +4,14 @@ import classNames from 'classnames/bind' import Avatar from '@/shared/ui/avatar' -import Chart from '../line-chart' +import LineChart from '../line-chart' import styles from './styles.module.scss' const cx = classNames.bind(styles) export type CardSizeType = 'small' | 'large' -export interface ScoreCardProps { +interface Props { name: string title: string score: number @@ -31,7 +31,7 @@ const ScoreCard = ({ ranking, profileImage, size = 'small', -}: ScoreCardProps) => { +}: Props) => { const isNegative = percentageChange < 0 return ( @@ -46,7 +46,7 @@ const ScoreCard = ({ <h3 className={cx('title')}>{title}</h3> </div> <div className={cx('chart-area', size)}> - <Chart data={chartData} isNegative={isNegative} size={size} /> + <LineChart data={chartData} isNegative={isNegative} size={size} /> </div> </div> <div className={cx('bottom-frame')}> diff --git a/shared/ui/sm-score-card/styles.module.scss b/shared/ui/sm-score-card/styles.module.scss index 9874f0ba..9b122c6e 100644 --- a/shared/ui/sm-score-card/styles.module.scss +++ b/shared/ui/sm-score-card/styles.module.scss @@ -36,8 +36,8 @@ } .rank { - font-size: 20px; - font-weight: 700; + font-size: $text-b1; + font-weight: $text-bold; color: $color-black; } @@ -46,16 +46,16 @@ align-items: center; .profile-name { - font-size: 12px; + font-size: $text-c1; color: $color-gray-500; - font-weight: 500; + font-weight: $text-medium; margin-left: 0.5rem; } } .title { - font-size: 16px; - font-weight: 600; + font-size: $text-b2; + font-weight: $text-semibold; color: $color-black; margin-top: -0.5rem; } @@ -87,8 +87,8 @@ display: flex; gap: 0.5rem; align-items: center; - font-size: 16px; - font-weight: 600; + font-size: $text-b2; + font-weight: $text-semibold; margin-bottom: 7px; .label { From 367f2faef843c0dc118431eff6e1cbeac0c6cc9e Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sun, 17 Nov 2024 18:04:00 +0900 Subject: [PATCH 135/648] =?UTF-8?q?rename:=20shared/ui=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20(landing)/(home)/=5Fui=EB=A1=9C=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EB=B3=80=EA=B2=BD=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_ui/.gitkeep | 0 {shared/ui => app/(landing)/(home)/_ui}/line-chart/index.tsx | 0 {shared/ui => app/(landing)/(home)/_ui}/sm-score-card/index.tsx | 0 .../(landing)/(home)/_ui}/sm-score-card/sm-score-card.stories.tsx | 0 .../(landing)/(home)/_ui}/sm-score-card/styles.module.scss | 0 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/(landing)/(home)/_ui/.gitkeep rename {shared/ui => app/(landing)/(home)/_ui}/line-chart/index.tsx (100%) rename {shared/ui => app/(landing)/(home)/_ui}/sm-score-card/index.tsx (100%) rename {shared/ui => app/(landing)/(home)/_ui}/sm-score-card/sm-score-card.stories.tsx (100%) rename {shared/ui => app/(landing)/(home)/_ui}/sm-score-card/styles.module.scss (100%) diff --git a/app/(landing)/(home)/_ui/.gitkeep b/app/(landing)/(home)/_ui/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/shared/ui/line-chart/index.tsx b/app/(landing)/(home)/_ui/line-chart/index.tsx similarity index 100% rename from shared/ui/line-chart/index.tsx rename to app/(landing)/(home)/_ui/line-chart/index.tsx diff --git a/shared/ui/sm-score-card/index.tsx b/app/(landing)/(home)/_ui/sm-score-card/index.tsx similarity index 100% rename from shared/ui/sm-score-card/index.tsx rename to app/(landing)/(home)/_ui/sm-score-card/index.tsx diff --git a/shared/ui/sm-score-card/sm-score-card.stories.tsx b/app/(landing)/(home)/_ui/sm-score-card/sm-score-card.stories.tsx similarity index 100% rename from shared/ui/sm-score-card/sm-score-card.stories.tsx rename to app/(landing)/(home)/_ui/sm-score-card/sm-score-card.stories.tsx diff --git a/shared/ui/sm-score-card/styles.module.scss b/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss similarity index 100% rename from shared/ui/sm-score-card/styles.module.scss rename to app/(landing)/(home)/_ui/sm-score-card/styles.module.scss From 88a6dd22f47fc74bfd8088edf8a99ee63afc526f Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sun, 17 Nov 2024 18:52:53 +0900 Subject: [PATCH 136/648] =?UTF-8?q?feat:=20=EC=A0=9C=EB=AA=A9=20ellipsis?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(home)/_ui/sm-score-card/styles.module.scss | 1 + shared/styles/base/_mixins.scss | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss b/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss index 9b122c6e..8d571acd 100644 --- a/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss +++ b/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss @@ -58,6 +58,7 @@ font-weight: $text-semibold; color: $color-black; margin-top: -0.5rem; + @include ellipsis(2); } .chart-area { diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index d20d9670..3ae51ca0 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -99,3 +99,17 @@ @extend %base-caption; font-size: $text-c2; } + +// ellipsis +@mixin ellipsis($lines: 1) { + @if $lines == 1 { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } @else { + display: -webkit-box; + -webkit-line-clamp: $lines; + -webkit-box-orient: vertical; + overflow: hidden; + } +} \ No newline at end of file From 96713f017fde110a276eb0df9f0614e7eb40de03 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 19:26:24 +0900 Subject: [PATCH 137/648] =?UTF-8?q?design:=20css=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=EC=97=90=20=EB=A7=9E=EC=B6=B0=20border:=200=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_reset.scss | 2 +- shared/ui/side-navigation/styles.module.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/styles/base/_reset.scss b/shared/styles/base/_reset.scss index 58df2786..ce27462e 100644 --- a/shared/styles/base/_reset.scss +++ b/shared/styles/base/_reset.scss @@ -43,7 +43,7 @@ button { input, textarea { - border: none; + border: 0; outline: none; background: none; font: inherit; diff --git a/shared/ui/side-navigation/styles.module.scss b/shared/ui/side-navigation/styles.module.scss index 01dd42e7..43087dc7 100644 --- a/shared/ui/side-navigation/styles.module.scss +++ b/shared/ui/side-navigation/styles.module.scss @@ -60,7 +60,7 @@ gap: 24px; width: 100%; padding: 10px 12px; - border: none; + border: 0; border-radius: 4px; text-align: left; font-weight: $text-semibold; From b820c75c5b96172b28b303e5aad528b95a108582 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 19:41:54 +0900 Subject: [PATCH 138/648] =?UTF-8?q?design:=20HomeLayout=EC=97=90=20main=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=B6=94=EA=B0=80=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/layout.tsx | 6 +++++- shared/styles/base/_variables.scss | 3 +++ shared/styles/global.scss | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/(landing)/layout.tsx b/app/(landing)/layout.tsx index 59f738c6..5015777f 100644 --- a/app/(landing)/layout.tsx +++ b/app/(landing)/layout.tsx @@ -3,7 +3,11 @@ interface Props { } const HomeLayout = ({ children }: Props) => { - return <>{children}</> + return ( + <> + <main className="landing-main">{children}</main> + </> + ) } export default HomeLayout diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index 497c80f9..cc09826a 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -64,3 +64,6 @@ $z-index: ( base: 1, hidden: -1, ); + +/* Width */ +$max-width: 1440px; diff --git a/shared/styles/global.scss b/shared/styles/global.scss index e936e215..09bd4d76 100644 --- a/shared/styles/global.scss +++ b/shared/styles/global.scss @@ -29,3 +29,27 @@ textarea { overflow: hidden; white-space: nowrap; } + +.landing-main { + position: relative; + width: 100%; + max-width: $max-width; + margin: 0 auto; + padding: 80px 20px 0; + + &::before { + content: ''; + position: absolute; + top: -160px; + right: -130px; + width: 600px; + height: 660px; + border-radius: 300px; + background: linear-gradient( + 142deg, + rgba(255, 119, 82, 0.15) 4.71%, + rgba(246, 238, 155, 0.15) 95.29% + ); + filter: blur(150px); + } +} From c7e7571b83a09f71cfa855ff036d8722c4534d50 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 19:52:55 +0900 Subject: [PATCH 139/648] =?UTF-8?q?design:=20dashboard=20layout=EC=97=90?= =?UTF-8?q?=20main=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=B6=94=EA=B0=80=20(#?= =?UTF-8?q?63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/layout.tsx | 2 +- app/admin/layout.tsx | 2 +- shared/styles/base/_variables.scss | 1 + shared/styles/global.scss | 7 +++++++ shared/ui/side-navigation/styles.module.scss | 2 +- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx index 48d78914..50a267c8 100644 --- a/app/(dashboard)/layout.tsx +++ b/app/(dashboard)/layout.tsx @@ -4,7 +4,7 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => { return ( <> <DashboardNavigation /> - <main>{children}</main> + <main className="dashboard-main">{children}</main> </> ) } diff --git a/app/admin/layout.tsx b/app/admin/layout.tsx index b9eade5e..c21699d6 100644 --- a/app/admin/layout.tsx +++ b/app/admin/layout.tsx @@ -4,7 +4,7 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => { return ( <> <AdminNavigation /> - <main>{children}</main> + <main className="dashboard-main">{children}</main> </> ) } diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index cc09826a..20d1dfc1 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -67,3 +67,4 @@ $z-index: ( /* Width */ $max-width: 1440px; +$navigation-width: 90px; diff --git a/shared/styles/global.scss b/shared/styles/global.scss index 09bd4d76..b47aba00 100644 --- a/shared/styles/global.scss +++ b/shared/styles/global.scss @@ -53,3 +53,10 @@ textarea { filter: blur(150px); } } + +.dashboard-main { + width: calc(100% - $navigation-width); + max-width: $max-width; + padding: 0 28px; + margin-left: $navigation-width; +} diff --git a/shared/ui/side-navigation/styles.module.scss b/shared/ui/side-navigation/styles.module.scss index 43087dc7..fa59c42f 100644 --- a/shared/ui/side-navigation/styles.module.scss +++ b/shared/ui/side-navigation/styles.module.scss @@ -12,7 +12,7 @@ cursor: pointer; &:not(:hover) { - width: 90px; + width: $navigation-width; overflow: hidden; .text { From 67ded7a8ec426497aab2482f1083bf979358dc82 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 19:55:28 +0900 Subject: [PATCH 140/648] =?UTF-8?q?design:=20step=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20icon=20=EC=82=AC=EC=9D=B4=EC=A6=88=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=88=20(#63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/styles.module.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/(landing)/signup/_ui/step/styles.module.scss b/app/(landing)/signup/_ui/step/styles.module.scss index 5197438f..4a235abf 100644 --- a/app/(landing)/signup/_ui/step/styles.module.scss +++ b/app/(landing)/signup/_ui/step/styles.module.scss @@ -99,8 +99,10 @@ } .icon { - color: $color-gray-700; z-index: 2; + width: 24px; + height: 24px; + color: $color-gray-700; } } From 4e954a87e64bef6b0f799322451b8a0ba4d630bc Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 21:43:33 +0900 Subject: [PATCH 141/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=99=84=EB=A3=8C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/signup-complete-message/index.tsx | 24 +++++++++++++++++++ .../styles.module.scss | 9 +++++++ .../signup/complete/page.module.scss | 6 +++++ app/(landing)/signup/complete/page.tsx | 14 ++++++++++- 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 app/(landing)/signup/_ui/signup-complete-message/index.tsx create mode 100644 app/(landing)/signup/_ui/signup-complete-message/styles.module.scss create mode 100644 app/(landing)/signup/complete/page.module.scss diff --git a/app/(landing)/signup/_ui/signup-complete-message/index.tsx b/app/(landing)/signup/_ui/signup-complete-message/index.tsx new file mode 100644 index 00000000..499618bb --- /dev/null +++ b/app/(landing)/signup/_ui/signup-complete-message/index.tsx @@ -0,0 +1,24 @@ +'use client' + +import Logo from '@/public/images/logo.svg' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const SignupCompleteMessage = () => { + return ( + <section className={cx('container')}> + <p>홍길동의 회원가입이 완료되었습니다.</p> + <p> + 지금 바로 인베스트메틱 + <Logo /> + 에서 <br /> + 다양한 투자 전략들을 찾아보세요! + </p> + </section> + ) +} + +export default SignupCompleteMessage diff --git a/app/(landing)/signup/_ui/signup-complete-message/styles.module.scss b/app/(landing)/signup/_ui/signup-complete-message/styles.module.scss new file mode 100644 index 00000000..0dae4ea0 --- /dev/null +++ b/app/(landing)/signup/_ui/signup-complete-message/styles.module.scss @@ -0,0 +1,9 @@ +.container { + margin: 130px 0; + @include typo-h2; + text-align: center; + + svg { + margin: 0 4px; + } +} diff --git a/app/(landing)/signup/complete/page.module.scss b/app/(landing)/signup/complete/page.module.scss new file mode 100644 index 00000000..b7784624 --- /dev/null +++ b/app/(landing)/signup/complete/page.module.scss @@ -0,0 +1,6 @@ +.button-wrapper { + display: flex; + justify-content: center; + width: 100%; + gap: 48px; +} diff --git a/app/(landing)/signup/complete/page.tsx b/app/(landing)/signup/complete/page.tsx index b12ab20e..8c805815 100644 --- a/app/(landing)/signup/complete/page.tsx +++ b/app/(landing)/signup/complete/page.tsx @@ -1,13 +1,25 @@ +import SignupCompleteMessage from '@/app/(landing)/signup/_ui/signup-complete-message' import Step from '@/app/(landing)/signup/_ui/step' +import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { LinkButton } from '@/shared/ui/link-button' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + const CompletePage = () => { return ( <> <Step /> - <LinkButton href={PATH.SIGN_UP_INFORMATION}>이전</LinkButton> + <SignupCompleteMessage /> + <div className={cx('button-wrapper')}> + <LinkButton href={PATH.SIGN_IN} variant="filled"> + 로그인하기 + </LinkButton> + <LinkButton href={PATH.HOME}>홈으로</LinkButton> + </div> </> ) } From 5692e0bc3c35a6f5abbf9df7524a8d74656a15d7 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 17 Nov 2024 21:55:11 +0900 Subject: [PATCH 142/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=99=84=EB=A3=8C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_stores/use-signup-store.ts | 2 ++ .../_ui/signup-complete-message/index.tsx | 18 +++++++++++++++--- app/(landing)/signup/complete/page.tsx | 16 +++++++++++++++- shared/styles/base/_mixins.scss | 2 +- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/app/(landing)/signup/_stores/use-signup-store.ts b/app/(landing)/signup/_stores/use-signup-store.ts index d4776f0d..2f66b433 100644 --- a/app/(landing)/signup/_stores/use-signup-store.ts +++ b/app/(landing)/signup/_stores/use-signup-store.ts @@ -4,6 +4,7 @@ import { UserType } from '@/shared/types/user' interface StateModel { userType: UserType | null + nickname: string | null } interface ActionModel { @@ -16,6 +17,7 @@ interface ActionsModel { const initialState = { userType: null, + nickname: null, } as const const useSignupStore = create<StateModel & ActionsModel>((set) => ({ diff --git a/app/(landing)/signup/_ui/signup-complete-message/index.tsx b/app/(landing)/signup/_ui/signup-complete-message/index.tsx index 499618bb..bca00890 100644 --- a/app/(landing)/signup/_ui/signup-complete-message/index.tsx +++ b/app/(landing)/signup/_ui/signup-complete-message/index.tsx @@ -3,19 +3,31 @@ import Logo from '@/public/images/logo.svg' import classNames from 'classnames/bind' +import { UserType } from '@/shared/types/user' + import styles from './styles.module.scss' const cx = classNames.bind(styles) -const SignupCompleteMessage = () => { +const COMPLETE_MESSAGE = { + investor: '다양한 투자 전략들을 찾아보세요!', + trader: '나만의 투자 전략들을 공유해보세요!', +} + +interface Props { + nickname: string + userType: UserType +} + +const SignupCompleteMessage = ({ nickname, userType }: Props) => { return ( <section className={cx('container')}> - <p>홍길동의 회원가입이 완료되었습니다.</p> + <p>{nickname}의 회원가입이 완료되었습니다.</p> <p> 지금 바로 인베스트메틱 <Logo /> 에서 <br /> - 다양한 투자 전략들을 찾아보세요! + {COMPLETE_MESSAGE[userType]} </p> </section> ) diff --git a/app/(landing)/signup/complete/page.tsx b/app/(landing)/signup/complete/page.tsx index 8c805815..3261d777 100644 --- a/app/(landing)/signup/complete/page.tsx +++ b/app/(landing)/signup/complete/page.tsx @@ -1,3 +1,8 @@ +'use client' + +import { useRouter } from 'next/navigation' + +import useSignupStore from '@/app/(landing)/signup/_stores/use-signup-store' import SignupCompleteMessage from '@/app/(landing)/signup/_ui/signup-complete-message' import Step from '@/app/(landing)/signup/_ui/step' import classNames from 'classnames/bind' @@ -10,10 +15,19 @@ import styles from './page.module.scss' const cx = classNames.bind(styles) const CompletePage = () => { + const router = useRouter() + const nickname = useSignupStore((state) => state.nickname) + const userType = useSignupStore((state) => state.userType) + + if (!nickname || !userType) { + router.push(PATH.SIGN_UP_USER_TYPE) + return + } + return ( <> <Step /> - <SignupCompleteMessage /> + <SignupCompleteMessage nickname={nickname} userType={userType} /> <div className={cx('button-wrapper')}> <LinkButton href={PATH.SIGN_IN} variant="filled"> 로그인하기 diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index 3ae51ca0..339b00a1 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -112,4 +112,4 @@ -webkit-box-orient: vertical; overflow: hidden; } -} \ No newline at end of file +} From 32029843c5a647f043053148b6bb68c0bb4f4a5c Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 17 Nov 2024 22:56:51 +0900 Subject: [PATCH 143/648] =?UTF-8?q?feat:=20=EA=B5=AC=EB=8F=85=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=82=AC=EC=9D=B4=EC=A6=88,=20fill=20colo?= =?UTF-8?q?r=EC=88=98=EC=A0=95=20(#47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/bookmark-outline.svg | 2 +- public/icons/bookmark.svg | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/icons/bookmark-outline.svg b/public/icons/bookmark-outline.svg index 72705823..7716ef71 100644 --- a/public/icons/bookmark-outline.svg +++ b/public/icons/bookmark-outline.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<svg width="30" height="30" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M11.9937 14.9316L16.6 17.2343V5.4H7.4V17.2746L11.9937 14.9316ZM6 19.5603V5C6 4.44772 6.44772 4 7 4H17C17.5523 4 18 4.44772 18 5V19.4994L12 16.5L6 19.5603Z" fill="currentColor"/> </svg> diff --git a/public/icons/bookmark.svg b/public/icons/bookmark.svg index 68b247bd..ec1655fa 100644 --- a/public/icons/bookmark.svg +++ b/public/icons/bookmark.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4C6.44772 4 6 4.44772 6 5V19.5603L12 16.5L18 19.4994V5C18 4.44772 17.5523 4 17 4H7Z" fill="currentColor"/> +<svg width="30" height="30" viewBox="0 0 24 24" fill="#ff5f33" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4C6.44772 4 6 4.44772 6 5V19.5603L12 16.5L18 19.4994V5C18 4.44772 17.5523 4 17 4H7Z" fill="#ff5f33"/> </svg> From 984da464b4a3e9a6fa0cd2c6ab30c5a7af2c1c12 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 17 Nov 2024 22:57:35 +0900 Subject: [PATCH 144/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8,=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/area-chart.tsx | 74 ++++++++++++++++++ .../strategies/_ui/strategies-item/index.tsx | 51 +++++++++++++ .../_ui/strategies-item/strategies-icon.tsx | 8 ++ .../strategies-item.stories.tsx | 75 +++++++++++++++++++ .../strategies-item/strategies-summary.tsx | 49 ++++++++++++ .../_ui/strategies-item/styles.module.scss | 64 ++++++++++++++++ .../_ui/strategies-item/subscribe.tsx | 44 +++++++++++ shared/types/strategy-details-data.ts | 23 ++++++ 8 files changed, 388 insertions(+) create mode 100644 app/(dashboard)/strategies/_ui/strategies-item/area-chart.tsx create mode 100644 app/(dashboard)/strategies/_ui/strategies-item/index.tsx create mode 100644 app/(dashboard)/strategies/_ui/strategies-item/strategies-icon.tsx create mode 100644 app/(dashboard)/strategies/_ui/strategies-item/strategies-item.stories.tsx create mode 100644 app/(dashboard)/strategies/_ui/strategies-item/strategies-summary.tsx create mode 100644 app/(dashboard)/strategies/_ui/strategies-item/styles.module.scss create mode 100644 app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx diff --git a/app/(dashboard)/strategies/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/strategies/_ui/strategies-item/area-chart.tsx new file mode 100644 index 00000000..f3e62e03 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategies-item/area-chart.tsx @@ -0,0 +1,74 @@ +'use client' + +import dynamic from 'next/dynamic' + +import Highcharts from 'highcharts' + +import { ProfitRateChartDataModel } from '@/shared/types/strategy-details-data' + +const HighchartsReact = dynamic(() => import('highcharts-react-official'), { + ssr: false, +}) +interface Props { + profitRateChartData: ProfitRateChartDataModel[] +} +const AreaChart = ({ profitRateChartData }: Props) => { + const profit = profitRateChartData.map((data) => data.profitRate) + const chartOptions: Highcharts.Options = { + chart: { + type: 'areaspline', + height: 100, + width: 180, + backgroundColor: 'transparent', + margin: [0, 0, 0, 0], + }, + title: { text: undefined }, + xAxis: { + visible: false, + }, + yAxis: { + visible: false, + min: Math.min(...profit), + max: Math.max(...profit), + }, + legend: { enabled: false }, + plotOptions: { + areaspline: { + lineWidth: 1, + lineColor: '#4d4d4d', + marker: { + enabled: false, + symbol: 'circle', + radius: 2, + states: { + hover: { + enabled: false, + }, + }, + }, + }, + }, + series: [ + { + type: 'areaspline', + data: profitRateChartData.map((data) => ({ + x: new Date(data.date).getTime(), + y: data.profitRate, + })), + color: { + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, + stops: [ + [0, '#4d4d4d'], + [1, 'rgba(255, 255, 255, 0)'], + ], + }, + }, + ], + credits: { enabled: false }, + tooltip: { enabled: false }, + } + + return <HighchartsReact highcharts={Highcharts} options={chartOptions} /> +} + +export default AreaChart diff --git a/app/(dashboard)/strategies/_ui/strategies-item/index.tsx b/app/(dashboard)/strategies/_ui/strategies-item/index.tsx new file mode 100644 index 00000000..c7c037b7 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategies-item/index.tsx @@ -0,0 +1,51 @@ +import Link from 'next/link' + +import AreaChart from '@/app/(dashboard)/strategies/_ui/strategies-item/area-chart' +import StrategiesSummary from '@/app/(dashboard)/strategies/_ui/strategies-item/strategies-summary' +import Subscribe from '@/app/(dashboard)/strategies/_ui/strategies-item/subscribe' +import classNames from 'classnames/bind' + +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + strategiesData: StrategiesModel +} +const StrategiesItem = ({ strategiesData: data }: Props) => { + return ( + <Link className={cx('container')} href={`/strategies/${data.strategyId}`}> + <StrategiesSummary + icon={[...data.stockTypeIconUrl, data.tradeTypeIconUrl]} + title={data.strategyName} + profile={{ + traderImage: data.traderImage, + nickname: data.nickname, + }} + subscriptionCount={data.subscriptionCnt} + averageRating={data.averageRating} + totalReview={data.totalReview} + /> + <AreaChart profitRateChartData={data.profitRateChartData} /> + <div className={cx('mdd')}> + <p>{data.mdd}</p> + </div> + <div className={cx('sm-score')}> + <p>{data.smScore}</p> + </div> + <div className={cx('profit')}> + <span>누적 수익률</span> + <p>{data.cumulativeProfitLossRate}%</p> + <span>최근 1년 수익률</span> + <p>{data.recentYearProfitLossRate ? data.recentYearProfitLossRate + '%' : '-'}</p> + </div> + <div className={cx('subscribe')}> + <Subscribe subscriptionStatus={data.isSubscribed} /> + </div> + </Link> + ) +} + +export default StrategiesItem diff --git a/app/(dashboard)/strategies/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/strategies/_ui/strategies-item/strategies-icon.tsx new file mode 100644 index 00000000..26cabe8d --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategies-item/strategies-icon.tsx @@ -0,0 +1,8 @@ +interface Props { + icon: string[] | React.ElementType +} +const StrategiesIcon = ({ icon }: Props) => { + return <div>[종목 & 매매 Icon]</div> +} + +export default StrategiesIcon diff --git a/app/(dashboard)/strategies/_ui/strategies-item/strategies-item.stories.tsx b/app/(dashboard)/strategies/_ui/strategies-item/strategies-item.stories.tsx new file mode 100644 index 00000000..052ac035 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategies-item/strategies-item.stories.tsx @@ -0,0 +1,75 @@ +import StrategiesItem from '@/app/(dashboard)/strategies/_ui/strategies-item' +import type { Meta, StoryFn } from '@storybook/react' + +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +const meta: Meta<typeof StrategiesItem> = { + title: 'components/StrategiesItem', + component: StrategiesItem, + tags: ['autodocs'], +} + +const strategy: StoryFn<{ strategiesData: StrategiesModel[] }> = ({ strategiesData }) => ( + <> + {strategiesData.map((strategy) => ( + <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> + ))} + </> +) + +export const Primary = strategy.bind({}) +Primary.args = { + strategiesData: [ + { + strategyId: '123', + strategyName: '리치테크 FuturesDay', + nickname: 'MACS', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 5.2 }, + { date: '2024-02-01', profitRate: 6.4 }, + { date: '2024-03-01', profitRate: 12.8 }, + { date: '2024-04-01', profitRate: 8.2 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + ], + tradeTypeIconUrl: '', + mdd: '-20,580,856', + smScore: 60.6, + cumulativeProfitLossRate: 120.1, + recentYearProfitLossRate: 30.1, + subscriptionCnt: 23, + isSubscribed: true, + averageRating: 4.8, + totalReview: 12, + }, + { + strategyId: '12345', + strategyName: 'ETF 레버리지/인버', + nickname: '수밍', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2023-12-01', profitRate: 7.2 }, + { date: '2024-01-01', profitRate: 5.2 }, + { date: '2024-02-01', profitRate: 25 }, + { date: '2024-03-01', profitRate: 12.8 }, + { date: '2024-04-01', profitRate: 17.2 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-20,580,856', + smScore: 60.6, + cumulativeProfitLossRate: 120.1, + recentYearProfitLossRate: 30.1, + subscriptionCnt: 23, + isSubscribed: false, + averageRating: 4.8, + totalReview: 12, + }, + ], +} + +export default meta diff --git a/app/(dashboard)/strategies/_ui/strategies-item/strategies-summary.tsx b/app/(dashboard)/strategies/_ui/strategies-item/strategies-summary.tsx new file mode 100644 index 00000000..e931fdde --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategies-item/strategies-summary.tsx @@ -0,0 +1,49 @@ +import StrategiesIcon from '@/app/(dashboard)/strategies/_ui/strategies-item/strategies-icon' +import classNames from 'classnames/bind' + +import Avatar from '@/shared/ui/avatar' +import TotalStar from '@/shared/ui/total-star' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface ProfileModel { + traderImage?: string + nickname: string +} + +interface Props { + icon: string[] + title: string + profile: ProfileModel + subscriptionCount: number + averageRating: number + totalReview: number +} +const StrategiesSummary = ({ + icon, + title, + profile, + subscriptionCount, + averageRating, + totalReview, +}: Props) => { + return ( + <div className={cx('summary')}> + <StrategiesIcon icon={icon} /> + <p className={cx('title')}>{title}</p> + <div className={cx('trader-profile')}> + <Avatar size={'medium'} src={profile.traderImage} /> + <p>{profile.nickname}</p> + </div> + <div className={cx('total-subscribe-star')}> + <p>구독 {subscriptionCount}개</p> + <p>|</p> + <TotalStar averageRating={averageRating} totalElements={totalReview} textColor="black" /> + </div> + </div> + ) +} + +export default StrategiesSummary diff --git a/app/(dashboard)/strategies/_ui/strategies-item/styles.module.scss b/app/(dashboard)/strategies/_ui/strategies-item/styles.module.scss new file mode 100644 index 00000000..da420b5e --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategies-item/styles.module.scss @@ -0,0 +1,64 @@ +.container { + margin-bottom: 24px; + display: grid; + grid-template-columns: 1.6fr 1.2fr repeat(3, 0.6fr) 0.4fr; + height: 158px; + width: 100%; + align-items: center; + background-color: $color-white; + .mdd, + .sm-score, + .profit, + .subscribe { + place-items: center center; + } + .mdd, + .sm-score { + @include typo-b2; + } + .profit { + & span { + @include typo-c2; + } + p { + @include typo-b2; + color: $color-orange-500; + } + } +} + +.summary { + padding-left: 30px; + * { + margin: 4px 0; + } + .title { + @include typo-h4; + } + .trader-profile { + display: flex; + align-items: center; + p { + margin-left: 10px; + @include typo-b2; + } + } + .total-subscribe-star { + display: flex; + align-items: center; + height: 20px; + p { + @include typo-c2; + padding-top: 10px; + &:first-child { + margin-right: 6px; + } + } + } +} + +.subscribe-icon { + button { + background: transparent; + } +} diff --git a/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx b/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx new file mode 100644 index 00000000..8350b2ce --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx @@ -0,0 +1,44 @@ +'use client' + +import { useEffect, useState } from 'react' + +import { BookmarkIcon, BookmarkOutlineIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + subscriptionStatus: boolean +} +const Subscribe = ({ subscriptionStatus }: Props) => { + const [isSubscribed, setIsSubscribed] = useState(false) + + useEffect(() => { + if (subscriptionStatus) { + setIsSubscribed(true) + } + }, []) + + const handleSubscribe = (event: React.MouseEvent) => { + event.preventDefault() + setIsSubscribed(!isSubscribed) + } + + return ( + <div className={cx('subscribe-icon')}> + {isSubscribed ? ( + <button onClick={handleSubscribe}> + <BookmarkIcon /> + </button> + ) : ( + <button onClick={handleSubscribe}> + <BookmarkOutlineIcon /> + </button> + )} + </div> + ) +} + +export default Subscribe diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts index dd8b001b..8a59fef0 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-details-data.ts @@ -18,3 +18,26 @@ export interface MonthlyAnalysisModel { cumulativeProfitLoss: number cumulativeProfitLossRate: number } + +export interface ProfitRateChartDataModel { + date: string + profitRate: number +} + +export interface StrategiesModel { + strategyId: string + strategyName: string + nickname: string + traderImage?: string + stockTypeIconUrl: string[] + profitRateChartData: ProfitRateChartDataModel[] + tradeTypeIconUrl: string + mdd: string + smScore: number + cumulativeProfitLossRate: number + recentYearProfitLossRate?: number + subscriptionCnt: number + isSubscribed: boolean + averageRating: number + totalReview: number +} From 24b3b696b792f37ad4d865150dae001802faaf3c Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 09:29:58 +0900 Subject: [PATCH 145/648] =?UTF-8?q?feat:=20Modal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 1 + shared/ui/modal/index.tsx | 60 ++++++++++++++++++++++++++++++ shared/ui/modal/styles.module.scss | 57 ++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 shared/ui/modal/index.tsx create mode 100644 shared/ui/modal/styles.module.scss diff --git a/app/layout.tsx b/app/layout.tsx index 922c9aac..62afa32c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -18,6 +18,7 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { <MSWInitializer /> {children} </QueryProvider> + <div id="modal-root"></div> </body> </html> ) diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx new file mode 100644 index 00000000..c645bf13 --- /dev/null +++ b/shared/ui/modal/index.tsx @@ -0,0 +1,60 @@ +'use client' + +import { ReactNode } from 'react' + +import classNames from 'classnames/bind' +import { createPortal } from 'react-dom' + +import { Button } from '../button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + title?: string + icon?: ReactNode + isModalOpen: boolean + hasTwoButtons?: boolean + confirmModal?: () => void + closeModal: () => void +} + +const Modal = ({ + title, + icon, + isModalOpen, + hasTwoButtons = false, + // confirmModal, + closeModal, +}: Props) => { + if (!isModalOpen) return null + + const modalRoot = document.getElementById('modal-root') + + if (!modalRoot) return null + + return createPortal( + <> + <div className={cx('overlay')}>오버레이</div> + <div className={cx('modal')}> + {/* <div onClick={closeModal}>X</div> */} + <div className={cx('icon')}>{icon}</div> + <p className={cx('title')}>{title}</p> + + {hasTwoButtons ? ( + <div className={cx('two-buttons')}> + {/* <Button onClick={confirmModal}>예 </Button> */} + <Button onClick={closeModal}>아니오</Button> + </div> + ) : ( + <Button className={cx('close-button')} onClick={closeModal}> + 닫기 + </Button> + )} + </div> + </>, + modalRoot + ) +} + +export default Modal diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss new file mode 100644 index 00000000..a9798201 --- /dev/null +++ b/shared/ui/modal/styles.module.scss @@ -0,0 +1,57 @@ +.overlay { + position: fixed; + width: 100%; + height: 100%; + background-color: $color-black; + opacity: 0.2; + z-index: 2; +} + +.modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: $color-gray-100; + border: 1px solid $color-gray-200; + border-radius: 8px; + z-index: 3; + padding: 20px; + width: 360px; + height: 280px; +} + +.close-icon { + position: absolute; + top: 10px; + right: 10px; + font-size: 20px; + cursor: pointer; +} + +.icon { + margin-bottom: 10px; + display: flex; + justify-content: center; + align-items: center; +} + +.title { + margin-bottom: 20px; + font-size: 18px; + font-weight: bold; + text-align: center; +} + +.two-buttons { + display: flex; + justify-content: space-between; + width: 100%; + gap: 10px; +} + +.close-button { + margin-top: 20px; + padding: 10px 20px; + cursor: pointer; +} From e8eb0e0681f81bb7e26e21fc04f3ebf750800bac Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 09:44:00 +0900 Subject: [PATCH 146/648] =?UTF-8?q?feat:=20constants=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EC=97=90=20Modal=20=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/modal-titles.ts | 6 ++++++ shared/ui/modal/index.tsx | 4 +++- shared/ui/modal/styles.module.scss | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 shared/constants/modal-titles.ts diff --git a/shared/constants/modal-titles.ts b/shared/constants/modal-titles.ts new file mode 100644 index 00000000..22ea04a4 --- /dev/null +++ b/shared/constants/modal-titles.ts @@ -0,0 +1,6 @@ +export const MODAL_TITLES = { + SUBSCRIBE: '전략을 구독합니다. \n 구독한 전략은 나의 관심전략 \n 페이지에서 확인 가능합니다.', + VERIFICATIONCODE: '인증코드가 이메일로 발송되었습니다.\n 메일을 확인해주세요.', + LOGIN: '로그인이 필요합니다.\n 로그인하시겠습니까?', + DELETE: '리뷰를 삭제하겠습니까?', +} diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index c645bf13..b180f330 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -5,6 +5,8 @@ import { ReactNode } from 'react' import classNames from 'classnames/bind' import { createPortal } from 'react-dom' +import { MODAL_TITLES } from '@/shared/constants/modal-titles' + import { Button } from '../button' import styles from './styles.module.scss' @@ -20,7 +22,7 @@ interface Props { } const Modal = ({ - title, + title = MODAL_TITLES.SUBSCRIBE, icon, isModalOpen, hasTwoButtons = false, diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index a9798201..fe46648e 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -41,6 +41,7 @@ font-size: 18px; font-weight: bold; text-align: center; + white-space: pre-line; } .two-buttons { From 8a7539f90ea313c666fc97b90681758a36088d81 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 10:25:19 +0900 Subject: [PATCH 147/648] =?UTF-8?q?chore:=20Modal=EC=97=90=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EB=8A=94=20SVG=20=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/close-icon.svg | 4 ++++ public/icons/index.tsx | 4 ++++ public/icons/modal-alert.svg | 13 +++++++++++++ public/icons/modal-check.svg | 14 ++++++++++++++ public/icons/modal-subscribe.svg | 14 ++++++++++++++ shared/styles/stories/icons.stories.tsx | 8 ++++++++ 6 files changed, 57 insertions(+) create mode 100644 public/icons/close-icon.svg create mode 100644 public/icons/modal-alert.svg create mode 100644 public/icons/modal-check.svg create mode 100644 public/icons/modal-subscribe.svg diff --git a/public/icons/close-icon.svg b/public/icons/close-icon.svg new file mode 100644 index 00000000..08036833 --- /dev/null +++ b/public/icons/close-icon.svg @@ -0,0 +1,4 @@ +<svg width="25" height="25" viewBox="0 0 25 25" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6433 20.7327L3.8125 5.73511L5.18799 4.2832L21.0187 19.2808L19.6433 20.7327Z" fill="#797979"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M5.35674 20.7327L21.1875 5.73511L19.812 4.2832L3.98125 19.2808L5.35674 20.7327Z" fill="#797979"/> +</svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 9b314739..17294274 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -25,3 +25,7 @@ export { default as StrategyRankingIcon } from './strategy-ranking.svg' export { default as StrategyIcon } from './strategy.svg' export { default as TradersIcon } from './traders.svg' export { default as StarIcon } from './star.svg' +export { default as CloseIcon } from './close-icon.svg' +export { default as ModalAlertIcon } from './modal-alert.svg' +export { default as ModalSubscribeIcon } from './modal-subscribe.svg' +export { default as ModalCheckIcon } from './modal-check.svg' diff --git a/public/icons/modal-alert.svg b/public/icons/modal-alert.svg new file mode 100644 index 00000000..a4790be8 --- /dev/null +++ b/public/icons/modal-alert.svg @@ -0,0 +1,13 @@ +<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M32.9344 20.4145C32.9344 27.3707 27.2906 33.0145 20.3344 33.0145C13.3781 33.0145 7.73438 27.3707 7.73438 20.4145C7.73438 13.4582 13.3781 7.81445 20.3344 7.81445C27.2906 7.81445 32.9344 13.4582 32.9344 20.4145ZM18.2344 13.4145L18.9344 21.8145H21.7344L22.4344 13.4145H18.2344ZM18.9344 27.4145V24.6145H21.7344V27.4145H18.9344Z" fill="#FF5F33"/> +<g filter="url(#filter0_f_1401_4699)"> +<circle cx="20.332" cy="20.4141" r="13.5" stroke="#FF5F33"/> +</g> +<defs> +<filter id="filter0_f_1401_4699" x="2.33203" y="2.41406" width="36" height="36" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> +<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_1401_4699"/> +</filter> +</defs> +</svg> diff --git a/public/icons/modal-check.svg b/public/icons/modal-check.svg new file mode 100644 index 00000000..6be252cf --- /dev/null +++ b/public/icons/modal-check.svg @@ -0,0 +1,14 @@ +<svg width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M32.5984 20.4145C32.5984 27.3707 26.9547 33.0145 19.9984 33.0145C13.0422 33.0145 7.39844 27.3707 7.39844 20.4145C7.39844 13.4582 13.0422 7.81445 19.9984 7.81445C26.9547 7.81445 32.5984 13.4582 32.5984 20.4145Z" fill="#6877FF"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M28.2431 15.8004L17.9439 26.5901L12.6836 21.3298L13.9833 20.0301L17.9133 23.9601L26.9135 14.5312L28.2431 15.8004Z" fill="white"/> +<g filter="url(#filter0_f_1401_4703)"> +<circle cx="20" cy="20.4141" r="13.5" stroke="#6877FF"/> +</g> +<defs> +<filter id="filter0_f_1401_4703" x="2" y="2.41406" width="36" height="36" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> +<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_1401_4703"/> +</filter> +</defs> +</svg> diff --git a/public/icons/modal-subscribe.svg b/public/icons/modal-subscribe.svg new file mode 100644 index 00000000..4fe28229 --- /dev/null +++ b/public/icons/modal-subscribe.svg @@ -0,0 +1,14 @@ +<svg width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M32.5984 20.4145C32.5984 27.3707 26.9547 33.0145 19.9984 33.0145C13.0422 33.0145 7.39844 27.3707 7.39844 20.4145C7.39844 13.4582 13.0422 7.81445 19.9984 7.81445C26.9547 7.81445 32.5984 13.4582 32.5984 20.4145Z" fill="#6877FF"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M16.543 15.002C15.9907 15.002 15.543 15.4497 15.543 16.002V26.56L19.9982 24.2877L24.4568 26.5165V16.002C24.4568 15.4497 24.0091 15.002 23.4568 15.002H16.543Z" fill="white"/> +<g filter="url(#filter0_f_1401_9601)"> +<circle cx="20" cy="20.4141" r="13.5" stroke="#6877FF"/> +</g> +<defs> +<filter id="filter0_f_1401_9601" x="2" y="2.41406" width="36" height="36" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> +<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_1401_9601"/> +</filter> +</defs> +</svg> diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index 8e9d5a93..8b9ebdb1 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -10,8 +10,12 @@ import { ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, + CloseIcon, DailyGraphIcon, FileIcon, + ModalAlertIcon, + ModalCheckIcon, + ModalSubscribeIcon, MoneyIcon, MonthlyGraphIcon, NoticeIcon, @@ -61,6 +65,10 @@ const icons = [ { name: 'StrategyIcon', icon: StrategyIcon }, { name: 'TradersIcon', icon: TradersIcon }, { name: 'StarIcon', icon: StarIcon }, + { name: 'CloseIcon', icon: CloseIcon }, + { name: 'ModalAlertIcon', icon: ModalAlertIcon }, + { name: 'ModalSubscribeIcon', icon: ModalSubscribeIcon }, + { name: 'ModalCheckIcon', icon: ModalCheckIcon }, ] export const Icons: Story = { From 165f3896871478f8a9f1f6aaac322fbeea9a99a2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 18 Nov 2024 10:52:40 +0900 Subject: [PATCH 148/648] =?UTF-8?q?feat:=20=EA=B5=AC=EB=8F=85=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=A1=B0=EA=B1=B4=EB=B6=80=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20(#4?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/subscribe.tsx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx b/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx index 8350b2ce..183595ac 100644 --- a/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx +++ b/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx @@ -12,6 +12,7 @@ const cx = classNames.bind(styles) interface Props { subscriptionStatus: boolean } + const Subscribe = ({ subscriptionStatus }: Props) => { const [isSubscribed, setIsSubscribed] = useState(false) @@ -19,24 +20,18 @@ const Subscribe = ({ subscriptionStatus }: Props) => { if (subscriptionStatus) { setIsSubscribed(true) } - }, []) + }, [subscriptionStatus]) - const handleSubscribe = (event: React.MouseEvent) => { - event.preventDefault() + const handleSubscribe = (e: React.MouseEvent) => { + e.preventDefault() setIsSubscribed(!isSubscribed) } return ( <div className={cx('subscribe-icon')}> - {isSubscribed ? ( - <button onClick={handleSubscribe}> - <BookmarkIcon /> - </button> - ) : ( - <button onClick={handleSubscribe}> - <BookmarkOutlineIcon /> - </button> - )} + <button onClick={handleSubscribe}> + {isSubscribed ? <BookmarkIcon /> : <BookmarkOutlineIcon />} + </button> </div> ) } From a7c28fa8b3c1dfa9f17dd216b0336600dd178cf4 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 18 Nov 2024 11:01:39 +0900 Subject: [PATCH 149/648] =?UTF-8?q?rename:=20strategies=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EC=97=90=EC=84=9C=20(dashboard)/=5Fui=20=ED=95=98=EC=9C=84?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{strategies => }/_ui/strategies-item/area-chart.tsx | 1 + .../{strategies => }/_ui/strategies-item/index.tsx | 7 ++++--- .../_ui/strategies-item/strategies-icon.tsx | 1 + .../_ui/strategies-item/strategies-item.stories.tsx | 2 +- .../_ui/strategies-item/strategies-summary.tsx | 3 ++- .../_ui/strategies-item/styles.module.scss | 4 ++-- .../{strategies => }/_ui/strategies-item/subscribe.tsx | 0 7 files changed, 11 insertions(+), 7 deletions(-) rename app/(dashboard)/{strategies => }/_ui/strategies-item/area-chart.tsx (99%) rename app/(dashboard)/{strategies => }/_ui/strategies-item/index.tsx (84%) rename app/(dashboard)/{strategies => }/_ui/strategies-item/strategies-icon.tsx (99%) rename app/(dashboard)/{strategies => }/_ui/strategies-item/strategies-item.stories.tsx (96%) rename app/(dashboard)/{strategies => }/_ui/strategies-item/strategies-summary.tsx (92%) rename app/(dashboard)/{strategies => }/_ui/strategies-item/styles.module.scss (95%) rename app/(dashboard)/{strategies => }/_ui/strategies-item/subscribe.tsx (100%) diff --git a/app/(dashboard)/strategies/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/_ui/strategies-item/area-chart.tsx similarity index 99% rename from app/(dashboard)/strategies/_ui/strategies-item/area-chart.tsx rename to app/(dashboard)/_ui/strategies-item/area-chart.tsx index f3e62e03..0bd8134d 100644 --- a/app/(dashboard)/strategies/_ui/strategies-item/area-chart.tsx +++ b/app/(dashboard)/_ui/strategies-item/area-chart.tsx @@ -12,6 +12,7 @@ const HighchartsReact = dynamic(() => import('highcharts-react-official'), { interface Props { profitRateChartData: ProfitRateChartDataModel[] } + const AreaChart = ({ profitRateChartData }: Props) => { const profit = profitRateChartData.map((data) => data.profitRate) const chartOptions: Highcharts.Options = { diff --git a/app/(dashboard)/strategies/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx similarity index 84% rename from app/(dashboard)/strategies/_ui/strategies-item/index.tsx rename to app/(dashboard)/_ui/strategies-item/index.tsx index c7c037b7..09a00900 100644 --- a/app/(dashboard)/strategies/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -1,8 +1,8 @@ import Link from 'next/link' -import AreaChart from '@/app/(dashboard)/strategies/_ui/strategies-item/area-chart' -import StrategiesSummary from '@/app/(dashboard)/strategies/_ui/strategies-item/strategies-summary' -import Subscribe from '@/app/(dashboard)/strategies/_ui/strategies-item/subscribe' +import AreaChart from '@/app/(dashboard)/_ui/strategies-item/area-chart' +import StrategiesSummary from '@/app/(dashboard)/_ui/strategies-item/strategies-summary' +import Subscribe from '@/app/(dashboard)/_ui/strategies-item/subscribe' import classNames from 'classnames/bind' import { StrategiesModel } from '@/shared/types/strategy-details-data' @@ -14,6 +14,7 @@ const cx = classNames.bind(styles) interface Props { strategiesData: StrategiesModel } + const StrategiesItem = ({ strategiesData: data }: Props) => { return ( <Link className={cx('container')} href={`/strategies/${data.strategyId}`}> diff --git a/app/(dashboard)/strategies/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx similarity index 99% rename from app/(dashboard)/strategies/_ui/strategies-item/strategies-icon.tsx rename to app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 26cabe8d..383993d3 100644 --- a/app/(dashboard)/strategies/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -1,6 +1,7 @@ interface Props { icon: string[] | React.ElementType } + const StrategiesIcon = ({ icon }: Props) => { return <div>[종목 & 매매 Icon]</div> } diff --git a/app/(dashboard)/strategies/_ui/strategies-item/strategies-item.stories.tsx b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx similarity index 96% rename from app/(dashboard)/strategies/_ui/strategies-item/strategies-item.stories.tsx rename to app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx index 052ac035..fc65ad81 100644 --- a/app/(dashboard)/strategies/_ui/strategies-item/strategies-item.stories.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx @@ -1,4 +1,4 @@ -import StrategiesItem from '@/app/(dashboard)/strategies/_ui/strategies-item' +import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import type { Meta, StoryFn } from '@storybook/react' import { StrategiesModel } from '@/shared/types/strategy-details-data' diff --git a/app/(dashboard)/strategies/_ui/strategies-item/strategies-summary.tsx b/app/(dashboard)/_ui/strategies-item/strategies-summary.tsx similarity index 92% rename from app/(dashboard)/strategies/_ui/strategies-item/strategies-summary.tsx rename to app/(dashboard)/_ui/strategies-item/strategies-summary.tsx index e931fdde..18389d84 100644 --- a/app/(dashboard)/strategies/_ui/strategies-item/strategies-summary.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-summary.tsx @@ -1,4 +1,4 @@ -import StrategiesIcon from '@/app/(dashboard)/strategies/_ui/strategies-item/strategies-icon' +import StrategiesIcon from '@/app/(dashboard)/_ui/strategies-item/strategies-icon' import classNames from 'classnames/bind' import Avatar from '@/shared/ui/avatar' @@ -21,6 +21,7 @@ interface Props { averageRating: number totalReview: number } + const StrategiesSummary = ({ icon, title, diff --git a/app/(dashboard)/strategies/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss similarity index 95% rename from app/(dashboard)/strategies/_ui/strategies-item/styles.module.scss rename to app/(dashboard)/_ui/strategies-item/styles.module.scss index da420b5e..d36ed422 100644 --- a/app/(dashboard)/strategies/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -18,7 +18,7 @@ } .profit { & span { - @include typo-c2; + @include typo-c1; } p { @include typo-b2; @@ -48,7 +48,7 @@ align-items: center; height: 20px; p { - @include typo-c2; + @include typo-c1; padding-top: 10px; &:first-child { margin-right: 6px; diff --git a/app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx b/app/(dashboard)/_ui/strategies-item/subscribe.tsx similarity index 100% rename from app/(dashboard)/strategies/_ui/strategies-item/subscribe.tsx rename to app/(dashboard)/_ui/strategies-item/subscribe.tsx From ae0d490e5bc3151ebd6d54a1bfd139b482b2c3eb Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 18 Nov 2024 11:51:25 +0900 Subject: [PATCH 150/648] =?UTF-8?q?feat:=20=EC=9B=90=ED=98=95=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/circle.svg | 3 +++ public/icons/index.tsx | 1 + 2 files changed, 4 insertions(+) create mode 100644 public/icons/circle.svg diff --git a/public/icons/circle.svg b/public/icons/circle.svg new file mode 100644 index 00000000..d7d31c87 --- /dev/null +++ b/public/icons/circle.svg @@ -0,0 +1,3 @@ +<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle id="Ellipse 27" cx="6" cy="6.30664" r="5.5" stroke="#1D1D1D"/> +</svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 9b314739..8d8b637c 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -25,3 +25,4 @@ export { default as StrategyRankingIcon } from './strategy-ranking.svg' export { default as StrategyIcon } from './strategy.svg' export { default as TradersIcon } from './traders.svg' export { default as StarIcon } from './star.svg' +export { default as CircleIcon } from './circle.svg' From d8eeae778651f22647472036a50ce87f6470178d Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 18 Nov 2024 11:56:56 +0900 Subject: [PATCH 151/648] =?UTF-8?q?feat:=20=EC=B2=B4=ED=81=AC=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 1 - shared/styles/base/_mixins.scss | 2 +- shared/ui/check-box/check-box.stories.tsx | 34 +++++++++++++++ shared/ui/check-box/index.tsx | 39 +++++++++++++++++ shared/ui/check-box/styles.module.scss | 53 +++++++++++++++++++++++ 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 shared/ui/check-box/check-box.stories.tsx create mode 100644 shared/ui/check-box/index.tsx create mode 100644 shared/ui/check-box/styles.module.scss diff --git a/.eslintrc.js b/.eslintrc.js index 89b5764e..7999be84 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,7 +15,6 @@ module.exports = { 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react/recommended', - 'plugin:jsx-a11y/recommended', 'plugin:prettier/recommended', 'next/core-web-vitals', 'next/typescript', diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index 3ae51ca0..339b00a1 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -112,4 +112,4 @@ -webkit-box-orient: vertical; overflow: hidden; } -} \ No newline at end of file +} diff --git a/shared/ui/check-box/check-box.stories.tsx b/shared/ui/check-box/check-box.stories.tsx new file mode 100644 index 00000000..20e5c537 --- /dev/null +++ b/shared/ui/check-box/check-box.stories.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react' + +import type { Meta, StoryObj } from '@storybook/react' + +import Checkbox from './index' + +const meta = { + title: 'Components/Checkbox', + component: Checkbox, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta<typeof Checkbox> + +export default meta +type StoryType = StoryObj<typeof Checkbox> + +const CheckboxWithHook = ({ ...props }) => { + const [isChecked, setIsChecked] = useState(false) + return <Checkbox isChecked={isChecked} onChange={setIsChecked} {...props} /> +} + +export const Gray500: StoryType = { + render: () => <CheckboxWithHook label="로그인 유지" textColor="gray500" />, +} + +export const Gray600: StoryType = { + render: () => <CheckboxWithHook label="로그인 유지" textColor="gray600" />, +} + +export const Gray800: StoryType = { + render: () => <CheckboxWithHook label="동의합니다" textColor="gray800" />, +} diff --git a/shared/ui/check-box/index.tsx b/shared/ui/check-box/index.tsx new file mode 100644 index 00000000..920c29ff --- /dev/null +++ b/shared/ui/check-box/index.tsx @@ -0,0 +1,39 @@ +'use client' + +import { CircleIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + label?: string + isChecked: boolean + onChange: (checked: boolean) => void + className?: string + textColor?: 'gray500' | 'gray600' | 'gray800' +} + +const Checkbox = ({ label, isChecked, onChange, className = '', textColor = 'gray500' }: Props) => { + const handleClick = () => { + onChange(!isChecked) + } + + return ( + <div className={cx('checkboxContainer', className)} onClick={handleClick}> + <div + className={cx('checkbox', { + isChecked, + [`checked${textColor}`]: isChecked, + })} + > + <CircleIcon /> + {isChecked && <div className={cx('innerCircle', textColor)} />} + </div> + {label && <span className={cx('label', textColor)}>{label}</span>} + </div> + ) +} + +export default Checkbox diff --git a/shared/ui/check-box/styles.module.scss b/shared/ui/check-box/styles.module.scss new file mode 100644 index 00000000..5c6efb6e --- /dev/null +++ b/shared/ui/check-box/styles.module.scss @@ -0,0 +1,53 @@ +.checkboxContainer { + display: inline-flex; + align-items: center; + gap: 8px; + cursor: pointer; +} + +.checkbox { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + position: relative; +} + +.innerCircle { + position: absolute; + width: 8px; + height: 8px; + border-radius: 50%; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + &.gray500 { + background-color: $color-gray-500; + } + + &.gray600 { + background-color: $color-gray-600; + } + + &.gray800 { + background-color: $color-gray-800; + } +} + +.label { + font-size: $text-c1; + font-weight: $text-medium; + + &.gray500 { + color: $color-gray-500; + } + + &.gray600 { + color: $color-gray-600; + } + + &.gray800 { + color: $color-gray-800; + } +} From 213b4c0d9cbf193cfefac1fec0c6423574f789f9 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 12:16:20 +0900 Subject: [PATCH 152/648] =?UTF-8?q?design:=20Modal=20SCSS=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20modal-root=20=EC=88=98=EC=A0=95=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 2 +- public/mockServiceWorker.js | 9 ++++++--- shared/ui/modal/index.tsx | 17 +++++++++------- shared/ui/modal/styles.module.scss | 32 ++++++++++++++++++++---------- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 62afa32c..ad62da4b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -14,11 +14,11 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { return ( <html lang="ko" className={pretendard.variable}> <body> + <div id="modal-root"></div> <QueryProvider> <MSWInitializer /> {children} </QueryProvider> - <div id="modal-root"></div> </body> </html> ) diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index 742f3083..8241ef8c 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -145,7 +145,7 @@ async function handleRequest(event, requestId) { headers: Object.fromEntries(responseClone.headers.entries()), }, }, - [responseClone.body] + [responseClone.body], ) })() } @@ -240,7 +240,7 @@ async function getResponse(event, client, requestId) { keepalive: request.keepalive, }, }, - [requestBuffer] + [requestBuffer], ) switch (clientMessage.type) { @@ -268,7 +268,10 @@ function sendToClient(client, message, transferrables = []) { resolve(event.data) } - client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean))) + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)), + ) }) } diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index b180f330..f054d75a 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -2,6 +2,7 @@ import { ReactNode } from 'react' +import { CloseIcon } from '@/public/icons' import classNames from 'classnames/bind' import { createPortal } from 'react-dom' @@ -17,16 +18,16 @@ interface Props { icon?: ReactNode isModalOpen: boolean hasTwoButtons?: boolean - confirmModal?: () => void + confirmButton: () => void closeModal: () => void } const Modal = ({ - title = MODAL_TITLES.SUBSCRIBE, + title = MODAL_TITLES.LOGIN, icon, isModalOpen, hasTwoButtons = false, - // confirmModal, + confirmButton, closeModal, }: Props) => { if (!isModalOpen) return null @@ -37,15 +38,17 @@ const Modal = ({ return createPortal( <> - <div className={cx('overlay')}>오버레이</div> - <div className={cx('modal')}> - {/* <div onClick={closeModal}>X</div> */} + <div className={cx('overlay')}></div> + <div className={cx('modal', { 'has-two-buttons': hasTwoButtons })}> + <CloseIcon className={cx('close-icon')} onClick={closeModal}></CloseIcon> <div className={cx('icon')}>{icon}</div> <p className={cx('title')}>{title}</p> {hasTwoButtons ? ( <div className={cx('two-buttons')}> - {/* <Button onClick={confirmModal}>예 </Button> */} + <Button className={cx('confirm-button')} onClick={confirmButton}> + 예 + </Button> <Button onClick={closeModal}>아니오</Button> </div> ) : ( diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index fe46648e..bc63f582 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -19,40 +19,52 @@ padding: 20px; width: 360px; height: 280px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + + &.has-two-buttons { + height: 240px; + } } .close-icon { position: absolute; - top: 10px; - right: 10px; - font-size: 20px; + top: 12px; + right: 12px; cursor: pointer; } .icon { - margin-bottom: 10px; display: flex; justify-content: center; align-items: center; + margin-top: 20px; } .title { - margin-bottom: 20px; - font-size: 18px; + @include typo-b1; font-weight: bold; text-align: center; white-space: pre-line; + color: $color-gray-800; } .two-buttons { display: flex; - justify-content: space-between; + justify-content: center; width: 100%; - gap: 10px; + gap: 18px; +} +.confirm-button { + border-color: $color-orange-500; + color: $color-orange-500; + cursor: pointer; + width: 90px; } .close-button { - margin-top: 20px; - padding: 10px 20px; cursor: pointer; + align-self: center; } From 63a142c6af6aa9191b1a8ae8c5b32a946cd9fee1 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 12:22:58 +0900 Subject: [PATCH 153/648] =?UTF-8?q?refactor:=20Modal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index f054d75a..b72762ca 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -6,8 +6,6 @@ import { CloseIcon } from '@/public/icons' import classNames from 'classnames/bind' import { createPortal } from 'react-dom' -import { MODAL_TITLES } from '@/shared/constants/modal-titles' - import { Button } from '../button' import styles from './styles.module.scss' @@ -23,7 +21,7 @@ interface Props { } const Modal = ({ - title = MODAL_TITLES.LOGIN, + title, icon, isModalOpen, hasTwoButtons = false, From 53857b0521960c00636cfca1bbc248d2f618e887 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 13:15:10 +0900 Subject: [PATCH 154/648] =?UTF-8?q?feat:=20Modal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/modal.stories.tsx | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 shared/ui/modal/modal.stories.tsx diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx new file mode 100644 index 00000000..c9182d0e --- /dev/null +++ b/shared/ui/modal/modal.stories.tsx @@ -0,0 +1,43 @@ +import { Meta, StoryObj } from '@storybook/react' +import { ModalAlertIcon } from 'public/icons/index' + +import Modal from './index' + +const meta: Meta<typeof Modal> = { + title: 'Components/Modal', + component: Modal, + args: { + title: '기본 모달 제목', + icon: <ModalAlertIcon />, + isModalOpen: true, + hasTwoButtons: false, + confirmButton: () => alert('확인 버튼 클릭'), + closeModal: () => alert('닫기 버튼 클릭'), + }, + argTypes: { + title: { control: 'text' }, + isModalOpen: { control: 'boolean' }, + hasTwoButtons: { control: 'boolean' }, + confirmButton: { action: 'confirmButton' }, + closeModal: { action: 'closeModal' }, + }, + tags: ['autodocs'], +} + +type StoryType = StoryObj<typeof Modal> + +export const Default: StoryType = {} + +export const TwoButtons: StoryType = { + args: { + hasTwoButtons: true, + }, +} + +export const WithCustomIcon: StoryType = { + args: { + icon: <ModalAlertIcon />, + }, +} + +export default meta From ddb466abe33755786468138b8ca5504d88f28bcb Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 18 Nov 2024 13:20:21 +0900 Subject: [PATCH 155/648] =?UTF-8?q?fix:=20Step=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/step/step-item.tsx | 14 +++++++------- app/(landing)/signup/_ui/step/styles.module.scss | 5 +++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/(landing)/signup/_ui/step/step-item.tsx b/app/(landing)/signup/_ui/step/step-item.tsx index 2ba80a0d..0ba6b29d 100644 --- a/app/(landing)/signup/_ui/step/step-item.tsx +++ b/app/(landing)/signup/_ui/step/step-item.tsx @@ -23,11 +23,12 @@ interface Props { } const StepItem = ({ children, step, pathname, icon: Icon, prevStep }: Props) => { + const currentPath = usePathname() + const currentPathIdx = STEPS.findIndex((step) => step.path === currentPath) const stepHistory = useStepHistoryStore((state) => state.stepHistory) const [isNextStep, setIsNextStep] = useState(false) const [isPrevStep, setIsPrev] = useState(false) - const [isFix, setIsFix] = useState(false) - const currentPath = usePathname() + const [isFixedStep, setIsFixedStep] = useState(() => currentPathIdx - step >= 0) useEffect(() => { if (currentPathIdx - (prevStep - 1) > 0) { @@ -37,14 +38,13 @@ const StepItem = ({ children, step, pathname, icon: Icon, prevStep }: Props) => } }, [currentPath]) - const currentPathIdx = STEPS.findIndex((step) => step.path === currentPath) const stepIdx = step - 1 const handleNextStep = () => { if (currentPathIdx > stepIdx) { setIsNextStep(true) if (currentPathIdx - step > 0) { - setIsFix(true) + setIsFixedStep(true) } } } @@ -55,7 +55,7 @@ const StepItem = ({ children, step, pathname, icon: Icon, prevStep }: Props) => } if (currentPathIdx - stepIdx > 0) { setIsNextStep(true) - setIsFix(true) + setIsFixedStep(true) } } @@ -65,14 +65,14 @@ const StepItem = ({ children, step, pathname, icon: Icon, prevStep }: Props) => current: isCurrentStep, next: isNextStep, prev: isPrevStep, - fix: isFix, + fix: isFixedStep, } return ( <> <div className={cx('step-item')}> <div className={cx('circle', stepCondition)}> - {isCurrentStep || isNextStep ? <Icon className={cx('icon')} /> : step} + {isFixedStep || isCurrentStep || isNextStep ? <Icon className={cx('icon')} /> : step} </div> <p className={cx(stepCondition)}>{children}</p> </div> diff --git a/app/(landing)/signup/_ui/step/styles.module.scss b/app/(landing)/signup/_ui/step/styles.module.scss index 4a235abf..91aa1118 100644 --- a/app/(landing)/signup/_ui/step/styles.module.scss +++ b/app/(landing)/signup/_ui/step/styles.module.scss @@ -76,6 +76,11 @@ background-color: $color-orange-200; } + &.fix { + background-color: $color-orange-200; + color: $color-white; + } + &.current { position: relative; From 6c61ae94f76b28f502c53074434026fafdf93272 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 18 Nov 2024 13:25:25 +0900 Subject: [PATCH 156/648] =?UTF-8?q?design:=20mixin=20typo-c1=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/check-box/styles.module.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shared/ui/check-box/styles.module.scss b/shared/ui/check-box/styles.module.scss index 5c6efb6e..db9f7d33 100644 --- a/shared/ui/check-box/styles.module.scss +++ b/shared/ui/check-box/styles.module.scss @@ -36,8 +36,7 @@ } .label { - font-size: $text-c1; - font-weight: $text-medium; + @include typo-c1; &.gray500 { color: $color-gray-500; From ba1ac646fc44ba986d90a7037f38f6505f51895b Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 13:43:11 +0900 Subject: [PATCH 157/648] =?UTF-8?q?chore:=20Modal=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/mockServiceWorker.js | 9 +++------ shared/styles/base/_mixins.scss | 2 +- shared/ui/modal/modal.stories.tsx | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index 8241ef8c..742f3083 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -145,7 +145,7 @@ async function handleRequest(event, requestId) { headers: Object.fromEntries(responseClone.headers.entries()), }, }, - [responseClone.body], + [responseClone.body] ) })() } @@ -240,7 +240,7 @@ async function getResponse(event, client, requestId) { keepalive: request.keepalive, }, }, - [requestBuffer], + [requestBuffer] ) switch (clientMessage.type) { @@ -268,10 +268,7 @@ function sendToClient(client, message, transferrables = []) { resolve(event.data) } - client.postMessage( - message, - [channel.port2].concat(transferrables.filter(Boolean)), - ) + client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean))) }) } diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index 3ae51ca0..339b00a1 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -112,4 +112,4 @@ -webkit-box-orient: vertical; overflow: hidden; } -} \ No newline at end of file +} diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index c9182d0e..f75728e0 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -34,7 +34,7 @@ export const TwoButtons: StoryType = { }, } -export const WithCustomIcon: StoryType = { +export const WithIcon: StoryType = { args: { icon: <ModalAlertIcon />, }, From 4efffd292681ce69f9c86f47a8056c5ef90248dc Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 18 Nov 2024 17:38:20 +0900 Subject: [PATCH 158/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=82=AC=EC=9D=B4=EB=93=9C=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8,=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details-side-item.stories.tsx | 47 +++++++++++++++++ .../_ui/details-side-item/index.tsx | 50 +++++++++++++++++++ .../_ui/details-side-item/styles.module.scss | 40 +++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx new file mode 100644 index 00000000..b1563431 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx @@ -0,0 +1,47 @@ +import DetailsSideItem, { + TitleType, +} from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' +import type { Meta, StoryFn } from '@storybook/react' + +const meta: Meta<typeof DetailsSideItem> = { + title: 'components/DetailsSideItem', + component: DetailsSideItem, + tags: ['autodocs'], +} + +const sideItem: StoryFn<{ data: { title: TitleType; data: string | number }[] }> = ({ data }) => ( + <div style={{ width: '100%', height: '100%', background: '#fafafa', padding: '20px' }}> + {data.map((item, idx) => ( + <DetailsSideItem + key={item.title} + title={item.title} + data={item.data} + hasGap={idx === 3 || idx === 5 ? false : true} + /> + ))} + </div> +) +export const Default = sideItem.bind({}) +Default.args = { + data: [{ title: '투자 원금', data: '10,000,000' }], +} + +export const Trader = sideItem.bind({}) +Trader.args = { + data: [{ title: '트레이더', data: '수밍' }], +} + +export const LayoutExample = sideItem.bind({}) +LayoutExample.args = { + data: [ + { title: '트레이더', data: '수밍' }, + { title: '최소 투자 금액', data: '1억 ~ 2억' }, + { title: '투자 원금', data: '10,000,000' }, + { title: 'KP Ratio', data: 0.3993 }, + { title: 'SM SCORE', data: 67.38 }, + { title: '최종손익입력일자', data: '2016.04.30' }, + { title: '등록일', data: '2016.02.30' }, + ], +} + +export default meta diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx new file mode 100644 index 00000000..f21812dd --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx @@ -0,0 +1,50 @@ +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import Avatar from '@/shared/ui/avatar' +import { LinkButton } from '@/shared/ui/link-button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export type TitleType = + | '트레이더' + | '최소 투자 금액' + | '투자 원금' + | 'KP Ratio' + | 'SM SCORE' + | '최종손익입력일자' + | '등록일' + +interface Props { + title: TitleType + data: string | number + imageUrl?: string + hasGap?: boolean +} + +const DetailsSideItem = ({ title, data, imageUrl, hasGap = true }: Props) => { + return ( + <div className={cx('side-item', hasGap && 'gap')}> + <div className={cx('title')}>{title}</div> + <div className={cx('data')}> + {title === '트레이더' ? ( + <> + <div className={cx('avatar')}> + <Avatar size="medium" src={imageUrl} /> + <p>{data}</p> + </div> + <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> + 문의하기 + </LinkButton> + </> + ) : ( + <p>{data}</p> + )} + </div> + </div> + ) +} + +export default DetailsSideItem diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss new file mode 100644 index 00000000..959b6dc7 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss @@ -0,0 +1,40 @@ +.side-item { + width: 276px; + height: 100px; + background-color: $color-white; + padding: 20px 20px 0; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + + &.gap { + height: 120px; + margin-bottom: 20px; + border-radius: 5px; + padding: 20px; + } + + .title { + font-weight: $text-bold; + font-size: $text-b2; + color: $color-gray-500; + border-bottom: 0.75px solid $color-gray-500; + padding-bottom: 12px; + } + + .data { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 12px; + .avatar { + display: flex; + align-items: center; + p { + margin-left: 5px; + } + } + p { + @include typo-b1; + } + } +} From 6911a1e19a8c5e07c3e510229537f8948daab3bc Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 18 Nov 2024 18:32:11 +0900 Subject: [PATCH 159/648] =?UTF-8?q?design:=20trader=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_ui/details-side-item/index.tsx | 2 +- .../[strategyId]/_ui/details-side-item/styles.module.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx index f21812dd..b7ec9602 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx @@ -32,7 +32,7 @@ const DetailsSideItem = ({ title, data, imageUrl, hasGap = true }: Props) => { {title === '트레이더' ? ( <> <div className={cx('avatar')}> - <Avatar size="medium" src={imageUrl} /> + <Avatar src={imageUrl} /> <p>{data}</p> </div> <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss index 959b6dc7..879b5812 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss @@ -30,7 +30,7 @@ display: flex; align-items: center; p { - margin-left: 5px; + margin-left: 11px; } } p { From 9b19f582d775272e38d6879464ca03f9fc2e66b8 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 18 Nov 2024 18:41:32 +0900 Subject: [PATCH 160/648] =?UTF-8?q?feat:=20=EC=B2=B4=ED=81=AC=EB=90=9C=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/checked-circle.svg | 6 ++++++ public/icons/circle.svg | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 public/icons/checked-circle.svg diff --git a/public/icons/checked-circle.svg b/public/icons/checked-circle.svg new file mode 100644 index 00000000..3e86839c --- /dev/null +++ b/public/icons/checked-circle.svg @@ -0,0 +1,6 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g> + <circle cx="12" cy="12" r="7.5" fill="#A7A7A7"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M16.5 9.2367L10.5427 15.5L7.5 12.4465L8.25178 11.692L10.525 13.9733L15.7309 8.5L16.5 9.2367Z" fill="white"/> + </g> +</svg> \ No newline at end of file diff --git a/public/icons/circle.svg b/public/icons/circle.svg index d7d31c87..6f7cdea2 100644 --- a/public/icons/circle.svg +++ b/public/icons/circle.svg @@ -1,3 +1,3 @@ -<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"> -<circle id="Ellipse 27" cx="6" cy="6.30664" r="5.5" stroke="#1D1D1D"/> -</svg> +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <circle cx="12" cy="12" r="7.5" stroke="#1D1D1D"/> +</svg> \ No newline at end of file From f82cfcbaf7dfd0b5e7613ccd488f62da18f886b1 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 18 Nov 2024 18:42:20 +0900 Subject: [PATCH 161/648] =?UTF-8?q?design:=20=EC=B2=B4=ED=81=AC=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EC=83=81=ED=83=9C=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/index.tsx | 1 + shared/ui/check-box/index.tsx | 5 ++--- shared/ui/check-box/styles.module.scss | 22 ++++++---------------- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 8d8b637c..aeb424f9 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -26,3 +26,4 @@ export { default as StrategyIcon } from './strategy.svg' export { default as TradersIcon } from './traders.svg' export { default as StarIcon } from './star.svg' export { default as CircleIcon } from './circle.svg' +export { default as CheckedCircleIcon } from './checked-circle.svg' diff --git a/shared/ui/check-box/index.tsx b/shared/ui/check-box/index.tsx index 920c29ff..2ad48b8b 100644 --- a/shared/ui/check-box/index.tsx +++ b/shared/ui/check-box/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { CircleIcon } from '@/public/icons' +import { CheckedCircleIcon, CircleIcon } from '@/public/icons' import classNames from 'classnames/bind' import styles from './styles.module.scss' @@ -28,8 +28,7 @@ const Checkbox = ({ label, isChecked, onChange, className = '', textColor = 'gra [`checked${textColor}`]: isChecked, })} > - <CircleIcon /> - {isChecked && <div className={cx('innerCircle', textColor)} />} + {isChecked ? <CheckedCircleIcon /> : <CircleIcon />} </div> {label && <span className={cx('label', textColor)}>{label}</span>} </div> diff --git a/shared/ui/check-box/styles.module.scss b/shared/ui/check-box/styles.module.scss index db9f7d33..17ddf3f5 100644 --- a/shared/ui/check-box/styles.module.scss +++ b/shared/ui/check-box/styles.module.scss @@ -11,27 +11,17 @@ justify-content: center; flex-shrink: 0; position: relative; -} - -.innerCircle { - position: absolute; - width: 8px; - height: 8px; - border-radius: 50%; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - &.gray500 { - background-color: $color-gray-500; + &.checkedgray500 svg circle { + fill: $color-gray-500; } - &.gray600 { - background-color: $color-gray-600; + &.checkedgray600 svg circle { + fill: $color-gray-600; } - &.gray800 { - background-color: $color-gray-800; + &.checkedgray800 svg circle { + fill: $color-gray-800; } } From 2559d9151a83ee02a82826332273716d4bbc3bb3 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 18 Nov 2024 20:02:34 +0900 Subject: [PATCH 162/648] =?UTF-8?q?fix:=20Modal=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 2 +- .../{modal-titles.ts => modal-contents.ts} | 2 +- shared/ui/modal/index.tsx | 6 +- shared/ui/modal/modal.stories.tsx | 86 ++++++++++++++----- shared/ui/modal/styles.module.scss | 3 +- 5 files changed, 69 insertions(+), 30 deletions(-) rename shared/constants/{modal-titles.ts => modal-contents.ts} (91%) diff --git a/app/layout.tsx b/app/layout.tsx index ad62da4b..f1411d18 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -14,10 +14,10 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { return ( <html lang="ko" className={pretendard.variable}> <body> - <div id="modal-root"></div> <QueryProvider> <MSWInitializer /> {children} + <div id="modal-root"></div> </QueryProvider> </body> </html> diff --git a/shared/constants/modal-titles.ts b/shared/constants/modal-contents.ts similarity index 91% rename from shared/constants/modal-titles.ts rename to shared/constants/modal-contents.ts index 22ea04a4..90304617 100644 --- a/shared/constants/modal-titles.ts +++ b/shared/constants/modal-contents.ts @@ -1,4 +1,4 @@ -export const MODAL_TITLES = { +export const MODAL_CONTENTS = { SUBSCRIBE: '전략을 구독합니다. \n 구독한 전략은 나의 관심전략 \n 페이지에서 확인 가능합니다.', VERIFICATIONCODE: '인증코드가 이메일로 발송되었습니다.\n 메일을 확인해주세요.', LOGIN: '로그인이 필요합니다.\n 로그인하시겠습니까?', diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index b72762ca..dfa959f6 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -14,7 +14,7 @@ const cx = classNames.bind(styles) interface Props { title?: string icon?: ReactNode - isModalOpen: boolean + isOpen: boolean hasTwoButtons?: boolean confirmButton: () => void closeModal: () => void @@ -23,12 +23,12 @@ interface Props { const Modal = ({ title, icon, - isModalOpen, + isOpen, hasTwoButtons = false, confirmButton, closeModal, }: Props) => { - if (!isModalOpen) return null + if (!isOpen) return null const modalRoot = document.getElementById('modal-root') diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index f75728e0..b4c2d1f9 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -1,43 +1,83 @@ +import React from 'react' + import { Meta, StoryObj } from '@storybook/react' import { ModalAlertIcon } from 'public/icons/index' +import { Button } from '@/shared/ui/button' + import Modal from './index' const meta: Meta<typeof Modal> = { title: 'Components/Modal', component: Modal, - args: { - title: '기본 모달 제목', - icon: <ModalAlertIcon />, - isModalOpen: true, - hasTwoButtons: false, - confirmButton: () => alert('확인 버튼 클릭'), - closeModal: () => alert('닫기 버튼 클릭'), - }, - argTypes: { - title: { control: 'text' }, - isModalOpen: { control: 'boolean' }, - hasTwoButtons: { control: 'boolean' }, - confirmButton: { action: 'confirmButton' }, - closeModal: { action: 'closeModal' }, - }, tags: ['autodocs'], } +export default meta + type StoryType = StoryObj<typeof Modal> -export const Default: StoryType = {} +const ModalStory = ({ + title, + icon, + hasTwoButtons, +}: { + title?: string + icon?: React.ReactNode + hasTwoButtons?: boolean +}) => { + const [isOpen, setIsOpen] = React.useState(false) + + React.useEffect(() => { + if (typeof window !== 'undefined' && !document.getElementById('modal-root')) { + const modalRoot = document.createElement('div') + modalRoot.id = 'modal-root' + document.body.appendChild(modalRoot) + } + }, []) + + return ( + <div style={{ padding: '20px' }}> + <Button onClick={() => setIsOpen(true)}>모달 열기</Button> + <Modal + title={title} + icon={icon} + isOpen={isOpen} + hasTwoButtons={hasTwoButtons} + closeModal={() => setIsOpen(false)} + confirmButton={() => { + alert('확인') + setIsOpen(false) + }} + /> + </div> + ) +} + +export const Default: StoryType = { + render: () => <ModalStory title="기본 모달 제목" />, +} export const TwoButtons: StoryType = { - args: { - hasTwoButtons: true, - }, + render: () => <ModalStory title="정말 삭제하시겠습니까?" hasTwoButtons={true} />, } export const WithIcon: StoryType = { - args: { - icon: <ModalAlertIcon />, - }, + render: () => <ModalStory title="알림이 있습니다." icon={<ModalAlertIcon />} />, } -export default meta +export const LongText: StoryType = { + render: () => ( + <ModalStory title="이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다." /> + ), +} + +export const FullFeatured: StoryType = { + render: () => ( + <ModalStory + title="모든 기능이 포함된 모달입니다. 확인해주세요." + icon={<ModalAlertIcon />} + hasTwoButtons={true} + /> + ), +} diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index bc63f582..2c81b2a7 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -1,7 +1,6 @@ .overlay { position: fixed; - width: 100%; - height: 100%; + inset: 0; background-color: $color-black; opacity: 0.2; z-index: 2; From 01155d83114394987afb4a0fad7fd17c8759e5a0 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 18 Nov 2024 22:16:23 +0900 Subject: [PATCH 163/648] =?UTF-8?q?feat:=20dropdown=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/dropdown.module.scss | 47 ++++++++++ .../ui/dropdown/hooks/use-dropdown-context.ts | 12 +++ shared/ui/dropdown/hooks/use-dropdown.ts | 63 +++++++++++++ shared/ui/dropdown/index.tsx | 92 +++++++++++++++++++ shared/ui/dropdown/option/range-option.tsx | 36 ++++++++ 5 files changed, 250 insertions(+) create mode 100644 shared/ui/dropdown/dropdown.module.scss create mode 100644 shared/ui/dropdown/hooks/use-dropdown-context.ts create mode 100644 shared/ui/dropdown/hooks/use-dropdown.ts create mode 100644 shared/ui/dropdown/index.tsx create mode 100644 shared/ui/dropdown/option/range-option.tsx diff --git a/shared/ui/dropdown/dropdown.module.scss b/shared/ui/dropdown/dropdown.module.scss new file mode 100644 index 00000000..0c5ba769 --- /dev/null +++ b/shared/ui/dropdown/dropdown.module.scss @@ -0,0 +1,47 @@ +// NOTE: color, padding 등 세부 수치들 미정, 일단 제일 가까운 걸로 함 + +.container { + width: 100%; + padding: 7px 11px; + border: 1px solid $color-gray-300; + border-radius: 4px; + display: flex; + align-items: center; + @include typo-c2; +} + +.trigger { + width: 100%; + display: flex; + justify-content: space-between; + color: $color-gray-400; + background-color: #fff; +} + +.options { + list-style: none; + padding: 0; + margin: 0; + background-color: #fff; +} + +.option { + border-top-color: transparent; + cursor: pointer; + + .icon { + color: $color-gray-300; + } + + .selected-icon { + color: $color-gray-800; + } + + &.selected { + background-color: $color-gray-300; + } + + &:not(.selected):hover { + background-color: #f0f0f0; + } +} diff --git a/shared/ui/dropdown/hooks/use-dropdown-context.ts b/shared/ui/dropdown/hooks/use-dropdown-context.ts new file mode 100644 index 00000000..6c4e5d3c --- /dev/null +++ b/shared/ui/dropdown/hooks/use-dropdown-context.ts @@ -0,0 +1,12 @@ +// hooks/useDropdown.ts +import { useContext } from 'react' + +import { DropdownContext } from '..' + +export const useDropdownContext = () => { + const context = useContext(DropdownContext) + if (!context) { + throw new Error('useDropdown must be used within a DropdownProvider') + } + return context +} diff --git a/shared/ui/dropdown/hooks/use-dropdown.ts b/shared/ui/dropdown/hooks/use-dropdown.ts new file mode 100644 index 00000000..3182799a --- /dev/null +++ b/shared/ui/dropdown/hooks/use-dropdown.ts @@ -0,0 +1,63 @@ +import { useEffect, useRef, useState } from 'react' + +interface UseDropdownProps { + onChange?: (value: string | string[]) => void + isMultiple?: boolean +} + +export const useDropdown = ({ onChange, isMultiple = false }: UseDropdownProps) => { + const [isOpen, setIsOpen] = useState(false) + const [selectedValues, setSelectedValues] = useState<string[]>([]) + const dropdownRef = useRef<HTMLDivElement>(null) + + const toggleOpen = () => setIsOpen((prev) => !prev) + + const handleSelect = (value: string) => { + if (isMultiple) { + // 다중 선택 모드 + setSelectedValues((prev) => { + if (prev.includes(value)) { + const result = prev.filter((item) => item !== value) + onChange?.(result) + return result + } + const result = [...prev, value] + onChange?.(result) + return result + }) + } else { + // 단일 선택 모드 + switchValue(value) + } + } + + const switchValue = (value: string) => { + setSelectedValues((prev) => { + const result = prev[0] === value ? [] : [value] + onChange?.(result) + return result + }) + } + + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setIsOpen(false) + } + } + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside) + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, []) + + return { + isOpen, + toggleOpen, + selectedValues, + handleSelect, + switchValue, + dropdownRef, + } +} diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx new file mode 100644 index 00000000..71d1df8b --- /dev/null +++ b/shared/ui/dropdown/index.tsx @@ -0,0 +1,92 @@ +'use client' + +import { CSSProperties, ReactNode, createContext } from 'react' + +import { CheckboxIcon, ChevronDownIcon, ChevronUpIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './dropdown.module.scss' +import { useDropdown } from './hooks/use-dropdown' +import { useDropdownContext } from './hooks/use-dropdown-context' +import RangeOption from './option/range-option' + +const cx = classNames.bind(styles) + +interface DropdownContextProps { + isOpen: boolean + toggleOpen: () => void + selectedValues: string[] + handleSelect: (value: string) => void +} + +const defaultContext = { + isOpen: false, + toggleOpen: () => {}, + selectedValues: [], + handleSelect: () => {}, +} + +export const DropdownContext = createContext<DropdownContextProps>(defaultContext) + +export interface DropdownProps { + Trigger: ReactNode + onChange?: (value: string | string[]) => void + isMultiple: boolean + containerStyle?: CSSProperties + labelStyle?: CSSProperties + children?: ReactNode +} + +const Dropdown = ({ + Trigger, + onChange, + isMultiple = false, + containerStyle, + labelStyle, + children, +}: DropdownProps) => { + const { isOpen, toggleOpen, selectedValues, handleSelect, dropdownRef } = useDropdown({ + onChange, + isMultiple, + }) + + return ( + <DropdownContext.Provider value={{ isOpen, toggleOpen, selectedValues, handleSelect }}> + <div style={containerStyle} ref={dropdownRef}> + <button onClick={toggleOpen} className={cx('container', 'trigger')} style={labelStyle}> + {Trigger} + {isOpen ? <ChevronUpIcon /> : <ChevronDownIcon />} + </button> + {isOpen && <ul className={cx('options')}>{children}</ul>} + </div> + </DropdownContext.Provider> + ) +} +interface ItemProps { + value: string + label: string + hasCheck?: boolean + isMultiple?: boolean + style?: CSSProperties +} + +const Item = ({ value, label, hasCheck = false, style }: ItemProps) => { + const context = useDropdownContext() + + const { handleSelect, selectedValues } = context + const isSelected = selectedValues.includes(value) + + return ( + <li + className={cx('container', 'option', { selected: isSelected })} + style={style} + onClick={() => handleSelect(value)} + aria-hidden + > + {label} + {hasCheck && <CheckboxIcon className={cx('icon', { 'selected-icon': isSelected })} />} + </li> + ) +} + +export default Object.assign(Dropdown, { Item, RangeOption }) diff --git a/shared/ui/dropdown/option/range-option.tsx b/shared/ui/dropdown/option/range-option.tsx new file mode 100644 index 00000000..04e1cd12 --- /dev/null +++ b/shared/ui/dropdown/option/range-option.tsx @@ -0,0 +1,36 @@ +import { useState } from 'react' + +// import { DropdownContext } from '..' +import styles from '../dropdown.module.scss' + +const RangeOption = ({ onRangeChange }: { onRangeChange: (min: string, max: string) => void }) => { + const [range, setRange] = useState({ min: '', max: '' }) + + const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const { name, value } = e.target + setRange((prev) => ({ ...prev, [name]: value })) + onRangeChange(range.min, range.max) + } + + return ( + <div className={styles.inputRange}> + <input + type="text" + placeholder="최소" + name="min" + value={range.min} + onChange={handleInputChange} + /> + ~ + <input + type="text" + placeholder="최대" + name="max" + value={range.max} + onChange={handleInputChange} + /> + </div> + ) +} + +export default RangeOption From 7426cce17a6fd192d85b5b8d991a651a8dece78d Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 18 Nov 2024 22:17:41 +0900 Subject: [PATCH 164/648] =?UTF-8?q?feat:=20select=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/select/index.tsx | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 shared/ui/select/index.tsx diff --git a/shared/ui/select/index.tsx b/shared/ui/select/index.tsx new file mode 100644 index 00000000..a54ff2d8 --- /dev/null +++ b/shared/ui/select/index.tsx @@ -0,0 +1,59 @@ +'use client' + +import { CSSProperties } from 'react' + +import Dropdown from '../dropdown' + +interface OptionModel { + value: string + label: string +} + +interface SelectProps { + title?: string + isRounded?: boolean + isMultiple?: boolean + hasCheck?: boolean + containerStyle?: CSSProperties + titleStyle?: CSSProperties + onChange?: (value: string | string[]) => void + options: OptionModel[] +} + +const Select = ({ + isRounded = false, + isMultiple = false, + hasCheck = false, + containerStyle, + titleStyle, + onChange, + options, + title = options[0].label, +}: SelectProps) => { + if (!options.length) return null + + const roundStyle = { borderRadius: '40px', overflow: 'hidden' } + const labelStyle = isRounded ? { ...roundStyle, ...titleStyle } : titleStyle + + return ( + <Dropdown + Trigger={<span>{title}</span>} + isMultiple={isMultiple} + onChange={onChange} + containerStyle={containerStyle} + labelStyle={labelStyle} + > + {options.map(({ value, label }) => ( + <Dropdown.Item + value={value} + label={label} + isMultiple={isMultiple} + hasCheck={hasCheck} + key={label} + /> + ))} + </Dropdown> + ) +} + +export default Select From daf0c75324bfd740a7c58b22d25a7dc7b7f5f088 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 18 Nov 2024 22:37:18 +0900 Subject: [PATCH 165/648] =?UTF-8?q?feat:=20select=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=9E=91=EC=84=B1=20=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/select/select.stories.tsx | 70 +++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 shared/ui/select/select.stories.tsx diff --git a/shared/ui/select/select.stories.tsx b/shared/ui/select/select.stories.tsx new file mode 100644 index 00000000..e59c296a --- /dev/null +++ b/shared/ui/select/select.stories.tsx @@ -0,0 +1,70 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import Select from '.' + +const meta = { + title: 'Components/Select', + component: Select, + decorators: [ + (Story) => ( + <div style={{ width: '256px', height: '200px', margin: '60px' }}> + <Story /> + </div> + ), + ], + tags: ['autodocs'], +} satisfies Meta<typeof Select> + +export default meta +type StoryType = StoryObj<typeof Select> + +const options = [ + { + value: '1-', + label: '1년이하', + }, + { + value: '1+', + label: '1년~2년', + }, + { + value: '2+', + label: '2년~3년', + }, + { + value: '3+', + label: '3년이상', + }, +] + +export const Default: StoryType = { + args: { + title: '수익률', + isRounded: false, + isMultiple: false, + hasCheck: false, + onChange: () => {}, + options, + }, +} + +export const Rounded: StoryType = { + args: { + ...Default.args, + isRounded: true, + }, +} + +export const Muliple: StoryType = { + args: { + ...Default.args, + isMultiple: true, + }, +} + +export const WithCheck: StoryType = { + args: { + ...Default.args, + hasCheck: true, + }, +} From 534ed3703d46c3d41b4d5e41c3c332c201ce7e79 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 18 Nov 2024 22:44:13 +0900 Subject: [PATCH 166/648] =?UTF-8?q?fix:=20With=20Check=20option=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B0=98=EC=98=81=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/dropdown.module.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shared/ui/dropdown/dropdown.module.scss b/shared/ui/dropdown/dropdown.module.scss index 0c5ba769..78964ab7 100644 --- a/shared/ui/dropdown/dropdown.module.scss +++ b/shared/ui/dropdown/dropdown.module.scss @@ -31,6 +31,7 @@ .icon { color: $color-gray-300; + margin-left: auto; } .selected-icon { @@ -45,3 +46,7 @@ background-color: #f0f0f0; } } + +.with-check-container { + +} \ No newline at end of file From 045657ada5d9757750548f4ac05e740a7778133c Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 18 Nov 2024 23:07:37 +0900 Subject: [PATCH 167/648] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/dropdown.module.scss | 4 ---- .../ui/dropdown/hooks/use-dropdown-context.ts | 1 - shared/ui/dropdown/hooks/use-dropdown.ts | 9 +++----- shared/ui/dropdown/index.tsx | 2 +- shared/ui/select/index.tsx | 22 +++++++++---------- 5 files changed, 14 insertions(+), 24 deletions(-) diff --git a/shared/ui/dropdown/dropdown.module.scss b/shared/ui/dropdown/dropdown.module.scss index 78964ab7..db645d3c 100644 --- a/shared/ui/dropdown/dropdown.module.scss +++ b/shared/ui/dropdown/dropdown.module.scss @@ -46,7 +46,3 @@ background-color: #f0f0f0; } } - -.with-check-container { - -} \ No newline at end of file diff --git a/shared/ui/dropdown/hooks/use-dropdown-context.ts b/shared/ui/dropdown/hooks/use-dropdown-context.ts index 6c4e5d3c..549cf11b 100644 --- a/shared/ui/dropdown/hooks/use-dropdown-context.ts +++ b/shared/ui/dropdown/hooks/use-dropdown-context.ts @@ -1,4 +1,3 @@ -// hooks/useDropdown.ts import { useContext } from 'react' import { DropdownContext } from '..' diff --git a/shared/ui/dropdown/hooks/use-dropdown.ts b/shared/ui/dropdown/hooks/use-dropdown.ts index 3182799a..d8474240 100644 --- a/shared/ui/dropdown/hooks/use-dropdown.ts +++ b/shared/ui/dropdown/hooks/use-dropdown.ts @@ -16,12 +16,9 @@ export const useDropdown = ({ onChange, isMultiple = false }: UseDropdownProps) if (isMultiple) { // 다중 선택 모드 setSelectedValues((prev) => { - if (prev.includes(value)) { - const result = prev.filter((item) => item !== value) - onChange?.(result) - return result - } - const result = [...prev, value] + const result = prev.includes(value) + ? prev.filter((item) => item !== value) + : [...prev, value] onChange?.(result) return result }) diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index 71d1df8b..a042f73e 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -62,7 +62,7 @@ const Dropdown = ({ </DropdownContext.Provider> ) } -interface ItemProps { +export interface ItemProps { value: string label: string hasCheck?: boolean diff --git a/shared/ui/select/index.tsx b/shared/ui/select/index.tsx index a54ff2d8..7eff3941 100644 --- a/shared/ui/select/index.tsx +++ b/shared/ui/select/index.tsx @@ -2,23 +2,21 @@ import { CSSProperties } from 'react' -import Dropdown from '../dropdown' +import Dropdown, { DropdownProps, ItemProps } from '../dropdown' interface OptionModel { value: string label: string } -interface SelectProps { - title?: string - isRounded?: boolean - isMultiple?: boolean - hasCheck?: boolean - containerStyle?: CSSProperties - titleStyle?: CSSProperties - onChange?: (value: string | string[]) => void - options: OptionModel[] -} +type SelectType = Pick<DropdownProps, 'isMultiple' | 'containerStyle' | 'labelStyle'> & + Pick<ItemProps, 'hasCheck'> & { + title?: string + isRounded?: boolean + titleStyle?: CSSProperties + onChange?: (value: string | string[]) => void + options: OptionModel[] + } const Select = ({ isRounded = false, @@ -29,7 +27,7 @@ const Select = ({ onChange, options, title = options[0].label, -}: SelectProps) => { +}: SelectType) => { if (!options.length) return null const roundStyle = { borderRadius: '40px', overflow: 'hidden' } From 918eb2d8ee4de8ac698dc9094a3c3a4caf111f34 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 01:08:39 +0900 Subject: [PATCH 168/648] =?UTF-8?q?feat:=20Logo=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/logo/index.tsx | 28 ++++++++++++++++++++++++++++ shared/ui/logo/logo.module.scss | 14 ++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 shared/ui/logo/index.tsx create mode 100644 shared/ui/logo/logo.module.scss diff --git a/shared/ui/logo/index.tsx b/shared/ui/logo/index.tsx new file mode 100644 index 00000000..cdda7f60 --- /dev/null +++ b/shared/ui/logo/index.tsx @@ -0,0 +1,28 @@ +import Link from 'next/link' + +import ImageLogo from '@/public/images/logo.svg' +import TextLogo from '@/public/images/text-logo.svg' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' + +import styles from './logo.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + href?: string + hasText?: boolean + className?: string +} + +const Logo = ({ href = PATH.HOME, hasText = false, className }: Props) => { + return ( + <Link href={href} className={cx('container', className)}> + <ImageLogo /> + {hasText && <TextLogo className={cx('text')} aria-label="인베스트메틱" />} + </Link> + ) +} + +export default Logo diff --git a/shared/ui/logo/logo.module.scss b/shared/ui/logo/logo.module.scss new file mode 100644 index 00000000..e03cbdda --- /dev/null +++ b/shared/ui/logo/logo.module.scss @@ -0,0 +1,14 @@ +.container { + display: flex; + align-items: center; + gap: 12px; + + svg { + flex-shrink: 0; + } +} + +.text { + color: $color-gray-500; + line-height: normal; +} From 6bceba25cce27e8cbf9b7175255769d532c14669 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 01:11:54 +0900 Subject: [PATCH 169/648] =?UTF-8?q?feat:=20ButtonGroup=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/button/index.tsx | 17 +++++++++++++++-- shared/ui/link-button/link-button.stories.tsx | 5 +++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index 5baf0728..0f39f29a 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { ComponentProps } from 'react' +import { ComponentProps, ReactNode } from 'react' import classNames from 'classnames/bind' @@ -17,7 +17,7 @@ interface Props extends ComponentProps<'button'> { onClick: () => void } -export const Button = ({ +const _Button = ({ children, size = 'medium', variant = 'outline', @@ -35,3 +35,16 @@ export const Button = ({ {children} </button> ) + +interface ButtonGruopProps { + gap?: string + children: ReactNode +} + +const ButtonGroup = ({ gap = '8px', children }: ButtonGruopProps) => { + return <div style={{ display: 'flex', gap }}>{children}</div> +} + +_Button.ButtonGroup = ButtonGroup + +export { _Button as Button } diff --git a/shared/ui/link-button/link-button.stories.tsx b/shared/ui/link-button/link-button.stories.tsx index 0895d836..a0afe38c 100644 --- a/shared/ui/link-button/link-button.stories.tsx +++ b/shared/ui/link-button/link-button.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react' +import { Button } from '../button' import { LinkButton } from './index' const meta = { @@ -76,13 +77,13 @@ export const Filled: StoryType = { export const GroupExample: StoryType = { render: () => ( - <div style={{ display: 'flex', gap: '8px' }}> + <Button.ButtonGroup> <LinkButton size="medium" variant="outline" href="/cancel"> 로그인 </LinkButton> <LinkButton size="medium" variant="filled" href="/confirm"> 회원가입 </LinkButton> - </div> + </Button.ButtonGroup> ), } From a01ceab5b13382b212b0d171cb861f1e4008ffad Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 01:13:41 +0900 Subject: [PATCH 170/648] =?UTF-8?q?feat:=20HeaderLinks=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/_ui/header-links/index.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 shared/ui/header/_ui/header-links/index.tsx diff --git a/shared/ui/header/_ui/header-links/index.tsx b/shared/ui/header/_ui/header-links/index.tsx new file mode 100644 index 00000000..9b53e1ca --- /dev/null +++ b/shared/ui/header/_ui/header-links/index.tsx @@ -0,0 +1,21 @@ +import { PATH } from '@/shared/constants/path' +import { Button } from '@/shared/ui/button' +import { LinkButton } from '@/shared/ui/link-button' + +const HeaderLinks = () => { + return ( + <Button.ButtonGroup gap="24px"> + <LinkButton href={PATH.HOME} size="small"> + 대시보드 + </LinkButton> + <LinkButton href={PATH.SIGN_IN} size="small"> + 로그인 + </LinkButton> + <LinkButton href={PATH.SIGN_UP} size="small" variant="filled"> + 회원가입 + </LinkButton> + </Button.ButtonGroup> + ) +} + +export default HeaderLinks From 6e98adb3532e1f29f4cd78b504e104b0a2166c09 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 01:19:22 +0900 Subject: [PATCH 171/648] =?UTF-8?q?feat:=20Header=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/header.module.scss | 9 +++++++++ shared/ui/header/index.tsx | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 shared/ui/header/header.module.scss create mode 100644 shared/ui/header/index.tsx diff --git a/shared/ui/header/header.module.scss b/shared/ui/header/header.module.scss new file mode 100644 index 00000000..0b610743 --- /dev/null +++ b/shared/ui/header/header.module.scss @@ -0,0 +1,9 @@ +.container { + height: 80px; + padding: 0 42px; + display: flex; + justify-content: space-between; + align-items: center; + background: rgba(251, 251, 251, 0.5); + backdrop-filter: blur(60px); +} diff --git a/shared/ui/header/index.tsx b/shared/ui/header/index.tsx new file mode 100644 index 00000000..fdabb109 --- /dev/null +++ b/shared/ui/header/index.tsx @@ -0,0 +1,22 @@ +import classNames from 'classnames/bind' + +import Logo from '../logo' +import HeaderLinks from './_ui/header-links' +import styles from './header.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + hasLinks?: boolean +} + +const Header = ({ hasLinks = false }: Props) => { + return ( + <header className={cx('container')}> + <Logo hasText /> + {hasLinks && <HeaderLinks />} + </header> + ) +} + +export default Header From 85d8af47467cc34a77e3e7e910c56d89e57d9240 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 01:57:31 +0900 Subject: [PATCH 172/648] =?UTF-8?q?refactor:=20Header=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/header.module.scss | 2 -- shared/ui/header/index.tsx | 16 +++++++++------- shared/ui/header/logo-header/index.tsx | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 shared/ui/header/logo-header/index.tsx diff --git a/shared/ui/header/header.module.scss b/shared/ui/header/header.module.scss index 0b610743..29ec90a9 100644 --- a/shared/ui/header/header.module.scss +++ b/shared/ui/header/header.module.scss @@ -4,6 +4,4 @@ display: flex; justify-content: space-between; align-items: center; - background: rgba(251, 251, 251, 0.5); - backdrop-filter: blur(60px); } diff --git a/shared/ui/header/index.tsx b/shared/ui/header/index.tsx index fdabb109..67d0068f 100644 --- a/shared/ui/header/index.tsx +++ b/shared/ui/header/index.tsx @@ -1,20 +1,22 @@ +import { CSSProperties, ReactNode } from 'react' + import classNames from 'classnames/bind' -import Logo from '../logo' -import HeaderLinks from './_ui/header-links' import styles from './header.module.scss' const cx = classNames.bind(styles) interface Props { - hasLinks?: boolean + Left?: ReactNode + Right?: ReactNode + styles?: CSSProperties } -const Header = ({ hasLinks = false }: Props) => { +const Header = ({ Left, Right }: Props) => { return ( - <header className={cx('container')}> - <Logo hasText /> - {hasLinks && <HeaderLinks />} + <header className={cx('container')} style={styles}> + {Left} + {Right} </header> ) } diff --git a/shared/ui/header/logo-header/index.tsx b/shared/ui/header/logo-header/index.tsx new file mode 100644 index 00000000..dc957a65 --- /dev/null +++ b/shared/ui/header/logo-header/index.tsx @@ -0,0 +1,25 @@ +import Header from '..' +import Logo from '../../logo' +import HeaderLinks from '../_ui/header-links' + +const headerStyles = { + background: 'rgba(251, 251, 251, 0.5)', + backdropFilter: 'blur(60px)', +} + +interface Props { + hasLinks?: boolean + hasText?: boolean +} + +const LogoHeader = ({ hasLinks = false, hasText = false }: Props) => { + return ( + <Header + Left={<Logo hasText={hasText} />} + Right={hasLinks && <HeaderLinks />} + styles={headerStyles} + /> + ) +} + +export default LogoHeader From 1306d405c5305ea597aae8e11b97c545c940c7bf Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 02:39:18 +0900 Subject: [PATCH 173/648] =?UTF-8?q?feat:=20BackHeader=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../back-header/back-header.module.scss | 13 +++++++ shared/ui/header/back-header/index.tsx | 37 +++++++++++++++++++ shared/ui/header/logo-header/index.tsx | 1 + 3 files changed, 51 insertions(+) create mode 100644 shared/ui/header/back-header/back-header.module.scss create mode 100644 shared/ui/header/back-header/index.tsx diff --git a/shared/ui/header/back-header/back-header.module.scss b/shared/ui/header/back-header/back-header.module.scss new file mode 100644 index 00000000..b05c0f39 --- /dev/null +++ b/shared/ui/header/back-header/back-header.module.scss @@ -0,0 +1,13 @@ +.container { + @include typo-c2; + display: flex; + align-items: center; + gap: 8px; + background-color: transparent; + color: $color-gray-500; + + svg { + width: 9px; + flex-shrink: 0; + } +} diff --git a/shared/ui/header/back-header/index.tsx b/shared/ui/header/back-header/index.tsx new file mode 100644 index 00000000..d0968701 --- /dev/null +++ b/shared/ui/header/back-header/index.tsx @@ -0,0 +1,37 @@ +'use client' + +// NOTE: 이름 공모전 개최 +import { useRouter } from 'next/navigation' + +import { ChevronLeftIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Header from '..' +import styles from './back-header.module.scss' + +const cx = classNames.bind(styles) + +const headerStyles = { + background: 'rgba(251, 251, 251, 0.5)', + backdropFilter: 'blur(60px)', +} + +const BackHeader = () => { + return <Header Left={<Left />} styles={headerStyles} /> +} + +const Left = () => { + const router = useRouter() + const onButtonClick = () => { + router.back() + } + + return ( + <button onClick={onButtonClick} className={cx('container')}> + <ChevronLeftIcon /> + <span>목록으로 돌아가기</span> + </button> + ) +} + +export default BackHeader diff --git a/shared/ui/header/logo-header/index.tsx b/shared/ui/header/logo-header/index.tsx index dc957a65..9848864b 100644 --- a/shared/ui/header/logo-header/index.tsx +++ b/shared/ui/header/logo-header/index.tsx @@ -1,3 +1,4 @@ +// NOTE: 이름 공모전 개최 import Header from '..' import Logo from '../../logo' import HeaderLinks from '../_ui/header-links' From 0fcafef13b238c6ca7c64fbdbfb99c0b8046707b Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 03:24:39 +0900 Subject: [PATCH 174/648] =?UTF-8?q?feat:=20BackHeader=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=9E=91=EC=84=B1=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../back-header/back-header.stories.tsx | 20 +++++++++++++++++++ shared/ui/header/back-header/index.tsx | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 shared/ui/header/back-header/back-header.stories.tsx diff --git a/shared/ui/header/back-header/back-header.stories.tsx b/shared/ui/header/back-header/back-header.stories.tsx new file mode 100644 index 00000000..cfe1ff5c --- /dev/null +++ b/shared/ui/header/back-header/back-header.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import BackHeader from '.' + +const meta = { + title: 'Components/Header/BackHeader', + component: BackHeader, + tags: ['autodocs'], +} satisfies Meta<typeof BackHeader> +export default meta +type StoryType = StoryObj<typeof BackHeader> + +export const Default: StoryType = { + render: () => <BackHeader />, + parameters: { + nextjs: { + appDirectory: true, + }, + }, +} diff --git a/shared/ui/header/back-header/index.tsx b/shared/ui/header/back-header/index.tsx index d0968701..2b1a5d94 100644 --- a/shared/ui/header/back-header/index.tsx +++ b/shared/ui/header/back-header/index.tsx @@ -1,6 +1,7 @@ 'use client' // NOTE: 이름 공모전 개최 +// TODO: 아이콘 바꿔야함 import { useRouter } from 'next/navigation' import { ChevronLeftIcon } from '@/public/icons' @@ -23,7 +24,7 @@ const BackHeader = () => { const Left = () => { const router = useRouter() const onButtonClick = () => { - router.back() + router?.back() } return ( From b74b5edaceb4d2858ca2e77d4374cf0ec3e007c0 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 04:03:00 +0900 Subject: [PATCH 175/648] =?UTF-8?q?feat:=20LogoHeader=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=9E=91=EC=84=B1=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/images/index.ts | 2 ++ shared/ui/header/index.tsx | 2 +- shared/ui/header/logo-header/index.tsx | 3 +- .../logo-header/logo-header.stories.tsx | 32 +++++++++++++++++++ shared/ui/logo/index.tsx | 3 +- 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 public/images/index.ts create mode 100644 shared/ui/header/logo-header/logo-header.stories.tsx diff --git a/public/images/index.ts b/public/images/index.ts new file mode 100644 index 00000000..fc0dc062 --- /dev/null +++ b/public/images/index.ts @@ -0,0 +1,2 @@ +export { default as ImageLogo } from './logo.svg' +export { default as TextLogo } from './text-logo.svg' diff --git a/shared/ui/header/index.tsx b/shared/ui/header/index.tsx index 67d0068f..82f70bce 100644 --- a/shared/ui/header/index.tsx +++ b/shared/ui/header/index.tsx @@ -12,7 +12,7 @@ interface Props { styles?: CSSProperties } -const Header = ({ Left, Right }: Props) => { +const Header = ({ Left, Right, styles }: Props) => { return ( <header className={cx('container')} style={styles}> {Left} diff --git a/shared/ui/header/logo-header/index.tsx b/shared/ui/header/logo-header/index.tsx index 9848864b..94a23ebc 100644 --- a/shared/ui/header/logo-header/index.tsx +++ b/shared/ui/header/logo-header/index.tsx @@ -1,6 +1,7 @@ // NOTE: 이름 공모전 개최 +import Logo from '@/shared/ui/logo' + import Header from '..' -import Logo from '../../logo' import HeaderLinks from '../_ui/header-links' const headerStyles = { diff --git a/shared/ui/header/logo-header/logo-header.stories.tsx b/shared/ui/header/logo-header/logo-header.stories.tsx new file mode 100644 index 00000000..0cbe7b57 --- /dev/null +++ b/shared/ui/header/logo-header/logo-header.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import LogoHeader from '.' + +const meta = { + title: 'Components/Header/LogoHeader', + component: LogoHeader, + tags: ['autodocs'], +} satisfies Meta<typeof LogoHeader> +export default meta +type StoryType = StoryObj<typeof LogoHeader> + +export const Default: StoryType = { + args: { + hasLinks: true, + hasText: false, + }, +} + +export const WithoutLogoText: StoryType = { + args: { + ...Default.args, + hasText: false, + }, +} + +export const WithoutLinks: StoryType = { + args: { + ...Default.args, + hasLinks: false, + }, +} diff --git a/shared/ui/logo/index.tsx b/shared/ui/logo/index.tsx index cdda7f60..81003307 100644 --- a/shared/ui/logo/index.tsx +++ b/shared/ui/logo/index.tsx @@ -1,7 +1,6 @@ import Link from 'next/link' -import ImageLogo from '@/public/images/logo.svg' -import TextLogo from '@/public/images/text-logo.svg' +import { ImageLogo, TextLogo } from '@/public/images' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' From 8e2cee942b1048f2deced41b8919c9fc238b7ec3 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 10:43:31 +0900 Subject: [PATCH 176/648] =?UTF-8?q?feat:=20=EB=B3=84=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20margin,=20size=20large=20=EC=B6=94=EA=B0=80=20(#73)?= =?UTF-8?q?'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/total-star/star-icon.tsx | 3 +-- shared/ui/total-star/styles.module.scss | 13 +++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/shared/ui/total-star/star-icon.tsx b/shared/ui/total-star/star-icon.tsx index 78a4d64e..9b1f9a22 100644 --- a/shared/ui/total-star/star-icon.tsx +++ b/shared/ui/total-star/star-icon.tsx @@ -3,12 +3,11 @@ import { StarIcon } from '@/public/icons' import classNames from 'classnames/bind' -import { SizeType } from '@/shared/ui/total-star' - import styles from './styles.module.scss' const cx = classNames.bind(styles) +type SizeType = 'small' | 'medium' | 'large' interface Props { size?: SizeType } diff --git a/shared/ui/total-star/styles.module.scss b/shared/ui/total-star/styles.module.scss index 034e2209..09e01e30 100644 --- a/shared/ui/total-star/styles.module.scss +++ b/shared/ui/total-star/styles.module.scss @@ -23,12 +23,17 @@ } .icon-wrapper { display: block; + margin: -3px; &.small { - width: 22px; - height: 22px; + width: 20px; + height: 20px; } &.medium { - width: 28px; - height: 28px; + width: 26px; + height: 26px; + } + &.large { + width: 32px; + height: 32px; } } From 46c7a6ec9535a63a038b00df0ac51612d92a3be9 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 10:44:22 +0900 Subject: [PATCH 177/648] =?UTF-8?q?feat:=20=EB=B3=84=EC=A0=90=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8,=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_ui/star-rating/index.tsx | 37 +++++++++++++++++++ .../_ui/star-rating/star-rating.stories.tsx | 24 ++++++++++++ .../_ui/star-rating/styles.module.scss | 13 +++++++ 3 files changed, 74 insertions(+) create mode 100644 app/(dashboard)/strategies/_ui/star-rating/index.tsx create mode 100644 app/(dashboard)/strategies/_ui/star-rating/star-rating.stories.tsx create mode 100644 app/(dashboard)/strategies/_ui/star-rating/styles.module.scss diff --git a/app/(dashboard)/strategies/_ui/star-rating/index.tsx b/app/(dashboard)/strategies/_ui/star-rating/index.tsx new file mode 100644 index 00000000..3115a387 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/star-rating/index.tsx @@ -0,0 +1,37 @@ +'use client' + +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import Star from '@/shared/ui/total-star/star-icon' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + starRating?: number +} + +const StarRating = ({ starRating }: Props) => { + const [clickedIdx, setClickedIdx] = useState(0) + + return ( + <div className={cx('container')}> + {starRating + ? [...Array(Math.floor(starRating))].map((_, idx) => <Star key={idx} size="small" />) + : [...Array(5)].map((_, idx) => ( + <button + key={idx} + className={cx('click-star', idx < clickedIdx && 'onColor')} + onClick={() => setClickedIdx(idx + 1)} + > + <Star size="large" /> + </button> + ))} + </div> + ) +} + +export default StarRating diff --git a/app/(dashboard)/strategies/_ui/star-rating/star-rating.stories.tsx b/app/(dashboard)/strategies/_ui/star-rating/star-rating.stories.tsx new file mode 100644 index 00000000..e5eaa2b9 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/star-rating/star-rating.stories.tsx @@ -0,0 +1,24 @@ +import StarRating from '@/app/(dashboard)/strategies/_ui/star-rating' +import type { Meta, StoryFn } from '@storybook/react' + +const meta: Meta = { + title: 'components/StarRating', + component: StarRating, + tags: ['autodocs'], +} + +const starRating: StoryFn<{ starRating: number | undefined }> = ({ starRating }) => ( + <StarRating starRating={starRating} /> +) + +export const Rated = starRating.bind({}) +Rated.args = { + starRating: 5, +} + +export const Rating = starRating.bind({}) +Rating.args = { + starRating: undefined, +} + +export default meta diff --git a/app/(dashboard)/strategies/_ui/star-rating/styles.module.scss b/app/(dashboard)/strategies/_ui/star-rating/styles.module.scss new file mode 100644 index 00000000..d7747a7f --- /dev/null +++ b/app/(dashboard)/strategies/_ui/star-rating/styles.module.scss @@ -0,0 +1,13 @@ +.container { + display: flex; + padding: 0; + color: $color-yellow; + .click-star { + margin: -1px; + background-color: transparent; + color: #e3e3e3; + &.onColor { + color: $color-yellow; + } + } +} From 3fb6ef53a50db7841e5cfc4ad83b9e40e751153b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 11:37:16 +0900 Subject: [PATCH 178/648] =?UTF-8?q?rename:=20=EC=A0=84=EB=9E=B5=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8F=B4=EB=8D=94=20=ED=95=98=EC=9C=84=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/{ => [strategyId]}/_ui/star-rating/index.tsx | 0 .../{ => [strategyId]}/_ui/star-rating/star-rating.stories.tsx | 2 +- .../{ => [strategyId]}/_ui/star-rating/styles.module.scss | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename app/(dashboard)/strategies/{ => [strategyId]}/_ui/star-rating/index.tsx (100%) rename app/(dashboard)/strategies/{ => [strategyId]}/_ui/star-rating/star-rating.stories.tsx (85%) rename app/(dashboard)/strategies/{ => [strategyId]}/_ui/star-rating/styles.module.scss (100%) diff --git a/app/(dashboard)/strategies/_ui/star-rating/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/index.tsx similarity index 100% rename from app/(dashboard)/strategies/_ui/star-rating/index.tsx rename to app/(dashboard)/strategies/[strategyId]/_ui/star-rating/index.tsx diff --git a/app/(dashboard)/strategies/_ui/star-rating/star-rating.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/star-rating.stories.tsx similarity index 85% rename from app/(dashboard)/strategies/_ui/star-rating/star-rating.stories.tsx rename to app/(dashboard)/strategies/[strategyId]/_ui/star-rating/star-rating.stories.tsx index e5eaa2b9..688b6ca5 100644 --- a/app/(dashboard)/strategies/_ui/star-rating/star-rating.stories.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/star-rating.stories.tsx @@ -1,4 +1,4 @@ -import StarRating from '@/app/(dashboard)/strategies/_ui/star-rating' +import StarRating from '@/app/(dashboard)/strategies/[strategyId]/_ui/star-rating' import type { Meta, StoryFn } from '@storybook/react' const meta: Meta = { diff --git a/app/(dashboard)/strategies/_ui/star-rating/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/styles.module.scss similarity index 100% rename from app/(dashboard)/strategies/_ui/star-rating/styles.module.scss rename to app/(dashboard)/strategies/[strategyId]/_ui/star-rating/styles.module.scss From c1fc07a55f5c99e7c6e6d45d41c628597227fac2 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 19 Nov 2024 13:34:39 +0900 Subject: [PATCH 179/648] =?UTF-8?q?design:=20landing=20layout=EC=9D=98=20?= =?UTF-8?q?=EA=B7=B8=EB=9D=BC=EB=8D=B0=EC=9D=B4=EC=85=98=20overflow=20hidd?= =?UTF-8?q?en=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20z-index=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/global.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/styles/global.scss b/shared/styles/global.scss index b47aba00..fd350584 100644 --- a/shared/styles/global.scss +++ b/shared/styles/global.scss @@ -9,6 +9,7 @@ body { line-height: 132%; color: $color-black; background-color: $color-gray-100; + overflow-x: hidden; } input, @@ -39,6 +40,7 @@ textarea { &::before { content: ''; + z-index: zIndex(hidden); position: absolute; top: -160px; right: -130px; From 9b5ff46c3e6db8abf2b1f73895f864a8d557d02a Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 19 Nov 2024 13:36:25 +0900 Subject: [PATCH 180/648] =?UTF-8?q?feat:=20=EB=8C=80=ED=91=9C=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=ED=86=B5=ED=95=A9=20=ED=8F=89=EA=B7=A0=20=EC=A7=80?= =?UTF-8?q?=ED=91=9C=20=EC=B0=A8=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../average-metrics-chart.tsx | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 app/(landing)/(home)/_ui/average-metrics-container/average-metrics-chart.tsx diff --git a/app/(landing)/(home)/_ui/average-metrics-container/average-metrics-chart.tsx b/app/(landing)/(home)/_ui/average-metrics-container/average-metrics-chart.tsx new file mode 100644 index 00000000..b2b0b155 --- /dev/null +++ b/app/(landing)/(home)/_ui/average-metrics-container/average-metrics-chart.tsx @@ -0,0 +1,185 @@ +'use client' + +import dynamic from 'next/dynamic' + +import Highcharts from 'highcharts' +import mouseWheelZoom from 'highcharts/modules/mouse-wheel-zoom' + +mouseWheelZoom(Highcharts) + +const HighchartsReact = dynamic(() => import('highcharts-react-official'), { + ssr: false, +}) + +export interface AverageMetricsChartDataModel { + dates: string[] + averagePrice: number[] + topSmScore: number[] + topStrategyPrice: number[] +} + +interface Props { + data: AverageMetricsChartDataModel +} + +const AverageMetricsChart = ({ data }: Props) => { + const chartOptions: Highcharts.Options = { + chart: { + type: 'areaspline', + width: 1000, + height: 450, + backgroundColor: '#FFFFFF', + zooming: { + mouseWheel: { + enabled: true, + }, + type: 'x', + }, + }, + title: { text: undefined }, + xAxis: { + categories: data.dates, + labels: { enabled: false }, + gridLineWidth: 0, + tickLength: 0, + lineColor: '#E3E3E3', + startOnTick: true, + endOnTick: true, + tickmarkPlacement: 'on', + }, + yAxis: [ + { + title: { + text: '통합기준가', + style: { + color: '#797979', + fontSize: '10px', + }, + }, + labels: { + style: { + color: '#797979', + fontSize: '10px', + }, + }, + }, + { + title: { + text: '기준가', + style: { + color: '#797979', + fontSize: '10px', + }, + }, + opposite: true, + labels: { + style: { + color: '#797979', + fontSize: '10px', + }, + }, + }, + ], + legend: { + enabled: true, + align: 'left', + verticalAlign: 'top', + layout: 'vertical', + x: 70, + y: -10, + itemStyle: { + color: '#4D4D4D', + fontSize: '12px', + }, + floating: true, + backgroundColor: '#FFFFFF', + borderColor: '#A7A7A7', + borderRadius: 4, + borderWidth: 1, + padding: 16, + }, + tooltip: { + useHTML: true, + headerFormat: '<div style="margin-bottom: 5px;">{point.key}</div>', + pointFormat: '<b>{point.y:.2f}</b>', + footerFormat: '', + borderColor: '#ECECEC', + borderWidth: 1, + shadow: false, + backgroundColor: '#FFFFFF', + style: { + padding: '10px', + }, + }, + + plotOptions: { + series: { + animation: { + duration: 2000, + }, + marker: { + enabled: false, + }, + }, + areaspline: { + fillOpacity: 0.5, + lineWidth: 2, + marker: { + enabled: false, + }, + fillColor: { + linearGradient: { + x1: 0, + y1: 0, + x2: 0, + y2: 1, + }, + stops: [ + [0, '#FF4F1F'], + [1, '#FFFFFF'], + ], + }, + }, + spline: { + lineWidth: 2, + marker: { + enabled: false, + }, + }, + }, + series: [ + { + type: 'areaspline', + name: '평균', + data: data.averagePrice, + color: '#FF4F1F', + yAxis: 0, + stickyTracking: false, + pointPlacement: 'on', + }, + { + type: 'spline', + name: 'SM SCORE 1위', + data: data.topSmScore, + color: '#6877FF', + yAxis: 1, + stickyTracking: false, + pointPlacement: 'on', + }, + { + type: 'spline', + name: '구독 1위', + data: data.topStrategyPrice, + color: '#FFE070', + yAxis: 1, + stickyTracking: false, + pointPlacement: 'on', + }, + ], + credits: { enabled: false }, + } + + return <HighchartsReact highcharts={Highcharts} options={chartOptions} /> +} + +export default AverageMetricsChart From abfdedf255393b9890d3a4860fb3ab7d53097cb0 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 19 Nov 2024 13:38:11 +0900 Subject: [PATCH 181/648] =?UTF-8?q?feat:=20=EB=8C=80=ED=91=9C=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=ED=86=B5=ED=95=A9=20=ED=8F=89=EA=B7=A0=20=EC=A7=80?= =?UTF-8?q?=ED=91=9C=20=EC=B0=A8=ED=8A=B8=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/average-metrics-container/index.tsx | 48 +++++++++++++++++++ .../style.module.scss | 33 +++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 app/(landing)/(home)/_ui/average-metrics-container/index.tsx create mode 100644 app/(landing)/(home)/_ui/average-metrics-container/style.module.scss diff --git a/app/(landing)/(home)/_ui/average-metrics-container/index.tsx b/app/(landing)/(home)/_ui/average-metrics-container/index.tsx new file mode 100644 index 00000000..89e04959 --- /dev/null +++ b/app/(landing)/(home)/_ui/average-metrics-container/index.tsx @@ -0,0 +1,48 @@ +import classNames from 'classnames/bind' + +import AverageMetricsChart, { AverageMetricsChartDataModel } from './average-metrics-chart' +import styles from './style.module.scss' + +const cx = classNames.bind(styles) + +const AverageMetricsContainer = () => { + // 임시 데이터 + const chartData: AverageMetricsChartDataModel = { + dates: [ + 'Jan 1, 2023', + 'Feb 1, 2023', + 'Mar 1, 2023', + 'Apr 1, 2023', + 'May 1, 2023', + 'Jun 1, 2023', + 'Jul 1, 2023', + 'Aug 1, 2023', + 'Sep 1, 2023', + 'Oct 1, 2023', + 'Nov 1, 2023', + 'Dec 1, 2023', + ], + averagePrice: [200, 220, 260, 310, 290, 340, 400, 380, 450, 530, 510, 580], + topSmScore: [240, 270, 320, 330, 350, 420, 450, 470, 550, 530, 600, 680], + topStrategyPrice: [350, 330, 380, 450, 430, 500, 580, 560, 630, 710, 690, 760], + } + + const startDate = chartData.dates[0] + const endDate = chartData.dates.at(-1) + + return ( + <> + <div className={cx('container')}> + <div className={cx('contents-wrapper')}> + <div className={cx('date-wrapper')}> + FROM <span className={cx('date')}>{startDate}</span>TO + <span className={cx('date')}>{endDate}</span> + </div> + <AverageMetricsChart data={chartData} /> + </div> + </div> + </> + ) +} + +export default AverageMetricsContainer diff --git a/app/(landing)/(home)/_ui/average-metrics-container/style.module.scss b/app/(landing)/(home)/_ui/average-metrics-container/style.module.scss new file mode 100644 index 00000000..2cb5392c --- /dev/null +++ b/app/(landing)/(home)/_ui/average-metrics-container/style.module.scss @@ -0,0 +1,33 @@ +.container { + width: 100%; + max-width: 1260px; + margin: 0 auto; + padding: 48px 0 60px; + border-radius: 10px; + background-color: $color-white; +} + +.contents-wrapper { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + width: 100%; + max-width: 1000px; + margin: 0 auto; +} + +.date-wrapper { + align-self: flex-end; + padding-right: 24px; + margin-bottom: 20px; + @include typo-c1; + + .date { + padding: 2px 8px; + margin: 0 6px; + border-radius: 20px; + color: $color-gray-500; + background-color: $color-gray-200; + } +} From cf89ffb77982bee7fa02efd263007bb2d2f915b5 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 19 Nov 2024 13:38:54 +0900 Subject: [PATCH 182/648] =?UTF-8?q?feat:=20=EB=8C=80=ED=91=9C=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=ED=86=B5=ED=95=A9=20=ED=8F=89=EA=B7=A0=20=EC=A7=80?= =?UTF-8?q?=ED=91=9C=20=EC=B0=A8=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=EC=97=90=20=EC=B6=94=EA=B0=80=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../average-metrics.stories.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app/(landing)/(home)/_ui/average-metrics-container/average-metrics.stories.tsx diff --git a/app/(landing)/(home)/_ui/average-metrics-container/average-metrics.stories.tsx b/app/(landing)/(home)/_ui/average-metrics-container/average-metrics.stories.tsx new file mode 100644 index 00000000..aa036746 --- /dev/null +++ b/app/(landing)/(home)/_ui/average-metrics-container/average-metrics.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import AverageMetricsContainer from './index' + +const meta = { + title: 'Components/AverageMetricsChart', + component: AverageMetricsContainer, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta<typeof AverageMetricsContainer> + +type StoryType = StoryObj<typeof meta> + +export const Default: StoryType = {} + +export default meta From c6ca9948ef5ff34d3073edd307aeb02da73d6130 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 13:57:54 +0900 Subject: [PATCH 183/648] =?UTF-8?q?fix:=20Back=20Header=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20label=EB=A1=9C=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/back-header/index.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/shared/ui/header/back-header/index.tsx b/shared/ui/header/back-header/index.tsx index 2b1a5d94..5e72f18a 100644 --- a/shared/ui/header/back-header/index.tsx +++ b/shared/ui/header/back-header/index.tsx @@ -17,20 +17,24 @@ const headerStyles = { backdropFilter: 'blur(60px)', } -const BackHeader = () => { - return <Header Left={<Left />} styles={headerStyles} /> +interface Props { + label: string } -const Left = () => { +const BackHeader = ({ label }: Props) => { + return <Header Left={<Left label={label} />} styles={headerStyles} /> +} + +const Left = ({ label }: Props) => { const router = useRouter() - const onButtonClick = () => { - router?.back() + const onClick = () => { + router.back() } return ( - <button onClick={onButtonClick} className={cx('container')}> + <button onClick={onClick} className={cx('container')}> <ChevronLeftIcon /> - <span>목록으로 돌아가기</span> + <span>{label}</span> </button> ) } From 659b2c425360ce79e96d08826c6ab2b890686cce Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 13:59:30 +0900 Subject: [PATCH 184/648] =?UTF-8?q?fix:=20Header=EA=B0=80=20=EC=9A=94?= =?UTF-8?q?=EC=86=8C=EC=9D=98=20=EC=83=81=EB=8B=A8=EC=97=90=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95=20=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20header-height=20=EB=B3=80=EC=88=98=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_variables.scss | 3 +++ shared/ui/header/header.module.scss | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index 20d1dfc1..bb73983c 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -68,3 +68,6 @@ $z-index: ( /* Width */ $max-width: 1440px; $navigation-width: 90px; + +// height +$header-height: 80px; diff --git a/shared/ui/header/header.module.scss b/shared/ui/header/header.module.scss index 29ec90a9..32b8ddc8 100644 --- a/shared/ui/header/header.module.scss +++ b/shared/ui/header/header.module.scss @@ -1,5 +1,8 @@ .container { - height: 80px; + position: sticky; + top: 0; + height: $header-height; + width: 100%; padding: 0 42px; display: flex; justify-content: space-between; From d4feb295210c47abf70855c117a347c34e97636f Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 14:01:19 +0900 Subject: [PATCH 185/648] =?UTF-8?q?fix:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=EC=97=90=EC=84=9C=20=EC=8A=A4=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=B4=20header=EA=B0=80=20=EC=83=81?= =?UTF-8?q?=EB=8B=A8=EC=97=90=20=EA=B3=A0=EC=A0=95=EB=90=A8=EC=9D=84=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20decorator=20=EC=B6=94=EA=B0=80=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../back-header/back-header.stories.tsx | 22 ++++++++++++++++++- .../logo-header/logo-header.stories.tsx | 22 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/shared/ui/header/back-header/back-header.stories.tsx b/shared/ui/header/back-header/back-header.stories.tsx index cfe1ff5c..20ad48c9 100644 --- a/shared/ui/header/back-header/back-header.stories.tsx +++ b/shared/ui/header/back-header/back-header.stories.tsx @@ -5,13 +5,33 @@ import BackHeader from '.' const meta = { title: 'Components/Header/BackHeader', component: BackHeader, + decorators: [ + (Story) => ( + <div + style={{ + height: '400px', + overflow: 'auto', + border: '1px solid #ddd', + }} + > + <Story /> + <div style={{ marginTop: '80px' }}> + {Array.from({ length: 5 }, (_, idx) => ( + <div key={idx} style={{ height: '100px', marginBottom: '10px', background: '#f4f4f4' }}> + Scrollable Content {idx + 1} + </div> + ))} + </div> + </div> + ), + ], tags: ['autodocs'], } satisfies Meta<typeof BackHeader> export default meta type StoryType = StoryObj<typeof BackHeader> export const Default: StoryType = { - render: () => <BackHeader />, + render: () => <BackHeader label="목록으로 돌아가기" />, parameters: { nextjs: { appDirectory: true, diff --git a/shared/ui/header/logo-header/logo-header.stories.tsx b/shared/ui/header/logo-header/logo-header.stories.tsx index 0cbe7b57..95b887a6 100644 --- a/shared/ui/header/logo-header/logo-header.stories.tsx +++ b/shared/ui/header/logo-header/logo-header.stories.tsx @@ -5,6 +5,26 @@ import LogoHeader from '.' const meta = { title: 'Components/Header/LogoHeader', component: LogoHeader, + decorators: [ + (Story) => ( + <div + style={{ + height: '400px', + overflow: 'auto', + border: '1px solid #ddd', + }} + > + <Story /> + <div style={{ marginTop: '80px' }}> + {Array.from({ length: 5 }, (_, idx) => ( + <div key={idx} style={{ height: '100px', marginBottom: '10px', background: '#f4f4f4' }}> + Scrollable Content {idx + 1} + </div> + ))} + </div> + </div> + ), + ], tags: ['autodocs'], } satisfies Meta<typeof LogoHeader> export default meta @@ -13,7 +33,7 @@ type StoryType = StoryObj<typeof LogoHeader> export const Default: StoryType = { args: { hasLinks: true, - hasText: false, + hasText: true, }, } From 4e44c74f07e230598f1ff6f49c6df8976ad13f11 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 15:22:04 +0900 Subject: [PATCH 186/648] =?UTF-8?q?fix:=20header=20styles=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=AA=85=20=EC=88=98=EC=A0=95=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/back-header/index.tsx | 2 +- .../back-header/{back-header.module.scss => styles.module.scss} | 0 shared/ui/header/index.tsx | 2 +- shared/ui/header/{header.module.scss => styles.module.scss} | 0 shared/ui/logo/index.tsx | 2 +- shared/ui/logo/{logo.module.scss => styles.module.scss} | 0 6 files changed, 3 insertions(+), 3 deletions(-) rename shared/ui/header/back-header/{back-header.module.scss => styles.module.scss} (100%) rename shared/ui/header/{header.module.scss => styles.module.scss} (100%) rename shared/ui/logo/{logo.module.scss => styles.module.scss} (100%) diff --git a/shared/ui/header/back-header/index.tsx b/shared/ui/header/back-header/index.tsx index 5e72f18a..620cfbbd 100644 --- a/shared/ui/header/back-header/index.tsx +++ b/shared/ui/header/back-header/index.tsx @@ -8,7 +8,7 @@ import { ChevronLeftIcon } from '@/public/icons' import classNames from 'classnames/bind' import Header from '..' -import styles from './back-header.module.scss' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/shared/ui/header/back-header/back-header.module.scss b/shared/ui/header/back-header/styles.module.scss similarity index 100% rename from shared/ui/header/back-header/back-header.module.scss rename to shared/ui/header/back-header/styles.module.scss diff --git a/shared/ui/header/index.tsx b/shared/ui/header/index.tsx index 82f70bce..0428d2bb 100644 --- a/shared/ui/header/index.tsx +++ b/shared/ui/header/index.tsx @@ -2,7 +2,7 @@ import { CSSProperties, ReactNode } from 'react' import classNames from 'classnames/bind' -import styles from './header.module.scss' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/shared/ui/header/header.module.scss b/shared/ui/header/styles.module.scss similarity index 100% rename from shared/ui/header/header.module.scss rename to shared/ui/header/styles.module.scss diff --git a/shared/ui/logo/index.tsx b/shared/ui/logo/index.tsx index 81003307..00062944 100644 --- a/shared/ui/logo/index.tsx +++ b/shared/ui/logo/index.tsx @@ -5,7 +5,7 @@ import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' -import styles from './logo.module.scss' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/shared/ui/logo/logo.module.scss b/shared/ui/logo/styles.module.scss similarity index 100% rename from shared/ui/logo/logo.module.scss rename to shared/ui/logo/styles.module.scss From dc5f410f3cbaac7e6a7ebea7b9246cb81f76a115 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 15:32:23 +0900 Subject: [PATCH 187/648] =?UTF-8?q?fix:=20dropdown=20styles=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=AA=85=20=EC=88=98=EC=A0=95=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/index.tsx | 2 +- shared/ui/dropdown/option/range-option.tsx | 2 +- shared/ui/dropdown/{dropdown.module.scss => styles.module.scss} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename shared/ui/dropdown/{dropdown.module.scss => styles.module.scss} (100%) diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index a042f73e..e4be7162 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -5,10 +5,10 @@ import { CSSProperties, ReactNode, createContext } from 'react' import { CheckboxIcon, ChevronDownIcon, ChevronUpIcon } from '@/public/icons' import classNames from 'classnames/bind' -import styles from './dropdown.module.scss' import { useDropdown } from './hooks/use-dropdown' import { useDropdownContext } from './hooks/use-dropdown-context' import RangeOption from './option/range-option' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/shared/ui/dropdown/option/range-option.tsx b/shared/ui/dropdown/option/range-option.tsx index 04e1cd12..8b86cd5d 100644 --- a/shared/ui/dropdown/option/range-option.tsx +++ b/shared/ui/dropdown/option/range-option.tsx @@ -1,7 +1,7 @@ import { useState } from 'react' // import { DropdownContext } from '..' -import styles from '../dropdown.module.scss' +import styles from '../styles.module.scss' const RangeOption = ({ onRangeChange }: { onRangeChange: (min: string, max: string) => void }) => { const [range, setRange] = useState({ min: '', max: '' }) diff --git a/shared/ui/dropdown/dropdown.module.scss b/shared/ui/dropdown/styles.module.scss similarity index 100% rename from shared/ui/dropdown/dropdown.module.scss rename to shared/ui/dropdown/styles.module.scss From e2ec427ba387c3e87ed58c89bde70a6da6f09c54 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 19 Nov 2024 15:39:13 +0900 Subject: [PATCH 188/648] =?UTF-8?q?rename:=20styles.module.scss=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_ui/average-metrics-container/index.tsx | 2 +- .../{style.module.scss => styles.module.scss} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename app/(landing)/(home)/_ui/average-metrics-container/{style.module.scss => styles.module.scss} (100%) diff --git a/app/(landing)/(home)/_ui/average-metrics-container/index.tsx b/app/(landing)/(home)/_ui/average-metrics-container/index.tsx index 89e04959..3f221b29 100644 --- a/app/(landing)/(home)/_ui/average-metrics-container/index.tsx +++ b/app/(landing)/(home)/_ui/average-metrics-container/index.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames/bind' import AverageMetricsChart, { AverageMetricsChartDataModel } from './average-metrics-chart' -import styles from './style.module.scss' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/app/(landing)/(home)/_ui/average-metrics-container/style.module.scss b/app/(landing)/(home)/_ui/average-metrics-container/styles.module.scss similarity index 100% rename from app/(landing)/(home)/_ui/average-metrics-container/style.module.scss rename to app/(landing)/(home)/_ui/average-metrics-container/styles.module.scss From 2b4e5d1e463682de197c5555e204832cd16f0eeb Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 19 Nov 2024 16:21:13 +0900 Subject: [PATCH 189/648] =?UTF-8?q?fix:=20props=EB=AA=85=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=EA=B5=90=EC=A0=95=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/button/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index 0f39f29a..7075e6c2 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -36,12 +36,12 @@ const _Button = ({ </button> ) -interface ButtonGruopProps { +interface ButtonGroupProps { gap?: string children: ReactNode } -const ButtonGroup = ({ gap = '8px', children }: ButtonGruopProps) => { +const ButtonGroup = ({ gap = '8px', children }: ButtonGroupProps) => { return <div style={{ display: 'flex', gap }}>{children}</div> } From 8937899a1dc8c878af567e2f42dbf55184f2e654 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 18:11:10 +0900 Subject: [PATCH 190/648] =?UTF-8?q?feat:=20svg=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=B6=94=EA=B0=80=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/close.svg | 3 +++ public/icons/index.tsx | 1 + public/icons/open.svg | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 public/icons/close.svg diff --git a/public/icons/close.svg b/public/icons/close.svg new file mode 100644 index 00000000..0c4360d2 --- /dev/null +++ b/public/icons/close.svg @@ -0,0 +1,3 @@ +<svg width="15" height="11" viewBox="0 0 15 11" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M0.511719 10.0195L7.48821 1.0013L10.4986 4.99346L14.0117 9.98431" stroke="#797979"/> +</svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index aeb424f9..0c4cc750 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -15,6 +15,7 @@ export { default as MoneyIcon } from './money.svg' export { default as MonthlyGraphIcon } from './monthly-graph.svg' export { default as NoticeIcon } from './notice.svg' export { default as OpenIcon } from './open.svg' +export { default as CloseIcon } from './close.svg' export { default as PencilIcon } from './pencil.svg' export { default as ProfileIcon } from './profile.svg' export { default as QuestionIcon } from './question.svg' diff --git a/public/icons/open.svg b/public/icons/open.svg index 1e60bbe1..1160a20b 100644 --- a/public/icons/open.svg +++ b/public/icons/open.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7689 14.9019L17.9645 7.54883L19.0352 8.45092L11.7493 17.0979L8.31954 12.8766L4.94209 8.42284L6.05761 7.57691L9.4209 12.012L11.7689 14.9019Z" fill="currentColor"/> +<svg width="15" height="11" viewBox="0 0 15 11" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M14 1L7 10L4 6L0.5 1" stroke="#797979"/> </svg> From 639df56a96f81282fbb37986aa13f19750bfa8df Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 18:12:58 +0900 Subject: [PATCH 191/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8,=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/introduction/index.tsx | 58 +++++++++++++++++++ .../_ui/introduction/introduction.stories.tsx | 27 +++++++++ .../_ui/introduction/styles.module.scss | 45 ++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx new file mode 100644 index 00000000..6df8bea7 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx @@ -0,0 +1,58 @@ +'use client' + +import { useEffect, useRef, useState } from 'react' + +import { CloseIcon, OpenIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + content: string +} + +const StrategyIntroduction = ({ content }: Props) => { + const [shouldShowMore, setShouldShowMore] = useState(false) + const [isContentOverflow, setIsContentOverflow] = useState(false) + const contentRef = useRef<HTMLParagraphElement>(null) + + useEffect(() => { + checkOverflow() + }, [content]) + + const checkOverflow = () => { + if (contentRef.current) { + setIsContentOverflow(contentRef.current.scrollHeight > contentRef.current.offsetHeight) + } + } + + return ( + <div className={cx('container')}> + <p className={cx('title')}>전략 상세 소개</p> + <div className={cx('content', { expand: shouldShowMore })}> + <p ref={contentRef}>{content}</p> + </div> + {isContentOverflow && ( + <div className={cx('button-wrapper')}> + <button onClick={() => setShouldShowMore(!shouldShowMore)}> + {shouldShowMore ? ( + <span> + 접기 + <CloseIcon /> + </span> + ) : ( + <span> + 더보기 + <OpenIcon /> + </span> + )} + </button> + </div> + )} + </div> + ) +} + +export default StrategyIntroduction diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx new file mode 100644 index 00000000..14386fef --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx @@ -0,0 +1,27 @@ +import StrategyIntroduction from '@/app/(dashboard)/strategies/[strategyId]/_ui/introduction' +import { Meta, StoryFn } from '@storybook/react' + +const meta: Meta = { + title: 'components/StrategyIntroduction', + component: StrategyIntroduction, + tags: ['autodocs'], +} + +export default meta + +const introduction: StoryFn<{ content: string }> = ({ content }) => ( + <div style={{ padding: '20px', backgroundColor: '#fafafa' }}> + <StrategyIntroduction content={content} /> + </div> +) + +export const Default = introduction.bind({}) +Default.args = { + content: '안녕하세요. 전랙에 대한 설명입니다.', +} + +export const MaxContent = introduction.bind({}) +MaxContent.args = { + content: + '전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 안녕하세요. 안녕하세요. 안녕하세요..전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 안녕하세요. 안녕하세요.', +} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss new file mode 100644 index 00000000..8bf35364 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss @@ -0,0 +1,45 @@ +.container { + width: 100%; + background-color: $color-white; + border-radius: 5px; + padding: 20px; + .title { + @include typo-b2; + margin-bottom: 15px; + } + + .content { + width: 100%; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + line-clamp: 4; + -webkit-line-clamp: 4; + &.expand { + display: contents; + } + p { + @include typo-c1; + line-height: 18px; + color: $color-gray-600; + } + } + + .button-wrapper { + width: 100%; + display: flex; + justify-content: flex-end; + margin-top: 10px; + button { + padding: 2px; + color: $color-gray-500; + background-color: transparent; + svg { + margin-left: 10px; + } + span { + @include typo-c1; + } + } + } +} From d49625d3be512dfab0b3e09974d827f8a1c206ec Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 18:54:58 +0900 Subject: [PATCH 192/648] =?UTF-8?q?feat:=20svg=EC=95=84=EC=9D=B4=EC=BD=98,?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=95=84=EC=9D=B4=EC=BD=98=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_ui/introduction/index.tsx | 8 ++++---- .../[strategyId]/_ui/introduction/styles.module.scss | 9 ++++----- public/icons/close.svg | 4 ++-- public/icons/open.svg | 4 ++-- shared/styles/stories/icons.stories.tsx | 2 ++ 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx index 6df8bea7..239ab22f 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx @@ -38,15 +38,15 @@ const StrategyIntroduction = ({ content }: Props) => { <div className={cx('button-wrapper')}> <button onClick={() => setShouldShowMore(!shouldShowMore)}> {shouldShowMore ? ( - <span> + <> 접기 <CloseIcon /> - </span> + </> ) : ( - <span> + <> 더보기 <OpenIcon /> - </span> + </> )} </button> </div> diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss index 8bf35364..c652a5c3 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss @@ -31,14 +31,13 @@ justify-content: flex-end; margin-top: 10px; button { - padding: 2px; + @include typo-c1; + display: flex; + align-items: center; color: $color-gray-500; background-color: transparent; svg { - margin-left: 10px; - } - span { - @include typo-c1; + margin: -3px 0 0 7px; } } } diff --git a/public/icons/close.svg b/public/icons/close.svg index 0c4360d2..40d24485 100644 --- a/public/icons/close.svg +++ b/public/icons/close.svg @@ -1,3 +1,3 @@ -<svg width="15" height="11" viewBox="0 0 15 11" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M0.511719 10.0195L7.48821 1.0013L10.4986 4.99346L14.0117 9.98431" stroke="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2311 9.10008L6.03546 16.4531L4.96484 15.551L12.2507 6.90408L15.6805 11.1253L19.0579 15.5791L17.9424 16.425L14.5791 11.9899L12.2311 9.10008Z" fill="#797979"/> </svg> diff --git a/public/icons/open.svg b/public/icons/open.svg index 1160a20b..850afa8d 100644 --- a/public/icons/open.svg +++ b/public/icons/open.svg @@ -1,3 +1,3 @@ -<svg width="15" height="11" viewBox="0 0 15 11" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M14 1L7 10L4 6L0.5 1" stroke="#797979"/> +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7689 14.8999L17.9645 7.54688L19.0352 8.44897L11.7493 17.0959L8.31954 12.8747L4.94209 8.42089L6.05761 7.57495L9.4209 12.0101L11.7689 14.8999Z" fill="#797979"/> </svg> diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index 8e9d5a93..cc6dc7bc 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -10,6 +10,7 @@ import { ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, + CloseIcon, DailyGraphIcon, FileIcon, MoneyIcon, @@ -51,6 +52,7 @@ const icons = [ { name: 'MonthlyGraphIcon', icon: MonthlyGraphIcon }, { name: 'NoticeIcon', icon: NoticeIcon }, { name: 'OpenIcon', icon: OpenIcon }, + { name: 'CloseIcon', icon: CloseIcon }, { name: 'PencilIcon', icon: PencilIcon }, { name: 'ProfileIcon', icon: ProfileIcon }, { name: 'QuestionIcon', icon: QuestionIcon }, From 0c2483924ad2308f042cc0c4959263f8c0984f2a Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 18:59:48 +0900 Subject: [PATCH 193/648] =?UTF-8?q?rename:=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_ui/introduction/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx index 239ab22f..b9ee51cd 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/index.tsx @@ -15,7 +15,7 @@ interface Props { const StrategyIntroduction = ({ content }: Props) => { const [shouldShowMore, setShouldShowMore] = useState(false) - const [isContentOverflow, setIsContentOverflow] = useState(false) + const [isOverflow, setIsOverflow] = useState(false) const contentRef = useRef<HTMLParagraphElement>(null) useEffect(() => { @@ -24,7 +24,7 @@ const StrategyIntroduction = ({ content }: Props) => { const checkOverflow = () => { if (contentRef.current) { - setIsContentOverflow(contentRef.current.scrollHeight > contentRef.current.offsetHeight) + setIsOverflow(contentRef.current.scrollHeight > contentRef.current.offsetHeight) } } @@ -34,7 +34,7 @@ const StrategyIntroduction = ({ content }: Props) => { <div className={cx('content', { expand: shouldShowMore })}> <p ref={contentRef}>{content}</p> </div> - {isContentOverflow && ( + {isOverflow && ( <div className={cx('button-wrapper')}> <button onClick={() => setShouldShowMore(!shouldShowMore)}> {shouldShowMore ? ( From b257ca16c1588e812a38a1ca7c76a8fd6286aef6 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 19 Nov 2024 20:34:34 +0900 Subject: [PATCH 194/648] =?UTF-8?q?feat:=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81-=20=EC=A0=84=EB=9E=B5=EC=86=8C=EA=B0=9C=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=EB=B6=80=EB=AA=A8=20?= =?UTF-8?q?=EC=9A=94=EC=86=8C=EC=97=90=20width=EC=A7=80=EC=A0=95=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/introduction/introduction.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx index 14386fef..1836ccfa 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/introduction.stories.tsx @@ -10,7 +10,7 @@ const meta: Meta = { export default meta const introduction: StoryFn<{ content: string }> = ({ content }) => ( - <div style={{ padding: '20px', backgroundColor: '#fafafa' }}> + <div style={{ width: '900px', padding: '20px', backgroundColor: '#fafafa' }}> <StrategyIntroduction content={content} /> </div> ) @@ -23,5 +23,5 @@ Default.args = { export const MaxContent = introduction.bind({}) MaxContent.args = { content: - '전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 안녕하세요. 안녕하세요. 안녕하세요..전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 안녕하세요. 안녕하세요.', + '전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 안녕하세요. 안녕하세요. 안녕하세요..전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 전략에 대한 상세한 설명을 입력해주세요. 안녕하세요. 안녕하세요.', } From 72d238b1ad0f5288e1bab1a8ad5a8e1d77f23b0c Mon Sep 17 00:00:00 2001 From: SuMin <ksm9805@naver.com> Date: Tue, 19 Nov 2024 21:55:17 +0900 Subject: [PATCH 195/648] =?UTF-8?q?Revert=20"[Feat]=20=EC=A0=84=EB=9E=B5?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=82=AC=EC=9D=B4=EB=93=9C=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8,=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details-side-item.stories.tsx | 47 ----------------- .../_ui/details-side-item/index.tsx | 50 ------------------- .../_ui/details-side-item/styles.module.scss | 40 --------------- 3 files changed, 137 deletions(-) delete mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx delete mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx delete mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx deleted file mode 100644 index b1563431..00000000 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import DetailsSideItem, { - TitleType, -} from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' -import type { Meta, StoryFn } from '@storybook/react' - -const meta: Meta<typeof DetailsSideItem> = { - title: 'components/DetailsSideItem', - component: DetailsSideItem, - tags: ['autodocs'], -} - -const sideItem: StoryFn<{ data: { title: TitleType; data: string | number }[] }> = ({ data }) => ( - <div style={{ width: '100%', height: '100%', background: '#fafafa', padding: '20px' }}> - {data.map((item, idx) => ( - <DetailsSideItem - key={item.title} - title={item.title} - data={item.data} - hasGap={idx === 3 || idx === 5 ? false : true} - /> - ))} - </div> -) -export const Default = sideItem.bind({}) -Default.args = { - data: [{ title: '투자 원금', data: '10,000,000' }], -} - -export const Trader = sideItem.bind({}) -Trader.args = { - data: [{ title: '트레이더', data: '수밍' }], -} - -export const LayoutExample = sideItem.bind({}) -LayoutExample.args = { - data: [ - { title: '트레이더', data: '수밍' }, - { title: '최소 투자 금액', data: '1억 ~ 2억' }, - { title: '투자 원금', data: '10,000,000' }, - { title: 'KP Ratio', data: 0.3993 }, - { title: 'SM SCORE', data: 67.38 }, - { title: '최종손익입력일자', data: '2016.04.30' }, - { title: '등록일', data: '2016.02.30' }, - ], -} - -export default meta diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx deleted file mode 100644 index b7ec9602..00000000 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import classNames from 'classnames/bind' - -import { PATH } from '@/shared/constants/path' -import Avatar from '@/shared/ui/avatar' -import { LinkButton } from '@/shared/ui/link-button' - -import styles from './styles.module.scss' - -const cx = classNames.bind(styles) - -export type TitleType = - | '트레이더' - | '최소 투자 금액' - | '투자 원금' - | 'KP Ratio' - | 'SM SCORE' - | '최종손익입력일자' - | '등록일' - -interface Props { - title: TitleType - data: string | number - imageUrl?: string - hasGap?: boolean -} - -const DetailsSideItem = ({ title, data, imageUrl, hasGap = true }: Props) => { - return ( - <div className={cx('side-item', hasGap && 'gap')}> - <div className={cx('title')}>{title}</div> - <div className={cx('data')}> - {title === '트레이더' ? ( - <> - <div className={cx('avatar')}> - <Avatar src={imageUrl} /> - <p>{data}</p> - </div> - <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> - 문의하기 - </LinkButton> - </> - ) : ( - <p>{data}</p> - )} - </div> - </div> - ) -} - -export default DetailsSideItem diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss deleted file mode 100644 index 879b5812..00000000 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss +++ /dev/null @@ -1,40 +0,0 @@ -.side-item { - width: 276px; - height: 100px; - background-color: $color-white; - padding: 20px 20px 0; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - - &.gap { - height: 120px; - margin-bottom: 20px; - border-radius: 5px; - padding: 20px; - } - - .title { - font-weight: $text-bold; - font-size: $text-b2; - color: $color-gray-500; - border-bottom: 0.75px solid $color-gray-500; - padding-bottom: 12px; - } - - .data { - display: flex; - justify-content: space-between; - align-items: center; - padding-top: 12px; - .avatar { - display: flex; - align-items: center; - p { - margin-left: 11px; - } - } - p { - @include typo-b1; - } - } -} From 28d9673de070da34730acf22488fab92bec4a3ee Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 20 Nov 2024 01:26:27 +0900 Subject: [PATCH 196/648] =?UTF-8?q?refactor:=20dropdown=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20controlled=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/hooks/use-dropdown.ts | 43 ++++++++++-------------- shared/ui/dropdown/index.tsx | 23 +++++-------- shared/ui/dropdown/types.ts | 22 ++++++++++++ 3 files changed, 48 insertions(+), 40 deletions(-) create mode 100644 shared/ui/dropdown/types.ts diff --git a/shared/ui/dropdown/hooks/use-dropdown.ts b/shared/ui/dropdown/hooks/use-dropdown.ts index d8474240..7a931699 100644 --- a/shared/ui/dropdown/hooks/use-dropdown.ts +++ b/shared/ui/dropdown/hooks/use-dropdown.ts @@ -1,39 +1,32 @@ import { useEffect, useRef, useState } from 'react' -interface UseDropdownProps { - onChange?: (value: string | string[]) => void +import { DropdownStateModel, DropdownValueType } from '../types' + +interface UseDropdownProps extends DropdownStateModel { isMultiple?: boolean } -export const useDropdown = ({ onChange, isMultiple = false }: UseDropdownProps) => { +export const useDropdown = ({ isMultiple = false, value, onChange }: UseDropdownProps) => { + if (isMultiple && typeof value === 'string') { + throw new Error('multiple 옵션은 객체 value와 사용해야합니다.') + } + const [isOpen, setIsOpen] = useState(false) - const [selectedValues, setSelectedValues] = useState<string[]>([]) const dropdownRef = useRef<HTMLDivElement>(null) const toggleOpen = () => setIsOpen((prev) => !prev) - const handleSelect = (value: string) => { - if (isMultiple) { - // 다중 선택 모드 - setSelectedValues((prev) => { - const result = prev.includes(value) - ? prev.filter((item) => item !== value) - : [...prev, value] - onChange?.(result) - return result - }) - } else { - // 단일 선택 모드 - switchValue(value) + const handleSelect = (selectedValue: string) => { + let result: DropdownValueType = selectedValue + + if (isMultiple && Array.isArray(value)) { + result = value.includes(selectedValue) + ? value.filter((v) => v !== selectedValue) + : [...value, selectedValue] } - } - const switchValue = (value: string) => { - setSelectedValues((prev) => { - const result = prev[0] === value ? [] : [value] - onChange?.(result) - return result - }) + onChange?.(result) + if (!isMultiple) setIsOpen(false) } const handleClickOutside = (event: MouseEvent) => { @@ -52,9 +45,7 @@ export const useDropdown = ({ onChange, isMultiple = false }: UseDropdownProps) return { isOpen, toggleOpen, - selectedValues, handleSelect, - switchValue, dropdownRef, } } diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index e4be7162..cb196d8e 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -9,13 +9,13 @@ import { useDropdown } from './hooks/use-dropdown' import { useDropdownContext } from './hooks/use-dropdown-context' import RangeOption from './option/range-option' import styles from './styles.module.scss' +import { DropdownItemProps, DropdownValueType } from './types' const cx = classNames.bind(styles) interface DropdownContextProps { isOpen: boolean toggleOpen: () => void - selectedValues: string[] handleSelect: (value: string) => void } @@ -30,7 +30,8 @@ export const DropdownContext = createContext<DropdownContextProps>(defaultContex export interface DropdownProps { Trigger: ReactNode - onChange?: (value: string | string[]) => void + value: DropdownValueType + onChange: (value: DropdownValueType) => void isMultiple: boolean containerStyle?: CSSProperties labelStyle?: CSSProperties @@ -39,19 +40,21 @@ export interface DropdownProps { const Dropdown = ({ Trigger, + value, onChange, isMultiple = false, containerStyle, labelStyle, children, }: DropdownProps) => { - const { isOpen, toggleOpen, selectedValues, handleSelect, dropdownRef } = useDropdown({ + const { isOpen, toggleOpen, handleSelect, dropdownRef } = useDropdown({ + value, onChange, isMultiple, }) return ( - <DropdownContext.Provider value={{ isOpen, toggleOpen, selectedValues, handleSelect }}> + <DropdownContext.Provider value={{ isOpen, toggleOpen, handleSelect }}> <div style={containerStyle} ref={dropdownRef}> <button onClick={toggleOpen} className={cx('container', 'trigger')} style={labelStyle}> {Trigger} @@ -62,19 +65,11 @@ const Dropdown = ({ </DropdownContext.Provider> ) } -export interface ItemProps { - value: string - label: string - hasCheck?: boolean - isMultiple?: boolean - style?: CSSProperties -} -const Item = ({ value, label, hasCheck = false, style }: ItemProps) => { +const Item = ({ isSelected, value, label, hasCheck = false, style }: DropdownItemProps) => { const context = useDropdownContext() - const { handleSelect, selectedValues } = context - const isSelected = selectedValues.includes(value) + const { handleSelect } = context return ( <li diff --git a/shared/ui/dropdown/types.ts b/shared/ui/dropdown/types.ts new file mode 100644 index 00000000..e8cf0e7a --- /dev/null +++ b/shared/ui/dropdown/types.ts @@ -0,0 +1,22 @@ +import { CSSProperties } from 'react' + +export interface DropdownOptionModel { + value: string + label: string +} + +export type DropdownValueType = string | string[] | null + +export interface DropdownItemProps { + value: string + label: string + isSelected: boolean + hasCheck?: boolean + isMultiple?: boolean + style?: CSSProperties +} + +export interface DropdownStateModel { + value: DropdownValueType + onChange?: (value: DropdownValueType) => void +} From 01c45d9edcb85bbb83a785ac1b27c6c5af809420 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 20 Nov 2024 01:27:11 +0900 Subject: [PATCH 197/648] =?UTF-8?q?refactor:=20select=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A5=BC=20controlled=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/select/hooks/useSelect.ts | 14 ++++++++++ shared/ui/select/index.tsx | 40 ++++++++++++++++------------- 2 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 shared/ui/select/hooks/useSelect.ts diff --git a/shared/ui/select/hooks/useSelect.ts b/shared/ui/select/hooks/useSelect.ts new file mode 100644 index 00000000..130133cf --- /dev/null +++ b/shared/ui/select/hooks/useSelect.ts @@ -0,0 +1,14 @@ +import { DropdownOptionModel, DropdownValueType } from '../../dropdown/types' + +export const useSelect = () => { + const isSelected = (value: DropdownValueType, itemValue: string) => { + if (Array.isArray(value)) return value.includes(itemValue) + return value === itemValue + } + + const findLabel = (options: DropdownOptionModel[], value: DropdownValueType) => { + if (Array.isArray(value)) return + return options.find((option) => option.value === value)?.label + } + return { isSelected, findLabel } +} diff --git a/shared/ui/select/index.tsx b/shared/ui/select/index.tsx index 7eff3941..8c1666f0 100644 --- a/shared/ui/select/index.tsx +++ b/shared/ui/select/index.tsx @@ -1,21 +1,20 @@ 'use client' +// TODO: muliple 일 때는 상단에 placeholder를 그대로 둘지 고민 import { CSSProperties } from 'react' -import Dropdown, { DropdownProps, ItemProps } from '../dropdown' +import Dropdown, { DropdownProps } from '../dropdown' +import { DropdownItemProps, DropdownOptionModel, DropdownValueType } from '../dropdown/types' +import { useSelect } from './hooks/useSelect' -interface OptionModel { - value: string - label: string -} - -type SelectType = Pick<DropdownProps, 'isMultiple' | 'containerStyle' | 'labelStyle'> & - Pick<ItemProps, 'hasCheck'> & { - title?: string +export type SelectType = Pick<DropdownProps, 'isMultiple' | 'containerStyle' | 'labelStyle'> & + Pick<DropdownItemProps, 'hasCheck'> & { + placeholder?: string + value: DropdownValueType + onChange: (value: DropdownValueType) => void isRounded?: boolean titleStyle?: CSSProperties - onChange?: (value: string | string[]) => void - options: OptionModel[] + options: DropdownOptionModel[] } const Select = ({ @@ -24,30 +23,35 @@ const Select = ({ hasCheck = false, containerStyle, titleStyle, + placeholder, + value, onChange, options, - title = options[0].label, }: SelectType) => { + const { isSelected, findLabel } = useSelect() + if (!options.length) return null const roundStyle = { borderRadius: '40px', overflow: 'hidden' } - const labelStyle = isRounded ? { ...roundStyle, ...titleStyle } : titleStyle + const placeholderStyle = isRounded ? { ...roundStyle, ...titleStyle } : titleStyle return ( <Dropdown - Trigger={<span>{title}</span>} + Trigger={<span>{findLabel(options, value) ?? placeholder ?? options[0].label}</span>} isMultiple={isMultiple} + value={value} onChange={onChange} containerStyle={containerStyle} - labelStyle={labelStyle} + labelStyle={placeholderStyle} > - {options.map(({ value, label }) => ( + {options.map(({ value: itemValue, label }) => ( <Dropdown.Item - value={value} + value={itemValue} label={label} + isSelected={isSelected(value, itemValue)} isMultiple={isMultiple} hasCheck={hasCheck} - key={label} + key={placeholder} /> ))} </Dropdown> From 0635e8b800f2d6b47e1a206c1a54b75073c9d23d Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 20 Nov 2024 01:27:51 +0900 Subject: [PATCH 198/648] =?UTF-8?q?fix:=20select=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=88=98=EC=A0=95=20=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/select/select.stories.tsx | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/shared/ui/select/select.stories.tsx b/shared/ui/select/select.stories.tsx index e59c296a..e7b64e86 100644 --- a/shared/ui/select/select.stories.tsx +++ b/shared/ui/select/select.stories.tsx @@ -1,6 +1,9 @@ +import { useState } from 'react' + import type { Meta, StoryObj } from '@storybook/react' -import Select from '.' +import Select, { SelectType } from '.' +import { DropdownValueType } from '../dropdown/types' const meta = { title: 'Components/Select', @@ -37,25 +40,35 @@ const options = [ }, ] +const ControlledSelect = (args: SelectType) => { + let initialValue = null + const { isMultiple } = args + if (isMultiple) initialValue = [] + const [value, setValue] = useState<DropdownValueType>(initialValue) + return <Select {...args} value={value} onChange={(newValue) => setValue(newValue)} /> +} + export const Default: StoryType = { + render: (args) => <ControlledSelect {...args} />, args: { - title: '수익률', + placeholder: '수익률', isRounded: false, isMultiple: false, hasCheck: false, - onChange: () => {}, options, }, } export const Rounded: StoryType = { + render: (args) => <ControlledSelect {...args} />, args: { ...Default.args, isRounded: true, }, } -export const Muliple: StoryType = { +export const Multiple: StoryType = { + render: (args) => <ControlledSelect {...args} />, args: { ...Default.args, isMultiple: true, @@ -63,6 +76,7 @@ export const Muliple: StoryType = { } export const WithCheck: StoryType = { + render: (args) => <ControlledSelect {...args} />, args: { ...Default.args, hasCheck: true, From f167b3ce92566b4758f3a156e16abd0637fd02f7 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Wed, 20 Nov 2024 01:54:34 +0900 Subject: [PATCH 199/648] =?UTF-8?q?refactor:=20Modal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20props=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/index.tsx | 6 +++--- shared/ui/modal/modal.stories.tsx | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index dfa959f6..ad1b01b4 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -12,7 +12,7 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - title?: string + contents?: string icon?: ReactNode isOpen: boolean hasTwoButtons?: boolean @@ -21,7 +21,7 @@ interface Props { } const Modal = ({ - title, + contents, icon, isOpen, hasTwoButtons = false, @@ -40,7 +40,7 @@ const Modal = ({ <div className={cx('modal', { 'has-two-buttons': hasTwoButtons })}> <CloseIcon className={cx('close-icon')} onClick={closeModal}></CloseIcon> <div className={cx('icon')}>{icon}</div> - <p className={cx('title')}>{title}</p> + <p className={cx('title')}>{contents}</p> {hasTwoButtons ? ( <div className={cx('two-buttons')}> diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index b4c2d1f9..b2a68c87 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -18,11 +18,11 @@ export default meta type StoryType = StoryObj<typeof Modal> const ModalStory = ({ - title, + contents, icon, hasTwoButtons, }: { - title?: string + contents?: string icon?: React.ReactNode hasTwoButtons?: boolean }) => { @@ -40,7 +40,7 @@ const ModalStory = ({ <div style={{ padding: '20px' }}> <Button onClick={() => setIsOpen(true)}>모달 열기</Button> <Modal - title={title} + contents={contents} icon={icon} isOpen={isOpen} hasTwoButtons={hasTwoButtons} @@ -55,27 +55,27 @@ const ModalStory = ({ } export const Default: StoryType = { - render: () => <ModalStory title="기본 모달 제목" />, + render: () => <ModalStory contents="기본 모달 제목" />, } export const TwoButtons: StoryType = { - render: () => <ModalStory title="정말 삭제하시겠습니까?" hasTwoButtons={true} />, + render: () => <ModalStory contents="정말 삭제하시겠습니까?" hasTwoButtons={true} />, } export const WithIcon: StoryType = { - render: () => <ModalStory title="알림이 있습니다." icon={<ModalAlertIcon />} />, + render: () => <ModalStory contents="알림이 있습니다." icon={<ModalAlertIcon />} />, } export const LongText: StoryType = { render: () => ( - <ModalStory title="이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다." /> + <ModalStory contents="이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다." /> ), } export const FullFeatured: StoryType = { render: () => ( <ModalStory - title="모든 기능이 포함된 모달입니다. 확인해주세요." + contents="모든 기능이 포함된 모달입니다. 확인해주세요." icon={<ModalAlertIcon />} hasTwoButtons={true} /> From 31d17bd12fe77e0ab5382a22289700552ee2ce24 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Wed, 20 Nov 2024 08:21:43 +0900 Subject: [PATCH 200/648] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1=20(#7?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/traders-list-card/index.tsx | 45 ++++++++++++++++ .../ui/traders-list-card/styles.module.scss | 52 +++++++++++++++++++ .../traders-list-card.stories.tsx | 34 ++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 shared/ui/traders-list-card/index.tsx create mode 100644 shared/ui/traders-list-card/styles.module.scss create mode 100644 shared/ui/traders-list-card/traders-list-card.stories.tsx diff --git a/shared/ui/traders-list-card/index.tsx b/shared/ui/traders-list-card/index.tsx new file mode 100644 index 00000000..c58423fc --- /dev/null +++ b/shared/ui/traders-list-card/index.tsx @@ -0,0 +1,45 @@ +import classNames from 'classnames/bind' + +import Avatar from '@/shared/ui/avatar' +import { LinkButton } from '@/shared/ui/link-button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + name: string + profileImage?: string + strategy: number + subscribe: number + traderId: string +} + +const TradersListCard = ({ name, profileImage, strategy, subscribe }: Props) => { + return ( + <div className={cx('traders-list-card')}> + <div className={cx('contents')}> + <div className={cx('profile-name')}>{name}</div> + <div className={cx('avatar')}> + <Avatar src={profileImage} size="large" /> + </div> + <div className={cx('information')}> + <div>전략 {strategy}개</div> + <div>구독 {subscribe}개</div> + </div> + </div> + <div className="link-button-wrapper"> + <LinkButton + href={'${PATH.TRADERS}/${traderId}'} + size="medium" + className={cx('link-button')} + style={{ color: '#ff5f33' }} + > + 전략 목록 상세보기 + </LinkButton> + </div> + </div> + ) +} + +export default TradersListCard diff --git a/shared/ui/traders-list-card/styles.module.scss b/shared/ui/traders-list-card/styles.module.scss new file mode 100644 index 00000000..7617437a --- /dev/null +++ b/shared/ui/traders-list-card/styles.module.scss @@ -0,0 +1,52 @@ +.traders-list-card { + background-color: $color-white; + border-radius: 8px; + width: 300px; + height: 220px; + padding: 28px 25px; + display: flex; + flex-direction: column; + position: relative; +} + +.contents { + margin-bottom: 35px; + display: flex; + flex-direction: column; + position: relative; +} + +.profile-name { + @include typo-h3; + font-weight: $text-semibold; + position: absolute; + margin-top: 5px; +} + +.avatar { + position: absolute; + right: 0px; +} + +.information { + @include typo-b3; + color: $color-gray-500; + font-weight: $text-semibold; + margin-top: 45px; + display: flex; + flex-direction: column; +} + +.link-button-wrapper { + display: flex; + justify-content: center; + width: 100%; + margin-top: auto; +} + +.link-button { + align-self: center; + text-align: center; + border-color: $color-orange-600; + width: 100%; +} diff --git a/shared/ui/traders-list-card/traders-list-card.stories.tsx b/shared/ui/traders-list-card/traders-list-card.stories.tsx new file mode 100644 index 00000000..0dbf0a84 --- /dev/null +++ b/shared/ui/traders-list-card/traders-list-card.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import TradersListCard from './index' + +const meta = { + title: 'Components/TradersListCard', + component: TradersListCard, + parameters: { + layout: 'centered', + backgrounds: { + default: 'dark', + values: [ + { + name: 'dark', + value: '#222222', + }, + ], + }, + }, + tags: ['autodocs'], +} satisfies Meta<typeof TradersListCard> + +export default meta +type StoryType = StoryObj<typeof meta> + +export const Default: StoryType = { + args: { + name: '고양이', + profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', + strategy: 10, + subscribe: 10, + traderId: '1234', + }, +} From d2e1b4e4ed845ba665ed997bfeee960f24823600 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 20 Nov 2024 10:16:30 +0900 Subject: [PATCH 201/648] =?UTF-8?q?rename:=20=EA=B8=B0=EC=A1=B4=20imageUrl?= =?UTF-8?q?,=20profileImage=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_ui/details-side-item/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx index b7ec9602..9e7fc96e 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx @@ -20,11 +20,11 @@ export type TitleType = interface Props { title: TitleType data: string | number - imageUrl?: string + profileImage?: string hasGap?: boolean } -const DetailsSideItem = ({ title, data, imageUrl, hasGap = true }: Props) => { +const DetailsSideItem = ({ title, data, profileImage, hasGap = true }: Props) => { return ( <div className={cx('side-item', hasGap && 'gap')}> <div className={cx('title')}>{title}</div> @@ -32,7 +32,7 @@ const DetailsSideItem = ({ title, data, imageUrl, hasGap = true }: Props) => { {title === '트레이더' ? ( <> <div className={cx('avatar')}> - <Avatar src={imageUrl} /> + <Avatar src={profileImage} /> <p>{data}</p> </div> <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> From 86fb083f2f829dc1913bd3e79801a7791fcba33a Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 20 Nov 2024 13:30:43 +0900 Subject: [PATCH 202/648] =?UTF-8?q?feat:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20=EB=B0=8F=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=88=98=EC=A0=95=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details-side-item.stories.tsx | 52 +++++++++++-------- .../_ui/details-side-item/index.tsx | 47 ++++++++--------- .../_ui/details-side-item/side-item.tsx | 41 +++++++++++++++ .../_ui/details-side-item/styles.module.scss | 44 +++++++++------- .../strategies/[strategyId]/page.tsx | 24 ++++++++- 5 files changed, 142 insertions(+), 66 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/side-item.tsx diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx index b1563431..c4546f04 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx @@ -1,5 +1,5 @@ import DetailsSideItem, { - TitleType, + InformationModel, } from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' import type { Meta, StoryFn } from '@storybook/react' @@ -7,40 +7,48 @@ const meta: Meta<typeof DetailsSideItem> = { title: 'components/DetailsSideItem', component: DetailsSideItem, tags: ['autodocs'], + argTypes: { + information: { + title: { + control: 'select', + options: [ + '트레이더', + '최소 투자 금액', + '투자 원금', + 'KP Ratio', + 'SM SCORE', + '최종손익입력일자', + '등록일', + ], + }, + data: { + control: 'text', + }, + }, + }, } -const sideItem: StoryFn<{ data: { title: TitleType; data: string | number }[] }> = ({ data }) => ( +const sideItems: StoryFn<{ information: InformationModel | InformationModel[] }> = (args) => ( <div style={{ width: '100%', height: '100%', background: '#fafafa', padding: '20px' }}> - {data.map((item, idx) => ( - <DetailsSideItem - key={item.title} - title={item.title} - data={item.data} - hasGap={idx === 3 || idx === 5 ? false : true} - /> - ))} + <DetailsSideItem {...args} /> </div> ) -export const Default = sideItem.bind({}) + +export const Default = sideItems.bind({}) Default.args = { - data: [{ title: '투자 원금', data: '10,000,000' }], + information: { title: '투자 원금', data: '10,000,000' }, } -export const Trader = sideItem.bind({}) +export const Trader = sideItems.bind({}) Trader.args = { - data: [{ title: '트레이더', data: '수밍' }], + information: { title: '트레이더', data: '수밍' }, } -export const LayoutExample = sideItem.bind({}) -LayoutExample.args = { - data: [ - { title: '트레이더', data: '수밍' }, - { title: '최소 투자 금액', data: '1억 ~ 2억' }, - { title: '투자 원금', data: '10,000,000' }, +export const Multiple = sideItems.bind({}) +Multiple.args = { + information: [ { title: 'KP Ratio', data: 0.3993 }, { title: 'SM SCORE', data: 67.38 }, - { title: '최종손익입력일자', data: '2016.04.30' }, - { title: '등록일', data: '2016.02.30' }, ], } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx index 9e7fc96e..4686bc23 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx @@ -1,9 +1,6 @@ +import SideItem from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/side-item' import classNames from 'classnames/bind' -import { PATH } from '@/shared/constants/path' -import Avatar from '@/shared/ui/avatar' -import { LinkButton } from '@/shared/ui/link-button' - import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -17,33 +14,35 @@ export type TitleType = | '최종손익입력일자' | '등록일' -interface Props { +export interface InformationModel { title: TitleType data: string | number +} + +interface Props { + information: InformationModel | InformationModel[] profileImage?: string - hasGap?: boolean } -const DetailsSideItem = ({ title, data, profileImage, hasGap = true }: Props) => { +const DetailsSideItem = ({ information, profileImage }: Props) => { + const isArray = Array.isArray(information) return ( - <div className={cx('side-item', hasGap && 'gap')}> - <div className={cx('title')}>{title}</div> - <div className={cx('data')}> - {title === '트레이더' ? ( - <> - <div className={cx('avatar')}> - <Avatar src={profileImage} /> - <p>{data}</p> + <> + {isArray ? ( + <div className={cx('side-items')}> + {information.map((item) => ( + <div key={item.title}> + <div className={cx('title')}>{item.title}</div> + <div className={cx('data')}> + <p>{item.data}</p> + </div> </div> - <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> - 문의하기 - </LinkButton> - </> - ) : ( - <p>{data}</p> - )} - </div> - </div> + ))} + </div> + ) : ( + <SideItem title={information.title} data={information.data} profileImage={profileImage} /> + )} + </> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/side-item.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/side-item.tsx new file mode 100644 index 00000000..427f24f3 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/side-item.tsx @@ -0,0 +1,41 @@ +import { TitleType } from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import Avatar from '@/shared/ui/avatar' +import { LinkButton } from '@/shared/ui/link-button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + title: Omit<TitleType, '트레이더' | '최소 투자 금액' | '투자 원금'> + data: number | string + profileImage?: string +} + +const SideItem = ({ title, data, profileImage }: Props) => { + return ( + <div className={cx('side-item')}> + <div className={cx('title')}>{title}</div> + <div className={cx('data')}> + {title === '트레이더' ? ( + <> + <div className={cx('avatar')}> + <Avatar src={profileImage} /> + <p>{data}</p> + </div> + <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> + 문의하기 + </LinkButton> + </> + ) : ( + <p>{data}</p> + )} + </div> + </div> + ) +} + +export default SideItem diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss index 879b5812..9c126b9b 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/styles.module.scss @@ -1,18 +1,24 @@ .side-item { - width: 276px; - height: 100px; - background-color: $color-white; - padding: 20px 20px 0; - border-top-left-radius: 5px; - border-top-right-radius: 5px; + height: 120px; + padding: 20px; +} - &.gap { - height: 120px; - margin-bottom: 20px; - border-radius: 5px; - padding: 20px; +.side-items { + height: 210px; + padding: 20px 20px 0; + .data { + p { + margin-bottom: 20px; + } } +} +.side-item, +.side-items { + width: 276px; + background-color: $color-white; + border-radius: 5px; + margin-bottom: 20px; .title { font-weight: $text-bold; font-size: $text-b2; @@ -20,21 +26,21 @@ border-bottom: 0.75px solid $color-gray-500; padding-bottom: 12px; } - .data { display: flex; justify-content: space-between; align-items: center; padding-top: 12px; - .avatar { - display: flex; - align-items: center; - p { - margin-left: 11px; - } - } p { @include typo-b1; } } } + +.avatar { + display: flex; + align-items: center; + p { + margin-left: 11px; + } +} diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 1fffbb1f..426275be 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,5 +1,27 @@ +import DetailsSideItem, { + TitleType, +} from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' + const StrategyDetailPage = () => { - return <></> + const arr1: { title: TitleType; data: string | number }[] = [ + { title: '최종손익입력일자', data: '2016.04.30' }, + { title: '등록일', data: '2016.02.30' }, + ] + const arr2: { title: TitleType; data: string | number }[] = [ + { title: '최종손익입력일자', data: '2016.04.30' }, + { title: '등록일', data: '2016.02.30' }, + ] + const trader: { title: TitleType; data: string | number } = { title: '트레이더', data: '수밍' } + const arr = [trader, arr1, arr2] + return ( + <> + {arr.map((i, idx) => ( + <div key={idx}> + <DetailsSideItem information={i} /> + </div> + ))} + </> + ) } export default StrategyDetailPage From 5e080f5fd52a7c0ecae11efa6e65beda600a9627 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 20 Nov 2024 13:34:04 +0900 Subject: [PATCH 203/648] =?UTF-8?q?remove:=20=EC=98=88=EC=8B=9C=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/page.tsx | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 426275be..12345c67 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,27 +1,4 @@ -import DetailsSideItem, { - TitleType, -} from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' - const StrategyDetailPage = () => { - const arr1: { title: TitleType; data: string | number }[] = [ - { title: '최종손익입력일자', data: '2016.04.30' }, - { title: '등록일', data: '2016.02.30' }, - ] - const arr2: { title: TitleType; data: string | number }[] = [ - { title: '최종손익입력일자', data: '2016.04.30' }, - { title: '등록일', data: '2016.02.30' }, - ] - const trader: { title: TitleType; data: string | number } = { title: '트레이더', data: '수밍' } - const arr = [trader, arr1, arr2] - return ( - <> - {arr.map((i, idx) => ( - <div key={idx}> - <DetailsSideItem information={i} /> - </div> - ))} - </> - ) + return <></> } - export default StrategyDetailPage From 0634d689280813d894e17e758fa898898b9d0558 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 20 Nov 2024 14:46:47 +0900 Subject: [PATCH 204/648] =?UTF-8?q?feat:=20=EA=B2=BD=EB=A1=9C,=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=88=98=EC=A0=95=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details-side-item.stories.tsx | 16 +++------------- .../[strategyId]/_ui/details-side-item/index.tsx | 2 +- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx index c4546f04..54d13b3a 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx @@ -1,8 +1,7 @@ -import DetailsSideItem, { - InformationModel, -} from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' import type { Meta, StoryFn } from '@storybook/react' +import DetailsSideItem, { InformationModel } from './index' + const meta: Meta<typeof DetailsSideItem> = { title: 'components/DetailsSideItem', component: DetailsSideItem, @@ -10,16 +9,7 @@ const meta: Meta<typeof DetailsSideItem> = { argTypes: { information: { title: { - control: 'select', - options: [ - '트레이더', - '최소 투자 금액', - '투자 원금', - 'KP Ratio', - 'SM SCORE', - '최종손익입력일자', - '등록일', - ], + control: 'object', }, data: { control: 'text', diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx index 4686bc23..3e7138ea 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/index.tsx @@ -1,6 +1,6 @@ -import SideItem from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/side-item' import classNames from 'classnames/bind' +import SideItem from './side-item' import styles from './styles.module.scss' const cx = classNames.bind(styles) From af6c6789a707abdf93aca0f435335188db0838da Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 20 Nov 2024 14:50:53 +0900 Subject: [PATCH 205/648] =?UTF-8?q?remove:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20control=EC=98=B5=EC=85=98=20=EC=A0=9C=EA=B1=B0=20(#?= =?UTF-8?q?69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details-side-item/details-side-item.stories.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx index 54d13b3a..5a5f0f1a 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item/details-side-item.stories.tsx @@ -6,16 +6,6 @@ const meta: Meta<typeof DetailsSideItem> = { title: 'components/DetailsSideItem', component: DetailsSideItem, tags: ['autodocs'], - argTypes: { - information: { - title: { - control: 'object', - }, - data: { - control: 'text', - }, - }, - }, } const sideItems: StoryFn<{ information: InformationModel | InformationModel[] }> = (args) => ( From bab45e3f610b8338d404ebef0cc007f560205fa1 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 20 Nov 2024 15:04:25 +0900 Subject: [PATCH 206/648] =?UTF-8?q?design:=20=EC=88=98=EC=A0=95=EB=90=9C?= =?UTF-8?q?=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EB=B0=98=EC=98=81=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/close.svg | 4 ++-- public/icons/open.svg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/icons/close.svg b/public/icons/close.svg index 40d24485..35ba1729 100644 --- a/public/icons/close.svg +++ b/public/icons/close.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2311 9.10008L6.03546 16.4531L4.96484 15.551L12.2507 6.90408L15.6805 11.1253L19.0579 15.5791L17.9424 16.425L14.5791 11.9899L12.2311 9.10008Z" fill="#797979"/> +<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8199 9.86603L16.6098 15.5508L17.4375 14.8534L11.8047 8.16827L9.1531 11.4318L6.54194 14.8751L7.40437 15.5291L10.0046 12.1002L11.8199 9.86603Z" fill="#797979"/> </svg> diff --git a/public/icons/open.svg b/public/icons/open.svg index 850afa8d..25be0528 100644 --- a/public/icons/open.svg +++ b/public/icons/open.svg @@ -1,3 +1,3 @@ -<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7689 14.8999L17.9645 7.54688L19.0352 8.44897L11.7493 17.0959L8.31954 12.8747L4.94209 8.42089L6.05761 7.57495L9.4209 12.0101L11.7689 14.8999Z" fill="#797979"/> +<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8199 14.4933L16.6098 8.80859L17.4375 9.50602L11.8047 16.1911L9.1531 12.9276L6.54194 9.48431L7.40437 8.8303L10.0046 12.2591L11.8199 14.4933Z" fill="#797979"/> </svg> From d801cfb59557bd0a24f452dd3310d9f8003763f1 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 20 Nov 2024 15:14:30 +0900 Subject: [PATCH 207/648] =?UTF-8?q?design:=20ellipsis=EB=AF=B9=EC=8A=A4?= =?UTF-8?q?=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/introduction/styles.module.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss index c652a5c3..502c9827 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/introduction/styles.module.scss @@ -10,11 +10,7 @@ .content { width: 100%; - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; - line-clamp: 4; - -webkit-line-clamp: 4; + @include ellipsis(4); &.expand { display: contents; } From 2864580bfbf0c1361618e12bc60684ef005ad70e Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 20 Nov 2024 20:55:26 +0900 Subject: [PATCH 208/648] =?UTF-8?q?feat:=20CommonTItle=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B8=B0=EB=B3=B8=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EC=84=A4=EA=B3=84=20(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/common-title/index.tsx | 25 +++++++++++++++++++++++ shared/ui/common-title/styles.module.scss | 16 +++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 shared/ui/common-title/index.tsx create mode 100644 shared/ui/common-title/styles.module.scss diff --git a/shared/ui/common-title/index.tsx b/shared/ui/common-title/index.tsx new file mode 100644 index 00000000..d9663b14 --- /dev/null +++ b/shared/ui/common-title/index.tsx @@ -0,0 +1,25 @@ +import { CSSProperties } from 'react' + +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + label: string + subtitle?: string + marginLeft?: CSSProperties['marginLeft'] + style?: CSSProperties +} + +const CommonTitle = ({ label, subtitle, marginLeft, style }: Props) => { + return ( + <div className={cx('container')} style={{ ...style, marginLeft }}> + <h1 className={cx('title')}>{label}</h1> + {subtitle && <p className={cx('sub-title')}>{subtitle}</p>} + </div> + ) +} + +export default CommonTitle diff --git a/shared/ui/common-title/styles.module.scss b/shared/ui/common-title/styles.module.scss new file mode 100644 index 00000000..1a92d4e1 --- /dev/null +++ b/shared/ui/common-title/styles.module.scss @@ -0,0 +1,16 @@ +.container { + width: fit-content; + display: flex; + flex-direction: column; + gap: 18.5px; +} + +.title { + @include typo-h3; + color: $color-gray-700; +} + +.sub-title { + @include typo-c1; + color: $color-gray-500; +} From 309b950b420b0a26d987a69a84ca97940b0d658d Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 20 Nov 2024 20:58:43 +0900 Subject: [PATCH 209/648] =?UTF-8?q?feat:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EC=8A=A4=ED=81=AC=EB=A1=A4=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20mock=20contents=20util=20=EC=B6=94=EA=B0=80=20(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/utils/storybook-mock-contents.tsx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 shared/utils/storybook-mock-contents.tsx diff --git a/shared/utils/storybook-mock-contents.tsx b/shared/utils/storybook-mock-contents.tsx new file mode 100644 index 00000000..82449f01 --- /dev/null +++ b/shared/utils/storybook-mock-contents.tsx @@ -0,0 +1,24 @@ +const StorybookMockContents = () => { + return ( + <> + {Array.from({ length: 5 }, (_, idx) => ( + <div + key={idx} + style={{ + display: 'flex', + alignItems: 'center', + height: '100px', + paddingLeft: '20px', + marginBottom: '10px', + background: '#f4f4f4', + fontSize: '32px', + }} + > + Scrollable Content {idx + 1} + </div> + ))} + </> + ) +} + +export default StorybookMockContents From fb51301ecc71d4064db9bec60d25e569e5589030 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 20 Nov 2024 20:59:06 +0900 Subject: [PATCH 210/648] =?UTF-8?q?feat:=20CommonTItle=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/common-title/common-title.stories.tsx | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 shared/ui/common-title/common-title.stories.tsx diff --git a/shared/ui/common-title/common-title.stories.tsx b/shared/ui/common-title/common-title.stories.tsx new file mode 100644 index 00000000..a727db14 --- /dev/null +++ b/shared/ui/common-title/common-title.stories.tsx @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import StorybookMockContents from '@/shared/utils/storybook-mock-contents' + +import CommonTitle from '.' +import BackHeader from '../header/back-header' + +const meta = { + title: 'Components/CommonTItle', + component: CommonTitle, + tags: ['autodocs'], +} satisfies Meta<typeof CommonTitle> +export default meta +type StoryType = StoryObj<typeof CommonTitle> + +export const Default: StoryType = { + args: { + label: '전략 상세보기', + }, +} + +export const WithSubtitle: StoryType = { + args: { + ...Default.args, + subtitle: '트레이더 40명의 200가지 전략을 볼 수 있습니다.', + }, +} + +export const WithHeader: StoryType = { + decorators: [ + (Story) => ( + <> + <div + style={{ + height: '400px', + overflow: 'auto', + border: '1px solid #ddd', + }} + > + <BackHeader label="목록으로 돌아가기" /> + <Story /> + <div style={{ marginTop: '20px', marginLeft: '40px' }}> + <StorybookMockContents /> + </div> + </div> + </> + ), + ], + args: { + ...Default.args, + marginLeft: '40px', + }, + parameters: { + nextjs: { + appDirectory: true, + }, + }, +} From 69cdf6ca93d8a1fea8c737a0d553b117e886c021 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 20 Nov 2024 21:01:24 +0900 Subject: [PATCH 211/648] =?UTF-8?q?refactor:=20mock=20contents=20util=20he?= =?UTF-8?q?ader=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EC=97=90=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/back-header/back-header.stories.tsx | 10 +++------- shared/ui/header/back-header/index.tsx | 1 + shared/ui/header/logo-header/logo-header.stories.tsx | 10 +++------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/shared/ui/header/back-header/back-header.stories.tsx b/shared/ui/header/back-header/back-header.stories.tsx index 20ad48c9..a1e9c25f 100644 --- a/shared/ui/header/back-header/back-header.stories.tsx +++ b/shared/ui/header/back-header/back-header.stories.tsx @@ -1,5 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react' +import StorybookMockContents from '@/shared/utils/storybook-mock-contents' + import BackHeader from '.' const meta = { @@ -15,13 +17,7 @@ const meta = { }} > <Story /> - <div style={{ marginTop: '80px' }}> - {Array.from({ length: 5 }, (_, idx) => ( - <div key={idx} style={{ height: '100px', marginBottom: '10px', background: '#f4f4f4' }}> - Scrollable Content {idx + 1} - </div> - ))} - </div> + <StorybookMockContents /> </div> ), ], diff --git a/shared/ui/header/back-header/index.tsx b/shared/ui/header/back-header/index.tsx index 620cfbbd..45d33f63 100644 --- a/shared/ui/header/back-header/index.tsx +++ b/shared/ui/header/back-header/index.tsx @@ -2,6 +2,7 @@ // NOTE: 이름 공모전 개최 // TODO: 아이콘 바꿔야함 +// TODO: 스타일 추가되면 바꿔야함 import { useRouter } from 'next/navigation' import { ChevronLeftIcon } from '@/public/icons' diff --git a/shared/ui/header/logo-header/logo-header.stories.tsx b/shared/ui/header/logo-header/logo-header.stories.tsx index 95b887a6..328d999c 100644 --- a/shared/ui/header/logo-header/logo-header.stories.tsx +++ b/shared/ui/header/logo-header/logo-header.stories.tsx @@ -1,5 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react' +import StorybookMockContents from '@/shared/utils/storybook-mock-contents' + import LogoHeader from '.' const meta = { @@ -15,13 +17,7 @@ const meta = { }} > <Story /> - <div style={{ marginTop: '80px' }}> - {Array.from({ length: 5 }, (_, idx) => ( - <div key={idx} style={{ height: '100px', marginBottom: '10px', background: '#f4f4f4' }}> - Scrollable Content {idx + 1} - </div> - ))} - </div> + <StorybookMockContents /> </div> ), ], From 2f3ac22341c4ac37167af2892e5d2a644bd1e9ca Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 11:52:00 +0900 Subject: [PATCH 212/648] =?UTF-8?q?feat:=20=EA=B0=9C=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=B2=98=EB=A6=AC=EB=B0=A9=EC=B9=A8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../terms-of-use/_ui/terms/privacy-terms.tsx | 241 ++++++++++++++++++ .../terms-of-use/_ui/terms/styles.module.scss | 48 ++++ shared/styles/base/_variables.scss | 1 + 3 files changed, 290 insertions(+) create mode 100644 app/(landing)/signup/terms-of-use/_ui/terms/privacy-terms.tsx create mode 100644 app/(landing)/signup/terms-of-use/_ui/terms/styles.module.scss diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/privacy-terms.tsx b/app/(landing)/signup/terms-of-use/_ui/terms/privacy-terms.tsx new file mode 100644 index 00000000..45b73759 --- /dev/null +++ b/app/(landing)/signup/terms-of-use/_ui/terms/privacy-terms.tsx @@ -0,0 +1,241 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const PrivacyTerms = () => ( + <div className={cx('container')}> + <h3>개인정보 처리방침</h3> + <div> + <span> +  이 개인정보처리방침은 주식회사 시스메틱이 어떤 정보를 수집, 사용, 관리하는지에 대한 + 내용을 포함하고 있습니다. 주식회사 시스메틱("회사")은 정보통신망 이용촉진 및 + 정보보호 등에 관한 법률, 개인정보보호법 등 관련 법령상의 개인정보보호 규정을 준수하며, 관련 + 법령에 의거한 개인정보처리방침을 정해 이용자 권익 보호에 최선을 다하고 있습니다. 회사는 + 개인정보처리방침을 통해 제공되는 서비스("서비스")를 이용하는 회원 및 콘텐츠 + 구독자(이하 "이용자")로부터 수집하는 개인정보의 항목, 개인정보의 수집 및 이용 + 목적, 개인정보의 보유 및 이용 기간과 개인정보 보호를 위해 취해지고 있는 조치를 안내해 + 드립니다. 이 개인정보처리방침은 2024년 12월 1일부터 적용됩니다. + </span> + </div> + <div> + <strong>수집하는 개인정보의 항목 및 수집 방법</strong> + <ol> + <li> + 이름(닉네임), 아이디(이메일 주소), 비밀번호, 휴대폰 번호(본인확인시 필요)는 필수로 + 수집합니다. + </li> + <li>유료 서비스를 이용하면 결제와 관련된 정보를 추가로 수집합니다.</li> + <li> + IP 주소, 쿠키, 방문기록, 서비스 이용 기록은 서비스를 이용할 때 자동으로 수집됩니다. 회사는 + 서비스 제공을 위해 기본적으로 몇 가지 정보를 요청합니다. + <ul className={cx('bullet-list')}> + <li>필수: 이름(닉네임), 아이디(이메일 주소), 비밀번호, 휴대폰 번호</li> + <li> + IP 주소, 쿠키, 방문 기록, 서비스 이용 기록은 이용자가 서비스를 이용할 때 자동으로 + 수집됩니다. + </li> + <li>회사는 사이트를 통해 개인정보를 수집합니다.</li> + </ul> + </li> + </ol> + </div> + <div> + <strong>개인정보의 수집 및 이용 목적</strong> + <ol> + <li> + 수집한 개인정보는 서비스 제공에 관한 계약 이행및 이용 요금 정산, 회원 관리, 서비스 개선 및 + 마케팅, 광고에의 활용을 위해 사용됩니다. + </li> + <li> + 회사는 수집한 개인정보를 서비스 제공에 관한 계약 이행, 이용자 관리, 서비스 개선 및 마케팅, + 광고에의 활용을 목적으로 사용하고 있습니다. + <ul className={cx('bullet-list')}> + <li> + 서비스 제공에 관한 계약 이행 및 서비스 제공에 따른 이용 요금 정산: 서비스 및 콘텐츠 + 제공, 특정 맞춤 서비스 제공, 서비스 구매 및 요금 결제(구독결제서비스 결제 포함), + 증빙서류 발급, 요금추심, 환불 처리 등 + </li> + <li> + 이용자 관리: 서비스 이용에 따른 본인 확인, 개인 식별, 부정 이용 방지, 회원 가입 및 + 서비스 이용 의사 확인, 가입 횟수 제한, 분쟁 조정을 위한 기록 보존, 불만처리 등 + 민원처리, 고지사항 전달 등 + </li> + <li> + 서비스 개선 및 마케팅, 광고에의 활용: 서비스 이용에 대한 통계 확인, 서비스의 유효성 + 확인, 이벤트 및 광고성 정보 제공, 접속 빈도 파악 등 + </li> + </ul> + </li> + </ol> + </div> + <div> + <strong>개인정보의 보유 및 이용기간</strong> + <ol> + <li> + 개인정보는 원칙적으로 수집 및 이용 목적이 달성되면 파기하지만 경우에 따라 일정 기간 보관할 + 수 있습니다. + </li> + <li> + 회사가 수집한 개인정보는 원칙적으로 수집 및 이용 목적이 달성되면 지체없이 파기합니다. + <span> + 단, 이용자에게 개인정보 보관기간에 대해 별도의 동의를 얻은 경우, 또는 법령에서 일정 기간 + 정보보관 의무를 부과하는 경우에는 해당 기간 동안 개인정보를 안전하게 보관합니다. + 이용자에게 개인정보 보관기간에 대해 회원가입 시 또는 서비스 가입 시 동의를 얻은 경우는 + 아래와 같습니다. + </span> + <ul className={cx('no-bullet-list')}> + <li> + [부정 가입 및 이용 방지] + <ul className={cx('bullet-list')}> + <li>항목: 이메일 주소, 닉네임, 휴대폰 번호</li> + <li>보존 기간: 탈퇴 후 즉시 영구삭제</li> + </ul> + </li> + <li> + [분쟁 조정 및 이용자 문의 대응] + <ul className={cx('bullet-list')}> + <li>항목: 이메일 주소, 이름, 휴대폰 번호</li> + <li>보존 기간: 탈퇴 후 즉시 영구삭제</li> + </ul> + </li> + </ul> + </li> + </ol> + </div> + <div> + <strong>개인정보 수집 이용에 대한 동의를 거부할 권리</strong> + <br /> + <span> + 위의 개인정보 수집,이용에 대한 동의를 거부할 권리가 있습니다. 그러나 동의를 거부할 경우 + 서비스 제공을 받을수 없거나 제약이 있을수 있습니다. + </span> + </div> + <div> + <strong>개인정보 제공 및 취급위탁</strong> + <br /> + <span> + 개인정보는 원칙적으로 외부에 제공하지 않지만 경우에 따라 제공할 수 있습니다. 유료 서비스를 + 이용하는 경우 요금 정산을 위해 정해진 수탁업체에게 정해진 범위 내에서 개인정보를 위탁 + 처리합니다. 회사는 이용자의 개인정보를 원칙적으로 외부에 제공하지 않습니다. 다만, 아래의 + 경우는 예외로 합니다. + </span> + <ul className={cx('bullet-list')}> + <li>이용자가 사전에 동의한 경우</li> + <li> + 법령의 규정에 의거하거나, 수사 목적으로 법령에 정해진 절차와 방법에 따라 수사기관의 요구가 + 있는 경우 + </li> + <li> + 통계작성, 학술연구나 시장조사를 위해 특정 개인을 식별할 수 없는 형태로 가공해 제공하는 + 경우 + </li> + <li>이용자가 사전에 동의한 경우</li> + </ul> + <span> + 회사는 편리하고 더 나은 서비스를 제공하기 위해 정해진 범위 내에서 개인정보를 위탁해 + 처리합니다. + </span> + <ul className={cx('bullet-list')}> + <li>서비스 제공을 위한 서버 운영: AWS(Amazon Web Services, Inc.)</li> + <li> + 이용되는 개인정보 항목은 이메일 발송 및 관리 솔루션 제공업체인 + 스티비(https://stibee.com/)에 계정 이메일 주소, 이름, 핸드폰번호 등 이메일 발송관리에 + 필요한 서비스 이용 기록에 한함. + </li> + </ul> + </div> + <div> + <strong>개인정보 파기절차 및 파기방법</strong> + <br /> + <span> + 개인정보의 수집 및 이용 목적이 달성 되면, 개인정보를 신속하고 안전한 방법으로 파기합니다. + 회사는 개인정보 보유기간의 경과, 처리 목적 달성 등 개인정보가 불필요하게 되었을 때에는 해당 + 정보를 지체 없이 파기합니다. 이용자가 제공한 정보는 수집 및 이용 목적이 달성된 후 별도의 + 데이터베이스로 옮겨져(종이의 경우 별도의 서류함) 내부 방침 및 기타 관련 법령에 의한 정보보호 + 사유에 따라(보유 및 이용기간 참조) 일정기간 저장된 후 파기됩니다. + <br /> + 별도 데이터베이스로 옮겨진 개인정보는 법률에 의한 경우가 아니면 보유되는 이외의 다른 + 목적으로 이용되지 않습니다. 전자적 파일형태로 저장된 개인정보는 기록을 재생할 수 없는 기술적 + 방법을 이용해 삭제하고, 종이에 출력된 개인정보는 분쇄기로 분쇄하거나 소각해 파기됩니다. + </span> + </div> + <div> + <strong>만 14세 미만 아동에 관한 사항</strong> + <br /> + <span>회사는 만 14세 미만 아동의 회원 가입을 받지 않습니다.</span> + </div> + <div> + <strong>개인정보 자동 수집 장치의 설치/운영 및 거부에 관한 사항</strong> + <br /> + <span> + 적절하고 유용한 서비스를 제공하기 위해 쿠키를 사용합니다. 웹 브라우저 설정에서 쿠키 저장을 + 거부할 수 있습니다. 회사는 이용자에게 보다 적절하고 유용한 서비스를 제공하기 위해 이용자의 + 정보를 수시로 저장하고 불러오는 "쿠키(cookie)"를 사용합니다. 쿠키란 회사의 + 웹사이트를 운영하는데 이용되는 서버가 이용자의 컴퓨터로 전송하는 아주 작은 텍스트 파일로서 + 이용자의 기기에 저장됩니다. 이용자는 쿠키의 사용여부를 선택할 수 있습니다. 쿠키 설정을 + 거부하는 방법으로는 이용자가 사용하는 웹 브라우저의 옵션을 선택함으로써 모든 쿠키를 + 허용하거나 쿠키를 저장할 때마다 확인을 거치거나, 모든 쿠키의 저장을 거부할 수 있습니다. 단, + 쿠키 설치를 거부하였을 경우 로그인이 필요한 일부 서비스 이용에 어려움이 있을 수 있습니다. + </span> + </div> + <div> + <strong>개인정보보호를 위한 기술적, 관리적 대책</strong> + <br /> + <span> + 회사는 개인정보보호를 위해 최선의 최선의 기술적, 관리적 노력을 하고 있습니다. 비밀번호는 + 암호화되어 저장 및 관리되고 있어 본인만이 알고 있으며, 개인정보의 확인 및 변경도 비밀번호를 + 알고 있는 본인에 의해서만 가능합니다. 따라서 비밀번호가 다른 사람에게 알려지지 않도록 + 주의하시기 바랍니다. 회사는 해킹이나 컴퓨터 바이러스에 의해 이용자의 개인정보가 유출되거나 + 훼손되는 것을 막기 위해 필요한 보안조치를 이용하고 있으며, 더욱 향상된 보안조치를 확보할 수 + 있도록 가능한 모든 기술적 방법을 구비하기 위해 노력하고 있습니다. 회사는 개인정보를 취급하는 + 직원을 최소한으로 제한하고 있으며, 관련 직원들에 대한 교육을 수시로 실시해 본 방침의 이행 및 + 준수 여부를 확인하고 있습니다. + </span> + </div> + <div> + <strong>개인정보에 관한 민원 서비스</strong> + <br /> + <span> + 회사는 고객의 개인정보를 보호하고 개인정보와 관련한 불만을 처리하기 위해 + 개인정보관리책임자를 두고 있습니다. 개인정보와 관련한 문의사항이 있으시면 아래의 + 개인정보관리책임자에게 연락 주시기 바랍니다. 귀하의 문의 사항에 신속하고 성실하게 + 답변해드리겠습니다. + </span> + </div> + <div> + <strong>개인정보관리책임자</strong> + <ul className={cx('bullet-list')}> + <li>이름: 박혜정</li> + <li>전화번호: 02-6338-1880</li> + <li>이메일: ceo@sysmetic.co.kr</li> + </ul> + <span> + 기타 개인정보침해에 대한 신고나 상담이 필요하신 경우에는 아래 기관에 문의하시기 바랍니다. + </span> + <ul className={cx('bullet-list')}> + <li>개인정보 분쟁조정위원회 (kopico.go.kr)</li> + <li>개인정보침해신고센터(privacy.kisa.or.kr)</li> + <li>경찰청 사이버안전국(cyberbureau.police.go.kr)</li> + </ul> + </div> + <div> + <strong>개인정보처리방침의 변경</strong> + <br /> + <span> + 법령 및 방침에 따른 추가, 삭제 및 정정 시 변경사항 시행 7일 전부터 공지사항을 통해 고지할 + 것입니다. <br />이 개인정보 처리방침은 2024년 12월 1일부터 적용됩니다. + </span> + </div> + <div> + <strong>개인정보 처리방침 버전번호: 1.0</strong> + <br /> + <strong>공고일자: 2024년 12월 1일</strong> + <br /> + <strong>시행일자: 2024년 12월 1일</strong> + </div> + </div> +) + +export default PrivacyTerms diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/styles.module.scss b/app/(landing)/signup/terms-of-use/_ui/terms/styles.module.scss new file mode 100644 index 00000000..f1843ef2 --- /dev/null +++ b/app/(landing)/signup/terms-of-use/_ui/terms/styles.module.scss @@ -0,0 +1,48 @@ +.container { + font-weight: $text-normal; + font-size: $text-b3; + + h3 { + font-weight: $text-semibold; + margin-bottom: 24px; + } + + div { + margin-bottom: 12px; + } + + ol { + padding-left: 16px; + } + + li { + list-style-type: decimal; + } + + div > strong { + display: inline-block; + padding-right: 8px; + font-weight: $text-semibold; + } +} + +.bullet-list { + li { + list-style-type: disc; + margin-left: 16px; + } + + span { + display: block; + } +} + +.no-bullet-list { + li { + list-style-type: none; + } + + span { + display: block; + } +} diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index bb73983c..732f0f3d 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -50,6 +50,7 @@ $text-c2: 10px; $text-bold: 700; $text-semibold: 600; $text-medium: 500; +$text-normal: 400; /* Breakpoints */ $breakpoint-sm: 576px; From 07af2e62b22c985e0db9fe40f43a4f6bc5410130 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 11:53:01 +0900 Subject: [PATCH 213/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90(=ED=8A=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EB=8D=94/=ED=88=AC=EC=9E=90=EC=9E=90)?= =?UTF-8?q?=EC=95=BD=EA=B4=80=20=EC=B6=94=EA=B0=80=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../terms-of-use/_ui/terms/investor-terms.tsx | 602 +++++++++++++++ .../terms-of-use/_ui/terms/trader-terms.tsx | 717 ++++++++++++++++++ 2 files changed, 1319 insertions(+) create mode 100644 app/(landing)/signup/terms-of-use/_ui/terms/investor-terms.tsx create mode 100644 app/(landing)/signup/terms-of-use/_ui/terms/trader-terms.tsx diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/investor-terms.tsx b/app/(landing)/signup/terms-of-use/_ui/terms/investor-terms.tsx new file mode 100644 index 00000000..f7f75a05 --- /dev/null +++ b/app/(landing)/signup/terms-of-use/_ui/terms/investor-terms.tsx @@ -0,0 +1,602 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const InvestorTerms = () => ( + <div className={cx('container')}> + <h3>일반 회원(일반투자자) 이용약관</h3> + <div> + <strong>제 1 조 (목적)</strong> + <span> + 이 약관은 시스메틱(이하 "회사")주식회사가 온라인으로 제공하는 + <strong> 인베스트메틱</strong> 플랫폼 서비스 (웹, 모바일 서비스, 이하 서비스라 한다)를 + 일반회원 및 일반투자자들이 활용하기 위해 가입하는 절차와 서비스에서에서 제공되는 정보, + 서비스의 이용조건 및 절차, 회원의 권리, 의무 및 기타 필요한 사항을 규정함을 목적으로 합니다. + </span> + </div> + <div> + <strong>제 2 조 (용어의 정의)</strong> + <span>이 약관에서 사용하는 용어의 정의는 다음 각 호와 같다.</span> + <ol> + <li> + "플랫폼"은 이용자가 컴퓨터, 휴대형단말기 등 유·무선 정보통신설비를 통하여 + 투자성과의 상담 및 수익률 분석, 투자성과 정보 입력 등의 방식으로 참여할 수 있도록 하는 + 회사의 플랫폼을 말합니다. + </li> + <li> + "트레이더"는 본인의 실 금융거래 투자 성과 들을 올리는 전문투자자를 의미합니다. + </li> + <li> + "일반회원 또는 투자가"는 플랫폼에 접속하여, 표준 가입약관에 따라 회사와 + 약관동의를 체결하고 회원 가입을 한 자로서 회사와 파트너가 제공하는 정보를 지속적으로 + 제공받으며, 플랫폼을 계속적으로 이용할 수 있는 자를 의미합니다. + </li> + </ol> + </div> + <div> + <strong>제 3 조 (약관의 명시와 설명 및 개정)</strong> + <ol> + <li> + 서비스는 이 약관의 내용을 이용자가 쉽게 알 수 있도록 서비스 화면에 게시합니다. 다만, + 약관의 구체적 내용은 이용자가 연결화면을 통하여 볼 수 있습니다. + </li> + <li> + 서비스는 『약관의 규제 등에 관한 법률』,『전자서명법』, 『정보통신망 이용촉진 및 정보보호 + 등에 관한 법률』(정보통신망법) 등 관련법을 위배하지 않는 범위에서 이 약관을 개정할 수 + 있습니다. + </li> + <li> + 서비스가 약관을 개정할 경우에는 적용일자 및 개정사유를 명시하여 현행 약관과 함께 사이트의 + 공지사항에 그 개정약관의 적용일자 30일 전부터 적용일자 전일까지 공지합니다. 다만, 회원에게 + 불리한 약관의 개정의 경우에는 공지 외에 일정기간 서비스 내 전자우편, 로그인 시 동의창 등의 + 전자적 수단을 통하여 따로 명확히 통지하도록 합니다. + </li> + <li> + 제3항에 따라 공지된 적용일자 이후에 회원이 서비스를 계속 이용하는 경우에는 개정된 약관에 + 동의하는 것으로 봅니다. 개정된 약관에 동의하지 아니하는 회원은 언제든지 자유롭게 서비스 + 이용계약을 해지할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 4 조 (관련법령과의 관계)</strong> + <span> + 이 약관 또는 개별약관에서 정하지 않은 사항은 전기통신사업법, 정보통신망법 등에서의 + 소비자보호에 관한 법률, 개인정보보호법 등 관련 법령의 규정과 일반적인 상관례에 의합니다. + </span> + </div> + <div> + <strong>제 5 조 (서비스의 종류)</strong> + <span> + 시스메틱 서비스는 회원에게 아래와 같은 서비스를 제공합니다.{' '} + <span> - 시스메틱 : PC용 웹사이트, 모바일 용 반응형 사이트</span> + </span> + </div> + <div> + <strong>제 6 조 (이용계약의 성립)</strong> + <ol> + <li> + 이용계약은 회원이 되고자 하는 자(이하 "가입신청자")가 서비스가 정한 가입 양식에 + 따라 회원정보(전자우편주소, 비밀번호, 이름, 연락처 등)를 기입하여 회원가입신청을 하고 + 서비스가 이러한 신청에 대하여 승인함으로써 체결됩니다. + </li> + <li> + 서비스는 다음 각 호에 해당하는 신청에 대하여는 승인을 하지 않거나 사후에 이용계약을 해지할 + 수 있습니다. + <ul className={cx('no-bullet-list')}> + <li> + 1) 가입신청자가 이 약관에 의하여 이전에 회원자격을 상실한 적이 있는 경우. 다만, + 회원자격 상실 후 3개월이 경과한 자로서 서비스의 회원 재가입 승낙을 얻은 경우에는 + 예외로 함 + </li> + <li>2) 타인의 명의를 이용한 경우</li> + <li> + 3) 서비스가 실명확인절차를 실시할 경우에 이용자의 실명가입신청이 사실 아님이 확인된 + 경우 + </li> + <li> + 4) 서비스에 의하여 이용계약이 해지된 날로부터3개월 이내에 재이용 신청을 하는 경우 + </li> + <li>5) 등록내용에 허위의 정보를 기재하거나, 기재누락, 오기가 있는 경우</li> + <li>6) 이미 가입된 회원과 전화번호나 전자우편주소가 동일한 경우</li> + <li>7) 부정한 용도 또는 영리를 추구할 목적으로 본 서비스를 이용하고자 하는 경우</li> + <li> + 8) 기타 이 약관에 위배되거나 위법 또는 부당한 이용신청임이 확인된 경우 및 서비스가 + 합리적인 판단에 의하여 필요하다고 인정하는 경우 + </li> + <li> + 9) 관계법령에 위배되거나 사회의 안녕질서 혹은 미풍양속을 저해할 수 있는 목적으로 + 신청한 경우 + </li> + <li> + 10) 이용자의 귀책사유로 인하여 승인이 불가능하거나 기타 규정한 제반 사항을 위반하여 + 신청하는 경우 + </li> + </ul> + </li> + <li> + 제1항에 따른 신청에 있어 서비스는 회원의 종류에 따라 전문기관을 통한 실명확인 및 + 본인인증을 요청할 수 있습니다. + </li> + <li> + 서비스는 서비스관련설비의 여유가 없거나, 기술상 또는 업무상 문제가 있는 경우에는 승낙을 + 유보할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 7 조 (이용계약의 종료)</strong> + <ol> + <li> + 회원의 해지 + <ul className={cx('no-bullet-list')}> + <li> + 1) 회원은 언제든지 서비스에게 해지의사를 통지함으로써 이용계약을 해지할 수 있습니다. + </li> + <li>2) 이용계약은 회원의 해지의사가 서비스에 도달한 때에 종료 됩니다.</li> + </ul> + </li> + <li> + 서비스의 해지 + <ul className={cx('no-bullet-list')}> + <li> + 1) 서비스는 다음과 같은 사유가 있는 경우, 이용계약을 해지할 수 있습니다. 이 경우 + 서비스는 회원에게 전자우편, 전화, 팩스 기타의 방법을 통하여 해지사유를 밝혀 해지의사를 + 통지합니다. 다만 서비스는 해당 회원에게 사전에 해지사유에 대한 의견진술의 기회를 부여 + 할 수 있습니다. + <span> + ① 제6조 제2항에서 정하고 있는 이용계약의 승낙거부사유가 있음이 확인된 경우 + </span> + <span> + ② 회원이 서비스나 다른 회원 기타 타인의 권리나 명예, 신용 기타 정당한 이익을 + 침해하는 행위를 한 경우 + </span> + <span> + ③ 기타 회원이 이 약관에 위배되는 행위를 하거나 이 약관에서 정한 해지사유가 발생한 + 경우 + </span> + </li> + <li> + 2) 이용계약은 서비스가 해지의사를 회원에게 통지함으로써 종료됩니다. 이 경우 서비스가 + 해지의사를 회원이 등록한 전자우편주소로 발송하거나 서비스 게시판에 게시함으로써 통지에 + 갈음합니다. + </li> + </ul> + </li> + <li> + 이용계약의 종료와 관련하여 발생한 손해는 이용계약이 종료된 해당 회원이 책임을 부담하여야 + 하고 서비스는 일체의 책임을 지지 않습니다. + </li> + </ol> + </div> + <div> + <strong>제 8 조 (회원탈퇴 및 자격 상실)</strong> + <ol> + <li> + 회원은 언제든지 서비스 내 <span>"계정삭제 및 회원탈퇴"</span> 화면을 통하여 + 이용계약 해지 신청을 할 수 있으며, 서비스는 관련법 등이 정하는 바에 따라 이를 즉시 + 처리하여야 합니다. + </li> + <li> + 회원이 계약을 해지할 경우, 관련법 및 개인정보취급방침에 따라 서비스가 회원정보를 보유하는 + 경우를 제외하고는 해지 즉시 회원의 개인정보는 소멸됩니다. 또한 회원의 아이디는 재사용을 할 + 수 없습니다. + </li> + <li>회원이 계약을 해지하는 경우, 회원이 작성한 게시물 일체는 삭제되지 않습니다.</li> + </ol> + </div> + <div> + <strong>제 9 조 (회원의 ID 및 비밀번호에 대한 의무)</strong> + <ol> + <li>ID와 비밀번호에 관한 관리책임은 회원에게 있습니다.</li> + <li>회원은 자신의ID 및 비밀번호를 제3자에게 이용하게 해서는 안됩니다.</li> + <li> + 회원이 자신의ID 및 비밀번호를 도난 당하거나 제3자가 사용하고 있음을 인지한 경우에는 즉시 + 서비스에 통보하고 서비스의 조치가 있는 경우에는 그에 따라야 합니다. + </li> + <li> + 회원이 제3항에 따른 통지를 하지 않거나 서비스의 조치에 응하지 아니하여 발생하는 모든 + 불이익에 대한 책임은 회원에게 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 10 조 (회원, 이용자의 의무)</strong> + <ol> + <li> + 회원은 관계법령이 약관의 규정, 이용안내 등 서비스가 통지하는 사항을 준수하여야 하며, 기타 + 서비스 업무에 방해되는 행위를 하여서는 안됩니다. + </li> + <li> + 회원은 서비스 이용과 관련하여 다음 각 호의 행위를 하여서는 안됩니다. + <ul className={cx('no-bullet-list')}> + <li>1) 서비스 신청 또는 변경 시 허위내용의 등록</li> + <li>2) 서비스에 게시된 정보의 허가 받지 않은 변경</li> + <li>3) 서비스가 정한 정보 이외의 정보(컴퓨터 프로그램 등)의 송신 또는 게시</li> + <li>4) 서비스 기타 제3자의 저작권 등 지적 재산권에 대한 침해</li> + <li>5) 서비스 기타 제3자의 명예를 손상시키거나 업무를 방해하는 행위</li> + <li> + 6) 외설 또는 폭력적인 메시지, 화상, 음성 기타 공공질서 미풍양속에 반하는 정보를 + 서비스에 공개 또는 게시하는 행위 + </li> + <li>7) 기타 관계 법령이나 서비스에서 정한 규정에 위배되는 행위</li> + <li>8) 정당한 사유 없이 당사의 영업을 방해하는 내용을 기재하는 행위</li> + <li> + 9) 리버스엔지니어링, 디컴파일, 디스어셈블 및 기타 일체의 가공행위를 통하여 서비스를 + 복제, 분해 또는 모방 기타 변형하는 행위 + </li> + <li> + 10) 자동 접속 프로그램 등을 사용하는 등 정상적인 용법과 다른 방법으로 서비스를 + 이용하여 서비스의 서버에 부하를 일으켜 정상적인 서비스를 방해하는 행위 + </li> + <li>11) 기타 관계법령에 위반된다고 판단되는 행위</li> + </ul> + </li> + </ol> + </div> + <div> + <strong>제 11 조 (회원의 게시물)</strong> + <span> + 회원이 작성한 게시물에 대한 모든 권리 및 책임은 이를 게시한 회원에게 있으며, 서비스는 회원이 + 게시하거나 등록하는 서비스의 내용물이 다음 각 항에 해당한다고 판단되는 경우에 사전통지 없이 + 삭제할 수 있고, 이에 대하여 서비스는 어떠한 책임도 지지 않습니다. + </span> + <ol> + <li>다른 회원 또는 제3자를 비방하거나 중상모략으로 명예를 손상시키는 내용인 경우</li> + <li>공공질서 및 미풍양속에 위반되는 내용일 경우</li> + <li>범죄적 행위에 결부된다고 인정되는 경우</li> + <li>서비스의 저작권, 제3자의 저작권 등 기타 권리를 침해하는 내용인 경우</li> + <li>회원이 사이트와 게시판에 음란물을 게재하거나 음란사이트를 링크하는 경우</li> + <li>서비스로부터 사전 승인 받지 아니한 상업광고, 판촉 내용을 게시하는 경우</li> + <li>해당 상품과 관련 없는 내용인 경우</li> + <li>정당한 사유 없이 서비스의 영업을 방해하는 내용을 기재하는 경우</li> + <li>기타 관계법령에 위반된다고 판단되는 경우</li> + </ol> + </div> + <div> + <strong>제 12 조 (회원게시물의 관리)</strong> + <ol> + <li> + 회원의 "게시물"이 정보통신망법 및 저작권법 등 관련법에 위반되는 내용을 포함하는 + 경우, 권리자는 관련법이 정한 절차에 따라 해당 "게시물"의 게시중단 및 삭제 등을 + 요청할 수 있으며, 서비스는 관련법에 따라 조치를 취하여야 합니다. + </li> + <li> + 서비스는 전항에 따른 권리자의 요청이 없는 경우라도 권리침해가 인정될 만한 사유가 있거나 + 기타 서비스 정책 및 관련법에 위반되는 경우에는 관련법에 따라 해당 "게시물"에 + 대해 임시조치 등을 취할 수 있습니다. + </li> + <li> + 본 조에 따른 세부절차는 정보통신망법 및 저작권법이 규정한 범위 내에서 서비스가 정한 + 게시중단요청서비스에 따릅니다. + </li> + </ol> + <span>- 게시중단요청 : ceo@sysmetic.co.kr</span> + </div> + <div> + <strong>제 13 조 (책임제한)</strong> + <ol> + <li> + 천재지변 또는 이에 준하는 불가항력으로 인하여 서비스를 제공할 수 없는 경우에는 서비스 + 제공에 관한 책임이 면제됩니다. + </li> + <li>회원의 귀책사유로 인한 서비스 이용의 장애에 대하여는 책임을 지지 않습니다.</li> + <li> + 회원이 서비스와 관련하여 게재한 정보, 자료, 사실의 신뢰도, 정확성 등의 내용에 관하여는 + 책임을 지지 않습니다. + </li> + <li> + 회원 간 또는 회원과 제3자 상호간에 서비스를 매개로 하여 거래 등을 한 경우에는 책임이 + 면제됩니다. + </li> + <li> + 무료로 제공되는 서비스 이용과 관련하여 관련법에 특별한 규정이 없는 한 책임을 지지 + 않습니다. + </li> + <li> + 제3자가 서비스 내 화면 또는 링크된 웹사이트를 통하여 광고한 제품 또는 서비스의 내용과 + 품질에 대하여 감시할 의무 기타 어떠한 책임도 지지 아니합니다. + </li> + <li> + 서비스 및 서비스의 임직원 그리고 대리인은 다음과 같은 사항으로부터 발생하는 손해에 대해 + 책임을 지지 아니합니다. + <ul className={cx('no-bullet-list')}> + <li>① 회원 상태정보의 허위 또는 부정확성에 기인하는 손해</li> + <li> + ② 그 성질과 경위를 불문하고 서비스에 대한 접속 및 서비스의 이용과정에서 발생하는 + 개인적인 손해 + </li> + <li> + ③ 서버에 대한 제3자의 모든 불법적인 접속 또는 서버의 불법적인 이용으로부터 발생하는 + 손해 + </li> + <li> + ④ 서버에 대한 전송 또는 서버로부터의 전송에 대한 제3자의 모든 불법적인 방해 또는 + 단행위로부터 발생하는 손해 + </li> + <li> + ⑤ 제3자가 서비스를 이용하여 불법적으로 전송, 유포하거나 또는 전송, 유포되도록 한 모든 + 바이러스, 스파이웨어 및 기타 악성 프로그램으로 인한 손해 + </li> + <li>⑥ 전송된 데이터의 오류 및 생략, 누락, 파괴 등으로 발생되는 손해</li> + <li> + ⑦ 회원간의 회원 상태정보 등록 및 서비스 이용 과정에서 발생하는 명예훼손 기타 + 불법행위로 인한 각종 민형사상 책임 + </li> + </ul> + </li> + </ol> + </div> + <div> + <strong>제 14 조 (권리의 귀속)</strong> + <ol> + <li> + 서비스에 대한 저작권 및 지적재산권은 서비스에 귀속됩니다. 단, 회원의 "게시물" 및 + 제휴계약에 따라 제공된 저작물은 제외합니다. + </li> + <li> + 서비스가 제공하는 서비스의 디자인, 서비스가 만든 텍스트, 스크립트(script), 그래픽, 회원 + 상호간 전송 기능 등 서비스가 제공하는 서비스에 관련된 모든 상표, 서비스 마크, 로고 등에 + 관한 저작권 기타 지적재산권은 대한민국 및 외국의 법령에 기하여 서비스가 보유하고 있거나 + 서비스에게 소유권 또는 사용권이 있습니다. + </li> + <li> + 회원은 본 이용약관으로 인하여 서비스를 소유하거나 서비스에 관한 저작권을 보유하게 되는 + 것이 아니라, 서비스로부터 서비스의 이용을 허락 받게 되는 바, 서비스는 정보취득 또는 + 개인용도로만 제공되는 형태로 회원이 이용할 수 있습니다. + </li> + <li> + 회원은 명시적으로 허락된 내용을 제외하고는 서비스를 통해 얻어지는 회원 상태정보를 영리 + 목적으로 사용, 복사, 유통하는 것을 포함하여 서비스가 만든 텍스트, 스크립트, 그래픽의 회원 + 상호간 전송기능 등을 복사하거나 유통할 수 없습니다. + </li> + <li> + 서비스는 서비스와 관련하여 회원에게 서비스가 정한 이용조건에 따라 계정, + "아이디", 콘텐츠 등을 이용할 수 있는 이용권만을 부여하며, 회원은 이를 양도, + 판매, 담보제공 등의 처분행위를 할 수 없습니다. + </li> + <li> + 서비스는 서비스와 관련하여 회원에게 서비스가 정한 이용조건에 따라 계정, ID, 콘텐츠 등을 + 이용할 수 있는 이용권만을 부여하며, 이용자는 서비스를 이용함으로써 얻은 정보를 서비스의 + 사전 승낙 없이 복제, 송신, 출판, 배포, 방송 등 기타 방법에 의하여 영리 목적으로 이용하거나 + 제3자에게 이용하게 하여서는 안됩니다. + </li> + </ol> + </div> + <div> + <strong>제 15 조 (회원에 대한 통지)</strong> + <ol> + <li> + 서비스가 회원에 대한 통지를 하는 경우, 회원이 가입신청 시 서비스에 제출한 전자우편 주소나 + SMS로 할 수 있습니다. + </li> + <li> + 서비스는 불특정다수 회원에 대한 통지의 경우, 1주일 이상 사이트에 게시함으로써 개별 통지에 + 갈음할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 16 조 (서비스의 의무)</strong> + <ol> + <li> + 서비스는 관련법과 이 약관이 금지하거나 미풍양속에 반하는 행위를 하지 않으며, 계속적이고 + 안정적으로 서비스를 제공하기 위하여 최선을 다하여 노력합니다. + </li> + <li>서비스는 관계 법령이 정한 의무사항을 준수합니다.</li> + </ol> + </div> + <div> + <strong>제 17 조 (개별 서비스에 대한 약관 및 이용조건)</strong> + <span> + 서비스는 제공하는 서비스내의 개별 서비스에 대한 별도의 약관 및 이용조건을 둘 수 있으며 + 개별서비스에서 별도로 적용되는 약관에 대한 동의는 회원이 개별서비스를 최초로 이용할 경우 + 별도의 동의절차를 거치게 됩니다. 이 경우 개별 서비스에 대한 이용약관 등이 본 약관에 + 우선합니다. + </span> + </div> + <div> + <strong>제 18 조 (서비스 이용시간)</strong> + <span> + 서비스의 이용은 서비스의 업무상 또는 기술상 특별한 지장이 없는 한 연중무휴 1일 24시간을 + 원칙으로 합니다. 다만 정기 점검 등의 필요로 서비스가 정한 날이나 시간은 제외됩니다. + 정기점검시간은 서비스제공화면에 공지한 바에 따릅니다. + </span> + </div> + <div> + <strong>제 19 조 (서비스 이용)</strong> + <ol> + <li> + 회사의 플랫폼 서비스는 정보제공의 목적으로 제공되는 것이며 실제 + <span>금융상품에 관련된 주식이나,</span> 파생상품 매매를 위해 사용할 경우 정보의 오류 및 + 지연으로 인한 피해에 대해 회사는 어떠한 책임도 없습니다. + </li> + <li> + 회사의 플랫폼 서비스에 나오는 트레이더의 투자성과 수익률 기타 관련된 모든 정보 및 자료에 + 대해서 시스메틱 회사는 정보의 신뢰여부에 대해 책임지지 않으며 일반회원이 모든 정보에 + 대해서는 직접 판단하여 결정해야 합니다. <br /> + <span>시스메틱 서비스에서의 모든 정보와 자료는</span> 투자권유 행위가 아니며 회원의 서비스 + 이용과 관련하여 결과의 대한 책임은 모두 회원에게 있습니다. + </li> + <li> + 회사의 플랫폼 서비스는 상품정보에 대해 회사는 접수, 표시, 관리업무만 대행하며 최종적인 + 상품정보에 대한 정확성과, 신뢰의 대한 판단은 회원에게 있으며 회사는 어떠한 책임도 + 없습니다. + </li> + </ol> + </div> + <div> + <strong>제 20조 (서비스 제공의 변경)</strong> + <ol> + <li> + 서비스는 이용 감소로 인한 원활한 서비스 제공의 곤란 및 수익성 악화, 기술 진보에 따른 + 차세대 서비스로의 전환 필요성, 서비스 제공과 관련한 서비스 정책의 변경 등 기타 상당한 + 이유가 있는 경우에 운영상, 기술상의 필요에 따라 제공하고 있는 전부 또는 일부 서비스를 변경 + 또는 중단할 수 있습니다. + </li> + <li> + 서비스는 무료로 제공되는 서비스의 일부 또는 전부를 서비스의 정책 및 운영의 필요상 수정, + 중단, 변경할 수 있으며, 이에 대하여 관련법에 특별한 규정이 없는 한 회원에게 별도의 보상을 + 하지 않습니다. + </li> + <li> + 서비스의 내용, 이용방법, 이용시간에 대하여 변경 또는 서비스 중단이 있는 경우에는 변경 또는 + 중단될 서비스의 내용 및 사유와 일자 등은 그 변경 또는 중단 전에 서비스 + "웹사이트" 또는 서비스 내 "공지사항" 화면 등 회원이 충분이 인지할 수 + 있는 방법으로 30일의 기간을 두고 사전에 공지합니다. + </li> + </ol> + </div> + <div> + <strong>제 21조 (서비스 중지)</strong> + <ol> + <li> + 회사는 다음 각 호에 해당되는 경우 서비스 제공을 중지할 수 있습니다. + <ul className={cx('no-bullet-list')}> + <li>① 서비스용 설비의 보수 등 공사로 인한 부득이한 경우</li> + <li>② 전기통신사업법에 규정된 기간 통신 사업자가 전기통신 서비스를 중지했을 경우</li> + <li>③ 기타 불가항력적 사유가 있는 경우</li> + </ul> + </li> + <li> + 회사는 국가 비상사태, 정전, 서비스 설비의 장애 또는 서비스 이용의 폭주 등으로 정상적인 + 서비스 이용에 지장이 있는 때에는 서비스의 전부 또는 일부를 제한하거나 정지할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제22조(서비스 이용제한)</strong> + <ol> + <li> + 회사는 회원이 다음 각호의1에 해당하는 경우에는 회원의 서비스 이용을 일부 또는 전부 제한할 + 수 있습니다. + <ul className={cx('no-bullet-list')}> + <li>① 제10조 “회원, 이용자의 의무” 각항의 규정에 따른 의무를 이행하지 않는 경우</li> + <li>② 타인명의 신청 또는 허위의 신청, 중복가입인 것이 확인된 경우</li> + <li>③ 다량의 정보를 전송하여 서비스의 안정적 운영을 방해하는 경우</li> + <li>④ 수신자의 의사에 반하는 광고성 정보, 전자우편을 지속적으로 전송하는 경우</li> + <li> + ⑤ 정보통신설비의 오작동이나 정보 등의 파괴를 유발하는 컴퓨터 바이러스 등을 유포하는 + 경우 + </li> + <li>⑥ 타인의 지적재산권을 침해하는 경우</li> + <li>⑦ 서비스를 이용하여 타인의 명예를 훼손하는 행위를 하는 경우</li> + <li>⑧ 정보통신윤리위원회로부터의 이용제한 요구대상인 경우</li> + <li>⑨ 선거관리위원회의 유권해석상의 불법선거운동을 하는 경우</li> + <li>⑩ 다른 회원의 회원 아이디를 부정하게 사용하는 경우</li> + <li>⑪ 서비스를 이용하여 얻은 정보를 회사의 동의 없이 상업적으로 이용하는 경우</li> + <li>⑫ 전기통신관련법령 또는 이 약관의 규정을 위반하는 행위를 하는 경우</li> + </ul> + </li> + <li> + 서비스는 전항에도 불구하고, "주민등록법"을 위반한 명의도용 및 결제도용, 전화번호 + 도용, "저작권법" 및 "컴퓨터프로그램보호법"을 위반한 불법프로그램의 + 제공 및 운영방해, "정보통신망법"을 위반한 불법통신 및 해킹, 악성프로그램의 배포, + 접속권한 초과행위 등과 같이 관련법을 위반한 경우에는 즉시 영구이용정지를 할 수 있습니다. + 본 항에 따른 영구이용정지 시 서비스 이용을 통해 획득한 혜택 등도 모두 소멸되며, 서비스는 + 이에 대해 별도로 보상하지 않습니다. + </li> + <li> + 전항의 규정에 의하여 회원의 이용을 제한하는 경우의 제한의 종류 및 기간 등 구체적인 기준은 + 회사의 공지, 이용안내에서 별도로 정하는 바에 의합니다. + </li> + <li> + 회원은 본 조에 따른 이용제한 등에 대해 서비스가 정한 절차에 따라 이의신청을 할 수 + 있습니다. 이 때 이의가 정당하다고 서비스가 인정하는 경우 즉시 서비스의 이용을 재개합니다. + </li> + </ol> + </div> + <div> + <strong>제 23조 (광고에 대한 동의)</strong> + <span> + 회원은 회사가 광고, 정보 등을 회원에게 전자우편, 전자통신장비 등의 방법으로 송신하는 것에 + 대하여 이 약관을 통하여 동의합니다. + </span> + </div> + <div> + <strong>제 24조 (손해배상)</strong> + <span> + 회사에서 제공하는 서비스의 이용과 관련하여 회사는 고의가 없는 한 회원에게 발생한 손해를 + 배상하지 않습니다. + </span> + </div> + <div> + <strong>제 25조 (개인정보보호)</strong> + <ol> + <li> + 서비스는 회원의 개인정보를 보호하기 위하여 정보통신망법 및 개인정보 보호법 등 관계 + 법령에서 정하는 바를 준수합니다. + </li> + <li> + 서비스는 회원의 개인정보를 보호하기 위하여 개인정보취급방침을 제정, 서비스 화면에 + 게시합니다. 다만, 개인정보취급방침의 구체적 내용은 연결화면을 통하여 볼 수 있습니다. + </li> + <li> + 서비스는 개인정보취급방침에 따라 회원의 개인정보를 최대한 보호하기 위하여 노력합니다. + </li> + <li> + 서비스의 공식 사이트 이외의 링크된 사이트에서는 서비스의 개인정보취급방침이 적용되지 + 않습니다. 링크된 사이트 및 서비스를 제공하는 제3자의 개인정보 취급과 관련하여는 해당 + 사이트 및 제3자의 개인정보취급방침을 확인할 책임이 회원에게 있으며, 서비스는 이에 대하여 + 책임을 부담하지 않습니다. + </li> + <li> + 서비스는 다음과 같은 경우에 법이 허용하는 범위 내에서 회원의 개인정보를 제3자에게 제공할 + 수 있습니다. + <ul className={cx('no-bullet-list')}> + <li>① 수사기관이나 기타 정부기관으로부터 정보제공을 요청 받은 경우</li> + <li> + ② 회원의 법령 또는 약관의 위반을 포함하여 부정행위 확인 등의 정보보호 업무를 위해 + 필요한 경우 + </li> + <li>③ 기타 법률에 의해 요구되는 경우</li> + </ul> + </li> + </ol> + </div> + <div> + <strong>제 26조 (면책 조항)</strong> + <ol> + <li> + 서비스는 천재지변 또는 이에 준하는 불가항력으로 인하여 서비스를 제공할 수 없는 경우에는 + 서비스 제공에 관한 책임이 면제됩니다. + </li> + <li>서비스는 회원의 귀책사유로 인한 서비스 이용의 장애에 대하여 책임을 지지 않습니다.</li> + <li> + 서비스는 회원이 서비스를 이용하여 얻은 자료로 인한 손해에 관하여 책임을 지지 않습니다. + </li> + <li> + 서비스는 회원이 게재한 정보, 자료, 사실의 신뢰도, 정확성 등 내용에 관해서는 책임을 지지 + 않습니다. + </li> + <li>회원이 발송한 메일 내용에 대한 법적인 책임은 회원에게 있습니다.</li> + <li> + 서비스는 회원간 또는 회원과 제3자 상호간에 서비스를 매개로 하여 거래 등을 한 경우에는 + 책임이 면제됩니다. + </li> + <li> + 서비스는 무료로 제공되는 서비스 이용과 관련하여 관련법에 특별한 규정이 없는 한 책임을 지지 + 않습니다. + </li> + </ol> + </div> + <div> + <strong>제 27조 (준거법 및 관할법원)</strong> + <ol> + <li>이 약관의 해석 및 서비스와 회원간의 분쟁에 대하여는 대한민국의 법을 적용합니다.</li> + <li> + 서비스 이용 중 발생한 회원과 서비스간의 소송은 민사소송법에 의한 + 관할법원(서울중앙지방법원)에 제소합니다. + </li> + </ol> + </div> + <div> + <strong>부칙 1.</strong> + <span>이 약관은 2024년 12월 1일부터 적용됩니다.</span> + </div> + </div> +) + +export default InvestorTerms diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/trader-terms.tsx b/app/(landing)/signup/terms-of-use/_ui/terms/trader-terms.tsx new file mode 100644 index 00000000..e9160ed1 --- /dev/null +++ b/app/(landing)/signup/terms-of-use/_ui/terms/trader-terms.tsx @@ -0,0 +1,717 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const TraderTerms = () => ( + <div className={cx('container')}> + <h3>파트너(트레이더/전문투자자) 이용약관</h3> + <div> + <strong>제 1 조 (목적)</strong> + <span> + 이 약관은 시스메틱(이하 "회사")주식회사가 온라인으로 제공하는 + <strong> 인베스트메틱</strong> 플랫폼 서비스 (웹, 모바일 서비스 이하 서비스라 한다)을 + 통해 들어온 일반 회원들에게 트레이더 혹은 전문투자자(이하 "파트너"라고 한다)가 + 직접 금융상품의 운용성과를 게시하고 관리와 같은 업무 수행을 함에 있어 관련 법령을 준수하고 + 이용에 관한 권리, 의무 및 책임사항 등 신의성실의 원칙에 따라 업무에 임하는 것을 목적으로 + 합니다. + </span> + <br /> + <span> + 본 약관은 계약에 따른 업무 수행을 함에 있어 관련 법령을 준수하고 이용에 관한 권리, 의무 및 + 책임사항 등 신의성실의 원칙에 따라 업무에 임하는 것을 목적으로 합니다. 체결하게 되는 계약은 + 회사와 파트너가 각각 서비스 이용 계약에 따라 회사의 서비스 이용 약관에 동의하는 것으로 그 + 효력이 있으며 계약에 명시되지 않은 항목에 대해서는 회사의 이용약관을 따르게 됩니다. + </span> + </div> + <div> + <strong>제 2 조 (용어의 정의)</strong> + <span>이 약관에서 사용하는 용어의 정의는 다음 각 호와 같다.</span> + <ol> + <li> + "플랫폼"은 이용자가 컴퓨터, 휴대형단말기 등 유·무선 정보통신설비를 통하여 + 투자성과의 상담 및 수익률 분석, 투자성과 정보 입력 등의 방식으로 참여할 수 있도록 하는 + 회사의 플랫폼을 말합니다. + </li> + <li> + "트레이더"는 본인의 실 금융거래 투자 성과 들을 올리는 전문투자자를 의미하며 개인 + 및 법인형태로 존재가능합니다. + </li> + <li> + "일반회원 또는 투자가"는 플랫폼에 접속하여, 표준 가입약관에 따라 회사와 + 약관동의를 체결하고 회원 가입을 한 자로서 회사와 파트너가 제공하는 정보를 지속적으로 + 제공받으며, 플랫폼을 계속적으로 이용할 수 있는 자를 의미합니다. + </li> + </ol> + </div> + <div> + <strong>제 3 조 (약관의 명시와 설명 및 개정)</strong> + <ol> + <li> + 서비스는 이 약관의 내용을 이용자가 쉽게 알 수 있도록 서비스 화면에 게시합니다. 다만, + 약관의 구체적 내용은 이용자가 연결화면을 통하여 볼 수 있습니다. + </li> + <li> + 서비스는 『약관의 규제 등에 관한 법률』,『전자서명법』, 『정보통신망 이용촉진 및 정보보호 + 등에 관한 법률』(정보통신망법) 등 관련법을 위배하지 않는 범위에서 이 약관을 개정할 수 + 있습니다. + </li> + <li> + 서비스가 약관을 개정할 경우에는 적용일자 및 개정사유를 명시하여 현행 약관과 함께 사이트의 + 공지사항에 그 개정약관의 적용일자 30일 전부터 적용일자 전일까지 공지합니다. 다만, 회원에게 + 불리한 약관의 개정의 경우에는 공지 외에 일정기간 서비스 내 전자우편, 로그인 시 동의창 등의 + 전자적 수단을 통하여 따로 명확히 통지하도록 합니다. + </li> + <li> + 제3항에 따라 공지된 적용일자 이후에 회원이 서비스를 계속 이용하는 경우에는 개정된 약관에 + 동의하는 것으로 봅니다. 개정된 약관에 동의하지 아니하는 회원은 언제든지 자유롭게 서비스 + 이용계약을 해지할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 4 조 (역할과 의무)</strong> + <br /> + <span>계약의 성립과 계약 기간의 개시와 종료</span> + <ol> + <li> + 본 계약은 해당 업무에 대해 회사와 파트너가 <span>시스메틱</span> 플랫폼에 가입하여 상호 + 확인함으로써 성립하게 됩니다. 그에 따른 계약 기간은 파트너의 타입형태의 가입시부터 + 탈퇴시까지 유효합니다. + </li> + <li> + 계약성립은 <span>시스메틱</span> 플랫폼에 파트너가 트레이더 타입 형태로 가입신청을 + 완료하고 이를 회사에서 승인함으로써 성립합니다. 승인 시 파트너는 반드시 실제 본인이 + 거래하는 실계좌의 성과만을 플랫폼에 올릴 수 있으며 업로드 후 회사의 승인하게 전략성과가 + 게재됩니다. + </li> + </ol> + <span>계약개시 및 종료 후 책임과 의무</span> + <ol> + <li> + 파트너는 계약의 종료 후에는 <span>시스메틱</span> 플랫폼 내에서 취득한 고객정보와 + 관련정보를 개인의 목적으로 활용할 수 없습니다. 계약기간이라도 <span>시스메틱</span> 의 + 규정에 위배되는 행위, 준법규정에 위배되는 행위 등이 발견 될 경우 1차 경고 할 수 있으며 + 재차 위배행위가 발생할 경우 회사는 일방적으로 파트너와의 계약을 해지할 수 있습니다. + </li> + <li> + 파트너는 법령, 관계 기관 규정 등 법적 규제 내에서 관련 서비스를 제공하며, 해당 서비스는 + <span>시스메틱</span>의 내부 규정에 따라야 합니다. + </li> + <li> + <span>시스메틱</span>의 법적 기준 내에서 통합 서비스가 제공 될 수 있도록 하며, 시스메틱의 + 법적 기준 및 관련 내용을 파트너는 충실히 이행하여야 합니다. 만약, 이를 위반할 경우, 모든 + 법적 책임은 파트너에게 귀속됩니다. + </li> + <li> + 파트너는 일반회원과 투자자의 원만한 관계를 위해서, 적극적 마케팅 활동 및 대외 홍보에 + 최선을 다하여, 최상의 서비스를 제공해야 합니다. + </li> + </ol> + <span>트레이더의 책임과 직접적인 의무</span> + <ol> + <li> + 트레이더들은 투자를 운용하는 운용 형태에 따라, 자동 거래(System/Auto Trading)과 수동 + 거래(Manual Trading)으로 구분되며 그 중간 형태인 반자동(Hybrid Trading) 형태가 있습니다. + 트레이더는 시스메틱에 기본 전략명을 정하고 해당 실적과 형태, 방식 등을 시스메틱에서 정한 + 규칙에 의해 직접 등록해야 합니다.{' '} + </li> + <li> + 트레이더는 투자 성과에 대한 정보를 공유할 수 있습니다. 특히, 성과를 외부에 공개 혹은 + 비공개를 선택하여, 등록 및 관리합니다. 비공개 상품은 시스메틱 상품랭킹 페이지에 노출되지 + 않으며 일정기간 성과정보를 업로드 하지 않을 경우 회사에서는 직접 임의적으로 트레이더에게 + 통보없이 공개를 비공개로 전환할 수 있습니다 + </li> + <li> + 트레이더는 상품등록시 본인의 판단하에 공개, 비공개를 정할 수 있으며 비공개 일 경우 해당 + 비공개 상품의 성과가 공개된 인터넷 주소를 통해 개별적으로 마케팅활동이나 개인적인 용도로 + 활옹이 가능합니다. 또한 포트폴리오를 통해 본인만의 상품관리를 활용할 수 있습니다.{' '} + </li> + <li> + 트레이더는 상품의 운용성과 실적을 조작하거나 임의로 변경하거나 할 경우 형사상, 민사상 법적 + 처벌을 받을 수 있으며 이에 대한 모든 귀책 사유는 트레이더 책임으로 간주됩니다.{' '} + </li> + </ol> + <span> + 이 약관 또는 개별약관에서 정하지 않은 사항은 전기통신사업법, 정보통신망법 등에서의 + 소비자보호에 관한 법률, 개인정보보호법 등 관련 법령의 규정과 일반적인 상관례에 의합니다. + </span> + </div> + <div> + <strong>제 5 조 (권리와 의무)</strong> + <ol> + <li> + <span>시스메틱</span> 에 제공되는 모든 정보에 대해서 지적 소유권은 회사가 갖습니다. 다만, + 파트너와 일반(투자자) 회원 등이 합의에 이루어진 공동 정보 및 기획은 사전에 합의한 기준에 + 따라서, 그 소유권이 개별적 귀속됩니다. 여기서 말하는 공동 기획 및 기존의 서비스가 아닌, + 합의에 따른 신규 서비스를 의미합니다. + </li> + <li> + <span>시스메틱</span>에서, 제휴한 회사 이외에 재판매 및 정보 서비스를 제공할 경우, 모든 + 판매권 및 운영권은 현재 계약이 이행되는 한 회사로 자동 귀속됩니다. + </li> + <li> + 본 계약과 관련된 모든 서비스와 관련하여, 장애가 발생하였을 경우, 상호 협조하여, 신속히 + 조치 합니다. + </li> + <li> + 비즈니스의 안정화를 위하여, 필요한 추가 컨텐츠 및 솔루션 개발을 상호 지원하며, 공동 + 프로모션 및 홍보에 노력합니다. + </li> + <li> + 파트너와 일반(투자가) 회원에게 제공되는 모든 서비스의 제공 방법과 내용들은 자본 시장 + 법률에 위반되는 내용이 아니어야 합니다. + </li> + <li> + 일반(투자자)가 제공하는 정보에 오류 및 문제의 소지가 있을 경우, 회사는 이를 시정을 요구할 + 수 있으며, 파트너는 이를 적극 수용해야 합니다. 만약, 이를 수용하지 않을 경우, 회사는 해당 + 관련 서비스 및 계약 내용에 동의한 서비스를 제한하거나 필요한 제제 가할 수 있습니다. + </li> + <li> + 해당 제휴 업무로 습득하게 된 이용자 정보 및 개발 계획, 마케팅 전략, 등의 영업 상의 비밀 + 내용들을 계약 기간 이후에도 제 3자에게 제공하거나, 계약 이행 목적 이외 다른 용도로 + 사용되어서는 아니되며, 이에 대하여 회사가 손해가 발생할 경우, 파트너는 이를 전액 배상할 + 책임이 있습니다. + </li> + <li> + <span>시스메틱</span>에서 제공된 모든 컨텐츠 (온라인, 무선 인터넷, 스마트폰, 태블릿, + 연계된 APPLICATIONS 등) 중, 무료로 이용되는 것들을, 외부에 유료로 제공할 수 없습니다. (단, + 사전에 합의된 경우 제외) + </li> + <li> + <span>시스메틱</span>에서 제공한 서비스 이외에 발생한 분쟁에 대해서, 이용자에게 피해 + 사실이 확인될 경우, 파트너는 모든 손해에 대해 배상하여야 합니다. + </li> + </ol> + </div> + <div> + <strong>제 6 조 (이용계약의 성립)</strong> + <ol> + <li> + 이용계약은 회원이 되고자 하는 자(이하 "가입신청자")가 서비스가 정한 가입 양식에 + 따라 회원정보(전자우편주소, 비밀번호, 이름, 연락처 등)를 기입하여 회원가입신청을 하고 + 서비스가 이러한 신청에 대하여 승인함으로써 체결됩니다. + </li> + <li> + 서비스는 다음 각 호에 해당하는 신청에 대하여는 승인을 하지 않거나 사후에 이용계약을 해지할 + 수 있습니다. + <ul className={cx('no-bullet-list')}> + <li> + 1) 가입신청자가 이 약관에 의하여 이전에 회원자격을 상실한 적이 있는 경우. 다만, + 회원자격 상실 후 3개월이 경과한 자로서 서비스의 회원 재가입 승낙을 얻은 경우에는 + 예외로 함 + </li> + <li>2) 타인의 명의를 이용한 경우</li> + <li> + 3) 서비스가 실명확인절차를 실시할 경우에 이용자의 실명가입신청이 사실 아님이 확인된 + 경우 + </li> + <li> + 4) 서비스에 의하여 이용계약이 해지된 날로부터3개월 이내에 재이용 신청을 하는 경우 + </li> + <li>5) 등록내용에 허위의 정보를 기재하거나, 기재누락, 오기가 있는 경우</li> + <li>6) 이미 가입된 회원과 전화번호나 전자우편주소가 동일한 경우</li> + <li>7) 부정한 용도 또는 영리를 추구할 목적으로 본 서비스를 이용하고자 하는 경우</li> + <li> + 8) 기타 이 약관에 위배되거나 위법 또는 부당한 이용신청임이 확인된 경우 및 서비스가 + 합리적인 판단에 의하여 필요하다고 인정하는 경우 + </li> + <li> + 9) 관계법령에 위배되거나 사회의 안녕질서 혹은 미풍양속을 저해할 수 있는 목적으로 + 신청한 경우 + </li> + <li> + 10) 이용자의 귀책사유로 인하여 승인이 불가능하거나 기타 규정한 제반 사항을 위반하여 + 신청하는 경우 + </li> + </ul> + </li> + <li> + 제1항에 따른 신청에 있어 서비스는 회원의 종류에 따라 전문기관을 통한 실명확인 및 + 본인인증을 요청할 수 있습니다. + </li> + <li> + 서비스는 서비스관련설비의 여유가 없거나, 기술상 또는 업무상 문제가 있는 경우에는 승낙을 + 유보할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 7 조 (이용계약의 종료)</strong> + <ol> + <li> + 회원의 해지 + <ul className={cx('no-bullet-list')}> + <li> + 1) 회원은 언제든지 서비스에게 해지의사를 통지함으로써 이용계약을 해지할 수 있습니다. + </li> + <li>2) 이용계약은 회원의 해지의사가 서비스에 도달한 때에 종료 됩니다.</li> + </ul> + </li> + <li> + 서비스의 해지 + <ul className={cx('no-bullet-list')}> + <li> + 1) 서비스는 다음과 같은 사유가 있는 경우, 이용계약을 해지할 수 있습니다. 이 경우 + 서비스는 회원에게 전자우편, 전화, 팩스 기타의 방법을 통하여 해지사유를 밝혀 해지의사를 + 통지합니다. 다만 서비스는 해당 회원에게 사전에 해지사유에 대한 의견진술의 기회를 부여 + 할 수 있습니다. + <span> + ① 제6조 제2항에서 정하고 있는 이용계약의 승낙거부사유가 있음이 확인된 경우 + </span> + <span> + ② 회원이 서비스나 다른 회원 기타 타인의 권리나 명예, 신용 기타 정당한 이익을 + 침해하는 행위를 한 경우 + </span> + <span> + ③ 기타 회원이 이 약관에 위배되는 행위를 하거나 이 약관에서 정한 해지사유가 발생한 + 경우 + </span> + </li> + <li> + 2) 이용계약은 서비스가 해지의사를 회원에게 통지함으로써 종료됩니다. 이 경우 서비스가 + 해지의사를 회원이 등록한 전자우편주소로 발송하거나 서비스 게시판에 게시함으로써 통지에 + 갈음합니다. + </li> + </ul> + </li> + <li> + 이용계약의 종료와 관련하여 발생한 손해는 이용계약이 종료된 해당 회원이 책임을 부담하여야 + 하고 서비스는 일체의 책임을 지지 않습니다. + </li> + </ol> + </div> + <div> + <strong>제 8 조 (회원탈퇴 및 자격 상실)</strong> + <ol> + <li> + 회원은 언제든지 서비스 내<span>"계정삭제 및 회원탈퇴"</span> 화면을 통하여 + 이용계약 해지 신청을 할 수 있으며, 서비스는 관련법 등이 정하는 바에 따라 이를 즉시 + 처리하여야 합니다. + </li> + <li> + 회원이 계약을 해지할 경우, 관련법 및 개인정보취급방침에 따라 서비스가 회원정보를 보유하는 + 경우를 제외하고는 해지 즉시 회원의 개인정보는 소멸됩니다. 또한 회원의 아이디는 재사용을 할 + 수 없습니다. + </li> + <li>회원이 계약을 해지하는 경우, 회원이 작성한 게시물 일체는 삭제되지 않습니다.</li> + </ol> + </div> + <div> + <strong>제 9 조 (회원의 ID 및 비밀번호에 대한 의무)</strong> + <ol> + <li>ID와 비밀번호에 관한 관리책임은 회원에게 있습니다.</li> + <li>회원은 자신의ID 및 비밀번호를 제3자에게 이용하게 해서는 안됩니다.</li> + <li> + 회원이 자신의ID 및 비밀번호를 도난 당하거나 제3자가 사용하고 있음을 인지한 경우에는 즉시 + 서비스에 통보하고 서비스의 조치가 있는 경우에는 그에 따라야 합니다. + </li> + <li> + 회원이 제3항에 따른 통지를 하지 않거나 서비스의 조치에 응하지 아니하여 발생하는 모든 + 불이익에 대한 책임은 회원에게 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 10 조 (회원, 이용자의 의무)</strong> + <ol> + <li> + 회원은 관계법령이 약관의 규정, 이용안내 등 서비스가 통지하는 사항을 준수하여야 하며, 기타 + 서비스 업무에 방해되는 행위를 하여서는 안됩니다. + </li> + <li> + 회원은 서비스 이용과 관련하여 다음 각 호의 행위를 하여서는 안됩니다. + <ul className={cx('no-bullet-list')}> + <li>1) 서비스 신청 또는 변경 시 허위내용의 등록</li> + <li>2) 서비스에 게시된 정보의 허가 받지 않은 변경</li> + <li>3) 서비스가 정한 정보 이외의 정보(컴퓨터 프로그램 등)의 송신 또는 게시</li> + <li>4) 서비스 기타 제3자의 저작권 등 지적 재산권에 대한 침해</li> + <li>5) 서비스 기타 제3자의 명예를 손상시키거나 업무를 방해하는 행위</li> + <li> + 6) 외설 또는 폭력적인 메시지, 화상, 음성 기타 공공질서 미풍양속에 반하는 정보를 + 서비스에 공개 또는 게시하는 행위 + </li> + <li>7) 기타 관계 법령이나 서비스에서 정한 규정에 위배되는 행위</li> + <li>8) 정당한 사유 없이 당사의 영업을 방해하는 내용을 기재하는 행위</li> + <li> + 9) 리버스엔지니어링, 디컴파일, 디스어셈블 및 기타 일체의 가공행위를 통하여 서비스를 + 복제, 분해 또는 모방 기타 변형하는 행위 + </li> + <li> + 10) 자동 접속 프로그램 등을 사용하는 등 정상적인 용법과 다른 방법으로 서비스를 + 이용하여 서비스의 서버에 부하를 일으켜 정상적인 서비스를 방해하는 행위 + </li> + <li>11) 기타 관계법령에 위반된다고 판단되는 행위</li> + </ul> + </li> + </ol> + </div> + <div> + <strong>제 11 조 (회원의 게시물)</strong> + <span> + 회원이 작성한 게시물에 대한 모든 권리 및 책임은 이를 게시한 회원에게 있으며, 서비스는 회원이 + 게시하거나 등록하는 서비스의 내용물이 다음 각 항에 해당한다고 판단되는 경우에 사전통지 없이 + 삭제할 수 있고, 이에 대하여 서비스는 어떠한 책임도 지지 않습니다. + </span> + <ol> + <li>다른 회원 또는 제3자를 비방하거나 중상모략으로 명예를 손상시키는 내용인 경우</li> + <li>공공질서 및 미풍양속에 위반되는 내용일 경우</li> + <li>범죄적 행위에 결부된다고 인정되는 경우</li> + <li>서비스의 저작권, 제3자의 저작권 등 기타 권리를 침해하는 내용인 경우</li> + <li>회원이 사이트와 게시판에 음란물을 게재하거나 음란사이트를 링크하는 경우</li> + <li>서비스로부터 사전 승인 받지 아니한 상업광고, 판촉 내용을 게시하는 경우</li> + <li>해당 상품과 관련 없는 내용인 경우</li> + <li>정당한 사유 없이 서비스의 영업을 방해하는 내용을 기재하는 경우</li> + <li>기타 관계법령에 위반된다고 판단되는 경우</li> + </ol> + </div> + <div> + <strong>제 12 조 (회원게시물의 관리)</strong> + <ol> + <li> + 회원의 "게시물"이 정보통신망법 및 저작권법 등 관련법에 위반되는 내용을 포함하는 + 경우, 권리자는 관련법이 정한 절차에 따라 해당 "게시물"의 게시중단 및 삭제 등을 + 요청할 수 있으며, 서비스는 관련법에 따라 조치를 취하여야 합니다. + </li> + <li> + 서비스는 전항에 따른 권리자의 요청이 없는 경우라도 권리침해가 인정될 만한 사유가 있거나 + 기타 서비스 정책 및 관련법에 위반되는 경우에는 관련법에 따라 해당 "게시물"에 + 대해 임시조치 등을 취할 수 있습니다. + </li> + <li> + 본 조에 따른 세부절차는 정보통신망법 및 저작권법이 규정한 범위 내에서 서비스가 정한 + 게시중단요청서비스에 따릅니다. + </li> + </ol> + <span>- 게시중단요청 : ceo@sysmetic.co.kr</span> + </div> + <div> + <strong>제 13 조 (책임제한)</strong> + <ol> + <li> + 천재지변 또는 이에 준하는 불가항력으로 인하여 서비스를 제공할 수 없는 경우에는 서비스 + 제공에 관한 책임이 면제됩니다. + </li> + <li>회원의 귀책사유로 인한 서비스 이용의 장애에 대하여는 책임을 지지 않습니다.</li> + <li> + 회원이 서비스와 관련하여 게재한 정보, 자료, 사실의 신뢰도, 정확성 등의 내용에 관하여는 + 책임을 지지 않습니다. + </li> + <li> + 회원 간 또는 회원과 제3자 상호간에 서비스를 매개로 하여 거래 등을 한 경우에는 책임이 + 면제됩니다. + </li> + <li> + 무료로 제공되는 서비스 이용과 관련하여 관련법에 특별한 규정이 없는 한 책임을 지지 + 않습니다. + </li> + <li> + 제3자가 서비스 내 화면 또는 링크된 웹사이트를 통하여 광고한 제품 또는 서비스의 내용과 + 품질에 대하여 감시할 의무 기타 어떠한 책임도 지지 아니합니다. + </li> + <li> + 서비스 및 서비스의 임직원 그리고 대리인은 다음과 같은 사항으로부터 발생하는 손해에 대해 + 책임을 지지 아니합니다. + <ul className={cx('no-bullet-list')}> + <li>① 회원 상태정보의 허위 또는 부정확성에 기인하는 손해</li> + <li> + ② 그 성질과 경위를 불문하고 서비스에 대한 접속 및 서비스의 이용과정에서 발생하는 + 개인적인 손해 + </li> + <li> + ③ 서버에 대한 제3자의 모든 불법적인 접속 또는 서버의 불법적인 이용으로부터 발생하는 + 손해 + </li> + <li> + ④ 서버에 대한 전송 또는 서버로부터의 전송에 대한 제3자의 모든 불법적인 방해 또는 + 단행위로부터 발생하는 손해 + </li> + <li> + ⑤ 제3자가 서비스를 이용하여 불법적으로 전송, 유포하거나 또는 전송, 유포되도록 한 모든 + 바이러스, 스파이웨어 및 기타 악성 프로그램으로 인한 손해 + </li> + <li>⑥ 전송된 데이터의 오류 및 생략, 누락, 파괴 등으로 발생되는 손해</li> + <li> + ⑦ 회원간의 회원 상태정보 등록 및 서비스 이용 과정에서 발생하는 명예훼손 기타 + 불법행위로 인한 각종 민형사상 책임 + </li> + </ul> + </li> + </ol> + </div> + <div> + <strong>제 14 조 (권리의 귀속)</strong> + <ol> + <li> + 서비스에 대한 저작권 및 지적재산권은 서비스에 귀속됩니다. 단, 회원의 "게시물" 및 + 제휴계약에 따라 제공된 저작물은 제외합니다. + </li> + <li> + 서비스가 제공하는 서비스의 디자인, 서비스가 만든 텍스트, 스크립트(script), 그래픽, 회원 + 상호간 전송 기능 등 서비스가 제공하는 서비스에 관련된 모든 상표, 서비스 마크, 로고 등에 + 관한 저작권 기타 지적재산권은 대한민국 및 외국의 법령에 기하여 서비스가 보유하고 있거나 + 서비스에게 소유권 또는 사용권이 있습니다. + </li> + <li> + 회원은 본 이용약관으로 인하여 서비스를 소유하거나 서비스에 관한 저작권을 보유하게 되는 + 것이 아니라, 서비스로부터 서비스의 이용을 허락 받게 되는 바, 서비스는 정보취득 또는 + 개인용도로만 제공되는 형태로 회원이 이용할 수 있습니다. + </li> + <li> + 회원은 명시적으로 허락된 내용을 제외하고는 서비스를 통해 얻어지는 회원 상태정보를 영리 + 목적으로 사용, 복사, 유통하는 것을 포함하여 서비스가 만든 텍스트, 스크립트, 그래픽의 회원 + 상호간 전송기능 등을 복사하거나 유통할 수 없습니다. + </li> + <li> + 서비스는 서비스와 관련하여 회원에게 서비스가 정한 이용조건에 따라 계정, + "아이디", 콘텐츠 등을 이용할 수 있는 이용권만을 부여하며, 회원은 이를 양도, + 판매, 담보제공 등의 처분행위를 할 수 없습니다. + </li> + <li> + 서비스는 서비스와 관련하여 회원에게 서비스가 정한 이용조건에 따라 계정, ID, 콘텐츠 등을 + 이용할 수 있는 이용권만을 부여하며, 이용자는 서비스를 이용함으로써 얻은 정보를 서비스의 + 사전 승낙 없이 복제, 송신, 출판, 배포, 방송 등 기타 방법에 의하여 영리 목적으로 이용하거나 + 제3자에게 이용하게 하여서는 안됩니다. + </li> + </ol> + </div> + <div> + <strong>제 15 조 (회원에 대한 통지)</strong> + <ol> + <li> + 서비스가 회원에 대한 통지를 하는 경우, 회원이 가입신청 시 서비스에 제출한 전자우편 주소나 + SMS로 할 수 있습니다. + </li> + <li> + 서비스는 불특정다수 회원에 대한 통지의 경우, 1주일 이상 사이트에 게시함으로써 개별 통지에 + 갈음할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제 16 조 (서비스의 의무)</strong> + <ol> + <li> + 서비스는 관련법과 이 약관이 금지하거나 미풍양속에 반하는 행위를 하지 않으며, 계속적이고 + 안정적으로 서비스를 제공하기 위하여 최선을 다하여 노력합니다. + </li> + <li>서비스는 관계 법령이 정한 의무사항을 준수합니다.</li> + </ol> + </div> + <div> + <strong>제 17 조 (개별 서비스에 대한 약관 및 이용조건)</strong> + <span> + 서비스는 제공하는 서비스내의 개별 서비스에 대한 별도의 약관 및 이용조건을 둘 수 있으며 + 개별서비스에서 별도로 적용되는 약관에 대한 동의는 회원이 개별서비스를 최초로 이용할 경우 + 별도의 동의절차를 거치게 됩니다. 이 경우 개별 서비스에 대한 이용약관 등이 본 약관에 + 우선합니다. + </span> + </div> + <div> + <strong>제 18 조 (서비스 이용시간)</strong> + <span> + 서비스의 이용은 서비스의 업무상 또는 기술상 특별한 지장이 없는 한 연중무휴 1일 24시간을 + 원칙으로 합니다. 다만 정기 점검 등의 필요로 서비스가 정한 날이나 시간은 제외됩니다. + 정기점검시간은 서비스제공화면에 공지한 바에 따릅니다. + </span> + </div> + <div> + <strong>제 19 조 (서비스 제공의 변경)</strong> + <ol> + <li> + 서비스는 이용 감소로 인한 원활한 서비스 제공의 곤란 및 수익성 악화, 기술 진보에 따른 + 차세대 서비스로의 전환 필요성, 서비스 제공과 관련한 서비스 정책의 변경 등 기타 상당한 + 이유가 있는 경우에 운영상, 기술상의 필요에 따라 제공하고 있는 전부 또는 일부 서비스를 변경 + 또는 중단할 수 있습니다. + </li> + <li> + 서비스는 무료로 제공되는 서비스의 일부 또는 전부를 서비스의 정책 및 운영의 필요상 수정, + 중단, 변경할 수 있으며, 이에 대하여 관련법에 특별한 규정이 없는 한 회원에게 별도의 보상을 + 하지 않습니다. + </li> + <li> + 서비스의 내용, 이용방법, 이용시간에 대하여 변경 또는 서비스 중단이 있는 경우에는 변경 또는 + 중단될 서비스의 내용 및 사유와 일자 등은 그 변경 또는 중단 전에 서비스 + "웹사이트" 또는 서비스 내 "공지사항" 화면 등 회원이 충분이 인지할 수 + 있는 방법으로 30일의 기간을 두고 사전에 공지합니다. + </li> + </ol> + </div> + <div> + <strong>제 20조 (서비스 중지)</strong> + <ol> + <li> + 회사는 다음 각 호에 해당되는 경우 서비스 제공을 중지할 수 있습니다. + <ul className={cx('no-bullet-list')}> + <li>① 서비스용 설비의 보수 등 공사로 인한 부득이한 경우</li> + <li>② 전기통신사업법에 규정된 기간 통신 사업자가 전기통신 서비스를 중지했을 경우</li> + <li>③ 기타 불가항력적 사유가 있는 경우</li> + </ul> + </li> + <li> + 회사는 국가 비상사태, 정전, 서비스 설비의 장애 또는 서비스 이용의 폭주 등으로 정상적인 + 서비스 이용에 지장이 있는 때에는 서비스의 전부 또는 일부를 제한하거나 정지할 수 있습니다. + </li> + </ol> + </div> + <div> + <strong>제21조(서비스 이용제한)</strong> + <ol> + <li> + 회사는 회원이 다음 각호의1에 해당하는 경우에는 회원의 서비스 이용을 일부 또는 전부 제한할 + 수 있습니다. + <ul className={cx('no-bullet-list')}> + <li>① 제10조 “회원, 이용자의 의무” 각항의 규정에 따른 의무를 이행하지 않는 경우</li> + <li>② 타인명의 신청 또는 허위의 신청, 중복가입인 것이 확인된 경우</li> + <li>③ 다량의 정보를 전송하여 서비스의 안정적 운영을 방해하는 경우</li> + <li>④ 수신자의 의사에 반하는 광고성 정보, 전자우편을 지속적으로 전송하는 경우</li> + <li> + ⑤ 정보통신설비의 오작동이나 정보 등의 파괴를 유발하는 컴퓨터 바이러스 등을 유포하는 + 경우 + </li> + <li>⑥ 타인의 지적재산권을 침해하는 경우</li> + <li>⑦ 서비스를 이용하여 타인의 명예를 훼손하는 행위를 하는 경우</li> + <li>⑧ 정보통신윤리위원회로부터의 이용제한 요구대상인 경우</li> + <li>⑨ 선거관리위원회의 유권해석상의 불법선거운동을 하는 경우</li> + <li>⑩ 다른 회원의 회원 아이디를 부정하게 사용하는 경우</li> + <li>⑪ 서비스를 이용하여 얻은 정보를 회사의 동의 없이 상업적으로 이용하는 경우</li> + <li>⑫ 전기통신관련법령 또는 이 약관의 규정을 위반하는 행위를 하는 경우</li> + </ul> + </li> + <li> + 서비스는 전항에도 불구하고, "주민등록법"을 위반한 명의도용 및 결제도용, 전화번호 + 도용, "저작권법" 및 "컴퓨터프로그램보호법"을 위반한 불법프로그램의 + 제공 및 운영방해, "정보통신망법"을 위반한 불법통신 및 해킹, 악성프로그램의 배포, + 접속권한 초과행위 등과 같이 관련법을 위반한 경우에는 즉시 영구이용정지를 할 수 있습니다. + 본 항에 따른 영구이용정지 시 서비스 이용을 통해 획득한 혜택 등도 모두 소멸되며, 서비스는 + 이에 대해 별도로 보상하지 않습니다. + </li> + <li> + 전항의 규정에 의하여 회원의 이용을 제한하는 경우의 제한의 종류 및 기간 등 구체적인 기준은 + 회사의 공지, 이용안내에서 별도로 정하는 바에 의합니다. + </li> + <li> + 회원은 본 조에 따른 이용제한 등에 대해 서비스가 정한 절차에 따라 이의신청을 할 수 + 있습니다. 이 때 이의가 정당하다고 서비스가 인정하는 경우 즉시 서비스의 이용을 재개합니다. + </li> + </ol> + </div> + <div> + <strong>제 22조 (광고에 대한 동의)</strong> + <span> + 회원은 회사가 광고, 정보 등을 회원에게 전자우편, 전자통신장비 등의 방법으로 송신하는 것에 + 대하여 이 약관을 통하여 동의합니다. + </span> + </div> + <div> + <strong>제 23조 (손해배상)</strong> + <span> + 회사에서 제공하는 서비스의 이용과 관련하여 회사는 고의가 없는 한 회원에게 발생한 손해를 + 배상하지 않습니다. + </span> + </div> + <div> + <strong>제 24조 (개인 정보 처리 및 관리)</strong> + <ol> + <li> + 회사와의 계약 기간은 만료 이후에도 기존 업무 이외에 목적을 벗어난, 개인 정보를 이용하거나 + 이를 제 3자에게 제공 또는 외부에 누설해서는 안됩니다. + </li> + <li> + 계약이 해지되거나, 만료된 이후에는 각자 보유하고 있는 개인 정보를 즉시 파기하거나 + 시스메틱에 반납해야 합니다. 아울러, 파기된 개인 정보의 경우, 그 결과를 즉시 시스메틱에 + 통보해야 합니다. + <ul className={cx('no-bullet-list')}> + <li>① 개인정보 처리 현황</li> + <li>② 개인정보의 접근 또는 접속 현황</li> + <li>③ 개인정보 접근 또는 접속 비대상자</li> + <li>④ 목적 이외에 이용 금지</li> + <li>⑤ 암호화 등 안정성 확보 조치 이행</li> + <li>⑥ 그 밖에 개인 정보의 보호를 위하여 필요한 사항들</li> + </ul> + </li> + </ol> + </div> + <div> + <strong>제 26조 (회사의 면책)</strong> + <ol> + <li> + 회사는 천재지변 또는 이에 준하는 불가항력적 사유로 인한 정보통신설비의 보수점검, 교체 또는 + 고장, 통신의 두절 등으로 인하여 일시적 또는 종국적으로 플랫폼을 제공할 수 없는 경우, + 플랫폼 제공에 관한 책임이 면제되고, 이로 인한 이용자의 손해에 대하여 책임을 지지 않습니다. + 이 경우 회사는 플랫폼에 게시하거나 기타의 방법으로 회원들에게 통지하여야 하며, 불가피한 + 경우에는 사후통지로 대체할 수 있습니다. + </li> + <li> + 회사는 파트너에 관한 정보를 제공하는 역할을 할 뿐 회원 간의 또는 회원과 비회원인 이용자 + 간의 투자과정의 문제, 이용자가 플랫폼상의 정보를 통하여 얻은 기대 등으로 인하여 입은 + 손해에 대하여는 책임을 지지 않습니다. + </li> + <li> + 회사는 회원의 다음과 같은 행위로 인한 피해에 대하여 책임을 지지 않습니다. + <ul className={cx('no-bullet-list')}> + <li>① 회원의 허위 또는 과장된 정보의 게시</li> + <li> + ② 파트너, 일반회원(투자가) 간의 당사자간의 개별적 계약을 통해 발생할 수 있는 결과 + </li> + </ul> + </li> + <li> + 회사는 이용자가 혹은 제3자가 플랫폼의 정보를 이용하여 회사가 제공하는 플랫폼을 통하지 + 아니하고 이루어지는 거래 등에 대하여 책임을 지지 않습니다. + </li> + <li> + 회사는 회원 및 비회원이 게시 또는 전송한 자료 및 본 플랫폼으로부터 회원 및 비회원이 + 제공받을 수 있는 모든 자료들의 진위, 신뢰도, 정확성 등 그 내용에 대해서는 책임지지 + 않습니다. + </li> + <li> + 회사는 회원 및 비회원 상호간 또는 회원, 비회원과 제3자 상호간에 플랫폼을 매개로 하여 거래 + 등을 한 경우에 그로부터 발생하는 일체의 손해에 대하여 책임지지 않습니다. + </li> + <li> + 회사는 회원, 비회원이나 제3자에 의해 표출된 어떠한 의견이나 정보에 대해 확신이나 대표할 + 의무가 없으며 의견을 승인하거나 반대하거나 수정하지 않습니다. 회사는 어떠한 경우라도 + 회원이 플랫폼에 담긴 정보에 의존해 얻은 이득이나 입은 손해에 대해 책임이 없습니다. + </li> + <li> + 회사는 회원, 비회원 등이 플랫폼의 이용과 관련하여 기대하는 이익에 관하여 책임을 부담하지 + 않습니다. + </li> + <li> + 회사는 이용자 또는 기타 유관기관이 플랫폼에 게재한 정보에 대해 정확성, 신뢰도에 대하여 + 보장하지 않습니다. 따라서 회사는 이용자가 위 내용을 이용함으로 인해 입게 된 모든 종류의 + 손실이나 손해에 대하여 책임을 부담하지 않습니다. + </li> + <li> + 회사는 이용자의 플랫폼 이용과 관련하여 이용자에게 발생한 손해 중 이용자의 고의, 과실에 + 의한 손해 및 다른 이용자로 인해 입게 되는 정신적 피해에 대하여 보상할 책임을 지지 + 않습니다. + </li> + <li> + 회사는 관련 법령, 정부 정책 등에 의하여 서비스 또는 회원에 따라 서비스 이용시간 등을 + 제한할 수 있으며, 이러한 제한사항 및 제한에 따라 발생하는 서비스 이용 관련 제반사항에 + 대해서는 책임이 면제됩니다. + </li> + </ol> + </div> + <div> + <strong>제 26조 (준거법 및 관할법원)</strong> + <ol> + <li>이 약관의 해석 및 서비스와 회원간의 분쟁에 대하여는 대한민국의 법을 적용합니다.</li> + <li>회사와 파트너는 신의 성실의 원칙에 따라 이행합니다.</li> + <li> + 서비스 이용 중 발생한 회원과 서비스간의 소송은 민사소송법에 의한 + 관할법원(서울중앙지방법원)에 제소합니다. + </li> + </ol> + </div> + <div> + <strong>부칙 1.</strong> + <span>이 약관은 2024년 12월 1일부터 적용됩니다.</span> + </div> + </div> +) + +export default TraderTerms From 46541e5e91bc1f6d7d9b4b5cd1d2327ebd8cab2e Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 11:54:59 +0900 Subject: [PATCH 214/648] =?UTF-8?q?feat:=20Checkbox=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=20=EC=82=AC=EC=9D=B4=EC=A6=88=20pro?= =?UTF-8?q?p=20=EC=B6=94=EA=B0=80=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/check-box/index.tsx | 12 ++++++++++-- shared/ui/check-box/styles.module.scss | 12 +++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/shared/ui/check-box/index.tsx b/shared/ui/check-box/index.tsx index 2ad48b8b..ab743396 100644 --- a/shared/ui/check-box/index.tsx +++ b/shared/ui/check-box/index.tsx @@ -13,9 +13,17 @@ interface Props { onChange: (checked: boolean) => void className?: string textColor?: 'gray500' | 'gray600' | 'gray800' + textSize?: 'c1' | 'b3' | 'b2' } -const Checkbox = ({ label, isChecked, onChange, className = '', textColor = 'gray500' }: Props) => { +const Checkbox = ({ + label, + isChecked, + onChange, + className = '', + textColor = 'gray500', + textSize = 'c1', +}: Props) => { const handleClick = () => { onChange(!isChecked) } @@ -30,7 +38,7 @@ const Checkbox = ({ label, isChecked, onChange, className = '', textColor = 'gra > {isChecked ? <CheckedCircleIcon /> : <CircleIcon />} </div> - {label && <span className={cx('label', textColor)}>{label}</span>} + {label && <span className={cx('label', textColor, textSize)}>{label}</span>} </div> ) } diff --git a/shared/ui/check-box/styles.module.scss b/shared/ui/check-box/styles.module.scss index 17ddf3f5..be766af5 100644 --- a/shared/ui/check-box/styles.module.scss +++ b/shared/ui/check-box/styles.module.scss @@ -26,7 +26,17 @@ } .label { - @include typo-c1; + &.c1 { + @include typo-c1; + } + + &.b2 { + @include typo-b2; + } + + &.b3 { + @include typo-b3; + } &.gray500 { color: $color-gray-500; From 3855312751cfa00cdf657d146bbcf01818833c92 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 15:45:30 +0900 Subject: [PATCH 215/648] =?UTF-8?q?feat:=20TermsContainer=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/terms-container/index.tsx | 32 +++++++++++++++ .../_ui/terms-container/styles.module.scss | 40 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 app/(landing)/signup/terms-of-use/_ui/terms-container/index.tsx create mode 100644 app/(landing)/signup/terms-of-use/_ui/terms-container/styles.module.scss diff --git a/app/(landing)/signup/terms-of-use/_ui/terms-container/index.tsx b/app/(landing)/signup/terms-of-use/_ui/terms-container/index.tsx new file mode 100644 index 00000000..37bd0669 --- /dev/null +++ b/app/(landing)/signup/terms-of-use/_ui/terms-container/index.tsx @@ -0,0 +1,32 @@ +import classNames from 'classnames/bind' + +import Checkbox from '@/shared/ui/check-box' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + title: string + isChecked: boolean + onChange: (checked: boolean) => void + children: React.ReactNode +} + +const TermsContainer = ({ title, isChecked, onChange, children }: Props) => { + return ( + <section className={cx('container')}> + <h2 className={cx('title')}>{title}</h2> + <div className={cx('terms-wrapper')}>{children}</div> + <Checkbox + className={cx('checkbox')} + label="동의합니다." + isChecked={isChecked} + onChange={onChange} + textSize="b3" + /> + </section> + ) +} + +export default TermsContainer diff --git a/app/(landing)/signup/terms-of-use/_ui/terms-container/styles.module.scss b/app/(landing)/signup/terms-of-use/_ui/terms-container/styles.module.scss new file mode 100644 index 00000000..ad2602ff --- /dev/null +++ b/app/(landing)/signup/terms-of-use/_ui/terms-container/styles.module.scss @@ -0,0 +1,40 @@ +.container { + display: flex; + flex-direction: column; + gap: 16px; + margin-bottom: 12px; + + .title { + color: $color-gray-600; + @include typo-b1; + } +} + +.terms-wrapper { + height: 146px; + padding: 16px 40px 16px 16px; + border-radius: 6px; + background-color: $color-white; + overflow-y: scroll; + + &::-webkit-scrollbar { + display: block; + width: 24px; + } + + &::-webkit-scrollbar-track { + border-radius: 4px; + background-color: transparent; + } + + &::-webkit-scrollbar-thumb { + height: 73px; + border: 10px solid $color-white; + border-radius: 100px; + background-color: $color-gray-300; + } +} + +.checkbox { + align-self: flex-end; +} From 04277c49d01b968e4b0ee7b5326164c53169edb5 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 16:37:36 +0900 Subject: [PATCH 216/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=95=BD=EA=B4=80=20=EB=8F=99=EC=9D=98=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_stores/use-signup-store.ts | 4 + .../signup/terms-of-use/page.module.scss | 21 +++++ app/(landing)/signup/terms-of-use/page.tsx | 89 ++++++++++++++++++- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 app/(landing)/signup/terms-of-use/page.module.scss diff --git a/app/(landing)/signup/_stores/use-signup-store.ts b/app/(landing)/signup/_stores/use-signup-store.ts index 2f66b433..cc398197 100644 --- a/app/(landing)/signup/_stores/use-signup-store.ts +++ b/app/(landing)/signup/_stores/use-signup-store.ts @@ -5,10 +5,12 @@ import { UserType } from '@/shared/types/user' interface StateModel { userType: UserType | null nickname: string | null + isAgreedTerm: boolean } interface ActionModel { setUserType: (userType: UserType) => void + setIsAgreedTerm: (isAgreedTerm: boolean) => void } interface ActionsModel { @@ -18,12 +20,14 @@ interface ActionsModel { const initialState = { userType: null, nickname: null, + isAgreedTerm: false, } as const const useSignupStore = create<StateModel & ActionsModel>((set) => ({ ...initialState, actions: { setUserType: (userType) => set((state) => ({ ...state, userType })), + setIsAgreedTerm: (isAgreedTerm) => set((state) => ({ ...state, isAgreedTerm })), }, })) diff --git a/app/(landing)/signup/terms-of-use/page.module.scss b/app/(landing)/signup/terms-of-use/page.module.scss new file mode 100644 index 00000000..e90264c7 --- /dev/null +++ b/app/(landing)/signup/terms-of-use/page.module.scss @@ -0,0 +1,21 @@ +.container { + max-width: 940px; + margin: 0 auto; +} + +.all-check-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding: 15px 0; + margin: 48px 0 32px; + border-radius: 6px; + background-color: $color-white; +} + +.button-wrapper { + display: flex; + justify-content: center; + gap: 48px; + margin-bottom: 70px; +} diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx index 8f528aa6..62dc7832 100644 --- a/app/(landing)/signup/terms-of-use/page.tsx +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -1,14 +1,99 @@ +'use client' + +import { useEffect, useState } from 'react' + +import { useRouter } from 'next/navigation' + +import useSignupStore from '@/app/(landing)/signup/_stores/use-signup-store' import Step from '@/app/(landing)/signup/_ui/step' +import TermsContainer from '@/app/(landing)/signup/terms-of-use/_ui/terms-container' +import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' +import { Button } from '@/shared/ui/button' +import Checkbox from '@/shared/ui/check-box' import { LinkButton } from '@/shared/ui/link-button' +import InvestorTerms from './_ui/terms/investor-terms' +import PrivacyTerms from './_ui/terms/privacy-terms' +import TraderTerms from './_ui/terms/trader-terms' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + const TermsOfUsePage = () => { + const router = useRouter() + const userType = useSignupStore((state) => state.userType) + const isAgreedTerm = useSignupStore((state) => state.isAgreedTerm) + const { setIsAgreedTerm } = useSignupStore((state) => state.actions) + + const [isUserTermChecked, setIsUserTermChecked] = useState(isAgreedTerm) + const [isPrivacyTermChecked, setIsPrivacyTermChecked] = useState(isAgreedTerm) + const [isAllChecked, setIsAllChecked] = useState(isAgreedTerm) + + useEffect(() => { + if (!userType) { + router.push(PATH.SIGN_UP_USER_TYPE) + } + }, [userType, router]) + + const handleAllCheck = () => { + setIsAllChecked(!isAllChecked) + setIsUserTermChecked(!isAllChecked) + setIsPrivacyTermChecked(!isAllChecked) + } + + const handleUserTermCheck = () => { + setIsUserTermChecked(!isUserTermChecked) + setIsAllChecked(!isUserTermChecked && isPrivacyTermChecked) + } + + const handlePrivacyTermCheck = () => { + setIsPrivacyTermChecked(!isPrivacyTermChecked) + setIsAllChecked(!isPrivacyTermChecked && isUserTermChecked) + } + + const handleNextClick = () => { + setIsAgreedTerm(isAllChecked) + router.push(PATH.SIGN_UP_INFORMATION) + } + return ( <> <Step /> - <LinkButton href={PATH.SIGN_UP_INFORMATION}>다음</LinkButton> - <LinkButton href={PATH.SIGN_UP_USER_TYPE}>이전</LinkButton> + <div className={cx('container')}> + <div className={cx('all-check-wrapper')}> + <Checkbox + label=" 이용약관, 개인정보 취급방침에 모두 동의합니다." + isChecked={isAllChecked} + onChange={handleAllCheck} + textColor="gray800" + textSize="b2" + /> + </div> + + <TermsContainer + title="이용약관" + isChecked={isPrivacyTermChecked} + onChange={handlePrivacyTermCheck} + > + <PrivacyTerms /> + </TermsContainer> + <TermsContainer + title="개인정보 취급방침" + isChecked={isUserTermChecked} + onChange={handleUserTermCheck} + > + {userType === 'trader' ? <TraderTerms /> : <InvestorTerms />} + </TermsContainer> + + <div className={cx('button-wrapper')}> + <LinkButton href={PATH.SIGN_UP_USER_TYPE}>이전</LinkButton> + <Button onClick={handleNextClick} variant="filled" disabled={!isAllChecked}> + 다음 + </Button> + </div> + </div> </> ) } From 997224dfcdbff95b348ebeff2afc4c38d0652076 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 22:15:33 +0900 Subject: [PATCH 217/648] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=83=81=ED=83=9C=EA=B4=80=EB=A6=AC=EB=A5=BC=20sto?= =?UTF-8?q?re=20=EA=B8=B0=EB=B0=98=EC=97=90=EC=84=9C=20cookie=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - zustand store에서 관리하던 회원가입 상태를 cookie로 마이그레이션 - 페이지 새로고침 시에도 상태 유지 가능 --- app/(landing)/signup/_constants/cookies.ts | 8 +++ app/(landing)/signup/_lib/cookies.ts | 59 +++++++++++++++++++ .../signup/_stores/use-signup-store.ts | 34 ----------- .../signup/_ui/user-type-card/index.tsx | 5 +- app/(landing)/signup/complete/page.tsx | 14 ++--- app/(landing)/signup/terms-of-use/page.tsx | 21 +++---- 6 files changed, 81 insertions(+), 60 deletions(-) create mode 100644 app/(landing)/signup/_constants/cookies.ts create mode 100644 app/(landing)/signup/_lib/cookies.ts delete mode 100644 app/(landing)/signup/_stores/use-signup-store.ts diff --git a/app/(landing)/signup/_constants/cookies.ts b/app/(landing)/signup/_constants/cookies.ts new file mode 100644 index 00000000..1c0cf05e --- /dev/null +++ b/app/(landing)/signup/_constants/cookies.ts @@ -0,0 +1,8 @@ +export const SIGN_UP_COOKIE = { + USER_TYPE: 'userType', + IS_AGREED_TERMS: 'isAgreedTerms', + NICKNAME: 'nickname', +} as const + +export type SignUpCookieKeyType = keyof typeof SIGN_UP_COOKIE +export type SignUpCookieValueType = (typeof SIGN_UP_COOKIE)[SignUpCookieKeyType] diff --git a/app/(landing)/signup/_lib/cookies.ts b/app/(landing)/signup/_lib/cookies.ts new file mode 100644 index 00000000..7b861bbc --- /dev/null +++ b/app/(landing)/signup/_lib/cookies.ts @@ -0,0 +1,59 @@ +import { UserType } from '@/shared/types/user' + +import { SIGN_UP_COOKIE, SignUpCookieValueType } from '../_constants/cookies' + +const setSignupCookie = (key: SignUpCookieValueType, value: string) => { + document.cookie = `${key}=${value}; path=/signup; sameSite=strict` +} + +const getSignupCookie = (key: SignUpCookieValueType) => { + const value = `; ${document.cookie}` + const parts = value.split(`; ${key}=`) + + if (parts.length === 2) return parts.pop()?.split(';').shift() +} + +export const removeAllSignupCookies = () => { + try { + const signupCookies = Object.values(SIGN_UP_COOKIE) + const expireDate = 'Thu, 01 Jan 1970 00:00:00 GMT' + + signupCookies.forEach((cookieName) => { + if (typeof cookieName === 'string') { + document.cookie = `${cookieName}=; path=/signup; expires=${expireDate}; secure` + } + }) + + return true + } catch (error) { + console.error('회원가입 쿠키 삭제 실패:', error) + return false + } +} + +export const getUserTypeCookie = () => { + const userType = getSignupCookie(SIGN_UP_COOKIE.USER_TYPE) + return userType ? (userType as UserType) : undefined +} + +export const getIsAgreedTermsCookie = () => { + const isAgreedTerms = getSignupCookie(SIGN_UP_COOKIE.IS_AGREED_TERMS) + return isAgreedTerms === 'Y' +} + +export const getNicknameCookie = () => { + const nickname = getSignupCookie(SIGN_UP_COOKIE.NICKNAME) + return nickname +} + +export const setUserTypeCookie = (userType: UserType) => { + setSignupCookie(SIGN_UP_COOKIE.USER_TYPE, userType) +} + +export const setIsAgreedTermsCookie = (isAgreed: boolean) => { + setSignupCookie(SIGN_UP_COOKIE.IS_AGREED_TERMS, isAgreed ? 'Y' : 'N') +} + +export const setNicknameCookie = (nickname: string) => { + setSignupCookie(SIGN_UP_COOKIE.NICKNAME, nickname) +} diff --git a/app/(landing)/signup/_stores/use-signup-store.ts b/app/(landing)/signup/_stores/use-signup-store.ts deleted file mode 100644 index cc398197..00000000 --- a/app/(landing)/signup/_stores/use-signup-store.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { create } from 'zustand' - -import { UserType } from '@/shared/types/user' - -interface StateModel { - userType: UserType | null - nickname: string | null - isAgreedTerm: boolean -} - -interface ActionModel { - setUserType: (userType: UserType) => void - setIsAgreedTerm: (isAgreedTerm: boolean) => void -} - -interface ActionsModel { - actions: ActionModel -} - -const initialState = { - userType: null, - nickname: null, - isAgreedTerm: false, -} as const - -const useSignupStore = create<StateModel & ActionsModel>((set) => ({ - ...initialState, - actions: { - setUserType: (userType) => set((state) => ({ ...state, userType })), - setIsAgreedTerm: (isAgreedTerm) => set((state) => ({ ...state, isAgreedTerm })), - }, -})) - -export default useSignupStore diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx index 600a1b01..25882ff9 100644 --- a/app/(landing)/signup/_ui/user-type-card/index.tsx +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/navigation' -import useSignupStore from '@/app/(landing)/signup/_stores/use-signup-store' +import { setUserTypeCookie } from '@/app/(landing)/signup/_lib/cookies' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -20,10 +20,9 @@ interface Props { const UserTypeCard = ({ userType, title, highlight }: Props) => { const router = useRouter() - const { setUserType } = useSignupStore((state) => state.actions) const handleTypeSelect = () => { - setUserType(userType) + setUserTypeCookie(userType) router.push(PATH.SIGN_UP_TERMS_OF_USE) } diff --git a/app/(landing)/signup/complete/page.tsx b/app/(landing)/signup/complete/page.tsx index 3261d777..085c562f 100644 --- a/app/(landing)/signup/complete/page.tsx +++ b/app/(landing)/signup/complete/page.tsx @@ -1,8 +1,6 @@ 'use client' -import { useRouter } from 'next/navigation' - -import useSignupStore from '@/app/(landing)/signup/_stores/use-signup-store' +import { getNicknameCookie, getUserTypeCookie } from '@/app/(landing)/signup/_lib/cookies' import SignupCompleteMessage from '@/app/(landing)/signup/_ui/signup-complete-message' import Step from '@/app/(landing)/signup/_ui/step' import classNames from 'classnames/bind' @@ -15,13 +13,11 @@ import styles from './page.module.scss' const cx = classNames.bind(styles) const CompletePage = () => { - const router = useRouter() - const nickname = useSignupStore((state) => state.nickname) - const userType = useSignupStore((state) => state.userType) + const userType = getUserTypeCookie() + const nickname = getNicknameCookie() - if (!nickname || !userType) { - router.push(PATH.SIGN_UP_USER_TYPE) - return + if (!userType || !nickname) { + return null } return ( diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx index 62dc7832..ca2ef30a 100644 --- a/app/(landing)/signup/terms-of-use/page.tsx +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -1,12 +1,9 @@ 'use client' -import { useEffect, useState } from 'react' +import { useState } from 'react' import { useRouter } from 'next/navigation' -import useSignupStore from '@/app/(landing)/signup/_stores/use-signup-store' -import Step from '@/app/(landing)/signup/_ui/step' -import TermsContainer from '@/app/(landing)/signup/terms-of-use/_ui/terms-container' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -14,6 +11,9 @@ import { Button } from '@/shared/ui/button' import Checkbox from '@/shared/ui/check-box' import { LinkButton } from '@/shared/ui/link-button' +import { getIsAgreedTermsCookie, getUserTypeCookie, setIsAgreedTermsCookie } from '../_lib/cookies' +import Step from '../_ui/step' +import TermsContainer from './_ui/terms-container' import InvestorTerms from './_ui/terms/investor-terms' import PrivacyTerms from './_ui/terms/privacy-terms' import TraderTerms from './_ui/terms/trader-terms' @@ -23,20 +23,13 @@ const cx = classNames.bind(styles) const TermsOfUsePage = () => { const router = useRouter() - const userType = useSignupStore((state) => state.userType) - const isAgreedTerm = useSignupStore((state) => state.isAgreedTerm) - const { setIsAgreedTerm } = useSignupStore((state) => state.actions) + const userType = getUserTypeCookie() + const isAgreedTerm = getIsAgreedTermsCookie() const [isUserTermChecked, setIsUserTermChecked] = useState(isAgreedTerm) const [isPrivacyTermChecked, setIsPrivacyTermChecked] = useState(isAgreedTerm) const [isAllChecked, setIsAllChecked] = useState(isAgreedTerm) - useEffect(() => { - if (!userType) { - router.push(PATH.SIGN_UP_USER_TYPE) - } - }, [userType, router]) - const handleAllCheck = () => { setIsAllChecked(!isAllChecked) setIsUserTermChecked(!isAllChecked) @@ -54,7 +47,7 @@ const TermsOfUsePage = () => { } const handleNextClick = () => { - setIsAgreedTerm(isAllChecked) + setIsAgreedTermsCookie(isAllChecked) router.push(PATH.SIGN_UP_INFORMATION) } From fd52087d6cd372cb23c5dc47dd2b7960bfeb52b5 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 22:17:39 +0900 Subject: [PATCH 218/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=91=EA=B7=BC=20?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=AF=B8?= =?UTF-8?q?=EB=93=A4=EC=9B=A8=EC=96=B4=20=EC=B6=94=EA=B0=80=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware.ts | 21 +++++++++++++++++++ middleware/signup.ts | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 middleware.ts create mode 100644 middleware/signup.ts diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 00000000..3f668494 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +import { handleSignupMiddleware } from '@/middleware/signup' + +import { PATH } from '@/shared/constants/path' + +export const middleware = (request: NextRequest) => { + const path = request.nextUrl.pathname + + if (path.startsWith(PATH.SIGN_UP)) { + const response = handleSignupMiddleware(request) + if (response) return response + } + + return NextResponse.next() +} + +export const config = { + matcher: [`${PATH.SIGN_UP}/:path*`], +} diff --git a/middleware/signup.ts b/middleware/signup.ts new file mode 100644 index 00000000..93d17406 --- /dev/null +++ b/middleware/signup.ts @@ -0,0 +1,50 @@ +import { NextRequest, NextResponse } from 'next/server' + +import { SIGN_UP_COOKIE } from '@/app/(landing)/signup/_constants/cookies' + +import { PATH } from '@/shared/constants/path' + +const USER_TYPE_CHECK_PAGES = [ + PATH.SIGN_UP_TERMS_OF_USE, + PATH.SIGN_UP_INFORMATION, + PATH.SIGN_UP_COMPLETE, +] as const + +const TERMS_CHECK_PAGES = [PATH.SIGN_UP_INFORMATION, PATH.SIGN_UP_COMPLETE] as const + +const isUserTypeCheckPages = (path: string) => { + return USER_TYPE_CHECK_PAGES.includes(path as (typeof USER_TYPE_CHECK_PAGES)[number]) +} + +const isTermsCheckPages = (path: string) => { + return TERMS_CHECK_PAGES.includes(path as (typeof TERMS_CHECK_PAGES)[number]) +} + +const isNicknameCheckPage = (path: string) => { + return path === PATH.SIGN_UP_COMPLETE +} + +export const handleSignupMiddleware = (request: NextRequest) => { + const path = request.nextUrl.pathname + + if (path === PATH.SIGN_UP_USER_TYPE) { + return NextResponse.next() + } + + const userType = request.cookies.get(SIGN_UP_COOKIE.USER_TYPE)?.value + if (isUserTypeCheckPages(path) && !userType) { + return NextResponse.redirect(new URL(PATH.SIGN_UP_USER_TYPE, request.url)) + } + + const isAgreedTerm = request.cookies.get(SIGN_UP_COOKIE.IS_AGREED_TERMS)?.value + if (isTermsCheckPages(path) && !isAgreedTerm) { + return NextResponse.redirect(new URL(PATH.SIGN_UP_TERMS_OF_USE, request.url)) + } + + const nickname = request.cookies.get(SIGN_UP_COOKIE.NICKNAME)?.value + if (isNicknameCheckPage(path) && !nickname) { + return NextResponse.redirect(new URL(PATH.SIGN_UP_INFORMATION, request.url)) + } + + return NextResponse.next() +} From 8b019e83ca2b5d74289122688d4ab114bab603b6 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 23:08:58 +0900 Subject: [PATCH 219/648] =?UTF-8?q?refactor:=20useTermsCheck=20=ED=9B=85?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../terms-of-use/_hooks/use-terms-check.ts | 56 +++++++++++++++++++ app/(landing)/signup/terms-of-use/page.tsx | 43 ++++---------- 2 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 app/(landing)/signup/terms-of-use/_hooks/use-terms-check.ts diff --git a/app/(landing)/signup/terms-of-use/_hooks/use-terms-check.ts b/app/(landing)/signup/terms-of-use/_hooks/use-terms-check.ts new file mode 100644 index 00000000..0486d17a --- /dev/null +++ b/app/(landing)/signup/terms-of-use/_hooks/use-terms-check.ts @@ -0,0 +1,56 @@ +import { useState } from 'react' + +import { useRouter } from 'next/navigation' + +import { getIsAgreedTermsCookie, setIsAgreedTermsCookie } from '@/app/(landing)/signup/_lib/cookies' + +import { PATH } from '@/shared/constants/path' + +interface UseTermsCheckReturnModel { + isAllChecked: boolean + isUserTermChecked: boolean + isPrivacyTermChecked: boolean + handleAllCheck: () => void + handleUserTermCheck: () => void + handlePrivacyTermCheck: () => void + handleNextClick: () => void +} +export const useTermsCheck = (): UseTermsCheckReturnModel => { + const router = useRouter() + const isAgreedTerm = getIsAgreedTermsCookie() + + const [isUserTermChecked, setIsUserTermChecked] = useState(isAgreedTerm) + const [isPrivacyTermChecked, setIsPrivacyTermChecked] = useState(isAgreedTerm) + const [isAllChecked, setIsAllChecked] = useState(isAgreedTerm) + + const handleAllCheck = () => { + setIsAllChecked(!isAllChecked) + setIsUserTermChecked(!isAllChecked) + setIsPrivacyTermChecked(!isAllChecked) + } + + const handleUserTermCheck = () => { + setIsUserTermChecked(!isUserTermChecked) + setIsAllChecked(!isUserTermChecked && isPrivacyTermChecked) + } + + const handlePrivacyTermCheck = () => { + setIsPrivacyTermChecked(!isPrivacyTermChecked) + setIsAllChecked(!isPrivacyTermChecked && isUserTermChecked) + } + + const handleNextClick = () => { + setIsAgreedTermsCookie(isAllChecked) + router.push(PATH.SIGN_UP_INFORMATION) + } + + return { + isAllChecked, + isUserTermChecked, + isPrivacyTermChecked, + handleAllCheck, + handleUserTermCheck, + handlePrivacyTermCheck, + handleNextClick, + } +} diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx index ca2ef30a..7252ee17 100644 --- a/app/(landing)/signup/terms-of-use/page.tsx +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -1,9 +1,6 @@ 'use client' -import { useState } from 'react' - -import { useRouter } from 'next/navigation' - +import { useTermsCheck } from '@/app/(landing)/signup/terms-of-use/_hooks/use-terms-check' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -11,7 +8,7 @@ import { Button } from '@/shared/ui/button' import Checkbox from '@/shared/ui/check-box' import { LinkButton } from '@/shared/ui/link-button' -import { getIsAgreedTermsCookie, getUserTypeCookie, setIsAgreedTermsCookie } from '../_lib/cookies' +import { getUserTypeCookie } from '../_lib/cookies' import Step from '../_ui/step' import TermsContainer from './_ui/terms-container' import InvestorTerms from './_ui/terms/investor-terms' @@ -22,34 +19,16 @@ import styles from './page.module.scss' const cx = classNames.bind(styles) const TermsOfUsePage = () => { - const router = useRouter() const userType = getUserTypeCookie() - const isAgreedTerm = getIsAgreedTermsCookie() - - const [isUserTermChecked, setIsUserTermChecked] = useState(isAgreedTerm) - const [isPrivacyTermChecked, setIsPrivacyTermChecked] = useState(isAgreedTerm) - const [isAllChecked, setIsAllChecked] = useState(isAgreedTerm) - - const handleAllCheck = () => { - setIsAllChecked(!isAllChecked) - setIsUserTermChecked(!isAllChecked) - setIsPrivacyTermChecked(!isAllChecked) - } - - const handleUserTermCheck = () => { - setIsUserTermChecked(!isUserTermChecked) - setIsAllChecked(!isUserTermChecked && isPrivacyTermChecked) - } - - const handlePrivacyTermCheck = () => { - setIsPrivacyTermChecked(!isPrivacyTermChecked) - setIsAllChecked(!isPrivacyTermChecked && isUserTermChecked) - } - - const handleNextClick = () => { - setIsAgreedTermsCookie(isAllChecked) - router.push(PATH.SIGN_UP_INFORMATION) - } + const { + isAllChecked, + isUserTermChecked, + isPrivacyTermChecked, + handleAllCheck, + handleUserTermCheck, + handlePrivacyTermCheck, + handleNextClick, + } = useTermsCheck() return ( <> From d403a2b9f2bddc66ad7c72f6798529efc65693a4 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 20 Nov 2024 23:37:06 +0900 Subject: [PATCH 220/648] =?UTF-8?q?fix:=20=EC=83=81=EB=8C=80=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/complete/page.tsx | 6 +++--- app/(landing)/signup/information/page.tsx | 4 ++-- app/(landing)/signup/terms-of-use/page.tsx | 2 +- app/(landing)/signup/user-type/page.tsx | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/(landing)/signup/complete/page.tsx b/app/(landing)/signup/complete/page.tsx index 085c562f..e25278ca 100644 --- a/app/(landing)/signup/complete/page.tsx +++ b/app/(landing)/signup/complete/page.tsx @@ -1,13 +1,13 @@ 'use client' -import { getNicknameCookie, getUserTypeCookie } from '@/app/(landing)/signup/_lib/cookies' -import SignupCompleteMessage from '@/app/(landing)/signup/_ui/signup-complete-message' -import Step from '@/app/(landing)/signup/_ui/step' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { LinkButton } from '@/shared/ui/link-button' +import { getNicknameCookie, getUserTypeCookie } from '../_lib/cookies' +import SignupCompleteMessage from '../_ui/signup-complete-message' +import Step from '../_ui/step' import styles from './page.module.scss' const cx = classNames.bind(styles) diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index 166540c5..6acc85a4 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -1,8 +1,8 @@ -import Step from '@/app/(landing)/signup/_ui/step' - import { PATH } from '@/shared/constants/path' import { LinkButton } from '@/shared/ui/link-button' +import Step from '../_ui/step' + const InformationPage = () => { return ( <> diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx index 7252ee17..fcc358e6 100644 --- a/app/(landing)/signup/terms-of-use/page.tsx +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -1,6 +1,5 @@ 'use client' -import { useTermsCheck } from '@/app/(landing)/signup/terms-of-use/_hooks/use-terms-check' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -10,6 +9,7 @@ import { LinkButton } from '@/shared/ui/link-button' import { getUserTypeCookie } from '../_lib/cookies' import Step from '../_ui/step' +import { useTermsCheck } from './_hooks/use-terms-check' import TermsContainer from './_ui/terms-container' import InvestorTerms from './_ui/terms/investor-terms' import PrivacyTerms from './_ui/terms/privacy-terms' diff --git a/app/(landing)/signup/user-type/page.tsx b/app/(landing)/signup/user-type/page.tsx index 325380b8..71721eab 100644 --- a/app/(landing)/signup/user-type/page.tsx +++ b/app/(landing)/signup/user-type/page.tsx @@ -1,7 +1,7 @@ -import Step from '@/app/(landing)/signup/_ui/step' -import UserTypeCard from '@/app/(landing)/signup/_ui/user-type-card' import classNames from 'classnames/bind' +import Step from '../_ui/step' +import UserTypeCard from '../_ui/user-type-card' import styles from './page.module.scss' const cx = classNames.bind(styles) From 08ea17267bf8457654c1d0d7cf4be9b1e3622443 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Thu, 21 Nov 2024 09:02:01 +0900 Subject: [PATCH 221/648] =?UTF-8?q?fix:=20CommonTItle=20styles=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=EA=B5=90=EC=A0=95=20(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/common-title/styles.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/common-title/styles.module.scss b/shared/ui/common-title/styles.module.scss index 1a92d4e1..cb835f86 100644 --- a/shared/ui/common-title/styles.module.scss +++ b/shared/ui/common-title/styles.module.scss @@ -6,7 +6,7 @@ } .title { - @include typo-h3; + @include typo-h4; color: $color-gray-700; } From a3211dd03f9ee10508d3daa5f855c94853d92b6b Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 21 Nov 2024 10:05:48 +0900 Subject: [PATCH 222/648] =?UTF-8?q?chore:=20svg=20=ED=81=AC=EA=B8=B0=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=88=EC=9D=84=20=EC=9C=84=ED=95=9C=20svgr=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/next.config.mjs b/next.config.mjs index 413f6eb6..705b5595 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -42,7 +42,14 @@ const nextConfig = { config.module.rules.push({ test: /\.svg$/, issuer: /\.[jt]sx?$/, - use: ['@svgr/webpack'], + use: [ + { + loader: '@svgr/webpack', + options: { + dimensions: false, + }, + }, + ], }) return config From e8f772d20c76eabf66b1876d472ed03b986e8d90 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 21 Nov 2024 10:06:48 +0900 Subject: [PATCH 223/648] =?UTF-8?q?fix:=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=ED=81=AC=EA=B8=B0=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/logo/styles.module.scss | 2 ++ shared/ui/side-navigation/styles.module.scss | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/shared/ui/logo/styles.module.scss b/shared/ui/logo/styles.module.scss index e03cbdda..70e9f64b 100644 --- a/shared/ui/logo/styles.module.scss +++ b/shared/ui/logo/styles.module.scss @@ -4,11 +4,13 @@ gap: 12px; svg { + width: 40px; flex-shrink: 0; } } .text { + width: 142px; color: $color-gray-500; line-height: normal; } diff --git a/shared/ui/side-navigation/styles.module.scss b/shared/ui/side-navigation/styles.module.scss index fa59c42f..2968e15f 100644 --- a/shared/ui/side-navigation/styles.module.scss +++ b/shared/ui/side-navigation/styles.module.scss @@ -29,8 +29,13 @@ padding: 24px 0 0 24px; svg { + width: 40px; flex-shrink: 0; } + + .text { + width: 142px; + } } .main-navigation { From 71def04a123ee79ee640cadc3fd25774cfe9aceb Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 21 Nov 2024 10:08:13 +0900 Subject: [PATCH 224/648] =?UTF-8?q?feat:=20hero=20=EC=84=B9=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(home)/_ui/hero-section/index.tsx | 30 +++++++++++++++++++ .../_ui/hero-section/styles.module.scss | 19 ++++++++++++ app/(landing)/(home)/page.tsx | 8 ++++- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 app/(landing)/(home)/_ui/hero-section/index.tsx create mode 100644 app/(landing)/(home)/_ui/hero-section/styles.module.scss diff --git a/app/(landing)/(home)/_ui/hero-section/index.tsx b/app/(landing)/(home)/_ui/hero-section/index.tsx new file mode 100644 index 00000000..1e8cb8a1 --- /dev/null +++ b/app/(landing)/(home)/_ui/hero-section/index.tsx @@ -0,0 +1,30 @@ +'use client' + +import Logo from '@/public/images/logo.svg' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import { LinkButton } from '@/shared/ui/link-button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const HeroSection = () => { + return ( + <section className={cx('section')}> + <h1 className={cx('title')}> + 성공적인 투자 전략을 + <br /> + 참고하거나 공유하고 싶다면 + <br /> + <Logo className={cx('logo')} width="65" height="39" /> 인베스트메틱에서! + </h1> + <LinkButton href={PATH.SIGN_UP_USER_TYPE} variant="filled" className={cx('button')}> + 바로 함께하기 + </LinkButton> + </section> + ) +} + +export default HeroSection diff --git a/app/(landing)/(home)/_ui/hero-section/styles.module.scss b/app/(landing)/(home)/_ui/hero-section/styles.module.scss new file mode 100644 index 00000000..9379f456 --- /dev/null +++ b/app/(landing)/(home)/_ui/hero-section/styles.module.scss @@ -0,0 +1,19 @@ +.section { + text-align: center; +} + +.title { + @include typo-h1; + padding-top: 100px; + color: $color-gray-800; +} + +.logo { + display: inline-block; + width: 65px; + height: auto; +} + +.button { + margin-top: 77px; +} diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index ae3650fe..c12d38bd 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,5 +1,11 @@ +import HeroSection from './_ui/hero-section' + const HomePage = () => { - return <>Home</> + return ( + <> + <HeroSection /> + </> + ) } export default HomePage From 22c782753d46f49228f6ba6e6ca3f27d39896097 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 21 Nov 2024 12:32:50 +0900 Subject: [PATCH 225/648] =?UTF-8?q?feat:=20UserMetricsSection=20=ED=8D=BC?= =?UTF-8?q?=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(home)/_ui/home-subtitle/index.tsx | 15 +++++++++ .../_ui/home-subtitle/styles.module.scss | 7 +++++ .../(home)/_ui/metric-card/index.tsx | 21 +++++++++++++ .../(home)/_ui/metric-card/styles.module.scss | 20 ++++++++++++ .../(home)/_ui/user-metrics-section/index.tsx | 31 +++++++++++++++++++ .../user-metrics-section/styles.module.scss | 6 ++++ app/(landing)/(home)/page.tsx | 2 ++ 7 files changed, 102 insertions(+) create mode 100644 app/(landing)/(home)/_ui/home-subtitle/index.tsx create mode 100644 app/(landing)/(home)/_ui/home-subtitle/styles.module.scss create mode 100644 app/(landing)/(home)/_ui/metric-card/index.tsx create mode 100644 app/(landing)/(home)/_ui/metric-card/styles.module.scss create mode 100644 app/(landing)/(home)/_ui/user-metrics-section/index.tsx create mode 100644 app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss diff --git a/app/(landing)/(home)/_ui/home-subtitle/index.tsx b/app/(landing)/(home)/_ui/home-subtitle/index.tsx new file mode 100644 index 00000000..712be719 --- /dev/null +++ b/app/(landing)/(home)/_ui/home-subtitle/index.tsx @@ -0,0 +1,15 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + children: React.ReactNode +} + +const HomeSubtitle = ({ children }: Props) => { + return <h2 className={cx('subtitle')}>{children}</h2> +} + +export default HomeSubtitle diff --git a/app/(landing)/(home)/_ui/home-subtitle/styles.module.scss b/app/(landing)/(home)/_ui/home-subtitle/styles.module.scss new file mode 100644 index 00000000..03244175 --- /dev/null +++ b/app/(landing)/(home)/_ui/home-subtitle/styles.module.scss @@ -0,0 +1,7 @@ +.subtitle { + margin-top: 120px; + @include typo-h4; + color: $color-gray-600; + font-weight: $text-bold; + text-align: center; +} diff --git a/app/(landing)/(home)/_ui/metric-card/index.tsx b/app/(landing)/(home)/_ui/metric-card/index.tsx new file mode 100644 index 00000000..25ef5887 --- /dev/null +++ b/app/(landing)/(home)/_ui/metric-card/index.tsx @@ -0,0 +1,21 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + count: number + label: string +} + +const MetricCard = ({ count, label }: Props) => { + return ( + <div className={cx('container')}> + <strong className={cx('count')}>{count.toLocaleString()}</strong> + <p className={cx('label')}>{label}</p> + </div> + ) +} + +export default MetricCard diff --git a/app/(landing)/(home)/_ui/metric-card/styles.module.scss b/app/(landing)/(home)/_ui/metric-card/styles.module.scss new file mode 100644 index 00000000..c21b5655 --- /dev/null +++ b/app/(landing)/(home)/_ui/metric-card/styles.module.scss @@ -0,0 +1,20 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 15px; + width: 300px; + height: 160px; + border-radius: 5px; + background-color: $color-white; +} + +.count { + @include typo-h1; + color: $color-orange-500; +} + +.label { + color: $color-gray-800; +} diff --git a/app/(landing)/(home)/_ui/user-metrics-section/index.tsx b/app/(landing)/(home)/_ui/user-metrics-section/index.tsx new file mode 100644 index 00000000..52f4bf92 --- /dev/null +++ b/app/(landing)/(home)/_ui/user-metrics-section/index.tsx @@ -0,0 +1,31 @@ +import classNames from 'classnames/bind' + +import HomeSubtitle from '../home-subtitle' +import MetricCard from '../metric-card' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const UserMetricsSection = () => { + const { totalTraderCount, totalStrategiesCount, totalUserCount, totalSubscribeCount } = { + totalTraderCount: 512, + totalStrategiesCount: 5210, + totalUserCount: 43431, + totalSubscribeCount: 8210, + } + + return ( + <section> + <HomeSubtitle>이미 이렇게 많은 사람들이 주식 전략을 공유하고, 구독하고 있어요!</HomeSubtitle> + + <div className={cx('card-wrapper')}> + <MetricCard count={totalTraderCount} label="명의 트레이더가" /> + <MetricCard count={totalStrategiesCount} label="개의 투자 전략을 올렸어요!" /> + <MetricCard count={totalUserCount} label="명의 투자자가" /> + <MetricCard count={totalSubscribeCount} label="개의 전략을 구독 중이에요!" /> + </div> + </section> + ) +} + +export default UserMetricsSection diff --git a/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss b/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss new file mode 100644 index 00000000..53ef6847 --- /dev/null +++ b/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss @@ -0,0 +1,6 @@ +.card-wrapper { + display: flex; + gap: 20px; + justify-content: center; + margin-top: 36px; +} diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index c12d38bd..0ce6aca0 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,9 +1,11 @@ import HeroSection from './_ui/hero-section' +import UserMetricsSection from './_ui/user-metrics-section' const HomePage = () => { return ( <> <HeroSection /> + <UserMetricsSection /> </> ) } From 4b64f75648609efeb1f984731f3a73a4ebb12b3f Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Thu, 21 Nov 2024 15:32:39 +0900 Subject: [PATCH 226/648] =?UTF-8?q?fix:=20CommonTItle=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A5=BC=20Title=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EA=B0=9C=EB=AA=85=20(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/{common-title => title}/index.tsx | 4 ++-- .../{common-title => title}/styles.module.scss | 0 .../title.stories.tsx} | 16 +++++++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) rename shared/ui/{common-title => title}/index.tsx (83%) rename shared/ui/{common-title => title}/styles.module.scss (100%) rename shared/ui/{common-title/common-title.stories.tsx => title/title.stories.tsx} (81%) diff --git a/shared/ui/common-title/index.tsx b/shared/ui/title/index.tsx similarity index 83% rename from shared/ui/common-title/index.tsx rename to shared/ui/title/index.tsx index d9663b14..1f5c8800 100644 --- a/shared/ui/common-title/index.tsx +++ b/shared/ui/title/index.tsx @@ -13,7 +13,7 @@ interface Props { style?: CSSProperties } -const CommonTitle = ({ label, subtitle, marginLeft, style }: Props) => { +const Title = ({ label, subtitle, marginLeft, style }: Props) => { return ( <div className={cx('container')} style={{ ...style, marginLeft }}> <h1 className={cx('title')}>{label}</h1> @@ -22,4 +22,4 @@ const CommonTitle = ({ label, subtitle, marginLeft, style }: Props) => { ) } -export default CommonTitle +export default Title diff --git a/shared/ui/common-title/styles.module.scss b/shared/ui/title/styles.module.scss similarity index 100% rename from shared/ui/common-title/styles.module.scss rename to shared/ui/title/styles.module.scss diff --git a/shared/ui/common-title/common-title.stories.tsx b/shared/ui/title/title.stories.tsx similarity index 81% rename from shared/ui/common-title/common-title.stories.tsx rename to shared/ui/title/title.stories.tsx index a727db14..6234f8e9 100644 --- a/shared/ui/common-title/common-title.stories.tsx +++ b/shared/ui/title/title.stories.tsx @@ -2,16 +2,22 @@ import type { Meta, StoryObj } from '@storybook/react' import StorybookMockContents from '@/shared/utils/storybook-mock-contents' -import CommonTitle from '.' +import Title from '.' import BackHeader from '../header/back-header' const meta = { - title: 'Components/CommonTItle', - component: CommonTitle, + title: 'Components/Title', + component: Title, + argTypes: { + marginLeft: { + control: 'select', + options: ['0px', '40px', '80px'], + }, + }, tags: ['autodocs'], -} satisfies Meta<typeof CommonTitle> +} satisfies Meta<typeof Title> export default meta -type StoryType = StoryObj<typeof CommonTitle> +type StoryType = StoryObj<typeof Title> export const Default: StoryType = { args: { From ad336b63f24a5f4f7c283f11953428b11b554b29 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 21 Nov 2024 16:30:58 +0900 Subject: [PATCH 227/648] =?UTF-8?q?fix:=20signup=20=EB=AF=B8=EB=93=A4?= =?UTF-8?q?=EC=9B=A8=EC=96=B4=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_lib/cookies.ts | 8 ++++++++ app/(landing)/signup/information/page.tsx | 2 ++ app/(landing)/signup/user-type/page.tsx | 2 ++ middleware.ts | 2 +- next.config.mjs | 3 --- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/(landing)/signup/_lib/cookies.ts b/app/(landing)/signup/_lib/cookies.ts index 7b861bbc..f250ea19 100644 --- a/app/(landing)/signup/_lib/cookies.ts +++ b/app/(landing)/signup/_lib/cookies.ts @@ -1,12 +1,18 @@ +'use client' + import { UserType } from '@/shared/types/user' import { SIGN_UP_COOKIE, SignUpCookieValueType } from '../_constants/cookies' +const isBrowser = typeof window !== 'undefined' + const setSignupCookie = (key: SignUpCookieValueType, value: string) => { + if (!isBrowser) return document.cookie = `${key}=${value}; path=/signup; sameSite=strict` } const getSignupCookie = (key: SignUpCookieValueType) => { + if (!isBrowser) return const value = `; ${document.cookie}` const parts = value.split(`; ${key}=`) @@ -14,6 +20,8 @@ const getSignupCookie = (key: SignUpCookieValueType) => { } export const removeAllSignupCookies = () => { + if (!isBrowser) return false + try { const signupCookies = Object.values(SIGN_UP_COOKIE) const expireDate = 'Thu, 01 Jan 1970 00:00:00 GMT' diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index 6acc85a4..e9048faf 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -1,3 +1,5 @@ +'use client' + import { PATH } from '@/shared/constants/path' import { LinkButton } from '@/shared/ui/link-button' diff --git a/app/(landing)/signup/user-type/page.tsx b/app/(landing)/signup/user-type/page.tsx index 71721eab..e9db7a5d 100644 --- a/app/(landing)/signup/user-type/page.tsx +++ b/app/(landing)/signup/user-type/page.tsx @@ -1,3 +1,5 @@ +'use client' + import classNames from 'classnames/bind' import Step from '../_ui/step' diff --git a/middleware.ts b/middleware.ts index 3f668494..1f818883 100644 --- a/middleware.ts +++ b/middleware.ts @@ -17,5 +17,5 @@ export const middleware = (request: NextRequest) => { } export const config = { - matcher: [`${PATH.SIGN_UP}/:path*`], + matcher: ['/signup/:path*'], } diff --git a/next.config.mjs b/next.config.mjs index 413f6eb6..4352ad16 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,9 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ['msw'], - experimental: { - appDir: true, - }, sassOptions: { includePaths: ['./shared/styles'], prependData: ` From f8d3b57fed3017dff3a7dc56d665c5ab0639dd16 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 21 Nov 2024 16:33:51 +0900 Subject: [PATCH 228/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=BF=A0=ED=82=A4=20=EB=A7=8C=EB=A3=8C=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=B6=94=EA=B0=80=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_lib/cookies.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/(landing)/signup/_lib/cookies.ts b/app/(landing)/signup/_lib/cookies.ts index f250ea19..8989e1bf 100644 --- a/app/(landing)/signup/_lib/cookies.ts +++ b/app/(landing)/signup/_lib/cookies.ts @@ -4,11 +4,17 @@ import { UserType } from '@/shared/types/user' import { SIGN_UP_COOKIE, SignUpCookieValueType } from '../_constants/cookies' +const COOKIE_EXPIRATION_MINUTES = 30 + const isBrowser = typeof window !== 'undefined' const setSignupCookie = (key: SignUpCookieValueType, value: string) => { if (!isBrowser) return - document.cookie = `${key}=${value}; path=/signup; sameSite=strict` + + const expirationDate = new Date() + expirationDate.setMinutes(expirationDate.getMinutes() + COOKIE_EXPIRATION_MINUTES) + + document.cookie = `${key}=${value}; path=/signup; sameSite=strict; expires=${expirationDate.toUTCString()}` } const getSignupCookie = (key: SignUpCookieValueType) => { From 5d56e8ca1e49cc3bb47dea12cc84d44281c01eed Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 16:55:19 +0900 Subject: [PATCH 229/648] =?UTF-8?q?chore:=20axios=20=EC=84=A4=EC=B9=98=20(?= =?UTF-8?q?#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 81 +++++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09a9258d..ea5cf357 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@tanstack/react-query": "^5.59.19", + "axios": "^1.7.7", "classnames": "^2.5.1", "highcharts": "^11.4.8", "highcharts-react-official": "^3.2.1", @@ -6324,6 +6325,12 @@ "dev": true, "license": "MIT" }, + "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/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -6350,6 +6357,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -7251,6 +7269,18 @@ "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/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -7749,6 +7779,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -9290,6 +9329,26 @@ "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/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -9389,6 +9448,20 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -11250,7 +11323,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -11260,7 +11332,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -12479,6 +12550,12 @@ "dev": true, "license": "MIT" }, + "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/psl": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", diff --git a/package.json b/package.json index c563d596..59b6cc43 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@tanstack/react-query": "^5.59.19", + "axios": "^1.7.7", "classnames": "^2.5.1", "highcharts": "^11.4.8", "highcharts-react-official": "^3.2.1", From fbe214a30154f0f0b76ecbf56fbe119698428a1d Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 16:56:49 +0900 Subject: [PATCH 230/648] =?UTF-8?q?design:=20navigation=20z-index=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/side-navigation/styles.module.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/ui/side-navigation/styles.module.scss b/shared/ui/side-navigation/styles.module.scss index fa59c42f..3e17821e 100644 --- a/shared/ui/side-navigation/styles.module.scss +++ b/shared/ui/side-navigation/styles.module.scss @@ -9,6 +9,8 @@ border-right: 1px solid $color-gray-200; background-color: $color-gray-100; transition: width 0.2s ease; + z-index: zIndex(base); + cursor: pointer; &:not(:hover) { From 2616d1a9dcd040d90bb974c74b9098c50822c450 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 16:57:57 +0900 Subject: [PATCH 231/648] =?UTF-8?q?design:=20=EC=A0=84=EB=9E=B5=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20width=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_variables.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index bb73983c..b8404299 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -68,6 +68,7 @@ $z-index: ( /* Width */ $max-width: 1440px; $navigation-width: 90px; +$strategy-sidebar-width: 280px; // height $header-height: 80px; From 4d7104131c05faa2a71ff0015d4b64cf226e1994 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:00:04 +0900 Subject: [PATCH 232/648] =?UTF-8?q?design:=20=EC=A0=84=EB=9E=B5=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20layout=20=EC=A0=81=EC=9A=A9=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/layout.module.scss | 10 ++++++++++ app/(dashboard)/strategies/layout.tsx | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 app/(dashboard)/strategies/layout.module.scss create mode 100644 app/(dashboard)/strategies/layout.tsx diff --git a/app/(dashboard)/strategies/layout.module.scss b/app/(dashboard)/strategies/layout.module.scss new file mode 100644 index 00000000..29e26f4b --- /dev/null +++ b/app/(dashboard)/strategies/layout.module.scss @@ -0,0 +1,10 @@ +.strategy-layout { + display: flex; + position: relative; + + .strategy { + width: calc(100% - $strategy-sidebar-width); + max-width: $max-width; + padding-right: 10px; + } +} diff --git a/app/(dashboard)/strategies/layout.tsx b/app/(dashboard)/strategies/layout.tsx new file mode 100644 index 00000000..d342a119 --- /dev/null +++ b/app/(dashboard)/strategies/layout.tsx @@ -0,0 +1,19 @@ +import classNames from 'classnames/bind' + +import styles from './layout.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + children: React.ReactNode +} + +const StrategiesLayout = ({ children }: Props) => { + return ( + <div className={cx('strategy-layout')}> + <section className={cx('strategy')}>{children}</section> + </div> + ) +} + +export default StrategiesLayout From 805e625e0a17056cec720291bc74ab7b3d3438aa Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:02:26 +0900 Subject: [PATCH 233/648] =?UTF-8?q?feat:=20msw=20=EB=AA=A9=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0,=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mocks/handlers.ts | 14 +- mocks/handlers/index.ts | 2 + mocks/handlers/strategies.ts | 520 +++++++++++++++++++++++++++++ mocks/handlers/strategy-details.ts | 147 ++++++++ 4 files changed, 681 insertions(+), 2 deletions(-) create mode 100644 mocks/handlers/strategies.ts create mode 100644 mocks/handlers/strategy-details.ts diff --git a/mocks/handlers.ts b/mocks/handlers.ts index 4af399c8..6e106498 100644 --- a/mocks/handlers.ts +++ b/mocks/handlers.ts @@ -1,5 +1,15 @@ -import { postHandlers, userHandlers } from './handlers/' +import { + postHandlers, + strategiesHandlers, + strategyDetailsHandlers, + userHandlers, +} from './handlers/' -const handlers = [...userHandlers, ...postHandlers] +const handlers = [ + ...userHandlers, + ...postHandlers, + ...strategyDetailsHandlers, + ...strategiesHandlers, +] export { handlers } diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts index 3f70ad6f..18e51a54 100644 --- a/mocks/handlers/index.ts +++ b/mocks/handlers/index.ts @@ -1,2 +1,4 @@ export { postHandlers } from './post' export { userHandlers } from './user' +export { strategyDetailsHandlers } from './strategy-details' +export { strategiesHandlers } from './strategies' diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts new file mode 100644 index 00000000..d9e4f2c9 --- /dev/null +++ b/mocks/handlers/strategies.ts @@ -0,0 +1,520 @@ +import { HttpResponse, http } from 'msw' + +const strategiesData = [ + { + strategyId: '1', + strategyName: 'Dynamic ETF 전략', + nickname: 'AlphaTrader', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 5.2 }, + { date: '2024-02-01', profitRate: 6.4 }, + { date: '2024-03-01', profitRate: 12.8 }, + { date: '2024-04-01', profitRate: 8.2 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + ], + tradeTypeIconUrl: '', + mdd: '-15,432,567', + smScore: 72.1, + cumulativeProfitLossRate: 140.5, + recentYearProfitLossRate: 35.2, + subscriptionCnt: 45, + isSubscribed: true, + averageRating: 4.9, + totalReview: 34, + }, + { + strategyId: '2', + strategyName: '고수익 ETF', + nickname: 'BetaTrader', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2023-12-01', profitRate: 7.2 }, + { date: '2024-01-01', profitRate: 5.2 }, + { date: '2024-02-01', profitRate: 25 }, + { date: '2024-03-01', profitRate: 12.8 }, + { date: '2024-04-01', profitRate: 17.2 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-12,786,543', + smScore: 65.4, + cumulativeProfitLossRate: 125.3, + recentYearProfitLossRate: 28.4, + subscriptionCnt: 67, + isSubscribed: false, + averageRating: 4.6, + totalReview: 19, + }, + { + strategyId: '3', + strategyName: 'Futures Pro', + nickname: 'Gamma', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 10.2 }, + { date: '2024-02-01', profitRate: 11.5 }, + { date: '2024-03-01', profitRate: 15.6 }, + { date: '2024-04-01', profitRate: 18.3 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + ], + tradeTypeIconUrl: '', + mdd: '-18,234,678', + smScore: 79.6, + cumulativeProfitLossRate: 160.4, + recentYearProfitLossRate: 40.1, + subscriptionCnt: 89, + isSubscribed: true, + averageRating: 4.7, + totalReview: 45, + }, + { + strategyId: '4', + strategyName: '월별 수익 전략', + nickname: 'TraderX', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 6.3 }, + { date: '2024-02-01', profitRate: 7.8 }, + { date: '2024-03-01', profitRate: 9.4 }, + { date: '2024-04-01', profitRate: 14.6 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-11,456,789', + smScore: 68.4, + cumulativeProfitLossRate: 134.5, + recentYearProfitLossRate: 32.7, + subscriptionCnt: 34, + isSubscribed: false, + averageRating: 4.5, + totalReview: 29, + }, + { + strategyId: '5', + strategyName: '리스크 관리 전략', + nickname: 'DeltaOne', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 4.1 }, + { date: '2024-02-01', profitRate: 5.2 }, + { date: '2024-03-01', profitRate: 7.3 }, + { date: '2024-04-01', profitRate: 9.5 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + ], + tradeTypeIconUrl: '', + mdd: '-14,345,678', + smScore: 62.3, + cumulativeProfitLossRate: 120.3, + recentYearProfitLossRate: 25.7, + subscriptionCnt: 21, + isSubscribed: true, + averageRating: 4.8, + totalReview: 22, + }, + { + strategyId: '6', + strategyName: 'Active LongShort', + nickname: 'Hedger', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 9.8 }, + { date: '2024-02-01', profitRate: 12.1 }, + { date: '2024-03-01', profitRate: 14.3 }, + { date: '2024-04-01', profitRate: 16.7 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-17,890,234', + smScore: 80.2, + cumulativeProfitLossRate: 175.4, + recentYearProfitLossRate: 45.8, + subscriptionCnt: 120, + isSubscribed: true, + averageRating: 4.9, + totalReview: 52, + }, + { + strategyId: '7', + strategyName: '안정적 성장 전략', + nickname: 'SafeGrow', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 4.8 }, + { date: '2024-02-01', profitRate: 6.3 }, + { date: '2024-03-01', profitRate: 8.9 }, + { date: '2024-04-01', profitRate: 10.2 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-13,567,890', + smScore: 70.1, + cumulativeProfitLossRate: 138.2, + recentYearProfitLossRate: 30.9, + subscriptionCnt: 54, + isSubscribed: false, + averageRating: 4.4, + totalReview: 18, + }, + { + strategyId: '8', + strategyName: 'High Risk High Return', + nickname: 'RiskyTrader', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 15.3 }, + { date: '2024-02-01', profitRate: 18.2 }, + { date: '2024-03-01', profitRate: 22.8 }, + { date: '2024-04-01', profitRate: 26.4 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-25,678,345', + smScore: 85.7, + cumulativeProfitLossRate: 200.5, + recentYearProfitLossRate: 50.4, + subscriptionCnt: 76, + isSubscribed: true, + averageRating: 4.7, + totalReview: 37, + }, + { + strategyId: '9', + strategyName: 'ETF 분산 투자', + nickname: 'Diversifier', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 3.2 }, + { date: '2024-02-01', profitRate: 4.5 }, + { date: '2024-03-01', profitRate: 6.1 }, + { date: '2024-04-01', profitRate: 7.8 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-9,876,543', + smScore: 58.3, + cumulativeProfitLossRate: 112.4, + recentYearProfitLossRate: 20.7, + subscriptionCnt: 23, + isSubscribed: false, + averageRating: 4.3, + totalReview: 11, + }, + { + strategyId: '10', + strategyName: 'Momentum 전략', + nickname: 'MomentumKing', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 10.5 }, + { date: '2024-02-01', profitRate: 12.7 }, + { date: '2024-03-01', profitRate: 15.9 }, + { date: '2024-04-01', profitRate: 18.4 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-18,456,789', + smScore: 75.6, + cumulativeProfitLossRate: 165.3, + recentYearProfitLossRate: 38.5, + subscriptionCnt: 89, + isSubscribed: true, + averageRating: 4.9, + totalReview: 48, + }, + { + strategyId: '11', + strategyName: '소형주 집중 전략', + nickname: 'SmallCapPro', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 7.5 }, + { date: '2024-02-01', profitRate: 8.9 }, + { date: '2024-03-01', profitRate: 10.3 }, + { date: '2024-04-01', profitRate: 12.7 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-14,345,678', + smScore: 69.4, + cumulativeProfitLossRate: 130.7, + recentYearProfitLossRate: 28.2, + subscriptionCnt: 67, + isSubscribed: false, + averageRating: 4.6, + totalReview: 26, + }, + { + strategyId: '12', + strategyName: 'Active Bond 전략', + nickname: 'BondExpert', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 5.2 }, + { date: '2024-02-01', profitRate: 6.3 }, + { date: '2024-03-01', profitRate: 7.8 }, + { date: '2024-04-01', profitRate: 9.4 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-10,567,890', + smScore: 64.3, + cumulativeProfitLossRate: 125.8, + recentYearProfitLossRate: 27.4, + subscriptionCnt: 42, + isSubscribed: true, + averageRating: 4.5, + totalReview: 21, + }, + { + strategyId: '13', + strategyName: 'Smart Index 전략', + nickname: 'IndexGuru', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 6.2 }, + { date: '2024-02-01', profitRate: 8.3 }, + { date: '2024-03-01', profitRate: 10.4 }, + { date: '2024-04-01', profitRate: 13.5 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-16,543,678', + smScore: 71.8, + cumulativeProfitLossRate: 145.6, + recentYearProfitLossRate: 32.7, + subscriptionCnt: 54, + isSubscribed: true, + averageRating: 4.6, + totalReview: 33, + }, + { + strategyId: '14', + strategyName: 'MACD Pro', + nickname: 'TrendRider', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 8.3 }, + { date: '2024-02-01', profitRate: 10.7 }, + { date: '2024-03-01', profitRate: 13.6 }, + { date: '2024-04-01', profitRate: 17.2 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-19,567,890', + smScore: 77.5, + cumulativeProfitLossRate: 175.8, + recentYearProfitLossRate: 42.3, + subscriptionCnt: 78, + isSubscribed: true, + averageRating: 4.8, + totalReview: 49, + }, + { + strategyId: '15', + strategyName: '균형 성장 전략', + nickname: 'BalancedGrow', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 5.6 }, + { date: '2024-02-01', profitRate: 6.8 }, + { date: '2024-03-01', profitRate: 8.2 }, + { date: '2024-04-01', profitRate: 9.9 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-12,567,890', + smScore: 62.8, + cumulativeProfitLossRate: 118.5, + recentYearProfitLossRate: 24.3, + subscriptionCnt: 31, + isSubscribed: false, + averageRating: 4.2, + totalReview: 16, + }, + { + strategyId: '16', + strategyName: '단기 스윙 전략', + nickname: 'SwingPro', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 11.5 }, + { date: '2024-02-01', profitRate: 14.8 }, + { date: '2024-03-01', profitRate: 18.2 }, + { date: '2024-04-01', profitRate: 20.9 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + ], + tradeTypeIconUrl: '', + mdd: '-22,345,678', + smScore: 80.3, + cumulativeProfitLossRate: 190.7, + recentYearProfitLossRate: 45.8, + subscriptionCnt: 85, + isSubscribed: true, + averageRating: 4.9, + totalReview: 42, + }, + { + strategyId: '17', + strategyName: '로우볼 전략', + nickname: 'LowVolKing', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 3.4 }, + { date: '2024-02-01', profitRate: 4.7 }, + { date: '2024-03-01', profitRate: 5.8 }, + { date: '2024-04-01', profitRate: 7.1 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + ], + tradeTypeIconUrl: '', + mdd: '-8,345,678', + smScore: 55.9, + cumulativeProfitLossRate: 102.4, + recentYearProfitLossRate: 18.5, + subscriptionCnt: 18, + isSubscribed: false, + averageRating: 4.1, + totalReview: 9, + }, + { + strategyId: '18', + strategyName: '인컴 중심 전략', + nickname: 'IncomeMaster', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 6.7 }, + { date: '2024-02-01', profitRate: 7.9 }, + { date: '2024-03-01', profitRate: 9.3 }, + { date: '2024-04-01', profitRate: 10.8 }, + { date: '2024-05-01', profitRate: 9.4 }, + { date: '2024-06-01', profitRate: 15.8 }, + ], + tradeTypeIconUrl: '', + mdd: '-10,678,345', + smScore: 60.2, + cumulativeProfitLossRate: 115.9, + recentYearProfitLossRate: 23.6, + subscriptionCnt: 40, + isSubscribed: true, + averageRating: 4.5, + totalReview: 20, + }, + { + strategyId: '19', + strategyName: '글로벌 주식 전략', + nickname: 'GlobalTrader', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 8.5 }, + { date: '2024-02-01', profitRate: 10.6 }, + { date: '2024-03-01', profitRate: 12.7 }, + { date: '2024-04-01', profitRate: 15.4 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-17,890,567', + smScore: 74.2, + cumulativeProfitLossRate: 153.6, + recentYearProfitLossRate: 36.4, + subscriptionCnt: 72, + isSubscribed: true, + averageRating: 4.7, + totalReview: 38, + }, + { + strategyId: '20', + strategyName: '테크 성장 전략', + nickname: 'TechGrow', + stockTypeIconUrl: [], + profitRateChartData: [ + { date: '2024-01-01', profitRate: 13.7 }, + { date: '2024-02-01', profitRate: 16.8 }, + { date: '2024-03-01', profitRate: 20.2 }, + { date: '2024-04-01', profitRate: 23.5 }, + { date: '2024-05-01', profitRate: 11.4 }, + { date: '2024-06-01', profitRate: 20 }, + { date: '2024-07-01', profitRate: 16 }, + { date: '2024-08-01', profitRate: 18 }, + ], + tradeTypeIconUrl: '', + mdd: '-23,567,890', + smScore: 88.3, + cumulativeProfitLossRate: 210.9, + recentYearProfitLossRate: 52.8, + subscriptionCnt: 95, + isSubscribed: true, + averageRating: 5.0, + totalReview: 50, + }, +] +export const strategiesHandlers = [ + http.get('/api/strategies', ({ request }) => { + const url = new URL(request.url) + const page = parseInt(url.searchParams.get('page') || '1') + const size = parseInt(url.searchParams.get('size') || '8') + + if (!isNaN(page) && !isNaN(size)) { + const croppedStrategiesData = strategiesData.slice( + size * (page - 1), + size * (page - 1) + size + ) + + return HttpResponse.json({ + strategiesData: croppedStrategiesData, + totalCount: strategiesData.length, + }) + } + return HttpResponse.json( + { + isSuccess: false, + message: '전략 목록을 찾을 수 없습니다.', + code: 1004, + }, + { status: 400 } + ) + }), +] diff --git a/mocks/handlers/strategy-details.ts b/mocks/handlers/strategy-details.ts new file mode 100644 index 00000000..4f6059ce --- /dev/null +++ b/mocks/handlers/strategy-details.ts @@ -0,0 +1,147 @@ +import { HttpResponse, http } from 'msw' + +const data = [ + { + strategyId: '1', + strategyName: 'Dynamic ETF 전략', + stockTypeIconURLs: [], + tradeTypeIconURL: '', + stockTypeNames: [], + tradeTypeName: 'ETF 매매', + operationCycle: 'DAY', + strategyDescription: '전략 설명 예시', + cumulativeProfitRate: 4924.0, + maxDrawdownRate: -13068.0, + averageProfitLossRate: 600.0, + profitFactor: 148.0, + winRate: 0.69, + subscriptionCount: 100, + traderImgUrl: '', + nickname: 'AlphaTrader', + minimumInvestmentAmount: '1억~ 2억', + initialInvestment: '10,000,000', + kpRatio: 0.513, + smScore: 72.1, + finalProfitLossDate: '2024.03.11', + createdAt: '2024.01.11', + subscribed: true, + }, + { + strategyId: '2', + strategyName: '고수익 ETF', + stockTypeIconURLs: [], + tradeTypeIconURL: '', + stockTypeNames: [], + tradeTypeName: 'ETF 매매', + operationCycle: 'DAY', + strategyDescription: '전략 설명 예시', + cumulativeProfitRate: 4924.0, + maxDrawdownRate: -13068.0, + averageProfitLossRate: 600.0, + profitFactor: 148.0, + winRate: 0.69, + subscriptionCount: 100, + traderImgUrl: '', + nickname: 'BetaTrader', + minimumInvestmentAmount: '1억~ 2억', + initialInvestment: '10,000,000', + kpRatio: 0.513, + smScore: 65.4, + finalProfitLossDate: '2024.03.11', + createdAt: '2024.01.11', + subscribed: true, + }, + { + strategyId: '3', + strategyName: 'Futures Pro', + stockTypeIconURLs: [], + tradeTypeIconURL: '', + stockTypeNames: [], + tradeTypeName: 'ETF 매매', + operationCycle: 'DAY', + strategyDescription: '전략 설명 예시', + cumulativeProfitRate: 4924.0, + maxDrawdownRate: -13068.0, + averageProfitLossRate: 600.0, + profitFactor: 148.0, + winRate: 0.69, + subscriptionCount: 100, + traderImgUrl: '', + nickname: 'Gamma', + minimumInvestmentAmount: '1억~ 2억', + initialInvestment: '10,000,000', + kpRatio: 0.513, + smScore: 79.6, + finalProfitLossDate: '2024.03.11', + createdAt: '2024.01.11', + subscribed: true, + }, + { + strategyId: '4', + strategyName: '월별 수익 전략', + stockTypeIconURLs: [], + tradeTypeIconURL: '', + stockTypeNames: [], + tradeTypeName: 'ETF 매매', + operationCycle: 'DAY', + strategyDescription: '전략 설명 예시', + cumulativeProfitRate: 4924.0, + maxDrawdownRate: -13068.0, + averageProfitLossRate: 600.0, + profitFactor: 148.0, + winRate: 0.69, + subscriptionCount: 100, + traderImgUrl: '', + nickname: 'user1', + minimumInvestmentAmount: '1억~ 2억', + initialInvestment: '10,000,000', + kpRatio: 0.513, + smScore: 68.4, + finalProfitLossDate: '2024.03.11', + createdAt: '2024.01.11', + subscribed: true, + }, + { + strategyId: '5', + strategyName: '리스크 관리 전략', + stockTypeIconURLs: [], + tradeTypeIconURL: '', + stockTypeNames: [], + tradeTypeName: 'ETF 매매', + operationCycle: 'DAY', + strategyDescription: '전략 설명 예시', + cumulativeProfitRate: 4924.0, + maxDrawdownRate: -13068.0, + averageProfitLossRate: 600.0, + profitFactor: 148.0, + winRate: 0.69, + subscriptionCount: 100, + traderImgUrl: '', + nickname: 'DeltaOne', + minimumInvestmentAmount: '1억~ 2억', + initialInvestment: '10,000,000', + kpRatio: 0.513, + smScore: 62.3, + finalProfitLossDate: '2024.03.11', + createdAt: '2024.01.11', + subscribed: true, + }, +] + +export const strategyDetailsHandlers = [ + http.get('/api/strategies/:strategyId', ({ params }) => { + const { strategyId } = params + const strategyData = data.find((item) => item.strategyId === strategyId) + if (strategyData) { + return HttpResponse.json({ ...strategyData }) + } + return HttpResponse.json( + { + isSuccess: false, + message: '전략을 찾을 수 없습니다.', + code: 4002, + }, + { status: 400 } + ) + }), +] From 89e42ee39703b4fa3aba7b2b91d6100b3a9d23e4 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:04:34 +0900 Subject: [PATCH 234/648] =?UTF-8?q?feat:=20msw=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=97=AC=20API=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20mockin?= =?UTF-8?q?g,=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A0=8C=EB=8D=94=EB=A7=81?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/page.tsx | 42 ++++++++++++++- .../_api/get-details-information/index.ts | 33 ++++++++++++ .../strategies/_api/get-strategies/index.ts | 27 ++++++++++ app/(dashboard)/strategies/page.module.scss | 12 +++++ app/(dashboard)/strategies/page.tsx | 54 ++++++++++++++++++- 5 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 app/(dashboard)/strategies/_api/get-details-information/index.ts create mode 100644 app/(dashboard)/strategies/_api/get-strategies/index.ts create mode 100644 app/(dashboard)/strategies/page.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 12345c67..3d39f99f 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,4 +1,42 @@ -const StrategyDetailPage = () => { - return <></> +'use client' + +import DetailsSideItem, { + InformationModel, + TitleType, +} from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' +import useGetDetailsInformationData from '@/app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data' +import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' + +import { useMSWStore } from '@/shared/stores/msw' +import BackHeader from '@/shared/ui/header/back-header' +import Title from '@/shared/ui/title' + +export type InformationType = { title: TitleType; data: string | number } | InformationModel[] + +const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { + const isReady = useMSWStore((state) => state.isReady) + const { data: detailsSideData } = useGetDetailsInformationData({ + isReady, + strategyId: params.strategyId, + }) + + const hasDetailsSideData = detailsSideData?.map((data) => { + if (!Array.isArray(data)) return data.data !== undefined + }) + + return ( + <div> + <BackHeader label={'목록으로 돌아가기'} /> + <Title label={'전략 상세보기'} /> + <SideContainer> + {hasDetailsSideData?.[0] && + detailsSideData?.map((data, idx) => ( + <div key={idx}> + <DetailsSideItem information={data} /> + </div> + ))} + </SideContainer> + </div> + ) } export default StrategyDetailPage diff --git a/app/(dashboard)/strategies/_api/get-details-information/index.ts b/app/(dashboard)/strategies/_api/get-details-information/index.ts new file mode 100644 index 00000000..25bb4461 --- /dev/null +++ b/app/(dashboard)/strategies/_api/get-details-information/index.ts @@ -0,0 +1,33 @@ +import { InformationType } from '@/app/(dashboard)/strategies/[strategyId]/page' +import axios from 'axios' + +const getDetailsInformation = async (isReady: boolean, strategyId: string) => { + if (!isReady || !strategyId) return + + try { + const response = await axios.get(`/api/strategies/${strategyId}`) + if (response) { + const data = await response.data + const newDetailsData: InformationType[] = [ + { title: '트레이더', data: data.nickname }, + { title: '최소 투자 금액', data: data.minimumInvestmentAmount }, + { title: '투자 원금', data: data.initialInvestment }, + + [ + { title: 'KP Ratio', data: data.kpRatio }, + { title: 'SM SCORE', data: data.smScore }, + ], + + [ + { title: '최종손익입력일자', data: data.finalProfitLossDate }, + { title: '등록일', data: data.createdAt }, + ], + ] + return newDetailsData + } + } catch (error) { + console.error(error) + } +} + +export default getDetailsInformation diff --git a/app/(dashboard)/strategies/_api/get-strategies/index.ts b/app/(dashboard)/strategies/_api/get-strategies/index.ts new file mode 100644 index 00000000..0e4b5ea9 --- /dev/null +++ b/app/(dashboard)/strategies/_api/get-strategies/index.ts @@ -0,0 +1,27 @@ +import axios from 'axios' + +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +const getStrategiesData = async ( + isReady: boolean, + page: number, + size: number +): Promise<{ strategiesData: StrategiesModel[]; totalCount: number } | undefined> => { + if (!isReady) return { strategiesData: [], totalCount: 0 } + try { + const response = await axios.get(`/api/strategies?page=${page}&size=${size}`) + + if (response) { + const { + strategiesData, + totalCount, + }: { strategiesData: StrategiesModel[]; totalCount: number } = await response.data + + return { strategiesData, totalCount } + } + } catch (error) { + console.error(error) + } +} + +export default getStrategiesData diff --git a/app/(dashboard)/strategies/page.module.scss b/app/(dashboard)/strategies/page.module.scss new file mode 100644 index 00000000..d529dca4 --- /dev/null +++ b/app/(dashboard)/strategies/page.module.scss @@ -0,0 +1,12 @@ +.container { + margin-top: 80px; +} + +.pagination { + margin-bottom: 24px; +} + +.search-bar { + width: 100%; + background-color: $color-orange-200; +} diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 95276f9b..1a24a51f 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -1,5 +1,57 @@ +'use client' + +import { useRouter, useSearchParams } from 'next/navigation' + +import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' +import useGetStrategiesData from '@/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data' +import ListHeader from '@/app/(dashboard)/strategies/_ui/list-header' +import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' +import classNames from 'classnames/bind' + +import { useMSWStore } from '@/shared/stores/msw' +import Pagination from '@/shared/ui/pagination' +import Title from '@/shared/ui/title' + +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const COUNT_PER_PAGE = 8 + const StrategiesPage = () => { - return <></> + const searchParams = useSearchParams() + const router = useRouter() + const isReady = useMSWStore((state) => state.isReady) + const page = parseInt(searchParams.get('page') || '1') + + const { data } = useGetStrategiesData({ isReady, page, size: COUNT_PER_PAGE }) + + const strategiesData = data?.strategiesData || [] + const totalCount = data?.totalCount || 0 + + const handlePageChange = (page: number) => { + router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`) + } + + return ( + <div className={cx('container')}> + <Title label={'전략 랭킹 모음'} /> + <ListHeader /> + {strategiesData.map((strategy) => ( + <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> + ))} + <div className={cx('pagination')}> + <Pagination + currentPage={page} + maxPage={Math.ceil(totalCount / COUNT_PER_PAGE)} + onPageChange={handlePageChange} + /> + </div> + <SideContainer> + <p className={cx('search-bar')}>Search-Bar</p> + </SideContainer> + </div> + ) } export default StrategiesPage From 0e44ecf8aa2ac8845cac3ca2b8598a029cd6a40d Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:05:23 +0900 Subject: [PATCH 235/648] =?UTF-8?q?design:=20=EC=B0=A8=ED=8A=B8=20width?= =?UTF-8?q?=EA=B0=92=20=EB=B6=80=EB=AA=A8=EC=9A=94=EC=86=8C=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20=EC=88=98=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/area-chart.tsx | 27 +++++++++++++++++-- .../_ui/strategies-item/styles.module.scss | 5 ++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/_ui/strategies-item/area-chart.tsx index 0bd8134d..3c646590 100644 --- a/app/(dashboard)/_ui/strategies-item/area-chart.tsx +++ b/app/(dashboard)/_ui/strategies-item/area-chart.tsx @@ -2,13 +2,19 @@ import dynamic from 'next/dynamic' +import classNames from 'classnames/bind' import Highcharts from 'highcharts' import { ProfitRateChartDataModel } from '@/shared/types/strategy-details-data' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + const HighchartsReact = dynamic(() => import('highcharts-react-official'), { ssr: false, }) + interface Props { profitRateChartData: ProfitRateChartDataModel[] } @@ -19,7 +25,6 @@ const AreaChart = ({ profitRateChartData }: Props) => { chart: { type: 'areaspline', height: 100, - width: 180, backgroundColor: 'transparent', margin: [0, 0, 0, 0], }, @@ -67,9 +72,27 @@ const AreaChart = ({ profitRateChartData }: Props) => { ], credits: { enabled: false }, tooltip: { enabled: false }, + responsive: { + rules: [ + { + condition: { + maxWidth: 200, + }, + chartOptions: { + chart: { + width: null, + }, + }, + }, + ], + }, } - return <HighchartsReact highcharts={Highcharts} options={chartOptions} /> + return ( + <div className={cx('chart')}> + <HighchartsReact highcharts={Highcharts} options={chartOptions} /> + </div> + ) } export default AreaChart diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index d36ed422..3a0f7339 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -62,3 +62,8 @@ background: transparent; } } + +.chart { + width: 100%; + overflow: hidden; +} From faf16196783b751a8eff7368a0f27602d9656c3f Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:06:35 +0900 Subject: [PATCH 236/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5,=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=EC=83=81=EC=84=B8=EC=97=90=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=A0=20=EC=82=AC=EC=9D=B4=EB=93=9C=EC=BB=A8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=84=88=20=EC=B6=94=EA=B0=80=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_ui/side-container/index.tsx | 17 +++++++++++++++++ .../_ui/side-container/styles.module.scss | 6 ++++++ 2 files changed, 23 insertions(+) create mode 100644 app/(dashboard)/strategies/_ui/side-container/index.tsx create mode 100644 app/(dashboard)/strategies/_ui/side-container/styles.module.scss diff --git a/app/(dashboard)/strategies/_ui/side-container/index.tsx b/app/(dashboard)/strategies/_ui/side-container/index.tsx new file mode 100644 index 00000000..82b4c956 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/side-container/index.tsx @@ -0,0 +1,17 @@ +'use client' + +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + children: React.ReactNode +} + +const SideContainer = ({ children }: Props) => { + return <aside className={cx('side-bar')}>{children}</aside> +} + +export default SideContainer diff --git a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss new file mode 100644 index 00000000..092744ff --- /dev/null +++ b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss @@ -0,0 +1,6 @@ +.side-bar { + width: $strategy-sidebar-width; + position: fixed; + right: 28px; + top: 130px; +} From 6ba151b9473b793850cfdcea183523d4c66648fb Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:07:13 +0900 Subject: [PATCH 237/648] =?UTF-8?q?feat:=20tanstack-query=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_query/use-get-details-information-data.ts | 17 +++++++++++++++++ .../_hooks/_query/use-get-strategies-data.ts | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data.ts create mode 100644 app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data.ts diff --git a/app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data.ts b/app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data.ts new file mode 100644 index 00000000..4348ca45 --- /dev/null +++ b/app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data.ts @@ -0,0 +1,17 @@ +import getDetailsInformation from '@/app/(dashboard)/strategies/_api/get-details-information' +import { useQuery } from '@tanstack/react-query' + +interface Props { + isReady: boolean + strategyId: string +} + +const useGetDetailsInformationData = ({ isReady, strategyId }: Props) => { + return useQuery({ + queryKey: ['strategyDetails', strategyId], + queryFn: () => getDetailsInformation(isReady, strategyId), + enabled: isReady, + }) +} + +export default useGetDetailsInformationData diff --git a/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data.ts b/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data.ts new file mode 100644 index 00000000..a084dfcf --- /dev/null +++ b/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data.ts @@ -0,0 +1,18 @@ +import getStrategiesData from '@/app/(dashboard)/strategies/_api/get-strategies' +import { useQuery } from '@tanstack/react-query' + +interface Props { + isReady: boolean + page: number + size: number +} + +const useGetStrategiesData = ({ isReady, page, size }: Props) => { + return useQuery({ + queryKey: ['strategies', page, size], + queryFn: () => getStrategiesData(isReady, page, size), + enabled: isReady, + }) +} + +export default useGetStrategiesData From 74ba0000a0f98e2e12cc4b8b1670f78c96c95998 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:08:06 +0900 Subject: [PATCH 238/648] =?UTF-8?q?design:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EB=B2=84=ED=8A=BC=20background?= =?UTF-8?q?=20color=20=EC=88=98=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/pagination/pagination.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/ui/pagination/pagination.module.scss b/shared/ui/pagination/pagination.module.scss index 8063d902..3f637602 100644 --- a/shared/ui/pagination/pagination.module.scss +++ b/shared/ui/pagination/pagination.module.scss @@ -37,5 +37,6 @@ border: 0; margin: 0 -10px; padding: 2px; + background-color: transparent; } } From 92df9d5a2430f18d76688925988e79520f2d8560 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:09:11 +0900 Subject: [PATCH 239/648] =?UTF-8?q?design:=20header=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=ED=8F=B0=ED=8A=B8=ED=81=AC=EA=B8=B0,=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EC=BD=98=EC=9C=84=EC=B9=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/list-header/styles.module.scss | 21 +++++++++++++++++++ .../ui/header/back-header/styles.module.scss | 5 +++-- shared/ui/header/styles.module.scss | 1 - 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 app/(dashboard)/strategies/_ui/list-header/styles.module.scss diff --git a/app/(dashboard)/strategies/_ui/list-header/styles.module.scss b/app/(dashboard)/strategies/_ui/list-header/styles.module.scss new file mode 100644 index 00000000..0050d4c6 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/list-header/styles.module.scss @@ -0,0 +1,21 @@ +.container { + display: grid; + grid-template-columns: 1.6fr 1.2fr repeat(3, 0.6fr) 0.4fr; + width: 100%; + height: 42px; + margin: 20px 0 10px; + + .category { + display: flex; + align-items: center; + justify-content: center; + background-color: $color-white; + border: 1px solid $color-gray-200; + border-radius: 4px; + @include typo-b2; + + &:not(:first-child) { + margin-left: 5px; + } + } +} diff --git a/shared/ui/header/back-header/styles.module.scss b/shared/ui/header/back-header/styles.module.scss index b05c0f39..4206b017 100644 --- a/shared/ui/header/back-header/styles.module.scss +++ b/shared/ui/header/back-header/styles.module.scss @@ -1,5 +1,5 @@ .container { - @include typo-c2; + @include typo-c1; display: flex; align-items: center; gap: 8px; @@ -7,7 +7,8 @@ color: $color-gray-500; svg { - width: 9px; + width: 11px; + height: 26px; flex-shrink: 0; } } diff --git a/shared/ui/header/styles.module.scss b/shared/ui/header/styles.module.scss index 32b8ddc8..211b3c25 100644 --- a/shared/ui/header/styles.module.scss +++ b/shared/ui/header/styles.module.scss @@ -3,7 +3,6 @@ top: 0; height: $header-height; width: 100%; - padding: 0 42px; display: flex; justify-content: space-between; align-items: center; From 6993e1d475a2d16f24811ba82f393e41376956c9 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:09:40 +0900 Subject: [PATCH 240/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=ED=97=A4=EB=8D=94=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_ui/list-header/index.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/(dashboard)/strategies/_ui/list-header/index.tsx diff --git a/app/(dashboard)/strategies/_ui/list-header/index.tsx b/app/(dashboard)/strategies/_ui/list-header/index.tsx new file mode 100644 index 00000000..c1edf917 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/list-header/index.tsx @@ -0,0 +1,21 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const LIST_HEADER = ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '구독'] + +const ListHeader = () => { + return ( + <div className={cx('container')}> + {LIST_HEADER.map((category) => ( + <div key={category} className={cx('category')}> + {category} + </div> + ))} + </div> + ) +} + +export default ListHeader From bd2f59f6cf71dcbc2b18e8231b88b0484ae973ad Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 17:23:09 +0900 Subject: [PATCH 241/648] =?UTF-8?q?fix:=20client=EC=98=B5=EC=85=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_ui/side-container/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/(dashboard)/strategies/_ui/side-container/index.tsx b/app/(dashboard)/strategies/_ui/side-container/index.tsx index 82b4c956..3bc6e425 100644 --- a/app/(dashboard)/strategies/_ui/side-container/index.tsx +++ b/app/(dashboard)/strategies/_ui/side-container/index.tsx @@ -1,5 +1,3 @@ -'use client' - import classNames from 'classnames/bind' import styles from './styles.module.scss' From 43512566e988e7b98284fca95fb23b3948b52dd2 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Thu, 21 Nov 2024 20:21:45 +0900 Subject: [PATCH 242/648] =?UTF-8?q?fix:=20select=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EA=B5=90=EC=B2=B4=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index cb196d8e..bc24f508 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -2,7 +2,7 @@ import { CSSProperties, ReactNode, createContext } from 'react' -import { CheckboxIcon, ChevronDownIcon, ChevronUpIcon } from '@/public/icons' +import { CheckboxIcon, CloseIcon, OpenIcon } from '@/public/icons' import classNames from 'classnames/bind' import { useDropdown } from './hooks/use-dropdown' @@ -58,7 +58,7 @@ const Dropdown = ({ <div style={containerStyle} ref={dropdownRef}> <button onClick={toggleOpen} className={cx('container', 'trigger')} style={labelStyle}> {Trigger} - {isOpen ? <ChevronUpIcon /> : <ChevronDownIcon />} + {isOpen ? <OpenIcon /> : <CloseIcon />} </button> {isOpen && <ul className={cx('options')}>{children}</ul>} </div> From ae450385047616a1e2dbe0d3c2b20d50687ef9a1 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 21 Nov 2024 22:19:52 +0900 Subject: [PATCH 243/648] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9D=B4=20=EB=B0=94=EB=80=8C=EC=97=88=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EC=95=BD=EA=B4=80=EB=8F=99=EC=9D=98=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=ED=95=B4=EC=A0=9C=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/user-type-card/index.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx index 25882ff9..55ef1c8f 100644 --- a/app/(landing)/signup/_ui/user-type-card/index.tsx +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -2,7 +2,11 @@ import { useRouter } from 'next/navigation' -import { setUserTypeCookie } from '@/app/(landing)/signup/_lib/cookies' +import { + getUserTypeCookie, + setIsAgreedTermsCookie, + setUserTypeCookie, +} from '@/app/(landing)/signup/_lib/cookies' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -20,8 +24,13 @@ interface Props { const UserTypeCard = ({ userType, title, highlight }: Props) => { const router = useRouter() + const userTypeCookie = getUserTypeCookie() const handleTypeSelect = () => { + if (userTypeCookie && userTypeCookie !== userType) { + setIsAgreedTermsCookie(false) + } + setUserTypeCookie(userType) router.push(PATH.SIGN_UP_TERMS_OF_USE) } From 29f1e0e624ccc3c8b3815f0c952448183478a450 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 23:14:22 +0900 Subject: [PATCH 244/648] =?UTF-8?q?fix:=20page=EB=82=B4=EC=97=90=20=20useS?= =?UTF-8?q?earchParams=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_ui/strategy-list/index.tsx | 54 +++++++++++++++++++ .../_ui/strategy-list/styles.module.scss | 3 ++ app/(dashboard)/strategies/page.module.scss | 4 -- app/(dashboard)/strategies/page.tsx | 50 ++++------------- next.config.mjs | 3 -- 5 files changed, 67 insertions(+), 47 deletions(-) create mode 100644 app/(dashboard)/strategies/_ui/strategy-list/index.tsx create mode 100644 app/(dashboard)/strategies/_ui/strategy-list/styles.module.scss diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx new file mode 100644 index 00000000..a3867354 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -0,0 +1,54 @@ +'use client' + +import { useRouter, useSearchParams } from 'next/navigation' + +import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' +import useGetStrategiesData from '@/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data' +import ListHeader from '@/app/(dashboard)/strategies/_ui/list-header' +import classNames from 'classnames/bind' + +import { useMSWStore } from '@/shared/stores/msw' +import Pagination from '@/shared/ui/pagination' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const COUNT_PER_PAGE = 8 + +const StrategyList = () => { + const searchParams = useSearchParams() + const router = useRouter() + const isReady = useMSWStore((state) => state.isReady) + const page = parseInt(searchParams?.get('page') || '1') + + const { data } = useGetStrategiesData({ isReady, page, size: COUNT_PER_PAGE }) + + const strategiesData = data?.strategiesData || [] + const totalCount = data?.totalCount || 0 + + const handlePageChange = (page: number) => { + if (typeof window !== 'undefined') { + router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`) + } + } + return ( + <> + <ListHeader /> + {strategiesData?.map((strategy) => ( + <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> + ))} + <div className={cx('pagination')}> + {totalCount && ( + <Pagination + currentPage={page} + maxPage={Math.ceil(totalCount / COUNT_PER_PAGE)} + onPageChange={handlePageChange} + /> + )} + </div> + </> + ) +} + +export default StrategyList diff --git a/app/(dashboard)/strategies/_ui/strategy-list/styles.module.scss b/app/(dashboard)/strategies/_ui/strategy-list/styles.module.scss new file mode 100644 index 00000000..f5081c9f --- /dev/null +++ b/app/(dashboard)/strategies/_ui/strategy-list/styles.module.scss @@ -0,0 +1,3 @@ +.pagination { + margin-bottom: 24px; +} diff --git a/app/(dashboard)/strategies/page.module.scss b/app/(dashboard)/strategies/page.module.scss index d529dca4..4a762d84 100644 --- a/app/(dashboard)/strategies/page.module.scss +++ b/app/(dashboard)/strategies/page.module.scss @@ -2,10 +2,6 @@ margin-top: 80px; } -.pagination { - margin-bottom: 24px; -} - .search-bar { width: 100%; background-color: $color-orange-200; diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 1a24a51f..05fe2721 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -1,56 +1,26 @@ -'use client' +import { Suspense } from 'react' -import { useRouter, useSearchParams } from 'next/navigation' - -import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' -import useGetStrategiesData from '@/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data' -import ListHeader from '@/app/(dashboard)/strategies/_ui/list-header' import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' +import StrategyList from '@/app/(dashboard)/strategies/_ui/strategy-list' import classNames from 'classnames/bind' -import { useMSWStore } from '@/shared/stores/msw' -import Pagination from '@/shared/ui/pagination' import Title from '@/shared/ui/title' import styles from './page.module.scss' const cx = classNames.bind(styles) -const COUNT_PER_PAGE = 8 - const StrategiesPage = () => { - const searchParams = useSearchParams() - const router = useRouter() - const isReady = useMSWStore((state) => state.isReady) - const page = parseInt(searchParams.get('page') || '1') - - const { data } = useGetStrategiesData({ isReady, page, size: COUNT_PER_PAGE }) - - const strategiesData = data?.strategiesData || [] - const totalCount = data?.totalCount || 0 - - const handlePageChange = (page: number) => { - router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`) - } - return ( - <div className={cx('container')}> - <Title label={'전략 랭킹 모음'} /> - <ListHeader /> - {strategiesData.map((strategy) => ( - <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> - ))} - <div className={cx('pagination')}> - <Pagination - currentPage={page} - maxPage={Math.ceil(totalCount / COUNT_PER_PAGE)} - onPageChange={handlePageChange} - /> + <Suspense fallback={<div>Loading...</div>}> + <div className={cx('container')}> + <Title label={'전략 랭킹 모음'} /> + <StrategyList /> + <SideContainer> + <p className={cx('search-bar')}>Search-Bar</p> + </SideContainer> </div> - <SideContainer> - <p className={cx('search-bar')}>Search-Bar</p> - </SideContainer> - </div> + </Suspense> ) } diff --git a/next.config.mjs b/next.config.mjs index 413f6eb6..4352ad16 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,9 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ['msw'], - experimental: { - appDir: true, - }, sassOptions: { includePaths: ['./shared/styles'], prependData: ` From 211e0c40a21971bcd02e01fee3e22105c6e2fb25 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 23:24:52 +0900 Subject: [PATCH 245/648] =?UTF-8?q?fix=20:=20suspense=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/page.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 05fe2721..94e82ea3 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -12,15 +12,15 @@ const cx = classNames.bind(styles) const StrategiesPage = () => { return ( - <Suspense fallback={<div>Loading...</div>}> - <div className={cx('container')}> - <Title label={'전략 랭킹 모음'} /> + <div className={cx('container')}> + <Title label={'전략 랭킹 모음'} /> + <Suspense fallback={<div>Loading...</div>}> <StrategyList /> - <SideContainer> - <p className={cx('search-bar')}>Search-Bar</p> - </SideContainer> - </div> - </Suspense> + </Suspense> + <SideContainer> + <p className={cx('search-bar')}>Search-Bar</p> + </SideContainer> + </div> ) } From fa7e1ed12aa50c6b882820c55af93e50a6c7bc62 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 21 Nov 2024 23:32:57 +0900 Subject: [PATCH 246/648] =?UTF-8?q?design:=20back=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EB=90=9C=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/back.svg | 3 +++ public/icons/index.tsx | 1 + shared/styles/stories/icons.stories.tsx | 2 ++ shared/ui/header/back-header/index.tsx | 4 ++-- shared/ui/header/back-header/styles.module.scss | 4 ++-- 5 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 public/icons/back.svg diff --git a/public/icons/back.svg b/public/icons/back.svg new file mode 100644 index 00000000..83ecbd0a --- /dev/null +++ b/public/icons/back.svg @@ -0,0 +1,3 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99591 11.8481L15.6807 7.05818L14.9832 6.23047L8.29816 11.8633L11.5617 14.5149L15.0049 17.126L15.659 16.2636L12.2301 13.6634L9.99591 11.8481Z" fill="#797979"/> +</svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 0c4cc750..37a6c3ac 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -28,3 +28,4 @@ export { default as TradersIcon } from './traders.svg' export { default as StarIcon } from './star.svg' export { default as CircleIcon } from './circle.svg' export { default as CheckedCircleIcon } from './checked-circle.svg' +export { default as BackIcon } from './back.svg' diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index cc6dc7bc..bf5e7202 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -1,4 +1,5 @@ import { + BackIcon, BarsIcon, BookmarkIcon, BookmarkOutlineIcon, @@ -63,6 +64,7 @@ const icons = [ { name: 'StrategyIcon', icon: StrategyIcon }, { name: 'TradersIcon', icon: TradersIcon }, { name: 'StarIcon', icon: StarIcon }, + { name: 'BackIcon', icon: BackIcon }, ] export const Icons: Story = { diff --git a/shared/ui/header/back-header/index.tsx b/shared/ui/header/back-header/index.tsx index 45d33f63..516141a9 100644 --- a/shared/ui/header/back-header/index.tsx +++ b/shared/ui/header/back-header/index.tsx @@ -5,7 +5,7 @@ // TODO: 스타일 추가되면 바꿔야함 import { useRouter } from 'next/navigation' -import { ChevronLeftIcon } from '@/public/icons' +import { BackIcon } from '@/public/icons' import classNames from 'classnames/bind' import Header from '..' @@ -34,7 +34,7 @@ const Left = ({ label }: Props) => { return ( <button onClick={onClick} className={cx('container')}> - <ChevronLeftIcon /> + <BackIcon /> <span>{label}</span> </button> ) diff --git a/shared/ui/header/back-header/styles.module.scss b/shared/ui/header/back-header/styles.module.scss index 4206b017..f338e45e 100644 --- a/shared/ui/header/back-header/styles.module.scss +++ b/shared/ui/header/back-header/styles.module.scss @@ -7,8 +7,8 @@ color: $color-gray-500; svg { - width: 11px; - height: 26px; + width: 14px; + height: 24px; flex-shrink: 0; } } From 41b275d4e0192d9e9e73162d78a569c8e735160d Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 01:45:00 +0900 Subject: [PATCH 247/648] =?UTF-8?q?design:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20SCSS=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#75)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/traders-list-card/index.tsx | 28 +++++++++------ .../ui/traders-list-card/styles.module.scss | 36 +++++++++---------- .../traders-list-card.stories.tsx | 6 ++-- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/shared/ui/traders-list-card/index.tsx b/shared/ui/traders-list-card/index.tsx index c58423fc..fe85ff35 100644 --- a/shared/ui/traders-list-card/index.tsx +++ b/shared/ui/traders-list-card/index.tsx @@ -8,32 +8,40 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - name: string + nickname: string profileImage?: string - strategy: number - subscribe: number + strategyCount: number + subscriberCount: number traderId: string } -const TradersListCard = ({ name, profileImage, strategy, subscribe }: Props) => { +const TradersListCard = ({ + nickname, + profileImage, + strategyCount, + subscriberCount, + traderId, +}: Props) => { return ( <div className={cx('traders-list-card')}> <div className={cx('contents')}> - <div className={cx('profile-name')}>{name}</div> + <div className={cx('trader-info')}> + <div className={cx('trader-nickname')}>{nickname}</div> + <div className={cx('count-info')}> + <div>전략 {strategyCount}개</div> + <div>구독 {subscriberCount}개</div> + </div> + </div> <div className={cx('avatar')}> <Avatar src={profileImage} size="large" /> </div> - <div className={cx('information')}> - <div>전략 {strategy}개</div> - <div>구독 {subscribe}개</div> - </div> </div> <div className="link-button-wrapper"> <LinkButton href={'${PATH.TRADERS}/${traderId}'} size="medium" + variant="filled" className={cx('link-button')} - style={{ color: '#ff5f33' }} > 전략 목록 상세보기 </LinkButton> diff --git a/shared/ui/traders-list-card/styles.module.scss b/shared/ui/traders-list-card/styles.module.scss index 7617437a..c2772ce5 100644 --- a/shared/ui/traders-list-card/styles.module.scss +++ b/shared/ui/traders-list-card/styles.module.scss @@ -1,52 +1,50 @@ .traders-list-card { + display: flex; + flex-direction: column; background-color: $color-white; border-radius: 8px; width: 300px; height: 220px; - padding: 28px 25px; - display: flex; - flex-direction: column; - position: relative; + padding: 32px 25px; } .contents { - margin-bottom: 35px; + margin-bottom: 17px; + display: flex; + justify-content: space-between; +} + +.trader-info { display: flex; flex-direction: column; - position: relative; + align-items: flex-start; } -.profile-name { - @include typo-h3; +.trader-nickname { + @include typo-b2; font-weight: $text-semibold; - position: absolute; - margin-top: 5px; } .avatar { - position: absolute; - right: 0px; + justify-content: flex-end; } -.information { +.count-info { @include typo-b3; color: $color-gray-500; - font-weight: $text-semibold; - margin-top: 45px; - display: flex; - flex-direction: column; + margin-top: 36px; } .link-button-wrapper { display: flex; justify-content: center; width: 100%; - margin-top: auto; } .link-button { align-self: center; text-align: center; - border-color: $color-orange-600; + border-color: $color-gray-800; width: 100%; + max-height: 45px; } diff --git a/shared/ui/traders-list-card/traders-list-card.stories.tsx b/shared/ui/traders-list-card/traders-list-card.stories.tsx index 0dbf0a84..12ead599 100644 --- a/shared/ui/traders-list-card/traders-list-card.stories.tsx +++ b/shared/ui/traders-list-card/traders-list-card.stories.tsx @@ -25,10 +25,10 @@ type StoryType = StoryObj<typeof meta> export const Default: StoryType = { args: { - name: '고양이', + nickname: '고양이는야옹하고울지', profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', - strategy: 10, - subscribe: 10, + strategyCount: 10, + subscriberCount: 10, traderId: '1234', }, } From 57768d6bdca3c5c20b01466cd87bed2e171b282f Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 22 Nov 2024 02:52:32 +0900 Subject: [PATCH 248/648] =?UTF-8?q?feat:=20dropdown,=20=20select=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=97=90=20size=20props=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/index.tsx | 38 ++++----- shared/ui/dropdown/styles.module.scss | 107 ++++++++++++++++++++------ shared/ui/dropdown/types.ts | 16 +++- shared/ui/select/index.tsx | 17 +++- 4 files changed, 131 insertions(+), 47 deletions(-) diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index bc24f508..a7e1716d 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { CSSProperties, ReactNode, createContext } from 'react' +import { createContext } from 'react' import { CheckboxIcon, CloseIcon, OpenIcon } from '@/public/icons' import classNames from 'classnames/bind' @@ -9,7 +9,7 @@ import { useDropdown } from './hooks/use-dropdown' import { useDropdownContext } from './hooks/use-dropdown-context' import RangeOption from './option/range-option' import styles from './styles.module.scss' -import { DropdownItemProps, DropdownValueType } from './types' +import { DropdownItemProps, DropdownProps } from './types' const cx = classNames.bind(styles) @@ -28,17 +28,8 @@ const defaultContext = { export const DropdownContext = createContext<DropdownContextProps>(defaultContext) -export interface DropdownProps { - Trigger: ReactNode - value: DropdownValueType - onChange: (value: DropdownValueType) => void - isMultiple: boolean - containerStyle?: CSSProperties - labelStyle?: CSSProperties - children?: ReactNode -} - const Dropdown = ({ + size = 'small', Trigger, value, onChange, @@ -55,31 +46,42 @@ const Dropdown = ({ return ( <DropdownContext.Provider value={{ isOpen, toggleOpen, handleSelect }}> - <div style={containerStyle} ref={dropdownRef}> - <button onClick={toggleOpen} className={cx('container', 'trigger')} style={labelStyle}> + <div className={cx(`dropdown-${size}`)} style={containerStyle} ref={dropdownRef}> + <button + onClick={toggleOpen} + className={cx('container', 'trigger', size, { open: isOpen })} + style={labelStyle} + > {Trigger} {isOpen ? <OpenIcon /> : <CloseIcon />} </button> - {isOpen && <ul className={cx('options')}>{children}</ul>} + {isOpen && <ul className={cx('options', size)}>{children}</ul>} </div> </DropdownContext.Provider> ) } -const Item = ({ isSelected, value, label, hasCheck = false, style }: DropdownItemProps) => { +const Item = ({ + isSelected, + value, + label, + hasCheck = false, + size = 'small', + style, +}: DropdownItemProps) => { const context = useDropdownContext() const { handleSelect } = context return ( <li - className={cx('container', 'option', { selected: isSelected })} + className={cx('container', 'option', size, { selected: isSelected })} style={style} onClick={() => handleSelect(value)} aria-hidden > {label} - {hasCheck && <CheckboxIcon className={cx('icon', { 'selected-icon': isSelected })} />} + {hasCheck && <CheckboxIcon className={cx({ 'selected-icon': isSelected })} />} </li> ) } diff --git a/shared/ui/dropdown/styles.module.scss b/shared/ui/dropdown/styles.module.scss index db645d3c..5279afc5 100644 --- a/shared/ui/dropdown/styles.module.scss +++ b/shared/ui/dropdown/styles.module.scss @@ -1,48 +1,107 @@ -// NOTE: color, padding 등 세부 수치들 미정, 일단 제일 가까운 걸로 함 +$small-width: 90px; +$small-height: 30px; +$large-width: 160px; +$large-height: 40px; + +$color-selected-text: #171717; + +.dropdown { + &-small { + width: $small-width; + height: fit-content; + } + + &-large { + width: $large-width; + height: fit-content; + } +} .container { - width: 100%; - padding: 7px 11px; - border: 1px solid $color-gray-300; + border: 1px solid $color-gray-200; border-radius: 4px; display: flex; + justify-content: space-between; + flex-shrink: 0; align-items: center; - @include typo-c2; + @include typo-c1; + + svg { + color: $color-gray-200; + margin-left: auto; + } } .trigger { - width: 100%; - display: flex; - justify-content: space-between; - color: $color-gray-400; - background-color: #fff; + color: $color-gray-800; + background-color: $color-white; + + svg { + path { + fill: $color-gray-800; + } + } + + &:hover { + border-color: $color-orange-500; + } + + &.open { + border-color: $color-orange-500; + box-shadow: 0px 0px 2px 0px $color-orange-500; + } +} + +.small { + width: $small-width; + height: $small-height; + padding: 3px 6px 3px 10px; +} + +.large { + width: $large-width; + height: $large-height; + padding-left: 10.5px; + padding-right: 14.5px; } .options { list-style: none; - padding: 0; + height: fit-content; margin: 0; - background-color: #fff; + margin-top: 4px; + padding: 0; + border-radius: 4px; + background-color: $color-white; + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.25); + overflow: hidden; + + &.small { + width: $small-width; + } + + &.large { + width: $large-width; + } } .option { - border-top-color: transparent; + width: 100%; + color: $color-gray-500; cursor: pointer; + border: none; + border-radius: 0; + user-select: none; - .icon { - color: $color-gray-300; - margin-left: auto; - } - - .selected-icon { - color: $color-gray-800; + &.selected { + color: $color-selected-text; } - &.selected { - background-color: $color-gray-300; + &:hover { + background-color: $color-orange-100; } - &:not(.selected):hover { - background-color: #f0f0f0; + .selected-icon { + color: $color-gray-800; } } diff --git a/shared/ui/dropdown/types.ts b/shared/ui/dropdown/types.ts index e8cf0e7a..7a6c0e89 100644 --- a/shared/ui/dropdown/types.ts +++ b/shared/ui/dropdown/types.ts @@ -1,4 +1,4 @@ -import { CSSProperties } from 'react' +import { CSSProperties, ReactNode } from 'react' export interface DropdownOptionModel { value: string @@ -7,7 +7,21 @@ export interface DropdownOptionModel { export type DropdownValueType = string | string[] | null +export type DropdownSizeType = 'small' | 'large' + +export interface DropdownProps { + size?: DropdownSizeType + Trigger: ReactNode + value: DropdownValueType + onChange: (value: DropdownValueType) => void + isMultiple: boolean + containerStyle?: CSSProperties + labelStyle?: CSSProperties + children?: ReactNode +} + export interface DropdownItemProps { + size?: DropdownSizeType value: string label: string isSelected: boolean diff --git a/shared/ui/select/index.tsx b/shared/ui/select/index.tsx index 8c1666f0..c675c918 100644 --- a/shared/ui/select/index.tsx +++ b/shared/ui/select/index.tsx @@ -1,13 +1,20 @@ 'use client' -// TODO: muliple 일 때는 상단에 placeholder를 그대로 둘지 고민 import { CSSProperties } from 'react' -import Dropdown, { DropdownProps } from '../dropdown' -import { DropdownItemProps, DropdownOptionModel, DropdownValueType } from '../dropdown/types' +import Dropdown from '../dropdown' +import { + DropdownItemProps, + DropdownOptionModel, + DropdownProps, + DropdownValueType, +} from '../dropdown/types' import { useSelect } from './hooks/useSelect' -export type SelectType = Pick<DropdownProps, 'isMultiple' | 'containerStyle' | 'labelStyle'> & +export type SelectType = Pick< + DropdownProps, + 'size' | 'isMultiple' | 'containerStyle' | 'labelStyle' +> & Pick<DropdownItemProps, 'hasCheck'> & { placeholder?: string value: DropdownValueType @@ -18,6 +25,7 @@ export type SelectType = Pick<DropdownProps, 'isMultiple' | 'containerStyle' | ' } const Select = ({ + size, isRounded = false, isMultiple = false, hasCheck = false, @@ -37,6 +45,7 @@ const Select = ({ return ( <Dropdown + size={size} Trigger={<span>{findLabel(options, value) ?? placeholder ?? options[0].label}</span>} isMultiple={isMultiple} value={value} From 3f7b42076cceca7a1f5333d2a7900bf81da5f1a8 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 22 Nov 2024 03:02:40 +0900 Subject: [PATCH 249/648] =?UTF-8?q?feat:=20select=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=B4=EC=A7=84=20isRound=20=20pr?= =?UTF-8?q?ops=20=EC=A0=9C=EA=B1=B0=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/select/index.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/shared/ui/select/index.tsx b/shared/ui/select/index.tsx index c675c918..20af3d21 100644 --- a/shared/ui/select/index.tsx +++ b/shared/ui/select/index.tsx @@ -19,14 +19,12 @@ export type SelectType = Pick< placeholder?: string value: DropdownValueType onChange: (value: DropdownValueType) => void - isRounded?: boolean titleStyle?: CSSProperties options: DropdownOptionModel[] } const Select = ({ size, - isRounded = false, isMultiple = false, hasCheck = false, containerStyle, @@ -40,9 +38,6 @@ const Select = ({ if (!options.length) return null - const roundStyle = { borderRadius: '40px', overflow: 'hidden' } - const placeholderStyle = isRounded ? { ...roundStyle, ...titleStyle } : titleStyle - return ( <Dropdown size={size} @@ -51,7 +46,7 @@ const Select = ({ value={value} onChange={onChange} containerStyle={containerStyle} - labelStyle={placeholderStyle} + labelStyle={titleStyle} > {options.map(({ value: itemValue, label }) => ( <Dropdown.Item From 56fa53abf97d4c2f6792f93fdfc730356fca4d05 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 22 Nov 2024 03:06:16 +0900 Subject: [PATCH 250/648] =?UTF-8?q?fix:=20useDropdown=EC=97=90=20multiple?= =?UTF-8?q?=EB=A5=BC=20string=20value=EC=99=80=20=ED=95=A8=EA=BB=98=20?= =?UTF-8?q?=EB=84=98=EA=B8=B8=20=EB=95=8C=EC=9D=98=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/hooks/use-dropdown.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/dropdown/hooks/use-dropdown.ts b/shared/ui/dropdown/hooks/use-dropdown.ts index 7a931699..9d5ac325 100644 --- a/shared/ui/dropdown/hooks/use-dropdown.ts +++ b/shared/ui/dropdown/hooks/use-dropdown.ts @@ -8,7 +8,7 @@ interface UseDropdownProps extends DropdownStateModel { export const useDropdown = ({ isMultiple = false, value, onChange }: UseDropdownProps) => { if (isMultiple && typeof value === 'string') { - throw new Error('multiple 옵션은 객체 value와 사용해야합니다.') + value = [value] } const [isOpen, setIsOpen] = useState(false) From 380746d059d67ceb0c79e92cb77cb006bd3a5e6a Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 22 Nov 2024 03:07:47 +0900 Subject: [PATCH 251/648] =?UTF-8?q?fix:=20select=EC=9D=98=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=82=AC=ED=95=AD=EC=97=90=20=EB=A7=9E=EC=B6=B0=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EC=88=98=EC=A0=95=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/select/select.stories.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/ui/select/select.stories.tsx b/shared/ui/select/select.stories.tsx index e7b64e86..674a1e94 100644 --- a/shared/ui/select/select.stories.tsx +++ b/shared/ui/select/select.stories.tsx @@ -51,19 +51,19 @@ const ControlledSelect = (args: SelectType) => { export const Default: StoryType = { render: (args) => <ControlledSelect {...args} />, args: { + size: 'small', placeholder: '수익률', - isRounded: false, isMultiple: false, hasCheck: false, options, }, } -export const Rounded: StoryType = { +export const Large: StoryType = { render: (args) => <ControlledSelect {...args} />, args: { ...Default.args, - isRounded: true, + size: 'large', }, } From ec04b02c04fa9bec1d8483d7c6cd650ece622658 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 05:03:43 +0900 Subject: [PATCH 252/648] =?UTF-8?q?refactor:=20Modal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20children=20props=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_variables.scss | 1 + shared/ui/modal/index.tsx | 36 ++++----------------- shared/ui/modal/modal.stories.tsx | 51 +++++++----------------------- shared/ui/modal/styles.module.scss | 43 ++++--------------------- 4 files changed, 25 insertions(+), 106 deletions(-) diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index 20d1dfc1..4b2e1681 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -63,6 +63,7 @@ $z-index: ( header: 100, base: 1, hidden: -1, + overlay: 900, ); /* Width */ diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index ad1b01b4..1df56efa 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -2,32 +2,21 @@ import { ReactNode } from 'react' -import { CloseIcon } from '@/public/icons' import classNames from 'classnames/bind' import { createPortal } from 'react-dom' -import { Button } from '../button' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - contents?: string icon?: ReactNode + contents?: string + children?: React.ReactNode isOpen: boolean - hasTwoButtons?: boolean - confirmButton: () => void - closeModal: () => void } -const Modal = ({ - contents, - icon, - isOpen, - hasTwoButtons = false, - confirmButton, - closeModal, -}: Props) => { +const Modal = ({ icon, contents, children, isOpen = false }: Props) => { if (!isOpen) return null const modalRoot = document.getElementById('modal-root') @@ -37,23 +26,10 @@ const Modal = ({ return createPortal( <> <div className={cx('overlay')}></div> - <div className={cx('modal', { 'has-two-buttons': hasTwoButtons })}> - <CloseIcon className={cx('close-icon')} onClick={closeModal}></CloseIcon> + <div className={cx('modal')}> <div className={cx('icon')}>{icon}</div> - <p className={cx('title')}>{contents}</p> - - {hasTwoButtons ? ( - <div className={cx('two-buttons')}> - <Button className={cx('confirm-button')} onClick={confirmButton}> - 예 - </Button> - <Button onClick={closeModal}>아니오</Button> - </div> - ) : ( - <Button className={cx('close-button')} onClick={closeModal}> - 닫기 - </Button> - )} + <p className={cx('contents')}>{contents}</p> + {children} </div> </>, modalRoot diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index b2a68c87..8965bb6b 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Meta, StoryObj } from '@storybook/react' -import { ModalAlertIcon } from 'public/icons/index' +import { ModalAlertIcon, ModalCheckIcon, ModalSubscribeIcon } from 'public/icons/index' import { Button } from '@/shared/ui/button' @@ -10,23 +10,14 @@ import Modal from './index' const meta: Meta<typeof Modal> = { title: 'Components/Modal', component: Modal, - tags: ['autodocs'], } export default meta type StoryType = StoryObj<typeof Modal> -const ModalStory = ({ - contents, - icon, - hasTwoButtons, -}: { - contents?: string - icon?: React.ReactNode - hasTwoButtons?: boolean -}) => { - const [isOpen, setIsOpen] = React.useState(false) +const ModalStory = ({ contents, icon }: { contents?: string; icon?: React.ReactNode }) => { + const [isOpen, setIsOpen] = React.useState(true) React.useEffect(() => { if (typeof window !== 'undefined' && !document.getElementById('modal-root')) { @@ -39,45 +30,25 @@ const ModalStory = ({ return ( <div style={{ padding: '20px' }}> <Button onClick={() => setIsOpen(true)}>모달 열기</Button> - <Modal - contents={contents} - icon={icon} - isOpen={isOpen} - hasTwoButtons={hasTwoButtons} - closeModal={() => setIsOpen(false)} - confirmButton={() => { - alert('확인') - setIsOpen(false) - }} - /> + <Modal contents={contents} icon={icon} isOpen={isOpen} /> </div> ) } export const Default: StoryType = { - render: () => <ModalStory contents="기본 모달 제목" />, + render: () => <ModalStory contents="기본모달" icon={<ModalAlertIcon />} />, } -export const TwoButtons: StoryType = { - render: () => <ModalStory contents="정말 삭제하시겠습니까?" hasTwoButtons={true} />, +export const AlertIcon: StoryType = { + render: () => <ModalStory contents="이것은 알림아이콘 모달입니다." icon={<ModalAlertIcon />} />, } -export const WithIcon: StoryType = { - render: () => <ModalStory contents="알림이 있습니다." icon={<ModalAlertIcon />} />, +export const CheckIcon: StoryType = { + render: () => <ModalStory contents="이것은 체크아이콘 모달입니다." icon={<ModalCheckIcon />} />, } -export const LongText: StoryType = { +export const SubscribeIcon: StoryType = { render: () => ( - <ModalStory contents="이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다. 이것은 긴 텍스트가 있는 모달입니다." /> - ), -} - -export const FullFeatured: StoryType = { - render: () => ( - <ModalStory - contents="모든 기능이 포함된 모달입니다. 확인해주세요." - icon={<ModalAlertIcon />} - hasTwoButtons={true} - /> + <ModalStory contents="이것은 구독아이콘 모달입니다." icon={<ModalSubscribeIcon />} /> ), } diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index 2c81b2a7..a8d3ca18 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -3,7 +3,7 @@ inset: 0; background-color: $color-black; opacity: 0.2; - z-index: 2; + z-index: zIndex(overlay); } .modal { @@ -14,56 +14,27 @@ background-color: $color-gray-100; border: 1px solid $color-gray-200; border-radius: 8px; - z-index: 3; - padding: 20px; + z-index: zIndex(modal); + padding: 80px 40px; width: 360px; - height: 280px; display: flex; flex-direction: column; align-items: center; - justify-content: space-between; - - &.has-two-buttons { - height: 240px; - } -} - -.close-icon { - position: absolute; - top: 12px; - right: 12px; - cursor: pointer; + justify-content: center; } .icon { display: flex; justify-content: center; align-items: center; - margin-top: 20px; + margin-bottom: 12px; } -.title { +.contents { @include typo-b1; font-weight: bold; text-align: center; white-space: pre-line; color: $color-gray-800; -} - -.two-buttons { - display: flex; - justify-content: center; - width: 100%; - gap: 18px; -} -.confirm-button { - border-color: $color-orange-500; - color: $color-orange-500; - cursor: pointer; - width: 90px; -} - -.close-button { - cursor: pointer; - align-self: center; + margin-bottom: 30px; } From 81bb44a349aa1497536736702820313cb19c54f8 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 05:10:42 +0900 Subject: [PATCH 253/648] =?UTF-8?q?feat:=20SVG=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/index.tsx | 1 + public/icons/register.svg | 15 +++++++++++++++ shared/styles/stories/icons.stories.tsx | 2 ++ 3 files changed, 18 insertions(+) create mode 100644 public/icons/register.svg diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 17294274..1cb76313 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -29,3 +29,4 @@ export { default as CloseIcon } from './close-icon.svg' export { default as ModalAlertIcon } from './modal-alert.svg' export { default as ModalSubscribeIcon } from './modal-subscribe.svg' export { default as ModalCheckIcon } from './modal-check.svg' +export { default as RegisterIcon } from './register.svg' diff --git a/public/icons/register.svg b/public/icons/register.svg new file mode 100644 index 00000000..2e9146a4 --- /dev/null +++ b/public/icons/register.svg @@ -0,0 +1,15 @@ +<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M32.806 20.2719C32.806 27.2281 27.1623 32.8719 20.206 32.8719C13.2498 32.8719 7.60602 27.2281 7.60602 20.2719C7.60602 13.3156 13.2498 7.67188 20.206 7.67188C27.1623 7.67188 32.806 13.3156 32.806 20.2719Z" fill="#6877FF"/> +<rect x="19.0826" y="14.8594" width="2.25" height="12" fill="white"/> +<rect x="14.2076" y="21.7969" width="1.875" height="12" transform="rotate(-90 14.2076 21.7969)" fill="white"/> +<g filter="url(#filter0_f_2006_13027)"> +<circle cx="20.2076" cy="20.2734" r="13.5" stroke="#6877FF"/> +</g> +<defs> +<filter id="filter0_f_2006_13027" x="2.20758" y="2.27344" width="36" height="36" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> +<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_2006_13027"/> +</filter> +</defs> +</svg> diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index 8b9ebdb1..8216e867 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -23,6 +23,7 @@ import { PencilIcon, ProfileIcon, QuestionIcon, + RegisterIcon, SearchIcon, SignOutIcon, StarIcon, @@ -69,6 +70,7 @@ const icons = [ { name: 'ModalAlertIcon', icon: ModalAlertIcon }, { name: 'ModalSubscribeIcon', icon: ModalSubscribeIcon }, { name: 'ModalCheckIcon', icon: ModalCheckIcon }, + { name: 'RegisterIcon', icon: RegisterIcon }, ] export const Icons: Story = { From fa309e01c1484ba2208b8c20651d278bdd49e76e Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 05:17:52 +0900 Subject: [PATCH 254/648] =?UTF-8?q?feat:=20Modal=20RegisterIcon=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#5?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/modal.stories.tsx | 11 ++++++++++- shared/ui/modal/styles.module.scss | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index 8965bb6b..44027d5b 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -1,7 +1,12 @@ import React from 'react' import { Meta, StoryObj } from '@storybook/react' -import { ModalAlertIcon, ModalCheckIcon, ModalSubscribeIcon } from 'public/icons/index' +import { + ModalAlertIcon, + ModalCheckIcon, + ModalSubscribeIcon, + RegisterIcon, +} from 'public/icons/index' import { Button } from '@/shared/ui/button' @@ -52,3 +57,7 @@ export const SubscribeIcon: StoryType = { <ModalStory contents="이것은 구독아이콘 모달입니다." icon={<ModalSubscribeIcon />} /> ), } + +export const PlusIcon: StoryType = { + render: () => <ModalStory contents="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, +} diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index a8d3ca18..e340b0d9 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -28,6 +28,11 @@ justify-content: center; align-items: center; margin-bottom: 12px; + + svg { + width: 40px; + flex-shrink: 0; + } } .contents { From 7c1c854410bd999e67128284a768a297478f86ef Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 05:33:29 +0900 Subject: [PATCH 255/648] =?UTF-8?q?feat:=20Modal=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=B6=94=EA=B0=80=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/modal-contents.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/constants/modal-contents.ts b/shared/constants/modal-contents.ts index 90304617..5a9d84a0 100644 --- a/shared/constants/modal-contents.ts +++ b/shared/constants/modal-contents.ts @@ -1,6 +1,11 @@ export const MODAL_CONTENTS = { SUBSCRIBE: '전략을 구독합니다. \n 구독한 전략은 나의 관심전략 \n 페이지에서 확인 가능합니다.', - VERIFICATIONCODE: '인증코드가 이메일로 발송되었습니다.\n 메일을 확인해주세요.', + VERIFICATION_CODE: '인증코드가 이메일로 발송되었습니다.\n 메일을 확인해주세요.', LOGIN: '로그인이 필요합니다.\n 로그인하시겠습니까?', DELETE: '리뷰를 삭제하겠습니까?', + CANCEL: '구독을 취소하시겠습니까?', + TRADE_UPDATE: '매매유형 수정', + TRADE_CREATE: '매매유형 등록', + ITEM_UPDATE: '종목 수정', + ITEM_CREATE: '종목 등록', } From 0dcc8f48eb13e44beab9833f6a146574b644d29f Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 06:30:31 +0900 Subject: [PATCH 256/648] =?UTF-8?q?feat:=20Modal=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9A=A9=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=ED=9B=85=20=EC=B6=94=EA=B0=80=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/hooks/custom/use-modal.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 shared/hooks/custom/use-modal.ts diff --git a/shared/hooks/custom/use-modal.ts b/shared/hooks/custom/use-modal.ts new file mode 100644 index 00000000..643f7e63 --- /dev/null +++ b/shared/hooks/custom/use-modal.ts @@ -0,0 +1,17 @@ +'use client' + +import { useState } from 'react' + +const useModal = () => { + const [isModalOpen, setIsModalOpen] = useState(false) + + const openModal = () => setIsModalOpen(true) + const closeModal = () => setIsModalOpen(false) + + return { + isModalOpen, + openModal, + closeModal, + } +} +export default useModal From 1ddfb8a8947f36ea7def4bd561da28517709b7c8 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 22 Nov 2024 11:00:22 +0900 Subject: [PATCH 257/648] =?UTF-8?q?rename:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=8E=81=EC=8A=A4=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=ED=8F=B4=EB=8D=94=EB=AA=85=20=EC=88=98=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_api/get-details-information.ts | 32 ++++++++++++++++++ .../_api/get-details-information/index.ts | 33 ------------------- .../index.ts => get-strategies.ts} | 17 +++++----- .../use-get-details-information-data.ts | 0 .../use-get-strategies-data.ts | 0 5 files changed, 40 insertions(+), 42 deletions(-) create mode 100644 app/(dashboard)/strategies/_api/get-details-information.ts delete mode 100644 app/(dashboard)/strategies/_api/get-details-information/index.ts rename app/(dashboard)/strategies/_api/{get-strategies/index.ts => get-strategies.ts} (63%) rename app/(dashboard)/strategies/_hooks/{_query => query}/use-get-details-information-data.ts (100%) rename app/(dashboard)/strategies/_hooks/{_query => query}/use-get-strategies-data.ts (100%) diff --git a/app/(dashboard)/strategies/_api/get-details-information.ts b/app/(dashboard)/strategies/_api/get-details-information.ts new file mode 100644 index 00000000..cd2af4a6 --- /dev/null +++ b/app/(dashboard)/strategies/_api/get-details-information.ts @@ -0,0 +1,32 @@ +import { InformationType } from '@/app/(dashboard)/strategies/[strategyId]/page' +import axios from 'axios' + +const getDetailsInformation = async (isReady: boolean, strategyId: string) => { + if (!isReady || !strategyId) return + + try { + const response = await axios.get(`/api/strategies/${strategyId}`) + + const data = await response.data + const newDetailsData: InformationType[] = [ + { title: '트레이더', data: data.nickname }, + { title: '최소 투자 금액', data: data.minimumInvestmentAmount }, + { title: '투자 원금', data: data.initialInvestment }, + + [ + { title: 'KP Ratio', data: data.kpRatio }, + { title: 'SM SCORE', data: data.smScore }, + ], + + [ + { title: '최종손익입력일자', data: data.finalProfitLossDate }, + { title: '등록일', data: data.createdAt }, + ], + ] + return newDetailsData + } catch (err) { + console.error(err) + } +} + +export default getDetailsInformation diff --git a/app/(dashboard)/strategies/_api/get-details-information/index.ts b/app/(dashboard)/strategies/_api/get-details-information/index.ts deleted file mode 100644 index 25bb4461..00000000 --- a/app/(dashboard)/strategies/_api/get-details-information/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { InformationType } from '@/app/(dashboard)/strategies/[strategyId]/page' -import axios from 'axios' - -const getDetailsInformation = async (isReady: boolean, strategyId: string) => { - if (!isReady || !strategyId) return - - try { - const response = await axios.get(`/api/strategies/${strategyId}`) - if (response) { - const data = await response.data - const newDetailsData: InformationType[] = [ - { title: '트레이더', data: data.nickname }, - { title: '최소 투자 금액', data: data.minimumInvestmentAmount }, - { title: '투자 원금', data: data.initialInvestment }, - - [ - { title: 'KP Ratio', data: data.kpRatio }, - { title: 'SM SCORE', data: data.smScore }, - ], - - [ - { title: '최종손익입력일자', data: data.finalProfitLossDate }, - { title: '등록일', data: data.createdAt }, - ], - ] - return newDetailsData - } - } catch (error) { - console.error(error) - } -} - -export default getDetailsInformation diff --git a/app/(dashboard)/strategies/_api/get-strategies/index.ts b/app/(dashboard)/strategies/_api/get-strategies.ts similarity index 63% rename from app/(dashboard)/strategies/_api/get-strategies/index.ts rename to app/(dashboard)/strategies/_api/get-strategies.ts index 0e4b5ea9..c34c1b70 100644 --- a/app/(dashboard)/strategies/_api/get-strategies/index.ts +++ b/app/(dashboard)/strategies/_api/get-strategies.ts @@ -8,19 +8,18 @@ const getStrategiesData = async ( size: number ): Promise<{ strategiesData: StrategiesModel[]; totalCount: number } | undefined> => { if (!isReady) return { strategiesData: [], totalCount: 0 } + try { const response = await axios.get(`/api/strategies?page=${page}&size=${size}`) - if (response) { - const { - strategiesData, - totalCount, - }: { strategiesData: StrategiesModel[]; totalCount: number } = await response.data + const { + strategiesData, + totalCount, + }: { strategiesData: StrategiesModel[]; totalCount: number } = await response.data - return { strategiesData, totalCount } - } - } catch (error) { - console.error(error) + return { strategiesData, totalCount } + } catch (err) { + console.error(err) } } diff --git a/app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data.ts b/app/(dashboard)/strategies/_hooks/query/use-get-details-information-data.ts similarity index 100% rename from app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data.ts rename to app/(dashboard)/strategies/_hooks/query/use-get-details-information-data.ts diff --git a/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data.ts b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts similarity index 100% rename from app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data.ts rename to app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts From 7586c511eafa6a7ef50efd4ded203c4725fdaff3 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 22 Nov 2024 11:02:33 +0900 Subject: [PATCH 258/648] =?UTF-8?q?feat:=20API=EB=AA=85=EC=84=B8=EC=84=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20?= =?UTF-8?q?=EB=AA=A9=EB=8D=B0=EC=9D=B4=ED=84=B0,=20=ED=83=80=EC=9E=85,=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8,=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/area-chart.tsx | 15 +- .../strategies-item.stories.tsx | 54 +- mocks/handlers/strategies.ts | 507 +++++++++++------- shared/types/strategy-details-data.ts | 8 +- 4 files changed, 364 insertions(+), 220 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/_ui/strategies-item/area-chart.tsx index 3c646590..6c61330f 100644 --- a/app/(dashboard)/_ui/strategies-item/area-chart.tsx +++ b/app/(dashboard)/_ui/strategies-item/area-chart.tsx @@ -16,11 +16,10 @@ const HighchartsReact = dynamic(() => import('highcharts-react-official'), { }) interface Props { - profitRateChartData: ProfitRateChartDataModel[] + profitRateChartData: ProfitRateChartDataModel } -const AreaChart = ({ profitRateChartData }: Props) => { - const profit = profitRateChartData.map((data) => data.profitRate) +const AreaChart = ({ profitRateChartData: data }: Props) => { const chartOptions: Highcharts.Options = { chart: { type: 'areaspline', @@ -34,8 +33,8 @@ const AreaChart = ({ profitRateChartData }: Props) => { }, yAxis: { visible: false, - min: Math.min(...profit), - max: Math.max(...profit), + min: Math.min(...data.yAxis), + max: Math.max(...data.yAxis), }, legend: { enabled: false }, plotOptions: { @@ -57,9 +56,9 @@ const AreaChart = ({ profitRateChartData }: Props) => { series: [ { type: 'areaspline', - data: profitRateChartData.map((data) => ({ - x: new Date(data.date).getTime(), - y: data.profitRate, + data: data.xAxis.map((x, idx) => ({ + x: new Date(x).getTime(), + y: data.yAxis[idx], })), color: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, diff --git a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx index fc65ad81..33d9f7d2 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx @@ -1,8 +1,9 @@ -import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import type { Meta, StoryFn } from '@storybook/react' import { StrategiesModel } from '@/shared/types/strategy-details-data' +import StrategiesItem from './index' + const meta: Meta<typeof StrategiesItem> = { title: 'components/StrategiesItem', component: StrategiesItem, @@ -25,15 +26,23 @@ Primary.args = { strategyName: '리치테크 FuturesDay', nickname: 'MACS', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 5.2 }, - { date: '2024-02-01', profitRate: 6.4 }, - { date: '2024-03-01', profitRate: 12.8 }, - { date: '2024-04-01', profitRate: 8.2 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-20,580,856', smScore: 60.6, cumulativeProfitLossRate: 120.1, @@ -48,18 +57,23 @@ Primary.args = { strategyName: 'ETF 레버리지/인버', nickname: '수밍', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2023-12-01', profitRate: 7.2 }, - { date: '2024-01-01', profitRate: 5.2 }, - { date: '2024-02-01', profitRate: 25 }, - { date: '2024-03-01', profitRate: 12.8 }, - { date: '2024-04-01', profitRate: 17.2 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-20,580,856', smScore: 60.6, cumulativeProfitLossRate: 120.1, diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts index d9e4f2c9..a6a7d314 100644 --- a/mocks/handlers/strategies.ts +++ b/mocks/handlers/strategies.ts @@ -6,15 +6,23 @@ const strategiesData = [ strategyName: 'Dynamic ETF 전략', nickname: 'AlphaTrader', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 5.2 }, - { date: '2024-02-01', profitRate: 6.4 }, - { date: '2024-03-01', profitRate: 12.8 }, - { date: '2024-04-01', profitRate: 8.2 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-15,432,567', smScore: 72.1, cumulativeProfitLossRate: 140.5, @@ -29,18 +37,23 @@ const strategiesData = [ strategyName: '고수익 ETF', nickname: 'BetaTrader', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2023-12-01', profitRate: 7.2 }, - { date: '2024-01-01', profitRate: 5.2 }, - { date: '2024-02-01', profitRate: 25 }, - { date: '2024-03-01', profitRate: 12.8 }, - { date: '2024-04-01', profitRate: 17.2 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-12,786,543', smScore: 65.4, cumulativeProfitLossRate: 125.3, @@ -55,15 +68,23 @@ const strategiesData = [ strategyName: 'Futures Pro', nickname: 'Gamma', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 10.2 }, - { date: '2024-02-01', profitRate: 11.5 }, - { date: '2024-03-01', profitRate: 15.6 }, - { date: '2024-04-01', profitRate: 18.3 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-18,234,678', smScore: 79.6, cumulativeProfitLossRate: 160.4, @@ -78,17 +99,22 @@ const strategiesData = [ strategyName: '월별 수익 전략', nickname: 'TraderX', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 6.3 }, - { date: '2024-02-01', profitRate: 7.8 }, - { date: '2024-03-01', profitRate: 9.4 }, - { date: '2024-04-01', profitRate: 14.6 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-11,456,789', smScore: 68.4, cumulativeProfitLossRate: 134.5, @@ -103,15 +129,23 @@ const strategiesData = [ strategyName: '리스크 관리 전략', nickname: 'DeltaOne', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 4.1 }, - { date: '2024-02-01', profitRate: 5.2 }, - { date: '2024-03-01', profitRate: 7.3 }, - { date: '2024-04-01', profitRate: 9.5 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-14,345,678', smScore: 62.3, cumulativeProfitLossRate: 120.3, @@ -126,17 +160,23 @@ const strategiesData = [ strategyName: 'Active LongShort', nickname: 'Hedger', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 9.8 }, - { date: '2024-02-01', profitRate: 12.1 }, - { date: '2024-03-01', profitRate: 14.3 }, - { date: '2024-04-01', profitRate: 16.7 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-17,890,234', smScore: 80.2, cumulativeProfitLossRate: 175.4, @@ -151,17 +191,23 @@ const strategiesData = [ strategyName: '안정적 성장 전략', nickname: 'SafeGrow', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 4.8 }, - { date: '2024-02-01', profitRate: 6.3 }, - { date: '2024-03-01', profitRate: 8.9 }, - { date: '2024-04-01', profitRate: 10.2 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-13,567,890', smScore: 70.1, cumulativeProfitLossRate: 138.2, @@ -176,17 +222,23 @@ const strategiesData = [ strategyName: 'High Risk High Return', nickname: 'RiskyTrader', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 15.3 }, - { date: '2024-02-01', profitRate: 18.2 }, - { date: '2024-03-01', profitRate: 22.8 }, - { date: '2024-04-01', profitRate: 26.4 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-25,678,345', smScore: 85.7, cumulativeProfitLossRate: 200.5, @@ -201,17 +253,23 @@ const strategiesData = [ strategyName: 'ETF 분산 투자', nickname: 'Diversifier', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 3.2 }, - { date: '2024-02-01', profitRate: 4.5 }, - { date: '2024-03-01', profitRate: 6.1 }, - { date: '2024-04-01', profitRate: 7.8 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-9,876,543', smScore: 58.3, cumulativeProfitLossRate: 112.4, @@ -226,17 +284,23 @@ const strategiesData = [ strategyName: 'Momentum 전략', nickname: 'MomentumKing', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 10.5 }, - { date: '2024-02-01', profitRate: 12.7 }, - { date: '2024-03-01', profitRate: 15.9 }, - { date: '2024-04-01', profitRate: 18.4 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-18,456,789', smScore: 75.6, cumulativeProfitLossRate: 165.3, @@ -251,17 +315,23 @@ const strategiesData = [ strategyName: '소형주 집중 전략', nickname: 'SmallCapPro', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 7.5 }, - { date: '2024-02-01', profitRate: 8.9 }, - { date: '2024-03-01', profitRate: 10.3 }, - { date: '2024-04-01', profitRate: 12.7 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-14,345,678', smScore: 69.4, cumulativeProfitLossRate: 130.7, @@ -276,17 +346,23 @@ const strategiesData = [ strategyName: 'Active Bond 전략', nickname: 'BondExpert', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 5.2 }, - { date: '2024-02-01', profitRate: 6.3 }, - { date: '2024-03-01', profitRate: 7.8 }, - { date: '2024-04-01', profitRate: 9.4 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-10,567,890', smScore: 64.3, cumulativeProfitLossRate: 125.8, @@ -301,17 +377,23 @@ const strategiesData = [ strategyName: 'Smart Index 전략', nickname: 'IndexGuru', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 6.2 }, - { date: '2024-02-01', profitRate: 8.3 }, - { date: '2024-03-01', profitRate: 10.4 }, - { date: '2024-04-01', profitRate: 13.5 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-16,543,678', smScore: 71.8, cumulativeProfitLossRate: 145.6, @@ -326,17 +408,23 @@ const strategiesData = [ strategyName: 'MACD Pro', nickname: 'TrendRider', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 8.3 }, - { date: '2024-02-01', profitRate: 10.7 }, - { date: '2024-03-01', profitRate: 13.6 }, - { date: '2024-04-01', profitRate: 17.2 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-19,567,890', smScore: 77.5, cumulativeProfitLossRate: 175.8, @@ -351,17 +439,23 @@ const strategiesData = [ strategyName: '균형 성장 전략', nickname: 'BalancedGrow', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 5.6 }, - { date: '2024-02-01', profitRate: 6.8 }, - { date: '2024-03-01', profitRate: 8.2 }, - { date: '2024-04-01', profitRate: 9.9 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-12,567,890', smScore: 62.8, cumulativeProfitLossRate: 118.5, @@ -376,15 +470,23 @@ const strategiesData = [ strategyName: '단기 스윙 전략', nickname: 'SwingPro', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 11.5 }, - { date: '2024-02-01', profitRate: 14.8 }, - { date: '2024-03-01', profitRate: 18.2 }, - { date: '2024-04-01', profitRate: 20.9 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-22,345,678', smScore: 80.3, cumulativeProfitLossRate: 190.7, @@ -399,15 +501,23 @@ const strategiesData = [ strategyName: '로우볼 전략', nickname: 'LowVolKing', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 3.4 }, - { date: '2024-02-01', profitRate: 4.7 }, - { date: '2024-03-01', profitRate: 5.8 }, - { date: '2024-04-01', profitRate: 7.1 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-8,345,678', smScore: 55.9, cumulativeProfitLossRate: 102.4, @@ -422,15 +532,23 @@ const strategiesData = [ strategyName: '인컴 중심 전략', nickname: 'IncomeMaster', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 6.7 }, - { date: '2024-02-01', profitRate: 7.9 }, - { date: '2024-03-01', profitRate: 9.3 }, - { date: '2024-04-01', profitRate: 10.8 }, - { date: '2024-05-01', profitRate: 9.4 }, - { date: '2024-06-01', profitRate: 15.8 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-10,678,345', smScore: 60.2, cumulativeProfitLossRate: 115.9, @@ -445,16 +563,21 @@ const strategiesData = [ strategyName: '글로벌 주식 전략', nickname: 'GlobalTrader', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 8.5 }, - { date: '2024-02-01', profitRate: 10.6 }, - { date: '2024-03-01', profitRate: 12.7 }, - { date: '2024-04-01', profitRate: 15.4 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', mdd: '-17,890,567', smScore: 74.2, @@ -470,17 +593,23 @@ const strategiesData = [ strategyName: '테크 성장 전략', nickname: 'TechGrow', stockTypeIconUrl: [], - profitRateChartData: [ - { date: '2024-01-01', profitRate: 13.7 }, - { date: '2024-02-01', profitRate: 16.8 }, - { date: '2024-03-01', profitRate: 20.2 }, - { date: '2024-04-01', profitRate: 23.5 }, - { date: '2024-05-01', profitRate: 11.4 }, - { date: '2024-06-01', profitRate: 20 }, - { date: '2024-07-01', profitRate: 16 }, - { date: '2024-08-01', profitRate: 18 }, - ], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, tradeTypeIconUrl: '', + tradeTypeName: '', mdd: '-23,567,890', smScore: 88.3, cumulativeProfitLossRate: 210.9, diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts index 8a59fef0..4a8ffe89 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-details-data.ts @@ -20,8 +20,8 @@ export interface MonthlyAnalysisModel { } export interface ProfitRateChartDataModel { - date: string - profitRate: number + xAxis: string[] + yAxis: number[] } export interface StrategiesModel { @@ -30,8 +30,10 @@ export interface StrategiesModel { nickname: string traderImage?: string stockTypeIconUrl: string[] - profitRateChartData: ProfitRateChartDataModel[] + stockTypeNames: string[] + profitRateChartData: ProfitRateChartDataModel tradeTypeIconUrl: string + tradeTypeName: string mdd: string smScore: number cumulativeProfitLossRate: number From a10e9f1be7982a891f125b5157a9b469cb5e71cb Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 22 Nov 2024 11:03:02 +0900 Subject: [PATCH 259/648] =?UTF-8?q?fix:=20=EC=83=81=EB=8C=80=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/strategies-item/index.tsx | 6 +++--- .../_ui/strategies-item/strategies-summary.tsx | 2 +- app/(dashboard)/strategies/[strategyId]/page.tsx | 15 ++++++--------- .../strategies/_ui/strategy-list/index.tsx | 10 ++++------ app/(dashboard)/strategies/page.tsx | 4 ++-- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 09a00900..7e305161 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -1,13 +1,13 @@ import Link from 'next/link' -import AreaChart from '@/app/(dashboard)/_ui/strategies-item/area-chart' -import StrategiesSummary from '@/app/(dashboard)/_ui/strategies-item/strategies-summary' -import Subscribe from '@/app/(dashboard)/_ui/strategies-item/subscribe' import classNames from 'classnames/bind' import { StrategiesModel } from '@/shared/types/strategy-details-data' +import AreaChart from './area-chart' +import StrategiesSummary from './strategies-summary' import styles from './styles.module.scss' +import Subscribe from './subscribe' const cx = classNames.bind(styles) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-summary.tsx b/app/(dashboard)/_ui/strategies-item/strategies-summary.tsx index 18389d84..51b42cd1 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-summary.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-summary.tsx @@ -1,9 +1,9 @@ -import StrategiesIcon from '@/app/(dashboard)/_ui/strategies-item/strategies-icon' import classNames from 'classnames/bind' import Avatar from '@/shared/ui/avatar' import TotalStar from '@/shared/ui/total-star' +import StrategiesIcon from './strategies-icon' import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 3d39f99f..b878a13e 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,16 +1,13 @@ 'use client' -import DetailsSideItem, { - InformationModel, - TitleType, -} from '@/app/(dashboard)/strategies/[strategyId]/_ui/details-side-item' -import useGetDetailsInformationData from '@/app/(dashboard)/strategies/_hooks/_query/use-get-details-information-data' -import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' - import { useMSWStore } from '@/shared/stores/msw' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' +import useGetDetailsInformationData from '../_hooks/query/use-get-details-information-data' +import SideContainer from '../_ui/side-container' +import DetailsSideItem, { InformationModel, TitleType } from './_ui/details-side-item' + export type InformationType = { title: TitleType; data: string | number } | InformationModel[] const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { @@ -30,8 +27,8 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { <Title label={'전략 상세보기'} /> <SideContainer> {hasDetailsSideData?.[0] && - detailsSideData?.map((data, idx) => ( - <div key={idx}> + detailsSideData?.map((data) => ( + <div key={data.toString()}> <DetailsSideItem information={data} /> </div> ))} diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index a3867354..bbb382c7 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -2,14 +2,14 @@ import { useRouter, useSearchParams } from 'next/navigation' -import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' -import useGetStrategiesData from '@/app/(dashboard)/strategies/_hooks/_query/use-get-strategies-data' -import ListHeader from '@/app/(dashboard)/strategies/_ui/list-header' import classNames from 'classnames/bind' import { useMSWStore } from '@/shared/stores/msw' import Pagination from '@/shared/ui/pagination' +import StrategiesItem from '../../../../(dashboard)/_ui/strategies-item' +import useGetStrategiesData from '../../_hooks/query/use-get-strategies-data' +import ListHeader from '../list-header' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -28,9 +28,7 @@ const StrategyList = () => { const totalCount = data?.totalCount || 0 const handlePageChange = (page: number) => { - if (typeof window !== 'undefined') { - router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`) - } + router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`) } return ( <> diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index 94e82ea3..d8d768c7 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -1,11 +1,11 @@ import { Suspense } from 'react' -import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' -import StrategyList from '@/app/(dashboard)/strategies/_ui/strategy-list' import classNames from 'classnames/bind' import Title from '@/shared/ui/title' +import SideContainer from './_ui/side-container' +import StrategyList from './_ui/strategy-list' import styles from './page.module.scss' const cx = classNames.bind(styles) From b57559a85d0b680f37cd1e63f0a693e080d677f1 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 22 Nov 2024 11:19:19 +0900 Subject: [PATCH 260/648] =?UTF-8?q?feat:=20fetch=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20(#8?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_api/get-details-information.ts | 5 ++++- app/(dashboard)/strategies/_api/get-strategies.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/strategies/_api/get-details-information.ts b/app/(dashboard)/strategies/_api/get-details-information.ts index cd2af4a6..2b6d6ee8 100644 --- a/app/(dashboard)/strategies/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/_api/get-details-information.ts @@ -6,7 +6,10 @@ const getDetailsInformation = async (isReady: boolean, strategyId: string) => { try { const response = await axios.get(`/api/strategies/${strategyId}`) - + if (!response.data) { + console.error('전략 상세 데이터 가져오기 실패') + return + } const data = await response.data const newDetailsData: InformationType[] = [ { title: '트레이더', data: data.nickname }, diff --git a/app/(dashboard)/strategies/_api/get-strategies.ts b/app/(dashboard)/strategies/_api/get-strategies.ts index c34c1b70..66dde4b0 100644 --- a/app/(dashboard)/strategies/_api/get-strategies.ts +++ b/app/(dashboard)/strategies/_api/get-strategies.ts @@ -11,7 +11,10 @@ const getStrategiesData = async ( try { const response = await axios.get(`/api/strategies?page=${page}&size=${size}`) - + if (!response.data) { + console.error('전략 목록 데이터 가져오기 실패') + return { strategiesData: [], totalCount: 0 } + } const { strategiesData, totalCount, From 7ad8c7c9e36ecdffd0007b2f93d0e0212207f85c Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 22 Nov 2024 16:07:20 +0900 Subject: [PATCH 261/648] =?UTF-8?q?feat:=20=EB=B6=84=EC=84=9D=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8,=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-chart.stories.tsx | 54 +++++++ .../_ui/analysis-container/analysis-chart.tsx | 148 ++++++++++++++++++ .../_ui/analysis-container/index.tsx | 5 + .../_ui/analysis-container/styles.module.scss | 4 + .../_ui/analysis-container/yaxis-options.ts | 19 +++ 5 files changed, 230 insertions(+) create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.stories.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/yaxis-options.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.stories.tsx new file mode 100644 index 00000000..0a648cb4 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.stories.tsx @@ -0,0 +1,54 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import AnalysisChart from './analysis-chart' + +const meta = { + title: 'Components/AnalysisChart', + component: AnalysisChart, + tags: ['autodocs'], +} satisfies Meta<typeof AnalysisChart> + +type StoryType = StoryObj<typeof meta> + +export const Default: StoryType = { + decorators: [ + (Story) => { + return ( + <div style={{ width: '900px' }}> + <Story /> + </div> + ) + }, + ], + args: { + analysisChartData: { + xAxis: ['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04', '2023-01-05', '2023-01-06'], + yAxis: { + CURRENT_DRAWDOWN: [2000, 5660, 4000, 9000, 7000, 10000], + PRINCIPAL: [50000, 60000, 80000, 80000, 80000, 80000], + }, + }, + }, +} + +export const SameOption: StoryType = { + decorators: [ + (Story) => { + return ( + <div style={{ width: '900px' }}> + <Story /> + </div> + ) + }, + ], + args: { + analysisChartData: { + xAxis: [...Default.args.analysisChartData.xAxis], + yAxis: { + CURRENT_DRAWDOWN: [2000, 5660, 4000, 9000, 7000, 10000], + }, + }, + }, +} + +export default meta diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx new file mode 100644 index 00000000..b7db6494 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx @@ -0,0 +1,148 @@ +'use client' + +import dynamic from 'next/dynamic' + +import classNames from 'classnames/bind' +import Highcharts, { SeriesOptionsType } from 'highcharts' + +import styles from './styles.module.scss' +import { YAXIS_OPTIONS } from './yaxis-options' + +const HighchartsReact = dynamic(() => import('highcharts-react-official'), { + ssr: false, +}) + +const cx = classNames.bind(styles) + +type YAxisType = keyof typeof YAXIS_OPTIONS + +interface AnalysisChartDataModel { + xAxis: string[] + yAxis: { + [key in YAxisType]?: number[] + } +} + +interface Props { + analysisChartData: AnalysisChartDataModel +} + +const AnalysisChart = ({ analysisChartData: data }: Props) => { + const getOptionName = (sequence: number) => { + const key = Object.keys(data.yAxis)[sequence] as YAxisType | undefined + return key ? YAXIS_OPTIONS[key] : '' + } + + const chartOptions: Highcharts.Options = { + chart: { + type: 'areaspline', + height: 367, + backgroundColor: 'transparent', + margin: [0, 0, 0, 0], + }, + title: { text: undefined }, + xAxis: { + visible: false, + categories: data.xAxis, + }, + yAxis: [{ visible: false }, { visible: false }], + legend: { + enabled: true, + align: 'left', + verticalAlign: 'top', + layout: 'vertical', + x: 10, + y: 10, + itemStyle: { + color: '#4D4D4D', + fontSize: '12px', + }, + backgroundColor: '#FFFFFF', + borderColor: '#A7A7A7', + borderRadius: 4, + borderWidth: 1, + padding: 5, + }, + tooltip: { + useHTML: true, + headerFormat: '<div style="margin-bottom: 5px;">{point.key}</div>', + pointFormat: '<b>{point.y:.2f}</b>', + footerFormat: '', + borderColor: '#ECECEC', + borderWidth: 1, + shadow: false, + backgroundColor: '#FFFFFF', + style: { + padding: '10px', + }, + }, + plotOptions: { + areaspline: { + fillOpacity: 0.5, + lineWidth: 2, + marker: { + enabled: false, + }, + fillColor: { + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, + stops: [ + [0, '#FF4F1F'], + [1, '#FFFFFF'], + ], + }, + }, + spline: { + lineWidth: 2, + marker: { + enabled: false, + }, + }, + }, + series: [ + { + type: 'areaspline', + name: getOptionName(0), + data: Object.values(data.yAxis)[0], + color: '#FF4F1F', + yAxis: 0, + stickyTracking: false, + pointPlacement: 'on', + }, + ...(Object.values(data.yAxis)[1] + ? [ + { + type: 'spline', + name: getOptionName(1), + data: Object.values(data.yAxis)[1], + color: '#6877FF', + yAxis: 1, + stickyTracking: false, + pointPlacement: 'on', + }, + ] + : []), + ] as SeriesOptionsType[], + responsive: { + rules: [ + { + condition: { + maxWidth: 960, + }, + chartOptions: { + chart: { + width: null, + }, + }, + }, + ], + }, + credits: { enabled: false }, + } + return ( + <div className={cx('chart')}> + <HighchartsReact highcharts={Highcharts} options={chartOptions} /> + </div> + ) +} + +export default AnalysisChart diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx new file mode 100644 index 00000000..d4ca87ef --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx @@ -0,0 +1,5 @@ +const AnalysisContainer = () => { + return <></> +} + +export default AnalysisContainer diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss new file mode 100644 index 00000000..9f81ee3a --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss @@ -0,0 +1,4 @@ +.chart { + width: 100%; + overflow: hidden; +} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/yaxis-options.ts b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/yaxis-options.ts new file mode 100644 index 00000000..a9be1ddf --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/yaxis-options.ts @@ -0,0 +1,19 @@ +export const YAXIS_OPTIONS = { + BALANCE: '잔고', + PRINCIPAL: '원금', + CUMULATIVE_TRANSACTION_AMOUNT: '누적 입출 금액', + TRANSACTION: '일별 입출 금액', + DAILY_PROFIT_LOSS: '일 손익 금액', + DAILY_PROFIT_LOSS_RATE: '일 손익률', + CUMULATIVE_PROFIT_LOSS: '누적 수익 금액', + CUMULATIVE_PROFIT_LOSS_RATE: '누적 수익률', + CURRENT_DRAWDOWN: '현재 자본 인하 금액', + CURRENT_DRAWDOWN_RATE: '현재 자본 인하율', + AVERAGE_PROFIT_LOSS: '평균 손익 금액', + AVERAGE_PROFIT_LOSS_RATIO: '평균 손익률', + WIN_RATE: '승률', + PROFIT_FACTOR: 'Profit Factor', + ROA: 'ROA', + TOTAL_PROFIT: '총 이익', + TOTAL_LOSS: '총 손실', +} as const From a1c026fc453cb38a809a53d117a556b34c5db4f8 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 16:19:14 +0900 Subject: [PATCH 262/648] =?UTF-8?q?feat:=20Modal=20=EB=AC=B8=EC=9D=98=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/modal-contents.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/constants/modal-contents.ts b/shared/constants/modal-contents.ts index 5a9d84a0..3cc22cda 100644 --- a/shared/constants/modal-contents.ts +++ b/shared/constants/modal-contents.ts @@ -8,4 +8,5 @@ export const MODAL_CONTENTS = { TRADE_CREATE: '매매유형 등록', ITEM_UPDATE: '종목 수정', ITEM_CREATE: '종목 등록', + INQUIRY: '문의가 등록되었습니다.\n 문의 등록 내용 및 답변은\n 문의 페이지에서 확인 가능합니다.', } From 1f025142f8fb8c0792030ebb9085f34cac730c5a Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 22 Nov 2024 17:30:39 +0900 Subject: [PATCH 263/648] =?UTF-8?q?feat:=20close=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20Modal=20contents?= =?UTF-8?q?=EB=A5=BC=20messages=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/close-icon.svg | 4 ---- public/icons/index.tsx | 1 - .../{modal-contents.ts => modal-messages.ts} | 4 ++-- shared/styles/stories/icons.stories.tsx | 1 - shared/ui/modal/index.tsx | 6 +++--- shared/ui/modal/modal.stories.tsx | 14 +++++++------- shared/ui/modal/styles.module.scss | 2 +- 7 files changed, 13 insertions(+), 19 deletions(-) delete mode 100644 public/icons/close-icon.svg rename shared/constants/{modal-contents.ts => modal-messages.ts} (85%) diff --git a/public/icons/close-icon.svg b/public/icons/close-icon.svg deleted file mode 100644 index 08036833..00000000 --- a/public/icons/close-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="25" height="25" viewBox="0 0 25 25" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6433 20.7327L3.8125 5.73511L5.18799 4.2832L21.0187 19.2808L19.6433 20.7327Z" fill="#797979"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M5.35674 20.7327L21.1875 5.73511L19.812 4.2832L3.98125 19.2808L5.35674 20.7327Z" fill="#797979"/> -</svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 2363e02d..81ca443e 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -26,7 +26,6 @@ export { default as StrategyRankingIcon } from './strategy-ranking.svg' export { default as StrategyIcon } from './strategy.svg' export { default as TradersIcon } from './traders.svg' export { default as StarIcon } from './star.svg' -export { default as CloseIcon } from './close-icon.svg' export { default as ModalAlertIcon } from './modal-alert.svg' export { default as ModalSubscribeIcon } from './modal-subscribe.svg' export { default as ModalCheckIcon } from './modal-check.svg' diff --git a/shared/constants/modal-contents.ts b/shared/constants/modal-messages.ts similarity index 85% rename from shared/constants/modal-contents.ts rename to shared/constants/modal-messages.ts index 3cc22cda..6dcc6542 100644 --- a/shared/constants/modal-contents.ts +++ b/shared/constants/modal-messages.ts @@ -1,7 +1,7 @@ -export const MODAL_CONTENTS = { +export const MODAL_MESSAGE = { SUBSCRIBE: '전략을 구독합니다. \n 구독한 전략은 나의 관심전략 \n 페이지에서 확인 가능합니다.', VERIFICATION_CODE: '인증코드가 이메일로 발송되었습니다.\n 메일을 확인해주세요.', - LOGIN: '로그인이 필요합니다.\n 로그인하시겠습니까?', + SIGNIN: '로그인이 필요합니다.\n 로그인하시겠습니까?', DELETE: '리뷰를 삭제하겠습니까?', CANCEL: '구독을 취소하시겠습니까?', TRADE_UPDATE: '매매유형 수정', diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index c5657e23..4e616abf 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -67,7 +67,6 @@ const icons = [ { name: 'StrategyIcon', icon: StrategyIcon }, { name: 'TradersIcon', icon: TradersIcon }, { name: 'StarIcon', icon: StarIcon }, - { name: 'CloseIcon', icon: CloseIcon }, { name: 'ModalAlertIcon', icon: ModalAlertIcon }, { name: 'ModalSubscribeIcon', icon: ModalSubscribeIcon }, { name: 'ModalCheckIcon', icon: ModalCheckIcon }, diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index 1df56efa..26ecce63 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -11,12 +11,12 @@ const cx = classNames.bind(styles) interface Props { icon?: ReactNode - contents?: string + message?: string children?: React.ReactNode isOpen: boolean } -const Modal = ({ icon, contents, children, isOpen = false }: Props) => { +const Modal = ({ icon, message, children, isOpen = false }: Props) => { if (!isOpen) return null const modalRoot = document.getElementById('modal-root') @@ -28,7 +28,7 @@ const Modal = ({ icon, contents, children, isOpen = false }: Props) => { <div className={cx('overlay')}></div> <div className={cx('modal')}> <div className={cx('icon')}>{icon}</div> - <p className={cx('contents')}>{contents}</p> + <p className={cx('contents')}>{message}</p> {children} </div> </>, diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index 44027d5b..6c043756 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -21,7 +21,7 @@ export default meta type StoryType = StoryObj<typeof Modal> -const ModalStory = ({ contents, icon }: { contents?: string; icon?: React.ReactNode }) => { +const ModalStory = ({ message, icon }: { message?: string; icon?: React.ReactNode }) => { const [isOpen, setIsOpen] = React.useState(true) React.useEffect(() => { @@ -35,29 +35,29 @@ const ModalStory = ({ contents, icon }: { contents?: string; icon?: React.ReactN return ( <div style={{ padding: '20px' }}> <Button onClick={() => setIsOpen(true)}>모달 열기</Button> - <Modal contents={contents} icon={icon} isOpen={isOpen} /> + <Modal message={message} icon={icon} isOpen={isOpen} /> </div> ) } export const Default: StoryType = { - render: () => <ModalStory contents="기본모달" icon={<ModalAlertIcon />} />, + render: () => <ModalStory message="기본모달" icon={<ModalAlertIcon />} />, } export const AlertIcon: StoryType = { - render: () => <ModalStory contents="이것은 알림아이콘 모달입니다." icon={<ModalAlertIcon />} />, + render: () => <ModalStory message="이것은 알림아이콘 모달입니다." icon={<ModalAlertIcon />} />, } export const CheckIcon: StoryType = { - render: () => <ModalStory contents="이것은 체크아이콘 모달입니다." icon={<ModalCheckIcon />} />, + render: () => <ModalStory message="이것은 체크아이콘 모달입니다." icon={<ModalCheckIcon />} />, } export const SubscribeIcon: StoryType = { render: () => ( - <ModalStory contents="이것은 구독아이콘 모달입니다." icon={<ModalSubscribeIcon />} /> + <ModalStory message="이것은 구독아이콘 모달입니다." icon={<ModalSubscribeIcon />} /> ), } export const PlusIcon: StoryType = { - render: () => <ModalStory contents="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, + render: () => <ModalStory message="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, } diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index e340b0d9..007089c4 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -35,7 +35,7 @@ } } -.contents { +.message { @include typo-b1; font-weight: bold; text-align: center; From 3d8267b50ae08de5262fcb5fb4bce7aa51385212 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 23 Nov 2024 12:24:15 +0900 Subject: [PATCH 264/648] =?UTF-8?q?feat:=20TopStrategyCard=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_ui/line-chart/index.tsx | 2 +- .../(home)/_ui/top-strategy-card/index.tsx | 102 ++++++++++++ .../_ui/top-strategy-card/styles.module.scss | 154 ++++++++++++++++++ .../(home)/_ui/top-strategy-card/types.ts | 22 +++ 4 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 app/(landing)/(home)/_ui/top-strategy-card/index.tsx create mode 100644 app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss create mode 100644 app/(landing)/(home)/_ui/top-strategy-card/types.ts diff --git a/app/(landing)/(home)/_ui/line-chart/index.tsx b/app/(landing)/(home)/_ui/line-chart/index.tsx index 7740671c..ec7b2f3f 100644 --- a/app/(landing)/(home)/_ui/line-chart/index.tsx +++ b/app/(landing)/(home)/_ui/line-chart/index.tsx @@ -17,7 +17,7 @@ interface Props { } const getChartDimensions = (size: CardSizeType) => ({ - height: size === 'small' ? 55 : 165, + height: size === 'small' ? 55 : 120, width: size === 'small' ? 90 : 185, }) diff --git a/app/(landing)/(home)/_ui/top-strategy-card/index.tsx b/app/(landing)/(home)/_ui/top-strategy-card/index.tsx new file mode 100644 index 00000000..f4074126 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-strategy-card/index.tsx @@ -0,0 +1,102 @@ +'use client' + +import { StarIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Avatar from '@/shared/ui/avatar' + +import LineChart from '../line-chart' +import styles from './styles.module.scss' +import { + CardSizeType, + TopCardContentDetailsProps, + TopCardContentProps, + TopCardProfitChartProps, +} from './types' + +const cx = classNames.bind(styles) + +interface Props { + size?: CardSizeType + children: React.ReactNode +} + +const TopStrategyCard = ({ size, children }: Props) => { + return <div className={cx('card-container', size)}>{children}</div> +} + +const ContentsWrapper = ({ children }: { children: React.ReactNode }) => { + return <div className={cx('contents-wrapper')}>{children}</div> +} + +const Content = ({ ranking, profileImage, nickname, title }: TopCardContentProps) => { + return ( + <div className={cx('content-wrapper')}> + <strong className={cx('ranking')}>Top {ranking}</strong> + <div className={cx('profile')}> + <Avatar src={profileImage} size="small" /> + <span className={cx('nickname')}>{nickname}</span> + </div> + <h3 className={cx('title')}>{title}</h3> + </div> + ) +} + +const ContentDetails = ({ + subscriptionCount, + averageRating, + reviewCount, +}: TopCardContentDetailsProps) => { + return ( + <div className={cx('content-details-wrapper')}> + <span className={cx('subscription')}>{subscriptionCount.toLocaleString()}명 구독</span> + <div className={cx('rating-wrapper')}> + <StarIcon width="24px" height="24px" /> + <span> + {averageRating} ({reviewCount}) + </span> + </div> + </div> + ) +} + +const SmScore = ({ score }: { score: number }) => { + return ( + <div className={cx('score-wrapper')}> + <span className={cx('label')}>SM SCORE</span> + <span className={cx('score')}>{score}</span> + </div> + ) +} + +const ProfitChart = ({ + chartData, + profitAlign = 'horizontal', + percentageChange, + size, +}: TopCardProfitChartProps) => { + const isNegative = percentageChange < 0 + + return ( + <div className={cx('profit-wrapper')}> + <div className={cx('chart')}> + <LineChart data={chartData} isNegative={isNegative} size={size} /> + </div> + <div className={cx('profit', profitAlign)}> + <span className={cx('label')}>누적수익률</span> + <span className={cx('value', { negative: isNegative })}> + {isNegative ? '' : '+'} + {percentageChange}% + </span> + </div> + </div> + ) +} + +export default Object.assign(TopStrategyCard, { + ContentsWrapper, + Content, + ContentDetails, + SmScore, + ProfitChart, +}) diff --git a/app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss b/app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss new file mode 100644 index 00000000..867c7607 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss @@ -0,0 +1,154 @@ +.card-container { + display: flex; + justify-content: space-between; + width: 300px; + height: 170px; + padding: 20px 28px 18px; + border-radius: 5px; + background-color: $color-white; + + &.large { + position: relative; + flex-direction: column; + height: 340px; + + .content-wrapper { + .title { + max-width: 100%; + } + } + + .score-wrapper { + position: absolute; + bottom: 19px; + } + + .profit-wrapper { + gap: 36px; + + .profit { + align-self: end; + } + } + + .chart { + margin: 0; + } + } +} + +.contents-wrapper { + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.content-wrapper { + display: flex; + flex-direction: column; + gap: 8px; + + .ranking { + @include typo-b1; + font-weight: $text-bold; + } + + .profile { + display: flex; + align-items: center; + + .nickname { + margin-left: 0.5rem; + color: $color-gray-500; + line-height: normal; + @include typo-c1; + } + } + + .title { + max-width: 128px; + @include typo-b2; + @include ellipsis(2); + line-height: normal; + } +} + +.content-details-wrapper { + display: flex; + align-items: center; + gap: 4px; + @include typo-c1; + font-weight: $text-semibold; + + .subscription { + margin-top: 2px; + color: $color-gray-800; + } + + .rating-wrapper { + display: flex; + align-items: center; + + svg { + color: $color-yellow; + } + + span { + margin-top: 2px; + color: $color-gray-400; + } + } +} + +.profit-wrapper { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + + .profit { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 2px; + + &.vertical { + flex-direction: column; + gap: 2px; + } + } + + .chart { + margin-top: 12px; + } + + .label { + color: $color-gray-700; + @include typo-b3; + } + + .value { + color: $color-orange-800; + } + + &.large { + height: 340px; + } +} + +.score-wrapper { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 2px; + line-height: normal; + + .label { + color: $color-gray-700; + @include typo-b3; + } + + .score { + color: $color-orange-800; + } +} diff --git a/app/(landing)/(home)/_ui/top-strategy-card/types.ts b/app/(landing)/(home)/_ui/top-strategy-card/types.ts new file mode 100644 index 00000000..35928daa --- /dev/null +++ b/app/(landing)/(home)/_ui/top-strategy-card/types.ts @@ -0,0 +1,22 @@ +export type ProfitAlignType = 'vertical' | 'horizontal' +export type CardSizeType = 'small' | 'large' + +export interface TopCardContentProps { + ranking: number + nickname: string + profileImage?: string + title: string +} + +export interface TopCardProfitChartProps { + chartData: number[] + profitAlign?: ProfitAlignType + percentageChange: number + size: CardSizeType +} + +export interface TopCardContentDetailsProps { + subscriptionCount: number + averageRating: number + reviewCount: number +} From f48fe7fbd61deb107e712b1234878bb55d7eb281 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 23 Nov 2024 12:39:13 +0900 Subject: [PATCH 265/648] =?UTF-8?q?feat:=20TopFavoriteCard=20=EB=B0=8F=20T?= =?UTF-8?q?opSmScoreCard=20=EC=B6=94=EA=B0=80=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../top-strategy-card/top-favorite-card.tsx | 42 +++++++++++++++++++ .../top-strategy-card/top-sm-score-card.tsx | 36 ++++++++++++++++ .../(home)/_ui/top-strategy-card/types.ts | 8 ++++ 3 files changed, 86 insertions(+) create mode 100644 app/(landing)/(home)/_ui/top-strategy-card/top-favorite-card.tsx create mode 100644 app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx diff --git a/app/(landing)/(home)/_ui/top-strategy-card/top-favorite-card.tsx b/app/(landing)/(home)/_ui/top-strategy-card/top-favorite-card.tsx new file mode 100644 index 00000000..c938c278 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-strategy-card/top-favorite-card.tsx @@ -0,0 +1,42 @@ +'use client' + +import TopStrategyCard from '.' +import { TopStrategyCardCommonProps } from './types' + +interface Props extends TopStrategyCardCommonProps { + subscriptionCount: number + averageRating: number + reviewCount: number +} + +const TopFavoriteCard = ({ + ranking, + nickname, + title, + chartData, + percentageChange, + subscriptionCount, + averageRating, + reviewCount, +}: Props) => { + return ( + <TopStrategyCard> + <TopStrategyCard.ContentsWrapper> + <TopStrategyCard.Content ranking={ranking} nickname={nickname} title={title} /> + <TopStrategyCard.ContentDetails + subscriptionCount={subscriptionCount} + averageRating={averageRating} + reviewCount={reviewCount} + /> + </TopStrategyCard.ContentsWrapper> + <TopStrategyCard.ProfitChart + chartData={chartData} + percentageChange={percentageChange} + size="small" + profitAlign="vertical" + /> + </TopStrategyCard> + ) +} + +export default TopFavoriteCard diff --git a/app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx b/app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx new file mode 100644 index 00000000..233ef2e9 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx @@ -0,0 +1,36 @@ +'use client' + +import TopStrategyCard from '.' +import { CardSizeType } from '../sm-score-card' +import { TopStrategyCardCommonProps } from './types' + +interface Props extends TopStrategyCardCommonProps { + size?: CardSizeType + score: number +} + +const TopSmScoreCard = ({ + size = 'small', + ranking, + nickname, + title, + chartData, + percentageChange, + score, +}: Props) => { + return ( + <TopStrategyCard size={size}> + <TopStrategyCard.ContentsWrapper> + <TopStrategyCard.Content ranking={ranking} nickname={nickname} title={title} /> + <TopStrategyCard.SmScore score={score} /> + </TopStrategyCard.ContentsWrapper> + <TopStrategyCard.ProfitChart + chartData={chartData} + percentageChange={percentageChange} + size={size} + /> + </TopStrategyCard> + ) +} + +export default TopSmScoreCard diff --git a/app/(landing)/(home)/_ui/top-strategy-card/types.ts b/app/(landing)/(home)/_ui/top-strategy-card/types.ts index 35928daa..2e2ece81 100644 --- a/app/(landing)/(home)/_ui/top-strategy-card/types.ts +++ b/app/(landing)/(home)/_ui/top-strategy-card/types.ts @@ -20,3 +20,11 @@ export interface TopCardContentDetailsProps { averageRating: number reviewCount: number } + +export interface TopStrategyCardCommonProps { + ranking: number + nickname: string + title: string + chartData: number[] + percentageChange: number +} From 6ca64101ed7d0d1fe77cb482285d9bbeaf6f59d5 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 23 Nov 2024 13:19:51 +0900 Subject: [PATCH 266/648] =?UTF-8?q?feat:=20=EC=9D=B8=EA=B8=B0=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EC=84=B9=EC=85=98=20=EC=B6=94=EA=B0=80=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(home)/_ui/top-favorite-section/index.tsx | 83 +++++++++++++++++++ .../top-favorite-section/styles.module.scss | 12 +++ app/(landing)/(home)/page.tsx | 2 + 3 files changed, 97 insertions(+) create mode 100644 app/(landing)/(home)/_ui/top-favorite-section/index.tsx create mode 100644 app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss diff --git a/app/(landing)/(home)/_ui/top-favorite-section/index.tsx b/app/(landing)/(home)/_ui/top-favorite-section/index.tsx new file mode 100644 index 00000000..e4645dc8 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-favorite-section/index.tsx @@ -0,0 +1,83 @@ +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import { LinkButton } from '@/shared/ui/link-button' + +import HomeSubtitle from '../home-subtitle' +import TopFavoriteCard from '../top-strategy-card/top-favorite-card' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const TopFavoriteSection = () => { + const favoriteStrategies = [ + { + strategyId: 3, + strategyName: '내 전략은 엄청나', + traderImgUrl: '', + nickname: '엄청난 트레이더', + profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], + smScore: 75.0, + cumulativeProfitRate: 60.63, + subscriptionCount: 1234, + averageRating: 5.0, + totalReviews: 32, + }, + { + strategyId: 1, + strategyName: '내 전략은 꽤나 대단해', + traderImgUrl: '', + nickname: '대단한 트레이더', + profitRateChartData: [15.0, 32.0, 98.0, 29.0, 1.0, 59.0], + smScore: 80.0, + cumulativeProfitRate: 50.0, + subscriptionCount: 759, + averageRating: 4.9, + totalReviews: 62, + }, + { + strategyId: 2, + strategyName: '나 유명한데 한번 믿어봐', + traderImgUrl: '', + nickname: '엄청난 트레이더', + profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], + smScore: 70.0, + cumulativeProfitRate: 57.0, + subscriptionCount: 777, + averageRating: 5.0, + totalReviews: 20, + }, + ] + + return ( + <section className={cx('section-container')}> + <HomeSubtitle> + 인베스트메틱에서 제공하는 <br /> + 인기 있는 전략을 확인해보세요! + </HomeSubtitle> + + <ul className={cx('strategy-wrapper')}> + {favoriteStrategies.map((strategy, idx) => ( + <li key={strategy.strategyId}> + <TopFavoriteCard + ranking={idx + 1} + nickname={strategy.nickname} + title={strategy.strategyName} + chartData={strategy.profitRateChartData} + percentageChange={strategy.cumulativeProfitRate} + subscriptionCount={strategy.subscriptionCount} + averageRating={strategy.averageRating} + reviewCount={strategy.totalReviews} + /> + </li> + ))} + </ul> + + <LinkButton href={PATH.STRATEGIES} variant="filled"> + 전략랭킹 더보기 + </LinkButton> + </section> + ) +} + +export default TopFavoriteSection diff --git a/app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss b/app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss new file mode 100644 index 00000000..c32ecf71 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss @@ -0,0 +1,12 @@ +.section-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 48px; +} + +.strategy-wrapper { + display: flex; + justify-content: center; + gap: 20px; +} diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index 0ce6aca0..6b02cb19 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,4 +1,5 @@ import HeroSection from './_ui/hero-section' +import TopFavoriteSection from './_ui/top-favorite-section' import UserMetricsSection from './_ui/user-metrics-section' const HomePage = () => { @@ -6,6 +7,7 @@ const HomePage = () => { <> <HeroSection /> <UserMetricsSection /> + <TopFavoriteSection /> </> ) } From e769d357b46c549c6da1a4ea27d05672d9e0c6ae Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 23 Nov 2024 13:27:39 +0900 Subject: [PATCH 267/648] =?UTF-8?q?feat:=20=EB=8C=80=ED=91=9C=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=ED=86=B5=ED=95=A9=20=ED=8F=89=EA=B7=A0=20=EC=A7=80?= =?UTF-8?q?=ED=91=9C=20=EC=84=B9=EC=85=98=20=EC=B6=94=EA=B0=80=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../average-metrics-chart.tsx | 0 .../average-metrics.stories.tsx | 0 .../index.tsx | 12 +++++++----- .../styles.module.scss | 2 +- app/(landing)/(home)/page.tsx | 2 ++ 5 files changed, 10 insertions(+), 6 deletions(-) rename app/(landing)/(home)/_ui/{average-metrics-container => average-metrics-section}/average-metrics-chart.tsx (100%) rename app/(landing)/(home)/_ui/{average-metrics-container => average-metrics-section}/average-metrics.stories.tsx (100%) rename app/(landing)/(home)/_ui/{average-metrics-container => average-metrics-section}/index.tsx (84%) rename app/(landing)/(home)/_ui/{average-metrics-container => average-metrics-section}/styles.module.scss (96%) diff --git a/app/(landing)/(home)/_ui/average-metrics-container/average-metrics-chart.tsx b/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx similarity index 100% rename from app/(landing)/(home)/_ui/average-metrics-container/average-metrics-chart.tsx rename to app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx diff --git a/app/(landing)/(home)/_ui/average-metrics-container/average-metrics.stories.tsx b/app/(landing)/(home)/_ui/average-metrics-section/average-metrics.stories.tsx similarity index 100% rename from app/(landing)/(home)/_ui/average-metrics-container/average-metrics.stories.tsx rename to app/(landing)/(home)/_ui/average-metrics-section/average-metrics.stories.tsx diff --git a/app/(landing)/(home)/_ui/average-metrics-container/index.tsx b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx similarity index 84% rename from app/(landing)/(home)/_ui/average-metrics-container/index.tsx rename to app/(landing)/(home)/_ui/average-metrics-section/index.tsx index 3f221b29..c8afe7f2 100644 --- a/app/(landing)/(home)/_ui/average-metrics-container/index.tsx +++ b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx @@ -1,12 +1,12 @@ import classNames from 'classnames/bind' +import HomeSubtitle from '../home-subtitle' import AverageMetricsChart, { AverageMetricsChartDataModel } from './average-metrics-chart' import styles from './styles.module.scss' const cx = classNames.bind(styles) -const AverageMetricsContainer = () => { - // 임시 데이터 +const AverageMetricsSection = () => { const chartData: AverageMetricsChartDataModel = { dates: [ 'Jan 1, 2023', @@ -31,7 +31,9 @@ const AverageMetricsContainer = () => { const endDate = chartData.dates.at(-1) return ( - <> + <section> + <HomeSubtitle>대표 전략 통합 평균 지표</HomeSubtitle> + <div className={cx('container')}> <div className={cx('contents-wrapper')}> <div className={cx('date-wrapper')}> @@ -41,8 +43,8 @@ const AverageMetricsContainer = () => { <AverageMetricsChart data={chartData} /> </div> </div> - </> + </section> ) } -export default AverageMetricsContainer +export default AverageMetricsSection diff --git a/app/(landing)/(home)/_ui/average-metrics-container/styles.module.scss b/app/(landing)/(home)/_ui/average-metrics-section/styles.module.scss similarity index 96% rename from app/(landing)/(home)/_ui/average-metrics-container/styles.module.scss rename to app/(landing)/(home)/_ui/average-metrics-section/styles.module.scss index 2cb5392c..2da855b3 100644 --- a/app/(landing)/(home)/_ui/average-metrics-container/styles.module.scss +++ b/app/(landing)/(home)/_ui/average-metrics-section/styles.module.scss @@ -1,7 +1,7 @@ .container { width: 100%; max-width: 1260px; - margin: 0 auto; + margin: 48px auto 0; padding: 48px 0 60px; border-radius: 10px; background-color: $color-white; diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index 6b02cb19..6a5bb644 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,3 +1,4 @@ +import AverageMetricsSection from './_ui/average-metrics-section' import HeroSection from './_ui/hero-section' import TopFavoriteSection from './_ui/top-favorite-section' import UserMetricsSection from './_ui/user-metrics-section' @@ -8,6 +9,7 @@ const HomePage = () => { <HeroSection /> <UserMetricsSection /> <TopFavoriteSection /> + <AverageMetricsSection /> </> ) } From 66cc349efc95d59594bd6b980571ae87003e2890 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 23 Nov 2024 14:22:56 +0900 Subject: [PATCH 268/648] =?UTF-8?q?feat:=20sm=20score=20=EC=84=B9=EC=85=98?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(home)/_ui/top-sm-score-section/index.tsx | 96 +++++++++++++++++++ .../top-sm-score-section/styles.module.scss | 21 ++++ app/(landing)/(home)/page.tsx | 2 + 3 files changed, 119 insertions(+) create mode 100644 app/(landing)/(home)/_ui/top-sm-score-section/index.tsx create mode 100644 app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss diff --git a/app/(landing)/(home)/_ui/top-sm-score-section/index.tsx b/app/(landing)/(home)/_ui/top-sm-score-section/index.tsx new file mode 100644 index 00000000..f36dbf99 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-sm-score-section/index.tsx @@ -0,0 +1,96 @@ +import classNames from 'classnames/bind' + +import HomeSubtitle from '../home-subtitle' +import TopSmScoreCard from '../top-strategy-card/top-sm-score-card' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const TopSmScoreSection = () => { + const topSmScoreStrategies = [ + { + strategyId: 3, + strategyName: '내 전략은 엄청나', + traderImgUrl: '', + nickname: '엄청난 트레이더', + profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], + smScore: 95.01, + cumulativeProfitRate: 60.6, + subscriptionCount: 1234, + averageRating: 5.0, + totalReviews: 32, + }, + { + strategyId: 1, + strategyName: '내 전략은 꽤나 대단해', + traderImgUrl: '', + nickname: '대단한 트레이더', + profitRateChartData: [15.0, 32.0, 98.0, 29.0, 1.0, 59.0], + smScore: 80.02, + cumulativeProfitRate: 50.0, + subscriptionCount: 759, + averageRating: 4.9, + totalReviews: 62, + }, + { + strategyId: 2, + strategyName: '나 유명한데 한번 믿어봐', + traderImgUrl: '', + nickname: '엄청난 트레이더', + profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], + smScore: 70.01, + cumulativeProfitRate: 57.0, + subscriptionCount: 777, + averageRating: 5.0, + totalReviews: 20, + }, + { + strategyId: 4, + strategyName: '나 유명한데 한번 믿어봐', + traderImgUrl: '', + nickname: '엄청난 트레이더', + profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], + smScore: 70.0, + cumulativeProfitRate: 57.0, + subscriptionCount: 777, + averageRating: 5.0, + totalReviews: 20, + }, + { + strategyId: 5, + strategyName: '나 유명한데 한번 믿어봐', + traderImgUrl: '', + nickname: '엄청난 트레이더', + profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], + smScore: 70.0, + cumulativeProfitRate: 57.0, + subscriptionCount: 777, + averageRating: 5.0, + totalReviews: 20, + }, + ] + + return ( + <section className={cx('section-container')}> + <HomeSubtitle>높은 SM 스코어별로 전략을 확인해보세요!</HomeSubtitle> + + <ul className={cx('strategy-wrapper')}> + {topSmScoreStrategies.map((strategy, idx) => ( + <li key={strategy.strategyId}> + <TopSmScoreCard + size={idx > 0 ? 'small' : 'large'} + ranking={idx + 1} + nickname={strategy.nickname} + title={strategy.strategyName} + chartData={strategy.profitRateChartData} + percentageChange={strategy.cumulativeProfitRate} + score={strategy.smScore} + /> + </li> + ))} + </ul> + </section> + ) +} + +export default TopSmScoreSection diff --git a/app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss b/app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss new file mode 100644 index 00000000..eeca3b16 --- /dev/null +++ b/app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss @@ -0,0 +1,21 @@ +.section-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 48px; + padding-bottom: 90px; +} + +.strategy-wrapper { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20px; + width: 940px; + + li { + &:nth-child(1) { + grid-column: 1; + grid-row: 1 / 3; + } + } +} diff --git a/app/(landing)/(home)/page.tsx b/app/(landing)/(home)/page.tsx index 6a5bb644..ac7a069c 100644 --- a/app/(landing)/(home)/page.tsx +++ b/app/(landing)/(home)/page.tsx @@ -1,6 +1,7 @@ import AverageMetricsSection from './_ui/average-metrics-section' import HeroSection from './_ui/hero-section' import TopFavoriteSection from './_ui/top-favorite-section' +import TopSmScoreSection from './_ui/top-sm-score-section' import UserMetricsSection from './_ui/user-metrics-section' const HomePage = () => { @@ -10,6 +11,7 @@ const HomePage = () => { <UserMetricsSection /> <TopFavoriteSection /> <AverageMetricsSection /> + <TopSmScoreSection /> </> ) } From d445633535deb0efb88675e598581e14d2b10cb2 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 15:45:08 +0900 Subject: [PATCH 269/648] =?UTF-8?q?chore:=20axios,=20jwt-decode=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index c563d596..9031e4e2 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,11 @@ }, "dependencies": { "@tanstack/react-query": "^5.59.19", + "axios": "^1.7.7", "classnames": "^2.5.1", "highcharts": "^11.4.8", "highcharts-react-official": "^3.2.1", + "jwt-decode": "^4.0.0", "next": "14.2.16", "react": "^18", "react-dom": "^18", From ce320a2598cf0c7f79142071771a5f2132228e59 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:16:28 +0900 Subject: [PATCH 270/648] =?UTF-8?q?feat:=20=EB=9E=9C=EB=94=A9=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=20=ED=97=A4=EB=8D=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/layout.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/(landing)/layout.tsx b/app/(landing)/layout.tsx index 5015777f..2faa7eb8 100644 --- a/app/(landing)/layout.tsx +++ b/app/(landing)/layout.tsx @@ -1,13 +1,27 @@ +'use client' + +import { usePathname } from 'next/navigation' + +import LogoHeader from '@/shared/ui/header/logo-header' + interface Props { children: React.ReactNode } -const HomeLayout = ({ children }: Props) => { +const LandingLayout = ({ children }: Props) => { + const pathname = usePathname() + + const getHeaderType = () => { + if (pathname.includes('/signin') || pathname.includes('/signup')) return false + return true + } + return ( <> + <LogoHeader hasText={true} hasLinks={getHeaderType()} /> <main className="landing-main">{children}</main> </> ) } -export default HomeLayout +export default LandingLayout From 0815d2a61767148d703a0b3290bdd3e058548d7d Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:18:59 +0900 Subject: [PATCH 271/648] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC?= =?UTF-8?q?=EC=8B=B1=20=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 193 +++++++++++++++++++++++- app/(landing)/signin/styles.module.scss | 99 ++++++++++++ 2 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 app/(landing)/signin/styles.module.scss diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index eb8da8c5..d433ccd9 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -1,5 +1,196 @@ +'use client' + +import { useCallback, useState } from 'react' + +import Link from 'next/link' +import { useRouter, useSearchParams } from 'next/navigation' + +import { AxiosError } from 'axios' +import classNames from 'classnames/bind' + +import { useLogin } from '@/shared/api/auth' +import { ERROR_MESSAGES } from '@/shared/constants/error-messages' +import { PATH } from '@/shared/constants/path' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import type { LoginFormDataModel } from '@/shared/types/auth' +import { Button } from '@/shared/ui/button' +import Checkbox from '@/shared/ui/check-box' +import { Input } from '@/shared/ui/input' +import { validate } from '@/shared/utils/validation' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + const SignInPage = () => { - return <></> + const router = useRouter() + const searchParams = useSearchParams() + const loginMutation = useLogin() + + const setKeepLoggedIn = useAuthStore((state) => state.setKeepLoggedIn) + const isKeepLoggedIn = useAuthStore((state) => state.isKeepLoggedIn) + + const [formData, setFormData] = useState<LoginFormDataModel>({ + email: '', + password: '', + }) + const [errors, setErrors] = useState<string | null>(null) + const [isSubmitting, setIsSubmitting] = useState(false) + + const validateForm = useCallback(() => { + const emailError = validate('EMAIL', formData.email) + if (emailError) { + setErrors(emailError) + return false + } + + const passwordError = validate('PASSWORD', formData.password) + if (passwordError) { + setErrors(passwordError) + return false + } + + return true + }, [formData.email, formData.password]) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setIsSubmitting(true) + setErrors(null) + + if (!validateForm()) { + setIsSubmitting(false) + return + } + + try { + const { data } = await loginMutation.mutateAsync(formData) + + if (!data?.accessToken || !data?.refreshToken || !data?.user) { + setErrors(ERROR_MESSAGES.AUTH.LOGIN_FAILED) + return + } + + const returnUrl = searchParams.get('returnUrl') + if (returnUrl) { + try { + const url = new URL(returnUrl, window.location.origin) + if (url.origin === window.location.origin) { + router.replace(returnUrl) + return + } + } catch (err) { + console.error('Invalid return URL:', err) + } + } + router.replace(PATH.STRATEGIES) + } catch (err) { + if (err instanceof AxiosError) { + if (err.response) { + setErrors(err.response.data.message) + } else { + setErrors(ERROR_MESSAGES.NETWORK.ERROR) + } + } else { + setErrors(ERROR_MESSAGES.AUTH.LOGIN_FAILED) + } + } finally { + setIsSubmitting(false) + } + } + + const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { + const { name, value } = e.target + setFormData((prev) => ({ ...prev, [name]: value })) + }, []) + + const handleKeepLoggedInChange = useCallback( + (checked: boolean) => { + setKeepLoggedIn(checked) + }, + [setKeepLoggedIn] + ) + + const isFormDisabled = isSubmitting || loginMutation.isPending + + return ( + <div className={styles.container}> + <div className={styles.loginBox}> + <h1 className={styles.title}>로그인</h1> + <form onSubmit={handleSubmit} className={styles.form}> + <div className={styles.inputGroup}> + <label htmlFor="email">이메일</label> + <Input + type="email" + inputSize="large" + id="email" + name="email" + value={formData.email} + onChange={handleInputChange} + placeholder="이메일을 입력하세요." + className={styles.input} + disabled={isFormDisabled} + required + autoComplete="email" + /> + </div> + <div className={styles.inputGroup}> + <label htmlFor="password">비밀번호</label> + <Input + type="password" + inputSize="large" + id="password" + name="password" + value={formData.password} + onChange={handleInputChange} + placeholder="비밀번호를 입력하세요." + className={styles.input} + disabled={isFormDisabled} + required + autoComplete="current-password" + /> + </div> + <div className={cx('error-container')}> + <p + className={cx('error-message', { + visible: errors, + hidden: !errors, + })} + role="alert" + > + {errors} + </p> + </div> + <div className={styles.options}> + <label className={styles.remember}> + <Checkbox + isChecked={isKeepLoggedIn} + onChange={handleKeepLoggedInChange} + label="로그인 유지" + textColor="gray600" + /> + </label> + <div className={styles.links}> + <Link href={PATH.FIND_ID}>아이디 찾기</Link> + <span className={styles.divider}>|</span> + <Link href={PATH.FIND_PASSWORD}>비밀번호 찾기</Link> + </div> + </div> + <Button type="submit" size="large" variant="filled" disabled={isFormDisabled}> + 로그인 + </Button> + <Button + type="button" + size="large" + onClick={() => router.push(PATH.SIGN_UP_USER_TYPE)} + disabled={isFormDisabled} + > + 회원가입 + </Button> + </form> + </div> + </div> + ) } export default SignInPage diff --git a/app/(landing)/signin/styles.module.scss b/app/(landing)/signin/styles.module.scss new file mode 100644 index 00000000..270b03f5 --- /dev/null +++ b/app/(landing)/signin/styles.module.scss @@ -0,0 +1,99 @@ +.container { + display: flex; + justify-content: center; + align-items: center; +} + +.loginBox { + max-width: 400px; +} + +.title { + @include typo-h2; + margin-bottom: 70px; +} + +.form { + display: flex; + flex-direction: column; + gap: 24px; +} + +.inputGroup { + display: flex; + flex-direction: column; + gap: 9px; + + label { + @include typo-b2; + } +} + +.input { + border-radius: 2px; + @include typo-b3; + &::placeholder { + color: $color-gray-400; + } + + &:required { + border-color: $color-gray-500; + } + + &:focus { + outline: none; + } +} + +.error-container { + min-height: 24px; + margin-top: -18px; + + .error-message { + @include typo-c1; + color: $color-orange-700; + + &.visible { + opacity: 1; + } + + &.hidden { + opacity: 0; + } + } +} + +.options { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30px; + margin-top: -25px; +} + +.remember { + display: flex; + align-items: center; + cursor: pointer; + gap: 8px; +} + +.links { + display: flex; + align-items: center; + gap: 8px; + + a { + color: $color-gray-600; + @include typo-c1; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} + +.divider { + color: #ddd; +} From 40c0c3d794f5f0ab639fd72b4f7f281206234bea Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:21:22 +0900 Subject: [PATCH 272/648] =?UTF-8?q?feat:=20MSW=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=9D=B8=EC=A6=9D=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?API=20=EB=AA=A8=ED=82=B9=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mocks/handlers.ts | 4 +- mocks/handlers/auth.ts | 87 +++++++++++++++++++++++++++++++++++++++++ mocks/handlers/index.ts | 1 + 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 mocks/handlers/auth.ts diff --git a/mocks/handlers.ts b/mocks/handlers.ts index 4af399c8..ff6c12bb 100644 --- a/mocks/handlers.ts +++ b/mocks/handlers.ts @@ -1,5 +1,5 @@ -import { postHandlers, userHandlers } from './handlers/' +import { authHandlers, postHandlers, userHandlers } from './handlers/' -const handlers = [...userHandlers, ...postHandlers] +const handlers = [...userHandlers, ...postHandlers, ...authHandlers] export { handlers } diff --git a/mocks/handlers/auth.ts b/mocks/handlers/auth.ts new file mode 100644 index 00000000..bdc1d7e8 --- /dev/null +++ b/mocks/handlers/auth.ts @@ -0,0 +1,87 @@ +import { jwtDecode } from 'jwt-decode' +import { HttpResponse, http } from 'msw' + +import { ERROR_MESSAGES } from '@/shared/constants/error-messages' +import { LoginFormDataModel, TokenPayloadModel, UserModel } from '@/shared/types/auth' + +const MOCK_USERS: Record<string, UserModel> = { + 'test@example.com': { + id: '1', + email: 'test@example.com', + name: 'user1', + role: 'user', + createdAt: '2024-01-01T00:00:00Z', + }, + 'admin@example.com': { + id: '2', + email: 'admin@example.com', + name: 'user2', + role: 'admin', + createdAt: '2024-01-01T00:00:00Z', + }, +} + +const createToken = (user: UserModel, isAdmin: boolean) => { + const now = Math.floor(Date.now() / 1000) + const exp = now + (isAdmin ? 1800 : 7 * 24 * 60 * 60) + return `mock_${btoa(JSON.stringify({ user, exp, iat: now }))}` +} + +export const authHandlers = [ + http.post('/api/users/login', async ({ request }) => { + const { email, password } = (await request.json()) as LoginFormDataModel + const user = MOCK_USERS[email] + + if (user && password === 'password123') { + const isAdmin = user.role === 'admin' + const accessToken = createToken(user, isAdmin) + const refreshToken = createToken(user, isAdmin) + + return HttpResponse.json({ + isSuccess: true, + message: '로그인 성공', + data: { accessToken, refreshToken, user }, + }) + } + + return HttpResponse.json( + { + isSuccess: false, + message: ERROR_MESSAGES.AUTH.INVALID_CREDENTIALS, + code: 'INVALID_CREDENTIALS', + }, + { status: 400 } + ) + }), + + http.post('/api/users/reissue/refreshtoken', async ({ request }) => { + const authHeader = request.headers.get('Authorization') + const refreshToken = authHeader?.replace('Bearer ', '') + + if (!refreshToken) { + return HttpResponse.json( + { isSuccess: false, message: ERROR_MESSAGES.AUTH.INVALID_TOKEN }, + { status: 401 } + ) + } + + try { + const decoded = jwtDecode<TokenPayloadModel>(refreshToken) + const isAdmin = decoded.user.role === 'admin' + + const accessToken = createToken(decoded.user, isAdmin) + const newRefreshToken = createToken(decoded.user, isAdmin) + + return HttpResponse.json({ + isSuccess: true, + message: '토큰 갱신 성공', + data: { accessToken, refreshToken: newRefreshToken }, + }) + } catch { + return HttpResponse.json( + { isSuccess: false, message: ERROR_MESSAGES.AUTH.REFRESH_FAILED }, + { status: 401 } + ) + } + }), +] diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts index 3f70ad6f..a3042340 100644 --- a/mocks/handlers/index.ts +++ b/mocks/handlers/index.ts @@ -1,2 +1,3 @@ export { postHandlers } from './post' export { userHandlers } from './user' +export { authHandlers } from './auth' From 6cea8680ff92d8b953d5e21b927da8fa0a096747 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:24:06 +0900 Subject: [PATCH 273/648] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EB=93=B1=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EA=B4=80=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/auth.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 shared/api/auth.ts diff --git a/shared/api/auth.ts b/shared/api/auth.ts new file mode 100644 index 00000000..341a216f --- /dev/null +++ b/shared/api/auth.ts @@ -0,0 +1,48 @@ +import { useMutation } from '@tanstack/react-query' + +import { + removeAccessToken, + removeRefreshToken, + setAccessToken, + setRefreshToken, +} from '@/shared/lib/auth-tokens' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import type { LoginFormDataModel, LoginResponseType } from '@/shared/types/auth' + +import axiosInstance from './axios' + +export const login = async (credentials: LoginFormDataModel): Promise<LoginResponseType> => { + const response = await axiosInstance.post('/api/users/login', credentials) + const { user, accessToken, refreshToken } = response.data.data + + setAccessToken(accessToken) + setRefreshToken(refreshToken) + + useAuthStore.getState().setAuthState({ + isAuthenticated: true, + user, + isLoggedOut: false, + }) + + return response.data +} + +export const logout = () => { + if (typeof window !== 'undefined') { + removeAccessToken() + removeRefreshToken() + + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + isKeepLoggedIn: false, + isLoggedOut: true, + }) + } +} + +export const useLogin = () => { + return useMutation({ + mutationFn: login, + }) +} From 69d0fac2362bf1d2a8f5746595cf7785ada3d0ae Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:27:13 +0900 Subject: [PATCH 274/648] =?UTF-8?q?feat:=20Axios=20=EC=9D=B8=EC=8A=A4?= =?UTF-8?q?=ED=84=B4=EC=8A=A4=20=EB=B0=8F=20=EC=9D=B8=ED=84=B0=EC=85=89?= =?UTF-8?q?=ED=84=B0=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20(#78)=20-?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EA=B8=B0=EB=B0=98=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EA=B3=BC=20=EC=9E=90=EB=8F=99=20=EA=B0=B1=EC=8B=A0=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=B4=20=ED=8F=AC=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/axios.ts | 86 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 shared/api/axios.ts diff --git a/shared/api/axios.ts b/shared/api/axios.ts new file mode 100644 index 00000000..c5ca04e5 --- /dev/null +++ b/shared/api/axios.ts @@ -0,0 +1,86 @@ +import axios from 'axios' +import { AxiosError, InternalAxiosRequestConfig } from 'axios' + +import { getAccessToken } from '@/shared/lib/auth-tokens' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import { getUserFromToken, isTokenExpired, refreshToken } from '@/shared/utils/token-utils' + +import { logout } from './auth' + +export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { + const instance = axios.create() + + if (options.withInterceptors && typeof window !== 'undefined') { + instance.interceptors.request.use( + async (config: InternalAxiosRequestConfig) => { + const { isLoggedOut } = useAuthStore.getState() + const accessToken = getAccessToken() + + if (isLoggedOut || !accessToken) { + return config + } + + try { + const user = getUserFromToken(accessToken) + const isAdmin = user?.role === 'admin' + + if (isAdmin && isTokenExpired(accessToken)) { + handleLogout() + return config + } + + config.headers.Authorization = `Bearer ${accessToken}` + } catch (error) { + console.error('토큰 디코딩 실패:', error) + handleLogout() + } + + return config + }, + (error) => Promise.reject(error) + ) + + instance.interceptors.response.use( + (response) => response, + async (error: AxiosError) => { + if (!error.config) return Promise.reject(error) + + const originalRequest = error.config + const { isLoggedOut } = useAuthStore.getState() + + if (isLoggedOut) { + return Promise.reject(error) + } + + if (error.response?.status === 401) { + try { + const newToken = await refreshToken() + if (newToken) { + originalRequest.headers.Authorization = `Bearer ${newToken}` + return instance(originalRequest) + } + } catch (refreshError) { + console.error('Token refresh failed:', refreshError) + } + + handleLogout() + } + + return Promise.reject(error) + } + ) + } + + return instance +} + +const handleLogout = () => { + if (typeof window !== 'undefined') { + sessionStorage.removeItem('sessionToken') + logout() + } +} + +const axiosInstance = createAxiosInstance({ withInterceptors: true }) + +export default axiosInstance From ce22887dd1172c5b972d6b2664ca771c4e0cf30d Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:27:48 +0900 Subject: [PATCH 275/648] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=84=B8=EB=B6=84=ED=99=94=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/error-messages.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/shared/constants/error-messages.ts b/shared/constants/error-messages.ts index 494a61c4..37841955 100644 --- a/shared/constants/error-messages.ts +++ b/shared/constants/error-messages.ts @@ -1,6 +1,19 @@ export const ERROR_MESSAGES = { - EMAIL: '이메일 형식이 잘못되었습니다.', - PASSWORD: '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', - PHONE: '전화번호는 10자리 이상이어야 합니다.', - REQUIRED: '필수 입력란입니다.', + AUTH: { + INVALID_CREDENTIALS: '이메일 또는 비밀번호가 올바르지 않습니다.', + SESSION_EXPIRED: '로그인이 만료되었습니다. 다시 로그인해주세요.', + LOGIN_FAILED: '서버 오류로 로그인에 실패했습니다.', + INVALID_TOKEN: '유효하지 않은 인증입니다.', + ALREADY_LOGGED_IN: '이미 로그인되어 있습니다.', + REFRESH_FAILED: '토큰 갱신에 실패했습니다.', + }, + FORM: { + REQUIRED_FIELDS: '이메일과 비밀번호를 입력해주세요.', + EMAIL: '이메일 형식이 올바르지 않습니다.', + PASSWORD: '비밀번호는 8자 이상, 영문과 숫자를 포함해야 합니다.', + PHONE: '휴대폰 번호 형식이 올바르지 않습니다.', + }, + NETWORK: { + ERROR: '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해주세요.', + }, } as const From f54c7930b5791ef402ff124db219a1ccfcfae57d Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:34:15 +0900 Subject: [PATCH 276/648] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EA=B4=80=EB=A6=AC=EC=99=80=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=ED=9B=85=20=EC=B6=94=EA=B0=80=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/hooks/custom/use-auth.ts | 122 ++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 shared/hooks/custom/use-auth.ts diff --git a/shared/hooks/custom/use-auth.ts b/shared/hooks/custom/use-auth.ts new file mode 100644 index 00000000..ce2c1563 --- /dev/null +++ b/shared/hooks/custom/use-auth.ts @@ -0,0 +1,122 @@ +import { useEffect } from 'react' + +import { useRouter } from 'next/navigation' + +import { useQuery } from '@tanstack/react-query' + +import { logout } from '@/shared/api/auth' +import { PATH } from '@/shared/constants/path' +import { getAccessToken, getRefreshToken } from '@/shared/lib/auth-tokens' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import { getTimeUntilExpiry, getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' + +const TOKEN_CHECK_INTERVAL = 600000 // 10분 +const ADMIN_EXPIRY_WARNING = 600000 // 10분 + +export const useAuth = () => { + const router = useRouter() + const { user, isAuthenticated, isKeepLoggedIn } = useAuthStore() + + useEffect(() => { + const initializeAuth = () => { + const accessToken = getAccessToken() + const refreshToken = getRefreshToken() + + if (!accessToken || !refreshToken) { + handleLogout() + return + } + + const user = getUserFromToken(accessToken) + if (!user) { + handleLogout() + return + } + + if (user.role === 'admin' && isTokenExpired(accessToken)) { + handleLogout() + return + } + + if (user.role === 'user') { + const sessionToken = sessionStorage.getItem('sessionToken') + if (!isKeepLoggedIn && !sessionToken) { + handleLogout() + return + } + } + + useAuthStore.getState().setAuthState({ + isAuthenticated: true, + user, + isLoggedOut: false, + }) + } + + initializeAuth() + }, [isKeepLoggedIn]) + + const { data: tokenStatus } = useQuery({ + queryKey: ['tokenStatus'], + queryFn: async () => { + const accessToken = getAccessToken() + const refreshToken = getRefreshToken() + + if (!accessToken || !refreshToken) { + handleLogout() + return null + } + + const user = getUserFromToken(accessToken) + if (!user) { + handleLogout() + return null + } + + const isExpired = isTokenExpired(accessToken) + const timeUntilExpiry = getTimeUntilExpiry(accessToken) + + if (user.role === 'admin') { + if (isExpired) { + handleLogout() + return null + } + return { + isValid: !isExpired, + timeUntilExpiry, + isNearExpiry: timeUntilExpiry < ADMIN_EXPIRY_WARNING, + } + } + + if (user.role === 'user') { + const sessionToken = sessionStorage.getItem('sessionToken') + if (!isKeepLoggedIn && !sessionToken) { + handleLogout() + return null + } + } + + return { + isValid: !isExpired, + timeUntilExpiry, + isNearExpiry: false, + } + }, + refetchInterval: TOKEN_CHECK_INTERVAL, + }) + + const handleLogout = () => { + sessionStorage.removeItem('sessionToken') + logout() + router.replace(PATH.SIGN_IN) + } + + return { + user, + isAuthenticated, + isKeepLoggedIn, + tokenStatus, + isAdminNearExpiry: tokenStatus?.isNearExpiry, + logout: handleLogout, + } +} From 5d7bb627ca68e644ba5fc6a5f82716b58037eba6 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:35:24 +0900 Subject: [PATCH 277/648] =?UTF-8?q?feat:=20=EB=A1=9C=EC=BB=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EC=A7=80=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20=ED=86=A0=ED=81=B0=20=EA=B4=80=EB=A6=AC=20=EC=9C=A0?= =?UTF-8?q?=ED=8B=B8=EB=A6=AC=ED=8B=B0=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/lib/auth-tokens.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 shared/lib/auth-tokens.ts diff --git a/shared/lib/auth-tokens.ts b/shared/lib/auth-tokens.ts new file mode 100644 index 00000000..9f88d0a0 --- /dev/null +++ b/shared/lib/auth-tokens.ts @@ -0,0 +1,23 @@ +export const setAccessToken = (token: string) => { + localStorage.setItem('accessToken', token) +} + +export const setRefreshToken = (token: string) => { + localStorage.setItem('refreshToken', token) +} + +export const getAccessToken = () => { + return localStorage.getItem('accessToken') +} + +export const getRefreshToken = () => { + return localStorage.getItem('refreshToken') +} + +export const removeAccessToken = () => { + localStorage.removeItem('accessToken') +} + +export const removeRefreshToken = () => { + localStorage.removeItem('refreshToken') +} From a28318a4ab8d993d4b15242f181a362140506cd7 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:36:33 +0900 Subject: [PATCH 278/648] =?UTF-8?q?feat:=20=EC=A0=84=EC=97=AD=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=83=81=ED=83=9C=20=EA=B4=80=EB=A6=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EC=96=B4=20=EC=B6=94=EA=B0=80=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/stores/use-auth-store.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 shared/stores/use-auth-store.ts diff --git a/shared/stores/use-auth-store.ts b/shared/stores/use-auth-store.ts new file mode 100644 index 00000000..9a6c411d --- /dev/null +++ b/shared/stores/use-auth-store.ts @@ -0,0 +1,22 @@ +import { create } from 'zustand' + +import type { UserModel } from '../types/auth' + +interface AuthStateModel { + isAuthenticated: boolean + user: UserModel | null + isKeepLoggedIn: boolean + isLoggedOut: boolean + setKeepLoggedIn: (value: boolean) => void + setAuthState: (state: Partial<AuthStateModel>) => void +} + +export const useAuthStore = create<AuthStateModel>((set) => ({ + isAuthenticated: false, + user: null, + isKeepLoggedIn: false, + isLoggedOut: false, + + setKeepLoggedIn: (value) => set({ isKeepLoggedIn: value }), + setAuthState: (state) => set(state), +})) From dcd0cbf9a92d4e707e4192e5ae8afcec7cb1bb42 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:37:47 +0900 Subject: [PATCH 279/648] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=83=80=EC=9E=85=20=EC=A0=95=EC=9D=98=20=EB=B0=8F?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EA=B0=80=EB=93=9C=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/types/auth.ts | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 shared/types/auth.ts diff --git a/shared/types/auth.ts b/shared/types/auth.ts new file mode 100644 index 00000000..52622c9a --- /dev/null +++ b/shared/types/auth.ts @@ -0,0 +1,50 @@ +export type UserRoleType = 'admin' | 'user' + +export interface UserModel { + id: string + email: string + name: string + role: UserRoleType + createdAt?: string +} + +export interface LoginFormDataModel { + email: string + password: string +} + +export interface TokenDataModel { + accessToken: string + refreshToken: string +} + +export interface AuthResponseModel { + accessToken: string + refreshToken: string + user: UserModel + keepLoggedIn?: boolean +} + +export interface ApiResponseModel<T> { + isSuccess: boolean + message: string + data?: T + code?: string +} + +export type LoginResponseType = ApiResponseModel<AuthResponseModel> +export type TokenResponseType = ApiResponseModel<TokenDataModel> + +export interface TokenPayloadModel { + user: UserModel + exp: number + iat: number +} + +export const isAdmin = (user: UserModel | null): user is UserModel & { role: 'admin' } => { + return user?.role === 'admin' +} + +export const isUser = (user: UserModel | null): user is UserModel & { role: 'user' } => { + return user?.role === 'user' +} From 06421be43b306c3597441569bb7464e084a92382 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:39:34 +0900 Subject: [PATCH 280/648] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EA=B0=B1?= =?UTF-8?q?=EC=8B=A0=EC=9D=B4=EB=82=98=20=EA=B2=80=EC=A6=9D=20=EB=93=B1=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=B2=98=EB=A6=AC=EC=97=90=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=EB=90=9C=20=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=93=A4=20=EC=B6=94=EA=B0=80=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/utils/token-utils.ts | 83 +++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 shared/utils/token-utils.ts diff --git a/shared/utils/token-utils.ts b/shared/utils/token-utils.ts new file mode 100644 index 00000000..c0e56866 --- /dev/null +++ b/shared/utils/token-utils.ts @@ -0,0 +1,83 @@ +import { jwtDecode } from 'jwt-decode' + +import { getRefreshToken, setAccessToken, setRefreshToken } from '@/shared/lib/auth-tokens' +import type { TokenPayloadModel, TokenResponseType } from '@/shared/types/auth' + +import axiosInstance from '../api/axios' + +let isRefreshing = false +let refreshSubscribers: ((token: string) => void)[] = [] + +export const refreshToken = async (): Promise<string | null> => { + const refreshToken = getRefreshToken() + if (!refreshToken) return null + + if (isRefreshing) { + return new Promise((resolve) => { + refreshSubscribers.push((token) => resolve(token)) + }) + } + + try { + isRefreshing = true + + const response = await axiosInstance.post<TokenResponseType>( + '/api/users/reissue/refreshtoken', + {}, + { + headers: { + Authorization: `Bearer ${refreshToken}`, + }, + } + ) + + if (!response.data.data) { + return null + } + + const { accessToken: newAccessToken, refreshToken: newRefreshToken } = response.data.data + + if (newAccessToken && newRefreshToken) { + setAccessToken(newAccessToken) + setRefreshToken(newRefreshToken) + refreshSubscribers.forEach((cb) => cb(newAccessToken)) + return newAccessToken + } + + return null + } catch (error) { + console.error('Token refresh failed:', error) + return null + } finally { + isRefreshing = false + refreshSubscribers = [] + } +} + +export const isTokenExpired = (token: string): boolean => { + try { + const decoded = jwtDecode<TokenPayloadModel>(token) + return decoded.exp * 1000 < Date.now() + } catch { + return true + } +} + +export const getTimeUntilExpiry = (token: string): number => { + try { + const decoded = jwtDecode<TokenPayloadModel>(token) + return decoded.exp * 1000 - Date.now() + } catch { + return 0 + } +} + +export const getUserFromToken = (token: string | null): TokenPayloadModel['user'] | null => { + if (!token) return null + try { + const decoded = jwtDecode<TokenPayloadModel>(token) + return decoded.user + } catch { + return null + } +} From 3e1469d9088a418dc23f9ad927e5299ccaf32df7 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 16:41:30 +0900 Subject: [PATCH 281/648] =?UTF-8?q?feat:=20=EA=B8=B0=ED=83=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=82=AC=ED=95=AD=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 4 ++++ shared/ui/button/index.tsx | 2 +- shared/ui/check-box/index.tsx | 10 ++++++++-- shared/ui/input/index.tsx | 4 +--- shared/ui/side-navigation/user-navigation.tsx | 15 +++++++++++++-- shared/utils/validation.ts | 8 +++----- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/shared/constants/path.ts b/shared/constants/path.ts index b7ad1f31..72909a11 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -26,4 +26,8 @@ export const PATH = { ADMIN_NOTICES: '/admin/notices', ADMIN_STRATEGIES: '/admin/strategies', ADMIN_QUESTIONS: '/admin/questions', + + // Etc + FIND_ID: '/find-id', + FIND_PASSWORD: '/find-password', } as const diff --git a/shared/ui/button/index.tsx b/shared/ui/button/index.tsx index 7075e6c2..14f40ef7 100644 --- a/shared/ui/button/index.tsx +++ b/shared/ui/button/index.tsx @@ -14,7 +14,7 @@ export type ButtonVariantType = 'outline' | 'filled' interface Props extends ComponentProps<'button'> { size?: ButtonSizeType variant?: ButtonVariantType - onClick: () => void + onClick?: () => void } const _Button = ({ diff --git a/shared/ui/check-box/index.tsx b/shared/ui/check-box/index.tsx index 2ad48b8b..8602e905 100644 --- a/shared/ui/check-box/index.tsx +++ b/shared/ui/check-box/index.tsx @@ -9,13 +9,19 @@ const cx = classNames.bind(styles) interface Props { label?: string - isChecked: boolean + isChecked?: boolean onChange: (checked: boolean) => void className?: string textColor?: 'gray500' | 'gray600' | 'gray800' } -const Checkbox = ({ label, isChecked, onChange, className = '', textColor = 'gray500' }: Props) => { +const Checkbox = ({ + label, + isChecked = false, + onChange, + className = '', + textColor = 'gray500', +}: Props) => { const handleClick = () => { onChange(!isChecked) } diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index bd5be9af..9e027fe6 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -4,8 +4,6 @@ import { ComponentProps } from 'react' import classNames from 'classnames/bind' -import { ErrorMessageType } from '@/shared/types/error-message' - import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -14,7 +12,7 @@ export type InputSizeType = 'small' | 'medium' | 'large' | 'full' interface Props extends ComponentProps<'input'> { inputSize?: InputSizeType - errorMessage?: ErrorMessageType | null + errorMessage?: string | null } export const Input = ({ inputSize = 'medium', errorMessage, className, ...props }: Props) => { diff --git a/shared/ui/side-navigation/user-navigation.tsx b/shared/ui/side-navigation/user-navigation.tsx index c680bb66..5ba238ae 100644 --- a/shared/ui/side-navigation/user-navigation.tsx +++ b/shared/ui/side-navigation/user-navigation.tsx @@ -1,10 +1,11 @@ 'use client' -import { usePathname } from 'next/navigation' +import { usePathname, useRouter } from 'next/navigation' import { ChangeIcon, ProfileIcon, SignOutIcon } from '@/public/icons' import classNames from 'classnames/bind' +import { logout } from '@/shared/api/auth' import { fetchUser } from '@/shared/api/user' import { PATH } from '@/shared/constants/path' import NavButtonItem from '@/shared/ui/side-navigation/nav-button-item' @@ -16,11 +17,21 @@ const cx = classNames.bind(styles) const UserNavigation = () => { const path = usePathname() + const router = useRouter() const isAdminPage = path.startsWith(PATH.ADMIN) const user = fetchUser() const isAdmin = user.role.includes('admin') + const handleLogout = async () => { + try { + logout() + router.replace(PATH.SIGN_IN) + } catch (error) { + console.error('로그아웃 실패:', error) + } + } + return ( <nav className={cx('user-navigation')} aria-label="사용자 메뉴"> <ul> @@ -35,7 +46,7 @@ const UserNavigation = () => { </NavLinkItem> )} - <NavButtonItem icon={SignOutIcon} onClick={() => {}}> + <NavButtonItem icon={SignOutIcon} onClick={handleLogout}> 로그아웃 </NavButtonItem> </ul> diff --git a/shared/utils/validation.ts b/shared/utils/validation.ts index 69bc9cd2..d8266fc8 100644 --- a/shared/utils/validation.ts +++ b/shared/utils/validation.ts @@ -1,7 +1,5 @@ import { ERROR_MESSAGES } from '@/shared/constants/error-messages' -import { ErrorMessageType } from '../types/error-message' - const PATTERNS = { EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, PASSWORD: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/, @@ -26,13 +24,13 @@ const validators = { PHONE: isValidPhone, } as const -export const validate = (name: keyof typeof validators, value: string): ErrorMessageType | null => { +export const validate = (name: keyof typeof validators, value: string): string | null => { if (!value.trim()) { - return ERROR_MESSAGES.REQUIRED + return ERROR_MESSAGES.FORM.REQUIRED_FIELDS } if (!validators[name](value)) { - return ERROR_MESSAGES[name] + return ERROR_MESSAGES.FORM[name] } return null From 164ddd25c3b738cced79453f601822d2b679f1dc Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 23 Nov 2024 17:23:59 +0900 Subject: [PATCH 282/648] =?UTF-8?q?fix:=20AverageMetricsChart=EB=A5=BC=20d?= =?UTF-8?q?ynamic=20import=EB=A1=9C=20=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(home)/_ui/average-metrics-section/index.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/(landing)/(home)/_ui/average-metrics-section/index.tsx b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx index c8afe7f2..622195cc 100644 --- a/app/(landing)/(home)/_ui/average-metrics-section/index.tsx +++ b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx @@ -1,9 +1,16 @@ +import dynamic from 'next/dynamic' + import classNames from 'classnames/bind' import HomeSubtitle from '../home-subtitle' -import AverageMetricsChart, { AverageMetricsChartDataModel } from './average-metrics-chart' +import { AverageMetricsChartDataModel } from './average-metrics-chart' import styles from './styles.module.scss' +const AverageMetricsChart = dynamic(() => import('./average-metrics-chart'), { + ssr: false, + loading: () => <div>Loading...</div>, +}) + const cx = classNames.bind(styles) const AverageMetricsSection = () => { From c7ecaad0711331c85df291290d7a862110fcc25b Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 23 Nov 2024 17:25:04 +0900 Subject: [PATCH 283/648] =?UTF-8?q?feat:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_ui/top-strategy-card/index.tsx | 2 -- app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/(landing)/(home)/_ui/top-strategy-card/index.tsx b/app/(landing)/(home)/_ui/top-strategy-card/index.tsx index f4074126..61c86616 100644 --- a/app/(landing)/(home)/_ui/top-strategy-card/index.tsx +++ b/app/(landing)/(home)/_ui/top-strategy-card/index.tsx @@ -1,5 +1,3 @@ -'use client' - import { StarIcon } from '@/public/icons' import classNames from 'classnames/bind' diff --git a/app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss b/app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss index 867c7607..d1d9f731 100644 --- a/app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss +++ b/app/(landing)/(home)/_ui/top-strategy-card/styles.module.scss @@ -10,7 +10,7 @@ &.large { position: relative; flex-direction: column; - height: 340px; + height: 360px; .content-wrapper { .title { From 300f563e7bceaff2d71851bcedc3e927aca061b6 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 17:30:23 +0900 Subject: [PATCH 284/648] =?UTF-8?q?chore:=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 979 ++++++++++++++++++++++++++-------------------- 1 file changed, 544 insertions(+), 435 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09a9258d..ad1dee12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,11 @@ "version": "0.1.0", "dependencies": { "@tanstack/react-query": "^5.59.19", + "axios": "^1.7.7", "classnames": "^2.5.1", "highcharts": "^11.4.8", "highcharts-react-official": "^3.2.1", + "jwt-decode": "^4.0.0", "next": "14.2.16", "react": "^18", "react-dom": "^18", @@ -53,9 +55,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", - "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", + "integrity": "sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==", "dev": true, "license": "MIT" }, @@ -1398,6 +1400,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -3190,9 +3193,9 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.36.10", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.36.10.tgz", - "integrity": "sha512-GXrJgakgJW3DWKueebkvtYgGKkxA7s0u5B0P5syJM5rvQUnrpLPigvci8Hukl7yEM+sU06l+er2Fgvx/gmiRgg==", + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.1.tgz", + "integrity": "sha512-SvE+tSpcX884RJrPCskXxoS965Ky/pYABDEhWW6oeSRhpUDLrS5nTvT5n1LLSDVDYvty4imVmXsy+3/ROVuknA==", "dev": true, "license": "MIT", "dependencies": { @@ -3853,9 +3856,9 @@ "license": "MIT" }, "node_modules/@storybook/addon-actions": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.2.tgz", - "integrity": "sha512-+hA200XN5aeA4T3jq8IifQq6Y+9FyNQ0Q+blM1L0Tl7WLzBc7B1kHQnKvhSj5pvMSBWc/Q/kY7Ev5t9gdOu13g==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.4.5.tgz", + "integrity": "sha512-rbB19uiGJ61XHbKIbS1a9bUS6re5L8rT5NMNeEJhCxXRpFUPrlTXMSoD/Pgcn3ENeEMVZsm8/eCzxAVgAP3Mgg==", "dev": true, "license": "MIT", "dependencies": { @@ -3870,13 +3873,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-backgrounds": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.2.tgz", - "integrity": "sha512-s4uag5VKuk8q2MSnuNS7Sv+v1/mykzGPXe/zZRW2ammtkdHp8Uy78eQS2G0aiG02chXCX+qQgWMyy5QItDcTFQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.5.tgz", + "integrity": "sha512-FeMt4qHCMYDQiLGGDKiRuSPXFup2WXOaZSdL137v1W36wEL/vGkK1A5iQt1qJ8MZzL5WZQuedox8rSybFy7eow==", "dev": true, "license": "MIT", "dependencies": { @@ -3889,13 +3892,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-controls": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.2.tgz", - "integrity": "sha512-raCbHEj1xl4F3wKH6IdfEXNRaxKpY4QGhjSTE8Pte5iJSVhKG86taLqqRr+4dC7H1/LVMPU1XCGV4mkgDGtyxQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.4.5.tgz", + "integrity": "sha512-RVTtDDuESLYc1+SJQv2kI7wzBddzAS9uoEe8P75quN6S4pC0GxAB6xirWZ2+WOcba4eHosY+PxMwuBXQfH78Ew==", "dev": true, "license": "MIT", "dependencies": { @@ -3908,20 +3911,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-docs": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.2.tgz", - "integrity": "sha512-jIpykha7hv2Inlrq31ZoYg2QhuCuvcO+Q+uvhT45RDTB+2US/fg3rJINKlw2Djq8RPPOXvty5W0yvE6CrWKhnQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.4.5.tgz", + "integrity": "sha512-zPELIl7wXormOylVaaSpkUIuuCCxrO+OFPMKZnlENt6zSReyy0dJu4V0tzfV8FCw+V4D6Y4wrLRk/TIG951Ojw==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.4.2", - "@storybook/csf-plugin": "8.4.2", - "@storybook/react-dom-shim": "8.4.2", + "@storybook/blocks": "8.4.5", + "@storybook/csf-plugin": "8.4.5", + "@storybook/react-dom-shim": "8.4.5", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "ts-dedent": "^2.0.0" @@ -3931,25 +3934,25 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-essentials": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.2.tgz", - "integrity": "sha512-+/vfPrXM/GWU3Kbrg92PepwAZr7lOeulTTYF4THK0CL3DfUUlkGNpBPLP5PtjCuIkVrTCjXiIEdVWk47d5m2+w==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.4.5.tgz", + "integrity": "sha512-AxetQo/zSPIu3RZqWG2opwAz22Bb+jpf1nWbHp0kEpCrBemcWd8X2gonVmXNOC1PDKNl3jcWyc3lmg/+3mxjYg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-actions": "8.4.2", - "@storybook/addon-backgrounds": "8.4.2", - "@storybook/addon-controls": "8.4.2", - "@storybook/addon-docs": "8.4.2", - "@storybook/addon-highlight": "8.4.2", - "@storybook/addon-measure": "8.4.2", - "@storybook/addon-outline": "8.4.2", - "@storybook/addon-toolbars": "8.4.2", - "@storybook/addon-viewport": "8.4.2", + "@storybook/addon-actions": "8.4.5", + "@storybook/addon-backgrounds": "8.4.5", + "@storybook/addon-controls": "8.4.5", + "@storybook/addon-docs": "8.4.5", + "@storybook/addon-highlight": "8.4.5", + "@storybook/addon-measure": "8.4.5", + "@storybook/addon-outline": "8.4.5", + "@storybook/addon-toolbars": "8.4.5", + "@storybook/addon-viewport": "8.4.5", "ts-dedent": "^2.0.0" }, "funding": { @@ -3957,13 +3960,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-highlight": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.2.tgz", - "integrity": "sha512-vTtwp7nyJ09SXrsMnH+pukCjHjRMjQXgHZHxvbrv09uoH8ldQMv9B7u+X+9Wcy/jYSKFz/ng7pWo4b4a2oXHkg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.4.5.tgz", + "integrity": "sha512-sMA7v+4unaKY+5RDhow6lLncJqNX9ZLUnBIt3vzY1ntUsOYVwykAY1Hq4Ysj0luCBXjJJdJ6223ylrycnb7Ilw==", "dev": true, "license": "MIT", "dependencies": { @@ -3974,19 +3977,19 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-interactions": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.2.tgz", - "integrity": "sha512-+/NTENTApeOcONgFNQ6Olbk0GH3pTDG3w0eh00slCB+2agD1BcVKg8SSlHQV0lQF1cK3vWL/X3jeaxdFLYOjjg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.4.5.tgz", + "integrity": "sha512-s6R8XVD8LTp+LQTDbhtDjDLE6S44I7FtMLxPdMNwN9VEJjBk01NONLDuGDpNq5o/0bnybA3rMHk9+3afsgzidQ==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.4.2", - "@storybook/test": "8.4.2", + "@storybook/instrumenter": "8.4.5", + "@storybook/test": "8.4.5", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, @@ -3995,13 +3998,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-measure": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.2.tgz", - "integrity": "sha512-z+j6xQwcUBSpgzl1XDU+xU4YYgLraLMljECW7NvRNyJ/PYixvol8R3wtzWbr+CBpxmvbXjEJCPlF+EjF9/mBWQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.4.5.tgz", + "integrity": "sha512-+sNjew991YaoXQyWWloFybjEGrDO40Jk6w8BgZs2X7oc3D5t/6oFzvyC862U++LGqKFA3quXDeBjEb92CI9cRA==", "dev": true, "license": "MIT", "dependencies": { @@ -4013,13 +4016,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-onboarding": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.4.2.tgz", - "integrity": "sha512-zWzOyRASnIPt2AcaEl1KhI+aOaKDuoIcNB7u1GoABj0YM+V9d6o3lvcsmOAQG5pgwgFyqyOnLwpTfvRSEyzGFA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-8.4.5.tgz", + "integrity": "sha512-+FW50yVw2NMxYvk3uMpIberfkG4Sn0qRpiMse7MGHgTimtaJ0Mo1AUIrSfyIJCVTuxiWZud1a5DAnH0ybbWjjA==", "dev": true, "license": "MIT", "dependencies": { @@ -4030,13 +4033,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-outline": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.2.tgz", - "integrity": "sha512-oTMlPEyT4CBqzcQbfemoJzJ6yzeRAmvrAx9ssaBcnQQRsKxo0D2Ri/Jmm6SNcR0yBHxYRkvIH+2phLw8aiflCQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.4.5.tgz", + "integrity": "sha512-XlpN98AUDnWQWNFSFVm+HkRUzm3xIUMjBGTkv6HsL6zt6XoJ+LsQMca+PPtYqlBJA+5CU41xMDaG8HC/p+sd3A==", "dev": true, "license": "MIT", "dependencies": { @@ -4048,13 +4051,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-toolbars": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.2.tgz", - "integrity": "sha512-DidzW/NQS224niMJIjcJI2ls83emqygUcS9GYNGgdc5Xwro/TPgGYOXP2qnXgYUxXQTHbrxmIbHdEehxC7CcYQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.4.5.tgz", + "integrity": "sha512-hOq5560ONOU/qrslrwosWzxnC4nrF8HZWD43ciKwtethm8HuptU2M+Jrui1CRsMScEZLopWWVE9o0vJMdKpIFQ==", "dev": true, "license": "MIT", "funding": { @@ -4062,13 +4065,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/addon-viewport": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.2.tgz", - "integrity": "sha512-qVQ2UaxCNsUSFHnAAAizNPIJ/QwfMg7p5bBdpYROTZXJe+bxVp0rFzZmQgHZ3/sn+lzE4ItM4QEfxkfQUWi1ag==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.4.5.tgz", + "integrity": "sha512-l7Y41gIbJAsIN/QCg1QJ9sr61FLz1C/imUotcDej41tOHxUTSQOlXpNtVnfhUM1vGQc0yNpP3pVxj8BpXi0cAw==", "dev": true, "license": "MIT", "dependencies": { @@ -4079,13 +4082,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/blocks": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.2.tgz", - "integrity": "sha512-yAAvmOWaD8gIrepOxCh/RxQqd/1xZIwd/V+gsvAhW/thawN+SpI+zK63gmcqAPLX84hJ3Dh5pegRk0SoHNuDVA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.4.5.tgz", + "integrity": "sha512-Z+LHauSqm3A4HBR9pUEf9KQhD3/3xYMt0FXgA+GHCAyDa6lFeD1C6r9Y2nlT+9dt8gv9B9oygTZvV6GqFVyRSQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4100,7 +4103,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.2" + "storybook": "^8.4.5" }, "peerDependenciesMeta": { "react": { @@ -4112,13 +4115,13 @@ } }, "node_modules/@storybook/builder-webpack5": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.2.tgz", - "integrity": "sha512-Pqa0/sqqEujzcvs+/Cwf/5qRLC+atmceROCFokMOgpIaorTXlbmiQdJ2dBhMFNugLvXfL7dVQBjBfiuzhsQ57g==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.4.5.tgz", + "integrity": "sha512-5TSpirK2LIL4Wultpowlkrv3iAje57HTw92Hy6c4Zn64tAs30123mkdE6MoJcXMBfD4JwX9I2K2Q+ofZXblJPg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-webpack": "8.4.2", + "@storybook/core-webpack": "8.4.5", "@types/node": "^22.0.0", "@types/semver": "^7.3.4", "browser-assert": "^1.2.1", @@ -4149,7 +4152,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" }, "peerDependenciesMeta": { "typescript": { @@ -4158,9 +4161,9 @@ } }, "node_modules/@storybook/builder-webpack5/node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz", + "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==", "dev": true, "license": "MIT", "dependencies": { @@ -4168,9 +4171,9 @@ } }, "node_modules/@storybook/components": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.2.tgz", - "integrity": "sha512-+W59oF7D73LAxLNmCfFrfs98cH9pyNHK9HlJoO5/lKbK4IdWhhOoqUR/AJ3ueksoLuetFat4DxyE8SN1H4Bvrg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.4.5.tgz", + "integrity": "sha512-2PdnKfqNNv3sO7qILgWXiNvmLOi503oN9OMemNCQjTIvdvySc5JpS9/eClwcl/JfmE4qHdSHZr8dLLkBM9S7+Q==", "dev": true, "license": "MIT", "funding": { @@ -4182,9 +4185,9 @@ } }, "node_modules/@storybook/core": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.2.tgz", - "integrity": "sha512-hF8GWoUZTjwwuV5j4OLhMHZtZQL/NYcVUBReC2Ba06c8PkFIKqKZwATr1zKd301gQ5Qwcn9WgmZxJTMgdKQtOg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.4.5.tgz", + "integrity": "sha512-aB1sQNX5nRoUAqg5u1py0MuR/VPd6c6PhECa4rW6pmr7kZcfyP4PP6UFpXuN71ypTQlkRE3Vc5PQZ3gLhE9o3g==", "dev": true, "license": "MIT", "dependencies": { @@ -4214,9 +4217,9 @@ } }, "node_modules/@storybook/core-webpack": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.2.tgz", - "integrity": "sha512-bzGvzrLK/oDE9YlKayDEplcECURSa1oRkvV7rxI2sOTNfwuoxHJapvxFxazEKAHMVeSwfWDf4uKK0XeG2R/arA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.4.5.tgz", + "integrity": "sha512-IpK/3fM+l2WjRNplTtP+MtnRf/394GcBwyemZknUCzFFDJWNYAN1+meEZmOaZKzJ3tQyRYiErrJLHzd1+UH6Dw==", "dev": true, "license": "MIT", "dependencies": { @@ -4228,13 +4231,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/core-webpack/node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz", + "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==", "dev": true, "license": "MIT", "dependencies": { @@ -4252,9 +4255,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.2.tgz", - "integrity": "sha512-1f0t6W5xbC1sSAHHs3uXYPIQs2NXAEtIGqn6X9i3xbbub6hDS8PF8BIm7dOjQ8dZOPp7d9ltR64V5CoLlsOigA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.4.5.tgz", + "integrity": "sha512-qd2rQTglOTS+phQmTbNTXNjNyxdGvolaqHqDNMw3Vf6h9o3U+mLkwnDWNVnQ9oqvOoUEAqpBthgwzU9FhkIk+A==", "dev": true, "license": "MIT", "dependencies": { @@ -4265,7 +4268,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/global": { @@ -4290,9 +4293,9 @@ } }, "node_modules/@storybook/instrumenter": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.2.tgz", - "integrity": "sha512-gPYCZ/0O6gRLI3zmenu2N6QtKzxDZFdT2xf4RWcNUSZyp28RZkRCIgKFMt3fTmvE0yMzAjQyRSkBdrONjQ44HA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.4.5.tgz", + "integrity": "sha512-8qM35FkueuRpJr0zA6ENvhQICbo+iKL1ln450DwV1kKJtc41KdbA3CuCvtZ/FnoPsFnwdtPjhhICFtRt8LRTSg==", "dev": true, "license": "MIT", "dependencies": { @@ -4304,13 +4307,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/manager-api": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.2.tgz", - "integrity": "sha512-rhPc4cgQDKDH8NUyRh/ZaJW7QIhR/PO5MNX4xc+vz71sM2nO7ONA/FrgLtCuu4SULdwilEPvGefYvLK0dE+Caw==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.4.5.tgz", + "integrity": "sha512-t39JaMy3UX4StbUH/tIDcaflBDxTcyIq853wQtBMhVL3e1+Dw3MIiiG/5bw79HU4R7kSmPVLXIIbV3FmXkq7KQ==", "dev": true, "license": "MIT", "funding": { @@ -4322,9 +4325,9 @@ } }, "node_modules/@storybook/nextjs": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.4.2.tgz", - "integrity": "sha512-HySwS9zfenurk+O+SX9gKskotkHo8mFRBKAIlEROIWi7iipp5GCVPyqb8gFWjvN81dKfEIAZs+fB/7ySulJ4rg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/nextjs/-/nextjs-8.4.5.tgz", + "integrity": "sha512-KhP9XVI20iwAvMFHqvlV0x5UqzvMbD42QjSW5/2KYy52CStnczfa/3Xyb2VBgAMQx3Ony0qnqlTVHi6qhJQtOA==", "dev": true, "license": "MIT", "dependencies": { @@ -4342,10 +4345,10 @@ "@babel/preset-typescript": "^7.24.1", "@babel/runtime": "^7.24.4", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@storybook/builder-webpack5": "8.4.2", - "@storybook/preset-react-webpack": "8.4.2", - "@storybook/react": "8.4.2", - "@storybook/test": "8.4.2", + "@storybook/builder-webpack5": "8.4.5", + "@storybook/preset-react-webpack": "8.4.5", + "@storybook/react": "8.4.5", + "@storybook/test": "8.4.5", "@types/node": "^22.0.0", "@types/semver": "^7.3.4", "babel-loader": "^9.1.3", @@ -4378,10 +4381,10 @@ "sharp": "^0.33.3" }, "peerDependencies": { - "next": "^13.5.0 || ^14.0.0", + "next": "^13.5.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.2", + "storybook": "^8.4.5", "webpack": "^5.0.0" }, "peerDependenciesMeta": { @@ -4394,9 +4397,9 @@ } }, "node_modules/@storybook/nextjs/node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz", + "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==", "dev": true, "license": "MIT", "dependencies": { @@ -4404,14 +4407,14 @@ } }, "node_modules/@storybook/preset-react-webpack": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.2.tgz", - "integrity": "sha512-Gt9hQRo1ythGFzATNV4WgQDlMDzBgiq7ks+YkW2/Xu5ZkrRrM/gK75fhmbICrknZl2pPPfNFXlECPWKAeTmwFA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.5.tgz", + "integrity": "sha512-BKPAN7G0yFXfojQdF8tvgwVJ0ldcl6+p1JtAPAieH69BMGni3TEPnvPhkefRWcM8oM8pl+Hch/J2PLHiZ6QKNQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-webpack": "8.4.2", - "@storybook/react": "8.4.2", + "@storybook/core-webpack": "8.4.5", + "@storybook/react": "8.4.5", "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", "@types/node": "^22.0.0", "@types/semver": "^7.3.4", @@ -4433,7 +4436,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.2" + "storybook": "^8.4.5" }, "peerDependenciesMeta": { "typescript": { @@ -4442,9 +4445,9 @@ } }, "node_modules/@storybook/preset-react-webpack/node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz", + "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==", "dev": true, "license": "MIT", "dependencies": { @@ -4452,9 +4455,9 @@ } }, "node_modules/@storybook/preview-api": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.2.tgz", - "integrity": "sha512-5X/xvIvDPaWJKUBCo5zVeBbbjkhnwcI2KPkuOgrHVRRhuQ5WqD0RYxVtOOFNyQXme7g0nNl5RFNgvT7qv9qGeg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.4.5.tgz", + "integrity": "sha512-MKIZ2jQO/3cUdsT57eq8jRgB6inALo9BxrQ88f7mqzltOkMvADvTAY6y8JZqTUoDzWTH/ny/8SGGdtpqlxRuiQ==", "dev": true, "license": "MIT", "funding": { @@ -4466,18 +4469,18 @@ } }, "node_modules/@storybook/react": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.4.2.tgz", - "integrity": "sha512-rO5/aVKBVhIKENcL7G8ud4QKC5OyWBPCkJIvY6XUHIuhErJy9/4pP+sZ85jypVwx5kq+EqCPF8AEOWjIxB/4/Q==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.4.5.tgz", + "integrity": "sha512-2+p4aGEdGOnu2XNhnMi1B8GPeszm34P905HgqGD1cuz9gMt7x/bgZQaVxs6kpHZ3Hb6V9qp62La2dbAYatHdSw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/components": "8.4.2", + "@storybook/components": "8.4.5", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.4.2", - "@storybook/preview-api": "8.4.2", - "@storybook/react-dom-shim": "8.4.2", - "@storybook/theming": "8.4.2" + "@storybook/manager-api": "8.4.5", + "@storybook/preview-api": "8.4.5", + "@storybook/react-dom-shim": "8.4.5", + "@storybook/theming": "8.4.5" }, "engines": { "node": ">=18.0.0" @@ -4487,10 +4490,10 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/test": "8.4.2", + "@storybook/test": "8.4.5", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.2", + "storybook": "^8.4.5", "typescript": ">= 4.2.x" }, "peerDependenciesMeta": { @@ -4523,9 +4526,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.2.tgz", - "integrity": "sha512-FZVTM1f34FpGnf6e3MDIKkz05gmn8H9wEccvQAgr8pEFe8VWfrpVWeUrmatSAfgrCMNXYC1avDend8UX6IM8Fg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.4.5.tgz", + "integrity": "sha512-YTWTfPagptEYXJsnxAl3zP97Ev0zebtaEV0WgjGaEeumr+zsfgKKwzzHxgrtumBmDzwkuKlzFwlQB5A8keOIGA==", "dev": true, "license": "MIT", "funding": { @@ -4535,19 +4538,19 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/test": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.2.tgz", - "integrity": "sha512-MipTdboStv0hsqF2Sw8TZgP0YnxCcDYwxkTOd4hmRzev/7Brtvpi4pqjqh8k98ZCvhrCPAPVIoX5drk+oi3YUA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.4.5.tgz", + "integrity": "sha512-mHsRc6m60nfcEBsjvUkKz+Jnz0or4WH5jmJ1VL2pGKO4VzESCPqAwDnwDqP2YyeSQ0b/MAKUT5kdoLE2RE2eVw==", "dev": true, "license": "MIT", "dependencies": { "@storybook/csf": "^0.1.11", "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.4.2", + "@storybook/instrumenter": "8.4.5", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.5.0", "@testing-library/user-event": "14.5.2", @@ -4559,13 +4562,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.4.2" + "storybook": "^8.4.5" } }, "node_modules/@storybook/theming": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.2.tgz", - "integrity": "sha512-9j4fnu5LcV+qSs1rdwf61Bt14lms0T1LOZkHxGNcS1c1oH+cPS+sxECh2lxtni+mvOAHUlBs9pKhVZzRPdWpvg==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.4.5.tgz", + "integrity": "sha512-45e/jeG4iuqdZcHg3PbB6dwXQTwlnnEB7r/QcVExyC7ibrkTnjUfvxzyUw4mmU3CXETFGD5EcUobFkgK+/aPxQ==", "dev": true, "license": "MIT", "funding": { @@ -4581,6 +4584,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -4597,6 +4601,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -4613,6 +4618,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -4629,6 +4635,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -4645,6 +4652,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -4661,6 +4669,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -4677,6 +4686,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -4693,6 +4703,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -4709,6 +4720,7 @@ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", "dev": true, + "license": "MIT", "dependencies": { "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", @@ -4735,6 +4747,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -4750,37 +4763,12 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@svgr/core/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dev": true, - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@svgr/hast-util-to-babel-ast": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.21.3", "entities": "^4.4.0" @@ -4793,23 +4781,12 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@svgr/hast-util-to-babel-ast/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/@svgr/plugin-jsx": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -4832,6 +4809,7 @@ "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", "dev": true, + "license": "MIT", "dependencies": { "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", @@ -4848,37 +4826,12 @@ "@svgr/core": "*" } }, - "node_modules/@svgr/plugin-svgo/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dev": true, - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@svgr/webpack": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-transform-react-constant-elements": "^7.21.3", @@ -4914,9 +4867,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.59.20", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.20.tgz", - "integrity": "sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==", + "version": "5.60.6", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.60.6.tgz", + "integrity": "sha512-tI+k0KyCo1EBJ54vxK1kY24LWj673ujTydCZmzEZKAew4NqZzTaVQJEuaG1qKj2M03kUHN46rchLRd+TxVq/zQ==", "license": "MIT", "funding": { "type": "github", @@ -4924,12 +4877,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.59.20", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.20.tgz", - "integrity": "sha512-Zly0egsK0tFdfSbh5/mapSa+Zfc3Et0Zkar7Wo5sQkFzWyB3p3uZWOHR2wrlAEEV2L953eLuDBtbgFvMYiLvUw==", + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.61.0.tgz", + "integrity": "sha512-SBzV27XAeCRBOQ8QcC94w2H1Md0+LI0gTWwc3qRJoaGuewKn5FNW4LSqwPFJZVEItfhMfGT7RpZuSFXjTi12pQ==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.59.20" + "@tanstack/query-core": "5.60.6" }, "funding": { "type": "github", @@ -5162,6 +5115,7 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10.13.0" } @@ -5290,9 +5244,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", - "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", + "version": "20.17.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.7.tgz", + "integrity": "sha512-sZXXnpBFMKbao30dUAvzKbdwA2JM1fwUtVEq/kxKuPI5mMwZiRElCpTXb0Biq/LMEVpXDZL5G5V0RPnxKeyaYg==", "dev": true, "license": "MIT", "dependencies": { @@ -5370,17 +5324,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz", - "integrity": "sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz", + "integrity": "sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.14.0", - "@typescript-eslint/type-utils": "8.14.0", - "@typescript-eslint/utils": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0", + "@typescript-eslint/scope-manager": "8.15.0", + "@typescript-eslint/type-utils": "8.15.0", + "@typescript-eslint/utils": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -5404,16 +5358,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.14.0.tgz", - "integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", + "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.14.0", - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/typescript-estree": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0", + "@typescript-eslint/scope-manager": "8.15.0", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/typescript-estree": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0", "debug": "^4.3.4" }, "engines": { @@ -5433,14 +5387,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz", - "integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", + "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0" + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5451,14 +5405,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz", - "integrity": "sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz", + "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.14.0", - "@typescript-eslint/utils": "8.14.0", + "@typescript-eslint/typescript-estree": "8.15.0", + "@typescript-eslint/utils": "8.15.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -5469,6 +5423,9 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -5476,9 +5433,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz", - "integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", + "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", "dev": true, "license": "MIT", "engines": { @@ -5490,14 +5447,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz", - "integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", + "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/visitor-keys": "8.15.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -5549,16 +5506,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz", - "integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz", + "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.14.0", - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/typescript-estree": "8.14.0" + "@typescript-eslint/scope-manager": "8.15.0", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/typescript-estree": "8.15.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5569,17 +5526,22 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz", - "integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz", + "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.14.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.15.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5589,6 +5551,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "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/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -5642,9 +5617,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", - "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.5.tgz", + "integrity": "sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==", "dev": true, "license": "MIT", "dependencies": { @@ -5668,13 +5643,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", - "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz", + "integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.5", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -6324,6 +6299,12 @@ "dev": true, "license": "MIT" }, + "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/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -6350,6 +6331,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -6935,6 +6927,7 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -6943,9 +6936,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001683", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz", + "integrity": "sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==", "funding": [ { "type": "opencollective", @@ -7055,9 +7048,9 @@ } }, "node_modules/chromatic": { - "version": "11.18.0", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.18.0.tgz", - "integrity": "sha512-3o9Frn1oIS1hFLsJxVH9yVJ1O7+TCYoyL7OZzUorL/DCYduhXr5LDSBfpUsp7EdCPb64ufkbyFzSRNbt/xy9kg==", + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.18.1.tgz", + "integrity": "sha512-hkNT9vA6K9+PnE/khhZYBnRCOm8NonaQDs7RZ8YHFo7/lh1b/x/uFMkTjWjaj/mkM6QOR/evu5VcZMtcaauSlw==", "dev": true, "license": "MIT", "bin": { @@ -7089,14 +7082,17 @@ } }, "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.5.tgz", + "integrity": "sha512-xq7ICKB4TMHUx7Tz1L9O2SGKOhYMOTR32oir45Bq28/AQTpHogKgHcoYFSdRbMtddl+ozNXfXY9jWcgYKmde0w==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" } }, "node_modules/cjs-module-lexer": { @@ -7251,6 +7247,18 @@ "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/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -7346,20 +7354,30 @@ "license": "MIT" }, "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "license": "MIT", "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/create-ecdh": { @@ -7410,9 +7428,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "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": { @@ -7509,6 +7527,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -7555,6 +7574,7 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "license": "MIT", "dependencies": { "css-tree": "~2.2.0" }, @@ -7568,6 +7588,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -7581,7 +7602,8 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/csstype": { "version": "3.1.3", @@ -7749,6 +7771,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -7848,6 +7879,16 @@ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/domain-browser": { "version": "4.23.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.23.0.tgz", @@ -7924,16 +7965,16 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.56", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.56.tgz", - "integrity": "sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw==", + "version": "1.5.64", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz", + "integrity": "sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==", "dev": true, "license": "ISC" }, "node_modules/elliptic": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", - "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "dev": true, "license": "MIT", "dependencies": { @@ -7997,11 +8038,14 @@ } }, "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -8037,9 +8081,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", + "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8058,7 +8102,7 @@ "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", + "globalthis": "^1.0.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", @@ -8074,10 +8118,10 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", + "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", @@ -9284,12 +9328,32 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "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/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -9357,6 +9421,23 @@ "concat-map": "0.0.1" } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -9389,6 +9470,20 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -9922,6 +10017,16 @@ "entities": "^2.0.0" } }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -9990,9 +10095,9 @@ } }, "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", "devOptional": true, "license": "MIT" }, @@ -10762,6 +10867,15 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10793,9 +10907,9 @@ } }, "node_modules/lefthook": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.8.2.tgz", - "integrity": "sha512-lMXbcFHNDr+gzy/7ghuJDVB/Yyycj+ZL/7pN3Gm/s5Xqrc9+5sj3IrDAPylcEJ1cKCbUnXbwESrhhqpcYv4d4g==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.8.4.tgz", + "integrity": "sha512-XNyMaTWNRuADOaocYiHidgNkNDz8SCekpdNJ7lqceFcBT2zjumnb28/o7IMaNROpLBZdQkLkJXSeaQWGqn3kog==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10803,22 +10917,22 @@ "lefthook": "bin/index.js" }, "optionalDependencies": { - "lefthook-darwin-arm64": "1.8.2", - "lefthook-darwin-x64": "1.8.2", - "lefthook-freebsd-arm64": "1.8.2", - "lefthook-freebsd-x64": "1.8.2", - "lefthook-linux-arm64": "1.8.2", - "lefthook-linux-x64": "1.8.2", - "lefthook-openbsd-arm64": "1.8.2", - "lefthook-openbsd-x64": "1.8.2", - "lefthook-windows-arm64": "1.8.2", - "lefthook-windows-x64": "1.8.2" + "lefthook-darwin-arm64": "1.8.4", + "lefthook-darwin-x64": "1.8.4", + "lefthook-freebsd-arm64": "1.8.4", + "lefthook-freebsd-x64": "1.8.4", + "lefthook-linux-arm64": "1.8.4", + "lefthook-linux-x64": "1.8.4", + "lefthook-openbsd-arm64": "1.8.4", + "lefthook-openbsd-x64": "1.8.4", + "lefthook-windows-arm64": "1.8.4", + "lefthook-windows-x64": "1.8.4" } }, "node_modules/lefthook-darwin-arm64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.8.2.tgz", - "integrity": "sha512-g41SoFUv8SzHpG1NOPkHUEPhC1tJM5FF3Vo+HESmLmL9cDfd7JncHFPy59rVnC9Q8nOS0rvoik5HTq+3/wcfww==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.8.4.tgz", + "integrity": "sha512-OS5MsU0gvd8LYSpuQCHtmDUqwNrJ/LjCO0LGC1wNepY4OkuVl9DfX+rQ506CVUQYZiGVcwy2/qPOOBjNzA5+wQ==", "cpu": [ "arm64" ], @@ -10830,9 +10944,9 @@ ] }, "node_modules/lefthook-darwin-x64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.8.2.tgz", - "integrity": "sha512-IlCm4PrA/aAZ1MChiExYTbladC87GaxmYHOMHCeChLecqn+lypAuiYLgf7w5r2s3MjH5rbXImfU925NRKi6RXQ==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.8.4.tgz", + "integrity": "sha512-QLRsqK9aTMRcVW8qz4pzI2OWnGCEcaEPJlIiFjwstYsS+wfkooxOS0UkfVMjy+QoGgEcki+cxF/FoY7lE7DDtw==", "cpu": [ "x64" ], @@ -10844,9 +10958,9 @@ ] }, "node_modules/lefthook-freebsd-arm64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.8.2.tgz", - "integrity": "sha512-f7AIvuIEXUUR1ZutIFxjYKFDAVUBrdsLm+cbwOCjdfpJh7j2Fjg6nKXbDcglPXlX9Ix+nw9pHbJE2DAgzkI1Vw==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.8.4.tgz", + "integrity": "sha512-chnQ1m/Cmn9c0sLdk5HL2SToE5LBJv5uQMdH1IGRRcw+nEqWqrMnDXvM75caiJAyjmUGvPH3czKTJDzTFV1E+A==", "cpu": [ "arm64" ], @@ -10858,9 +10972,9 @@ ] }, "node_modules/lefthook-freebsd-x64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.8.2.tgz", - "integrity": "sha512-DSDL64fRLSNSWOa1y2bGXwXPiwU1fbAXpj63j6jeQ0jkgu6k+3XL/PBXKh80cI6MvCKz/KQKCtIencXZZ2Ua4Q==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.8.4.tgz", + "integrity": "sha512-KQi+WBUdnGLnK0rHOR58kbMH5TDVN1ZjZLu66Pv9FCG7Y7shR1qtaTXu+wmxdRhMvaLeQIXRsUEPjNRC66yMmA==", "cpu": [ "x64" ], @@ -10872,9 +10986,9 @@ ] }, "node_modules/lefthook-linux-arm64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.8.2.tgz", - "integrity": "sha512-sJ95X+ZH8ayIE7ApiGEq5ZF9KGA+eKiocJU+536bLbAIHw5WjGmv2x3llFqUxH/zAmLe3k542oZ4d84wEO0EGQ==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.8.4.tgz", + "integrity": "sha512-CXNcqIskLwTwQARidGdFqmNxpvOU3jsWPK4KA7pq2+QmlWJ64w98ebMvNBoUmRUCXqzmUm7Udf/jpfz2fobewQ==", "cpu": [ "arm64" ], @@ -10886,9 +11000,9 @@ ] }, "node_modules/lefthook-linux-x64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.8.2.tgz", - "integrity": "sha512-2eirc61M0WjlbSHamAgGf9iWsQTYz4IT6PAPm66vUaeG34+5D66xFicIV6pK2niRGUOPtNs8Kt4lboKtW+ba5g==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.8.4.tgz", + "integrity": "sha512-pVNITkFBxUCEtamWSM/res2Gd48+m9YKbNyIBndAuZVC5pKV5aGKZy2DNq6PWUPYiUDPx+7hoAtCJg/tlAiqhw==", "cpu": [ "x64" ], @@ -10900,9 +11014,9 @@ ] }, "node_modules/lefthook-openbsd-arm64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.8.2.tgz", - "integrity": "sha512-ZMop7htaSwP3MiL06WHUV36EX05N33o0WzNzC8NO5KEubn8Z74vbXcaq6qYezmgi+erkG6dtTnlbcZy5PFvFIA==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.8.4.tgz", + "integrity": "sha512-l+i/Dg5X36kYzhpMGSPE3rMbWy1KSytbLB9lY1PmxYb6LRH6iQTYIoxvLabVUwSBPSq8HtIFa50+bvC5+scfVA==", "cpu": [ "arm64" ], @@ -10914,9 +11028,9 @@ ] }, "node_modules/lefthook-openbsd-x64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.8.2.tgz", - "integrity": "sha512-jXFoxmpYXO6ZafgQJVvk3MYlRgOBJD3n7H8A1Bj1E2yrLzOhKevUKlTNwZTxQdxlnvoo33yD6SjVSujZavEGpw==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.8.4.tgz", + "integrity": "sha512-CqhDDPPX8oHzMLgNi/Reba823DRzj+eMNWQ8axvSiIG+zmG1w20xZH5QSs/mD3tjrND90yfDd90mWMt181qPyA==", "cpu": [ "x64" ], @@ -10928,9 +11042,9 @@ ] }, "node_modules/lefthook-windows-arm64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.8.2.tgz", - "integrity": "sha512-hsQUSk6kmB8E0UMD3Mk6ROoa7qv6XmigfPsn9tFjmbZ2aO+kpBfWitZ5v+gcjNp44yaECs3YTMIfv3QFwXlRCw==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.8.4.tgz", + "integrity": "sha512-dvpvorICmVjmw29Aiczg7DcaSzkd86bEBomiGq4UsAEk3+7ExLrlWJDLFsI6xLjMKmTxy+F7eXb2uDtuFC1N4g==", "cpu": [ "arm64" ], @@ -10942,9 +11056,9 @@ ] }, "node_modules/lefthook-windows-x64": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.8.2.tgz", - "integrity": "sha512-YypbMhvgAtkL7y+O3OlF81vwua7X4jloBz5hO3fILAuzaGAiPE1VbeuqRweV8VuKK4L/ZVVKqmpesygBUNDp9w==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.8.4.tgz", + "integrity": "sha512-e+y8Jt4/7PnoplhOuK48twjGVJEsU4T3J5kxD4mWfl6Cbit0YSn4bme9nW41eqCqTUqOm+ky29XlfnPHFX5ZNA==", "cpu": [ "x64" ], @@ -11111,9 +11225,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.13", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.13.tgz", + "integrity": "sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==", "dev": true, "license": "MIT", "dependencies": { @@ -11169,7 +11283,8 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/memfs": { "version": "3.5.3", @@ -11250,7 +11365,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -11260,7 +11374,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -11337,9 +11450,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.4.tgz", - "integrity": "sha512-Pm4LmWQeytDsNCR+A7gt39XAdtH6zQb6jnIKRig0FlvYOn8eksn3s1nXxUfz5KYUjbckof7Z4p2ewzgffPoCbg==", + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.6.tgz", + "integrity": "sha512-npfIIVRHKQX3Lw4aLWX4wBh+lQwpqdZNyJYB5K/+ktK8NhtkdsTxGK7WDrgknozcVyRI7TOqY6yBS9j2FTR+YQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -11348,7 +11461,7 @@ "@bundled-es-modules/statuses": "^1.0.1", "@bundled-es-modules/tough-cookie": "^0.1.6", "@inquirer/confirm": "^5.0.0", - "@mswjs/interceptors": "^0.36.5", + "@mswjs/interceptors": "^0.37.0", "@open-draft/deferred-promise": "^2.2.0", "@open-draft/until": "^2.1.0", "@types/cookie": "^0.6.0", @@ -11382,9 +11495,9 @@ } }, "node_modules/msw/node_modules/type-fest": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", - "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.27.0.tgz", + "integrity": "sha512-3IMSWgP7C5KSQqmo1wjhKrwsvXAtF33jO3QY+Uy++ia7hqvgSK6iXbbg5PbDBc1P2ZbNEDgejOrN4YooXvhwCw==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -12479,10 +12592,16 @@ "dev": true, "license": "MIT" }, + "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/psl": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", - "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.13.0.tgz", + "integrity": "sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw==", "dev": true, "license": "MIT", "dependencies": { @@ -12522,9 +12641,9 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", + "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -12875,16 +12994,16 @@ } }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -12900,9 +13019,9 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", - "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -13185,14 +13304,14 @@ } }, "node_modules/sass": { - "version": "1.80.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz", - "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==", + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.81.0.tgz", + "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.0", - "immutable": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -13548,6 +13667,7 @@ "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", "dev": true, + "license": "MIT", "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -13611,13 +13731,13 @@ } }, "node_modules/storybook": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.2.tgz", - "integrity": "sha512-GMCgyAulmLNrkUtDkCpFO4SB77YrpiIxq6e5tzaQdXEuaDu1mdNwOuP3VG7nE2FzxmqDvagSgriM68YW9iFaZA==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.4.5.tgz", + "integrity": "sha512-9tfgabXnMibYp3SvoaJXXMD63Pw0SA9Hnf5v6TxysCYZs4DZ/04fAkK+9RW+K4C5JkV83qXMMlrsPj766R47fg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core": "8.4.2" + "@storybook/core": "8.4.5" }, "bin": { "getstorybook": "bin/index.cjs", @@ -14045,13 +14165,15 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/svgo": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, + "license": "MIT", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -14077,6 +14199,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } @@ -14086,6 +14209,7 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -14102,6 +14226,7 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -14116,6 +14241,7 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -14131,6 +14257,7 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -14140,18 +14267,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/svgo/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/synckit": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", @@ -14409,14 +14524,15 @@ } }, "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", - "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", "tsconfig-paths": "^4.1.2" }, "engines": { @@ -14505,9 +14621,9 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", + "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", "dev": true, "license": "MIT", "dependencies": { @@ -14516,7 +14632,8 @@ "for-each": "^0.3.3", "gopd": "^1.0.1", "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.13", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -14526,18 +14643,18 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -14547,9 +14664,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -14638,9 +14755,9 @@ } }, "node_modules/unplugin": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.15.0.tgz", - "integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.0.tgz", + "integrity": "sha512-5liCNPuJW8dqh3+DM6uNM2EI3MLLpCKp/KY+9pB5M2S2SR2qvvDHhKgBOaTWEbZTAws3CXfB0rKTIolWKL05VQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14649,14 +14766,6 @@ }, "engines": { "node": ">=14.0.0" - }, - "peerDependencies": { - "webpack-sources": "^3" - }, - "peerDependenciesMeta": { - "webpack-sources": { - "optional": true - } } }, "node_modules/update-browserslist-db": { From ba364caf32ea90ef15a08da28383c0018541a46d Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 17:52:07 +0900 Subject: [PATCH 285/648] =?UTF-8?q?fix:=20pre-render=20=EB=AC=B8=EC=A0=9C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index d433ccd9..6dcd3028 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { useCallback, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' @@ -23,6 +23,7 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const SignInPage = () => { + const [isMounted, setIsMounted] = useState(false) const router = useRouter() const searchParams = useSearchParams() const loginMutation = useLogin() @@ -37,6 +38,10 @@ const SignInPage = () => { const [errors, setErrors] = useState<string | null>(null) const [isSubmitting, setIsSubmitting] = useState(false) + useEffect(() => { + setIsMounted(true) + }, []) + const validateForm = useCallback(() => { const emailError = validate('EMAIL', formData.email) if (emailError) { @@ -113,6 +118,10 @@ const SignInPage = () => { const isFormDisabled = isSubmitting || loginMutation.isPending + if (!isMounted) { + return null + } + return ( <div className={styles.container}> <div className={styles.loginBox}> From e78a8327d06fba3e75c6caf191de95d7ea388b60 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 18:05:33 +0900 Subject: [PATCH 286/648] =?UTF-8?q?fix:=20pre-render=20=ED=95=B4=EA=B2=B02?= =?UTF-8?q?=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index 6dcd3028..06bccbe0 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -1,7 +1,8 @@ 'use client' -import { useCallback, useEffect, useState } from 'react' +import { useCallback, useState } from 'react' +import dynamic from 'next/dynamic' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' @@ -23,7 +24,6 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const SignInPage = () => { - const [isMounted, setIsMounted] = useState(false) const router = useRouter() const searchParams = useSearchParams() const loginMutation = useLogin() @@ -38,10 +38,6 @@ const SignInPage = () => { const [errors, setErrors] = useState<string | null>(null) const [isSubmitting, setIsSubmitting] = useState(false) - useEffect(() => { - setIsMounted(true) - }, []) - const validateForm = useCallback(() => { const emailError = validate('EMAIL', formData.email) if (emailError) { @@ -118,10 +114,6 @@ const SignInPage = () => { const isFormDisabled = isSubmitting || loginMutation.isPending - if (!isMounted) { - return null - } - return ( <div className={styles.container}> <div className={styles.loginBox}> @@ -202,4 +194,6 @@ const SignInPage = () => { ) } -export default SignInPage +export default dynamic(() => Promise.resolve(SignInPage), { + ssr: false, +}) From e7862b2b5a9688c4f097fad87eb447c9f25fca2f Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 23 Nov 2024 20:49:12 +0900 Subject: [PATCH 287/648] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B2=84=ED=8A=BC=20boo?= =?UTF-8?q?lean=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80=20(#98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/traders-list-card/index.tsx | 24 +++++++++++-------- .../ui/traders-list-card/styles.module.scss | 3 +-- .../traders-list-card.stories.tsx | 11 +++++++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/shared/ui/traders-list-card/index.tsx b/shared/ui/traders-list-card/index.tsx index fe85ff35..055489d1 100644 --- a/shared/ui/traders-list-card/index.tsx +++ b/shared/ui/traders-list-card/index.tsx @@ -13,6 +13,7 @@ interface Props { strategyCount: number subscriberCount: number traderId: string + hasLinkButton?: boolean } const TradersListCard = ({ @@ -21,6 +22,7 @@ const TradersListCard = ({ strategyCount, subscriberCount, traderId, + hasLinkButton = true, }: Props) => { return ( <div className={cx('traders-list-card')}> @@ -36,16 +38,18 @@ const TradersListCard = ({ <Avatar src={profileImage} size="large" /> </div> </div> - <div className="link-button-wrapper"> - <LinkButton - href={'${PATH.TRADERS}/${traderId}'} - size="medium" - variant="filled" - className={cx('link-button')} - > - 전략 목록 상세보기 - </LinkButton> - </div> + {hasLinkButton && ( + <div className="link-button-wrapper"> + <LinkButton + href={'${PATH.TRADERS}/${traderId}'} + size="medium" + variant="filled" + className={cx('link-button')} + > + 전략 목록 상세보기 + </LinkButton> + </div> + )} </div> ) } diff --git a/shared/ui/traders-list-card/styles.module.scss b/shared/ui/traders-list-card/styles.module.scss index c2772ce5..572545de 100644 --- a/shared/ui/traders-list-card/styles.module.scss +++ b/shared/ui/traders-list-card/styles.module.scss @@ -4,8 +4,7 @@ background-color: $color-white; border-radius: 8px; width: 300px; - height: 220px; - padding: 32px 25px; + padding: 28px 28px; } .contents { diff --git a/shared/ui/traders-list-card/traders-list-card.stories.tsx b/shared/ui/traders-list-card/traders-list-card.stories.tsx index 12ead599..82e154c5 100644 --- a/shared/ui/traders-list-card/traders-list-card.stories.tsx +++ b/shared/ui/traders-list-card/traders-list-card.stories.tsx @@ -32,3 +32,14 @@ export const Default: StoryType = { traderId: '1234', }, } + +export const WithoutButton: StoryType = { + args: { + nickname: '고양이는야옹하고울지', + profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', + strategyCount: 10, + subscriberCount: 10, + traderId: '1234', + hasLinkButton: false, + }, +} From 11f843521b8f58ccabaae2b8bb28482209dbbe21 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 23 Nov 2024 21:00:36 +0900 Subject: [PATCH 288/648] =?UTF-8?q?fix:=20svg=20=ED=81=AC=EA=B8=B0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/check-box/styles.module.scss | 2 ++ shared/ui/logo/styles.module.scss | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/shared/ui/check-box/styles.module.scss b/shared/ui/check-box/styles.module.scss index be766af5..297c9661 100644 --- a/shared/ui/check-box/styles.module.scss +++ b/shared/ui/check-box/styles.module.scss @@ -11,6 +11,8 @@ justify-content: center; flex-shrink: 0; position: relative; + width: 20px; + height: 20px; &.checkedgray500 svg circle { fill: $color-gray-500; diff --git a/shared/ui/logo/styles.module.scss b/shared/ui/logo/styles.module.scss index 70e9f64b..f94d4854 100644 --- a/shared/ui/logo/styles.module.scss +++ b/shared/ui/logo/styles.module.scss @@ -3,8 +3,8 @@ align-items: center; gap: 12px; - svg { - width: 40px; + & > svg:first-child { + width: 62px; flex-shrink: 0; } } @@ -13,4 +13,4 @@ width: 142px; color: $color-gray-500; line-height: normal; -} +} \ No newline at end of file From edc24047e693d7d3a7d7093d67fc0992d3f0286b Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 23 Nov 2024 22:13:12 +0900 Subject: [PATCH 289/648] =?UTF-8?q?feat:=20Modal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=B6=94=EA=B0=80=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/index.tsx | 11 +++++------ shared/ui/modal/modal.stories.tsx | 8 +++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index 26ecce63..d9b0a957 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -1,7 +1,5 @@ 'use client' -import { ReactNode } from 'react' - import classNames from 'classnames/bind' import { createPortal } from 'react-dom' @@ -10,13 +8,14 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - icon?: ReactNode + icon?: React.ReactNode message?: string children?: React.ReactNode isOpen: boolean + className?: string } -const Modal = ({ icon, message, children, isOpen = false }: Props) => { +const Modal = ({ icon, message, children, isOpen = false, className }: Props) => { if (!isOpen) return null const modalRoot = document.getElementById('modal-root') @@ -26,9 +25,9 @@ const Modal = ({ icon, message, children, isOpen = false }: Props) => { return createPortal( <> <div className={cx('overlay')}></div> - <div className={cx('modal')}> + <div className={cx('modal', className)}> <div className={cx('icon')}>{icon}</div> - <p className={cx('contents')}>{message}</p> + <p className={cx('message')}>{message}</p> {children} </div> </>, diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index 6c043756..5cff4d25 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -35,7 +35,9 @@ const ModalStory = ({ message, icon }: { message?: string; icon?: React.ReactNod return ( <div style={{ padding: '20px' }}> <Button onClick={() => setIsOpen(true)}>모달 열기</Button> - <Modal message={message} icon={icon} isOpen={isOpen} /> + <Modal message={message} icon={icon} isOpen={isOpen}> + <Button onClick={() => setIsOpen(false)}>닫기</Button> + </Modal> </div> ) } @@ -61,3 +63,7 @@ export const SubscribeIcon: StoryType = { export const PlusIcon: StoryType = { render: () => <ModalStory message="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, } + +export const CloseButton: StoryType = { + render: () => <ModalStory message="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, +} From 471eafff03318c564b94487cd53f556307bf19f2 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 23 Nov 2024 22:24:10 +0900 Subject: [PATCH 290/648] =?UTF-8?q?refactor:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B0=B1=ED=8B=B1=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=86=8D=EC=84=B1=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD=20(#98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/traders-list-card/index.tsx | 9 +++++---- .../ui/traders-list-card/traders-list-card.stories.tsx | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shared/ui/traders-list-card/index.tsx b/shared/ui/traders-list-card/index.tsx index 055489d1..7c53e6c4 100644 --- a/shared/ui/traders-list-card/index.tsx +++ b/shared/ui/traders-list-card/index.tsx @@ -1,5 +1,6 @@ import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' import Avatar from '@/shared/ui/avatar' import { LinkButton } from '@/shared/ui/link-button' @@ -13,7 +14,7 @@ interface Props { strategyCount: number subscriberCount: number traderId: string - hasLinkButton?: boolean + hasButton?: boolean } const TradersListCard = ({ @@ -22,7 +23,7 @@ const TradersListCard = ({ strategyCount, subscriberCount, traderId, - hasLinkButton = true, + hasButton = true, }: Props) => { return ( <div className={cx('traders-list-card')}> @@ -38,10 +39,10 @@ const TradersListCard = ({ <Avatar src={profileImage} size="large" /> </div> </div> - {hasLinkButton && ( + {hasButton && ( <div className="link-button-wrapper"> <LinkButton - href={'${PATH.TRADERS}/${traderId}'} + href={`${PATH.TRADERS}/${traderId}`} size="medium" variant="filled" className={cx('link-button')} diff --git a/shared/ui/traders-list-card/traders-list-card.stories.tsx b/shared/ui/traders-list-card/traders-list-card.stories.tsx index 82e154c5..9e786c63 100644 --- a/shared/ui/traders-list-card/traders-list-card.stories.tsx +++ b/shared/ui/traders-list-card/traders-list-card.stories.tsx @@ -40,6 +40,6 @@ export const WithoutButton: StoryType = { strategyCount: 10, subscriberCount: 10, traderId: '1234', - hasLinkButton: false, + hasButton: false, }, } From d0e6e2197e9b46633ec1e5c49270defe053f1ae0 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sat, 23 Nov 2024 22:48:53 +0900 Subject: [PATCH 291/648] =?UTF-8?q?chore:=20Modal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EC=A0=9C=EA=B1=B0=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/modal.stories.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index 5cff4d25..aebb1ced 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -63,7 +63,3 @@ export const SubscribeIcon: StoryType = { export const PlusIcon: StoryType = { render: () => <ModalStory message="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, } - -export const CloseButton: StoryType = { - render: () => <ModalStory message="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, -} From 7ab56d4711ba3950d55d20da5abbdee8c7aac9af Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Sat, 23 Nov 2024 23:25:54 +0900 Subject: [PATCH 292/648] =?UTF-8?q?fix:=20header=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EA=B0=80=20=EB=8B=A4=EB=A5=B8=20=ED=85=8C?= =?UTF-8?q?=EA=B7=B8=EB=A1=9C=20=EB=9E=9C=EB=8D=94=EB=90=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=ED=99=95=EC=9E=A5=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/shared/ui/header/index.tsx b/shared/ui/header/index.tsx index 0428d2bb..1e74a4f0 100644 --- a/shared/ui/header/index.tsx +++ b/shared/ui/header/index.tsx @@ -1,4 +1,4 @@ -import { CSSProperties, ReactNode } from 'react' +import { CSSProperties, ElementType, ReactNode } from 'react' import classNames from 'classnames/bind' @@ -9,15 +9,16 @@ const cx = classNames.bind(styles) interface Props { Left?: ReactNode Right?: ReactNode + as?: ElementType styles?: CSSProperties } -const Header = ({ Left, Right, styles }: Props) => { +const Header = ({ Left, Right, as: Component = 'header', styles }: Props) => { return ( - <header className={cx('container')} style={styles}> + <Component className={cx('container')} style={styles}> {Left} {Right} - </header> + </Component> ) } From 45af75fffc276232828fbc2a0c14b3dbfe595a21 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Sat, 23 Nov 2024 23:26:17 +0900 Subject: [PATCH 293/648] =?UTF-8?q?feat:=20admin=20=EA=B3=B5=EC=A7=80=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EB=94=94=EC=9E=90=EC=9D=B8=20=ED=8B=80=20?= =?UTF-8?q?=EC=9E=A1=EC=9D=8C=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/_ui/admin-header/index.tsx | 31 ++++++++++++++ app/admin/notices/page.module.scss | 3 ++ app/admin/notices/page.tsx | 63 +++++++++++++++++++++++++++- app/admin/notices/tabledata.tsx | 7 ++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 app/admin/_ui/admin-header/index.tsx create mode 100644 app/admin/notices/page.module.scss create mode 100644 app/admin/notices/tabledata.tsx diff --git a/app/admin/_ui/admin-header/index.tsx b/app/admin/_ui/admin-header/index.tsx new file mode 100644 index 00000000..a45bbd16 --- /dev/null +++ b/app/admin/_ui/admin-header/index.tsx @@ -0,0 +1,31 @@ +import { CSSProperties, ReactNode } from 'react' + +import Header from '@/shared/ui/header' +import { SearchInput } from '@/shared/ui/search-input' + +interface Props { + Left?: ReactNode + Right?: ReactNode + styles?: CSSProperties +} + +const headerStyles: CSSProperties = { + position: 'static', + padding: '0', + height: 'calc(40px * 2 + 18px)', + // font B3 + fontSize: '14px', + fontWeight: '600', + lineHeight: '132%', + letterSpacing: '-0.28px', +} + +const AdminContentsHeader = ({ Left, Right }: Props) => { + return <Header as="div" Left={Left} Right={Right} styles={headerStyles} /> +} + +const Search = () => { + return <SearchInput placeholder="제목을 검색하세요." /> +} + +export default Object.assign(AdminContentsHeader, { Search }) diff --git a/app/admin/notices/page.module.scss b/app/admin/notices/page.module.scss new file mode 100644 index 00000000..9dfc496a --- /dev/null +++ b/app/admin/notices/page.module.scss @@ -0,0 +1,3 @@ +.color-primary-500 { + color: $color-orange-500; +} diff --git a/app/admin/notices/page.tsx b/app/admin/notices/page.tsx index e1304ad7..2a5386b0 100644 --- a/app/admin/notices/page.tsx +++ b/app/admin/notices/page.tsx @@ -1,5 +1,66 @@ +'use client' + +// TODO: ssr +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import Pagination from '@/shared/ui/pagination' +import VerticalTable from '@/shared/ui/table/vertical' +import Title from '@/shared/ui/title' + +import AdminContentsHeader from '../_ui/admin-header' +import styles from './page.module.scss' +import { TABLE_DATA } from './tabledata' + +const cx = classNames.bind(styles) + const AdminNoticesPage = () => { - return <></> + return ( + <> + {/* TODO: inline css 제거 */} + <Title label="공지사항" style={{ margin: '80px 0 26px 12.6px' }} /> + <div + style={{ + padding: '0 45px 37px', + borderRadius: '8px', + marginBottom: '42px', + backgroundColor: 'aliceblue', + }} + > + <AdminContentsHeader + // TODO: 실제 개수로 바인딩 + Left={ + <span> + 총 <span className={cx('color-primary-500')}>30</span>명 + </span> + } + Right={<AdminContentsHeader.Search />} + /> + <VerticalTable + tableHead={['No.', '제목', '등록일', '작성일', '']} + tableBody={TABLE_DATA.map((d) => ({ + ...d, + content: ( + <Button.ButtonGroup> + {/* TODO: onclick 로직 정의 */} + <Button size="small" onClick={() => {}}> + 수정 + </Button> + <Button size="small" onClick={() => {}} variant="filled"> + 삭제 + </Button> + </Button.ButtonGroup> + ), + }))} + // TODO: 실제 값으로 추가 + countPerPage={10} + currentPage={1} + /> + {/* TODO: 실제 값으로 추가 */} + <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + </div> + </> + ) } export default AdminNoticesPage diff --git a/app/admin/notices/tabledata.tsx b/app/admin/notices/tabledata.tsx new file mode 100644 index 00000000..124113d9 --- /dev/null +++ b/app/admin/notices/tabledata.tsx @@ -0,0 +1,7 @@ +export const TABLE_DATA = Array.from({ length: 30 }, (_, i) => ({ + idx: i, + title: `제목 ${i + 1}`, + createdAt: new Intl.DateTimeFormat('ko-KR').format(new Date(2024, 11, i + 1)), + updatedAt: new Intl.DateTimeFormat('ko-KR').format(new Date(2024, 11, i + 2)), + content: <div>내용 ${i + 1}</div>, +})) From ba17e04d3746bdda722d9442286e848b18d14cef Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 24 Nov 2024 00:41:39 +0900 Subject: [PATCH 294/648] =?UTF-8?q?refactor:=20Input=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=ED=81=AC=EA=B8=B0=20=EC=A7=80=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20(#107)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/index.tsx | 11 ++++++++++- shared/ui/search-input/index.tsx | 16 ++++++++++++++-- shared/ui/search-input/styles.module.scss | 4 ++++ shared/ui/textarea/index.tsx | 10 ++++++++-- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index bd5be9af..83e4dae6 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -17,10 +17,19 @@ interface Props extends ComponentProps<'input'> { errorMessage?: ErrorMessageType | null } -export const Input = ({ inputSize = 'medium', errorMessage, className, ...props }: Props) => { +export const Input = ({ + inputSize = 'medium', + errorMessage, + className, + value, + onChange, + ...props +}: Props) => { return ( <div> <input + value={value} + onChange={onChange} className={cx('input', inputSize, className, { error: !!errorMessage, })} diff --git a/shared/ui/search-input/index.tsx b/shared/ui/search-input/index.tsx index 555fb67b..992b2bb4 100644 --- a/shared/ui/search-input/index.tsx +++ b/shared/ui/search-input/index.tsx @@ -14,10 +14,22 @@ interface Props extends ComponentProps<'input'> { handleSearchIconClick?: () => void } -export const SearchInput = ({ placeholder = '', handleSearchIconClick, ...props }: Props) => { +export const SearchInput = ({ + placeholder = '', + handleSearchIconClick, + value, + onChange, + ...props +}: Props) => { return ( <div className={cx('search-input-container')}> - <input placeholder={placeholder} className={cx('search-input')} {...props} /> + <input + value={value} + onChange={onChange} + placeholder={placeholder} + className={cx('search-input')} + {...props} + /> <SearchIcon className={cx('search-icon')} onClick={handleSearchIconClick} /> </div> ) diff --git a/shared/ui/search-input/styles.module.scss b/shared/ui/search-input/styles.module.scss index 9f632026..a2cf7c0a 100644 --- a/shared/ui/search-input/styles.module.scss +++ b/shared/ui/search-input/styles.module.scss @@ -3,6 +3,10 @@ display: inline-block; width: 240px; height: 30px; + + svg { + width: 24px; + } } .search-input { diff --git a/shared/ui/textarea/index.tsx b/shared/ui/textarea/index.tsx index dad3080f..290ca6d2 100644 --- a/shared/ui/textarea/index.tsx +++ b/shared/ui/textarea/index.tsx @@ -12,10 +12,16 @@ interface Props extends ComponentProps<'textarea'> { rows?: number } -export const Textarea = ({ rows = 5, className, ...props }: Props) => { +export const Textarea = ({ rows = 5, className, value, onChange, ...props }: Props) => { return ( <div> - <textarea rows={rows} className={cx('textarea', className)} {...props} /> + <textarea + value={value} + onChange={onChange} + rows={rows} + className={cx('textarea', className)} + {...props} + /> </div> ) } From b0eb70c0b5e357b0b5c5f3f5499e7342a06dda66 Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 24 Nov 2024 00:42:52 +0900 Subject: [PATCH 295/648] =?UTF-8?q?feat:=20admin=20=EA=B3=B5=EC=A7=80=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=94=94=EC=9E=90=EC=9D=B8=20=ED=8B=80=20?= =?UTF-8?q?=EC=9E=A1=EC=9D=8C=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/post/_ui/input-field/index.tsx | 33 ++++++++++ .../post/_ui/input-field/styles.module.scss | 16 +++++ app/admin/notices/post/page.tsx | 62 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 app/admin/notices/post/_ui/input-field/index.tsx create mode 100644 app/admin/notices/post/_ui/input-field/styles.module.scss create mode 100644 app/admin/notices/post/page.tsx diff --git a/app/admin/notices/post/_ui/input-field/index.tsx b/app/admin/notices/post/_ui/input-field/index.tsx new file mode 100644 index 00000000..145cf5f1 --- /dev/null +++ b/app/admin/notices/post/_ui/input-field/index.tsx @@ -0,0 +1,33 @@ +'use client' + +import classNames from 'classnames/bind' + +import { Input } from '@/shared/ui/input' +import { Textarea } from '@/shared/ui/textarea' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + label: string + inputType: JSX.IntrinsicElements['input']['type'] | 'textArea' + placeholder?: string +} + +const InputField = ({ label, inputType, placeholder }: Props) => { + return ( + <label className={cx('container')}> + <span className={cx('label')}>{label}</span> + <div className={cx('input')}> + {inputType !== 'textArea' ? ( + <Input type={inputType} inputSize="full" placeholder={placeholder} /> + ) : ( + <Textarea rows={26} placeholder={placeholder} /> + )} + </div> + </label> + ) +} + +export default InputField diff --git a/app/admin/notices/post/_ui/input-field/styles.module.scss b/app/admin/notices/post/_ui/input-field/styles.module.scss new file mode 100644 index 00000000..38b9c9fd --- /dev/null +++ b/app/admin/notices/post/_ui/input-field/styles.module.scss @@ -0,0 +1,16 @@ +.container { + display: flex; + justify-content: space-between; +} + +.label { + @include typo-b1; +} + +.input { + color: $color-gray-800; + width: 640px; +} + +// TODO : textarea palceholder 중앙에 오도록하기 +// TODO : file input UI 고치기 diff --git a/app/admin/notices/post/page.tsx b/app/admin/notices/post/page.tsx new file mode 100644 index 00000000..349e3519 --- /dev/null +++ b/app/admin/notices/post/page.tsx @@ -0,0 +1,62 @@ +'use client' + +import { Button } from '@/shared/ui/button' +import BackHeader from '@/shared/ui/header/back-header' +import Title from '@/shared/ui/title' + +import InputField from './_ui/input-field' + +const AdminNoticePostPage = () => { + return ( + <> + <BackHeader label="공지사항으로 돌아가기" /> + {/* TODO: inline css 제거 */} + <Title label="공지사항 등록" style={{ margin: '0 0 26px 12.6px' }} /> + <div + style={{ + padding: '0 45px 37px', + borderRadius: '8px', + marginBottom: '42px', + backgroundColor: 'aliceblue', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }} + > + {/* TODO: 디자인 계속 바뀌는 느낌이라서 일단 가운데 정렬 해둠 */} + <div + style={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }} + > + <div + style={{ + width: '795px', + backgroundColor: 'aliceblue', + display: 'flex', + flexDirection: 'column', + gap: '30px', + }} + > + <InputField label="제목" inputType="text" placeholder="제목을 입력하세요." /> + <InputField label="내용" inputType="textArea" placeholder="내용을 입력하세요." /> + <InputField label="파일첨부" inputType="file" /> + </div> + <Button.ButtonGroup> + {/* TODO: onclick 로직 정의 */} + <Button size="small" onClick={() => {}}> + 수정 + </Button> + <Button size="small" onClick={() => {}} variant="filled"> + 삭제 + </Button> + </Button.ButtonGroup> + </div> + </div> + </> + ) +} + +export default AdminNoticePostPage From dbd8d0f40ca1220792181d1c16a61ad193e8b29a Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 24 Nov 2024 02:14:30 +0900 Subject: [PATCH 296/648] =?UTF-8?q?design:=20Input=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EB=B3=80=EA=B2=BD=20=ED=9B=84=20SCSS=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95=20(#107)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/styles.module.scss | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss index 13358628..e50111bc 100644 --- a/shared/ui/input/styles.module.scss +++ b/shared/ui/input/styles.module.scss @@ -1,6 +1,6 @@ .input { color: $color-gray-400; - border: 1px solid $color-gray-300; + border: 1px solid $color-gray-400; border-radius: 2px; height: 48px; padding: 14px 24px; @@ -8,12 +8,12 @@ @include typo-b3; &:focus { - color: $color-gray-500; - border-color: $color-gray-500; + color: $color-gray-400; + border-color: $color-gray-700; } &.error { - border-color: $color-orange-500; + border-color: $color-orange-700; } &.small { @@ -30,19 +30,17 @@ height: 100%; } &:disabled { - color: $color-gray-200; - border-color: $color-gray-200; - background-color: $color-gray-100; + color: $color-gray-500; + border-color: $color-gray-400; + background-color: $color-gray-200; cursor: not-allowed; } - &:required { - border-color: $color-indigo; - } } .error-message { - color: $color-orange-500; + color: $color-orange-700; margin-top: 10px; margin-bottom: 10px; - @include typo-b3; + font-size: $text-b3; + font-weight: $text-medium; } From 717a7a6fe49f827c9efacb207743df36bc596790 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Sun, 24 Nov 2024 02:18:33 +0900 Subject: [PATCH 297/648] =?UTF-8?q?design:=20Search=20Input=EA=B3=BC=20Tex?= =?UTF-8?q?tarea=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=ED=9B=84=20SCSS=20=EC=83=89=EC=83=81=20=EB=B3=80=EA=B2=BD=20(#?= =?UTF-8?q?107)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/search-input/styles.module.scss | 6 +++--- shared/ui/textarea/styles.module.scss | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/shared/ui/search-input/styles.module.scss b/shared/ui/search-input/styles.module.scss index a2cf7c0a..e1a18e25 100644 --- a/shared/ui/search-input/styles.module.scss +++ b/shared/ui/search-input/styles.module.scss @@ -13,7 +13,7 @@ padding: 8px 12px; padding-right: 30px; border-radius: 4px; - border: 1px solid $color-gray-300; + border: 1px solid $color-gray-400; color: $color-gray-400; width: 100%; height: 100%; @@ -21,8 +21,8 @@ @include typo-c1; &:focus { - color: $color-gray-500; - border-color: $color-gray-500; + color: $color-gray-400; + border-color: $color-gray-700; } } diff --git a/shared/ui/textarea/styles.module.scss b/shared/ui/textarea/styles.module.scss index 1e2d8037..c81af07c 100644 --- a/shared/ui/textarea/styles.module.scss +++ b/shared/ui/textarea/styles.module.scss @@ -1,19 +1,19 @@ .textarea { padding: 8px 12px; border-radius: 2px; - border: 1px solid $color-gray-300; + border: 1px solid $color-gray-400; color: $color-gray-400; width: 100%; height: 100%; @include typo-b3; &:focus { - color: $color-gray-500; - border-color: $color-gray-500; + color: $color-gray-400; + border-color: $color-gray-700; } &.focused { - color: $color-gray-500; - border-color: $color-gray-500; + color: $color-gray-400; + border-color: $color-gray-700; } } From f889320979dcf8740c423aa0fafe18351ff7d792 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 24 Nov 2024 09:30:39 +0900 Subject: [PATCH 298/648] =?UTF-8?q?design:=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=20padding=20=EC=82=AD=EC=A0=9C=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/global.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/styles/global.scss b/shared/styles/global.scss index fd350584..aa42bef9 100644 --- a/shared/styles/global.scss +++ b/shared/styles/global.scss @@ -36,7 +36,7 @@ textarea { width: 100%; max-width: $max-width; margin: 0 auto; - padding: 80px 20px 0; + padding: 0 20px; &::before { content: ''; From a74b35e3ce7e80b861a61b459e2664000c6571d2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 24 Nov 2024 17:31:00 +0900 Subject: [PATCH 299/648] =?UTF-8?q?fix:=20=EA=B2=BD=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_ui/strategy-list/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index bbb382c7..a39a73f4 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -2,12 +2,12 @@ import { useRouter, useSearchParams } from 'next/navigation' +import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import classNames from 'classnames/bind' import { useMSWStore } from '@/shared/stores/msw' import Pagination from '@/shared/ui/pagination' -import StrategiesItem from '../../../../(dashboard)/_ui/strategies-item' import useGetStrategiesData from '../../_hooks/query/use-get-strategies-data' import ListHeader from '../list-header' import styles from './styles.module.scss' From 190e97784487b30f01a8af28e30146e16eb1a4cd Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 24 Nov 2024 23:10:54 +0900 Subject: [PATCH 300/648] =?UTF-8?q?rename:=20[strategyId]=ED=95=98?= =?UTF-8?q?=EC=9C=84=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => [strategyId]}/_api/get-details-information.ts | 3 ++- .../_hooks/query/use-get-details-information-data.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) rename app/(dashboard)/strategies/{ => [strategyId]}/_api/get-details-information.ts (92%) rename app/(dashboard)/strategies/{ => [strategyId]}/_hooks/query/use-get-details-information-data.ts (80%) diff --git a/app/(dashboard)/strategies/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts similarity index 92% rename from app/(dashboard)/strategies/_api/get-details-information.ts rename to app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index 2b6d6ee8..3f904ba3 100644 --- a/app/(dashboard)/strategies/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -1,6 +1,7 @@ -import { InformationType } from '@/app/(dashboard)/strategies/[strategyId]/page' import axios from 'axios' +import { InformationType } from '../page' + const getDetailsInformation = async (isReady: boolean, strategyId: string) => { if (!isReady || !strategyId) return diff --git a/app/(dashboard)/strategies/_hooks/query/use-get-details-information-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts similarity index 80% rename from app/(dashboard)/strategies/_hooks/query/use-get-details-information-data.ts rename to app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts index 4348ca45..4ddd8aa1 100644 --- a/app/(dashboard)/strategies/_hooks/query/use-get-details-information-data.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts @@ -1,6 +1,7 @@ -import getDetailsInformation from '@/app/(dashboard)/strategies/_api/get-details-information' import { useQuery } from '@tanstack/react-query' +import getDetailsInformation from '../../_api/get-details-information' + interface Props { isReady: boolean strategyId: string From a3527845ea49d9f5cba34155606aefedf5fc3a07 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 24 Nov 2024 23:12:25 +0900 Subject: [PATCH 301/648] =?UTF-8?q?design:=20medium=20font=20size=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/total-star/styles.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/total-star/styles.module.scss b/shared/ui/total-star/styles.module.scss index 09e01e30..f5e4151e 100644 --- a/shared/ui/total-star/styles.module.scss +++ b/shared/ui/total-star/styles.module.scss @@ -8,7 +8,7 @@ @include typo-c1; } &.medium { - @include typo-b2; + @include typo-b3; } &.black { color: $color-gray-800; From 59653213d6afb3b0b48fa2c1343f2c1a7887522b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 24 Nov 2024 23:16:15 +0900 Subject: [PATCH 302/648] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/review-container/add-review.tsx | 44 ++++++++++ .../_ui/review-container/index.tsx | 51 ++++++++++++ .../_ui/review-container/review-item.tsx | 51 ++++++++++++ .../_ui/review-container/review-list.tsx | 58 +++++++++++++ .../_ui/review-container/styles.module.scss | 82 +++++++++++++++++++ .../strategies/[strategyId]/page.tsx | 8 +- 6 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx new file mode 100644 index 00000000..6933368b --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx @@ -0,0 +1,44 @@ +'use client' + +import { useRef, useState } from 'react' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import { Textarea } from '@/shared/ui/textarea' + +import StarRating from '../star-rating/index' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const AddReview = () => { + const [starRatingValue, setStarRatingValue] = useState(0) + const textareaRef = useRef<HTMLTextAreaElement>(null) + + const handleStarRating = (idx: number) => setStarRatingValue(idx + 1) + + const handleAddReview = () => {} + + return ( + <div className={cx('add-review-wrapper')}> + <div className={cx('textarea-wrapper')}> + <Textarea rows={5} placeholder="리뷰를 작성해주세요." ref={textareaRef} /> + </div> + <div className={cx('add-button-wrapper')}> + <p>전략이 어땠나요?</p> + <StarRating starRatingValue={starRatingValue} onRatingChange={handleStarRating} /> + <Button + variant="filled" + size="small" + className={cx('review-button')} + onClick={handleAddReview} + > + 리뷰 등록하기 + </Button> + </div> + </div> + ) +} + +export default AddReview diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx new file mode 100644 index 00000000..9ca85d9e --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx @@ -0,0 +1,51 @@ +'use client' + +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import { useMSWStore } from '@/shared/stores/msw' +import TotalStar from '@/shared/ui/total-star' + +import useGetReviewsData from '../../_hooks/query/use-get-reviews-data' +import AddReview from './add-review' +import ReviewList from './review-list' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + strategyId: string +} + +const ReviewContainer = ({ strategyId }: Props) => { + const [currentPage, setCurrentPage] = useState(1) + const isReady = useMSWStore((state) => state.isReady) + const { data: reviewData } = useGetReviewsData({ isReady, strategyId, page: currentPage }) + + return ( + <> + {reviewData && ( + <div className={cx('container')}> + <div className={cx('title-wrapper')}> + <p className={cx('review-title')}>리뷰</p> + <TotalStar + size="medium" + averageRating={reviewData.averageRating} + totalElements={reviewData.reviews.totalElements} + /> + </div> + <AddReview /> + <ReviewList + reviews={reviewData.reviews.content} + totalReview={reviewData.reviews.totalElements} + currentPage={currentPage} + setCurrentPage={setCurrentPage} + /> + </div> + )} + </> + ) +} + +export default ReviewContainer diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx new file mode 100644 index 00000000..a2b93e1d --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx @@ -0,0 +1,51 @@ +import classNames from 'classnames/bind' + +import Avatar from '@/shared/ui/avatar' + +import StarRating from '../star-rating/index' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + nickname: string + content: string + profileImage?: string + createdAt: string + starRating: number + isReviewer: boolean +} + +const ReviewItem = ({ + nickname, + profileImage, + createdAt, + starRating, + content, + isReviewer, +}: Props) => { + const editedCreatedAt = createdAt.slice(0, -3) + + return ( + <li className={cx('review-item')}> + <div className={cx('information-wrapper')}> + <div className={cx('reviewer')}> + <Avatar src={profileImage} /> + <p className={cx('nickname')}>{nickname}</p> + <span>|</span> + <span>{editedCreatedAt}</span> + <StarRating starRating={starRating} /> + </div> + {isReviewer && ( + <div className={cx('button-wrapper')}> + <button>수정</button> + <button>삭제</button> + </div> + )} + </div> + <div className={cx('content')}>{content}</div> + </li> + ) +} + +export default ReviewItem diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx new file mode 100644 index 00000000..63173d24 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx @@ -0,0 +1,58 @@ +import classNames from 'classnames/bind' + +import { fetchUser } from '@/shared/api/user' +import Pagination from '@/shared/ui/pagination' + +import ReviewItem from './review-item' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export const COUNT_PER_PAGE = 4 + +interface ReviewContentModel { + reviewId: number + nickname: string + content: string + imageUrl?: string + createdAt: string + starRating: number +} + +interface Props { + reviews: ReviewContentModel[] + totalReview: number + currentPage: number + setCurrentPage: (page: number) => void +} + +const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props) => { + const handlePageChange = (page: number) => setCurrentPage(page) + + const user = fetchUser() + + return ( + <> + <ul className={cx('review-list')}> + {reviews.map((review) => ( + <ReviewItem + key={review.reviewId} + nickname={review.nickname} + profileImage={review.imageUrl} + createdAt={review.createdAt} + starRating={review.starRating} + content={review.content} + isReviewer={user.nickname === review.nickname} + /> + ))} + </ul> + <Pagination + currentPage={currentPage} + maxPage={Math.ceil(totalReview / COUNT_PER_PAGE)} + onPageChange={handlePageChange} + /> + </> + ) +} + +export default ReviewList diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss new file mode 100644 index 00000000..8202f363 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss @@ -0,0 +1,82 @@ +.container { + width: 100%; + padding: 25px; + margin: 10px 0 20px; + background-color: $color-white; + border-radius: 5px; + .title-wrapper { + display: flex; + align-items: end; + .review-title { + @include typo-h4; + color: $color-gray-600; + margin-right: 4px; + } + } +} + +.add-review-wrapper { + width: 100%; + height: 107px; + display: flex; + justify-content: space-between; + gap: 20px; + margin: 30px 0 40px; + .textarea-wrapper { + width: 100%; + height: 100%; + } + .add-button-wrapper { + width: 120px; + p { + font-size: $text-c1; + font-weight: $text-semibold; + color: $color-gray-600; + margin-left: 4px; + } + .review-button { + margin: 20px 0 0 4px; + } + } +} + +.review-list { + margin-bottom: 40px; +} + +.review-item { + width: 100%; + margin-bottom: 5px; + border-bottom: 1px solid $color-gray-400; + .information-wrapper { + display: flex; + justify-content: space-between; + .reviewer { + display: flex; + align-items: center; + p { + @include typo-b2; + margin: 0 10px 0 5px; + } + span { + color: $color-gray-500; + font-weight: $text-normal; + margin-right: 5px; + } + } + .button-wrapper { + button { + font-weight: $text-bold; + font-size: $text-c1; + color: $color-gray-500; + background-color: transparent; + margin-left: 20px; + } + } + } + .content { + margin: 10px 0; + font-size: 18px; + font-weight: $text-medium; + } +} diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index b878a13e..939ebbbb 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -4,9 +4,10 @@ import { useMSWStore } from '@/shared/stores/msw' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' -import useGetDetailsInformationData from '../_hooks/query/use-get-details-information-data' import SideContainer from '../_ui/side-container' +import useGetDetailsInformationData from './_hooks/query/use-get-details-information-data' import DetailsSideItem, { InformationModel, TitleType } from './_ui/details-side-item' +import ReviewContainer from './_ui/review-container' export type InformationType = { title: TitleType; data: string | number } | InformationModel[] @@ -25,10 +26,11 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { <div> <BackHeader label={'목록으로 돌아가기'} /> <Title label={'전략 상세보기'} /> + <ReviewContainer strategyId={params.strategyId} /> <SideContainer> {hasDetailsSideData?.[0] && - detailsSideData?.map((data) => ( - <div key={data.toString()}> + detailsSideData?.map((data, idx) => ( + <div key={`${data}_${idx}`}> <DetailsSideItem information={data} /> </div> ))} From 9cd749a830bbcbb691113d03c90c81e9bf11ae3e Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 24 Nov 2024 23:17:29 +0900 Subject: [PATCH 303/648] =?UTF-8?q?feat:=20=EB=B3=84=EC=A0=90=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20props=20=EC=A0=84=EB=8B=AC=EB=A1=9C=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B0=8F=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=88=98=EC=A0=95=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/star-rating/index.tsx | 12 +++++------- .../_ui/star-rating/star-rating.stories.tsx | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/index.tsx index 3115a387..642a0208 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/index.tsx @@ -1,7 +1,5 @@ 'use client' -import { useState } from 'react' - import classNames from 'classnames/bind' import Star from '@/shared/ui/total-star/star-icon' @@ -12,11 +10,11 @@ const cx = classNames.bind(styles) interface Props { starRating?: number + starRatingValue?: number + onRatingChange?: (value: number) => void } -const StarRating = ({ starRating }: Props) => { - const [clickedIdx, setClickedIdx] = useState(0) - +const StarRating = ({ starRating, starRatingValue, onRatingChange }: Props) => { return ( <div className={cx('container')}> {starRating @@ -24,8 +22,8 @@ const StarRating = ({ starRating }: Props) => { : [...Array(5)].map((_, idx) => ( <button key={idx} - className={cx('click-star', idx < clickedIdx && 'onColor')} - onClick={() => setClickedIdx(idx + 1)} + className={cx('click-star', idx < (starRatingValue || 0) && 'onColor')} + onClick={() => onRatingChange && onRatingChange(idx)} > <Star size="large" /> </button> diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/star-rating.stories.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/star-rating.stories.tsx index 688b6ca5..cc64e19e 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/star-rating.stories.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/star-rating/star-rating.stories.tsx @@ -1,15 +1,26 @@ -import StarRating from '@/app/(dashboard)/strategies/[strategyId]/_ui/star-rating' +import { useState } from 'react' + import type { Meta, StoryFn } from '@storybook/react' +import StarRating from './index' + const meta: Meta = { title: 'components/StarRating', component: StarRating, tags: ['autodocs'], } -const starRating: StoryFn<{ starRating: number | undefined }> = ({ starRating }) => ( - <StarRating starRating={starRating} /> -) +const starRating: StoryFn<{ starRating: number | undefined }> = ({ starRating }) => { + const [starRatingValue, setStarRatingValue] = useState(0) + const handleStarRating = (idx: number) => setStarRatingValue(idx + 1) + return ( + <StarRating + starRating={starRating} + starRatingValue={starRatingValue} + onRatingChange={handleStarRating} + /> + ) +} export const Rated = starRating.bind({}) Rated.args = { From c8761b1754d74ae93c548701dc2f85454986c494 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 24 Nov 2024 23:18:28 +0900 Subject: [PATCH 304/648] =?UTF-8?q?feat:=20msw=20=EB=AA=A9=ED=82=B9,=20api?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=ED=95=A8=EC=88=98,=20tanstackquery=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_api/get-reviews.ts | 19 +++ .../_hooks/query/use-get-reviews-data.ts | 19 +++ .../_hooks/query/use-get-strategies-data.ts | 3 +- mocks/handlers/strategy-details.ts | 154 ++++++++++++++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts new file mode 100644 index 00000000..ed2fc348 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts @@ -0,0 +1,19 @@ +import axios from 'axios' + +import { COUNT_PER_PAGE } from '../_ui/review-container/review-list' + +const getReviews = async (strategyId: string, page: number | undefined) => { + if (!page) return + console.log(page) + try { + const response = await axios.get( + `/api/strategies/${strategyId}/reviews?page=${page}&size=${COUNT_PER_PAGE}` + ) + const data = await response.data + return data + } catch (err) { + console.error(err, '리뷰 데이터 가져오기 실패') + } +} + +export default getReviews diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts new file mode 100644 index 00000000..b2f7f6ed --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts @@ -0,0 +1,19 @@ +import { useQuery } from '@tanstack/react-query' + +import getReviews from '../../_api/get-reviews' + +interface Props { + isReady: boolean + strategyId: string + page: number | undefined +} + +const useGetReviewsData = ({ isReady, strategyId, page }: Props) => { + return useQuery({ + queryKey: ['reviews', strategyId, page], + queryFn: () => getReviews(strategyId, page), + enabled: isReady, + }) +} + +export default useGetReviewsData diff --git a/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts index a084dfcf..508b7ab1 100644 --- a/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts +++ b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts @@ -1,6 +1,7 @@ -import getStrategiesData from '@/app/(dashboard)/strategies/_api/get-strategies' import { useQuery } from '@tanstack/react-query' +import getStrategiesData from '../../_api/get-strategies' + interface Props { isReady: boolean page: number diff --git a/mocks/handlers/strategy-details.ts b/mocks/handlers/strategy-details.ts index 4f6059ce..0798d7e7 100644 --- a/mocks/handlers/strategy-details.ts +++ b/mocks/handlers/strategy-details.ts @@ -128,6 +128,128 @@ const data = [ }, ] +const reviewData = [ + { + strategyId: '1', + averageRating: 3.8, + reviewCount: 4, + reviews: { + content: [ + { + reviewId: 85, + nickname: '김아무개씨', + content: 'good strategy!', + imageUrl: '', + createdAt: '2024-11-17 19:31:44', + starRating: 4, + isOwner: false, + }, + { + reviewId: 84, + nickname: 'user2', + content: 'good strategy!', + imageUrl: '', + createdAt: '2024-11-17 19:24:55', + starRating: 5, + isOwner: false, + }, + { + reviewId: 83, + nickname: 'user3', + content: 'good strategy!', + imageUrl: '', + createdAt: '2024-11-17 19:24:38', + starRating: 5, + isOwner: false, + }, + { + reviewId: 82, + nickname: 'user4', + content: 'bad strategy!', + imageUrl: '', + createdAt: '2024-11-17 19:24:09', + starRating: 1, + isOwner: false, + }, + { + reviewId: 81, + nickname: 'user4', + content: 'bad strategy!', + imageUrl: '', + createdAt: '2024-11-17 19:24:09', + starRating: 1, + isOwner: false, + }, + { + reviewId: 80, + nickname: 'user4', + content: 'bad strategy!', + imageUrl: '', + createdAt: '2024-11-17 19:24:09', + starRating: 1, + isOwner: false, + }, + { + reviewId: 79, + nickname: 'user4', + content: 'bad strategy!', + imageUrl: '', + createdAt: '2024-11-17 19:24:09', + starRating: 1, + isOwner: false, + }, + ], + totalElements: 7, + }, + }, + { + strategyId: '2', + averageRating: 3.8, + reviewCount: 4, + reviews: { + content: [ + { + reviewId: 85, + nickname: '김아무개씨', + content: 'good strategy!', + imageUrl: '/images/profiles/user1.jpg', + createdAt: '2024-11-17 19:31:44', + starRating: 4, + isOwner: false, + }, + { + reviewId: 84, + nickname: 'user2', + content: 'good strategy!', + imageUrl: '/images/profiles/user1.jpg', + createdAt: '2024-11-17 19:24:55', + starRating: 5, + isOwner: false, + }, + { + reviewId: 83, + nickname: 'user3', + content: 'good strategy!', + imageUrl: '/images/profiles/user1.jpg', + createdAt: '2024-11-17 19:24:38', + starRating: 5, + isOwner: false, + }, + { + reviewId: 82, + nickname: 'user4', + content: 'bad strategy!', + imageUrl: '/images/profiles/user1.jpg', + createdAt: '2024-11-17 19:24:09', + starRating: 1, + isOwner: false, + }, + ], + totalElements: 4, + }, + }, +] + export const strategyDetailsHandlers = [ http.get('/api/strategies/:strategyId', ({ params }) => { const { strategyId } = params @@ -144,4 +266,36 @@ export const strategyDetailsHandlers = [ { status: 400 } ) }), + + http.get('/api/strategies/:strategyId/reviews', ({ params, request }) => { + const { strategyId } = params + const url = new URL(request.url) + const page = parseInt(url.searchParams.get('page') || '1') + const size = parseInt(url.searchParams.get('size') || '4') + + const reviewDataForStrategy = reviewData.find((item) => item.strategyId === strategyId) + if (!isNaN(page) && !isNaN(size) && reviewDataForStrategy) { + const { content, totalElements } = reviewDataForStrategy.reviews + const slicedContent = content.slice(size * (page - 1), size * (page - 1) + size) + + return HttpResponse.json({ + strategyId, + averageRating: reviewDataForStrategy.averageRating, + reviewCount: reviewDataForStrategy.reviewCount, + reviews: { + content: slicedContent, + totalElements, + }, + }) + } + + return HttpResponse.json( + { + isSuccess: false, + message: '전략의 리뷰를 찾을 수 없습니다.', + code: 4002, + }, + { status: 400 } + ) + }), ] From 12e703500ea40791b039e02825299f14bea0b914 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sun, 24 Nov 2024 23:26:30 +0900 Subject: [PATCH 305/648] =?UTF-8?q?feat:=20textarea=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=B4=20forwardRef=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/textarea/index.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/shared/ui/textarea/index.tsx b/shared/ui/textarea/index.tsx index 290ca6d2..65b00998 100644 --- a/shared/ui/textarea/index.tsx +++ b/shared/ui/textarea/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { ComponentProps } from 'react' +import { ComponentProps, forwardRef } from 'react' import classNames from 'classnames/bind' @@ -12,16 +12,19 @@ interface Props extends ComponentProps<'textarea'> { rows?: number } -export const Textarea = ({ rows = 5, className, value, onChange, ...props }: Props) => { - return ( - <div> +export const Textarea = forwardRef<HTMLTextAreaElement, Props>( + ({ rows = 5, className, value, onChange, ...props }, ref) => { + return ( <textarea + ref={ref} value={value} onChange={onChange} rows={rows} className={cx('textarea', className)} {...props} /> - </div> - ) -} + ) + } +) + +Textarea.displayName = 'Textarea' From 4cac3e1533bb5612453fd00fbaf2b644ce9a74ab Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 24 Nov 2024 23:58:07 +0900 Subject: [PATCH 306/648] =?UTF-8?q?feat:=20admin=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EB=94=94=EC=9E=90=EC=9D=B8=20=ED=8B=80=20?= =?UTF-8?q?=EC=9E=A1=EC=9D=8C=20(#109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/users/page.module.scss | 3 ++ app/admin/users/page.tsx | 78 +++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 app/admin/users/page.module.scss diff --git a/app/admin/users/page.module.scss b/app/admin/users/page.module.scss new file mode 100644 index 00000000..9dfc496a --- /dev/null +++ b/app/admin/users/page.module.scss @@ -0,0 +1,3 @@ +.color-primary-500 { + color: $color-orange-500; +} diff --git a/app/admin/users/page.tsx b/app/admin/users/page.tsx index 024603db..0e3f1ee7 100644 --- a/app/admin/users/page.tsx +++ b/app/admin/users/page.tsx @@ -1,5 +1,81 @@ +'use client' + +// TODO: ssr +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import Pagination from '@/shared/ui/pagination' +import { SearchInput } from '@/shared/ui/search-input' +import Select from '@/shared/ui/select' +import VerticalTable from '@/shared/ui/table/vertical' +import Tabs from '@/shared/ui/tabs' +import Title from '@/shared/ui/title' + +import AdminContentsHeader from '../_ui/admin-header' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const tabs = ['모든 회원', '일반', '트레이더', '관리자'] + const AdminUsersPage = () => { - return <></> + const [select, setSelect] = useState(tabs[0]) + return ( + <> + {/* TODO: inline css 제거 */} + <Title label="회원 관리" style={{ margin: '80px 0 26px 12.6px' }} /> + <div + style={{ + padding: '0 45px 37px', + borderRadius: '8px', + marginBottom: '42px', + backgroundColor: 'aliceblue', + }} + > + <Tabs + tabs={tabs.map((tab) => ({ + id: tab, + label: tab, + }))} + activeTab={tabs[0]} + onTabChange={(id) => alert(id)} + /> + <AdminContentsHeader + // TODO: 실제 개수로 바인딩 + Left={ + <span> + 총 <span className={cx('color-primary-500')}>30</span>명 + </span> + } + Right={ + <div style={{ display: 'flex', gap: '24px' }}> + <Select + size="small" + options={tabs.map((tab) => ({ + label: tab, + value: tab, + }))} + value={select} + onChange={(v) => { + setSelect(v as string) + }} + /> + <SearchInput /> + </div> + } + /> + <VerticalTable + tableHead={['No.', '프로필', '이름', '닉네임', '이메일', '전화번호', '회원분류', '탈퇴']} + tableBody={[]} + countPerPage={10} + currentPage={1} + /> + {/* TODO: 실제 값으로 추가 */} + <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + </div> + </> + ) } export default AdminUsersPage From 218d05a5bfae68595dde575d671d5354b4dec31b Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 25 Nov 2024 00:24:32 +0900 Subject: [PATCH 307/648] =?UTF-8?q?fix:=20select=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20option=EC=9D=B4=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=EC=97=90=20=EC=98=81=ED=96=A5=EC=9D=84=20?= =?UTF-8?q?=EB=81=BC=EC=B9=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=ED=8E=B8=EC=9D=98=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20(#111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_variables.scss | 1 + shared/ui/dropdown/index.tsx | 2 +- shared/ui/dropdown/styles.module.scss | 10 +++++++++- shared/ui/dropdown/types.ts | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index d9ee0c6c..8f28d270 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -62,6 +62,7 @@ $breakpoint-xl: 1200px; $z-index: ( modal: 1000, header: 100, + select: 10, base: 1, hidden: -1, ); diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index a7e1716d..fa144f7e 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -46,7 +46,7 @@ const Dropdown = ({ return ( <DropdownContext.Provider value={{ isOpen, toggleOpen, handleSelect }}> - <div className={cx(`dropdown-${size}`)} style={containerStyle} ref={dropdownRef}> + <div className={cx('dropdown', `dropdown-${size}`)} style={containerStyle} ref={dropdownRef}> <button onClick={toggleOpen} className={cx('container', 'trigger', size, { open: isOpen })} diff --git a/shared/ui/dropdown/styles.module.scss b/shared/ui/dropdown/styles.module.scss index 5279afc5..e25c5955 100644 --- a/shared/ui/dropdown/styles.module.scss +++ b/shared/ui/dropdown/styles.module.scss @@ -5,6 +5,10 @@ $large-height: 40px; $color-selected-text: #171717; +.dropdown { + position: relative; +} + .dropdown { &-small { width: $small-width; @@ -29,6 +33,7 @@ $color-selected-text: #171717; svg { color: $color-gray-200; margin-left: auto; + width: 24px; } } @@ -66,10 +71,13 @@ $color-selected-text: #171717; } .options { + position: absolute; + bottom: 0; + transform: translateY(calc(100% + 4px)); + z-index: zIndex(select); list-style: none; height: fit-content; margin: 0; - margin-top: 4px; padding: 0; border-radius: 4px; background-color: $color-white; diff --git a/shared/ui/dropdown/types.ts b/shared/ui/dropdown/types.ts index 7a6c0e89..d0baaf4f 100644 --- a/shared/ui/dropdown/types.ts +++ b/shared/ui/dropdown/types.ts @@ -14,7 +14,7 @@ export interface DropdownProps { Trigger: ReactNode value: DropdownValueType onChange: (value: DropdownValueType) => void - isMultiple: boolean + isMultiple?: boolean containerStyle?: CSSProperties labelStyle?: CSSProperties children?: ReactNode From 65db79bcee1b2eadeffa4bf370bf142a883e775a Mon Sep 17 00:00:00 2001 From: hoyoen <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 25 Nov 2024 02:34:25 +0900 Subject: [PATCH 308/648] =?UTF-8?q?fix:=20table=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=9D=98=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EA=B0=80=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20ui=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=ED=9B=85=20=EB=B6=84=EB=A6=AC,=20body?= =?UTF-8?q?=EB=A1=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vertical/hooks/use-vertical-table.ts | 19 ++++++++ shared/ui/table/vertical/index.tsx | 43 ++++++++++++------- shared/ui/table/vertical/styles.module.scss | 8 ++++ shared/ui/table/vertical/table.stories.tsx | 6 +++ 4 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 shared/ui/table/vertical/hooks/use-vertical-table.ts diff --git a/shared/ui/table/vertical/hooks/use-vertical-table.ts b/shared/ui/table/vertical/hooks/use-vertical-table.ts new file mode 100644 index 00000000..9661e9de --- /dev/null +++ b/shared/ui/table/vertical/hooks/use-vertical-table.ts @@ -0,0 +1,19 @@ +import { VerticalTableProps } from '..' + +type ArgsType = Pick<VerticalTableProps, 'tableBody' | 'countPerPage' | 'currentPage'> + +const useVerticalTable = ({ tableBody, countPerPage, currentPage }: ArgsType) => { + const hasData = tableBody.length > 0 + + const croppedTableBody = tableBody.slice( + countPerPage * (currentPage - 1), + countPerPage * (currentPage - 1) + countPerPage + ) + + return { + hasData, + croppedTableBody, + } +} + +export default useVerticalTable diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 580adee3..e5b1aad4 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -1,25 +1,29 @@ +// import { TABLE_DATA } from '@/app/admin/notices/tabledata' +import { ReactNode } from 'react' + import classNames from 'classnames/bind' import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' +import useVerticalTable from './hooks/use-vertical-table' import styles from './styles.module.scss' const cx = classNames.bind(styles) -type TableBodyDataType = DailyAnalysisModel | MonthlyAnalysisModel +type TableBodyDataType = + | DailyAnalysisModel + | MonthlyAnalysisModel + | Array<ReactNode | string | number> -interface Props { +export interface VerticalTableProps { tableHead: string[] tableBody: TableBodyDataType[] countPerPage: number currentPage: number } -const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Props) => { - const croppedTableBody = tableBody.slice( - countPerPage * (currentPage - 1), - countPerPage * (currentPage - 1) + countPerPage - ) +const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: VerticalTableProps) => { + const { hasData, croppedTableBody } = useVerticalTable({ tableBody, countPerPage, currentPage }) return ( <div className={cx('container')}> @@ -31,16 +35,23 @@ const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Prop ))} </tr> </thead> - <tbody> - {croppedTableBody.map((row) => ( - <tr key={Object.values(row)[0]}> - {Object.entries(row).map((rowData, idx) => ( - <td key={idx}>{rowData[1]}</td> - ))} - </tr> - ))} - </tbody> + {hasData && ( + <tbody> + {croppedTableBody.map((row) => ( + <tr key={Object.values(row)[0]}> + {Object.values(row).map((data, idx) => ( + <td key={data + idx}>{data}</td> + ))} + </tr> + ))} + </tbody> + )} </table> + {!hasData && ( + <div className={cx('no-data')} style={{ height: `calc(40px * ${countPerPage}` }}> + 데이터가 존재하지 않습니다. + </div> + )} </div> ) } diff --git a/shared/ui/table/vertical/styles.module.scss b/shared/ui/table/vertical/styles.module.scss index cbc413ca..17ea63ed 100644 --- a/shared/ui/table/vertical/styles.module.scss +++ b/shared/ui/table/vertical/styles.module.scss @@ -17,3 +17,11 @@ } } } + +.no-data { + display: flex; + justify-content: center; + margin-top: 80px; + color: $color-gray-600; + @include typo-b1; +} diff --git a/shared/ui/table/vertical/table.stories.tsx b/shared/ui/table/vertical/table.stories.tsx index 1097dfe4..21b3fb8e 100644 --- a/shared/ui/table/vertical/table.stories.tsx +++ b/shared/ui/table/vertical/table.stories.tsx @@ -93,4 +93,10 @@ Primary.args = { countPerPage: 7, } +export const NoData = Table.bind({}) +NoData.args = { + // @ts-expect-error rornlcksg + tableBody: [], +} + export default meta From e029156954769312159431baded83ab0671a880e Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 25 Nov 2024 10:21:49 +0900 Subject: [PATCH 309/648] =?UTF-8?q?feat:=20nodata=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_api/get-reviews.ts | 4 +- .../_ui/review-container/index.tsx | 40 +++++++++---------- .../_ui/review-container/styles.module.scss | 8 ++++ shared/ui/total-star/index.tsx | 11 +++-- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts index ed2fc348..bdde39b8 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts @@ -3,8 +3,8 @@ import axios from 'axios' import { COUNT_PER_PAGE } from '../_ui/review-container/review-list' const getReviews = async (strategyId: string, page: number | undefined) => { - if (!page) return - console.log(page) + if (!strategyId && !page) return + try { const response = await axios.get( `/api/strategies/${strategyId}/reviews?page=${page}&size=${COUNT_PER_PAGE}` diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx index 9ca85d9e..b8a8a437 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx @@ -24,27 +24,27 @@ const ReviewContainer = ({ strategyId }: Props) => { const { data: reviewData } = useGetReviewsData({ isReady, strategyId, page: currentPage }) return ( - <> - {reviewData && ( - <div className={cx('container')}> - <div className={cx('title-wrapper')}> - <p className={cx('review-title')}>리뷰</p> - <TotalStar - size="medium" - averageRating={reviewData.averageRating} - totalElements={reviewData.reviews.totalElements} - /> - </div> - <AddReview /> - <ReviewList - reviews={reviewData.reviews.content} - totalReview={reviewData.reviews.totalElements} - currentPage={currentPage} - setCurrentPage={setCurrentPage} - /> - </div> + <div className={cx('container')}> + <div className={cx('title-wrapper')}> + <p className={cx('review-title')}>리뷰</p> + <TotalStar + size="medium" + averageRating={reviewData?.averageRating} + totalElements={reviewData?.reviews.totalElements} + /> + </div> + <AddReview /> + {reviewData ? ( + <ReviewList + reviews={reviewData.reviews.content} + totalReview={reviewData.reviews.totalElements} + currentPage={currentPage} + setCurrentPage={setCurrentPage} + /> + ) : ( + <div className={cx('no-review')}>등록된 리뷰가 없습니다.</div> )} - </> + </div> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss index 8202f363..87a72b07 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss @@ -13,6 +13,14 @@ margin-right: 4px; } } + .no-review { + width: 100%; + display: flex; + justify-content: center; + color: $color-gray-600; + margin: 40px 0; + @include typo-b2; + } } .add-review-wrapper { diff --git a/shared/ui/total-star/index.tsx b/shared/ui/total-star/index.tsx index f2c3bda1..7a9c2c5b 100644 --- a/shared/ui/total-star/index.tsx +++ b/shared/ui/total-star/index.tsx @@ -10,12 +10,17 @@ export type SizeType = 'small' | 'medium' export type TextColorType = 'black' | 'gray' interface Props { - averageRating: number - totalElements: number + averageRating?: number + totalElements?: number size?: SizeType textColor?: TextColorType } -const TotalStar = ({ averageRating, totalElements, size = 'small', textColor = 'gray' }: Props) => { +const TotalStar = ({ + averageRating = 0, + totalElements = 0, + size = 'small', + textColor = 'gray', +}: Props) => { return ( <div className={cx('container', size, textColor)}> <div className={cx('icon')}> From 14dcbaf7d04c9b642328be7bac7501d3c776ad0f Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 25 Nov 2024 15:28:10 +0900 Subject: [PATCH 310/648] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#78)=20-=20query=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC=20-=20?= =?UTF-8?q?=EA=B0=81=20=ED=8C=8C=EC=9D=BC=EB=A7=88=EB=8B=A4=20=EA=B4=80?= =?UTF-8?q?=EC=8B=AC=EC=82=AC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 9 +- app/(landing)/signin/styles.module.scss | 5 +- shared/api/auth.ts | 40 ------ shared/api/axios.ts | 30 +++-- shared/constants/auth.ts | 4 + shared/hooks/custom/use-auth.ts | 112 ++++------------- shared/hooks/query/auth-queries.ts | 116 ++++++++++++++++++ shared/stores/use-auth-store.ts | 24 +++- shared/types/auth.ts | 8 +- shared/ui/logo/styles.module.scss | 2 +- shared/ui/side-navigation/user-navigation.tsx | 26 ++-- 11 files changed, 203 insertions(+), 173 deletions(-) create mode 100644 shared/constants/auth.ts create mode 100644 shared/hooks/query/auth-queries.ts diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index 06bccbe0..7eb525f8 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -9,9 +9,9 @@ import { useRouter, useSearchParams } from 'next/navigation' import { AxiosError } from 'axios' import classNames from 'classnames/bind' -import { useLogin } from '@/shared/api/auth' import { ERROR_MESSAGES } from '@/shared/constants/error-messages' import { PATH } from '@/shared/constants/path' +import { useLoginMutation } from '@/shared/hooks/query/auth-queries' import { useAuthStore } from '@/shared/stores/use-auth-store' import type { LoginFormDataModel } from '@/shared/types/auth' import { Button } from '@/shared/ui/button' @@ -26,7 +26,7 @@ const cx = classNames.bind(styles) const SignInPage = () => { const router = useRouter() const searchParams = useSearchParams() - const loginMutation = useLogin() + const loginMutation = useLoginMutation() const setKeepLoggedIn = useAuthStore((state) => state.setKeepLoggedIn) const isKeepLoggedIn = useAuthStore((state) => state.isKeepLoggedIn) @@ -38,7 +38,7 @@ const SignInPage = () => { const [errors, setErrors] = useState<string | null>(null) const [isSubmitting, setIsSubmitting] = useState(false) - const validateForm = useCallback(() => { + const validateForm = () => { const emailError = validate('EMAIL', formData.email) if (emailError) { setErrors(emailError) @@ -52,7 +52,7 @@ const SignInPage = () => { } return true - }, [formData.email, formData.password]) + } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() @@ -155,7 +155,6 @@ const SignInPage = () => { <p className={cx('error-message', { visible: errors, - hidden: !errors, })} role="alert" > diff --git a/app/(landing)/signin/styles.module.scss b/app/(landing)/signin/styles.module.scss index 270b03f5..9be85e8b 100644 --- a/app/(landing)/signin/styles.module.scss +++ b/app/(landing)/signin/styles.module.scss @@ -52,14 +52,11 @@ .error-message { @include typo-c1; color: $color-orange-700; + opacity: 0; &.visible { opacity: 1; } - - &.hidden { - opacity: 0; - } } } diff --git a/shared/api/auth.ts b/shared/api/auth.ts index 341a216f..e39c85be 100644 --- a/shared/api/auth.ts +++ b/shared/api/auth.ts @@ -1,48 +1,8 @@ -import { useMutation } from '@tanstack/react-query' - -import { - removeAccessToken, - removeRefreshToken, - setAccessToken, - setRefreshToken, -} from '@/shared/lib/auth-tokens' -import { useAuthStore } from '@/shared/stores/use-auth-store' import type { LoginFormDataModel, LoginResponseType } from '@/shared/types/auth' import axiosInstance from './axios' export const login = async (credentials: LoginFormDataModel): Promise<LoginResponseType> => { const response = await axiosInstance.post('/api/users/login', credentials) - const { user, accessToken, refreshToken } = response.data.data - - setAccessToken(accessToken) - setRefreshToken(refreshToken) - - useAuthStore.getState().setAuthState({ - isAuthenticated: true, - user, - isLoggedOut: false, - }) - return response.data } - -export const logout = () => { - if (typeof window !== 'undefined') { - removeAccessToken() - removeRefreshToken() - - useAuthStore.getState().setAuthState({ - isAuthenticated: false, - user: null, - isKeepLoggedIn: false, - isLoggedOut: true, - }) - } -} - -export const useLogin = () => { - return useMutation({ - mutationFn: login, - }) -} diff --git a/shared/api/axios.ts b/shared/api/axios.ts index c5ca04e5..3c25ae72 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -5,8 +5,6 @@ import { getAccessToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' import { getUserFromToken, isTokenExpired, refreshToken } from '@/shared/utils/token-utils' -import { logout } from './auth' - export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { const instance = axios.create() @@ -25,14 +23,24 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} const isAdmin = user?.role === 'admin' if (isAdmin && isTokenExpired(accessToken)) { - handleLogout() + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + isKeepLoggedIn: false, + isLoggedOut: true, + }) return config } config.headers.Authorization = `Bearer ${accessToken}` } catch (error) { console.error('토큰 디코딩 실패:', error) - handleLogout() + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + isKeepLoggedIn: false, + isLoggedOut: true, + }) } return config @@ -63,7 +71,12 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} console.error('Token refresh failed:', refreshError) } - handleLogout() + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + isKeepLoggedIn: false, + isLoggedOut: true, + }) } return Promise.reject(error) @@ -74,13 +87,6 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} return instance } -const handleLogout = () => { - if (typeof window !== 'undefined') { - sessionStorage.removeItem('sessionToken') - logout() - } -} - const axiosInstance = createAxiosInstance({ withInterceptors: true }) export default axiosInstance diff --git a/shared/constants/auth.ts b/shared/constants/auth.ts new file mode 100644 index 00000000..4fe8b0fe --- /dev/null +++ b/shared/constants/auth.ts @@ -0,0 +1,4 @@ +export const AUTH_TIME = { + TOKEN_CHECK_INTERVAL: 10 * 60 * 1000, // 10분 + ADMIN_EXPIRY_WARNING: 10 * 60 * 1000, // 10분 +} as const diff --git a/shared/hooks/custom/use-auth.ts b/shared/hooks/custom/use-auth.ts index ce2c1563..967d5d90 100644 --- a/shared/hooks/custom/use-auth.ts +++ b/shared/hooks/custom/use-auth.ts @@ -1,122 +1,56 @@ import { useEffect } from 'react' -import { useRouter } from 'next/navigation' - -import { useQuery } from '@tanstack/react-query' - -import { logout } from '@/shared/api/auth' -import { PATH } from '@/shared/constants/path' +import { useLogoutMutation } from '@/shared/hooks/query/auth-queries' import { getAccessToken, getRefreshToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' -import { getTimeUntilExpiry, getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' - -const TOKEN_CHECK_INTERVAL = 600000 // 10분 -const ADMIN_EXPIRY_WARNING = 600000 // 10분 +import { getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' export const useAuth = () => { - const router = useRouter() const { user, isAuthenticated, isKeepLoggedIn } = useAuthStore() + const logout = useLogoutMutation() + // const { data: tokenStatus } = useTokenStatusQuery(logout) useEffect(() => { const initializeAuth = () => { const accessToken = getAccessToken() const refreshToken = getRefreshToken() - if (!accessToken || !refreshToken) { - handleLogout() - return - } + if (!accessToken || !refreshToken) return - const user = getUserFromToken(accessToken) - if (!user) { - handleLogout() - return - } + const tokenUser = getUserFromToken(accessToken) + if (!tokenUser) return - if (user.role === 'admin' && isTokenExpired(accessToken)) { - handleLogout() + if (tokenUser.role === 'admin' && isTokenExpired(accessToken)) { + logout() return } - if (user.role === 'user') { + if (tokenUser.role === 'user') { const sessionToken = sessionStorage.getItem('sessionToken') if (!isKeepLoggedIn && !sessionToken) { - handleLogout() + logout() return } } - useAuthStore.getState().setAuthState({ - isAuthenticated: true, - user, - isLoggedOut: false, - }) - } - - initializeAuth() - }, [isKeepLoggedIn]) - - const { data: tokenStatus } = useQuery({ - queryKey: ['tokenStatus'], - queryFn: async () => { - const accessToken = getAccessToken() - const refreshToken = getRefreshToken() - - if (!accessToken || !refreshToken) { - handleLogout() - return null - } - - const user = getUserFromToken(accessToken) - if (!user) { - handleLogout() - return null + if (!user || user.id !== tokenUser.id) { + useAuthStore.getState().setAuthState({ + isAuthenticated: true, + user: tokenUser, + isLoggedOut: false, + }) } + } - const isExpired = isTokenExpired(accessToken) - const timeUntilExpiry = getTimeUntilExpiry(accessToken) - - if (user.role === 'admin') { - if (isExpired) { - handleLogout() - return null - } - return { - isValid: !isExpired, - timeUntilExpiry, - isNearExpiry: timeUntilExpiry < ADMIN_EXPIRY_WARNING, - } - } - - if (user.role === 'user') { - const sessionToken = sessionStorage.getItem('sessionToken') - if (!isKeepLoggedIn && !sessionToken) { - handleLogout() - return null - } - } - - return { - isValid: !isExpired, - timeUntilExpiry, - isNearExpiry: false, - } - }, - refetchInterval: TOKEN_CHECK_INTERVAL, - }) - - const handleLogout = () => { - sessionStorage.removeItem('sessionToken') - logout() - router.replace(PATH.SIGN_IN) - } + // initializeAuth() + }, []) return { user, isAuthenticated, isKeepLoggedIn, - tokenStatus, - isAdminNearExpiry: tokenStatus?.isNearExpiry, - logout: handleLogout, + // tokenStatus, + // isAdminNearExpiry: tokenStatus?.isNearExpiry, + logout, } } diff --git a/shared/hooks/query/auth-queries.ts b/shared/hooks/query/auth-queries.ts new file mode 100644 index 00000000..7f45ce67 --- /dev/null +++ b/shared/hooks/query/auth-queries.ts @@ -0,0 +1,116 @@ +import { useRouter } from 'next/navigation' + +import { useMutation, useQuery } from '@tanstack/react-query' + +import { login } from '@/shared/api/auth' +import { AUTH_TIME } from '@/shared/constants/auth' +import { PATH } from '@/shared/constants/path' +import { + getAccessToken, + getRefreshToken, + removeAccessToken, + removeRefreshToken, + setAccessToken, + setRefreshToken, +} from '@/shared/lib/auth-tokens' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import { LoginResponseType, TokenStatusModel } from '@/shared/types/auth' +import { getTimeUntilExpiry, getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' + +export const useLoginMutation = () => { + return useMutation({ + mutationFn: login, + onSuccess: (response: LoginResponseType) => { + const { user, accessToken, refreshToken } = response.data + + setAccessToken(accessToken) + setRefreshToken(refreshToken) + + if (user.role === 'user') { + sessionStorage.setItem('sessionToken', 'true') + } + + useAuthStore.getState().setAuthState({ + isAuthenticated: true, + user, + isLoggedOut: false, + }) + }, + }) +} + +export const useLogoutMutation = () => { + const router = useRouter() + + return () => { + try { + removeAccessToken() + removeRefreshToken() + sessionStorage.removeItem('sessionToken') + + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + isKeepLoggedIn: false, + isLoggedOut: true, + }) + + router.replace(PATH.SIGN_IN) + } catch (error) { + console.error('로그아웃 실패:', error) + } + } +} + +// export const useTokenStatusQuery = (logout: () => void) => { +// const { isKeepLoggedIn } = useAuthStore() + +// return useQuery<TokenStatusModel | null>({ +// queryKey: ['tokenStatus'], +// queryFn: async () => { +// const accessToken = getAccessToken() +// const refreshToken = getRefreshToken() + +// if (!accessToken || !refreshToken) { +// logout() +// return null +// } + +// const user = getUserFromToken(accessToken) +// if (!user) { +// logout() +// return null +// } + +// const isExpired = isTokenExpired(accessToken) +// const timeUntilExpiry = getTimeUntilExpiry(accessToken) + +// if (user.role === 'admin') { +// if (isExpired) { +// // logout() +// return null +// } +// return { +// isValid: !isExpired, +// timeUntilExpiry, +// isNearExpiry: timeUntilExpiry < AUTH_TIME.ADMIN_EXPIRY_WARNING, +// } +// } + +// if (user.role === 'user') { +// const sessionToken = sessionStorage.getItem('sessionToken') +// if (!isKeepLoggedIn && !sessionToken) { +// // logout() +// return null +// } +// } + +// return { +// isValid: !isExpired, +// timeUntilExpiry, +// isNearExpiry: false, +// } +// }, +// refetchInterval: AUTH_TIME.TOKEN_CHECK_INTERVAL, +// }) +// } diff --git a/shared/stores/use-auth-store.ts b/shared/stores/use-auth-store.ts index 9a6c411d..7b3eecd8 100644 --- a/shared/stores/use-auth-store.ts +++ b/shared/stores/use-auth-store.ts @@ -7,16 +7,34 @@ interface AuthStateModel { user: UserModel | null isKeepLoggedIn: boolean isLoggedOut: boolean +} + +interface AuthActionsModel { setKeepLoggedIn: (value: boolean) => void setAuthState: (state: Partial<AuthStateModel>) => void } -export const useAuthStore = create<AuthStateModel>((set) => ({ +type AuthStoreType = AuthStateModel & AuthActionsModel + +const initialState: AuthStateModel = { isAuthenticated: false, user: null, isKeepLoggedIn: false, isLoggedOut: false, +} + +export const useAuthStore = create<AuthStoreType>((set) => ({ + ...initialState, + + setKeepLoggedIn: (value) => + set((state) => ({ + ...state, + isKeepLoggedIn: value, + })), - setKeepLoggedIn: (value) => set({ isKeepLoggedIn: value }), - setAuthState: (state) => set(state), + setAuthState: (newState) => + set((state) => ({ + ...state, + ...newState, + })), })) diff --git a/shared/types/auth.ts b/shared/types/auth.ts index 52622c9a..7d8b617d 100644 --- a/shared/types/auth.ts +++ b/shared/types/auth.ts @@ -28,7 +28,7 @@ export interface AuthResponseModel { export interface ApiResponseModel<T> { isSuccess: boolean message: string - data?: T + data: T code?: string } @@ -41,6 +41,12 @@ export interface TokenPayloadModel { iat: number } +export interface TokenStatusModel { + isValid: boolean + timeUntilExpiry: number + isNearExpiry: boolean +} + export const isAdmin = (user: UserModel | null): user is UserModel & { role: 'admin' } => { return user?.role === 'admin' } diff --git a/shared/ui/logo/styles.module.scss b/shared/ui/logo/styles.module.scss index f94d4854..3c3c453b 100644 --- a/shared/ui/logo/styles.module.scss +++ b/shared/ui/logo/styles.module.scss @@ -13,4 +13,4 @@ width: 142px; color: $color-gray-500; line-height: normal; -} \ No newline at end of file +} diff --git a/shared/ui/side-navigation/user-navigation.tsx b/shared/ui/side-navigation/user-navigation.tsx index 5ba238ae..91986117 100644 --- a/shared/ui/side-navigation/user-navigation.tsx +++ b/shared/ui/side-navigation/user-navigation.tsx @@ -1,13 +1,13 @@ 'use client' -import { usePathname, useRouter } from 'next/navigation' +import { usePathname } from 'next/navigation' import { ChangeIcon, ProfileIcon, SignOutIcon } from '@/public/icons' import classNames from 'classnames/bind' -import { logout } from '@/shared/api/auth' -import { fetchUser } from '@/shared/api/user' import { PATH } from '@/shared/constants/path' +import { useAuth } from '@/shared/hooks/custom/use-auth' +import { isAdmin } from '@/shared/types/auth' import NavButtonItem from '@/shared/ui/side-navigation/nav-button-item' import NavLinkItem from '@/shared/ui/side-navigation/nav-link-item' @@ -17,36 +17,26 @@ const cx = classNames.bind(styles) const UserNavigation = () => { const path = usePathname() - const router = useRouter() + const { user, logout } = useAuth() const isAdminPage = path.startsWith(PATH.ADMIN) - const user = fetchUser() - const isAdmin = user.role.includes('admin') - - const handleLogout = async () => { - try { - logout() - router.replace(PATH.SIGN_IN) - } catch (error) { - console.error('로그아웃 실패:', error) - } - } + if (!user) return null return ( <nav className={cx('user-navigation')} aria-label="사용자 메뉴"> <ul> <NavLinkItem href={PATH.PROFILE} icon={ProfileIcon} textClassName="user"> - <span className={cx('nickname')}>{user.nickname}</span> + <span className={cx('nickname')}>{user.name}</span> <span className={cx('email')}>{user.email}</span> </NavLinkItem> - {isAdmin && ( + {isAdmin(user) && ( <NavLinkItem href={isAdminPage ? PATH.STRATEGIES : PATH.ADMIN_USERS} icon={ChangeIcon}> {isAdminPage ? '메인 대시보드' : '관리자 대시보드'} </NavLinkItem> )} - <NavButtonItem icon={SignOutIcon} onClick={handleLogout}> + <NavButtonItem icon={SignOutIcon} onClick={logout}> 로그아웃 </NavButtonItem> </ul> From 3abc71781145935f658b2c9e9fe74a591623537e Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 25 Nov 2024 17:27:17 +0900 Subject: [PATCH 311/648] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=98=EC=98=81=20(#78)=20-=20name=20=EB=8C=80?= =?UTF-8?q?=EC=8B=A0=20nickname=20=EC=82=AC=EC=9A=A9=20-=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=95=84=EC=9B=83=EA=B3=BC=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=83=81=ED=83=9C=EC=B2=B4=ED=81=AC=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=ED=9B=85=EC=9C=BC=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/navigation.tsx | 8 +- app/(landing)/signup/_lib/cookies.ts | 2 +- .../_ui/signup-complete-message/index.tsx | 2 +- .../signup/_ui/user-type-card/index.tsx | 2 +- mocks/handlers/auth.ts | 35 ++++--- shared/api/axios.ts | 5 +- shared/api/user.ts | 13 --- shared/hooks/custom/use-auth.ts | 97 ++++++++++++++++--- shared/hooks/query/auth-queries.ts | 97 +------------------ shared/types/auth.ts | 18 ++-- shared/types/user.ts | 11 --- shared/ui/side-navigation/user-navigation.tsx | 6 +- 12 files changed, 134 insertions(+), 162 deletions(-) delete mode 100644 shared/api/user.ts delete mode 100644 shared/types/user.ts diff --git a/app/(dashboard)/_ui/navigation.tsx b/app/(dashboard)/_ui/navigation.tsx index d2a76c5c..951168a3 100644 --- a/app/(dashboard)/_ui/navigation.tsx +++ b/app/(dashboard)/_ui/navigation.tsx @@ -8,14 +8,14 @@ import { TradersIcon, } from '@/public/icons' -import { fetchUser } from '@/shared/api/user' import { PATH } from '@/shared/constants/path' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import { isTrader } from '@/shared/types/auth' import SideNavigation from '@/shared/ui/side-navigation' import NavLinkItem from '@/shared/ui/side-navigation/nav-link-item' const DashboardNavigation = () => { - const user = fetchUser() - const isTrader = user.role.includes('trader') + const { user } = useAuthStore() return ( <SideNavigation> @@ -25,7 +25,7 @@ const DashboardNavigation = () => { <NavLinkItem href={PATH.TRADERS} icon={TradersIcon}> 트레이더 목록 </NavLinkItem> - {isTrader && ( + {isTrader(user) && ( <NavLinkItem href={PATH.MY_STRATEGIES} icon={StrategyIcon}> 나의 전략 </NavLinkItem> diff --git a/app/(landing)/signup/_lib/cookies.ts b/app/(landing)/signup/_lib/cookies.ts index 8989e1bf..1c09ff4f 100644 --- a/app/(landing)/signup/_lib/cookies.ts +++ b/app/(landing)/signup/_lib/cookies.ts @@ -1,6 +1,6 @@ 'use client' -import { UserType } from '@/shared/types/user' +import { UserType } from '@/shared/types/auth' import { SIGN_UP_COOKIE, SignUpCookieValueType } from '../_constants/cookies' diff --git a/app/(landing)/signup/_ui/signup-complete-message/index.tsx b/app/(landing)/signup/_ui/signup-complete-message/index.tsx index bca00890..4e0eb928 100644 --- a/app/(landing)/signup/_ui/signup-complete-message/index.tsx +++ b/app/(landing)/signup/_ui/signup-complete-message/index.tsx @@ -3,7 +3,7 @@ import Logo from '@/public/images/logo.svg' import classNames from 'classnames/bind' -import { UserType } from '@/shared/types/user' +import { UserType } from '@/shared/types/auth' import styles from './styles.module.scss' diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx index 55ef1c8f..aa3f6a71 100644 --- a/app/(landing)/signup/_ui/user-type-card/index.tsx +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -10,7 +10,7 @@ import { import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' -import { UserType } from '@/shared/types/user' +import { UserType } from '@/shared/types/auth' import styles from './styles.module.scss' diff --git a/mocks/handlers/auth.ts b/mocks/handlers/auth.ts index bdc1d7e8..54bf974d 100644 --- a/mocks/handlers/auth.ts +++ b/mocks/handlers/auth.ts @@ -1,30 +1,39 @@ -import { jwtDecode } from 'jwt-decode' import { HttpResponse, http } from 'msw' import { ERROR_MESSAGES } from '@/shared/constants/error-messages' -import { LoginFormDataModel, TokenPayloadModel, UserModel } from '@/shared/types/auth' +import { LoginFormDataModel, TokenPayloadModel, UserModel, isAdmin } from '@/shared/types/auth' const MOCK_USERS: Record<string, UserModel> = { 'test@example.com': { id: '1', email: 'test@example.com', - name: 'user1', - role: 'user', + name: '김다은', + nickname: 'devdeun', + role: 'trader', createdAt: '2024-01-01T00:00:00Z', + imageUrl: 'https://randomuser.me/api', }, 'admin@example.com': { id: '2', email: 'admin@example.com', - name: 'user2', - role: 'admin', + name: '권혁준', + nickname: 'redhero', + role: 'trader_admin', createdAt: '2024-01-01T00:00:00Z', + imageUrl: 'https://randomuser.me/api', }, } const createToken = (user: UserModel, isAdmin: boolean) => { const now = Math.floor(Date.now() / 1000) const exp = now + (isAdmin ? 1800 : 7 * 24 * 60 * 60) - return `mock_${btoa(JSON.stringify({ user, exp, iat: now }))}` + const payload = { user, exp, iat: now } + return `mock_${btoa(encodeURIComponent(JSON.stringify(payload)))}` +} + +const decodeToken = (token: string) => { + const payload = token.replace('mock_', '') + return JSON.parse(decodeURIComponent(atob(payload))) as TokenPayloadModel } export const authHandlers = [ @@ -33,9 +42,8 @@ export const authHandlers = [ const user = MOCK_USERS[email] if (user && password === 'password123') { - const isAdmin = user.role === 'admin' - const accessToken = createToken(user, isAdmin) - const refreshToken = createToken(user, isAdmin) + const accessToken = createToken(user, isAdmin(user)) + const refreshToken = createToken(user, isAdmin(user)) return HttpResponse.json({ isSuccess: true, @@ -66,11 +74,10 @@ export const authHandlers = [ } try { - const decoded = jwtDecode<TokenPayloadModel>(refreshToken) - const isAdmin = decoded.user.role === 'admin' + const decoded = decodeToken(refreshToken) - const accessToken = createToken(decoded.user, isAdmin) - const newRefreshToken = createToken(decoded.user, isAdmin) + const accessToken = createToken(decoded.user, isAdmin(decoded.user)) + const newRefreshToken = createToken(decoded.user, isAdmin(decoded.user)) return HttpResponse.json({ isSuccess: true, diff --git a/shared/api/axios.ts b/shared/api/axios.ts index 3c25ae72..566ad61f 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -5,6 +5,8 @@ import { getAccessToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' import { getUserFromToken, isTokenExpired, refreshToken } from '@/shared/utils/token-utils' +import { isAdmin } from '../types/auth' + export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { const instance = axios.create() @@ -20,9 +22,8 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} try { const user = getUserFromToken(accessToken) - const isAdmin = user?.role === 'admin' - if (isAdmin && isTokenExpired(accessToken)) { + if (isAdmin(user) && isTokenExpired(accessToken)) { useAuthStore.getState().setAuthState({ isAuthenticated: false, user: null, diff --git a/shared/api/user.ts b/shared/api/user.ts deleted file mode 100644 index 51399bb9..00000000 --- a/shared/api/user.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { UserModel } from '@/shared/types/user' - -export const fetchUser = () => { - const user: UserModel = { - id: '123', - imageUrl: 'S3 링크', - nickname: '김아무개씨', - email: 'kimamugae@naver.com', - role: 'trader_admin', - } - - return user -} diff --git a/shared/hooks/custom/use-auth.ts b/shared/hooks/custom/use-auth.ts index 967d5d90..06986698 100644 --- a/shared/hooks/custom/use-auth.ts +++ b/shared/hooks/custom/use-auth.ts @@ -1,14 +1,84 @@ import { useEffect } from 'react' -import { useLogoutMutation } from '@/shared/hooks/query/auth-queries' -import { getAccessToken, getRefreshToken } from '@/shared/lib/auth-tokens' +import { useRouter } from 'next/navigation' + +import { AUTH_TIME } from '@/shared/constants/auth' +import { PATH } from '@/shared/constants/path' +import { + getAccessToken, + getRefreshToken, + removeAccessToken, + removeRefreshToken, +} from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' -import { getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' +import { TokenStatusModel, isAdmin } from '@/shared/types/auth' +import { getTimeUntilExpiry, getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' export const useAuth = () => { - const { user, isAuthenticated, isKeepLoggedIn } = useAuthStore() - const logout = useLogoutMutation() - // const { data: tokenStatus } = useTokenStatusQuery(logout) + const router = useRouter() + const { user, isKeepLoggedIn } = useAuthStore() + + const logout = () => { + try { + removeAccessToken() + removeRefreshToken() + sessionStorage.removeItem('sessionToken') + + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + isKeepLoggedIn: false, + isLoggedOut: true, + }) + + router.replace(PATH.SIGN_IN) + } catch (error) { + console.error('로그아웃 실패:', error) + } + } + + const checkTokenStatus = (): TokenStatusModel | null => { + const accessToken = getAccessToken() + const refreshToken = getRefreshToken() + + if (!accessToken || !refreshToken) { + logout() + return null + } + + const tokenUser = getUserFromToken(accessToken) + if (!tokenUser) { + logout() + return null + } + + const isExpired = isTokenExpired(accessToken) + const timeUntilExpiry = getTimeUntilExpiry(accessToken) + + if (isAdmin(tokenUser)) { + if (isExpired) { + logout() + return null + } + return { + isValid: !isExpired, + timeUntilExpiry, + isNearExpiry: timeUntilExpiry < AUTH_TIME.ADMIN_EXPIRY_WARNING, + } + } + + const sessionToken = sessionStorage.getItem('sessionToken') + if (!isKeepLoggedIn && !sessionToken) { + logout() + return null + } + + return { + isValid: !isExpired, + timeUntilExpiry, + isNearExpiry: false, + } + } useEffect(() => { const initializeAuth = () => { @@ -20,12 +90,12 @@ export const useAuth = () => { const tokenUser = getUserFromToken(accessToken) if (!tokenUser) return - if (tokenUser.role === 'admin' && isTokenExpired(accessToken)) { + if (isAdmin(tokenUser) && isTokenExpired(accessToken)) { logout() return } - if (tokenUser.role === 'user') { + if (!isAdmin(tokenUser)) { const sessionToken = sessionStorage.getItem('sessionToken') if (!isKeepLoggedIn && !sessionToken) { logout() @@ -43,14 +113,13 @@ export const useAuth = () => { } // initializeAuth() - }, []) + + // const interval = setInterval(checkTokenStatus, AUTH_TIME.TOKEN_CHECK_INTERVAL) + // return () => clearInterval(interval) + }, [isKeepLoggedIn, router, user]) return { - user, - isAuthenticated, - isKeepLoggedIn, - // tokenStatus, - // isAdminNearExpiry: tokenStatus?.isNearExpiry, logout, + checkTokenStatus, } } diff --git a/shared/hooks/query/auth-queries.ts b/shared/hooks/query/auth-queries.ts index 7f45ce67..169a6c60 100644 --- a/shared/hooks/query/auth-queries.ts +++ b/shared/hooks/query/auth-queries.ts @@ -1,32 +1,19 @@ -import { useRouter } from 'next/navigation' - -import { useMutation, useQuery } from '@tanstack/react-query' +import { useMutation } from '@tanstack/react-query' import { login } from '@/shared/api/auth' -import { AUTH_TIME } from '@/shared/constants/auth' -import { PATH } from '@/shared/constants/path' -import { - getAccessToken, - getRefreshToken, - removeAccessToken, - removeRefreshToken, - setAccessToken, - setRefreshToken, -} from '@/shared/lib/auth-tokens' +import { setAccessToken, setRefreshToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' -import { LoginResponseType, TokenStatusModel } from '@/shared/types/auth' -import { getTimeUntilExpiry, getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' +import { LoginResponseType, isAdmin } from '@/shared/types/auth' export const useLoginMutation = () => { return useMutation({ mutationFn: login, onSuccess: (response: LoginResponseType) => { const { user, accessToken, refreshToken } = response.data - setAccessToken(accessToken) setRefreshToken(refreshToken) - if (user.role === 'user') { + if (!isAdmin(user)) { sessionStorage.setItem('sessionToken', 'true') } @@ -38,79 +25,3 @@ export const useLoginMutation = () => { }, }) } - -export const useLogoutMutation = () => { - const router = useRouter() - - return () => { - try { - removeAccessToken() - removeRefreshToken() - sessionStorage.removeItem('sessionToken') - - useAuthStore.getState().setAuthState({ - isAuthenticated: false, - user: null, - isKeepLoggedIn: false, - isLoggedOut: true, - }) - - router.replace(PATH.SIGN_IN) - } catch (error) { - console.error('로그아웃 실패:', error) - } - } -} - -// export const useTokenStatusQuery = (logout: () => void) => { -// const { isKeepLoggedIn } = useAuthStore() - -// return useQuery<TokenStatusModel | null>({ -// queryKey: ['tokenStatus'], -// queryFn: async () => { -// const accessToken = getAccessToken() -// const refreshToken = getRefreshToken() - -// if (!accessToken || !refreshToken) { -// logout() -// return null -// } - -// const user = getUserFromToken(accessToken) -// if (!user) { -// logout() -// return null -// } - -// const isExpired = isTokenExpired(accessToken) -// const timeUntilExpiry = getTimeUntilExpiry(accessToken) - -// if (user.role === 'admin') { -// if (isExpired) { -// // logout() -// return null -// } -// return { -// isValid: !isExpired, -// timeUntilExpiry, -// isNearExpiry: timeUntilExpiry < AUTH_TIME.ADMIN_EXPIRY_WARNING, -// } -// } - -// if (user.role === 'user') { -// const sessionToken = sessionStorage.getItem('sessionToken') -// if (!isKeepLoggedIn && !sessionToken) { -// // logout() -// return null -// } -// } - -// return { -// isValid: !isExpired, -// timeUntilExpiry, -// isNearExpiry: false, -// } -// }, -// refetchInterval: AUTH_TIME.TOKEN_CHECK_INTERVAL, -// }) -// } diff --git a/shared/types/auth.ts b/shared/types/auth.ts index 7d8b617d..d8fce10e 100644 --- a/shared/types/auth.ts +++ b/shared/types/auth.ts @@ -1,11 +1,15 @@ -export type UserRoleType = 'admin' | 'user' +export type UserType = 'trader' | 'investor' + +export type RoleType = UserType | `${UserType}_admin` export interface UserModel { id: string email: string name: string - role: UserRoleType + nickname: string + role: RoleType createdAt?: string + imageUrl: string } export interface LoginFormDataModel { @@ -47,10 +51,12 @@ export interface TokenStatusModel { isNearExpiry: boolean } -export const isAdmin = (user: UserModel | null): user is UserModel & { role: 'admin' } => { - return user?.role === 'admin' +export const isAdmin = (user: UserModel | null): boolean => { + if (!user) return false + return user.role.includes('admin') } -export const isUser = (user: UserModel | null): user is UserModel & { role: 'user' } => { - return user?.role === 'user' +export const isTrader = (user: UserModel | null): boolean => { + if (!user) return false + return user.role.includes('trader') } diff --git a/shared/types/user.ts b/shared/types/user.ts deleted file mode 100644 index cb0d7f84..00000000 --- a/shared/types/user.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface UserModel { - id: string - imageUrl: string - nickname: string - email: string - role: RoleType -} - -export type UserType = 'trader' | 'investor' - -export type RoleType = UserType | `${UserType}_admin` diff --git a/shared/ui/side-navigation/user-navigation.tsx b/shared/ui/side-navigation/user-navigation.tsx index 91986117..c503aedd 100644 --- a/shared/ui/side-navigation/user-navigation.tsx +++ b/shared/ui/side-navigation/user-navigation.tsx @@ -7,6 +7,7 @@ import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { useAuth } from '@/shared/hooks/custom/use-auth' +import { useAuthStore } from '@/shared/stores/use-auth-store' import { isAdmin } from '@/shared/types/auth' import NavButtonItem from '@/shared/ui/side-navigation/nav-button-item' import NavLinkItem from '@/shared/ui/side-navigation/nav-link-item' @@ -17,7 +18,8 @@ const cx = classNames.bind(styles) const UserNavigation = () => { const path = usePathname() - const { user, logout } = useAuth() + const { user } = useAuthStore() + const { logout } = useAuth() const isAdminPage = path.startsWith(PATH.ADMIN) if (!user) return null @@ -26,7 +28,7 @@ const UserNavigation = () => { <nav className={cx('user-navigation')} aria-label="사용자 메뉴"> <ul> <NavLinkItem href={PATH.PROFILE} icon={ProfileIcon} textClassName="user"> - <span className={cx('nickname')}>{user.name}</span> + <span className={cx('nickname')}>{user.nickname}</span> <span className={cx('email')}>{user.email}</span> </NavLinkItem> From abb29239c962c54e2507e2ce0b518004dd409bff Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 25 Nov 2024 09:04:19 +0900 Subject: [PATCH 312/648] =?UTF-8?q?feat:=20Footer=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/images/text-logo.svg | 24 +++++----- shared/constants/path.ts | 3 ++ shared/ui/footer/footer-logo.tsx | 19 ++++++++ shared/ui/footer/index.tsx | 59 ++++++++++++++++++++++++ shared/ui/footer/styles.module.scss | 69 +++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 12 deletions(-) create mode 100644 shared/ui/footer/footer-logo.tsx create mode 100644 shared/ui/footer/index.tsx create mode 100644 shared/ui/footer/styles.module.scss diff --git a/public/images/text-logo.svg b/public/images/text-logo.svg index 36d7c26b..2ef4724b 100644 --- a/public/images/text-logo.svg +++ b/public/images/text-logo.svg @@ -1,13 +1,13 @@ -<svg width="142" height="24" viewBox="0 0 142 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M0.132269 22.3365V8.07017H2.88346V22.3365H0.132269ZM0 5.27286V2H3.04219V5.27286H0Z" fill="#0A0A0A"/> -<path d="M6.74572 22.3365V8.07017H9.36464V11.427H9.49691V22.3365H6.74572ZM16.322 22.3365V13.1613C16.322 12.1729 16.0839 11.4363 15.6077 10.9514C15.1492 10.4665 14.4614 10.2241 13.5443 10.2241C12.7507 10.2241 12.0453 10.4106 11.428 10.7836C10.8284 11.1565 10.3522 11.6694 9.99953 12.3221C9.66445 12.9748 9.49691 13.7487 9.49691 14.6439L9.15301 11.2311C9.59391 10.1682 10.2729 9.32897 11.19 8.71356C12.107 8.09815 13.174 7.79044 14.3909 7.79044C15.837 7.79044 16.9745 8.21936 17.8034 9.07721C18.6323 9.93505 19.0467 11.0726 19.0467 12.4899V22.3365H16.322Z" fill="#0A0A0A"/> -<path d="M26.1082 22.3365L20.6058 8.07017H23.5422L28.0394 21.0217H26.7431L31.1874 8.07017H34.0179L28.542 22.3365H26.1082Z" fill="#0A0A0A"/> -<path d="M41.7319 22.6162C40.3739 22.6162 39.1747 22.2992 38.1342 21.6651C37.1113 21.0311 36.3089 20.1546 35.7269 19.0356C35.1449 17.9167 34.8539 16.63 34.8539 15.1754C34.8539 13.7021 35.1361 12.4153 35.7004 11.3151C36.2824 10.1961 37.0937 9.32897 38.1342 8.71356C39.1747 8.09815 40.3651 7.79044 41.7054 7.79044C43.0105 7.79044 44.1392 8.08882 45.0915 8.68558C46.0615 9.26369 46.8022 10.0749 47.3136 11.1192C47.8427 12.1636 48.1072 13.4037 48.1072 14.8397C48.1072 15.0635 48.0984 15.2779 48.0808 15.4831C48.0808 15.6695 48.0632 15.8654 48.0279 16.0705H36.7057V13.8886H46.0968L45.409 14.7558C45.409 13.2266 45.0827 12.061 44.4302 11.2591C43.7776 10.4572 42.8694 10.0563 41.7054 10.0563C40.4533 10.0563 39.4569 10.5132 38.7162 11.427C37.9931 12.3221 37.6315 13.5716 37.6315 15.1754C37.6315 16.7978 37.9931 18.0659 38.7162 18.9797C39.4569 19.8935 40.4797 20.3504 41.7848 20.3504C42.5784 20.3504 43.2662 20.1825 43.8482 19.8469C44.4302 19.4925 44.8623 18.9797 45.1444 18.3083H47.7369C47.296 19.6511 46.5465 20.7047 45.4883 21.4693C44.4478 22.2339 43.1957 22.6162 41.7319 22.6162Z" fill="#0A0A0A"/> -<path d="M55.8406 22.6162C54.0771 22.6162 52.675 22.2059 51.6345 21.3854C50.594 20.5648 50.0032 19.4086 49.8621 17.9167H52.4016C52.5251 18.7373 52.8778 19.3713 53.4598 19.8189C54.0594 20.2478 54.8707 20.4623 55.8935 20.4623C56.793 20.4623 57.4808 20.2944 57.9569 19.9588C58.4331 19.6231 58.6712 19.1475 58.6712 18.5321C58.6712 18.0846 58.5301 17.7116 58.2479 17.4132C57.9834 17.0962 57.4543 16.8351 56.6607 16.63L54.4915 16.0985C53.1335 15.7441 52.1195 15.222 51.4493 14.532C50.7968 13.8233 50.4705 12.9561 50.4705 11.9305C50.4705 10.6437 50.9202 9.63667 51.8197 8.90937C52.7367 8.16342 53.9889 7.79044 55.5761 7.79044C57.1457 7.79044 58.4067 8.16342 59.359 8.90937C60.329 9.65532 60.8757 10.6996 60.9991 12.0424H58.4596C58.3538 11.371 58.0451 10.8582 57.5337 10.5038C57.0399 10.1309 56.3609 9.94437 55.4967 9.94437C54.6855 9.94437 54.0594 10.1029 53.6185 10.4199C53.1953 10.7183 52.9836 11.1379 52.9836 11.6787C52.9836 12.1263 53.1424 12.4993 53.4598 12.7976C53.7949 13.096 54.3416 13.3478 55.0999 13.5529L57.322 14.1403C58.6095 14.476 59.5794 15.0262 60.232 15.7908C60.8845 16.5367 61.2108 17.4412 61.2108 18.5042C61.2108 19.7909 60.7346 20.798 59.7823 21.5253C58.8476 22.2526 57.5337 22.6162 55.8406 22.6162Z" fill="#0A0A0A"/> -<path d="M69.4755 22.6162C67.9059 22.6162 66.7508 22.2339 66.0101 21.4693C65.287 20.6861 64.9255 19.5392 64.9255 18.0286V4.79732L67.6502 3.70636V18.0566C67.6502 18.8025 67.8354 19.3527 68.2057 19.707C68.5761 20.0613 69.1757 20.2385 70.0046 20.2385C70.322 20.2385 70.6042 20.2105 70.8511 20.1546C71.098 20.0986 71.3096 20.0334 71.486 19.9588V22.3085C71.292 22.4017 71.0186 22.4763 70.6659 22.5323C70.3132 22.5882 69.9164 22.6162 69.4755 22.6162ZM62.2272 10.3919V8.07017H71.486V10.3919H62.2272Z" fill="#0A0A0A"/> -<path d="M74.0944 22.3365V8.07017H76.7133V11.427H76.8456V22.3365H74.0944ZM83.1945 22.3365V13.0214C83.1945 12.0517 82.9828 11.343 82.5596 10.8955C82.1363 10.4479 81.5279 10.2241 80.7343 10.2241C79.9936 10.2241 79.3234 10.4106 78.7238 10.7836C78.1418 11.1379 77.6833 11.6414 77.3482 12.2941C77.0131 12.9282 76.8456 13.6835 76.8456 14.5599L76.5017 11.2311C76.9426 10.1682 77.6039 9.32897 78.4857 8.71356C79.3851 8.09815 80.3904 7.79044 81.5014 7.79044C82.8418 7.79044 83.9087 8.20071 84.7023 9.02126C85.5136 9.84181 85.9192 10.9234 85.9192 12.2661V22.3365H83.1945ZM92.2946 22.3365V13.0214C92.2946 12.0517 92.0741 11.343 91.6332 10.8955C91.21 10.4479 90.6015 10.2241 89.8079 10.2241C89.0849 10.2241 88.4235 10.4106 87.8239 10.7836C87.2419 11.1379 86.7746 11.6414 86.4218 12.2941C86.0868 12.9282 85.9192 13.6835 85.9192 14.5599L85.3637 11.2311C85.8222 10.1682 86.51 9.32897 87.4271 8.71356C88.3618 8.09815 89.4023 7.79044 90.5486 7.79044C91.9066 7.79044 92.9912 8.21004 93.8024 9.04923C94.6137 9.86978 95.0193 10.9701 95.0193 12.3501V22.3365H92.2946Z" fill="#0A0A0A"/> -<path d="M104.534 22.6162C103.176 22.6162 101.977 22.2992 100.936 21.6651C99.9132 21.0311 99.1108 20.1546 98.5288 19.0356C97.9468 17.9167 97.6558 16.63 97.6558 15.1754C97.6558 13.7021 97.938 12.4153 98.5023 11.3151C99.0843 10.1961 99.8956 9.32897 100.936 8.71356C101.977 8.09815 103.167 7.79044 104.507 7.79044C105.812 7.79044 106.941 8.08882 107.893 8.68558C108.863 9.26369 109.604 10.0749 110.116 11.1192C110.645 12.1636 110.909 13.4037 110.909 14.8397C110.909 15.0635 110.9 15.2779 110.883 15.4831C110.883 15.6695 110.865 15.8654 110.83 16.0705H99.5076V13.8886H108.899L108.211 14.7558C108.211 13.2266 107.885 12.061 107.232 11.2591C106.58 10.4572 105.671 10.0563 104.507 10.0563C103.255 10.0563 102.259 10.5132 101.518 11.427C100.795 12.3221 100.433 13.5716 100.433 15.1754C100.433 16.7978 100.795 18.0659 101.518 18.9797C102.259 19.8935 103.282 20.3504 104.587 20.3504C105.38 20.3504 106.068 20.1825 106.65 19.8469C107.232 19.4925 107.664 18.9797 107.946 18.3083H110.539C110.098 19.6511 109.348 20.7047 108.29 21.4693C107.25 22.2339 105.998 22.6162 104.534 22.6162Z" fill="#0A0A0A"/> -<path d="M119.231 22.6162C117.662 22.6162 116.507 22.2339 115.766 21.4693C115.043 20.6861 114.681 19.5392 114.681 18.0286V4.79732L117.406 3.70636V18.0566C117.406 18.8025 117.591 19.3527 117.962 19.707C118.332 20.0613 118.932 20.2385 119.76 20.2385C120.078 20.2385 120.36 20.2105 120.607 20.1546C120.854 20.0986 121.065 20.0334 121.242 19.9588V22.3085C121.048 22.4017 120.774 22.4763 120.422 22.5323C120.069 22.5882 119.672 22.6162 119.231 22.6162ZM111.983 10.3919V8.07017H121.242V10.3919H111.983Z" fill="#0A0A0A"/> -<path d="M123.799 22.3365V8.07017H126.55V22.3365H123.799ZM123.666 5.27286V2H126.708V5.27286H123.666Z" fill="#0A0A0A"/> -<path d="M135.969 22.6162C134.681 22.6162 133.535 22.2992 132.53 21.6651C131.524 21.0124 130.731 20.1266 130.149 19.0077C129.584 17.8887 129.302 16.6113 129.302 15.1754C129.302 13.7394 129.584 12.4713 130.149 11.371C130.731 10.2521 131.524 9.37559 132.53 8.74153C133.535 8.10747 134.672 7.79044 135.942 7.79044C137.018 7.79044 137.979 8.0049 138.826 8.43382C139.672 8.86275 140.369 9.45951 140.915 10.2241C141.462 10.9887 141.815 11.8932 141.974 12.9375H139.381C139.205 12.0983 138.817 11.427 138.217 10.9234C137.635 10.4199 136.894 10.1682 135.995 10.1682C135.219 10.1682 134.54 10.3733 133.958 10.7836C133.376 11.1938 132.926 11.772 132.609 12.5179C132.291 13.2639 132.133 14.1497 132.133 15.1754C132.133 16.1824 132.291 17.0682 132.609 17.8328C132.926 18.5974 133.376 19.1942 133.958 19.6231C134.54 20.0334 135.228 20.2385 136.021 20.2385C136.886 20.2385 137.617 19.9961 138.217 19.5112C138.834 19.0077 139.231 18.3363 139.408 17.4971H142C141.824 18.5228 141.453 19.4179 140.889 20.1825C140.342 20.9471 139.646 21.5439 138.799 21.9728C137.953 22.4017 137.009 22.6162 135.969 22.6162Z" fill="#0A0A0A"/> +<svg width="142" height="24" viewBox="0 0 142 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M0.132269 22.3365V8.07017H2.88346V22.3365H0.132269ZM0 5.27286V2H3.04219V5.27286H0Z" fill="currentColor"/> +<path d="M6.74572 22.3365V8.07017H9.36464V11.427H9.49691V22.3365H6.74572ZM16.322 22.3365V13.1613C16.322 12.1729 16.0839 11.4363 15.6077 10.9514C15.1492 10.4665 14.4614 10.2241 13.5443 10.2241C12.7507 10.2241 12.0453 10.4106 11.428 10.7836C10.8284 11.1565 10.3522 11.6694 9.99953 12.3221C9.66445 12.9748 9.49691 13.7487 9.49691 14.6439L9.15301 11.2311C9.59391 10.1682 10.2729 9.32897 11.19 8.71356C12.107 8.09815 13.174 7.79044 14.3909 7.79044C15.837 7.79044 16.9745 8.21936 17.8034 9.07721C18.6323 9.93505 19.0467 11.0726 19.0467 12.4899V22.3365H16.322Z" fill="currentColor"/> +<path d="M26.1082 22.3365L20.6058 8.07017H23.5422L28.0394 21.0217H26.7431L31.1874 8.07017H34.0179L28.542 22.3365H26.1082Z" fill="currentColor"/> +<path d="M41.7319 22.6162C40.3739 22.6162 39.1747 22.2992 38.1342 21.6651C37.1113 21.0311 36.3089 20.1546 35.7269 19.0356C35.1449 17.9167 34.8539 16.63 34.8539 15.1754C34.8539 13.7021 35.1361 12.4153 35.7004 11.3151C36.2824 10.1961 37.0937 9.32897 38.1342 8.71356C39.1747 8.09815 40.3651 7.79044 41.7054 7.79044C43.0105 7.79044 44.1392 8.08882 45.0915 8.68558C46.0615 9.26369 46.8022 10.0749 47.3136 11.1192C47.8427 12.1636 48.1072 13.4037 48.1072 14.8397C48.1072 15.0635 48.0984 15.2779 48.0808 15.4831C48.0808 15.6695 48.0632 15.8654 48.0279 16.0705H36.7057V13.8886H46.0968L45.409 14.7558C45.409 13.2266 45.0827 12.061 44.4302 11.2591C43.7776 10.4572 42.8694 10.0563 41.7054 10.0563C40.4533 10.0563 39.4569 10.5132 38.7162 11.427C37.9931 12.3221 37.6315 13.5716 37.6315 15.1754C37.6315 16.7978 37.9931 18.0659 38.7162 18.9797C39.4569 19.8935 40.4797 20.3504 41.7848 20.3504C42.5784 20.3504 43.2662 20.1825 43.8482 19.8469C44.4302 19.4925 44.8623 18.9797 45.1444 18.3083H47.7369C47.296 19.6511 46.5465 20.7047 45.4883 21.4693C44.4478 22.2339 43.1957 22.6162 41.7319 22.6162Z" fill="currentColor"/> +<path d="M55.8406 22.6162C54.0771 22.6162 52.675 22.2059 51.6345 21.3854C50.594 20.5648 50.0032 19.4086 49.8621 17.9167H52.4016C52.5251 18.7373 52.8778 19.3713 53.4598 19.8189C54.0594 20.2478 54.8707 20.4623 55.8935 20.4623C56.793 20.4623 57.4808 20.2944 57.9569 19.9588C58.4331 19.6231 58.6712 19.1475 58.6712 18.5321C58.6712 18.0846 58.5301 17.7116 58.2479 17.4132C57.9834 17.0962 57.4543 16.8351 56.6607 16.63L54.4915 16.0985C53.1335 15.7441 52.1195 15.222 51.4493 14.532C50.7968 13.8233 50.4705 12.9561 50.4705 11.9305C50.4705 10.6437 50.9202 9.63667 51.8197 8.90937C52.7367 8.16342 53.9889 7.79044 55.5761 7.79044C57.1457 7.79044 58.4067 8.16342 59.359 8.90937C60.329 9.65532 60.8757 10.6996 60.9991 12.0424H58.4596C58.3538 11.371 58.0451 10.8582 57.5337 10.5038C57.0399 10.1309 56.3609 9.94437 55.4967 9.94437C54.6855 9.94437 54.0594 10.1029 53.6185 10.4199C53.1953 10.7183 52.9836 11.1379 52.9836 11.6787C52.9836 12.1263 53.1424 12.4993 53.4598 12.7976C53.7949 13.096 54.3416 13.3478 55.0999 13.5529L57.322 14.1403C58.6095 14.476 59.5794 15.0262 60.232 15.7908C60.8845 16.5367 61.2108 17.4412 61.2108 18.5042C61.2108 19.7909 60.7346 20.798 59.7823 21.5253C58.8476 22.2526 57.5337 22.6162 55.8406 22.6162Z" fill="currentColor"/> +<path d="M69.4755 22.6162C67.9059 22.6162 66.7508 22.2339 66.0101 21.4693C65.287 20.6861 64.9255 19.5392 64.9255 18.0286V4.79732L67.6502 3.70636V18.0566C67.6502 18.8025 67.8354 19.3527 68.2057 19.707C68.5761 20.0613 69.1757 20.2385 70.0046 20.2385C70.322 20.2385 70.6042 20.2105 70.8511 20.1546C71.098 20.0986 71.3096 20.0334 71.486 19.9588V22.3085C71.292 22.4017 71.0186 22.4763 70.6659 22.5323C70.3132 22.5882 69.9164 22.6162 69.4755 22.6162ZM62.2272 10.3919V8.07017H71.486V10.3919H62.2272Z" fill="currentColor"/> +<path d="M74.0944 22.3365V8.07017H76.7133V11.427H76.8456V22.3365H74.0944ZM83.1945 22.3365V13.0214C83.1945 12.0517 82.9828 11.343 82.5596 10.8955C82.1363 10.4479 81.5279 10.2241 80.7343 10.2241C79.9936 10.2241 79.3234 10.4106 78.7238 10.7836C78.1418 11.1379 77.6833 11.6414 77.3482 12.2941C77.0131 12.9282 76.8456 13.6835 76.8456 14.5599L76.5017 11.2311C76.9426 10.1682 77.6039 9.32897 78.4857 8.71356C79.3851 8.09815 80.3904 7.79044 81.5014 7.79044C82.8418 7.79044 83.9087 8.20071 84.7023 9.02126C85.5136 9.84181 85.9192 10.9234 85.9192 12.2661V22.3365H83.1945ZM92.2946 22.3365V13.0214C92.2946 12.0517 92.0741 11.343 91.6332 10.8955C91.21 10.4479 90.6015 10.2241 89.8079 10.2241C89.0849 10.2241 88.4235 10.4106 87.8239 10.7836C87.2419 11.1379 86.7746 11.6414 86.4218 12.2941C86.0868 12.9282 85.9192 13.6835 85.9192 14.5599L85.3637 11.2311C85.8222 10.1682 86.51 9.32897 87.4271 8.71356C88.3618 8.09815 89.4023 7.79044 90.5486 7.79044C91.9066 7.79044 92.9912 8.21004 93.8024 9.04923C94.6137 9.86978 95.0193 10.9701 95.0193 12.3501V22.3365H92.2946Z" fill="currentColor"/> +<path d="M104.534 22.6162C103.176 22.6162 101.977 22.2992 100.936 21.6651C99.9132 21.0311 99.1108 20.1546 98.5288 19.0356C97.9468 17.9167 97.6558 16.63 97.6558 15.1754C97.6558 13.7021 97.938 12.4153 98.5023 11.3151C99.0843 10.1961 99.8956 9.32897 100.936 8.71356C101.977 8.09815 103.167 7.79044 104.507 7.79044C105.812 7.79044 106.941 8.08882 107.893 8.68558C108.863 9.26369 109.604 10.0749 110.116 11.1192C110.645 12.1636 110.909 13.4037 110.909 14.8397C110.909 15.0635 110.9 15.2779 110.883 15.4831C110.883 15.6695 110.865 15.8654 110.83 16.0705H99.5076V13.8886H108.899L108.211 14.7558C108.211 13.2266 107.885 12.061 107.232 11.2591C106.58 10.4572 105.671 10.0563 104.507 10.0563C103.255 10.0563 102.259 10.5132 101.518 11.427C100.795 12.3221 100.433 13.5716 100.433 15.1754C100.433 16.7978 100.795 18.0659 101.518 18.9797C102.259 19.8935 103.282 20.3504 104.587 20.3504C105.38 20.3504 106.068 20.1825 106.65 19.8469C107.232 19.4925 107.664 18.9797 107.946 18.3083H110.539C110.098 19.6511 109.348 20.7047 108.29 21.4693C107.25 22.2339 105.998 22.6162 104.534 22.6162Z" fill="currentColor"/> +<path d="M119.231 22.6162C117.662 22.6162 116.507 22.2339 115.766 21.4693C115.043 20.6861 114.681 19.5392 114.681 18.0286V4.79732L117.406 3.70636V18.0566C117.406 18.8025 117.591 19.3527 117.962 19.707C118.332 20.0613 118.932 20.2385 119.76 20.2385C120.078 20.2385 120.36 20.2105 120.607 20.1546C120.854 20.0986 121.065 20.0334 121.242 19.9588V22.3085C121.048 22.4017 120.774 22.4763 120.422 22.5323C120.069 22.5882 119.672 22.6162 119.231 22.6162ZM111.983 10.3919V8.07017H121.242V10.3919H111.983Z" fill="currentColor"/> +<path d="M123.799 22.3365V8.07017H126.55V22.3365H123.799ZM123.666 5.27286V2H126.708V5.27286H123.666Z" fill="currentColor"/> +<path d="M135.969 22.6162C134.681 22.6162 133.535 22.2992 132.53 21.6651C131.524 21.0124 130.731 20.1266 130.149 19.0077C129.584 17.8887 129.302 16.6113 129.302 15.1754C129.302 13.7394 129.584 12.4713 130.149 11.371C130.731 10.2521 131.524 9.37559 132.53 8.74153C133.535 8.10747 134.672 7.79044 135.942 7.79044C137.018 7.79044 137.979 8.0049 138.826 8.43382C139.672 8.86275 140.369 9.45951 140.915 10.2241C141.462 10.9887 141.815 11.8932 141.974 12.9375H139.381C139.205 12.0983 138.817 11.427 138.217 10.9234C137.635 10.4199 136.894 10.1682 135.995 10.1682C135.219 10.1682 134.54 10.3733 133.958 10.7836C133.376 11.1938 132.926 11.772 132.609 12.5179C132.291 13.2639 132.133 14.1497 132.133 15.1754C132.133 16.1824 132.291 17.0682 132.609 17.8328C132.926 18.5974 133.376 19.1942 133.958 19.6231C134.54 20.0334 135.228 20.2385 136.021 20.2385C136.886 20.2385 137.617 19.9961 138.217 19.5112C138.834 19.0077 139.231 18.3363 139.408 17.4971H142C141.824 18.5228 141.453 19.4179 140.889 20.1825C140.342 20.9471 139.646 21.5439 138.799 21.9728C137.953 22.4017 137.009 22.6162 135.969 22.6162Z" fill="currentColor"/> </svg> diff --git a/shared/constants/path.ts b/shared/constants/path.ts index 72909a11..cb3b0796 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -11,6 +11,9 @@ export const PATH = { HOME: '/', STRATEGIES: '/strategies', TRADERS: '/traders', + NOTICES: '/notices', + TERMS_OF_USE: '/terms-of-use', + PRIVACY_TERMS: '/privacy-terms', // My PROFILE: '/my/profile', diff --git a/shared/ui/footer/footer-logo.tsx b/shared/ui/footer/footer-logo.tsx new file mode 100644 index 00000000..603286f4 --- /dev/null +++ b/shared/ui/footer/footer-logo.tsx @@ -0,0 +1,19 @@ +'use client' + +import { ImageLogo, TextLogo } from '@/public/images' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const FooterLogo = () => { + return ( + <div className={cx('logo-container')}> + <ImageLogo width={108} /> + <TextLogo width={178} className={cx('text')} /> + </div> + ) +} + +export default FooterLogo diff --git a/shared/ui/footer/index.tsx b/shared/ui/footer/index.tsx new file mode 100644 index 00000000..cd860d3d --- /dev/null +++ b/shared/ui/footer/index.tsx @@ -0,0 +1,59 @@ +import Link from 'next/link' + +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' + +import FooterLogo from './footer-logo' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const Footer = () => { + return ( + <footer className={cx('footer-container')}> + <div className={cx('footer-wrapper')}> + <FooterLogo /> + <div className={cx('info-wrapper')}> + <div className={cx('info')}> + <strong>(주)인베스트메틱</strong> + <ul className={cx('contents')}> + <li>사업자 등록 번호 ㅣ 711-86-00050</li> + <li>통신판매업신고 ㅣ 제2020-서울 영등포-2864호</li> + <li>특허출원번호 ㅣ 10-2016-00262203</li> + </ul> + </div> + <div className={cx('info')}> + <strong>CONTACT</strong> + <ul className={cx('contents')}> + <li> + 서울시 영등포구 당산로41길 11, <br /> + E동 1202호 + </li> + <li>+82-2-6338-1880</li> + </ul> + </div> + </div> + </div> + <div className={cx('copyright-container')}> + <div className={cx('copyright-wrapper')}> + <p className={cx('copyright')}>COPYRIGHT INVESTMETIC ALL RIGHTS RESERVED</p> + + <ul className={cx('links')}> + <li> + <Link href={PATH.TERMS_OF_USE}>이용약관</Link> + </li> + <li> + <Link href={PATH.PRIVACY_TERMS}>개인정보 취급방침</Link> + </li> + <li> + <Link href={PATH.NOTICES}>공지사항</Link> + </li> + </ul> + </div> + </div> + </footer> + ) +} + +export default Footer diff --git a/shared/ui/footer/styles.module.scss b/shared/ui/footer/styles.module.scss new file mode 100644 index 00000000..d48ca79e --- /dev/null +++ b/shared/ui/footer/styles.module.scss @@ -0,0 +1,69 @@ +.footer-container { + background-color: $color-gray-800; + color: $color-gray-400; +} + +.footer-wrapper { + display: flex; + justify-content: space-between; + align-items: center; + max-width: $max-width; + height: 220px; + padding: 0 40px; + margin: 0 auto; + + strong { + display: block; + color: $color-gray-100; + margin-bottom: 30px; + } +} + +.info-wrapper { + display: flex; + gap: 60px; + + .contents { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 82px; + } +} + +.copyright-container { + height: 56px; + @include typo-b2; + color: $color-gray-800; + background-color: $color-gray-100; +} + +.copyright-wrapper { + display: flex; + justify-content: space-between; + align-items: center; + max-width: $max-width; + height: 100%; + margin: 0 auto; + padding: 0 40px; + + .links { + display: flex; + gap: 40px; + + a:hover { + text-decoration: underline; + } + } +} + +.logo-container { + display: flex; + flex-direction: column; + gap: 12px; + + .text { + margin-left: 2px; + fill: $color-gray-100; + } +} From 1016fbb37583ce8f664c77ee88c175e313756f29 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 25 Nov 2024 16:06:44 +0900 Subject: [PATCH 313/648] =?UTF-8?q?design:=20Footer=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/footer/footer-logo.tsx | 4 ++-- shared/ui/footer/index.tsx | 2 +- shared/ui/footer/styles.module.scss | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/shared/ui/footer/footer-logo.tsx b/shared/ui/footer/footer-logo.tsx index 603286f4..655f0e5a 100644 --- a/shared/ui/footer/footer-logo.tsx +++ b/shared/ui/footer/footer-logo.tsx @@ -10,8 +10,8 @@ const cx = classNames.bind(styles) const FooterLogo = () => { return ( <div className={cx('logo-container')}> - <ImageLogo width={108} /> - <TextLogo width={178} className={cx('text')} /> + <ImageLogo width={100} /> + <TextLogo width={177} className={cx('text')} /> </div> ) } diff --git a/shared/ui/footer/index.tsx b/shared/ui/footer/index.tsx index cd860d3d..707489f5 100644 --- a/shared/ui/footer/index.tsx +++ b/shared/ui/footer/index.tsx @@ -37,7 +37,7 @@ const Footer = () => { </div> <div className={cx('copyright-container')}> <div className={cx('copyright-wrapper')}> - <p className={cx('copyright')}>COPYRIGHT INVESTMETIC ALL RIGHTS RESERVED</p> + <p className={cx('copyright')}>Copyright 2024 Investmetic Co. All Rights Reserved.</p> <ul className={cx('links')}> <li> diff --git a/shared/ui/footer/styles.module.scss b/shared/ui/footer/styles.module.scss index d48ca79e..6648cf7a 100644 --- a/shared/ui/footer/styles.module.scss +++ b/shared/ui/footer/styles.module.scss @@ -6,10 +6,9 @@ .footer-wrapper { display: flex; justify-content: space-between; - align-items: center; + align-items: flex-end; max-width: $max-width; - height: 220px; - padding: 0 40px; + padding: 40px; margin: 0 auto; strong { From 9b4dda18019585740044314007fb31bc3c516568 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 25 Nov 2024 18:40:24 +0900 Subject: [PATCH 314/648] =?UTF-8?q?feat:=20layout=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=97=90=20Footer=20=EC=B6=94=EA=B0=80=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/layout.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/(landing)/layout.tsx b/app/(landing)/layout.tsx index 2faa7eb8..2668bf4c 100644 --- a/app/(landing)/layout.tsx +++ b/app/(landing)/layout.tsx @@ -2,6 +2,7 @@ import { usePathname } from 'next/navigation' +import Footer from '@/shared/ui/footer' import LogoHeader from '@/shared/ui/header/logo-header' interface Props { @@ -11,15 +12,13 @@ interface Props { const LandingLayout = ({ children }: Props) => { const pathname = usePathname() - const getHeaderType = () => { - if (pathname.includes('/signin') || pathname.includes('/signup')) return false - return true - } + const hasFooter = !pathname.includes('/signin') && !pathname.includes('/signup') return ( <> - <LogoHeader hasText={true} hasLinks={getHeaderType()} /> + <LogoHeader hasText={true} hasLinks={true} /> <main className="landing-main">{children}</main> + {hasFooter && <Footer />} </> ) } From c292aa9385cbf2927d3094a6454fbfbb048a9095 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 25 Nov 2024 18:43:38 +0900 Subject: [PATCH 315/648] =?UTF-8?q?design:=20Header=EC=97=90=20zIndex=20?= =?UTF-8?q?=EB=B0=8F=20padding=20=EC=B6=94=EA=B0=80=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/styles.module.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/ui/header/styles.module.scss b/shared/ui/header/styles.module.scss index 211b3c25..6fbb1f74 100644 --- a/shared/ui/header/styles.module.scss +++ b/shared/ui/header/styles.module.scss @@ -1,4 +1,5 @@ .container { + z-index: zIndex(header); position: sticky; top: 0; height: $header-height; @@ -6,4 +7,5 @@ display: flex; justify-content: space-between; align-items: center; + padding: 0 40px; } From 088e78028b691394364561606d4caccdfb97af41 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 25 Nov 2024 20:04:57 +0900 Subject: [PATCH 316/648] =?UTF-8?q?fix:=20=EC=82=AD=EC=A0=9C=EB=90=9C=20ap?= =?UTF-8?q?i=20=EC=A0=9C=EA=B1=B0=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/review-container/review-list.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx index 63173d24..d8baccbc 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx @@ -1,6 +1,5 @@ import classNames from 'classnames/bind' -import { fetchUser } from '@/shared/api/user' import Pagination from '@/shared/ui/pagination' import ReviewItem from './review-item' @@ -29,8 +28,6 @@ interface Props { const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props) => { const handlePageChange = (page: number) => setCurrentPage(page) - const user = fetchUser() - return ( <> <ul className={cx('review-list')}> @@ -42,7 +39,7 @@ const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props createdAt={review.createdAt} starRating={review.starRating} content={review.content} - isReviewer={user.nickname === review.nickname} + isReviewer={'' === review.nickname} /> ))} </ul> From 007bda1ef5b9ad4fbcb200fb0183ad4ca19efa8f Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 25 Nov 2024 20:11:07 +0900 Subject: [PATCH 317/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20api=20=EC=88=98=EC=A0=95,=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_api/get-details-information.ts | 22 +++++++++++++++++-- .../query/use-get-details-information-data.ts | 15 ++++++++++++- shared/types/strategy-details-data.ts | 19 ++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index 3f904ba3..7ef57317 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -12,7 +12,7 @@ const getDetailsInformation = async (isReady: boolean, strategyId: string) => { return } const data = await response.data - const newDetailsData: InformationType[] = [ + const detailsSideData: InformationType[] = [ { title: '트레이더', data: data.nickname }, { title: '최소 투자 금액', data: data.minimumInvestmentAmount }, { title: '투자 원금', data: data.initialInvestment }, @@ -27,7 +27,25 @@ const getDetailsInformation = async (isReady: boolean, strategyId: string) => { { title: '등록일', data: data.createdAt }, ], ] - return newDetailsData + const detailsInformationData = { + strategyId: data.strategyId, + strategyName: data.strategyName, + stockTypeIconURLs: data.stockTypeIconURLs, + tradeTypeIconURL: data.tradeTypeIconURL, + stockTypeNames: data.stockTypeNames, + tradeTypeName: data.tradeTypeName, + operationCycle: data.operationCycle, + strategyDescription: data.strategyDescription, + cumulativeProfitRate: data.cumulativeProfitRate, + maxDrawdownRate: data.maxDrawdownRate, + averageProfitLossRate: data.averageProfitLossRate, + profitFactor: data.profitFactor, + winRate: data.winRate, + subscriptionCount: data.subscriptionCount, + traderImgUrl: data.traderImgUrl, + subscribed: data.subscribed, + } + return { detailsSideData, detailsInformationData } } catch (err) { console.error(err) } diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts index 4ddd8aa1..419646cf 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts @@ -1,13 +1,26 @@ import { useQuery } from '@tanstack/react-query' +import { StrategyDetailsInformationModel } from '@/shared/types/strategy-details-data' + import getDetailsInformation from '../../_api/get-details-information' +import { InformationType } from '../../page' interface Props { isReady: boolean strategyId: string } -const useGetDetailsInformationData = ({ isReady, strategyId }: Props) => { +const useGetDetailsInformationData = ({ + isReady, + strategyId, +}: Props): { + data: + | { + detailsSideData: InformationType[] + detailsInformationData: StrategyDetailsInformationModel + } + | undefined +} => { return useQuery({ queryKey: ['strategyDetails', strategyId], queryFn: () => getDetailsInformation(isReady, strategyId), diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts index 4a8ffe89..1e91765a 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-details-data.ts @@ -43,3 +43,22 @@ export interface StrategiesModel { averageRating: number totalReview: number } + +export interface StrategyDetailsInformationModel { + strategyId: string + strategyName: string + stockTypeIconURLs: string[] + tradeTypeIconURL: string + stockTypeNames: string[] + tradeTypeName: string + operationCycle: string + strategyDescription: string + cumulativeProfitRate: number + maxDrawdownRate: number + averageProfitLossRate: number + profitFactor: number + winRate: number + subscriptionCount: number + traderImgUrl: string + subscribed: boolean +} From c31660700152b7192c802f52d65237102fb855de Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 25 Nov 2024 20:12:03 +0900 Subject: [PATCH 318/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=83=81=EB=8B=A8=20=EC=A0=95=EB=B3=B4=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/datails-information/index.tsx | 49 +++++++++++ .../invest-information.tsx | 31 +++++++ .../_ui/datails-information/percentage.tsx | 26 ++++++ .../datails-information/strategy-name-box.tsx | 26 ++++++ .../datails-information/styles.module.scss | 81 +++++++++++++++++++ .../strategies/[strategyId]/page.tsx | 7 +- mocks/handlers.ts | 1 - 7 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/datails-information/index.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/datails-information/invest-information.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/datails-information/percentage.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/datails-information/strategy-name-box.tsx create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/datails-information/styles.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/index.tsx new file mode 100644 index 00000000..f07ac0e5 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/index.tsx @@ -0,0 +1,49 @@ +import classNames from 'classnames/bind' + +import { StrategyDetailsInformationModel } from '@/shared/types/strategy-details-data' + +import StrategyIntroduction from '../introduction/index' +import InvestInformation from './invest-information' +import Percentage from './percentage' +import StrategyNameBox from './strategy-name-box' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + information: StrategyDetailsInformationModel +} + +const DetailsInformation = ({ information }: Props) => { + const percentageToArray = [ + { percent: information.cumulativeProfitRate, label: '누적 수익률' }, + { percent: information.maxDrawdownRate, label: '최대 자본 인하율' }, + { percent: information.averageProfitLossRate, label: '평균 손익률' }, + { percent: information.profitFactor, label: 'Profit Factor' }, + { percent: information.winRate, label: '승률' }, + ] + + return ( + <> + <div className={cx('information-top')}> + <StrategyNameBox + iconUrl={[information.tradeTypeIconURL, ...information.stockTypeIconURLs]} + name={information.strategyName} + /> + <InvestInformation + stock={information.stockTypeNames} + trade={information.tradeTypeName} + cycle={information.operationCycle} + /> + </div> + <StrategyIntroduction content={information.strategyDescription} /> + <div className={cx('percentage-container')}> + {percentageToArray.map((data) => ( + <Percentage key={data.label} percent={data.percent} label={data.label} /> + ))} + </div> + </> + ) +} + +export default DetailsInformation diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/invest-information.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/invest-information.tsx new file mode 100644 index 00000000..9a9a78a8 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/invest-information.tsx @@ -0,0 +1,31 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + stock: string[] + trade: string + cycle: string +} + +const InvestInformation = ({ stock, trade, cycle }: Props) => { + const investData = [ + { title: '투자 종목', data: stock.join(',') }, + { title: '매매 유형', data: trade }, + { title: '투자 주기', data: cycle }, + ] + return ( + <div className={cx('invest-container')}> + {investData.map((data, idx) => ( + <div key={`${data}${idx}`} className={cx('info-item')}> + <p className={cx('invest-title')}>{data.title}</p> + <p className={cx('invest-data')}>{data.data}</p> + </div> + ))} + </div> + ) +} + +export default InvestInformation diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/percentage.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/percentage.tsx new file mode 100644 index 00000000..9bb24fb1 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/percentage.tsx @@ -0,0 +1,26 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + percent: number + label: string +} + +const Percentage = ({ percent, label }: Props) => { + const isMinus = percent < 0 + + return ( + <div className={cx('percentage-wrapper')}> + <p className={cx('percent', isMinus && 'minus')}> + {percent} + {label !== 'Profit Factor' && '%'} + </p> + <p className={cx('label')}>{label}</p> + </div> + ) +} + +export default Percentage diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/strategy-name-box.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/strategy-name-box.tsx new file mode 100644 index 00000000..b23bb90b --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/strategy-name-box.tsx @@ -0,0 +1,26 @@ +import React from 'react' + +import StrategiesIcon from '@/app/(dashboard)/_ui/strategies-item/strategies-icon' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + iconUrl: string[] + name: string + hasButton?: boolean +} + +const StrategyNameBox = ({ iconUrl, name, hasButton = true }: Props) => { + return ( + <div className={cx('name-container')}> + <StrategiesIcon icon={iconUrl} /> + <p className={cx('name')}>{name}</p> + {hasButton && <button>제안서 다운로드</button>} + </div> + ) +} + +export default StrategyNameBox diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/styles.module.scss new file mode 100644 index 00000000..ce316d35 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/datails-information/styles.module.scss @@ -0,0 +1,81 @@ +.information-top { + display: flex; + margin: 20px 0; + gap: 10px; +} + +.name-container { + width: 30%; + height: 144px; + padding: 20px; + display: flex; + flex-direction: column; + gap: 10px; + border-radius: 5px; + background-color: $color-white; + .name { + @include typo-h4; + } + button { + height: 36px; + border-radius: 8px; + background-color: $color-gray-100; + color: $color-gray-700; + } +} + +.invest-container { + width: 70%; + height: 144px; + display: flex; + gap: 20px; + padding: 30px; + border-radius: 5px; + background-color: $color-white; + .info-item { + width: 100%; + height: 100%; + border-right: 1px solid $color-gray-200; + &:last-child { + border-right: 0; + } + .invest-title { + @include typo-b1; + color: $color-gray-500; + } + .invest-data { + @include typo-b3; + color: $color-gray-700; + margin-top: 16px; + } + } +} + +.percentage-container { + width: 100%; + display: flex; + gap: 10px; + margin: 20px 0; +} +.percentage-wrapper { + width: 20%; + height: 108px; + border-radius: 5px; + background-color: $color-white; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + .label { + @include typo-b2; + color: $color-gray-800; + } + .percent { + @include typo-h3; + color: #f53500; + &.minus { + color: #6877ff; + } + } +} diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 939ebbbb..730427ae 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,5 +1,7 @@ 'use client' +import DetailsInformation from '@/app/(dashboard)/strategies/[strategyId]/_ui/datails-information' + import { useMSWStore } from '@/shared/stores/msw' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' @@ -13,11 +15,13 @@ export type InformationType = { title: TitleType; data: string | number } | Info const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { const isReady = useMSWStore((state) => state.isReady) - const { data: detailsSideData } = useGetDetailsInformationData({ + const { data } = useGetDetailsInformationData({ isReady, strategyId: params.strategyId, }) + const { detailsSideData, detailsInformationData } = data || {} + const hasDetailsSideData = detailsSideData?.map((data) => { if (!Array.isArray(data)) return data.data !== undefined }) @@ -26,6 +30,7 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { <div> <BackHeader label={'목록으로 돌아가기'} /> <Title label={'전략 상세보기'} /> + {detailsInformationData && <DetailsInformation information={detailsInformationData} />} <ReviewContainer strategyId={params.strategyId} /> <SideContainer> {hasDetailsSideData?.[0] && diff --git a/mocks/handlers.ts b/mocks/handlers.ts index 9b79a72a..e326cd80 100644 --- a/mocks/handlers.ts +++ b/mocks/handlers.ts @@ -1,4 +1,3 @@ - import { authHandlers, postHandlers, From 58cb99c18e27185fc810275d57a3783a0a3d4fdd Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 25 Nov 2024 21:41:46 +0900 Subject: [PATCH 319/648] =?UTF-8?q?fix:=20=EA=B2=BD=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/[strategyId]/page.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 730427ae..68011420 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,13 +1,12 @@ 'use client' -import DetailsInformation from '@/app/(dashboard)/strategies/[strategyId]/_ui/datails-information' - import { useMSWStore } from '@/shared/stores/msw' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' import SideContainer from '../_ui/side-container' import useGetDetailsInformationData from './_hooks/query/use-get-details-information-data' +import DetailsInformation from './_ui/datails-information' import DetailsSideItem, { InformationModel, TitleType } from './_ui/details-side-item' import ReviewContainer from './_ui/review-container' From 064fcb9b47c81025a2195ce5903990575c981d60 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 25 Nov 2024 13:44:46 +0900 Subject: [PATCH 320/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=A0=95=EB=B3=B4=EC=9E=85=EB=A0=A5=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#1?= =?UTF-8?q?04)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_constants/signup.ts | 10 ++ .../signup/information/page.module.scss | 82 +++++++++ app/(landing)/signup/information/page.tsx | 157 +++++++++++++++++- app/(landing)/signup/information/types.ts | 31 ++++ app/(landing)/signup/information/utils.ts | 27 +++ shared/ui/dropdown/index.tsx | 1 + shared/ui/input/index.tsx | 1 + 7 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 app/(landing)/signup/_constants/signup.ts create mode 100644 app/(landing)/signup/information/page.module.scss create mode 100644 app/(landing)/signup/information/types.ts create mode 100644 app/(landing)/signup/information/utils.ts diff --git a/app/(landing)/signup/_constants/signup.ts b/app/(landing)/signup/_constants/signup.ts new file mode 100644 index 00000000..6cd971f1 --- /dev/null +++ b/app/(landing)/signup/_constants/signup.ts @@ -0,0 +1,10 @@ +export const SIGNUP_ERROR_MESSAGES = { + NAME_REQUIRED: '이름을 입력해주세요.', + NAME_MIN_LENGTH: '이름을 2자 이상 입력해주세요.', + PASSWORD_REQUIRED: '비밀번호를 입력해주세요.', + PASSWORD_INVALID: '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', + PASSWORD_MISMATCH: '비밀번호가 일치하지 않습니다.', + EMAIL_REQUIRED: '이메일을 입력해주세요.', + PHONE_REQUIRED: '휴대폰 번호를 입력해주세요.', + PHONE_INVALID: '올바른 휴대폰 번호를 입력해주세요.', +} as const diff --git a/app/(landing)/signup/information/page.module.scss b/app/(landing)/signup/information/page.module.scss new file mode 100644 index 00000000..62cba7e4 --- /dev/null +++ b/app/(landing)/signup/information/page.module.scss @@ -0,0 +1,82 @@ +.form-container { + width: 912px; + margin: 100px auto 0; + + .button { + height: 48px; + @include typo-b3; + } + + .input { + border: 1px solid $color-gray-300; + border-radius: 4px; + } + + .input-group, + .select-group { + display: flex; + margin-bottom: 32px; + + label { + width: 160px; + height: 48px; + line-height: 48px; + @include typo-b1; + color: $color-gray-800; + } + + small { + display: block; + margin-top: 8px; + @include typo-c1; + color: $color-gray-600; + } + + .wrapper { + display: flex; + align-items: center; + gap: 24px; + + &.verification { + margin-top: 8px; + } + } + + .symbol { + margin: 0 -12px; + color: $color-gray-400; + } + } + + .terms-wrapper { + display: flex; + flex-direction: column; + + label { + @include typo-b1; + color: $color-gray-800; + } + + .checkbox-wrapper { + display: flex; + align-items: center; + justify-content: center; + height: 60px; + margin: 24px 0 110px; + border-radius: 6px; + background-color: $color-white; + } + } +} + +.button-wrapper { + display: flex; + justify-content: center; + gap: 48px; + margin-bottom: 70px; +} + +.select-wrapper { + display: flex; + gap: 12px; +} diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index e9048faf..dc749e6a 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -1,16 +1,169 @@ 'use client' +import classNames from 'classnames/bind' + import { PATH } from '@/shared/constants/path' +import { Button } from '@/shared/ui/button' +import Checkbox from '@/shared/ui/check-box' +import { Input } from '@/shared/ui/input' import { LinkButton } from '@/shared/ui/link-button' +import Select from '@/shared/ui/select' import Step from '../_ui/step' +import styles from './page.module.scss' +import { SelectOptionModel } from './types' +import { generateDayOptions, generateMonthOptions, generateYearOptions } from './utils' + +const cx = classNames.bind(styles) + +const emailDomainOptions: SelectOptionModel[] = [ + { value: '', label: '직접 입력' }, + { value: 'gmail.com', label: 'gmail.com' }, + { value: 'naver.com', label: 'naver.com' }, + { value: 'daum.net', label: 'daum.net' }, +] + +const yearOptions = generateYearOptions() +const monthOptions = generateMonthOptions() +const dayOptions = generateDayOptions() + +const selectStyle = { + backgroundColor: 'transparent', + height: '48px', +} const InformationPage = () => { return ( <> <Step /> - <LinkButton href={PATH.SIGN_UP_COMPLETE}>다음</LinkButton> - <LinkButton href={PATH.SIGN_UP_TERMS_OF_USE}>이전</LinkButton> + + <form className={cx('form-container')}> + <div className={cx('input-group')}> + <label htmlFor="name">이름</label> + <Input id="name" placeholder="이름을 입력하세요" className={cx('input')} /> + </div> + + <div className={cx('input-group')}> + <label htmlFor="nickname">닉네임</label> + <div> + <div className={cx('wrapper')}> + <Input id="nickname" placeholder="닉네임을 입력하세요" className={cx('input')} /> + <Button onClick={() => {}} type="button" className={cx('button')}> + 중복확인 + </Button> + </div> + <small>* 사이트 이용 시 사용할 닉네임을 입력해주세요.</small> + </div> + </div> + + <div className={cx('input-group')}> + <label htmlFor="nickname">이메일 인증</label> + <div> + <div> + <div className={cx('wrapper')}> + <Input id="email" placeholder="이메일 주소를 입력하세요" className={cx('input')} /> + <span className={cx('symbol')}>@</span> + <Input id="domain" inputSize="small" className={cx('input')} /> + <Select + options={emailDomainOptions} + isMultiple={false} + value={emailDomainOptions[0].label} + size="large" + onChange={() => {}} + titleStyle={selectStyle} + /> + <Button onClick={() => {}} type="button" variant="filled" className={cx('button')}> + 인증 + </Button> + </div> + <small>* 사이트 이용 시 아이디로 사용됩니다.</small> + </div> + <div className={cx('wrapper', 'verification')}> + <Input id="verification" placeholder="인증번호" className={cx('input')} /> + <Button onClick={() => {}} type="button" className={cx('button')}> + 인증번호 확인 + </Button> + </div> + </div> + </div> + + <div className={cx('input-group')}> + <label htmlFor="password">비밀번호</label> + <div> + <Input + id="password" + placeholder="비밀번호를 입력하세요" + className={cx('input')} + type="password" + /> + <small>* 비밀번호는 문자, 숫자 포함 6~20자로 구성되어야 합니다.</small> + </div> + </div> + + <div className={cx('input-group')}> + <label htmlFor="password-check">비밀번호 확인</label> + <Input + id="password-check" + placeholder="한 번 더 입력하세요" + className={cx('input')} + type="password" + /> + </div> + + <div className={cx('input-group')}> + <label htmlFor="phone">휴대전화</label> + <Input id="phone" placeholder="전화번호를 입력하세요." className={cx('input')} /> + </div> + + <div className={cx('select-group')}> + <label htmlFor="birthday">생년월일</label> + <div className={cx('select-wrapper')}> + <Select + options={yearOptions} + value={'임시'} + size="small" + onChange={() => {}} + placeholder="년" + titleStyle={selectStyle} + /> + <Select + options={monthOptions} + value={'임시'} + size="small" + onChange={() => {}} + placeholder="월" + titleStyle={selectStyle} + /> + <Select + options={dayOptions} + value={'임시'} + size="small" + onChange={() => {}} + placeholder="일" + titleStyle={selectStyle} + /> + </div> + </div> + <div className={cx('terms-wrapper')}> + <label>정보수신동의</label> + <div className={cx('checkbox-wrapper')}> + <Checkbox + label="정보성 마케팅 정보 알림에 수신 동의합니다." + isChecked={true} + onChange={(isChecked) => console.log(isChecked)} + textColor="gray800" + textSize="b2" + /> + </div> + </div> + </form> + + <div className={cx('button-wrapper')}> + <LinkButton href={PATH.SIGN_UP_TERMS_OF_USE}>이전</LinkButton> + <Button variant="filled" onClick={() => {}}> + 다음 + </Button> + </div> </> ) } diff --git a/app/(landing)/signup/information/types.ts b/app/(landing)/signup/information/types.ts new file mode 100644 index 00000000..b19ed5b5 --- /dev/null +++ b/app/(landing)/signup/information/types.ts @@ -0,0 +1,31 @@ +import { DropdownValueType } from '@/shared/ui/dropdown/types' + +import { SIGNUP_ERROR_MESSAGES } from '../_constants/signup' + +export type SignupErrorMessageType = + (typeof SIGNUP_ERROR_MESSAGES)[keyof typeof SIGNUP_ERROR_MESSAGES] + +export interface SignupFormDataModel { + name: string + password: string + emailId: string + emailDomain: string + phone: string + birthYear: string + birthMonth: string + birthDay: string +} + +export interface SignupFormErrorsModel { + name?: SignupErrorMessageType + password?: SignupErrorMessageType + emailId?: SignupErrorMessageType + phone?: SignupErrorMessageType +} + +export interface SelectOptionModel { + value: string + label: string +} + +export type SelectChangeHandlerType = (value: DropdownValueType) => void diff --git a/app/(landing)/signup/information/utils.ts b/app/(landing)/signup/information/utils.ts new file mode 100644 index 00000000..bca12299 --- /dev/null +++ b/app/(landing)/signup/information/utils.ts @@ -0,0 +1,27 @@ +export const generateYearOptions = () => { + const currentYear = new Date().getFullYear() + return Array.from({ length: 100 }, (_, i) => ({ + value: String(currentYear - i), + label: String(currentYear - i), + })) +} + +export const generateMonthOptions = () => { + return Array.from({ length: 12 }, (_, i) => { + const month = String(i + 1).padStart(2, '0') + return { + value: month, + label: `${month}월`, + } + }) +} + +export const generateDayOptions = () => { + return Array.from({ length: 31 }, (_, i) => { + const day = String(i + 1).padStart(2, '0') + return { + value: day, + label: `${day}일`, + } + }) +} diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index fa144f7e..4e3eaeb6 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -51,6 +51,7 @@ const Dropdown = ({ onClick={toggleOpen} className={cx('container', 'trigger', size, { open: isOpen })} style={labelStyle} + type="button" > {Trigger} {isOpen ? <OpenIcon /> : <CloseIcon />} diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index 7836ae24..42407d76 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -2,6 +2,7 @@ import { ComponentProps } from 'react' +import { SignupErrorMessageType } from '@/app/(landing)/signup/information/types' import classNames from 'classnames/bind' import styles from './styles.module.scss' From 6d644dc5f69c7ec1aa0e4bffcf3a849e33cc4270 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 25 Nov 2024 13:45:36 +0900 Subject: [PATCH 321/648] =?UTF-8?q?remove:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/(landing)/signup/.gitkeep diff --git a/app/(landing)/signup/.gitkeep b/app/(landing)/signup/.gitkeep deleted file mode 100644 index e69de29b..00000000 From 5aea594df61fcaa057b15c7135932bdcf57a9658 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Mon, 25 Nov 2024 22:07:35 +0900 Subject: [PATCH 322/648] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20=EB=B0=8F=20select=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95=20(#9?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/page.module.scss | 27 +++++ app/(dashboard)/traders/page.tsx | 144 ++++++++++++++++++++++- public/mockServiceWorker.js | 2 +- shared/ui/dropdown/index.tsx | 2 +- shared/ui/dropdown/styles.module.scss | 4 +- 5 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 app/(dashboard)/traders/page.module.scss diff --git a/app/(dashboard)/traders/page.module.scss b/app/(dashboard)/traders/page.module.scss new file mode 100644 index 00000000..efff6100 --- /dev/null +++ b/app/(dashboard)/traders/page.module.scss @@ -0,0 +1,27 @@ +.page-container { + padding: 0 15px; +} + +.title { + margin-top: 80px; +} +.search-container { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 8px; + padding: 16px; + margin-top: 18px; + margin-bottom: 31px; +} + +.traders-list-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px 32px; + margin-bottom: 30px; +} + +.pagination-container { + margin-bottom: 64px; +} diff --git a/app/(dashboard)/traders/page.tsx b/app/(dashboard)/traders/page.tsx index 77fbb01d..0051d7b2 100644 --- a/app/(dashboard)/traders/page.tsx +++ b/app/(dashboard)/traders/page.tsx @@ -1,5 +1,147 @@ +'use client' + +import { useState } from 'react' + +import styles from '@/app/(dashboard)/traders/page.module.scss' +import classNames from 'classnames/bind' + +import { DropdownValueType } from '@/shared/ui/dropdown/types' +import Pagination from '@/shared/ui/pagination' +import { SearchInput } from '@/shared/ui/search-input' +import Select from '@/shared/ui/select' +import Title from '@/shared/ui/title' +import TradersListCard from '@/shared/ui/traders-list-card' + +const cx = classNames.bind(styles) + +// 임시데이터 +const TradersData = [ + { + nickname: '김트레이더', + strategyCount: 12, + subscriberCount: 345, + traderId: '123456', + }, + { + nickname: '박트레이더', + strategyCount: 8, + subscriberCount: 120, + traderId: '234567', + }, + { + nickname: '이트레이더', + strategyCount: 15, + subscriberCount: 289, + traderId: '345678', + }, + { + nickname: '최트레이더', + strategyCount: 5, + subscriberCount: 87, + traderId: '456789', + }, + { + nickname: '정트레이더', + strategyCount: 20, + subscriberCount: 512, + traderId: '567890', + }, + { + nickname: '조트레이더', + strategyCount: 10, + subscriberCount: 200, + traderId: '678901', + }, + { + nickname: '한트레이더', + strategyCount: 18, + subscriberCount: 467, + traderId: '789012', + }, + { + nickname: '장트레이더', + strategyCount: 7, + subscriberCount: 134, + traderId: '890123', + }, + { + nickname: '유트레이더', + strategyCount: 3, + subscriberCount: 56, + traderId: '901234', + }, + { + nickname: '홍트레이더', + strategyCount: 22, + subscriberCount: 600, + traderId: '012345', + }, + { + nickname: '서트레이더', + strategyCount: 4, + subscriberCount: 75, + traderId: '123457', + }, + { + nickname: '신트레이더', + strategyCount: 9, + subscriberCount: 300, + traderId: '234568', + }, +] + const TradersPage = () => { - return <></> + const initialValue = null + const [value, setValue] = useState<DropdownValueType>(initialValue) + return ( + <> + <div className={cx('page-container')}> + <div className={cx('title')}> + <Title label={'트레이더 목록'} marginLeft={'13px'}> +
    +
    + + +
    +
    + {TradersData.map((trader) => ( + + ))} +
    +
    + {}}> +
    +
    + + ) } export default TradersPage diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index 742f3083..64c9b1cb 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -8,7 +8,7 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.6.4' +const PACKAGE_VERSION = '2.6.6' const INTEGRITY_CHECKSUM = 'ca7800994cc8bfb5eb961e037c877074' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/shared/ui/dropdown/index.tsx b/shared/ui/dropdown/index.tsx index fa144f7e..a4d4ade8 100644 --- a/shared/ui/dropdown/index.tsx +++ b/shared/ui/dropdown/index.tsx @@ -53,7 +53,7 @@ const Dropdown = ({ style={labelStyle} > {Trigger} - {isOpen ? : } + {isOpen ? : } {isOpen &&
      {children}
    } diff --git a/shared/ui/dropdown/styles.module.scss b/shared/ui/dropdown/styles.module.scss index e25c5955..a9e4d6cf 100644 --- a/shared/ui/dropdown/styles.module.scss +++ b/shared/ui/dropdown/styles.module.scss @@ -22,7 +22,7 @@ $color-selected-text: #171717; } .container { - border: 1px solid $color-gray-200; + border: 1px solid $color-gray-400; border-radius: 4px; display: flex; justify-content: space-between; @@ -39,7 +39,7 @@ $color-selected-text: #171717; .trigger { color: $color-gray-800; - background-color: $color-white; + background-color: transparent; svg { path { From 400be8bf1d4a96878832813ad50bac5dda8f771d Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Mon, 25 Nov 2024 23:21:35 +0900 Subject: [PATCH 323/648] =?UTF-8?q?refactor:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20=EB=B0=8F=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=B9=B4=EB=A9=9C=EC=BC=80=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/page.module.scss | 6 +++--- app/(dashboard)/traders/page.tsx | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/(dashboard)/traders/page.module.scss b/app/(dashboard)/traders/page.module.scss index efff6100..f7744ebc 100644 --- a/app/(dashboard)/traders/page.module.scss +++ b/app/(dashboard)/traders/page.module.scss @@ -5,7 +5,7 @@ .title { margin-top: 80px; } -.search-container { +.search-wrapper { display: flex; justify-content: flex-end; align-items: center; @@ -15,13 +15,13 @@ margin-bottom: 31px; } -.traders-list-container { +.traders-list-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px 32px; margin-bottom: 30px; } -.pagination-container { +.pagination-wrapper { margin-bottom: 64px; } diff --git a/app/(dashboard)/traders/page.tsx b/app/(dashboard)/traders/page.tsx index 0051d7b2..8ebf0fe2 100644 --- a/app/(dashboard)/traders/page.tsx +++ b/app/(dashboard)/traders/page.tsx @@ -15,7 +15,7 @@ import TradersListCard from '@/shared/ui/traders-list-card' const cx = classNames.bind(styles) // 임시데이터 -const TradersData = [ +const tradersData = [ { nickname: '김트레이더', strategyCount: 12, @@ -99,7 +99,7 @@ const TradersPage = () => {
    -
    +
    -
    - {TradersData.map((trader) => ( +
    + {tradersData.map((trader) => ( { /> ))}
    -
    +
    {}}>
    From 40164aa95251691530218c0f75e9c4365fb0f883 Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Tue, 26 Nov 2024 01:09:22 +0900 Subject: [PATCH 324/648] =?UTF-8?q?refactor:=20search-input=20=ED=81=B4?= =?UTF-8?q?=EB=A1=9C=EC=A7=95=20=ED=83=9C=EA=B7=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/page.module.scss | 1 + app/(dashboard)/traders/page.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/traders/page.module.scss b/app/(dashboard)/traders/page.module.scss index f7744ebc..615e9a98 100644 --- a/app/(dashboard)/traders/page.module.scss +++ b/app/(dashboard)/traders/page.module.scss @@ -5,6 +5,7 @@ .title { margin-top: 80px; } + .search-wrapper { display: flex; justify-content: flex-end; diff --git a/app/(dashboard)/traders/page.tsx b/app/(dashboard)/traders/page.tsx index 8ebf0fe2..921d2f98 100644 --- a/app/(dashboard)/traders/page.tsx +++ b/app/(dashboard)/traders/page.tsx @@ -123,7 +123,7 @@ const TradersPage = () => { }, ]} > - +
    {tradersData.map((trader) => ( From 7203961c2605d6060be2bc72414f44bef14a0dc7 Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Tue, 26 Nov 2024 02:44:59 +0900 Subject: [PATCH 325/648] =?UTF-8?q?feat:=20page-not-found=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1=20(#121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/images/index.ts | 1 + public/images/page-not-found-img.svg | 76 +++++++++++++++++++ shared/ui/page-not-found/index.tsx | 30 ++++++++ .../page-not-found/page-not-found.stories.tsx | 30 ++++++++ shared/ui/page-not-found/styles.module.scss | 35 +++++++++ 5 files changed, 172 insertions(+) create mode 100644 public/images/page-not-found-img.svg create mode 100644 shared/ui/page-not-found/index.tsx create mode 100644 shared/ui/page-not-found/page-not-found.stories.tsx create mode 100644 shared/ui/page-not-found/styles.module.scss diff --git a/public/images/index.ts b/public/images/index.ts index fc0dc062..40862b03 100644 --- a/public/images/index.ts +++ b/public/images/index.ts @@ -1,2 +1,3 @@ export { default as ImageLogo } from './logo.svg' export { default as TextLogo } from './text-logo.svg' +export { default as PageNotFoundImg } from './page-not-found-img.svg' diff --git a/public/images/page-not-found-img.svg b/public/images/page-not-found-img.svg new file mode 100644 index 00000000..8f486b16 --- /dev/null +++ b/public/images/page-not-found-img.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shared/ui/page-not-found/index.tsx b/shared/ui/page-not-found/index.tsx new file mode 100644 index 00000000..1f421278 --- /dev/null +++ b/shared/ui/page-not-found/index.tsx @@ -0,0 +1,30 @@ +'use client' + +import { PageNotFoundImg } from '@/public/images' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' + +import { LinkButton } from '../link-button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export const PageNotFound = () => { + return ( +
    +
    +

    404

    +

    페이지를 찾을 수 없습니다.

    +
    + + 홈으로 돌아가기 + +
    +
    +
    + +
    +
    + ) +} diff --git a/shared/ui/page-not-found/page-not-found.stories.tsx b/shared/ui/page-not-found/page-not-found.stories.tsx new file mode 100644 index 00000000..b6443b8d --- /dev/null +++ b/shared/ui/page-not-found/page-not-found.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import { PageNotFound } from './index' + +const meta = { + title: 'Components/PageNotFound', + component: PageNotFound, + parameters: { + layout: 'fullscreen', + backgrounds: { + default: 'light', + values: [ + { + name: 'light', + value: '#ffffff', + }, + { + name: 'dark', + value: '#222222', + }, + ], + }, + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type StoryType = StoryObj + +export const Default: StoryType = {} diff --git a/shared/ui/page-not-found/styles.module.scss b/shared/ui/page-not-found/styles.module.scss new file mode 100644 index 00000000..29852bba --- /dev/null +++ b/shared/ui/page-not-found/styles.module.scss @@ -0,0 +1,35 @@ +.container { + display: flex; + position: absolute; + transform: translate(-50%, -50%); + justify-content: center; + align-items: center; + top: 50%; + left: 50%; +} + +.text-wrapper { + display: flex; + flex-direction: column; +} + +.number { + color: $color-orange-500; + font-size: 120px; + margin-bottom: 50px; + font-weight: bold; +} + +.text { + color: $color-gray-800; + margin-bottom: 120px; + @include typo-b1; +} + +.img-wrapper { + display: flex; + justify-content: center; + align-items: center; + width: 508px; + height: 338px; +} From b0fcb360192059ecdd31c07ab643bceef514d5f8 Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 26 Nov 2024 09:48:20 +0900 Subject: [PATCH 326/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EA=B2=80=EC=A6=9D=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_constants/signup.ts | 7 +- app/(landing)/signup/information/types.ts | 24 +++++-- app/(landing)/signup/information/utils.ts | 81 +++++++++++++++++++++++ shared/utils/validation.ts | 18 +++-- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/app/(landing)/signup/_constants/signup.ts b/app/(landing)/signup/_constants/signup.ts index 6cd971f1..db561f37 100644 --- a/app/(landing)/signup/_constants/signup.ts +++ b/app/(landing)/signup/_constants/signup.ts @@ -1,10 +1,15 @@ export const SIGNUP_ERROR_MESSAGES = { NAME_REQUIRED: '이름을 입력해주세요.', + NICKNAME_REQUIRED: '닉네임을 입력해주세요.', + NICKNAME_LENGTH: '닉네임은 2글자 이상 10글자 이하로 입력해주세요.', + NICKNAME_CHECK_REQUIRED: '닉네임 중복확인이 필요합니다', NAME_MIN_LENGTH: '이름을 2자 이상 입력해주세요.', PASSWORD_REQUIRED: '비밀번호를 입력해주세요.', - PASSWORD_INVALID: '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', + PASSWORD_INVALID: '비밀번호가 올바르지 않습니다.', PASSWORD_MISMATCH: '비밀번호가 일치하지 않습니다.', EMAIL_REQUIRED: '이메일을 입력해주세요.', + EMAIL_INVALID: '올바른 이메일을 입력해주세요.', + EMAIL_CHECK_REQUIRED: '이메일 인증이 필요합니다.', PHONE_REQUIRED: '휴대폰 번호를 입력해주세요.', PHONE_INVALID: '올바른 휴대폰 번호를 입력해주세요.', } as const diff --git a/app/(landing)/signup/information/types.ts b/app/(landing)/signup/information/types.ts index b19ed5b5..e0ca67af 100644 --- a/app/(landing)/signup/information/types.ts +++ b/app/(landing)/signup/information/types.ts @@ -7,20 +7,26 @@ export type SignupErrorMessageType = export interface SignupFormDataModel { name: string - password: string - emailId: string + nickname: string + email: string emailDomain: string + verificationCode: string + password: string + passwordConfirm: string phone: string birthYear: string birthMonth: string birthDay: string + isMarketingAgreed: boolean } export interface SignupFormErrorsModel { - name?: SignupErrorMessageType - password?: SignupErrorMessageType - emailId?: SignupErrorMessageType - phone?: SignupErrorMessageType + name?: SignupErrorMessageType | null + nickname?: SignupErrorMessageType | null + email?: SignupErrorMessageType | null + password?: SignupErrorMessageType | null + passwordConfirm?: SignupErrorMessageType | null + phone?: SignupErrorMessageType | null } export interface SelectOptionModel { @@ -28,4 +34,10 @@ export interface SelectOptionModel { label: string } +export interface SignupFormStateModel { + isEmailVerified: boolean + isNicknameVerified: boolean + isEmailSent: boolean +} + export type SelectChangeHandlerType = (value: DropdownValueType) => void diff --git a/app/(landing)/signup/information/utils.ts b/app/(landing)/signup/information/utils.ts index bca12299..f33393d3 100644 --- a/app/(landing)/signup/information/utils.ts +++ b/app/(landing)/signup/information/utils.ts @@ -1,3 +1,84 @@ +import { + isValidEmail, + isValidName, + isValidNickname, + isValidPassword, + isValidPhone, +} from '@/shared/utils/validation' + +import { SIGNUP_ERROR_MESSAGES } from '../_constants/signup' +import { SignupErrorMessageType, SignupFormDataModel } from './types' + +type ValidationFieldType = keyof typeof SIGNUP_ERROR_MESSAGES + +const validateField = (field: string, value: string): SignupErrorMessageType | null => { + if (!value.trim()) { + return SIGNUP_ERROR_MESSAGES[`${field}_REQUIRED` as ValidationFieldType] || null + } + + switch (field) { + case 'NAME': + return isValidName(value) ? null : SIGNUP_ERROR_MESSAGES.NAME_MIN_LENGTH + case 'NICKNAME': + return isValidNickname(value) ? null : SIGNUP_ERROR_MESSAGES.NICKNAME_LENGTH + case 'PASSWORD': + return isValidPassword(value) ? null : SIGNUP_ERROR_MESSAGES.PASSWORD_INVALID + case 'PHONE': + return isValidPhone(value) ? null : SIGNUP_ERROR_MESSAGES.PHONE_INVALID + default: + return null + } +} + +export const validateEmail = (emailId: string, domain: string): SignupErrorMessageType | null => { + if (!emailId.trim() || !domain.trim()) { + return SIGNUP_ERROR_MESSAGES.EMAIL_REQUIRED + } + + const fullEmail = `${emailId}@${domain}` + return isValidEmail(fullEmail) ? null : SIGNUP_ERROR_MESSAGES.EMAIL_INVALID +} + +export const validatePasswordMatch = ( + password: string, + confirmPassword: string +): SignupErrorMessageType | null => { + if (!confirmPassword.trim()) { + return SIGNUP_ERROR_MESSAGES.PASSWORD_REQUIRED + } + return password === confirmPassword ? null : SIGNUP_ERROR_MESSAGES.PASSWORD_MISMATCH +} + +export const validateSignupForm = ( + form: SignupFormDataModel, + isEmailVerified: boolean, + isNicknameVerified: boolean +): Record => { + const errors: Record = {} + + const nameError = validateField('NAME', form.name) + if (nameError) errors.name = nameError + + const nicknameError = validateField('NICKNAME', form.nickname) + if (nicknameError) errors.nickname = nicknameError + else if (!isNicknameVerified) errors.nickname = SIGNUP_ERROR_MESSAGES.NICKNAME_CHECK_REQUIRED + + const emailError = validateEmail(form.email, form.emailDomain) + if (emailError) errors.email = emailError + else if (!isEmailVerified) errors.email = SIGNUP_ERROR_MESSAGES.EMAIL_CHECK_REQUIRED + + const passwordError = validateField('PASSWORD', form.password) + if (passwordError) errors.password = passwordError + + const passwordMatchError = validatePasswordMatch(form.password, form.passwordConfirm) + if (passwordMatchError) errors.passwordConfirm = passwordMatchError + + const phoneError = validateField('PHONE', form.phone) + if (phoneError) errors.phone = phoneError + + return errors +} + export const generateYearOptions = () => { const currentYear = new Date().getFullYear() return Array.from({ length: 100 }, (_, i) => ({ diff --git a/shared/utils/validation.ts b/shared/utils/validation.ts index d8266fc8..90d8ecb2 100644 --- a/shared/utils/validation.ts +++ b/shared/utils/validation.ts @@ -3,21 +3,31 @@ import { ERROR_MESSAGES } from '@/shared/constants/error-messages' const PATTERNS = { EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, PASSWORD: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/, - PHONE: /^\d{10,11}$/, + PHONE: /^01([0|1|6|7|8|9])([0-9]{3,4})([0-9]{4})$/, + NAME: /^.{2,}$/, + NICKNAME: /^.{2,10}$/, } as const -const isValidEmail = (email: string): boolean => { +export const isValidEmail = (email: string): boolean => { return PATTERNS.EMAIL.test(email) } -const isValidPassword = (password: string): boolean => { +export const isValidPassword = (password: string): boolean => { return PATTERNS.PASSWORD.test(password) } -const isValidPhone = (phone: string): boolean => { +export const isValidPhone = (phone: string): boolean => { return PATTERNS.PHONE.test(phone) } +export const isValidName = (name: string): boolean => { + return PATTERNS.NAME.test(name) +} + +export const isValidNickname = (nickname: string): boolean => { + return PATTERNS.NICKNAME.test(nickname) +} + const validators = { EMAIL: isValidEmail, PASSWORD: isValidPassword, From d2a011948cc3999d2fb319736d801f73fc261c50 Mon Sep 17 00:00:00 2001 From: deun Date: Tue, 26 Nov 2024 09:49:21 +0900 Subject: [PATCH 327/648] =?UTF-8?q?feat:=20handler=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/information/page.module.scss | 2 +- app/(landing)/signup/information/page.tsx | 273 ++++++++++++++++-- 2 files changed, 242 insertions(+), 33 deletions(-) diff --git a/app/(landing)/signup/information/page.module.scss b/app/(landing)/signup/information/page.module.scss index 62cba7e4..d467b43d 100644 --- a/app/(landing)/signup/information/page.module.scss +++ b/app/(landing)/signup/information/page.module.scss @@ -34,7 +34,7 @@ .wrapper { display: flex; - align-items: center; + align-items: baseline; gap: 24px; &.verification { diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index dc749e6a..fd10120b 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -1,5 +1,9 @@ 'use client' +import { ChangeEvent, useState } from 'react' + +import { useRouter } from 'next/navigation' + import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -11,12 +15,38 @@ import Select from '@/shared/ui/select' import Step from '../_ui/step' import styles from './page.module.scss' -import { SelectOptionModel } from './types' -import { generateDayOptions, generateMonthOptions, generateYearOptions } from './utils' +import { SignupFormDataModel, SignupFormErrorsModel, SignupFormStateModel } from './types' +import { + generateDayOptions, + generateMonthOptions, + generateYearOptions, + validateSignupForm, +} from './utils' const cx = classNames.bind(styles) -const emailDomainOptions: SelectOptionModel[] = [ +const initialForm: SignupFormDataModel = { + name: '', + nickname: '', + email: '', + emailDomain: '', + verificationCode: '', + password: '', + passwordConfirm: '', + phone: '', + birthYear: '', + birthMonth: '', + birthDay: '', + isMarketingAgreed: false, +} + +const initialFormState: SignupFormStateModel = { + isEmailVerified: false, + isNicknameVerified: false, + isEmailSent: false, +} + +const emailDomainOptions = [ { value: '', label: '직접 입력' }, { value: 'gmail.com', label: 'gmail.com' }, { value: 'naver.com', label: 'naver.com' }, @@ -33,6 +63,118 @@ const selectStyle = { } const InformationPage = () => { + const router = useRouter() + const [form, setForm] = useState(initialForm) + const [errors, setErrors] = useState({}) + const [formState, setFormState] = useState(initialFormState) + const [isValidated, setIsValidated] = useState(false) + const [selectedDomain, setSelectedDomain] = useState('') + + const handleInputChange = (e: ChangeEvent) => { + const { name, value } = e.target + setForm((prev) => ({ ...prev, [name]: value })) + + if (isValidated) { + setErrors((prev) => ({ + ...prev, + [name]: null, + })) + } + + if (name === 'emailDomain') { + setSelectedDomain('') + } + + if (name === 'email' || name === 'emailDomain') { + setFormState((prev) => ({ + ...prev, + isEmailVerified: false, + isEmailSent: false, + })) + } + + if (name === 'nickname') { + setFormState((prev) => ({ + ...prev, + isNicknameVerified: false, + })) + } + } + + const handleDomainSelect = (value: string | string[]) => { + if (typeof value === 'string') { + setSelectedDomain(value) + setForm((prev) => ({ ...prev, emailDomain: value !== '' ? value : '' })) + + if (isValidated) { + setErrors((prev) => ({ ...prev, email: null })) + } + + setFormState((prev) => ({ + ...prev, + isEmailVerified: false, + isEmailSent: false, + })) + } + } + + const handleNicknameCheck = async () => { + try { + // TODO: 닉네임 중복 확인 API 호출 + setFormState((prev) => ({ ...prev, isNicknameVerified: true })) + + if (errors.nickname) { + setErrors((prev) => ({ ...prev, nickname: null })) + } + } catch (error) { + console.error('닉네임 중복 확인 실패:', error) + } + } + + const handleEmailVerification = async () => { + try { + // TODO: 이메일 인증 API 호출 + setFormState((prev) => ({ ...prev, isEmailSent: true })) + } catch (error) { + console.error('이메일 인증 발송 실패:', error) + } + } + + const handleVerificationCodeCheck = async () => { + try { + // TODO: 인증번호 확인 API 호출 + setFormState((prev) => ({ ...prev, isEmailVerified: true })) + + if (errors.email) { + setErrors((prev) => ({ ...prev, email: null })) + } + } catch (error) { + console.error('인증번호 확인 실패:', error) + } + } + + const handleMarketingAgree = (checked: boolean) => { + setForm((prev) => ({ ...prev, isMarketingAgreed: checked })) + } + + const handleFormSubmit = () => { + const formErrors = validateSignupForm( + form, + formState.isEmailVerified, + formState.isNicknameVerified + ) + setIsValidated(true) + + if (Object.keys(formErrors).length > 0) { + setErrors(formErrors) + return + } + + // TODO: 회원가입 API 호출 + console.log('폼 제출', form) + router.push(PATH.SIGN_UP_COMPLETE) + } + return ( <> @@ -40,15 +182,31 @@ const InformationPage = () => {
    - +
    - -
    @@ -57,33 +215,67 @@ const InformationPage = () => {
    - +
    - + @ - + - -
    + {formState.isEmailSent && ( +
    + + +
    + )}
    @@ -92,27 +284,43 @@ const InformationPage = () => {
    * 비밀번호는 문자, 숫자 포함 6~20자로 구성되어야 합니다.
    - +
    - +
    @@ -120,37 +328,38 @@ const InformationPage = () => {
    setForm((prev) => ({ ...prev, birthMonth: String(value) }))} size="small" - onChange={() => {}} placeholder="월" titleStyle={selectStyle} /> setFirstValue(newValue as OptionsType)} + /> + +
    +
    + + +
    + * (-) 없이 숫자만 입력해주세요. +
    diff --git a/app/(landing)/signup/information/types.ts b/app/(landing)/signup/information/types.ts index e0ca67af..ad6ba87e 100644 --- a/app/(landing)/signup/information/types.ts +++ b/app/(landing)/signup/information/types.ts @@ -38,6 +38,7 @@ export interface SignupFormStateModel { isEmailVerified: boolean isNicknameVerified: boolean isEmailSent: boolean + isPhoneVerified: boolean } export type SelectChangeHandlerType = (value: DropdownValueType) => void From 2eb476089d8458ff93946ce0e9f9945e6a98924c Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Tue, 26 Nov 2024 18:55:55 +0900 Subject: [PATCH 346/648] =?UTF-8?q?refactor:=20select=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=B6=88=ED=95=84=EC=9A=94=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=A0=9C=EA=B1=B0(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/page.tsx | 4 +--- shared/ui/dropdown/styles.module.scss | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/(dashboard)/traders/page.tsx b/app/(dashboard)/traders/page.tsx index 921d2f98..7f0839f1 100644 --- a/app/(dashboard)/traders/page.tsx +++ b/app/(dashboard)/traders/page.tsx @@ -104,10 +104,7 @@ const TradersPage = () => { size="small" value={value} placeholder="구독순" - isMultiple={false} - hasCheck={false} onChange={(newValue) => setValue(newValue)} - titleStyle={{ borderRadius: '40px' }} options={[ { value: '1+', @@ -136,6 +133,7 @@ const TradersPage = () => { /> ))}
    + {/* TODO: 실제 데이터로 변경 */}
    {}}>
    diff --git a/shared/ui/dropdown/styles.module.scss b/shared/ui/dropdown/styles.module.scss index a9e4d6cf..5ad792c0 100644 --- a/shared/ui/dropdown/styles.module.scss +++ b/shared/ui/dropdown/styles.module.scss @@ -39,7 +39,7 @@ $color-selected-text: #171717; .trigger { color: $color-gray-800; - background-color: transparent; + background-color: $color-gray-100; svg { path { From 263e9ad051a490f3e4fdd88a14dcfda55b6662f5 Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Tue, 26 Nov 2024 21:21:33 +0900 Subject: [PATCH 347/648] =?UTF-8?q?feat:=20input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=95=88=EC=97=90=20=EC=9E=88=EB=8D=98=20?= =?UTF-8?q?error-messages=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EC=9E=91=EC=97=85=20(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../error-messages/error-messages.stories.tsx | 33 ++++++++++++++ shared/ui/error-messages/index.tsx | 12 +++++ shared/ui/error-messages/styles.module.scss | 5 +++ shared/ui/input/index.tsx | 45 +++++++++---------- shared/ui/input/input.stories.tsx | 11 ----- shared/ui/input/styles.module.scss | 8 ---- 6 files changed, 70 insertions(+), 44 deletions(-) create mode 100644 shared/ui/error-messages/error-messages.stories.tsx create mode 100644 shared/ui/error-messages/index.tsx create mode 100644 shared/ui/error-messages/styles.module.scss diff --git a/shared/ui/error-messages/error-messages.stories.tsx b/shared/ui/error-messages/error-messages.stories.tsx new file mode 100644 index 00000000..d6e70491 --- /dev/null +++ b/shared/ui/error-messages/error-messages.stories.tsx @@ -0,0 +1,33 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { ErrorMessages } from './index' + +const meta: Meta = { + title: 'Components/ErrorMessages', + component: ErrorMessages, + args: { + errorMessages: '에러메세지를 입력하세요.', + }, + argTypes: { + errorMessages: { + control: 'text', + description: 'The error message to display.', + table: { + type: { summary: 'string | null' }, + }, + }, + }, + tags: ['autodocs'], +} + +export default meta + +type StoryType = StoryObj + +export const Default: StoryType = {} + +export const EmptyMessage: StoryType = { + args: { + errorMessages: null, + }, +} diff --git a/shared/ui/error-messages/index.tsx b/shared/ui/error-messages/index.tsx new file mode 100644 index 00000000..194c3053 --- /dev/null +++ b/shared/ui/error-messages/index.tsx @@ -0,0 +1,12 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + errorMessages?: string | null +} +export const ErrorMessages = ({ errorMessages }: Props) => { + return
    {errorMessages &&

    {errorMessages}

    }
    +} diff --git a/shared/ui/error-messages/styles.module.scss b/shared/ui/error-messages/styles.module.scss new file mode 100644 index 00000000..a274cc0f --- /dev/null +++ b/shared/ui/error-messages/styles.module.scss @@ -0,0 +1,5 @@ +.error-messages { + color: $color-orange-800; + font-size: $text-b3; + font-weight: $text-medium; +} diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index 7836ae24..e44f184c 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { ComponentProps } from 'react' +import { ComponentPropsWithoutRef, forwardRef } from 'react' import classNames from 'classnames/bind' @@ -10,30 +10,25 @@ const cx = classNames.bind(styles) export type InputSizeType = 'small' | 'medium' | 'large' | 'full' -interface Props extends ComponentProps<'input'> { +interface Props extends ComponentPropsWithoutRef<'input'> { inputSize?: InputSizeType - errorMessage?: string | null + error?: boolean } -export const Input = ({ - inputSize = 'medium', - errorMessage, - className, - value, - onChange, - ...props -}: Props) => { - return ( -
    - - {errorMessage &&

    {errorMessage}

    } -
    - ) -} +export const Input = forwardRef( + ({ inputSize = 'medium', className, value, error = false, onChange, ...props }, ref) => { + return ( +
    + +
    + ) + } +) + +Input.displayName = 'Input' diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx index 7d5aab05..d79ea475 100644 --- a/shared/ui/input/input.stories.tsx +++ b/shared/ui/input/input.stories.tsx @@ -7,7 +7,6 @@ const meta: Meta = { component: Input, args: { inputSize: 'medium', - errorMessage: null, placeholder: 'Enter text', type: 'text', }, @@ -20,16 +19,6 @@ const meta: Meta = { control: { type: 'select' }, options: ['email', 'password', 'tel', 'text'], }, - errorMessage: { - control: { type: 'select' }, - options: [ - null, - '비밀번호는 8자리 이상, 문자와 숫자를 포함해야 합니다.', - '이메일 형식이 잘못되었습니다.', - '전화번호는 10자리 이상이어야 합니다.', - '필수 입력란입니다.', - ], - }, }, tags: ['autodocs'], } diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss index e50111bc..24e80ffb 100644 --- a/shared/ui/input/styles.module.scss +++ b/shared/ui/input/styles.module.scss @@ -36,11 +36,3 @@ cursor: not-allowed; } } - -.error-message { - color: $color-orange-700; - margin-top: 10px; - margin-bottom: 10px; - font-size: $text-b3; - font-weight: $text-medium; -} From 295bd0cbe731a4aa5a5124e780e9419211d3c98b Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Wed, 27 Nov 2024 01:41:53 +0900 Subject: [PATCH 348/648] =?UTF-8?q?chore:=20react-tooltip=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 40 ++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 41 insertions(+) diff --git a/package-lock.json b/package-lock.json index ad1dee12..77c48946 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "next": "14.2.16", "react": "^18", "react-dom": "^18", + "react-tooltip": "^5.28.0", "zustand": "^5.0.1" }, "devDependencies": { @@ -2527,6 +2528,31 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", + "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -12824,6 +12850,20 @@ "node": ">=0.10.0" } }, + "node_modules/react-tooltip": { + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.0.tgz", + "integrity": "sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.1", + "classnames": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, "node_modules/readable-stream": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", diff --git a/package.json b/package.json index 9031e4e2..1ba32b40 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "next": "14.2.16", "react": "^18", "react-dom": "^18", + "react-tooltip": "^5.28.0", "zustand": "^5.0.1" }, "devDependencies": { From f64db78f35f81c9bad9c4aea191bfc931cfc21b5 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Wed, 27 Nov 2024 01:44:12 +0900 Subject: [PATCH 349/648] =?UTF-8?q?feat:=20=EC=A2=85=EB=AA=A9&=EB=A7=A4?= =?UTF-8?q?=EB=A7=A4=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/strategies-icon.tsx | 72 +++++++++++++++++- .../_ui/strategies-item/styles.module.scss | 24 ++++++ public/images/stock.png | Bin 0 -> 1173 bytes public/images/trade.png | Bin 0 -> 453 bytes 4 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 public/images/stock.png create mode 100644 public/images/trade.png diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 383993d3..26e85a41 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -1,9 +1,75 @@ +'use client' + +import { useEffect, useState } from 'react' + +import Image from 'next/image' + +import classNames from 'classnames/bind' +import { Tooltip } from 'react-tooltip' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface ImageSizeModel { + width: number + height: number +} + interface Props { - icon: string[] | React.ElementType + iconUrls?: string[] + iconNames?: string[] } -const StrategiesIcon = ({ icon }: Props) => { - return
    [종목 & 매매 Icon]
    +const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { + const [imageSizes, setImageSizes] = useState<{ [key: string]: ImageSizeModel }>({}) + + useEffect(() => { + iconUrls?.forEach((url) => { + const image = new window.Image() + image.src = url + image.onload = () => handleImageLoad(url, image) + }) + }, [iconUrls]) + + const handleImageLoad = (url: string, image: HTMLImageElement) => { + setImageSizes((prev) => ({ + ...prev, + [url]: { width: image.width, height: image.height }, + })) + } + + if (iconUrls?.length === 0 || iconNames?.length === 0) return null + + return ( +
    + {iconUrls?.map((url, idx) => { + const name = iconNames?.[idx] + + if (!url || !name) return null + const size = imageSizes[url] || { width: 20, height: 20 } + return ( +
    +
    + {name} +
    + + {name} + +
    + ) + })} +
    + ) } export default StrategiesIcon diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index 3a0f7339..ca870fea 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -60,6 +60,9 @@ .subscribe-icon { button { background: transparent; + svg { + width: 36px; + } } } @@ -67,3 +70,24 @@ width: 100%; overflow: hidden; } + +.icon-container { + display: flex; + gap: 4px; + .icon-wrapper { + .icon { + position: relative; + } + .tooltip { + background-color: $color-gray-700; + border-radius: 20px; + padding: 8px 20px 6px; + @include typo-c1; + .arrow { + width: 14px; + height: 14px; + z-index: zIndex(hidden); + } + } + } +} diff --git a/public/images/stock.png b/public/images/stock.png new file mode 100644 index 0000000000000000000000000000000000000000..5ce47ca043de22d231cc97179f9bf9499b946442 GIT binary patch literal 1173 zcmV;G1Zw+=p8YEEF|h1VItCikLKN#H7jzDkM$R zq!N-g8k;0Vv`7(%UiP^EE2>YVu)QcbqF7y76l1P}xQYQ;JyO^lSuWq_z5uwfB&q(oITMd?5ACo z=7ceb@IlIV>+hgF_FauIGcoGMLWu=zV}2?Z(JnclL^5G;(lD|OPHB_!wOK?Ryz-#+q?jc{BZ(fC7C_ELfAV_Zn3&12h{OKkLK`-l5*C~4x+4pvq&Ej`mf1|>IHB?} zoHTq;#%F%cJuffRWrRLqJTpFwzz;{q%2?{DC24M-ce=vglcUygi@7hs2tvzjYM<(G zZD&4O2b>T((4LEt{=YF{^fUI=Mhr+f4S#lwg1m z9BmdL1jlN1iXJBu#wbzLJzcnc&P9zAMihN#0A(9a=6bJt=0v$L#DV=VL4!-_Xk2um zks8;ZWU$PuPP}afGEjYwtR3^iiwju7I=Dy8|^LhSvF+;sJp&FYZYiC>Khy`yTqGr;jV~nnZt?Wg+5x zl)5c}X-FoFPJkePWn9^ZgF5Rm=V`birycow`1wt z8Vn3!+uVBYg5iMyC1JIlxL$eW_m4$;y|2|l&o%;Y<>Q|Tqv3(qE@IrjG+UuaL@la5 zL2y7@JEP8H^|D$-c)G;C;j)a=zrwco_auI4A^JX4(%e>PHM$Se)~y>S`qogXJGl)7 zIII~>Y(>hC*r?C4X4?bLKIMc`&=n)>8T5JtK>+Bxv*b&TCpb8b;*$ex4N#i z;9%^fiQD<+2*8%RG4=qlh$EgUd|wsq?zoUmZ~hzbP=Q|(6QewJJ?tG(7x4!>QP~Uf nijt!aW7cqghwCYhsMr1iUIeQ@>J^`+00000NkvXXu0mjf6NMVD literal 0 HcmV?d00001 diff --git a/public/images/trade.png b/public/images/trade.png new file mode 100644 index 0000000000000000000000000000000000000000..bbd6a4f1d9616d597f6abc08ed4e310b84cb339f GIT binary patch literal 453 zcmV;$0XqJPP)nFd5sRRcVEqFW9o!17vx5cE*@By>QP4%9ag@&Dn!(8_ z2u?OR@4cvWNPZM6@EJ z9pnkoyd_HmdBfZwUY%eRfQ5o6$G$N-4u5G2tR_Pz&b5im!oB7N@bMms4GzPv)xiTp zC_S{(3-i<*cw`La<|atQ4PT$;fH8m*`-Y@M7f;-k4_1t8|L6|A zGY9_q4w8_#-B{f7%mRXqT~Ox=rOOkfcC0%=cZ6{u?oCHXN_ch=aYT28xD2-IW~J^^ z_`F;Ly}lrh*i;0SH6y0o>&~#AZ%iTe7i1DnOJTpX7WR-oo+Y!hjl3bUwc{4_8(Udt|spXp#pg-MRudF00000NkvXXu0mjfj_JOW literal 0 HcmV?d00001 From 5970ab2c14cabb41079f411d80b45805766f8687 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Wed, 27 Nov 2024 01:46:20 +0900 Subject: [PATCH 350/648] =?UTF-8?q?feat:=20=EC=A2=85=EB=AA=A9&=EB=A7=A4?= =?UTF-8?q?=EB=A7=A4=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=AA=A9=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-information/index.tsx | 3 +- .../details-information/strategy-name-box.tsx | 7 +- app/(dashboard)/_ui/strategies-item/index.tsx | 3 +- .../strategies-item.stories.tsx | 4 +- .../strategies-item/strategies-summary.tsx | 8 +- .../_api/get-details-information.ts | 4 +- mocks/handlers/strategies.ts | 158 +++++++++--------- mocks/handlers/strategy-details.ts | 40 ++--- shared/types/strategy-details-data.ts | 6 +- 9 files changed, 120 insertions(+), 113 deletions(-) diff --git a/app/(dashboard)/_ui/details-information/index.tsx b/app/(dashboard)/_ui/details-information/index.tsx index c511527f..b1a3f095 100644 --- a/app/(dashboard)/_ui/details-information/index.tsx +++ b/app/(dashboard)/_ui/details-information/index.tsx @@ -27,7 +27,8 @@ const DetailsInformation = ({ information }: Props) => { <>
    { +const StrategyNameBox = ({ iconUrls, iconNames, name, hasButton = true }: Props) => { return (
    - +

    {name}

    {hasButton && }
    diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 7e305161..dc8f508d 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -19,7 +19,8 @@ const StrategiesItem = ({ strategiesData: data }: Props) => { return ( { return (
    - +

    {title}

    diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index 7ef57317..feecbfa3 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -30,8 +30,8 @@ const getDetailsInformation = async (isReady: boolean, strategyId: string) => { const detailsInformationData = { strategyId: data.strategyId, strategyName: data.strategyName, - stockTypeIconURLs: data.stockTypeIconURLs, - tradeTypeIconURL: data.tradeTypeIconURL, + stockTypeIconUrls: data.stockTypeIconUrls, + tradeTypeIconUrl: data.tradeTypeIconUrl, stockTypeNames: data.stockTypeNames, tradeTypeName: data.tradeTypeName, operationCycle: data.operationCycle, diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts index a6a7d314..66854190 100644 --- a/mocks/handlers/strategies.ts +++ b/mocks/handlers/strategies.ts @@ -5,8 +5,8 @@ const strategiesData = [ strategyId: '1', strategyName: 'Dynamic ETF 전략', nickname: 'AlphaTrader', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -21,8 +21,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-15,432,567', smScore: 72.1, cumulativeProfitLossRate: 140.5, @@ -36,8 +36,8 @@ const strategiesData = [ strategyId: '2', strategyName: '고수익 ETF', nickname: 'BetaTrader', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -52,8 +52,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-12,786,543', smScore: 65.4, cumulativeProfitLossRate: 125.3, @@ -67,8 +67,8 @@ const strategiesData = [ strategyId: '3', strategyName: 'Futures Pro', nickname: 'Gamma', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -83,8 +83,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-18,234,678', smScore: 79.6, cumulativeProfitLossRate: 160.4, @@ -98,7 +98,8 @@ const strategiesData = [ strategyId: '4', strategyName: '월별 수익 전략', nickname: 'TraderX', - stockTypeIconUrl: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -113,8 +114,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-11,456,789', smScore: 68.4, cumulativeProfitLossRate: 134.5, @@ -128,8 +129,8 @@ const strategiesData = [ strategyId: '5', strategyName: '리스크 관리 전략', nickname: 'DeltaOne', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -144,8 +145,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-14,345,678', smScore: 62.3, cumulativeProfitLossRate: 120.3, @@ -159,8 +160,8 @@ const strategiesData = [ strategyId: '6', strategyName: 'Active LongShort', nickname: 'Hedger', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -175,8 +176,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-17,890,234', smScore: 80.2, cumulativeProfitLossRate: 175.4, @@ -190,8 +191,8 @@ const strategiesData = [ strategyId: '7', strategyName: '안정적 성장 전략', nickname: 'SafeGrow', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -206,8 +207,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-13,567,890', smScore: 70.1, cumulativeProfitLossRate: 138.2, @@ -221,8 +222,8 @@ const strategiesData = [ strategyId: '8', strategyName: 'High Risk High Return', nickname: 'RiskyTrader', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -237,8 +238,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-25,678,345', smScore: 85.7, cumulativeProfitLossRate: 200.5, @@ -252,8 +253,8 @@ const strategiesData = [ strategyId: '9', strategyName: 'ETF 분산 투자', nickname: 'Diversifier', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -268,8 +269,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-9,876,543', smScore: 58.3, cumulativeProfitLossRate: 112.4, @@ -283,8 +284,8 @@ const strategiesData = [ strategyId: '10', strategyName: 'Momentum 전략', nickname: 'MomentumKing', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -299,8 +300,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-18,456,789', smScore: 75.6, cumulativeProfitLossRate: 165.3, @@ -314,8 +315,8 @@ const strategiesData = [ strategyId: '11', strategyName: '소형주 집중 전략', nickname: 'SmallCapPro', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -330,8 +331,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-14,345,678', smScore: 69.4, cumulativeProfitLossRate: 130.7, @@ -345,8 +346,8 @@ const strategiesData = [ strategyId: '12', strategyName: 'Active Bond 전략', nickname: 'BondExpert', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -361,8 +362,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-10,567,890', smScore: 64.3, cumulativeProfitLossRate: 125.8, @@ -376,8 +377,8 @@ const strategiesData = [ strategyId: '13', strategyName: 'Smart Index 전략', nickname: 'IndexGuru', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -392,8 +393,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-16,543,678', smScore: 71.8, cumulativeProfitLossRate: 145.6, @@ -407,8 +408,8 @@ const strategiesData = [ strategyId: '14', strategyName: 'MACD Pro', nickname: 'TrendRider', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -423,8 +424,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-19,567,890', smScore: 77.5, cumulativeProfitLossRate: 175.8, @@ -438,8 +439,8 @@ const strategiesData = [ strategyId: '15', strategyName: '균형 성장 전략', nickname: 'BalancedGrow', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -454,8 +455,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-12,567,890', smScore: 62.8, cumulativeProfitLossRate: 118.5, @@ -469,8 +470,8 @@ const strategiesData = [ strategyId: '16', strategyName: '단기 스윙 전략', nickname: 'SwingPro', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -485,8 +486,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-22,345,678', smScore: 80.3, cumulativeProfitLossRate: 190.7, @@ -500,8 +501,8 @@ const strategiesData = [ strategyId: '17', strategyName: '로우볼 전략', nickname: 'LowVolKing', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -516,8 +517,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-8,345,678', smScore: 55.9, cumulativeProfitLossRate: 102.4, @@ -531,8 +532,8 @@ const strategiesData = [ strategyId: '18', strategyName: '인컴 중심 전략', nickname: 'IncomeMaster', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -547,8 +548,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-10,678,345', smScore: 60.2, cumulativeProfitLossRate: 115.9, @@ -562,8 +563,8 @@ const strategiesData = [ strategyId: '19', strategyName: '글로벌 주식 전략', nickname: 'GlobalTrader', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -578,7 +579,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-17,890,567', smScore: 74.2, cumulativeProfitLossRate: 153.6, @@ -592,8 +594,8 @@ const strategiesData = [ strategyId: '20', strategyName: '테크 성장 전략', nickname: 'TechGrow', - stockTypeIconUrl: [], - stockTypeNames: [], + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], profitRateChartData: { xAxis: [ '2023-01-01', @@ -608,8 +610,8 @@ const strategiesData = [ ], yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', mdd: '-23,567,890', smScore: 88.3, cumulativeProfitLossRate: 210.9, diff --git a/mocks/handlers/strategy-details.ts b/mocks/handlers/strategy-details.ts index 0798d7e7..7ad5cdbe 100644 --- a/mocks/handlers/strategy-details.ts +++ b/mocks/handlers/strategy-details.ts @@ -4,10 +4,10 @@ const data = [ { strategyId: '1', strategyName: 'Dynamic ETF 전략', - stockTypeIconURLs: [], - tradeTypeIconURL: '', - stockTypeNames: [], - tradeTypeName: 'ETF 매매', + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', operationCycle: 'DAY', strategyDescription: '전략 설명 예시', cumulativeProfitRate: 4924.0, @@ -29,10 +29,10 @@ const data = [ { strategyId: '2', strategyName: '고수익 ETF', - stockTypeIconURLs: [], - tradeTypeIconURL: '', - stockTypeNames: [], - tradeTypeName: 'ETF 매매', + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', operationCycle: 'DAY', strategyDescription: '전략 설명 예시', cumulativeProfitRate: 4924.0, @@ -54,10 +54,10 @@ const data = [ { strategyId: '3', strategyName: 'Futures Pro', - stockTypeIconURLs: [], - tradeTypeIconURL: '', - stockTypeNames: [], - tradeTypeName: 'ETF 매매', + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', operationCycle: 'DAY', strategyDescription: '전략 설명 예시', cumulativeProfitRate: 4924.0, @@ -79,10 +79,10 @@ const data = [ { strategyId: '4', strategyName: '월별 수익 전략', - stockTypeIconURLs: [], - tradeTypeIconURL: '', - stockTypeNames: [], - tradeTypeName: 'ETF 매매', + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', operationCycle: 'DAY', strategyDescription: '전략 설명 예시', cumulativeProfitRate: 4924.0, @@ -104,10 +104,10 @@ const data = [ { strategyId: '5', strategyName: '리스크 관리 전략', - stockTypeIconURLs: [], - tradeTypeIconURL: '', - stockTypeNames: [], - tradeTypeName: 'ETF 매매', + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', operationCycle: 'DAY', strategyDescription: '전략 설명 예시', cumulativeProfitRate: 4924.0, diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts index 1e91765a..4e78b564 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-details-data.ts @@ -29,7 +29,7 @@ export interface StrategiesModel { strategyName: string nickname: string traderImage?: string - stockTypeIconUrl: string[] + stockTypeIconUrls: string[] stockTypeNames: string[] profitRateChartData: ProfitRateChartDataModel tradeTypeIconUrl: string @@ -47,8 +47,8 @@ export interface StrategiesModel { export interface StrategyDetailsInformationModel { strategyId: string strategyName: string - stockTypeIconURLs: string[] - tradeTypeIconURL: string + stockTypeIconUrls: string[] + tradeTypeIconUrl: string stockTypeNames: string[] tradeTypeName: string operationCycle: string From ce3df574b4d60dff9e71110dc8bb3fe35d018853 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Wed, 27 Nov 2024 01:46:49 +0900 Subject: [PATCH 351/648] =?UTF-8?q?design:=20=EB=A6=AC=EB=B7=B0=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20margin=20=EC=88=98=EC=A0=95=20(#8?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/review-container/styles.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss index 87a72b07..cffd8a20 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss @@ -1,7 +1,7 @@ .container { width: 100%; padding: 25px; - margin: 10px 0 20px; + margin: 20px 0; background-color: $color-white; border-radius: 5px; .title-wrapper { From cad00600d2a6acfb988f8f206a98a83f5a5fb860 Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Wed, 27 Nov 2024 01:50:07 +0900 Subject: [PATCH 352/648] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#1?= =?UTF-8?q?01)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../traders/[traderId]/page.module.scss | 11 +++ app/(dashboard)/traders/[traderId]/page.tsx | 80 ++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 app/(dashboard)/traders/[traderId]/page.module.scss diff --git a/app/(dashboard)/traders/[traderId]/page.module.scss b/app/(dashboard)/traders/[traderId]/page.module.scss new file mode 100644 index 00000000..c385baef --- /dev/null +++ b/app/(dashboard)/traders/[traderId]/page.module.scss @@ -0,0 +1,11 @@ +.page-container { + padding: 0 15px; +} + +.title { + margin-bottom: 48px; +} + +.card-wrapper { + margin-bottom: 54px; +} diff --git a/app/(dashboard)/traders/[traderId]/page.tsx b/app/(dashboard)/traders/[traderId]/page.tsx index 66a91ecf..9f43ec7f 100644 --- a/app/(dashboard)/traders/[traderId]/page.tsx +++ b/app/(dashboard)/traders/[traderId]/page.tsx @@ -1,5 +1,83 @@ +'use client' + +import styles from '@/app/(dashboard)/traders/[traderId]/page.module.scss' +import classNames from 'classnames/bind' + +import BackHeader from '@/shared/ui/header/back-header' +import Title from '@/shared/ui/title' +import TradersListCard from '@/shared/ui/traders-list-card' + +import ListHeader from '../../_ui/list-header' +import StrategiesItem from '../../_ui/strategies-item' + +const cx = classNames.bind(styles) + const TraderDetailPage = () => { - return <> + // TODO: 임시데이터 제거 + const strategiesModel = { + strategyId: '123', + strategyName: '리치테크 FuturesDay', + nickname: 'MACS', + stockTypeIconUrl: [], + stockTypeNames: [], + profitRateChartData: { + xAxis: [ + '2023-01-01', + '2023-01-02', + '2023-01-03', + '2023-01-04', + '2023-01-05', + '2023-01-06', + '2023-01-07', + '2023-01-08', + '2023-01-09', + ], + yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + }, + tradeTypeIconUrl: '', + tradeTypeName: '', + mdd: '-20,580,856', + smScore: 60.6, + cumulativeProfitLossRate: 120.1, + recentYearProfitLossRate: 30.1, + subscriptionCnt: 23, + isSubscribed: true, + averageRating: 4.8, + totalReview: 12, + } + + const traderData = { + nickname: '냥냥펀치', + strategyCount: 10, + subscriberCount: 20, + traderId: '940504', + } + + return ( + <> +
    + +
    + +
    +
    + +
    + + {Array(3) + .fill(strategiesModel) + .map((item, index) => ( + + ))} +
    + + ) } export default TraderDetailPage From ee4f06fac65855b5fb946bb44bc1e5c707f200bc Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Wed, 27 Nov 2024 02:12:38 +0900 Subject: [PATCH 353/648] =?UTF-8?q?refactor:=20ErrorMessages=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20isErr=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/error-messages/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shared/ui/error-messages/index.tsx b/shared/ui/error-messages/index.tsx index 194c3053..0a7e0def 100644 --- a/shared/ui/error-messages/index.tsx +++ b/shared/ui/error-messages/index.tsx @@ -5,8 +5,9 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - errorMessages?: string | null + errorMessages: string | null + isErr?: boolean } -export const ErrorMessages = ({ errorMessages }: Props) => { - return
    {errorMessages &&

    {errorMessages}

    }
    +export const ErrorMessages = ({ errorMessages, isErr = true }: Props) => { + return <>{isErr &&

    {errorMessages}

    } } From 935aa04caf95963889d73c2c247644b00bd4c75e Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Wed, 27 Nov 2024 02:22:10 +0900 Subject: [PATCH 354/648] =?UTF-8?q?feat:=20SearchInput=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20ref=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/search-input/index.tsx | 43 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/shared/ui/search-input/index.tsx b/shared/ui/search-input/index.tsx index 992b2bb4..27940503 100644 --- a/shared/ui/search-input/index.tsx +++ b/shared/ui/search-input/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { ComponentProps } from 'react' +import { ComponentPropsWithoutRef, forwardRef } from 'react' import { SearchIcon } from '@/public/icons' import classNames from 'classnames/bind' @@ -9,28 +9,27 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -interface Props extends ComponentProps<'input'> { +interface Props extends ComponentPropsWithoutRef<'input'> { placeholder?: string handleSearchIconClick?: () => void } -export const SearchInput = ({ - placeholder = '', - handleSearchIconClick, - value, - onChange, - ...props -}: Props) => { - return ( -
    - - -
    - ) -} +export const SearchInput = forwardRef( + ({ placeholder = '', handleSearchIconClick, value, onChange, ...props }: Props, ref) => { + return ( +
    + + +
    + ) + } +) + +SearchInput.displayName = 'SearchInput' From 625f1baa6dfd0831acd0ed1c7311a1398ef50f0a Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 08:34:51 +0900 Subject: [PATCH 355/648] =?UTF-8?q?feat:=20SignupFormDataModel=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존 SignupFormDataModel은 SignupFormModel로 수정 --- .../signup/_hooks/custom/use-signup-email.ts | 4 ++-- .../signup/_hooks/custom/use-signup-form.ts | 10 +++++----- app/(landing)/signup/information/types.ts | 17 ++++++++++++++++- app/(landing)/signup/information/utils.ts | 4 ++-- shared/types/auth.ts | 4 ++-- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-email.ts b/app/(landing)/signup/_hooks/custom/use-signup-email.ts index e6a280b8..a560c6fe 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-email.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-email.ts @@ -1,15 +1,15 @@ import { Dispatch, SetStateAction, useState } from 'react' import { - SignupFormDataModel, SignupFormErrorsModel, + SignupFormModel, SignupFormStateModel, } from './../../information/types' interface Props { errors: SignupFormErrorsModel isValidated: boolean - setForm: Dispatch> + setForm: Dispatch> setErrors: Dispatch> setFormState: Dispatch> } diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index 983a3407..154aaa91 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -5,13 +5,13 @@ import { useRouter } from 'next/navigation' import { PATH } from '@/shared/constants/path' import { - SignupFormDataModel, SignupFormErrorsModel, + SignupFormModel, SignupFormStateModel, } from './../../information/types' import { validateSignupForm } from './../../information/utils' -const initialForm: SignupFormDataModel = { +const initialForm: SignupFormModel = { name: '', nickname: '', email: '', @@ -35,14 +35,14 @@ const initialFormState: SignupFormStateModel = { const useSignupForm = () => { const router = useRouter() - const [form, setForm] = useState(initialForm) + const [form, setForm] = useState(initialForm) const [errors, setErrors] = useState({}) const [formState, setFormState] = useState(initialFormState) const [isValidated, setIsValidated] = useState(false) const handleInputChange = (e: ChangeEvent) => { const { name, value } = e.target - setForm((prev: SignupFormDataModel) => ({ ...prev, [name]: value })) + setForm((prev) => ({ ...prev, [name]: value })) if (isValidated) { setErrors((prev) => ({ @@ -60,7 +60,7 @@ const useSignupForm = () => { } const handleMarketingAgree = (checked: boolean) => { - setForm((prev: SignupFormDataModel) => ({ ...prev, isMarketingAgreed: checked })) + setForm((prev) => ({ ...prev, isMarketingAgreed: checked })) } const handleNicknameCheck = async () => { diff --git a/app/(landing)/signup/information/types.ts b/app/(landing)/signup/information/types.ts index ad6ba87e..85c98830 100644 --- a/app/(landing)/signup/information/types.ts +++ b/app/(landing)/signup/information/types.ts @@ -1,3 +1,4 @@ +import { RoleType } from '@/shared/types/auth' import { DropdownValueType } from '@/shared/ui/dropdown/types' import { SIGNUP_ERROR_MESSAGES } from '../_constants/signup' @@ -5,7 +6,7 @@ import { SIGNUP_ERROR_MESSAGES } from '../_constants/signup' export type SignupErrorMessageType = (typeof SIGNUP_ERROR_MESSAGES)[keyof typeof SIGNUP_ERROR_MESSAGES] -export interface SignupFormDataModel { +export interface SignupFormModel { name: string nickname: string email: string @@ -41,4 +42,18 @@ export interface SignupFormStateModel { isPhoneVerified: boolean } +export interface SignupFormDataModel { + name: string + nickname: string + phone: string + birthYear: string + birthMonth: string + birthDay: string + password: string + email: string + emailDomain: string + role: RoleType + infoAgreement: boolean +} + export type SelectChangeHandlerType = (value: DropdownValueType) => void diff --git a/app/(landing)/signup/information/utils.ts b/app/(landing)/signup/information/utils.ts index f33393d3..07da7344 100644 --- a/app/(landing)/signup/information/utils.ts +++ b/app/(landing)/signup/information/utils.ts @@ -7,7 +7,7 @@ import { } from '@/shared/utils/validation' import { SIGNUP_ERROR_MESSAGES } from '../_constants/signup' -import { SignupErrorMessageType, SignupFormDataModel } from './types' +import { SignupErrorMessageType, SignupFormModel } from './types' type ValidationFieldType = keyof typeof SIGNUP_ERROR_MESSAGES @@ -50,7 +50,7 @@ export const validatePasswordMatch = ( } export const validateSignupForm = ( - form: SignupFormDataModel, + form: SignupFormModel, isEmailVerified: boolean, isNicknameVerified: boolean ): Record => { diff --git a/shared/types/auth.ts b/shared/types/auth.ts index d8fce10e..1025dc33 100644 --- a/shared/types/auth.ts +++ b/shared/types/auth.ts @@ -1,6 +1,6 @@ -export type UserType = 'trader' | 'investor' +export type UserType = 'TRADER' | 'INVESTOR' -export type RoleType = UserType | `${UserType}_admin` +export type RoleType = UserType | `${UserType}_ADMIN` export interface UserModel { id: string From 487da883bbe20ab7c0a6f596170e93ea5e059c72 Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 08:36:29 +0900 Subject: [PATCH 356/648] =?UTF-8?q?feat:=20api=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80=20(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_api/check-duplicate.ts | 33 ++++++++++++++++++++ app/(landing)/signup/_api/email-auth.ts | 28 +++++++++++++++++ app/(landing)/signup/_api/signup.ts | 29 +++++++++++++++++ app/(landing)/signup/_constants/signup.ts | 5 ++- 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 app/(landing)/signup/_api/check-duplicate.ts create mode 100644 app/(landing)/signup/_api/email-auth.ts create mode 100644 app/(landing)/signup/_api/signup.ts diff --git a/app/(landing)/signup/_api/check-duplicate.ts b/app/(landing)/signup/_api/check-duplicate.ts new file mode 100644 index 00000000..b8335fc9 --- /dev/null +++ b/app/(landing)/signup/_api/check-duplicate.ts @@ -0,0 +1,33 @@ +import axios from 'axios' + +export const checkNicknameDuplicate = async (nickname: string) => { + try { + const response = await axios.post(`/api/users/check/nickname?nickname=${nickname}`) + return response.data + } catch (err) { + console.error(err) + throw new Error('닉네임 중복 확인에 실패했습니다.') + } +} + +export const checkPhoneDuplicate = async (phone: string) => { + try { + const response = await axios.post(`/api/users/check/phone?phone=${phone}`) + return response.data + } catch (err) { + console.error(err) + throw new Error('휴대폰 전화 중복 확인에 실패했습니다.') + } +} + +export const checkEmailDuplicate = async (email: string, domain: string) => { + const emailAddress = `${email}@${domain}` + + try { + const response = await axios.get(`/api/users/check/email?email=${emailAddress}`) + return response.data + } catch (err) { + console.error(err) + throw new Error('이메일 중복 확인에 실패했습니다.') + } +} diff --git a/app/(landing)/signup/_api/email-auth.ts b/app/(landing)/signup/_api/email-auth.ts new file mode 100644 index 00000000..8c525ab5 --- /dev/null +++ b/app/(landing)/signup/_api/email-auth.ts @@ -0,0 +1,28 @@ +import axios from 'axios' + +export const requestEmailAuthentication = async (email: string, domain: string) => { + const emailAddress = `${email}@${domain}` + + try { + const response = await axios.get(`/api/users/authenticate?email=${emailAddress}`) + return response.data + } catch (err) { + console.error(err) + throw new Error('이메일 인증 코드 발송에 실패했습니다.') + } +} + +export const getEmailAuthenticationResult = async (email: string, domain: string, code: string) => { + const data = { + email: `${email}@${domain}`, + code, + } + + try { + const response = await axios.post(`/api/users/authenticate`, data) + return response.data + } catch (err) { + console.error(err) + throw new Error('이메일 인증 코드 확인에 실패했습니다.') + } +} diff --git a/app/(landing)/signup/_api/signup.ts b/app/(landing)/signup/_api/signup.ts new file mode 100644 index 00000000..b198d166 --- /dev/null +++ b/app/(landing)/signup/_api/signup.ts @@ -0,0 +1,29 @@ +import axios from 'axios' + +import { SignupFormDataModel } from '../information/types' + +export const signup = async (formData: SignupFormDataModel) => { + const data = { + username: formData.name, + nickname: formData.nickname, + phone: formData.phone, + birthdate: `${formData.birthYear}-${formData.birthMonth}-${formData.birthDay}`, + password: formData.password, + email: `${formData.email}@${formData.emailDomain}`, + role: formData.role, + infoAgreement: formData.infoAgreement, + imageMetadata: { + imageName: '', + extension: '', + size: 0, + }, + } + + try { + const response = await axios.post('/api/users/signup', data) + return response.data + } catch (error) { + console.error('회원가입 실패:', error) + throw new Error('회원가입에 실패했습니다.') + } +} diff --git a/app/(landing)/signup/_constants/signup.ts b/app/(landing)/signup/_constants/signup.ts index db561f37..e7527370 100644 --- a/app/(landing)/signup/_constants/signup.ts +++ b/app/(landing)/signup/_constants/signup.ts @@ -2,14 +2,17 @@ export const SIGNUP_ERROR_MESSAGES = { NAME_REQUIRED: '이름을 입력해주세요.', NICKNAME_REQUIRED: '닉네임을 입력해주세요.', NICKNAME_LENGTH: '닉네임은 2글자 이상 10글자 이하로 입력해주세요.', - NICKNAME_CHECK_REQUIRED: '닉네임 중복확인이 필요합니다', + NICKNAME_CHECK_REQUIRED: '닉네임 중복확인이 필요합니다.', + NICKNAME_DUPLICATED: '이미 사용 중인 닉네임입니다.', NAME_MIN_LENGTH: '이름을 2자 이상 입력해주세요.', PASSWORD_REQUIRED: '비밀번호를 입력해주세요.', PASSWORD_INVALID: '비밀번호가 올바르지 않습니다.', PASSWORD_MISMATCH: '비밀번호가 일치하지 않습니다.', EMAIL_REQUIRED: '이메일을 입력해주세요.', EMAIL_INVALID: '올바른 이메일을 입력해주세요.', + EMAIL_DUPLICATED: '이미 사용 중인 이메일입니다.', EMAIL_CHECK_REQUIRED: '이메일 인증이 필요합니다.', PHONE_REQUIRED: '휴대폰 번호를 입력해주세요.', PHONE_INVALID: '올바른 휴대폰 번호를 입력해주세요.', + PHONE_DUPLICATED: '이미 사용 중인 휴대폰 번호입니다.', } as const From 276fedb00b9db9469c7ed9acb401748961cff86b Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 10:38:03 +0900 Subject: [PATCH 357/648] =?UTF-8?q?fix:=20=EB=8C=80=EC=86=8C=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/signup-complete-message/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(landing)/signup/_ui/signup-complete-message/index.tsx b/app/(landing)/signup/_ui/signup-complete-message/index.tsx index 4e0eb928..ad801b81 100644 --- a/app/(landing)/signup/_ui/signup-complete-message/index.tsx +++ b/app/(landing)/signup/_ui/signup-complete-message/index.tsx @@ -10,8 +10,8 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const COMPLETE_MESSAGE = { - investor: '다양한 투자 전략들을 찾아보세요!', - trader: '나만의 투자 전략들을 공유해보세요!', + INVESTOR: '다양한 투자 전략들을 찾아보세요!', + TRADER: '나만의 투자 전략들을 공유해보세요!', } interface Props { From 85bfd12e176c9391b4623d7417f09b8b4468dd4e Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 10:40:30 +0900 Subject: [PATCH 358/648] =?UTF-8?q?fix:=20error=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=EC=97=90=20=EB=A7=9E=EA=B2=8C=20err=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_api/signup.ts | 4 ++-- app/(landing)/signup/_hooks/custom/use-signup-form.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/(landing)/signup/_api/signup.ts b/app/(landing)/signup/_api/signup.ts index b198d166..84ced2c5 100644 --- a/app/(landing)/signup/_api/signup.ts +++ b/app/(landing)/signup/_api/signup.ts @@ -22,8 +22,8 @@ export const signup = async (formData: SignupFormDataModel) => { try { const response = await axios.post('/api/users/signup', data) return response.data - } catch (error) { - console.error('회원가입 실패:', error) + } catch (err) { + console.error('회원가입 실패:', err) throw new Error('회원가입에 실패했습니다.') } } diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index 154aaa91..3ff2bae3 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -71,8 +71,8 @@ const useSignupForm = () => { if (errors.nickname) { setErrors((prev) => ({ ...prev, nickname: null })) } - } catch (error) { - console.error('닉네임 중복 확인 실패:', error) + } catch (err) { + console.error('닉네임 중복 확인 실패:', err) } } @@ -84,8 +84,8 @@ const useSignupForm = () => { if (errors.phone) { setErrors((prev) => ({ ...prev, phone: null })) } - } catch (error) { - console.error('휴대폰 번호 중복 확인 실패:', error) + } catch (err) { + console.error('휴대폰 번호 중복 확인 실패:', err) } } From a7359fe00a5c6e4a0e4dcecf7691bc51bb29f9d7 Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 10:42:25 +0900 Subject: [PATCH 359/648] =?UTF-8?q?fix:=20UserType=20=EB=8C=80=EC=86=8C?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/terms-of-use/page.tsx | 2 +- app/(landing)/signup/user-type/page.tsx | 4 ++-- mocks/handlers/auth.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx index fcc358e6..50136c98 100644 --- a/app/(landing)/signup/terms-of-use/page.tsx +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -56,7 +56,7 @@ const TermsOfUsePage = () => { isChecked={isUserTermChecked} onChange={handleUserTermCheck} > - {userType === 'trader' ? : } + {userType === 'TRADER' ? : }
    diff --git a/app/(landing)/signup/user-type/page.tsx b/app/(landing)/signup/user-type/page.tsx index e9db7a5d..99534fae 100644 --- a/app/(landing)/signup/user-type/page.tsx +++ b/app/(landing)/signup/user-type/page.tsx @@ -14,8 +14,8 @@ const UserTypePage = () => {

    회원 유형 선택

    - - + +
    ) diff --git a/mocks/handlers/auth.ts b/mocks/handlers/auth.ts index 54bf974d..6d352241 100644 --- a/mocks/handlers/auth.ts +++ b/mocks/handlers/auth.ts @@ -9,7 +9,7 @@ const MOCK_USERS: Record = { email: 'test@example.com', name: '김다은', nickname: 'devdeun', - role: 'trader', + role: 'TRADER', createdAt: '2024-01-01T00:00:00Z', imageUrl: 'https://randomuser.me/api', }, @@ -18,7 +18,7 @@ const MOCK_USERS: Record = { email: 'admin@example.com', name: '권혁준', nickname: 'redhero', - role: 'trader_admin', + role: 'TRADER_ADMIN', createdAt: '2024-01-01T00:00:00Z', imageUrl: 'https://randomuser.me/api', }, From 9053085e17bcb4883c69491d9a86864bc916be9e Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 11:17:17 +0900 Subject: [PATCH 360/648] =?UTF-8?q?fix:=20Input=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../error-message.stories.tsx} | 14 +++++++------- shared/ui/error-message/index.tsx | 12 ++++++++++++ .../styles.module.scss | 2 +- shared/ui/error-messages/index.tsx | 13 ------------- shared/ui/input/index.tsx | 8 +++++--- 5 files changed, 25 insertions(+), 24 deletions(-) rename shared/ui/{error-messages/error-messages.stories.tsx => error-message/error-message.stories.tsx} (61%) create mode 100644 shared/ui/error-message/index.tsx rename shared/ui/{error-messages => error-message}/styles.module.scss (82%) delete mode 100644 shared/ui/error-messages/index.tsx diff --git a/shared/ui/error-messages/error-messages.stories.tsx b/shared/ui/error-message/error-message.stories.tsx similarity index 61% rename from shared/ui/error-messages/error-messages.stories.tsx rename to shared/ui/error-message/error-message.stories.tsx index d6e70491..44e8fd01 100644 --- a/shared/ui/error-messages/error-messages.stories.tsx +++ b/shared/ui/error-message/error-message.stories.tsx @@ -1,15 +1,15 @@ import { Meta, StoryObj } from '@storybook/react' -import { ErrorMessages } from './index' +import { ErrorMessage } from './index' -const meta: Meta = { +const meta: Meta = { title: 'Components/ErrorMessages', - component: ErrorMessages, + component: ErrorMessage, args: { - errorMessages: '에러메세지를 입력하세요.', + errorMessage: '에러메세지를 입력하세요.', }, argTypes: { - errorMessages: { + errorMessage: { control: 'text', description: 'The error message to display.', table: { @@ -22,12 +22,12 @@ const meta: Meta = { export default meta -type StoryType = StoryObj +type StoryType = StoryObj export const Default: StoryType = {} export const EmptyMessage: StoryType = { args: { - errorMessages: null, + errorMessage: null, }, } diff --git a/shared/ui/error-message/index.tsx b/shared/ui/error-message/index.tsx new file mode 100644 index 00000000..25fd0efc --- /dev/null +++ b/shared/ui/error-message/index.tsx @@ -0,0 +1,12 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + errorMessage: string | null | undefined +} +export const ErrorMessage = ({ errorMessage }: Props) => { + return <>{errorMessage &&

    {errorMessage}

    } +} diff --git a/shared/ui/error-messages/styles.module.scss b/shared/ui/error-message/styles.module.scss similarity index 82% rename from shared/ui/error-messages/styles.module.scss rename to shared/ui/error-message/styles.module.scss index a274cc0f..5163dd56 100644 --- a/shared/ui/error-messages/styles.module.scss +++ b/shared/ui/error-message/styles.module.scss @@ -1,4 +1,4 @@ -.error-messages { +.error-message { color: $color-orange-800; font-size: $text-b3; font-weight: $text-medium; diff --git a/shared/ui/error-messages/index.tsx b/shared/ui/error-messages/index.tsx deleted file mode 100644 index 0a7e0def..00000000 --- a/shared/ui/error-messages/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import classNames from 'classnames/bind' - -import styles from './styles.module.scss' - -const cx = classNames.bind(styles) - -interface Props { - errorMessages: string | null - isErr?: boolean -} -export const ErrorMessages = ({ errorMessages, isErr = true }: Props) => { - return <>{isErr &&

    {errorMessages}

    } -} diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index e44f184c..6453cd0a 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -4,6 +4,7 @@ import { ComponentPropsWithoutRef, forwardRef } from 'react' import classNames from 'classnames/bind' +import { ErrorMessage } from '../error-message' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -12,20 +13,21 @@ export type InputSizeType = 'small' | 'medium' | 'large' | 'full' interface Props extends ComponentPropsWithoutRef<'input'> { inputSize?: InputSizeType - error?: boolean + errorMessage?: string | null } export const Input = forwardRef( - ({ inputSize = 'medium', className, value, error = false, onChange, ...props }, ref) => { + ({ inputSize = 'medium', className, value, errorMessage, onChange, ...props }, ref) => { return (
    +
    ) } From 2a6ea24fbe28311e4b380124805244684b2c4700 Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 11:25:11 +0900 Subject: [PATCH 361/648] =?UTF-8?q?fix:=20errormessage=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A5=BC=20=EC=98=B5?= =?UTF-8?q?=EC=85=94=EB=84=90=ED=95=98=EA=B2=8C=20=EB=B0=9B=EC=9D=84=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/error-message/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/error-message/index.tsx b/shared/ui/error-message/index.tsx index 25fd0efc..c1aab875 100644 --- a/shared/ui/error-message/index.tsx +++ b/shared/ui/error-message/index.tsx @@ -5,7 +5,7 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - errorMessage: string | null | undefined + errorMessage?: string | null } export const ErrorMessage = ({ errorMessage }: Props) => { return <>{errorMessage &&

    {errorMessage}

    } From 95cf44c77b21f5952240c392dbe560d37a4541a5 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Wed, 27 Nov 2024 12:19:48 +0900 Subject: [PATCH 362/648] =?UTF-8?q?fix:=20image=20onerror,=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC=ED=95=B8=EB=93=A4=EB=9F=AC=EB=A6=AC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/strategies-icon.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 26e85a41..249f2679 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -9,6 +9,8 @@ import { Tooltip } from 'react-tooltip' import styles from './styles.module.scss' +/* eslint-disable react-hooks/exhaustive-deps */ + const cx = classNames.bind(styles) interface ImageSizeModel { @@ -29,6 +31,7 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { const image = new window.Image() image.src = url image.onload = () => handleImageLoad(url, image) + image.onerror = () => handleImageLoadErr(url) }) }, [iconUrls]) @@ -39,22 +42,32 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { })) } + const handleImageLoadErr = (url: string) => { + setImageSizes((prev) => ({ + ...prev, + [url]: getImageSize(url), + })) + } + + const getImageSize = (url: string) => imageSizes[url] || { width: 20, height: 20 } + if (iconUrls?.length === 0 || iconNames?.length === 0) return null + if (iconUrls?.length !== iconNames?.length) return null return (
    {iconUrls?.map((url, idx) => { const name = iconNames?.[idx] - if (!url || !name) return null - const size = imageSizes[url] || { width: 20, height: 20 } + const { width, height } = getImageSize(url) + return (
    Date: Wed, 27 Nov 2024 12:25:31 +0900 Subject: [PATCH 363/648] =?UTF-8?q?fix:=20image=20=ED=81=B4=EB=A6=B0?= =?UTF-8?q?=EC=97=85=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/strategies-icon.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 249f2679..164f5ba5 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -27,12 +27,22 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { const [imageSizes, setImageSizes] = useState<{ [key: string]: ImageSizeModel }>({}) useEffect(() => { + const images: HTMLImageElement[] = [] + iconUrls?.forEach((url) => { const image = new window.Image() image.src = url image.onload = () => handleImageLoad(url, image) image.onerror = () => handleImageLoadErr(url) + images.push(image) }) + + return () => { + images.forEach((image) => { + image.onload = null + image.onerror = null + }) + } }, [iconUrls]) const handleImageLoad = (url: string, image: HTMLImageElement) => { From de2799a168f31e1e0ca46824aac7f09109a938c7 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Wed, 27 Nov 2024 12:32:00 +0900 Subject: [PATCH 364/648] =?UTF-8?q?fix:=20=EB=B9=84=EC=8A=B7=ED=95=9C=20?= =?UTF-8?q?=ED=95=B8=EB=93=A4=EB=9F=AC=20=ED=95=A8=EC=88=98=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/strategies-icon.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 164f5ba5..0f740762 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -28,12 +28,11 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { useEffect(() => { const images: HTMLImageElement[] = [] - iconUrls?.forEach((url) => { const image = new window.Image() image.src = url - image.onload = () => handleImageLoad(url, image) - image.onerror = () => handleImageLoadErr(url) + image.onload = () => updateImageSize(url, image.width, image.height) + image.onerror = () => updateImageSize(url, 20, 20) images.push(image) }) @@ -45,17 +44,10 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { } }, [iconUrls]) - const handleImageLoad = (url: string, image: HTMLImageElement) => { - setImageSizes((prev) => ({ - ...prev, - [url]: { width: image.width, height: image.height }, - })) - } - - const handleImageLoadErr = (url: string) => { + const updateImageSize = (url: string, width: number, height: number) => { setImageSizes((prev) => ({ ...prev, - [url]: getImageSize(url), + [url]: { width, height }, })) } From 95702523ad77df89605590a94d7b0b809736879e Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 15:20:48 +0900 Subject: [PATCH 365/648] =?UTF-8?q?feat:=20Label=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/label/index.tsx | 19 +++++++++++++++++++ shared/ui/label/styles.module.scss | 12 ++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 shared/ui/label/index.tsx create mode 100644 shared/ui/label/styles.module.scss diff --git a/shared/ui/label/index.tsx b/shared/ui/label/index.tsx new file mode 100644 index 00000000..6f54541d --- /dev/null +++ b/shared/ui/label/index.tsx @@ -0,0 +1,19 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export type ColorType = 'orange' | 'indigo' + +interface Props { + color?: ColorType + className?: string + children: React.ReactNode +} + +const Label = ({ color = 'indigo', className, children }: Props) => { + return {children} +} + +export default Label diff --git a/shared/ui/label/styles.module.scss b/shared/ui/label/styles.module.scss new file mode 100644 index 00000000..031f45dc --- /dev/null +++ b/shared/ui/label/styles.module.scss @@ -0,0 +1,12 @@ +.label-container { + padding: 7px 16px; + border: 1px solid $color-indigo; + border-radius: 4px; + color: $color-indigo; + @include typo-c1; + + &.orange { + border-color: $color-orange-600; + color: $color-orange-600; + } +} From b985ae9162901940ae055b2d0f7c56a5cac4f563 Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Wed, 27 Nov 2024 16:12:28 +0900 Subject: [PATCH 366/648] =?UTF-8?q?refactor:=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/[traderId]/page.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/traders/[traderId]/page.tsx b/app/(dashboard)/traders/[traderId]/page.tsx index 9f43ec7f..1aa8e50b 100644 --- a/app/(dashboard)/traders/[traderId]/page.tsx +++ b/app/(dashboard)/traders/[traderId]/page.tsx @@ -1,6 +1,5 @@ 'use client' -import styles from '@/app/(dashboard)/traders/[traderId]/page.module.scss' import classNames from 'classnames/bind' import BackHeader from '@/shared/ui/header/back-header' @@ -9,6 +8,7 @@ import TradersListCard from '@/shared/ui/traders-list-card' import ListHeader from '../../_ui/list-header' import StrategiesItem from '../../_ui/strategies-item' +import styles from './page.module.scss' const cx = classNames.bind(styles) @@ -67,9 +67,10 @@ const TraderDetailPage = () => { strategyCount={traderData.strategyCount} subscriberCount={traderData.subscriberCount} traderId={traderData.traderId} + hasButton={false} />
    - + {Array(3) .fill(strategiesModel) .map((item, index) => ( From c3e40a0392db2f7189864a634c5e97c26e48d418 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 27 Nov 2024 16:54:37 +0900 Subject: [PATCH 367/648] =?UTF-8?q?feat:=20post=20=3D>=20get=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_api/check-duplicate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(landing)/signup/_api/check-duplicate.ts b/app/(landing)/signup/_api/check-duplicate.ts index b8335fc9..c8b66342 100644 --- a/app/(landing)/signup/_api/check-duplicate.ts +++ b/app/(landing)/signup/_api/check-duplicate.ts @@ -2,7 +2,7 @@ import axios from 'axios' export const checkNicknameDuplicate = async (nickname: string) => { try { - const response = await axios.post(`/api/users/check/nickname?nickname=${nickname}`) + const response = await axios.get(`/api/users/check/nickname?nickname=${nickname}`) return response.data } catch (err) { console.error(err) @@ -12,7 +12,7 @@ export const checkNicknameDuplicate = async (nickname: string) => { export const checkPhoneDuplicate = async (phone: string) => { try { - const response = await axios.post(`/api/users/check/phone?phone=${phone}`) + const response = await axios.get(`/api/users/check/phone?phone=${phone}`) return response.data } catch (err) { console.error(err) From ac44835e17aab85a2ffff494eb5b8c29119bf741 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 27 Nov 2024 16:55:25 +0900 Subject: [PATCH 368/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=20=EB=93=B1=20api=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_hooks/custom/use-signup-email.ts | 40 ++++++++++-- .../signup/_hooks/custom/use-signup-form.ts | 62 ++++++++++++++----- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-email.ts b/app/(landing)/signup/_hooks/custom/use-signup-email.ts index a560c6fe..24be96ea 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-email.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-email.ts @@ -1,5 +1,8 @@ import { Dispatch, SetStateAction, useState } from 'react' +import { checkEmailDuplicate } from '../../_api/check-duplicate' +import { getEmailAuthenticationResult, requestEmailAuthentication } from '../../_api/email-auth' +import { SIGNUP_ERROR_MESSAGES } from '../../_constants/signup' import { SignupFormErrorsModel, SignupFormModel, @@ -7,6 +10,7 @@ import { } from './../../information/types' interface Props { + form: SignupFormModel errors: SignupFormErrorsModel isValidated: boolean setForm: Dispatch> @@ -14,7 +18,7 @@ interface Props { setFormState: Dispatch> } -const useSignupEmail = ({ errors, isValidated, setForm, setErrors, setFormState }: Props) => { +const useSignupEmail = ({ form, errors, isValidated, setForm, setErrors, setFormState }: Props) => { const [selectedDomain, setSelectedDomain] = useState('') const handleEmailChange = (name: string) => { @@ -48,23 +52,47 @@ const useSignupEmail = ({ errors, isValidated, setForm, setErrors, setFormState const handleEmailVerification = async () => { try { - // TODO: 이메일 인증 API 호출 + const response = await checkEmailDuplicate(form.email, form.emailDomain) + + if (!response.result.isAvailable) { + setErrors((prev) => ({ ...prev, email: SIGNUP_ERROR_MESSAGES.EMAIL_DUPLICATED })) + return + } + + await requestEmailAuthentication(form.email, form.emailDomain) setFormState((prev) => ({ ...prev, isEmailSent: true })) + + if (errors.email) { + setErrors((prev) => ({ ...prev, email: null })) + } } catch (error) { console.error('이메일 인증 발송 실패:', error) + setErrors((prev) => ({ ...prev, email: SIGNUP_ERROR_MESSAGES.EMAIL_SEND_FAILED })) } } const handleVerificationCodeCheck = async () => { try { - // TODO: 인증번호 확인 API 호출 - setFormState((prev) => ({ ...prev, isEmailVerified: true })) + const response = await getEmailAuthenticationResult( + form.email, + form.emailDomain, + form.verificationCode + ) - if (errors.email) { - setErrors((prev) => ({ ...prev, email: null })) + if (response.result.isVerified) { + setFormState((prev) => ({ ...prev, isEmailVerified: true })) + if (errors.email) { + setErrors((prev) => ({ ...prev, email: null })) + } + } else { + setErrors((prev) => ({ ...prev, email: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_MISMATCH })) } } catch (error) { console.error('인증번호 확인 실패:', error) + setErrors((prev) => ({ + ...prev, + email: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_CHECK_FAILED, + })) } } diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index 3ff2bae3..d7ab5fb3 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -4,7 +4,12 @@ import { useRouter } from 'next/navigation' import { PATH } from '@/shared/constants/path' +import { checkNicknameDuplicate, checkPhoneDuplicate } from '../../_api/check-duplicate' +import { signup } from '../../_api/signup' +import { SIGNUP_ERROR_MESSAGES } from '../../_constants/signup' +import { getUserTypeCookie, setNicknameCookie } from '../../_lib/cookies' import { + SignupFormDataModel, SignupFormErrorsModel, SignupFormModel, SignupFormStateModel, @@ -65,31 +70,40 @@ const useSignupForm = () => { const handleNicknameCheck = async () => { try { - // TODO: 닉네임 중복 확인 API 호출 - setFormState((prev) => ({ ...prev, isNicknameVerified: true })) - - if (errors.nickname) { - setErrors((prev) => ({ ...prev, nickname: null })) + const response = await checkNicknameDuplicate(form.nickname) + if (response.result.isAvailable) { + setFormState((prev) => ({ ...prev, isNicknameVerified: true })) + if (errors.nickname) { + setErrors((prev) => ({ ...prev, nickname: null })) + } + } else { + setErrors((prev) => ({ ...prev, nickname: SIGNUP_ERROR_MESSAGES.NICKNAME_DUPLICATED })) } } catch (err) { console.error('닉네임 중복 확인 실패:', err) + setErrors((prev) => ({ ...prev, nickname: SIGNUP_ERROR_MESSAGES.NICKNAME_CHECK_FAILED })) } } const handlePhoneCheck = async () => { try { - // TODO: 휴대폰 번호 중복 확인 API 호출 - setFormState((prev) => ({ ...prev, isPhoneVerified: true })) - - if (errors.phone) { - setErrors((prev) => ({ ...prev, phone: null })) + const response = await checkPhoneDuplicate(form.phone) + + if (response.result.isAvailable) { + setFormState((prev) => ({ ...prev, isPhoneVerified: true })) + if (errors.phone) { + setErrors((prev) => ({ ...prev, phone: null })) + } + } else { + setErrors((prev) => ({ ...prev, phone: SIGNUP_ERROR_MESSAGES.PHONE_DUPLICATED })) } } catch (err) { console.error('휴대폰 번호 중복 확인 실패:', err) + setErrors((prev) => ({ ...prev, phone: SIGNUP_ERROR_MESSAGES.PHONE_CHECK_FAILED })) } } - const handleFormSubmit = () => { + const handleFormSubmit = async () => { const formErrors = validateSignupForm( form, formState.isEmailVerified, @@ -101,10 +115,30 @@ const useSignupForm = () => { setErrors(formErrors) return } + const role = getUserTypeCookie() + + try { + const formData: SignupFormDataModel = { + name: form.name, + nickname: form.nickname, + phone: form.phone, + password: form.password, + email: form.email, + role: role || 'INVESTOR', + infoAgreement: form.isMarketingAgreed, + birthYear: form.birthYear, + birthMonth: form.birthMonth, + birthDay: form.birthDay, + emailDomain: form.emailDomain, + } - // TODO: 회원가입 API 호출 - console.log('폼 제출', form) - router.push(PATH.SIGN_UP_COMPLETE) + await signup(formData) + setNicknameCookie(form.nickname) + router.push(PATH.SIGN_UP_COMPLETE) + } catch (error) { + console.error('회원가입 실패:', error) + setErrors((prev) => ({ ...prev, submitError: '회원가입에 실패했습니다.' })) + } } return { From 0a0511432be21fbf1ef196d3a177037a566839eb Mon Sep 17 00:00:00 2001 From: James Date: Wed, 27 Nov 2024 16:55:50 +0900 Subject: [PATCH 369/648] =?UTF-8?q?fix:=20=ED=97=A4=EB=8D=94=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EB=B2=84=ED=8A=BC=20=EB=88=8C?= =?UTF-8?q?=EB=A0=80=EC=9D=84=EB=95=8C=20=EA=B2=BD=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/constants/path.ts b/shared/constants/path.ts index cb3b0796..17492201 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -1,7 +1,7 @@ export const PATH = { // Auth SIGN_IN: '/signin', - SIGN_UP: '/signup', + SIGN_UP: '/signup/user-type', SIGN_UP_USER_TYPE: '/signup/user-type', SIGN_UP_TERMS_OF_USE: '/signup/terms-of-use', SIGN_UP_INFORMATION: '/signup/information', From 11c7ca0ce1d53181a152447282c649ab7820f47f Mon Sep 17 00:00:00 2001 From: James Date: Wed, 27 Nov 2024 16:56:34 +0900 Subject: [PATCH 370/648] =?UTF-8?q?feat:=20=EC=85=80=EB=A0=89=ED=8A=B8=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EB=9D=BC=EB=8F=84=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=9C=EC=B6=9C=EB=90=98=EB=A9=B4=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=ED=95=A8=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_constants/signup.ts | 8 +++++++ .../signup/information/page.module.scss | 21 ++++++++++++++----- app/(landing)/signup/information/page.tsx | 7 +++++++ app/(landing)/signup/information/types.ts | 5 +++-- app/(landing)/signup/information/utils.ts | 4 ++++ 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/app/(landing)/signup/_constants/signup.ts b/app/(landing)/signup/_constants/signup.ts index e7527370..c2bcc969 100644 --- a/app/(landing)/signup/_constants/signup.ts +++ b/app/(landing)/signup/_constants/signup.ts @@ -4,6 +4,7 @@ export const SIGNUP_ERROR_MESSAGES = { NICKNAME_LENGTH: '닉네임은 2글자 이상 10글자 이하로 입력해주세요.', NICKNAME_CHECK_REQUIRED: '닉네임 중복확인이 필요합니다.', NICKNAME_DUPLICATED: '이미 사용 중인 닉네임입니다.', + NICKNAME_CHECK_FAILED: '닉네임 중복 확인에 실패했습니다.', NAME_MIN_LENGTH: '이름을 2자 이상 입력해주세요.', PASSWORD_REQUIRED: '비밀번호를 입력해주세요.', PASSWORD_INVALID: '비밀번호가 올바르지 않습니다.', @@ -15,4 +16,11 @@ export const SIGNUP_ERROR_MESSAGES = { PHONE_REQUIRED: '휴대폰 번호를 입력해주세요.', PHONE_INVALID: '올바른 휴대폰 번호를 입력해주세요.', PHONE_DUPLICATED: '이미 사용 중인 휴대폰 번호입니다.', + PHONE_CHECK_REQUIRED: '휴대폰 번호 인증이 필요합니다.', + PHONE_CHECK_FAILED: '휴대폰 번호 중복 확인에 실패했습니다.', + VERIFICATION_CODE_REQUIRED: '인증번호를 입력해주세요.', + VERIFICATION_CODE_MISMATCH: '인증번호가 일치하지 않습니다.', + VERIFICATION_CODE_CHECK_FAILED: '인증번호 확인에 실패했습니다.', + EMAIL_SEND_FAILED: '이메일 발송에 실패했습니다.', + SELECT_REQUIRED: '생년월일을 모두 입력해주세요.', } as const diff --git a/app/(landing)/signup/information/page.module.scss b/app/(landing)/signup/information/page.module.scss index d467b43d..ffa53870 100644 --- a/app/(landing)/signup/information/page.module.scss +++ b/app/(landing)/signup/information/page.module.scss @@ -48,6 +48,22 @@ } } + .select-group { + flex-direction: column; + + .select-wrapper { + display: flex; + gap: 12px; + margin-left: 160px; + margin-top: -50px; + } + + .select-error { + margin-left: 160px; + margin-top: 8px; + } + } + .terms-wrapper { display: flex; flex-direction: column; @@ -75,8 +91,3 @@ gap: 48px; margin-bottom: 70px; } - -.select-wrapper { - display: flex; - gap: 12px; -} diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index b1c17724..e0848731 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -7,6 +7,7 @@ import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { Button } from '@/shared/ui/button' import Checkbox from '@/shared/ui/check-box' +import { ErrorMessage } from '@/shared/ui/error-message' import { Input } from '@/shared/ui/input' import { LinkButton } from '@/shared/ui/link-button' import Select from '@/shared/ui/select' @@ -63,6 +64,7 @@ const InformationPage = () => { setForm, setErrors, setFormState, + form, }) const handleChange = (e: ChangeEvent) => { @@ -258,6 +260,11 @@ const InformationPage = () => { titleStyle={selectStyle} />
    + {errors.select && ( +
    + +
    + )}
    diff --git a/app/(landing)/signup/information/types.ts b/app/(landing)/signup/information/types.ts index 85c98830..29cee026 100644 --- a/app/(landing)/signup/information/types.ts +++ b/app/(landing)/signup/information/types.ts @@ -1,4 +1,4 @@ -import { RoleType } from '@/shared/types/auth' +import { UserType } from '@/shared/types/auth' import { DropdownValueType } from '@/shared/ui/dropdown/types' import { SIGNUP_ERROR_MESSAGES } from '../_constants/signup' @@ -28,6 +28,7 @@ export interface SignupFormErrorsModel { password?: SignupErrorMessageType | null passwordConfirm?: SignupErrorMessageType | null phone?: SignupErrorMessageType | null + select?: SignupErrorMessageType | null } export interface SelectOptionModel { @@ -52,7 +53,7 @@ export interface SignupFormDataModel { password: string email: string emailDomain: string - role: RoleType + role: UserType infoAgreement: boolean } diff --git a/app/(landing)/signup/information/utils.ts b/app/(landing)/signup/information/utils.ts index 07da7344..7d402589 100644 --- a/app/(landing)/signup/information/utils.ts +++ b/app/(landing)/signup/information/utils.ts @@ -76,6 +76,10 @@ export const validateSignupForm = ( const phoneError = validateField('PHONE', form.phone) if (phoneError) errors.phone = phoneError + if (!form.birthYear || !form.birthMonth || !form.birthDay) { + errors.select = SIGNUP_ERROR_MESSAGES.SELECT_REQUIRED + } + return errors } From 3d798d831bb0509e631afc0f0eebaeca9d48062d Mon Sep 17 00:00:00 2001 From: James Date: Wed, 27 Nov 2024 18:47:00 +0900 Subject: [PATCH 371/648] =?UTF-8?q?fix:=20=ED=97=A4=EB=8D=94=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EB=B2=84=ED=8A=BC=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 2 +- shared/ui/header/_ui/header-links/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/constants/path.ts b/shared/constants/path.ts index 17492201..a1b8b98f 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -1,7 +1,7 @@ export const PATH = { // Auth SIGN_IN: '/signin', - SIGN_UP: '/signup/user-type', + SIGN_UP: '/signup/', SIGN_UP_USER_TYPE: '/signup/user-type', SIGN_UP_TERMS_OF_USE: '/signup/terms-of-use', SIGN_UP_INFORMATION: '/signup/information', diff --git a/shared/ui/header/_ui/header-links/index.tsx b/shared/ui/header/_ui/header-links/index.tsx index 9b53e1ca..52658346 100644 --- a/shared/ui/header/_ui/header-links/index.tsx +++ b/shared/ui/header/_ui/header-links/index.tsx @@ -11,7 +11,7 @@ const HeaderLinks = () => { 로그인 - + 회원가입 From 1f18a9c1fd6ccafe3d0815758d5719ea63094b77 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 27 Nov 2024 18:50:49 +0900 Subject: [PATCH 372/648] =?UTF-8?q?fix:=20error=20->=20err=EB=A1=9C=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_hooks/custom/use-signup-email.ts | 8 ++++---- .../signup/_hooks/custom/use-signup-form.ts | 4 ++-- app/(landing)/signup/_lib/cookies.ts | 4 ++-- app/test/client/page.tsx | 4 ++-- shared/api/axios.ts | 18 +++++++++--------- shared/hooks/custom/use-auth.ts | 4 ++-- shared/providers/msw-Initializer.tsx | 4 ++-- shared/utils/token-utils.ts | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-email.ts b/app/(landing)/signup/_hooks/custom/use-signup-email.ts index 24be96ea..31f0f411 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-email.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-email.ts @@ -65,8 +65,8 @@ const useSignupEmail = ({ form, errors, isValidated, setForm, setErrors, setForm if (errors.email) { setErrors((prev) => ({ ...prev, email: null })) } - } catch (error) { - console.error('이메일 인증 발송 실패:', error) + } catch (err) { + console.error('이메일 인증 발송 실패:', err) setErrors((prev) => ({ ...prev, email: SIGNUP_ERROR_MESSAGES.EMAIL_SEND_FAILED })) } } @@ -87,8 +87,8 @@ const useSignupEmail = ({ form, errors, isValidated, setForm, setErrors, setForm } else { setErrors((prev) => ({ ...prev, email: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_MISMATCH })) } - } catch (error) { - console.error('인증번호 확인 실패:', error) + } catch (err) { + console.error('인증번호 확인 실패:', err) setErrors((prev) => ({ ...prev, email: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_CHECK_FAILED, diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index d7ab5fb3..cab61d20 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -135,8 +135,8 @@ const useSignupForm = () => { await signup(formData) setNicknameCookie(form.nickname) router.push(PATH.SIGN_UP_COMPLETE) - } catch (error) { - console.error('회원가입 실패:', error) + } catch (err) { + console.error('회원가입 실패:', err) setErrors((prev) => ({ ...prev, submitError: '회원가입에 실패했습니다.' })) } } diff --git a/app/(landing)/signup/_lib/cookies.ts b/app/(landing)/signup/_lib/cookies.ts index 1c09ff4f..0f985ceb 100644 --- a/app/(landing)/signup/_lib/cookies.ts +++ b/app/(landing)/signup/_lib/cookies.ts @@ -39,8 +39,8 @@ export const removeAllSignupCookies = () => { }) return true - } catch (error) { - console.error('회원가입 쿠키 삭제 실패:', error) + } catch (err) { + console.error('회원가입 쿠키 삭제 실패:', err) return false } } diff --git a/app/test/client/page.tsx b/app/test/client/page.tsx index 8c522409..27c5a2b5 100644 --- a/app/test/client/page.tsx +++ b/app/test/client/page.tsx @@ -22,8 +22,8 @@ const Sub = () => { const data = await res.json() setUser(data) } - } catch (error) { - console.error('Fetch error:', error) + } catch (err) { + console.error('Fetch error:', err) } } diff --git a/shared/api/axios.ts b/shared/api/axios.ts index 566ad61f..4de5f879 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -34,8 +34,8 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} } config.headers.Authorization = `Bearer ${accessToken}` - } catch (error) { - console.error('토큰 디코딩 실패:', error) + } catch (err) { + console.error('토큰 디코딩 실패:', err) useAuthStore.getState().setAuthState({ isAuthenticated: false, user: null, @@ -46,22 +46,22 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} return config }, - (error) => Promise.reject(error) + (err) => Promise.reject(err) ) instance.interceptors.response.use( (response) => response, - async (error: AxiosError) => { - if (!error.config) return Promise.reject(error) + async (err: AxiosError) => { + if (!err.config) return Promise.reject(err) - const originalRequest = error.config + const originalRequest = err.config const { isLoggedOut } = useAuthStore.getState() if (isLoggedOut) { - return Promise.reject(error) + return Promise.reject(err) } - if (error.response?.status === 401) { + if (err.response?.status === 401) { try { const newToken = await refreshToken() if (newToken) { @@ -80,7 +80,7 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} }) } - return Promise.reject(error) + return Promise.reject(err) } ) } diff --git a/shared/hooks/custom/use-auth.ts b/shared/hooks/custom/use-auth.ts index 06986698..624e20e3 100644 --- a/shared/hooks/custom/use-auth.ts +++ b/shared/hooks/custom/use-auth.ts @@ -32,8 +32,8 @@ export const useAuth = () => { }) router.replace(PATH.SIGN_IN) - } catch (error) { - console.error('로그아웃 실패:', error) + } catch (err) { + console.error('로그아웃 실패:', err) } } diff --git a/shared/providers/msw-Initializer.tsx b/shared/providers/msw-Initializer.tsx index 6adbbd69..a31ea417 100644 --- a/shared/providers/msw-Initializer.tsx +++ b/shared/providers/msw-Initializer.tsx @@ -19,8 +19,8 @@ export const MSWInitializer = () => { const { default: init } = await import('@/mocks') await init() setReady(true) - } catch (error) { - console.error('MSW initialization failed:', error) + } catch (err) { + console.error('MSW initialization failed:', err) } } diff --git a/shared/utils/token-utils.ts b/shared/utils/token-utils.ts index c0e56866..29fe3591 100644 --- a/shared/utils/token-utils.ts +++ b/shared/utils/token-utils.ts @@ -45,8 +45,8 @@ export const refreshToken = async (): Promise => { } return null - } catch (error) { - console.error('Token refresh failed:', error) + } catch (err) { + console.error('Token refresh failed:', err) return null } finally { isRefreshing = false From 067502e3ecdae64778ac160a80e7b9d64ba196b0 Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 20:00:49 +0900 Subject: [PATCH 373/648] =?UTF-8?q?feat:=20QuestionCard=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/questions/_ui/question-card/index.tsx | 50 +++++++++++++++++++ .../_ui/question-card/styles.module.scss | 47 +++++++++++++++++ shared/types/questions.ts | 1 + shared/utils/format.ts | 15 ++++++ 4 files changed, 113 insertions(+) create mode 100644 app/(dashboard)/my/questions/_ui/question-card/index.tsx create mode 100644 app/(dashboard)/my/questions/_ui/question-card/styles.module.scss create mode 100644 shared/types/questions.ts create mode 100644 shared/utils/format.ts diff --git a/app/(dashboard)/my/questions/_ui/question-card/index.tsx b/app/(dashboard)/my/questions/_ui/question-card/index.tsx new file mode 100644 index 00000000..396928a0 --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/question-card/index.tsx @@ -0,0 +1,50 @@ +import classNames from 'classnames/bind' + +import type { QuestionStatusType } from '@/shared/types/questions' +import Avatar from '@/shared/ui/avatar' +import Label from '@/shared/ui/label' +import { formatDateTime } from '@/shared/utils/format' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + strategyName: string + title: string + contents: string + nickname: string + profileImage?: string + createdAt: string + status: QuestionStatusType +} + +const QuestionCard = ({ + strategyName, + title, + contents, + nickname, + profileImage, + createdAt, + status, +}: Props) => { + return ( +
    +
    + {strategyName} + {formatDateTime(createdAt)} +
    +

    {title}

    +

    {contents}

    +
    +
    + + {nickname} +
    + +
    +
    + ) +} + +export default QuestionCard diff --git a/app/(dashboard)/my/questions/_ui/question-card/styles.module.scss b/app/(dashboard)/my/questions/_ui/question-card/styles.module.scss new file mode 100644 index 00000000..8b0c70b5 --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/question-card/styles.module.scss @@ -0,0 +1,47 @@ +.card-container { + padding: 24px 40px 16px; + border-radius: 8px; + background-color: $color-white; +} + +.top-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + + .strategy-name { + @include typo-b2; + color: $color-orange-600; + } + + .created-at { + @include typo-b3; + color: $color-gray-400; + } +} + +.title { + margin: 12px 0 8px; + @include typo-h4; +} + +.contents { + margin-bottom: 18px; + color: $color-gray-600; + @include typo-b2; + @include ellipsis(1); +} + +.bottom-wrapper { + display: flex; + align-items: center; + justify-content: space-between; +} + +.avatar-wrapper { + display: flex; + align-items: center; + gap: 16px; + color: $color-gray-600; + @include typo-b3; +} diff --git a/shared/types/questions.ts b/shared/types/questions.ts new file mode 100644 index 00000000..a77df7bd --- /dev/null +++ b/shared/types/questions.ts @@ -0,0 +1 @@ +export type QuestionStatusType = '답변 대기' | '답변 완료' diff --git a/shared/utils/format.ts b/shared/utils/format.ts new file mode 100644 index 00000000..a6b11d19 --- /dev/null +++ b/shared/utils/format.ts @@ -0,0 +1,15 @@ +export const formatDateTime = (dateString: string) => { + const date = new Date(dateString) + + if (isNaN(date.getTime())) { + return dateString + } + + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + + return `${year}.${month}.${day} ${hours}:${minutes}` +} From 4ff01d483cde0c9f9eddce13782a4fcf24d9a989 Mon Sep 17 00:00:00 2001 From: deun Date: Wed, 27 Nov 2024 20:01:31 +0900 Subject: [PATCH 374/648] =?UTF-8?q?feat:=20QuestionCard=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../question-card/question-card.stories.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx diff --git a/app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx b/app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx new file mode 100644 index 00000000..a0f077ed --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx @@ -0,0 +1,35 @@ +import { Meta, StoryFn } from '@storybook/react' + +import QuestionCard from '.' + +const meta: Meta = { + title: 'Components/QuestionCard', + component: QuestionCard, + tags: ['autodocs'], +} + +const Template: StoryFn = (args) => ( +
    + +
    +) + +export const Default = Template.bind({}) +Default.args = { + strategyName: '전략 이름', + title: '미국발 경제악화가 한국 증시에 미치는 영향은 무엇인가요?', + contents: + '안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........', + nickname: '투자할래요', + profileImage: '', + createdAt: '2024-11-03T15:00:00', + status: '답변 대기', +} + +export const Answered = Template.bind({}) +Answered.args = { + ...Default.args, + status: '답변 완료', +} + +export default meta From 43e0e3090fb2a8835c5db0adaea256a0e16478b2 Mon Sep 17 00:00:00 2001 From: deun Date: Thu, 28 Nov 2024 11:11:38 +0900 Subject: [PATCH 375/648] =?UTF-8?q?refactor:=20QuestionCard=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=8A=A4=ED=83=80=EC=9D=BC=20mixin=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20(#140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-card/card-mixins.scss | 40 +++++++++++++++++++ .../_ui/question-card/styles.module.scss | 25 ++++-------- 2 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 app/(dashboard)/my/questions/_ui/question-card/card-mixins.scss diff --git a/app/(dashboard)/my/questions/_ui/question-card/card-mixins.scss b/app/(dashboard)/my/questions/_ui/question-card/card-mixins.scss new file mode 100644 index 00000000..a3f373a3 --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/question-card/card-mixins.scss @@ -0,0 +1,40 @@ +@mixin card-base { + padding: 24px 40px 16px; + border-radius: 8px; + background-color: $color-white; +} + +@mixin card-subtitle { + @include typo-b2; + color: $color-orange-600; +} + +@mixin card-created-at { + @include typo-b3; + color: $color-gray-400; +} + +@mixin card-top { + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin card-title { + margin: 12px 0 8px; + @include typo-h4; +} + +@mixin card-bottom { + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin avatar-group { + display: flex; + align-items: center; + gap: 16px; + color: $color-gray-600; + @include typo-b3; +} diff --git a/app/(dashboard)/my/questions/_ui/question-card/styles.module.scss b/app/(dashboard)/my/questions/_ui/question-card/styles.module.scss index 8b0c70b5..68caf223 100644 --- a/app/(dashboard)/my/questions/_ui/question-card/styles.module.scss +++ b/app/(dashboard)/my/questions/_ui/question-card/styles.module.scss @@ -1,7 +1,7 @@ +@import './card-mixins.scss'; + .card-container { - padding: 24px 40px 16px; - border-radius: 8px; - background-color: $color-white; + @include card-base; } .top-wrapper { @@ -10,19 +10,16 @@ justify-content: space-between; .strategy-name { - @include typo-b2; - color: $color-orange-600; + @include card-subtitle; } .created-at { - @include typo-b3; - color: $color-gray-400; + @include card-created-at; } } .title { - margin: 12px 0 8px; - @include typo-h4; + @include card-title; } .contents { @@ -33,15 +30,9 @@ } .bottom-wrapper { - display: flex; - align-items: center; - justify-content: space-between; + @include card-bottom; } .avatar-wrapper { - display: flex; - align-items: center; - gap: 16px; - color: $color-gray-600; - @include typo-b3; + @include avatar-group; } From 40c11bf5281f0e89d87ee28041304091c2ac0752 Mon Sep 17 00:00:00 2001 From: deun Date: Thu, 28 Nov 2024 11:13:26 +0900 Subject: [PATCH 376/648] =?UTF-8?q?refactor:=20QuestionCard=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20Props=20=EB=B6=84=EB=A6=AC=20(#140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/questions/_ui/question-card/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/my/questions/_ui/question-card/index.tsx b/app/(dashboard)/my/questions/_ui/question-card/index.tsx index 396928a0..18addfdb 100644 --- a/app/(dashboard)/my/questions/_ui/question-card/index.tsx +++ b/app/(dashboard)/my/questions/_ui/question-card/index.tsx @@ -9,13 +9,16 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -interface Props { - strategyName: string - title: string +export interface QuestionCardProps { contents: string nickname: string profileImage?: string createdAt: string +} + +interface Props extends QuestionCardProps { + strategyName: string + title: string status: QuestionStatusType } From cc6c8033507fb609de9b4a2b3dbdf0dffee5498f Mon Sep 17 00:00:00 2001 From: deun Date: Thu, 28 Nov 2024 12:08:03 +0900 Subject: [PATCH 377/648] =?UTF-8?q?feat:=20QuestionDetailCard=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-detail-card/index.tsx | 64 +++++++++++++++++++ .../question-detail-card/styles.module.scss | 54 ++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx create mode 100644 app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx new file mode 100644 index 00000000..6f56a913 --- /dev/null +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx @@ -0,0 +1,64 @@ +import classNames from 'classnames/bind' + +import { QuestionStatusType } from '@/shared/types/questions' +import Avatar from '@/shared/ui/avatar' +import Label from '@/shared/ui/label' +import { formatDateTime } from '@/shared/utils/format' + +import { QuestionCardProps } from '../../../_ui/question-card' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +type QuestionDetailCardType = 'question' | 'answer' + +interface Props extends QuestionCardProps { + type?: QuestionDetailCardType + strategyName?: string + title?: string + status?: QuestionStatusType + isAuthor: boolean + onDelete?: () => void +} + +const QuestionDetailCard = ({ + profileImage, + nickname, + contents, + type = 'question', + status, + strategyName, + title = '답변', + createdAt, + isAuthor, + onDelete, +}: Props) => { + return ( +
    +
    + {type === 'question' && ( +
    + {strategyName} + +
    + )} +

    {title}

    +
    +
    + + {nickname} + ㅣ {formatDateTime(createdAt)} +
    + {isAuthor && ( + + )} +
    +
    +
    {contents}
    +
    + ) +} + +export default QuestionDetailCard diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss new file mode 100644 index 00000000..67f6e6b3 --- /dev/null +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss @@ -0,0 +1,54 @@ +@import '../../../_ui/question-card/card-mixins.scss'; + +.card-container { + @include card-base; + padding: 35px 40px; +} + +.top-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + + .strategy-name { + @include card-subtitle; + } +} + +.title { + @include card-title; + margin-bottom: 18px; + + &.answer { + @include typo-b1; + } +} + +.bottom-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 12px; + margin-bottom: 24px; + border-bottom: 1px solid $color-gray-300; + + .delete-button { + @include typo-c1; + background-color: transparent; + } +} + +.avatar-wrapper { + @include avatar-group; + + .created-at { + margin-left: -8px; + color: $color-gray-400; + } +} + +.card-contents { + color: $color-gray-600; + @include typo-b3; + line-height: 140%; +} From 4b2c6ada68e1b4cbee714d706e694d0736c9114a Mon Sep 17 00:00:00 2001 From: James Date: Thu, 28 Nov 2024 12:33:57 +0900 Subject: [PATCH 378/648] =?UTF-8?q?fix:=20=EC=9C=A0=EC=A0=80=20=EB=A1=A4?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EB=B3=80=EC=88=98=20=EC=88=98=EC=A0=95?= =?UTF-8?q?(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/types/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/types/auth.ts b/shared/types/auth.ts index 1025dc33..6b24d3cf 100644 --- a/shared/types/auth.ts +++ b/shared/types/auth.ts @@ -53,10 +53,10 @@ export interface TokenStatusModel { export const isAdmin = (user: UserModel | null): boolean => { if (!user) return false - return user.role.includes('admin') + return user.role.includes('ADMIN') } export const isTrader = (user: UserModel | null): boolean => { if (!user) return false - return user.role.includes('trader') + return user.role.includes('TRADER') } From fcbf6ed9cab13e0ee8df9651743ac643029b53d0 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 28 Nov 2024 12:36:19 +0900 Subject: [PATCH 379/648] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/list-header/index.tsx | 15 +++++-- .../_ui/list-header/styles.module.scss | 4 ++ .../my/_api/get-my-strategy-list.ts | 19 +++++++++ .../custom/use-intersection-observer.ts | 37 +++++++++++++++++ .../_hooks/query/use-get-my-strategy-list.ts | 24 +++++++++++ .../strategies/_ui/my-strategy-list/index.tsx | 41 +++++++++++++++++++ app/(dashboard)/my/strategies/page.tsx | 22 +++++++++- .../my/strategies/styles.module.scss | 3 ++ 8 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 app/(dashboard)/my/_api/get-my-strategy-list.ts create mode 100644 app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts create mode 100644 app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx create mode 100644 app/(dashboard)/my/strategies/styles.module.scss diff --git a/app/(dashboard)/_ui/list-header/index.tsx b/app/(dashboard)/_ui/list-header/index.tsx index c1edf917..b8ace106 100644 --- a/app/(dashboard)/_ui/list-header/index.tsx +++ b/app/(dashboard)/_ui/list-header/index.tsx @@ -4,12 +4,19 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -const LIST_HEADER = ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '구독'] +const LIST_HEADER = { + default: ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '구독'], + my: ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '공개', '관리'], +} + +interface Props { + type?: 'default' | 'my' +} -const ListHeader = () => { +const ListHeader = ({ type = 'default' }: Props) => { return ( -
    - {LIST_HEADER.map((category) => ( +
    + {LIST_HEADER[type].map((category) => (
    {category}
    diff --git a/app/(dashboard)/_ui/list-header/styles.module.scss b/app/(dashboard)/_ui/list-header/styles.module.scss index 0050d4c6..ee5053ef 100644 --- a/app/(dashboard)/_ui/list-header/styles.module.scss +++ b/app/(dashboard)/_ui/list-header/styles.module.scss @@ -5,6 +5,10 @@ height: 42px; margin: 20px 0 10px; + &.my { + grid-template-columns: 3fr 1.5fr 1.7fr 1.3fr 1.5fr 1.2fr 1.2fr; + } + .category { display: flex; align-items: center; diff --git a/app/(dashboard)/my/_api/get-my-strategy-list.ts b/app/(dashboard)/my/_api/get-my-strategy-list.ts new file mode 100644 index 00000000..3c072bcc --- /dev/null +++ b/app/(dashboard)/my/_api/get-my-strategy-list.ts @@ -0,0 +1,19 @@ +import axiosInstance from '@/shared/api/axios' +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +// 실제 api 나오면 수정 필요함 +// totalElements 사용해서 hasmore값 계산해야될 것 같음 + +interface StrategiesResponseModel { + result: { + strategies: StrategiesModel[] + hasMore: boolean + } +} + +export const getMyStrategyList = async ({ page = 1, size = 4 }: { page: number; size: number }) => { + const response = await axiosInstance.get( + `/api/my-strategies/page=${page}&size=${size}` + ) + return response.data.result +} diff --git a/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts b/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts new file mode 100644 index 00000000..0ffbf5bf --- /dev/null +++ b/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts @@ -0,0 +1,37 @@ +import { RefObject, useEffect } from 'react' + +interface UseIntersectionObserverProps { + ref: RefObject + onIntersect: (entry: IntersectionObserverEntry) => void + threshold?: number + rootMargin?: string +} + +export const useIntersectionObserver = ({ + ref, + onIntersect, + threshold = 0.1, + rootMargin = '0px', +}: UseIntersectionObserverProps) => { + useEffect(() => { + if (!ref.current) return + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => onIntersect(entry)) + }, + { + threshold, + rootMargin, + } + ) + + observer.observe(ref.current) + + return () => { + if (ref.current) { + observer.unobserve(ref.current) + } + } + }, [ref, threshold, rootMargin, onIntersect]) +} diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts new file mode 100644 index 00000000..8b87cc7c --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts @@ -0,0 +1,24 @@ +import { getMyStrategyList } from '@/app/(dashboard)/my/_api/get-my-strategy-list' +import { useInfiniteQuery } from '@tanstack/react-query' + +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +interface StrategiesPageModel { + strategies: StrategiesModel[] + hasMore: boolean +} + +export const useGetMyStrategyList = () => { + return useInfiniteQuery({ + queryKey: ['myStrategies'], + queryFn: async ({ pageParam = 1 }) => { + const page = typeof pageParam === 'number' ? pageParam : 1 + return getMyStrategyList({ page, size: 4 }) + }, + getNextPageParam: (lastPage, pages) => { + if (!lastPage.hasMore) return undefined + return pages.length + 1 + }, + initialPageParam: 1, + }) +} diff --git a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx new file mode 100644 index 00000000..79aace9a --- /dev/null +++ b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx @@ -0,0 +1,41 @@ +'use client' + +import { useCallback, useRef } from 'react' + +import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' +import { useIntersectionObserver } from '@/app/(dashboard)/my/_hooks/custom/use-intersection-observer' +import { useGetMyStrategyList } from '@/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list' + +const MyStrategyList = () => { + const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetMyStrategyList() + + const loadMoreRef = useRef(null) + + const onIntersect = useCallback( + (entry: IntersectionObserverEntry) => { + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage() + } + }, + [fetchNextPage, hasNextPage, isFetchingNextPage] + ) + + useIntersectionObserver({ + ref: loadMoreRef, + onIntersect, + }) + + const strategies = data?.pages.flatMap((page) => page.strategies) || [] + + return ( + <> + {strategies.map((strategy) => ( + + ))} +
    + {isFetchingNextPage &&
    로딩 중...
    } + + ) +} + +export default MyStrategyList diff --git a/app/(dashboard)/my/strategies/page.tsx b/app/(dashboard)/my/strategies/page.tsx index 6f7e77b5..7010a8a5 100644 --- a/app/(dashboard)/my/strategies/page.tsx +++ b/app/(dashboard)/my/strategies/page.tsx @@ -1,5 +1,25 @@ +import { Suspense } from 'react' + +import classNames from 'classnames/bind' + +import Title from '@/shared/ui/title' + +import ListHeader from '../../_ui/list-header' +import MyStrategyList from './_ui/my-strategy-list' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + const MyStrategiesPage = () => { - return <> + return ( +
    + + <ListHeader type="my" /> + <Suspense fallback={<div>Loading...</div>}> + <MyStrategyList /> + </Suspense> + </div> + ) } export default MyStrategiesPage diff --git a/app/(dashboard)/my/strategies/styles.module.scss b/app/(dashboard)/my/strategies/styles.module.scss new file mode 100644 index 00000000..f481a8ff --- /dev/null +++ b/app/(dashboard)/my/strategies/styles.module.scss @@ -0,0 +1,3 @@ +.container { + margin-top: 80px; +} From 82d3039aee69e78edf8ca317da1549ce6cc66d04 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 12:59:28 +0900 Subject: [PATCH 380/648] =?UTF-8?q?feat:=20QuestionDetailCard=20=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80=20(#140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../question-detail-card.stories.tsx | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx new file mode 100644 index 00000000..f778b621 --- /dev/null +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx @@ -0,0 +1,57 @@ +import { Meta, StoryFn } from '@storybook/react' + +import QuestionDetailCard from '.' + +const meta: Meta = { + title: 'Components/QuestionDetailCard', + component: QuestionDetailCard, + tags: ['autodocs'], +} + +const Template: StoryFn<typeof QuestionDetailCard> = (args) => ( + <div style={{ width: '900px', padding: '20px', backgroundColor: '#f8f9fa' }}> + <QuestionDetailCard {...args} /> + </div> +) + +export const Question = Template.bind({}) +Question.args = { + type: 'question', + strategyName: '엄청난 전략', + title: '미국발 경제악화가 한국 증시에 미치는 영향은 무엇인가요?', + contents: + '안녕하세요. 주식투자를 시작하려고 하는데 미국의 경제 상황이 좋지 않다고 들었습니다. 이런 상황에서 한국 증시는 어떤 영향을 받을까요? 구체적인 설명 부탁드립니다.', + nickname: '투자초보', + profileImage: '', + createdAt: '2024-11-03T15:00:00', + status: '답변 대기', + isAuthor: false, + onDelete: () => alert('삭제 버튼 클릭'), +} + +export const QuestionWithDeleteButton = Template.bind({}) +QuestionWithDeleteButton.args = { + ...Question.args, + isAuthor: true, +} + +export const Answer = Template.bind({}) +Answer.args = { + type: 'answer', + title: '답변', + contents: + '안녕하세요. 문의하신 내용에 대해 답변드리겠습니다. 미국과 한국 증시는 높은 상관관계를 보이고 있어 미국의 경제 상황이 한국 증시에 큰 영향을 미칠 수 있습니다. 구체적으로는...', + nickname: '전문가', + profileImage: '', + createdAt: '2024-11-03T16:30:00', + isAuthor: false, + onDelete: () => alert('삭제 버튼 클릭'), +} + +export const AnswerWithDeleteButton = Template.bind({}) +AnswerWithDeleteButton.args = { + ...Answer.args, + isAuthor: true, +} + +export default meta From 1256749d6417c68310a5ed6bcacb43e056930ec2 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 13:28:48 +0900 Subject: [PATCH 381/648] =?UTF-8?q?design:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=84=A0=ED=83=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=97=90=20=EC=9D=BC=EB=9F=AC=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#141)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_ui/user-type-card/index.tsx | 6 ++ .../_ui/user-type-card/styles.module.scss | 10 +++ .../signup/user-type/page.module.scss | 2 +- public/images/index.ts | 2 + public/images/investor.svg | 59 ++++++++++++++++ public/images/trader.svg | 68 +++++++++++++++++++ 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 public/images/investor.svg create mode 100644 public/images/trader.svg diff --git a/app/(landing)/signup/_ui/user-type-card/index.tsx b/app/(landing)/signup/_ui/user-type-card/index.tsx index aa3f6a71..7e62e318 100644 --- a/app/(landing)/signup/_ui/user-type-card/index.tsx +++ b/app/(landing)/signup/_ui/user-type-card/index.tsx @@ -7,6 +7,7 @@ import { setIsAgreedTermsCookie, setUserTypeCookie, } from '@/app/(landing)/signup/_lib/cookies' +import { InvestorImg, TraderImg } from '@/public/images' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -43,6 +44,11 @@ const UserTypeCard = ({ userType, title, highlight }: Props) => { 인베스트메틱을 통해 <br /> <span className={cx('highlight')}>{highlight}</span>해보세요! </p> + {userType === 'TRADER' ? ( + <TraderImg className={cx('image')} /> + ) : ( + <InvestorImg className={cx('image')} /> + )} </button> ) } diff --git a/app/(landing)/signup/_ui/user-type-card/styles.module.scss b/app/(landing)/signup/_ui/user-type-card/styles.module.scss index 76321cb9..d4bbf0a1 100644 --- a/app/(landing)/signup/_ui/user-type-card/styles.module.scss +++ b/app/(landing)/signup/_ui/user-type-card/styles.module.scss @@ -1,6 +1,9 @@ .card { + display: flex; + flex-direction: column; width: 100%; padding: 43px 44px 53px; + margin-bottom: 140px; text-align: left; background-color: $color-white; border: 2px solid $color-white; @@ -9,6 +12,13 @@ &:hover { border: 2px solid $color-orange-400; } + + .image { + width: 234px; + height: 210px; + margin-top: 32px; + align-self: flex-end; + } } .title { diff --git a/app/(landing)/signup/user-type/page.module.scss b/app/(landing)/signup/user-type/page.module.scss index 986a3f5b..4f5a27d3 100644 --- a/app/(landing)/signup/user-type/page.module.scss +++ b/app/(landing)/signup/user-type/page.module.scss @@ -3,5 +3,5 @@ gap: 32px; width: 100%; max-width: 952px; - margin: 120px auto 0; + margin: 100px auto 0; } diff --git a/public/images/index.ts b/public/images/index.ts index 40862b03..e55d8e3d 100644 --- a/public/images/index.ts +++ b/public/images/index.ts @@ -1,3 +1,5 @@ export { default as ImageLogo } from './logo.svg' export { default as TextLogo } from './text-logo.svg' export { default as PageNotFoundImg } from './page-not-found-img.svg' +export { default as TraderImg } from './trader.svg' +export { default as InvestorImg } from './investor.svg' diff --git a/public/images/investor.svg b/public/images/investor.svg new file mode 100644 index 00000000..8bec66c3 --- /dev/null +++ b/public/images/investor.svg @@ -0,0 +1,59 @@ +<svg width="234" height="210" viewBox="0 0 234 210" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M48.127 70.271C48.127 70.271 34.3325 29.4539 82.4479 34.4356C130.563 39.4173 107.574 21.351 125.756 10.1396C143.937 -1.07175 197.509 33.8078 175.096 67.4496C152.684 101.091 210.914 70.3171 223.06 120.908C230.556 152.128 211.959 175.107 196.617 188.133" stroke="#ECECEC" stroke-width="2" stroke-miterlimit="10"/> +<path d="M201.309 149.125C197.693 128.186 184.89 109.449 164.089 102.742C158.768 101.036 153.327 99.73 147.812 98.8344V98.8626C147.524 100.601 146.843 102.251 145.821 103.685C143.653 106.655 140.665 109.095 137.205 110.366C133.674 111.673 129.299 111.842 126.022 109.787C124.049 108.552 122.134 106.376 121.789 104.029C121.394 101.384 122.742 98.8805 124.638 97.1328C108.566 97.2635 93.4462 99.9081 81.3302 102.919C81.7502 104.677 82.3317 106.393 83.0677 108.044C84.0976 110.346 85.5529 112.433 87.3574 114.195C89.6082 111.965 91.5788 109.469 93.2258 106.763C94.8013 107.279 96.1907 108.246 97.2209 109.545C98.2508 110.844 98.8766 112.417 99.0198 114.069V114.154C99.6554 120.153 93.8793 124.66 89.0257 127.433C83.4187 130.657 77.1685 133.071 71.0286 135.077C68.4967 135.907 65.2063 136.615 62.5335 135.749C62.2458 135.657 61.9649 135.545 61.693 135.413C59.0279 134.106 57.2443 131.367 55.8528 128.863C51.8885 121.752 49.3105 114 47.5628 106.043C35.4341 107.593 34.0451 99.3802 33.325 97.366L22.9491 126.372C22.4048 128.457 22.3461 130.639 22.7769 132.75C23.2079 134.862 24.1174 136.846 25.4353 138.551C26.753 140.256 28.4443 141.636 30.3788 142.585C32.3136 143.534 34.44 144.027 36.5949 144.026H79.5492L99.0608 194.073L104.591 208.278H136.178C133.615 208.278 132.213 207.653 131.196 206.633C130.145 205.582 129.554 204.157 129.551 202.671C129.551 199.596 132.075 197.074 136.178 197.074H136.467C136.157 197.074 135.86 197.046 135.55 197.018C135.742 196.997 135.933 196.965 136.121 196.923C135.918 196.918 135.715 196.903 135.514 196.877C137.364 196.658 139.079 195.796 140.358 194.442C141.638 193.087 142.4 191.326 142.512 189.466C142.517 189.369 142.517 189.273 142.512 189.176V189.028C142.468 187.121 141.739 185.293 140.459 183.88C139.179 182.466 137.432 181.56 135.539 181.327C135.755 181.309 135.96 181.291 136.167 181.291C135.98 181.245 135.773 181.217 135.578 181.189C135.885 181.16 136.195 181.14 136.493 181.14L159.602 181.263L163.854 181.281H168.899C169.133 181.281 169.368 181.281 169.591 181.309L177.087 181.348L182.094 181.376C184.187 181.393 186.187 182.239 187.658 183.728C189.129 185.216 189.951 187.227 189.943 189.32C189.941 190.503 189.666 191.67 189.141 192.731C191.29 192.456 193.282 191.462 194.794 189.912C198.4 186.229 199.953 180.717 201.027 175.817C202.926 167.089 202.813 157.892 201.309 149.125ZM142.922 149.715C138.568 156.416 131.155 155.814 131.155 155.814C131.155 155.814 130.54 156.413 129.105 153.861C127.67 151.309 123.582 149.564 123.582 149.564C123.257 152.126 122.583 156.021 115.895 150.973C109.206 145.925 111.223 138.565 111.794 134.677C112.366 130.79 114.644 129.501 114.644 129.501L117.335 128.081C117.335 128.081 109.552 126.08 109.178 126.006C108.804 125.931 101.872 120.127 108.906 118.595C115.941 117.062 126.906 123.156 129.025 124.36C131.145 125.565 143.53 136.041 143.53 136.041C143.53 136.041 147.299 143.024 142.922 149.715Z" fill="#353642"/> +<path d="M47.5527 106.048C47.5527 106.048 53.4185 105.336 55.6147 101.148L53.7004 111.888C53.7004 111.888 77.0354 103.944 81.315 102.919C81.315 102.919 82.6937 111.809 88.1827 114.195L93.8461 106.994C93.8461 106.994 100.947 108.788 99.0046 118.464C97.0622 128.14 70.1959 136.228 65.6396 136.041C61.0833 135.854 56.1784 133.05 51.1558 117.985L47.5527 106.048Z" fill="#353642"/> +<path d="M57.1089 82.125C58.5952 84.5441 60.0892 86.9581 61.6319 89.3413C62.5826 90.8097 64.3149 92.9033 62.4724 94.4383C61.8728 94.898 61.1486 95.1661 60.3942 95.2071C59.2102 95.3121 57.9674 95.0277 57.2473 94.0103L53.6597 88.0651C52.9447 86.8794 51.8092 86.0066 50.4797 85.6199C49.15 85.2334 47.7236 85.3618 46.4844 85.9791" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M70.6772 89.9591C71.6152 92.1886 70.9796 93.6262 69.8828 94.3488C68.089 95.5251 65.6879 94.9613 64.4168 93.2444L57.1211 82.1406" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M63.2844 78.8942C65.9392 82.1231 69.2886 88.1196 71.1413 90.6232C72.9941 93.1269 75.8591 91.6175 75.8591 91.6175C78.1859 90.3644 77.7913 88.2861 76.7739 85.762C74.2677 79.5656 70.6903 75.4449 66.7003 72.744C54.3358 64.3796 41.6509 70.5504 37.8762 84.9881L22.9491 126.371C22.4048 128.457 22.3461 130.639 22.7769 132.75C23.2079 134.861 24.1174 136.846 25.4353 138.551C26.753 140.256 28.4443 141.636 30.3788 142.585C32.3136 143.534 34.44 144.027 36.5949 144.025H79.5492L104.599 208.28" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M78.6223 82.927C81.6974 81.861 81.5565 76.9561 77.1924 75.3007C72.3721 73.471 63.8131 70.8828 61.663 70.237C61.6497 70.2268 61.6336 70.2206 61.6169 70.2191C61.4657 70.1729 61.3606 70.1345 61.2786 70.1166L61.1582 70.0781" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M124.055 131.963C121.365 128.729 114 126.897 109.192 126.01C108.384 125.858 107.648 125.448 107.093 124.841C106.538 124.234 106.196 123.464 106.117 122.645C106.061 122.093 106.125 121.536 106.304 121.01C106.591 120.204 107.14 119.517 107.863 119.06C108.587 118.603 109.443 118.401 110.294 118.489C118.495 119.319 126.951 121.899 133.207 127.458C133.207 127.458 163.53 150.598 167.151 152.851C168.645 153.758 170.039 152.687 169.027 151.262L168.709 150.718C166.926 147.66 164.817 144.804 162.418 142.2" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M124.619 97.1345C86.4819 97.4523 53.7012 111.89 53.7012 111.89L56.9736 93.5469" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M183.851 196.966C183.851 196.966 179.697 196.026 178.592 199.352C177.488 202.679 176.335 207.97 184.276 208.283C192.218 208.596 187.554 207.035 187.095 204.465C186.636 201.894 183.851 196.966 183.851 196.966Z" fill="#353642"/> +<path d="M120.392 136.184C120.392 136.184 115.91 132.786 111.874 136.23C111.704 136.38 111.556 136.552 111.434 136.743" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<g opacity="0.77"> +<path opacity="0.77" d="M64.4019 93.2322C65.6832 94.9517 68.0818 95.513 69.8679 94.3342C70.9519 93.6166 71.5874 92.1867 70.6726 89.9777C70.8392 90.2212 70.9903 90.4364 71.139 90.6338C72.9892 93.1477 75.8567 91.6332 75.8567 91.6332C78.1836 90.3801 77.7915 88.3018 76.7741 85.7751C75.7786 83.2724 74.4724 80.9049 72.8867 78.7279C73.9245 77.7567 75.1571 76.7752 76.4564 77.2057C77.8581 77.6721 78.2502 79.4301 78.4091 80.8959C78.4834 81.5775 78.5577 82.2515 78.632 82.9332L79.7544 92.9683C80.1286 96.2997 80.5027 99.6797 81.3151 102.932C81.7351 104.69 82.3165 106.406 83.0525 108.057C84.0824 110.359 85.5377 112.446 87.3423 114.207C89.593 111.977 91.5637 109.482 93.2107 106.776C94.7861 107.291 96.1756 108.259 97.2057 109.558C98.2357 110.857 98.8614 112.43 99.0047 114.082V114.166C99.6402 120.165 93.8641 124.673 89.0106 127.446C83.4036 130.669 77.1534 133.083 71.0134 135.09C68.4816 135.92 65.1912 136.627 62.5184 135.761C62.2306 135.669 61.9498 135.557 61.6779 135.425C59.0128 134.119 57.2292 131.379 55.8377 128.875C49.1749 116.895 46.4073 103.139 44.9492 89.5165C47.8655 89.1141 50.7714 88.6657 53.6416 88.0481C53.6416 88.0481 53.6416 88.0686 53.6595 88.0763L56.9678 93.5628L57.2394 94.019C57.957 95.044 59.1998 95.3182 60.3863 95.2157C61.1407 95.1783 61.8654 94.91 62.462 94.4469C64.302 92.9093 62.5748 90.8234 61.6215 89.355" fill="white"/> +<path opacity="0.77" d="M64.4019 93.2322C65.6832 94.9517 68.0818 95.513 69.8679 94.3342C70.9519 93.6166 71.5874 92.1867 70.6726 89.9777C70.8392 90.2212 70.9903 90.4364 71.139 90.6338C72.9892 93.1477 75.8567 91.6332 75.8567 91.6332C78.1836 90.3801 77.7915 88.3018 76.7741 85.7751C75.7786 83.2724 74.4724 80.9049 72.8867 78.7279C73.9245 77.7567 75.1571 76.7752 76.4564 77.2057C77.8581 77.6721 78.2502 79.4301 78.4091 80.8959C78.4834 81.5775 78.5577 82.2515 78.632 82.9332L79.7544 92.9683C80.1286 96.2997 80.5027 99.6797 81.3151 102.932C81.7351 104.69 82.3165 106.406 83.0525 108.057C84.0824 110.359 85.5377 112.446 87.3423 114.207C89.593 111.977 91.5637 109.482 93.2107 106.776C94.7861 107.291 96.1756 108.259 97.2057 109.558C98.2357 110.857 98.8614 112.43 99.0047 114.082V114.166C99.6402 120.165 93.8641 124.673 89.0106 127.446C83.4036 130.669 77.1534 133.083 71.0134 135.09C68.4816 135.92 65.1912 136.627 62.5184 135.761C62.2306 135.669 61.9498 135.557 61.6779 135.425C59.0128 134.119 57.2292 131.379 55.8377 128.875C49.1749 116.895 46.4073 103.139 44.9492 89.5165C47.8655 89.1141 50.7714 88.6657 53.6416 88.0481C53.6416 88.0481 53.6416 88.0686 53.6595 88.0763L56.9678 93.5628L57.2394 94.019C57.957 95.044 59.1998 95.3182 60.3863 95.2157C61.1407 95.1783 61.8654 94.91 62.462 94.4469C64.302 92.9093 62.5748 90.8234 61.6215 89.355" fill="#FF7752"/> +<path opacity="0.77" d="M65.1884 94.0447C66.3236 95.5822 68.2635 95.3875 69.8677 94.3342C70.8364 93.6936 71.6308 93.8807 70.8133 91.9049C70.9645 92.1202 70.985 91.3924 71.1182 91.5538C72.7711 93.7987 71.1387 90.6262 71.1387 90.6262C73.4451 93.5398 78.4652 90.7953 77.5452 88.5428C76.5715 86.116 75.5464 82.5284 74.2472 80.7345C75.2722 79.7659 75.0749 79.9094 76.3793 80.6807C76.7296 80.9057 77.023 81.2094 77.2359 81.5674C77.4491 81.9254 77.576 82.328 77.6067 82.7436C77.7118 83.72 77.8169 84.6553 77.9219 85.6086L78.9214 94.5777C79.2571 97.558 79.5902 100.569 80.3154 103.47C80.6903 105.038 81.2103 106.569 81.8684 108.042C82.7858 110.069 85.7327 113.444 87.3421 114.989C89.3434 113.003 91.742 109.351 93.2104 106.924C96.0293 107.79 95.9063 110.486 96.1062 113.431V113.508C96.6751 118.861 91.5191 122.882 87.1986 125.36C82.1912 128.237 76.6125 130.39 71.1311 132.184C68.8708 132.927 65.9341 133.568 63.5483 132.783C63.291 132.702 63.0399 132.602 62.7975 132.484C60.4194 131.315 58.8255 128.873 57.5826 126.636C52.1422 116.865 49.618 105.771 48.2291 94.6495C48.1271 93.8171 48.3524 92.9781 48.8572 92.3088C49.362 91.6394 50.107 91.1922 50.9352 91.0618C52.4958 90.8055 54.0513 90.5493 55.5966 90.2059L55.6401 90.2264L58.5923 95.1235C59.2355 96.0332 59.3405 95.3208 60.3989 95.2286C61.0677 95.1696 62.3644 95.039 62.882 94.6059C64.5247 93.2374 63.597 92.6711 62.7462 91.3616" fill="#FF7752"/> +</g> +<path d="M119.851 141.131C119.851 141.131 111.341 138.332 111.213 143.037" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path opacity="0.54" d="M43.6721 189.06L26.9819 146.301C26.7751 145.473 26.7597 144.61 26.9365 143.776C27.1136 142.941 27.4785 142.158 28.0033 141.486C28.5284 140.814 29.1996 140.27 29.9663 139.896C30.7328 139.522 31.5746 139.328 32.4274 139.328H98.7219C100.21 139.328 101.656 139.829 102.825 140.75C103.994 141.672 104.819 142.96 105.167 144.407L123.438 187.156C123.636 187.981 123.644 188.84 123.462 189.668C123.28 190.496 122.912 191.273 122.387 191.938C121.862 192.604 121.193 193.142 120.43 193.513C119.667 193.883 118.83 194.075 117.982 194.076H50.1017C48.6241 194.077 47.1888 193.583 46.0238 192.675C44.8588 191.766 44.0311 190.494 43.6721 189.06Z" fill="white"/> +<path d="M122.622 148.344C122.622 148.344 124.003 149.61 123.478 151.419C123.088 152.759 121.756 153.605 120.436 153.146C116.233 151.693 112.448 149.226 111.526 145.031C111.09 142.907 110.995 140.727 111.246 138.573C111.449 136.577 111.743 134.422 112.415 132.523C113.44 129.606 116.579 127.574 119.626 128.979" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M43.6721 189.06L26.9819 146.301C26.7751 145.473 26.7597 144.61 26.9365 143.776C27.1136 142.941 27.4785 142.158 28.0033 141.486C28.5284 140.814 29.1996 140.27 29.9663 139.896C30.7328 139.522 31.5746 139.328 32.4274 139.328H98.7219C100.21 139.328 101.656 139.829 102.825 140.75C103.994 141.672 104.819 142.96 105.167 144.407L123.438 187.156C123.636 187.981 123.644 188.84 123.462 189.668C123.28 190.496 122.912 191.273 122.387 191.938C121.862 192.604 121.193 193.142 120.43 193.513C119.667 193.883 118.83 194.075 117.982 194.076H50.1017C48.6241 194.077 47.1888 193.583 46.0238 192.675C44.8588 191.766 44.0311 190.494 43.6721 189.06Z" fill="#FF7752"/> +<path d="M43.6721 189.06L26.9819 146.301C26.7751 145.473 26.7597 144.61 26.9365 143.776C27.1136 142.941 27.4785 142.158 28.0033 141.486C28.5284 140.814 29.1996 140.27 29.9663 139.896C30.7328 139.522 31.5746 139.328 32.4274 139.328H98.7219C100.21 139.328 101.656 139.829 102.825 140.75C103.994 141.672 104.819 142.96 105.167 144.407L123.438 187.156C123.636 187.981 123.644 188.84 123.462 189.668C123.28 190.496 122.912 191.273 122.387 191.938C121.862 192.604 121.193 193.142 120.43 193.513C119.667 193.883 118.83 194.075 117.982 194.076H50.1017C48.6241 194.077 47.1888 193.583 46.0238 192.675C44.8588 191.766 44.0311 190.494 43.6721 189.06Z" stroke="black" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/> +<path d="M46.115 187.334L30.7164 147.87C30.526 147.106 30.5121 146.31 30.6756 145.54C30.8391 144.77 31.1758 144.048 31.6604 143.428C32.1448 142.808 32.7641 142.307 33.4712 141.962C34.1784 141.617 34.9549 141.438 35.7416 141.438H96.9006C98.2737 141.437 99.607 141.899 100.686 142.749C101.764 143.599 102.525 144.787 102.846 146.122L119.705 185.563C119.887 186.324 119.894 187.116 119.726 187.881C119.558 188.645 119.219 189.361 118.734 189.975C118.25 190.589 117.632 191.086 116.928 191.427C116.224 191.769 115.452 191.946 114.67 191.946H52.0449C50.6839 191.948 49.3616 191.494 48.2876 190.659C47.2134 189.823 46.4489 188.653 46.115 187.334Z" fill="#FF7752"/> +<path d="M177.006 181.141L188.732 208.276" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M232.634 208.281H2" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M123.563 85.8125C121.661 87.6909 118.822 87.6883 117.205 85.9509" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M72.8867 78.7201C73.9246 77.7489 75.1572 76.7674 76.4564 77.1979C77.8582 77.6643 78.2502 79.4223 78.4091 80.8881C78.8583 84.9165 79.3068 88.9408 79.7545 92.9607C80.3234 98.0859 80.923 103.334 83.0526 108.044C84.0825 110.346 85.5378 112.433 87.3423 114.194" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M99.004 114.164V114.079C98.8607 112.428 98.2349 110.855 97.205 109.556C96.1749 108.257 94.7854 107.289 93.2099 106.773C91.563 109.48 89.5923 111.976 87.3416 114.205C81.4407 120.073 73.8603 123.958 65.6518 125.324C66.0464 126.895 66.8587 128.325 67.4199 129.837C67.9812 131.349 68.2605 133.099 67.5788 134.557C66.7178 136.418 64.2474 136.174 62.5305 135.762C62.5126 135.762 62.5023 135.762 62.4844 135.762" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M53.6455 88.0469C50.7754 88.6645 47.8694 89.1129 44.9531 89.5152C46.4112 103.138 49.1686 116.894 55.8416 128.874C57.2331 131.378 59.0167 134.117 61.6818 135.424C61.9537 135.556 62.2345 135.668 62.5223 135.76C65.1951 136.639 68.4855 135.919 71.0173 135.089C77.1676 133.079 83.4075 130.668 89.0145 127.444C93.8552 124.669 99.6313 120.164 99.0086 114.165C98.9484 113.504 98.8133 112.853 98.6063 112.222" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M101.639 60.8712C102.069 62.6645 103.05 64.2774 104.446 65.4829C105.841 66.6886 107.58 67.4256 109.416 67.5904C109.569 66.8508 109.899 66.1591 110.38 65.5764C112.43 63.1599 113.304 67.6931 119.861 64.4386C128.948 59.9182 132.398 62.5115 132.398 62.5115C132.7 62.7371 132.654 64.8176 132.705 65.1969C132.961 67.3572 133.192 69.5072 133.446 71.6649C133.599 72.9821 133.751 74.2893 133.894 75.5962C134.018 75.3737 134.155 75.1587 134.304 74.9527C135.132 72.0237 136.61 68.5644 139.798 70.0533C140.444 70.368 141.006 70.8305 141.438 71.4035C143.553 74.1814 141.951 76.8644 139.739 78.8504C139.185 79.4667 138.565 80.0208 137.892 80.5036C138.256 81.257 139.742 84.2475 139.742 84.2578C139.896 84.5602 145.09 78.6813 145.477 78.0406C146.055 76.4713 146.365 74.8159 146.394 73.1438C146.479 66.2683 143.681 58.2651 138.112 54.3212C136.731 53.3449 137.172 52.4044 137.087 51.077C137.081 51.0052 137.054 50.9371 137.008 50.8812C136.963 50.8253 136.901 50.7846 136.832 50.7643C136.763 50.7441 136.69 50.7454 136.621 50.7677C136.553 50.7902 136.493 50.8328 136.449 50.8902C134.704 52.8992 130.383 52.6994 130.383 52.6994C115.387 48.9426 110.346 55.6105 107.32 56.7995C106.085 57.2672 104.726 57.2897 103.476 56.8636C103.403 57.2772 103.217 57.6628 102.94 57.9791C102.663 58.2953 102.306 58.5298 101.905 58.6574L101.982 59.3516C102.047 59.8828 101.926 60.42 101.639 60.8712Z" fill="#FF7752"/> +<path d="M109.427 67.5961C107.591 67.4311 105.852 66.694 104.457 65.4886C103.062 64.2832 102.08 62.67 101.65 60.8769C101.481 60.1891 101.375 59.4875 101.332 58.7807L101.45 58.7679C101.611 58.7559 101.77 58.7213 101.921 58.6654C102.322 58.5378 102.68 58.3033 102.957 57.9871C103.233 57.6711 103.419 57.2852 103.492 56.8716C103.539 56.6422 103.549 56.4067 103.52 56.1746L103.136 52.7817C103.136 52.7817 105.847 54.7985 108.856 53.6197C111.864 52.4409 116.915 45.773 131.919 49.5195C131.919 49.5195 135.307 49.7194 137.044 47.7103C137.088 47.6529 137.148 47.6104 137.217 47.5881C137.285 47.5655 137.359 47.5645 137.428 47.5847C137.497 47.605 137.558 47.6457 137.604 47.7013C137.649 47.7572 137.677 47.8256 137.682 47.8974C137.772 49.2274 138.264 50.1678 139.648 51.1416C145.217 55.088 148.015 63.0885 147.928 69.9639C147.902 72.8161 146.954 75.6324 145.491 78.0489C145.104 78.6896 139.909 84.5682 139.756 84.2658C139.756 84.2658 138.269 81.265 137.905 80.5116L137.818 80.345L137.985 80.2348C138.604 79.8148 139.194 79.3551 139.753 78.8587C141.962 76.8727 143.566 74.1896 141.452 71.4118C141.019 70.8388 140.458 70.376 139.812 70.0613C136.614 68.5724 135.146 72.0319 134.318 74.961C134.157 75.5207 134.058 76.0965 134.023 76.6779C133.985 76.3192 133.946 75.9732 133.908 75.6145C133.764 74.3075 133.613 73.0006 133.459 71.6834C133.203 69.5257 132.962 67.3757 132.719 65.2154C132.68 64.8464 132.719 62.7553 132.411 62.5298C132.411 62.5298 128.959 59.9365 119.875 64.4569C113.317 67.7114 112.443 63.1756 110.393 65.5947C109.913 66.1777 109.582 66.8691 109.43 67.6089C109.383 67.8464 109.353 68.0871 109.34 68.329C109.335 68.3602 109.335 68.3923 109.34 68.4238C109.309 68.9543 109.299 69.4822 109.286 69.9998C109.238 74.9943 109.786 80.2118 111.746 84.8501C114.13 90.4724 120.436 96.0179 126.994 93.3963C129.044 92.5737 130.881 91.2489 132.562 89.842" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M113.915 79.779C114.567 79.5607 114.882 78.744 114.618 77.955C114.354 77.1657 113.611 76.7029 112.958 76.9212C112.306 77.1393 111.992 77.956 112.255 78.7453C112.52 79.5346 113.263 79.9974 113.915 79.779Z" fill="black"/> +<path d="M123.673 79.4665C124.325 79.2485 124.639 78.4318 124.376 77.6425C124.111 76.8532 123.369 76.3904 122.716 76.6087C122.064 76.8271 121.749 77.6438 122.013 78.4328C122.277 79.2221 123.02 79.6849 123.673 79.4665Z" fill="black"/> +<path d="M119.941 81.1566C119.941 81.1566 116.694 82.9991 116.497 81.0874C116.435 80.498 115.641 74.7245 115.641 74.7245C115.641 74.7245 113.017 71.9441 108.996 73.9993" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M119.211 73.1112C119.211 73.1112 124.454 70.4922 127.924 74.2669" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M124.019 94.0391L124.621 97.1296C122.722 98.8798 121.379 101.389 121.771 104.031C122.12 106.373 124.037 108.554 126.002 109.781C129.287 111.831 133.657 111.67 137.193 110.365C140.65 109.084 143.641 106.652 145.809 103.685C146.83 102.25 147.51 100.602 147.797 98.8644V98.8337" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M124.621 97.1328L125.795 102.714" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M141.219 82.9219L142.992 94.697C143.167 95.8497 143.749 96.9014 144.634 97.6607C145.519 98.4202 146.646 98.8372 147.812 98.8356C153.331 99.73 158.776 101.035 164.1 102.741C184.901 109.45 197.703 128.188 201.319 149.124C202.824 157.891 202.936 167.085 201.048 175.803C199.974 180.711 198.421 186.223 194.815 189.898C190.134 194.69 182.948 192.716 177.415 190.551C160.238 183.87 145.952 171.985 133.615 158.378C127.019 151.1 118.496 145.267 118.496 145.267" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M51.7904 178.5C49.8633 178.482 48.1848 176.927 47.7517 174.761C46.8958 170.502 46.6703 163.916 52.9154 162.479C60.7185 160.685 66.7201 169.182 69.662 174.554C70.6511 176.347 69.5313 178.654 67.6888 178.654L51.7904 178.5Z" fill="#FFDFD6"/> +<path d="M54.3678 158.87C56.0243 157.767 56.0499 154.894 54.4244 152.451C52.7992 150.009 50.1387 148.922 48.482 150.025C46.8253 151.127 46.7999 154.001 48.4251 156.443C50.0506 158.886 52.7111 159.972 54.3678 158.87Z" fill="#FFDFD6"/> +<path d="M182.604 197.103C182.483 197.121 182.347 197.139 182.229 197.167H182.183L163.956 197.072L136.605 196.931C136.5 196.931 136.397 196.931 136.295 196.931C136.092 196.926 135.889 196.911 135.688 196.885C137.541 196.67 139.258 195.808 140.538 194.451C141.819 193.094 142.579 191.329 142.686 189.467C142.691 189.37 142.691 189.274 142.686 189.177V189.028C142.679 187.206 142.044 185.442 140.888 184.033C139.732 182.624 138.127 181.656 136.341 181.292C136.154 181.246 135.949 181.218 135.752 181.189C136.062 181.161 136.369 181.141 136.669 181.141L159.776 181.264L164.03 181.282L169.768 181.31C171.732 181.489 173.559 182.395 174.891 183.85C176.223 185.305 176.965 187.204 176.971 189.177C176.971 189.549 176.943 189.921 176.886 190.289C176.613 192.167 175.675 193.885 174.243 195.13C172.81 196.375 170.979 197.064 169.081 197.072H182.965C182.165 197.072 182.388 197.072 182.604 197.103Z" fill="white"/> +<path d="M145.631 206.637C146.152 207.16 146.771 207.575 147.453 207.858C148.136 208.14 148.868 208.284 149.606 208.282H136.183C133.62 208.282 132.221 207.657 131.201 206.637C130.153 205.584 129.563 204.16 129.559 202.675C129.559 199.6 132.08 197.078 136.183 197.078H149.606C148.498 197.076 147.414 197.404 146.492 198.019C145.57 198.633 144.851 199.508 144.427 200.532C144.003 201.556 143.892 202.682 144.109 203.769C144.325 204.856 144.86 205.854 145.644 206.637H145.631Z" fill="#FFDFD6"/> +<path d="M189.962 189.321C189.959 190.504 189.683 191.671 189.157 192.731C188.782 193.533 188.266 194.261 187.635 194.881C186.595 195.913 185.288 196.634 183.86 196.965C183.272 197.101 182.67 197.17 182.067 197.17C182.19 197.142 182.323 197.124 182.441 197.106C182.226 197.085 182.003 197.078 182.805 197.078H168.918C170.817 197.07 172.65 196.381 174.084 195.136C175.517 193.89 176.456 192.171 176.729 190.292C176.785 189.924 176.814 189.552 176.813 189.18C176.807 187.207 176.066 185.307 174.733 183.852C173.4 182.397 171.572 181.491 169.607 181.312L177.103 181.351L182.11 181.379C184.203 181.397 186.203 182.242 187.674 183.73C189.146 185.218 189.968 187.228 189.962 189.321Z" fill="#FF7752"/> +<path d="M189.961 189.313C189.945 191.407 189.101 193.409 187.612 194.881C186.123 196.353 184.111 197.175 182.017 197.167L136.423 196.931C136.118 196.931 135.813 196.908 135.506 196.878C137.428 196.66 139.203 195.745 140.495 194.305C141.787 192.866 142.507 191.003 142.517 189.069C142.527 187.135 141.827 185.264 140.55 183.811C139.273 182.358 137.508 181.425 135.588 181.187C135.893 181.155 136.199 181.139 136.505 181.141L182.099 181.376C184.193 181.391 186.195 182.234 187.669 183.721C189.142 185.209 189.966 187.219 189.961 189.313Z" stroke="black" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/> +<path d="M176.808 189.174C176.804 191.267 175.971 193.274 174.49 194.754C173.01 196.235 171.003 197.068 168.91 197.072H136.47C136.164 197.07 135.857 197.053 135.553 197.021C137.473 196.793 139.243 195.868 140.528 194.422C141.812 192.976 142.522 191.109 142.522 189.175C142.522 187.241 141.812 185.374 140.528 183.928C139.243 182.483 137.473 181.558 135.553 181.33C135.858 181.302 136.165 181.281 136.47 181.281H168.91C171.002 181.286 173.008 182.119 174.488 183.598C175.968 185.077 176.802 187.082 176.808 189.174Z" stroke="black" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/> +<path d="M129.559 202.675C129.558 203.41 129.703 204.137 129.984 204.817C130.266 205.495 130.678 206.112 131.199 206.631C132.224 207.656 133.615 208.279 136.18 208.279H182.802C182 208.279 182.217 208.264 182.435 208.243C181.071 208.083 179.813 207.427 178.901 206.4C177.988 205.374 177.484 204.048 177.484 202.675C177.484 201.301 177.988 199.976 178.901 198.949C179.813 197.923 181.071 197.267 182.435 197.106C182.217 197.086 182 197.07 182.802 197.07H136.18C132.075 197.073 129.559 199.594 129.559 202.675Z" stroke="black" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/> +<path d="M143.986 202.675C143.99 204.16 144.581 205.584 145.632 206.634C146.682 207.684 148.105 208.276 149.591 208.279H181.785C182 208.279 182.218 208.264 182.436 208.243C181.071 208.083 179.814 207.427 178.901 206.4C177.988 205.374 177.484 204.048 177.484 202.675C177.484 201.301 177.988 199.976 178.901 198.949C179.814 197.923 181.071 197.267 182.436 197.106C182.218 197.086 182 197.07 181.785 197.07H149.591C148.106 197.074 146.682 197.666 145.632 198.716C144.582 199.766 143.99 201.19 143.986 202.675Z" stroke="black" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/> +<path d="M91.7988 159.916H77.4175C76.7594 159.914 76.1159 159.722 75.5655 159.361C75.015 159.001 74.5812 158.489 74.3167 157.886C74.2147 157.662 74.1709 157.415 74.1899 157.169C74.2086 156.924 74.2893 156.687 74.4243 156.481C74.5594 156.275 74.7444 156.106 74.9622 155.99C75.18 155.875 75.4232 155.817 75.6698 155.82H90.9582C90.2381 157.033 90.6046 158.716 91.7988 159.916Z" fill="#FFDFD6"/> +<path d="M101.098 158.434C101.097 158.826 100.941 159.202 100.664 159.479C100.387 159.756 100.011 159.912 99.6194 159.913H91.7983C90.6042 158.716 90.2377 157.032 90.9578 155.812H97.864C98.5213 155.813 99.1643 156.004 99.7152 156.362C100.266 156.72 100.701 157.231 100.967 157.832C101.05 158.022 101.094 158.227 101.098 158.434Z" fill="#FF7752"/> +<path d="M97.5001 159.916C97.4596 159.994 97.4125 160.068 97.3592 160.139C96.3341 161.54 94.1072 161.676 92.3775 160.41C92.173 160.259 91.9795 160.094 91.7983 159.916C90.6042 158.719 90.2377 157.035 90.9578 155.815C90.9983 155.737 91.0454 155.663 91.0987 155.592C92.1238 154.18 94.3404 154.055 96.0702 155.311C96.2785 155.466 96.4746 155.638 96.657 155.823C97.8409 157.015 98.2099 158.688 97.5001 159.916Z" fill="white"/> +<path d="M104.715 167.46C104.712 167.852 104.555 168.228 104.276 168.504C103.998 168.781 103.621 168.936 103.229 168.936H88.1375C88.8653 167.711 88.5014 166.04 87.3047 164.836H101.481C102.139 164.836 102.783 165.028 103.334 165.388C103.885 165.748 104.32 166.26 104.584 166.863C104.669 167.051 104.713 167.254 104.715 167.46Z" fill="#FF7752"/> +<path d="M88.1377 168.936H81.0367C80.3794 168.937 79.736 168.746 79.185 168.388C78.634 168.029 78.1992 167.518 77.9334 166.917C77.8496 166.729 77.8053 166.526 77.8027 166.32C77.8025 166.126 77.8404 165.934 77.9145 165.755C77.9885 165.575 78.0972 165.413 78.2343 165.275C78.3714 165.138 78.5344 165.03 78.7135 164.955C78.8929 164.881 79.0848 164.843 79.2788 164.844H87.3049C88.5016 166.04 88.8732 167.711 88.1377 168.936Z" fill="#FFDFD6"/> +<path d="M88.1358 168.936C88.1025 169.016 88.0585 169.092 88.0052 169.161C86.9801 170.571 84.7609 170.699 83.0337 169.441C82.8246 169.285 82.6278 169.114 82.4443 168.928C81.2578 167.731 80.8939 166.061 81.6038 164.828C81.6448 164.749 81.6919 164.674 81.7447 164.603C82.7698 163.201 84.9967 163.065 86.7239 164.333C86.9296 164.483 87.1231 164.648 87.303 164.828C88.4997 166.04 88.8713 167.711 88.1358 168.936Z" fill="white"/> +<path d="M93.164 177.905H84.1283C83.4712 177.904 82.8285 177.713 82.2778 177.355C81.7269 176.996 81.2917 176.486 81.025 175.886C80.9232 175.661 80.8799 175.414 80.8989 175.168C80.9181 174.921 80.9988 174.684 81.1339 174.477C81.2692 174.271 81.4542 174.101 81.672 173.985C81.8901 173.869 82.1335 173.81 82.3806 173.813H92.3235C91.6111 175.04 91.9775 176.708 93.164 177.905Z" fill="#FF7752"/> +<path d="M107.807 176.431C107.805 176.821 107.648 177.194 107.372 177.469C107.095 177.744 106.721 177.898 106.331 177.897H93.1642C91.9777 176.7 91.6113 175.029 92.3237 173.797H104.573C105.231 173.797 105.875 173.989 106.426 174.349C106.977 174.709 107.411 175.221 107.676 175.824C107.761 176.015 107.805 176.222 107.807 176.431Z" fill="#FFDFD6"/> +<path d="M98.8654 177.899C98.8244 177.977 98.7772 178.053 98.7244 178.124C97.6994 179.533 95.4827 179.662 93.753 178.403C93.5452 178.247 93.3491 178.076 93.1662 177.891C91.9797 176.694 91.6132 175.023 92.3256 173.791C92.3648 173.711 92.411 173.636 92.464 173.565C93.489 172.163 95.7159 172.043 97.4457 173.296C97.6512 173.445 97.8449 173.611 98.0248 173.791C99.219 175.003 99.5829 176.684 98.8654 177.899Z" fill="white"/> +<path d="M30.6016 96.2031L30.8271 96.2672L30.7425 96.5952C30.6889 96.4671 30.6418 96.3361 30.6016 96.2031Z" fill="#353642"/> +<path d="M70.7053 116.702C74.0787 115.712 75.851 111.632 74.6642 107.589C73.4775 103.546 69.7809 101.072 66.4078 102.062C63.0344 103.052 61.2621 107.132 62.4488 111.175C63.6356 115.218 67.3321 117.692 70.7053 116.702Z" fill="#FFDFD6"/> +</svg> diff --git a/public/images/trader.svg b/public/images/trader.svg new file mode 100644 index 00000000..d1afcd3c --- /dev/null +++ b/public/images/trader.svg @@ -0,0 +1,68 @@ +<svg width="234" height="210" viewBox="0 0 234 210" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M138.127 179.469V179.556C138.127 182.209 139.181 184.754 141.057 186.63C142.933 188.506 145.478 189.56 148.131 189.56C150.785 189.56 153.329 188.506 155.205 186.63C157.082 184.754 158.136 182.209 158.136 179.556" stroke="#ECECEC" stroke-width="2" stroke-miterlimit="10"/> +<path d="M198.832 86.8797C207.15 85.1013 216.107 88.3122 222.198 94.2472C228.289 100.182 231.679 108.552 232.553 117.011C233.96 130.472 230.188 146.022 222.767 157.328" stroke="#ECECEC" stroke-width="2" stroke-miterlimit="10"/> +<path d="M124.326 32.7586C138.262 27.7359 154.967 32.1897 168.036 66.8412C172.306 78.1653 177.728 83.4981 185.367 84.1644" stroke="#ECECEC" stroke-width="2" stroke-miterlimit="10"/> +<path d="M71.0684 49.4162C71.0684 49.4162 86.6233 47.6224 86.7182 66.7112L130.262 54.0314L137.099 51.8814C137.099 51.8814 127.289 39.9807 115.973 37.9332C116.095 39.0897 116.324 40.2324 116.657 41.3466C117.241 43.3966 115.304 44.6011 112.616 45.3647C109.928 46.1284 104.485 41.6105 103.549 37.6257C103.549 37.6257 104.892 36.4136 99.8899 37.2823C94.8877 38.151 79.7094 41.6771 71.0684 49.4162Z" fill="#2E2E2E"/> +<path d="M3.53622 142.05C3.96079 142.279 4.43518 142.399 4.91746 142.401C5.35211 142.401 5.78106 142.302 6.1727 142.114C6.56432 141.925 6.90863 141.651 7.18024 141.312L16.995 129.281L24.916 119.34L18.4326 115.704C17.7607 115.325 16.9658 115.228 16.2224 115.434C15.479 115.641 14.8481 116.134 14.4683 116.806L2.38049 138.032C2.1885 138.371 2.06603 138.744 2.02027 139.131C1.97452 139.518 2.0064 139.91 2.11403 140.284C2.22166 140.658 2.40289 141.007 2.6471 141.31C2.89132 141.614 3.19358 141.865 3.53622 142.05Z" fill="#2E2E2E"/> +<path d="M152.936 198.812L161.105 206.849C161.812 207.546 162.764 207.937 163.757 207.938H184.199C185.04 207.943 185.849 207.624 186.46 207.047C187.071 206.47 187.436 205.68 187.48 204.841C187.523 204.002 187.241 203.179 186.692 202.542C186.144 201.906 185.371 201.505 184.535 201.424L173.193 200.25L152.936 198.812Z" fill="#2E2E2E"/> +<path d="M115.297 37.0181V37.5716V37.7125C119.817 38.846 124.073 40.8476 127.828 43.6065C128.435 44.0524 129.03 44.5034 129.622 44.9801C129.637 43.0684 129.473 41.149 128.853 39.3424C128.352 37.7899 127.411 36.4161 126.144 35.3883C125.03 34.499 123.71 33.9276 122.382 33.4125C121.055 32.8974 119.753 32.4464 118.539 31.7519C117.514 31.1897 116.618 30.4189 115.909 29.4892C115.662 29.1527 115.507 28.7565 115.462 28.3411C115.417 27.9257 115.483 27.5057 115.653 27.1239C115.534 27.1159 115.416 27.0926 115.302 27.0547L115.297 37.0181Z" fill="#FF7752"/> +<path d="M115.297 37.0181V37.5716V37.7125C119.817 38.846 124.073 40.8476 127.828 43.6065C128.435 44.0524 129.03 44.5034 129.622 44.9801C129.637 43.0684 129.473 41.149 128.853 39.3424C128.352 37.7899 127.411 36.4161 126.144 35.3883C125.03 34.499 123.71 33.9276 122.382 33.4125C121.055 32.8974 119.753 32.4464 118.539 31.7519C117.514 31.1897 116.618 30.4189 115.909 29.4892C115.662 29.1527 115.507 28.7565 115.462 28.3411C115.417 27.9257 115.483 27.5057 115.653 27.1239C115.534 27.1159 115.416 27.0926 115.302 27.0547L115.297 37.0181Z" fill="#FF7752"/> +<path d="M52.8578 80.195C54.5545 81.4125 56.4426 82.3384 58.4443 82.9344C59.3027 83.1907 57.2732 84.1491 58.1547 84.3208C54.667 76.0897 57.1553 63.4356 68.1898 51.8885C70.0682 49.9205 74.3888 47.4271 76.5183 45.6947C79.9445 42.9092 83.6936 42.5479 87.2812 40.877C88.8846 40.1367 90.5311 39.4932 92.2116 38.95C95.1991 37.9572 98.2827 37.2804 101.411 36.9306C102.121 36.8435 102.823 36.782 103.533 36.7436C103.36 35.5235 103.395 34.2827 103.636 33.0739C104.289 29.7579 105.43 27.8539 107.505 25.2401C108.807 23.5949 110.837 19.6613 108.128 18.3057C106.421 17.4549 106.265 15.7149 107.067 14.654C107.746 13.7468 109.117 13.3342 110.814 14.3977C111.985 15.146 113.376 15.1947 114.714 14.8282C115.757 14.2209 116.661 13.4021 117.369 12.4245C119.334 9.60565 119.403 5.33892 116.331 3.2709C114.652 2.1408 112.515 1.8666 110.509 2.08442C109.612 2.1882 108.729 2.38655 107.874 2.67638C107.871 2.67484 107.867 2.67407 107.863 2.67407C107.859 2.67407 107.855 2.67484 107.851 2.67638C104.223 4.63421 101.624 8.25516 100.376 12.3348C99.6432 14.7462 98.8155 17.1346 97.8468 19.4742C97.0524 21.3936 96.4989 23.7281 94.7255 25.0376C93.9862 25.5868 93.146 25.9848 92.2526 26.2087C88.9059 28.3306 84.2625 27.1056 80.6031 26.6546C77.6768 26.2546 74.6976 26.556 71.9107 27.5336C68.7895 29.1096 66.1064 31.6337 63.9282 34.9062C60.3406 40.3286 64.1384 48.1471 58.1239 52.4241C56.3122 53.7054 54.2134 54.4921 52.23 55.4992C49.3906 56.9445 47.1048 58.9946 46.2617 62.162C44.578 68.507 47.5583 76.3486 52.8578 80.195Z" fill="#FF7752"/> +<path d="M127.054 126.609L173.322 200.179L153.589 199.246L146.23 192.532H137.817C137.817 192.532 114.887 158.906 109.628 153.207C104.37 147.507 109.628 133.062 109.628 133.062L127.054 126.609Z" fill="white"/> +<path d="M199.948 207.938H57.6191" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M120.516 9.85048C120.881 10.608 121.194 11.3896 121.451 12.1901C121.825 13.4945 121.42 14.2992 121.989 15.5394C122.184 15.9674 122.466 16.6926 122.758 17.4281C122.842 17.2564 122.922 17.1154 122.968 17.0053C125.2 12.0364 122.266 9.64034 120.854 8.82031C120.767 9.17164 120.653 9.51606 120.516 9.85048Z" fill="#FF7752"/> +<path d="M120.516 9.85048C120.881 10.608 121.194 11.3896 121.451 12.1901C121.825 13.4945 121.42 14.2992 121.989 15.5394C122.184 15.9674 122.466 16.6926 122.758 17.4281C122.842 17.2564 122.922 17.1154 122.968 17.0053C125.2 12.0364 122.266 9.64034 120.854 8.82031C120.767 9.17164 120.653 9.51606 120.516 9.85048Z" fill="#FF7752"/> +<path d="M99.4163 142.961C97.0306 151.348 93.1815 159.853 89.9911 165.23C87.6845 169.109 83.9956 171.972 79.6654 173.244C75.3351 174.516 70.684 174.103 66.6458 172.087L31.6279 154.71V146.845L16.9955 129.289L7.18073 141.31C6.90812 141.648 6.56335 141.92 6.17178 142.106C5.78019 142.293 5.35175 142.39 4.91795 142.389C4.40889 142.386 3.90936 142.251 3.46872 141.996C3.02809 141.741 2.66158 141.375 2.4054 140.935C2.14922 140.496 2.0122 139.996 2.00792 139.487C2.00364 138.978 2.13223 138.477 2.38098 138.033L14.4688 116.817C14.8488 116.146 15.4797 115.652 16.2228 115.446C16.9662 115.239 17.7612 115.336 18.4331 115.715L24.9165 119.351L62.0741 140.162C62.6922 140.508 63.3834 140.702 64.0912 140.729C64.7987 140.755 65.5026 140.613 66.1446 140.314C66.7868 140.016 67.3487 139.569 67.7841 139.01C68.2198 138.452 68.5163 137.798 68.6498 137.102L73.0523 114.039" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M83.2988 129.625L92.8189 137.369C102.839 145.274 111.559 154.699 118.663 165.302L137.682 192.611H168.382" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M146.64 192.615L152.936 198.819L161.106 206.855C161.812 207.552 162.765 207.944 163.758 207.944H184.2C185.04 207.949 185.85 207.63 186.461 207.054C187.072 206.477 187.437 205.687 187.48 204.847C187.523 204.008 187.241 203.185 186.693 202.548C186.145 201.912 185.372 201.511 184.536 201.43L173.193 200.256L118.441 113.192C115.622 108.697 113.111 101.299 115.297 96.453C123.623 78.0203 122.902 60.1641 122.902 60.1641" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M115.296 25.4852V36.9401V37.4936V37.6345C116.472 39.3515 117.067 41.7859 115.942 43.6105C114.438 46.0527 111.114 45.6478 109.005 44.2819C108.385 43.8798 107.794 43.4362 107.234 42.9545C105.36 41.3254 104.148 39.0665 103.826 36.6044C103.653 35.3851 103.687 34.1453 103.926 32.9373C104.584 29.6238 105.72 27.7198 107.803 25.1009C109.105 23.4582 111.134 19.5221 108.421 18.1665C105.246 16.5828 107.444 11.947 111.106 14.2636C113.946 16.0574 118.025 13.8203 119.711 11.4089C121.677 8.59003 121.749 4.3233 118.674 2.25528C116.995 1.12517 114.858 0.850973 112.854 1.06879C106.742 1.7402 102.396 6.51688 100.656 12.1956C99.9231 14.607 99.0954 16.9953 98.1293 19.335C97.3323 21.257 96.7788 23.5889 95.008 24.8984C91.6382 27.42 86.7539 26.1003 82.9382 25.6339C74.9941 24.6575 68.5876 28.176 64.2107 34.7618C60.6231 40.1843 64.4208 48.0028 58.4039 52.2798C56.5947 53.5611 54.4959 54.3478 52.5099 55.3549C49.668 56.7976 47.3847 58.8503 46.539 62.0177C44.8554 68.3832 47.8357 76.2273 53.1326 80.0737C55.5594 81.8368 58.3987 82.8926 61.3483 83.4666" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M130.977 46.4515C130.992 46.3054 131 46.1645 131.005 46.0261C131.138 43.5122 131.123 40.9009 130.303 38.4946C129.801 36.9435 128.86 35.5709 127.594 34.5431C126.474 33.6539 125.157 33.0773 123.835 32.5647C122.512 32.0522 121.203 31.6012 119.991 30.9042C118.964 30.3458 118.068 29.5744 117.364 28.6414C117.115 28.3067 116.959 27.9121 116.912 27.4977C116.864 27.0831 116.928 26.6636 117.095 26.2812" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M31.6309 146.849V123.109" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M112.834 22.9199C113.754 24.2345 115.002 25.6542 116.575 26.1693C118.613 26.8304 119.984 25.1032 120.714 23.4196C121.152 22.4176 121.452 21.3516 121.806 20.3163C121.921 19.9755 121.99 19.8038 122.382 19.7833C122.774 19.7628 123.182 19.8448 123.582 19.8448C123.612 19.8463 123.642 19.8391 123.668 19.824C123.695 19.8089 123.716 19.7866 123.73 19.7594C123.745 19.7325 123.75 19.702 123.748 19.6718C123.745 19.6415 123.733 19.6126 123.715 19.5885C123.177 18.771 122.375 16.5364 121.967 15.6421C121.403 14.4018 121.811 13.592 121.432 12.2953C121.173 11.4953 120.862 10.7129 120.501 9.95312" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M121.102 7.5625C121.102 7.5625 126.655 9.68946 123.787 16.0908C123.61 16.4803 123.175 17.2876 122.97 17.654" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M120.793 23.2229C120.349 23.2832 119.898 23.1796 119.524 22.9316C119.151 22.6835 118.88 22.3078 118.764 21.875" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M121.733 14.6537C120.582 13.7184 119.322 14.91 118.553 15.7838" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M115.297 37.6328C119.819 38.7665 124.077 40.7684 127.836 43.5268C128.927 44.3161 129.981 45.1566 131.003 46.0304C135.037 49.4541 138.586 53.4159 142.035 57.4212" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M147.209 95.2056C147.188 95.1931 147.169 95.1775 147.152 95.1595C142.627 90.634 138.068 86.1417 133.529 81.6315C129.727 77.8543 124.781 73.9873 122.459 69.0312" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M94.1445 127.788C94.5135 127.671 94.8646 127.553 95.1978 127.43C95.2037 127.432 95.2098 127.432 95.2157 127.43" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M59.6966 74.5748L86.7192 66.7076L136.344 52.2571C138.728 51.5706 141.287 51.8561 143.461 53.0507C145.635 54.2454 147.248 56.2522 147.947 58.6329L158.605 95.2447C159.292 97.6284 159.008 100.187 157.815 102.362C156.622 104.537 154.617 106.151 152.237 106.853L99.3503 122.249L94.302 123.715L89.1768 125.214L84.1284 126.688L80.0283 127.874L65.4727 132.113C63.0887 132.8 60.5292 132.516 58.3546 131.321C56.1797 130.126 54.5671 128.118 53.8693 125.737L43.2114 89.1252C42.5241 86.7428 42.8078 84.185 44.0004 82.0112C45.1933 79.8373 47.1983 78.2242 49.5769 77.5243L49.9587 77.4141L50.2791 77.3193L57.0751 75.3384L59.6966 74.5748Z" fill="white"/> +<path d="M63.7568 75.5365L88.7934 68.2484L134.777 54.8588C136.987 54.2187 139.361 54.4808 141.379 55.5881C143.396 56.6952 144.893 58.5572 145.54 60.7656L155.411 94.6791C156.047 96.8878 155.784 99.2582 154.68 101.274C153.576 103.29 151.72 104.788 149.517 105.442L100.507 119.711L95.8303 121.074L91.0716 122.46L86.3948 123.829L82.5919 124.918L67.0293 129.53C64.8188 130.171 62.4443 129.909 60.4267 128.802C58.409 127.695 56.9129 125.833 56.2664 123.624L46.4004 89.6922C45.7602 87.4835 46.0211 85.1111 47.1256 83.0941C48.2303 81.077 50.0887 79.5797 52.2943 78.9293L52.648 78.8268L52.9452 78.7423L61.3275 76.2309L63.7568 75.5365Z" fill="#FF7752"/> +<path d="M66.4686 76.7393L89.6832 69.9817L134.016 57.0021C136.064 56.4127 138.262 56.6577 140.13 57.6838C141.998 58.7099 143.384 60.4337 143.985 62.4784L153.143 93.9343C153.733 95.9824 153.489 98.1806 152.464 100.049C151.439 101.918 149.716 103.305 147.672 103.908L100.536 117.708L96.1998 118.968L91.7896 120.25L87.4511 121.516L83.9378 122.541L68.0984 127.205C66.0488 127.8 63.8473 127.557 61.9763 126.53C60.1056 125.504 58.7185 123.777 58.1196 121.728L48.9583 90.2749C48.3679 88.2277 48.6119 86.0297 49.6369 84.1619C50.6622 82.294 52.3853 80.9079 54.4295 80.3064L56.1669 79.8118L56.4437 79.7298L64.216 77.403L66.4686 76.7393Z" fill="#FF7752"/> +<path d="M100.975 95.4556L59.2534 107.231C59.031 107.294 58.7983 107.312 58.5687 107.285C58.3393 107.258 58.1174 107.185 57.9157 107.072C57.714 106.959 57.5367 106.808 57.3937 106.626C57.251 106.444 57.1451 106.236 57.0829 106.014C57.0373 105.86 57.0139 105.7 57.0137 105.539C57.0155 105.157 57.141 104.785 57.3722 104.479C57.6031 104.174 57.927 103.951 58.295 103.846L100.017 92.0781C99.8485 92.6527 99.8406 93.2623 99.9936 93.8412C100.159 94.4624 100.5 95.0225 100.975 95.4556Z" fill="#FF7752"/> +<path d="M110.72 91.3653C110.719 91.7494 110.594 92.1228 110.363 92.4298C110.132 92.7365 109.807 92.9602 109.438 93.0668L106.427 93.9176C106.599 93.3203 106.601 92.687 106.433 92.0887C106.265 91.4903 105.934 90.9506 105.477 90.5298L108.488 89.6816C108.937 89.5568 109.417 89.6147 109.823 89.8428C110.229 90.0709 110.528 90.4507 110.656 90.8989C110.701 91.0501 110.722 91.2074 110.72 91.3653Z" fill="#FFDFD6"/> +<path d="M112.616 98.0858C112.615 98.4689 112.489 98.841 112.258 99.1464C112.027 99.4519 111.703 99.6741 111.335 99.7796L69.872 111.475C70.1206 110.901 70.2013 110.267 70.1047 109.649C70.0081 109.03 69.738 108.451 69.3262 107.98L110.376 96.3919C110.825 96.2679 111.305 96.3263 111.711 96.5541C112.117 96.7822 112.417 97.1614 112.544 97.6091C112.592 97.7634 112.616 97.9241 112.616 98.0858Z" fill="#FFDFD6"/> +<path d="M64.8854 112.869L61.1491 113.922C60.9213 114.012 60.6773 114.053 60.4326 114.043C60.1881 114.033 59.9483 113.972 59.7287 113.864C59.5091 113.755 59.3146 113.602 59.1577 113.414C59.0009 113.226 58.8851 113.007 58.8177 112.772C58.7503 112.537 58.7331 112.29 58.7667 112.047C58.8005 111.805 58.8846 111.572 59.0137 111.364C59.1429 111.156 59.314 110.977 59.5162 110.839C59.7184 110.701 59.9473 110.607 60.1882 110.563L63.5195 109.625C63.4478 110.085 63.4696 110.555 63.5836 111.006C63.7832 111.762 64.2445 112.422 64.8854 112.869Z" fill="#FF7752"/> +<path d="M102.027 109.737L63.3162 120.661C62.8675 120.783 62.3886 120.724 61.9831 120.496C61.5777 120.269 61.2779 119.891 61.1483 119.444C61.1032 119.292 61.0816 119.134 61.0842 118.975C61.085 118.591 61.2103 118.218 61.4414 117.911C61.6723 117.604 61.9967 117.381 62.3655 117.274L101.02 106.375C100.858 106.974 100.867 107.606 101.045 108.2C101.222 108.794 101.563 109.326 102.027 109.737Z" fill="#FF7752"/> +<path d="M114.792 104.805C114.792 105.188 114.667 105.561 114.436 105.867C114.204 106.173 113.879 106.394 113.51 106.499L107.44 108.208L102.032 109.745C101.568 109.334 101.228 108.802 101.05 108.208C100.872 107.614 100.864 106.982 101.025 106.383L106.535 104.83L112.557 103.129C113.005 103.002 113.485 103.058 113.892 103.285C114.299 103.512 114.599 103.891 114.728 104.338C114.77 104.49 114.792 104.647 114.792 104.805Z" fill="#FFDFD6"/> +<path d="M106.428 93.9151C106.227 94.6183 105.799 95.2359 105.213 95.6723C104.625 96.1087 103.911 96.3398 103.18 96.3301C102.448 96.3201 101.74 96.0697 101.165 95.6174C100.59 95.1654 100.18 94.5363 99.9981 93.828C99.8877 93.4041 99.8621 92.9629 99.9225 92.5293C99.9828 92.0957 100.128 91.6782 100.35 91.3007C100.572 90.9233 100.866 90.5932 101.215 90.3295C101.565 90.0656 101.963 89.8734 102.386 89.7637C102.925 89.6235 103.49 89.6205 104.03 89.7547C104.569 89.889 105.067 90.1563 105.477 90.5325C105.934 90.9533 106.265 91.4929 106.433 92.0913C106.601 92.6897 106.599 93.3229 106.428 93.9202V93.9151Z" fill="white"/> +<g opacity="0.72"> +<path opacity="0.72" d="M104.64 92.3175C104.681 92.7211 104.606 93.1283 104.425 93.4912C104.337 93.6706 104.235 93.8422 104.117 94.0037C104.053 94.0883 103.717 94.5726 103.62 94.5521C102.959 94.4342 102.013 93.9191 101.708 93.2298C101.663 93.1214 101.631 93.0079 101.614 92.8915C101.59 92.7227 101.618 92.5507 101.694 92.3982C101.77 92.2458 101.89 92.1194 102.039 92.0364C102.188 91.9534 102.358 91.9172 102.528 91.9326C102.697 91.948 102.858 92.0141 102.99 92.1227C103.061 91.9493 103.186 91.8034 103.347 91.7076C103.508 91.6115 103.696 91.5708 103.882 91.591C104.069 91.6112 104.244 91.6915 104.38 91.8198C104.517 91.9482 104.608 92.1174 104.64 92.3021V92.3175Z" fill="#FF7752"/> +</g> +<path d="M69.8725 111.472C69.5991 112.111 69.1325 112.649 68.538 113.01C67.9437 113.371 67.251 113.536 66.5576 113.484C65.8644 113.431 65.2048 113.163 64.6715 112.717C64.1382 112.27 63.7579 111.668 63.5839 110.995C63.4699 110.544 63.4481 110.074 63.5199 109.614C63.6275 108.98 63.916 108.39 64.3507 107.915C64.785 107.441 65.3473 107.101 65.9697 106.938C66.5709 106.779 67.2051 106.793 67.7989 106.978C68.3929 107.162 68.9223 107.511 69.3267 107.984C69.7388 108.455 70.0086 109.034 70.1052 109.653C70.2018 110.271 70.1211 110.905 69.8725 111.479V111.472Z" fill="white"/> +<g opacity="0.72"> +<path opacity="0.72" d="M68.2279 109.483C68.2682 109.887 68.1928 110.294 68.0101 110.657C67.925 110.838 67.822 111.009 67.7026 111.169C67.6385 111.254 67.3028 111.738 67.208 111.718C66.5443 111.6 65.6013 111.085 65.2963 110.393C65.2494 110.285 65.2174 110.171 65.2015 110.055C65.1779 109.886 65.2058 109.714 65.2817 109.561C65.3576 109.409 65.4777 109.283 65.6266 109.2C65.7753 109.116 65.9457 109.08 66.1156 109.096C66.2852 109.111 66.4461 109.177 66.5776 109.286C66.6281 109.162 66.7068 109.051 66.8072 108.962C66.9077 108.873 67.0273 108.808 67.1568 108.773C67.2739 108.746 67.3956 108.743 67.514 108.764C67.6326 108.785 67.7456 108.83 67.8458 108.897C67.9463 108.963 68.0319 109.05 68.0977 109.15C68.1633 109.251 68.2077 109.365 68.2279 109.483Z" fill="#FF7752"/> +</g> +<path d="M107.435 108.206C107.202 108.973 106.699 109.63 106.019 110.054C105.339 110.479 104.529 110.644 103.737 110.518C102.945 110.391 102.226 109.982 101.712 109.367C101.199 108.752 100.925 107.971 100.942 107.169C100.959 106.368 101.266 105.6 101.806 105.007C102.345 104.414 103.081 104.036 103.878 103.944C104.674 103.852 105.477 104.051 106.138 104.504C106.799 104.958 107.273 105.636 107.473 106.412C107.625 107.002 107.612 107.623 107.435 108.206Z" fill="white"/> +<g opacity="0.72"> +<path opacity="0.72" d="M105.66 106.575C105.699 106.979 105.624 107.385 105.442 107.749C105.356 107.929 105.253 108.1 105.135 108.261C105.071 108.346 104.735 108.83 104.64 108.81C103.977 108.692 103.031 108.177 102.726 107.487C102.68 107.378 102.648 107.263 102.631 107.146C102.608 106.978 102.636 106.806 102.712 106.653C102.787 106.501 102.908 106.375 103.056 106.291C103.205 106.208 103.375 106.172 103.545 106.187C103.715 106.203 103.876 106.269 104.007 106.378C104.079 106.205 104.205 106.06 104.367 105.964C104.528 105.869 104.716 105.828 104.902 105.849C105.088 105.869 105.263 105.95 105.4 106.078C105.536 106.206 105.628 106.375 105.66 106.56V106.575Z" fill="#FF7752"/> +</g> +<path d="M100.559 117.17C98.337 117.293 95.9461 114.095 95.2158 113.298C95.1348 113.213 95.0426 113.14 94.9416 113.08L92.9556 111.473L93.2887 114C93.8192 115.217 96.6201 117.872 98.6164 120.919C100.613 123.966 98.7112 124.632 97.6426 125.019C96.574 125.406 95.4567 123.387 95.8923 125.58C96.328 127.774 93.7397 127.805 92.1253 127.531C90.5109 127.256 89.9317 125.481 90.2982 126.875C90.6646 128.269 89.4551 129.286 88.3634 129.332C87.5357 129.373 85.9443 128.256 85.2268 127.718C85.0628 128.599 84.4708 130.255 82.4079 129.373C79.6685 128.202 77.0085 121.204 74.9764 114.041C72.9442 106.879 59.7827 93.8145 57.8633 79.9816C57.8633 79.9585 57.8633 79.9252 57.8633 79.9021C57.7608 78.8566 57.7088 77.8034 57.707 76.7425C57.7075 74.726 57.9033 72.7143 58.2913 70.7357C58.5688 69.2961 58.9394 67.8759 59.4009 66.4844L81.1317 70.7998C80.9831 70.9638 80.8345 71.1278 80.6859 71.2995C75.5991 77.2063 78.5717 84.6968 82.4412 90.1961C86.3671 95.7928 90.885 101.323 96.0486 105.828C97.5647 107.147 98.9815 108.577 100.287 110.105L100.005 110.261C100.312 110.859 100.654 111.437 101.03 111.994C102.565 114.11 102.791 117.044 100.559 117.17Z" fill="white"/> +<path d="M86.7188 66.7076L136.343 52.2571C138.727 51.5706 141.286 51.8561 143.461 53.0507C145.635 54.2454 147.248 56.2522 147.947 58.6329L158.605 95.2447C159.292 97.6284 159.007 100.187 157.814 102.362C156.621 104.537 154.616 106.151 152.237 106.853L99.3498 122.249" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M80.0155 127.88L65.4727 132.118C63.0887 132.806 60.5292 132.521 58.3546 131.326C56.1797 130.131 54.5671 128.124 53.8693 125.742L43.2114 89.1305C42.5241 86.7481 42.8078 84.1904 44.0004 82.0168C45.1933 79.8429 47.1983 78.2295 49.5769 77.5296L49.9587 77.4195L50.2791 77.3246L57.0751 75.3438" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M94.3005 123.719H94.293" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M84.111 126.688H84.1035" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M86.8779 112.651C87.5929 116.551 90.8423 120.777 94.289 123.703C95.291 124.554 97.5051 125.59 98.7069 124.618C101.3 122.527 96.867 119.083 95.6446 117.699C94.1609 116.018 92.9616 114.048 92.5157 111.826C92.4767 111.689 92.4849 111.543 92.5387 111.411C92.5908 111.339 92.6643 111.286 92.7489 111.26C92.8334 111.233 92.9242 111.235 93.0077 111.265C93.173 111.326 93.3221 111.424 93.4433 111.552C94.8476 112.833 96.1007 114.25 97.3718 115.652C98.2713 116.654 100.611 117.897 101.759 116.62C104.25 113.85 97.2001 107.395 95.396 105.811C90.2375 101.301 85.712 95.7711 81.7861 90.1795C77.9165 84.6802 74.9439 77.1897 80.0307 71.2829C82.6855 68.2078 86.6166 66.865 90.1273 65.0481C95.7907 62.1268 96.9567 57.2578 96.9567 57.2578" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M83.5859 116.617C83.5859 116.617 86.5175 124.974 92.7447 127.575C93.1144 127.739 93.5165 127.819 93.9212 127.807C94.3255 127.794 94.7222 127.692 95.0818 127.506C95.8505 127.075 96.4784 126.224 95.3995 124.518" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M79.5273 118.57C80.6703 121.671 82.1873 124.913 84.6269 127.219C85.7391 128.267 87.1485 129.305 88.7271 129.372C90.6285 129.451 90.7772 127.534 90.0084 126.23C89.8421 125.95 89.6424 125.692 89.4138 125.461" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M84.6274 127.222C84.6274 127.222 84.489 130.554 81.7496 129.378C79.0101 128.201 76.3425 121.205 74.318 114.04C72.2936 106.875 59.0577 93.7728 57.1793 79.9066L57.1999 80.0757C57.1999 80.045 57.1999 80.0219 57.1999 79.9912C56.8798 76.903 57.0212 73.7841 57.6201 70.7376C63.5525 40.7142 103.55 36.7422 103.55 36.7422" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M162.81 100.143C161.703 103.41 158.966 105.493 155.593 106.001C154.148 106.213 152.59 106.408 151.237 105.745C150.245 105.26 149.269 104.13 149.566 102.944C149.125 102.949 148.685 102.899 148.257 102.795C147.67 102.654 147.063 102.375 146.804 101.834C146.545 101.293 146.727 100.54 147.147 100.056C147.605 99.604 148.169 99.2745 148.787 99.0972C148.937 99.0418 149.09 98.9947 149.246 98.9562C148.745 98.9606 148.245 98.9288 147.749 98.8614C146.796 98.7384 145.727 98.3309 145.443 97.4186C145.364 97.1086 145.365 96.7836 145.446 96.4741C145.526 96.1643 145.684 95.8801 145.904 95.6479C146.353 95.1871 146.915 94.853 147.534 94.6792C147.598 94.6639 147.652 94.6408 147.713 94.6254C146.583 94.61 145.428 94.3845 144.928 93.3134C144.767 92.9328 144.722 92.5136 144.797 92.1071C144.872 91.701 145.065 91.3256 145.351 91.0275C146.35 89.9051 147.847 89.7462 149.259 89.6386C150.671 89.531 152.388 89.4823 154.051 89.4515C154.412 89.442 154.766 89.3498 155.087 89.1825C155.407 89.0149 155.684 88.7763 155.898 88.4849C156.112 88.1938 156.257 87.8573 156.32 87.5016C156.385 87.146 156.366 86.7805 156.267 86.4328C155.619 84.1264 155.345 81.61 156.711 80.9949C158.748 80.0724 160.152 82.5863 160.737 84.1316C162.523 88.8673 164.511 95.1405 162.81 100.143Z" fill="white"/> +<path d="M154.218 98.2422C152.57 98.5933 150.902 98.952 149.231 98.9674C149.076 99.0061 148.923 99.0533 148.772 99.1083C148.154 99.2854 147.59 99.615 147.132 100.067C146.712 100.551 146.51 101.269 146.789 101.845C147.068 102.422 147.655 102.665 148.242 102.806C148.67 102.91 149.11 102.96 149.551 102.955C150.453 102.936 151.351 102.824 152.229 102.619C152.378 102.588 154.197 102.378 154.338 102.345" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M147.696 94.6406C147.635 94.656 147.581 94.6791 147.517 94.6944C146.898 94.8679 146.335 95.2021 145.887 95.6631C145.667 95.895 145.509 96.1792 145.428 96.489C145.348 96.7986 145.347 97.1238 145.426 97.4339C145.723 98.3462 146.792 98.7536 147.732 98.8766C148.228 98.9437 148.728 98.9755 149.229 98.9714" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<path d="M155.602 94.4691C155.476 94.4691 155.358 94.4691 155.235 94.4691L149.28 94.6023C148.813 94.6023 148.255 94.6485 147.696 94.6331C146.566 94.6177 145.41 94.3922 144.91 93.321C144.75 92.9405 144.704 92.521 144.779 92.1148C144.854 91.7087 145.047 91.3332 145.333 91.0352C146.333 89.9128 147.829 89.7539 149.241 89.6463C150.653 89.5386 152.373 89.4951 154.036 89.4592C154.398 89.451 154.754 89.3595 155.076 89.1922C155.397 89.0251 155.676 88.786 155.891 88.4941C156.106 88.202 156.251 87.8645 156.315 87.5075C156.379 87.1506 156.36 86.7839 156.26 86.4353C155.612 84.129 155.332 81.61 156.701 80.9949C158.738 80.0724 160.142 82.5863 160.727 84.1316C162.521 88.8672 164.512 95.1507 162.81 100.143C161.701 103.41 158.966 105.499 155.596 106.001C154.154 106.216 152.59 106.411 151.24 105.745C150.253 105.263 149.274 104.135 149.569 102.949" stroke="black" stroke-width="2" stroke-miterlimit="10"/> +<g opacity="0.54"> +<path opacity="0.54" d="M108.404 81.0416C108.359 80.7456 108.292 80.4535 108.202 80.1678L107.195 76.9107C106.887 75.9139 106.546 74.8606 105.762 74.1739C104.309 72.8926 102.069 73.3718 100.211 73.9304L95.6526 75.2988C94.6045 75.614 93.541 75.9369 92.6262 76.534C91.7113 77.1311 90.9477 78.0588 90.8042 79.1427C90.6965 79.9423 90.9323 80.7418 91.1655 81.5157L92.1034 84.6421C92.4135 85.6671 92.7569 86.7485 93.5641 87.4609C95.0017 88.7115 97.1979 88.2425 99.0276 87.7018L103.691 86.3257C106.505 85.4852 108.858 83.9809 108.404 81.0416Z" fill="white"/> +<path opacity="0.54" d="M102.008 81.4256C102.196 81.2278 102.328 80.9831 102.391 80.7173C102.453 80.4513 102.443 80.1736 102.363 79.9124C102.282 79.6516 102.133 79.4168 101.932 79.2323C101.73 79.0478 101.483 78.9204 101.216 78.863L99.7709 78.535L98.3282 78.207C98.0627 78.1429 97.7849 78.1506 97.5235 78.2296C97.2619 78.3085 97.0261 78.4556 96.8403 78.6557C96.6545 78.8559 96.5254 79.1019 96.4662 79.3684C96.407 79.6351 96.4201 79.9127 96.5036 80.1725L96.9418 81.5871L97.3774 83.0016C97.4546 83.264 97.6001 83.5011 97.7992 83.6884C97.9986 83.8757 98.2441 84.0064 98.5106 84.0674C98.7771 84.1282 99.0552 84.1169 99.3158 84.0346C99.5767 83.9521 99.8106 83.8017 99.9939 83.5987L101.001 82.5122L102.008 81.4256Z" fill="#FF7752"/> +</g> +<path d="M190.522 111.004C198.205 111.004 204.434 104.776 204.434 97.092C204.434 89.4086 198.205 83.1797 190.522 83.1797C182.838 83.1797 176.609 89.4086 176.609 97.092C176.609 104.776 182.838 111.004 190.522 111.004Z" fill="#FF7752"/> +<path d="M188.269 103.329C188.108 103.329 187.947 103.297 187.798 103.235C187.648 103.173 187.512 103.082 187.398 102.967L183.811 99.3798C183.579 99.1489 183.449 98.8358 183.449 98.5093C183.449 98.1828 183.579 97.8694 183.809 97.6385C184.04 97.4074 184.353 97.2775 184.68 97.2772C185.006 97.277 185.319 97.4064 185.551 97.6373L188.277 100.364L195.501 93.1348C195.732 92.9036 196.045 92.7737 196.372 92.7734C196.698 92.7732 197.011 92.9026 197.242 93.1335C197.474 93.3641 197.603 93.6773 197.604 94.0037C197.604 94.3302 197.475 94.6436 197.244 94.8748L189.138 102.98C188.906 103.207 188.594 103.332 188.269 103.329Z" fill="white"/> +</svg> From 77da69cb5b2957a1732977dd9f021efbf47b2dbb Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 13:31:03 +0900 Subject: [PATCH 382/648] =?UTF-8?q?design:=20border-radius=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#141)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/user-type-card/styles.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/(landing)/signup/_ui/user-type-card/styles.module.scss b/app/(landing)/signup/_ui/user-type-card/styles.module.scss index d4bbf0a1..7a392e7f 100644 --- a/app/(landing)/signup/_ui/user-type-card/styles.module.scss +++ b/app/(landing)/signup/_ui/user-type-card/styles.module.scss @@ -7,6 +7,7 @@ text-align: left; background-color: $color-white; border: 2px solid $color-white; + border-radius: 8px; transition: 0.2s ease-in-out; &:hover { From be9553c0a5ce3a4c753e86e50ffb53ebc971a7a2 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 13:38:42 +0900 Subject: [PATCH 383/648] =?UTF-8?q?feat:=20use=20client=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../questions/[questionId]/_ui/question-detail-card/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx index 6f56a913..fc93d249 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx @@ -1,3 +1,5 @@ +'use client' + import classNames from 'classnames/bind' import { QuestionStatusType } from '@/shared/types/questions' From a024de175e75fc119fa2d3a10e61b9a448aa663b Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 28 Nov 2024 14:25:29 +0900 Subject: [PATCH 384/648] =?UTF-8?q?rename:=20=EB=AC=B4=ED=95=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EA=B4=80=EB=A0=A8=20=ED=9B=85=20shared=20?= =?UTF-8?q?=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx | 3 ++- .../hooks}/custom/use-intersection-observer.ts | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename {app/(dashboard)/my/_hooks => shared/hooks}/custom/use-intersection-observer.ts (100%) diff --git a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx index 79aace9a..29e5edc1 100644 --- a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx +++ b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx @@ -3,9 +3,10 @@ import { useCallback, useRef } from 'react' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' -import { useIntersectionObserver } from '@/app/(dashboard)/my/_hooks/custom/use-intersection-observer' import { useGetMyStrategyList } from '@/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list' +import { useIntersectionObserver } from '@/shared/hooks/custom/use-intersection-observer' + const MyStrategyList = () => { const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetMyStrategyList() diff --git a/app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts b/shared/hooks/custom/use-intersection-observer.ts similarity index 100% rename from app/(dashboard)/my/_hooks/custom/use-intersection-observer.ts rename to shared/hooks/custom/use-intersection-observer.ts From ddb1296a8a645b4012ff64584f38e461ff3ea7e3 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 28 Nov 2024 14:44:34 +0900 Subject: [PATCH 385/648] =?UTF-8?q?settings:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20api=20=EB=8A=94=20msw=20=EC=82=AC=EC=9A=A9=ED=95=98=EA=B3=A0?= =?UTF-8?q?=20=EB=82=98=EB=A8=B8=EC=A7=80=20api=EB=8A=94=20=EC=8B=A4?= =?UTF-8?q?=EC=A0=9C=20api=20=EC=9A=94=EC=B2=AD=EA=B0=80=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#145)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index 16677b4f..2f1204bc 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -10,17 +10,27 @@ const nextConfig = { `, }, async rewrites() { - if (process.env.NODE_ENV === 'development') { - return [] - } return [ { - source: '/api/:path*', - destination: `${process.env.API_HOST}/api/:path*`, + source: '/api/users/reissue/refreshtoken', + destination: '/api/users/reissue/refreshtoken', + }, + { + source: '/api/users/login', + destination: '/api/users/login', }, + { + source: '/api/:path*', + destination: 'http://15.164.90.102:8081/api/:path*', + has: [ + { + type: 'header', + key: 'host' + } + ] + } ] }, - webpack: (config, { isServer }) => { if (isServer) { if (Array.isArray(config.resolve.alias)) { From cf8e40e06246d7f854392d45f9db8e78fb1346c7 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 15:34:49 +0900 Subject: [PATCH 386/648] =?UTF-8?q?rename:=20=EC=9D=B4=EC=9A=A9=EC=95=BD?= =?UTF-8?q?=EA=B4=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20shared?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#147)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/terms-of-use/page.tsx | 6 +++--- .../terms-of-use/_ui => shared/ui}/terms/investor-terms.tsx | 0 .../terms-of-use/_ui => shared/ui}/terms/privacy-terms.tsx | 0 .../terms-of-use/_ui => shared/ui}/terms/styles.module.scss | 0 .../terms-of-use/_ui => shared/ui}/terms/trader-terms.tsx | 0 5 files changed, 3 insertions(+), 3 deletions(-) rename {app/(landing)/signup/terms-of-use/_ui => shared/ui}/terms/investor-terms.tsx (100%) rename {app/(landing)/signup/terms-of-use/_ui => shared/ui}/terms/privacy-terms.tsx (100%) rename {app/(landing)/signup/terms-of-use/_ui => shared/ui}/terms/styles.module.scss (100%) rename {app/(landing)/signup/terms-of-use/_ui => shared/ui}/terms/trader-terms.tsx (100%) diff --git a/app/(landing)/signup/terms-of-use/page.tsx b/app/(landing)/signup/terms-of-use/page.tsx index 50136c98..6a12f57d 100644 --- a/app/(landing)/signup/terms-of-use/page.tsx +++ b/app/(landing)/signup/terms-of-use/page.tsx @@ -6,14 +6,14 @@ import { PATH } from '@/shared/constants/path' import { Button } from '@/shared/ui/button' import Checkbox from '@/shared/ui/check-box' import { LinkButton } from '@/shared/ui/link-button' +import InvestorTerms from '@/shared/ui/terms/investor-terms' +import PrivacyTerms from '@/shared/ui/terms/privacy-terms' +import TraderTerms from '@/shared/ui/terms/trader-terms' import { getUserTypeCookie } from '../_lib/cookies' import Step from '../_ui/step' import { useTermsCheck } from './_hooks/use-terms-check' import TermsContainer from './_ui/terms-container' -import InvestorTerms from './_ui/terms/investor-terms' -import PrivacyTerms from './_ui/terms/privacy-terms' -import TraderTerms from './_ui/terms/trader-terms' import styles from './page.module.scss' const cx = classNames.bind(styles) diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/investor-terms.tsx b/shared/ui/terms/investor-terms.tsx similarity index 100% rename from app/(landing)/signup/terms-of-use/_ui/terms/investor-terms.tsx rename to shared/ui/terms/investor-terms.tsx diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/privacy-terms.tsx b/shared/ui/terms/privacy-terms.tsx similarity index 100% rename from app/(landing)/signup/terms-of-use/_ui/terms/privacy-terms.tsx rename to shared/ui/terms/privacy-terms.tsx diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/styles.module.scss b/shared/ui/terms/styles.module.scss similarity index 100% rename from app/(landing)/signup/terms-of-use/_ui/terms/styles.module.scss rename to shared/ui/terms/styles.module.scss diff --git a/app/(landing)/signup/terms-of-use/_ui/terms/trader-terms.tsx b/shared/ui/terms/trader-terms.tsx similarity index 100% rename from app/(landing)/signup/terms-of-use/_ui/terms/trader-terms.tsx rename to shared/ui/terms/trader-terms.tsx From 95bf7e599728c1e56e954d647e9322acbb379854 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 15:35:27 +0900 Subject: [PATCH 387/648] =?UTF-8?q?feat:=20=EC=9D=B4=EC=9A=A9=EC=95=BD?= =?UTF-8?q?=EA=B4=80=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94?= =?UTF-8?q?=EB=A6=AC=EC=8B=B1=20(#147)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/terms-of-use/page.module.scss | 13 +++++++++++ app/(landing)/terms-of-use/page.tsx | 24 +++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 app/(landing)/terms-of-use/page.module.scss create mode 100644 app/(landing)/terms-of-use/page.tsx diff --git a/app/(landing)/terms-of-use/page.module.scss b/app/(landing)/terms-of-use/page.module.scss new file mode 100644 index 00000000..9c0ec141 --- /dev/null +++ b/app/(landing)/terms-of-use/page.module.scss @@ -0,0 +1,13 @@ +.title { + margin: 16px 0; + @include typo-h3; + font-weight: $text-bold; +} + +.section-container { + margin-bottom: 80px; + + h3 { + @include typo-b1; + } +} diff --git a/app/(landing)/terms-of-use/page.tsx b/app/(landing)/terms-of-use/page.tsx new file mode 100644 index 00000000..5fb414a9 --- /dev/null +++ b/app/(landing)/terms-of-use/page.tsx @@ -0,0 +1,24 @@ +import classNames from 'classnames/bind' + +import InvestorTerms from '@/shared/ui/terms/investor-terms' +import TraderTerms from '@/shared/ui/terms/trader-terms' + +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const TermsOfUsePage = () => { + return ( + <> + <h1 className={cx('title')}>이용약관</h1> + <section className={cx('section-container')}> + <InvestorTerms /> + </section> + <section className={cx('section-container')}> + <TraderTerms /> + </section> + </> + ) +} + +export default TermsOfUsePage From 5dc9b3d9d0df065f9c1c8660736a125021dcb27d Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 15:35:52 +0900 Subject: [PATCH 388/648] =?UTF-8?q?feat:=20=EA=B0=9C=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=B2=98=EB=A6=AC=EB=B0=A9=EC=B9=A8=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#1?= =?UTF-8?q?47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/privacy-terms/page.module.scss | 9 +++++++++ app/(landing)/privacy-terms/page.tsx | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 app/(landing)/privacy-terms/page.module.scss create mode 100644 app/(landing)/privacy-terms/page.tsx diff --git a/app/(landing)/privacy-terms/page.module.scss b/app/(landing)/privacy-terms/page.module.scss new file mode 100644 index 00000000..466e324e --- /dev/null +++ b/app/(landing)/privacy-terms/page.module.scss @@ -0,0 +1,9 @@ +.section-container { + margin-bottom: 80px; + + h3 { + margin: 16px 0; + @include typo-h3; + font-weight: $text-bold; + } +} diff --git a/app/(landing)/privacy-terms/page.tsx b/app/(landing)/privacy-terms/page.tsx new file mode 100644 index 00000000..a0ee53d3 --- /dev/null +++ b/app/(landing)/privacy-terms/page.tsx @@ -0,0 +1,19 @@ +import classNames from 'classnames/bind' + +import PrivacyTerms from '@/shared/ui/terms/privacy-terms' + +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const TermsOfUsePage = () => { + return ( + <> + <section className={cx('section-container')}> + <PrivacyTerms /> + </section> + </> + ) +} + +export default TermsOfUsePage From 650adb274082266b29bf7abefd198a64773b3704 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 28 Nov 2024 21:44:50 +0900 Subject: [PATCH 389/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=95=84=EC=BD=94?= =?UTF-8?q?=EB=94=94=EC=96=B8=20=ED=98=95=EC=8B=9D=EC=9D=98=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search-bar/_hooks/use-accordion-button.ts | 18 ++ .../_hooks/use-accordion-context.ts | 11 ++ .../_store/use-searching-item-store.ts | 89 +++++++++ .../strategies/_ui/search-bar/_type/search.ts | 19 ++ .../_ui/search-bar/_utils/type-validate.ts | 12 ++ .../_ui/search-bar/accordion-button.tsx | 48 +++++ .../_ui/search-bar/accordion-container.tsx | 43 ++++ .../_ui/search-bar/accordion-panel.tsx | 75 +++++++ .../_ui/search-bar/algorithm-item.tsx | 27 +++ .../strategies/_ui/search-bar/index.tsx | 94 +++++++++ .../_ui/search-bar/range-container.tsx | 44 +++++ .../_ui/search-bar/search-bar-tap.tsx | 33 ++++ .../_ui/search-bar/styles.module.scss | 185 ++++++++++++++++++ app/(dashboard)/strategies/page.tsx | 3 +- 14 files changed, 700 insertions(+), 1 deletion(-) create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_type/search.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/index.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/range-container.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/styles.module.scss diff --git a/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts new file mode 100644 index 00000000..bf65d5df --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts @@ -0,0 +1,18 @@ +import { useRef, useState } from 'react' + +export interface ButtonIdStateModel { + [key: string]: boolean +} + +const useAccordionButton = () => { + const [openIds, setOpenIds] = useState<ButtonIdStateModel | null>(null) + const panelRef = useRef<HTMLDivElement>(null) + + const handleButtonIds = (id: string, isOpen: boolean) => { + setOpenIds((prev) => ({ ...prev, [id]: isOpen })) + } + + return { panelRef, openIds, handleButtonIds } +} + +export default useAccordionButton diff --git a/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts new file mode 100644 index 00000000..49707f96 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts @@ -0,0 +1,11 @@ +import { useContext } from 'react' + +import { AccordionContext } from '../accordion-container' + +export const useAccordionContext = () => { + const context = useContext(AccordionContext) + if (!context) { + throw new Error('검색 메뉴 로드 실패') + } + return context +} diff --git a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts new file mode 100644 index 00000000..aa2f094a --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts @@ -0,0 +1,89 @@ +import { create } from 'zustand' + +import { AlgorithmItemType, RangeModel, SearchTermsModel } from '../_type/search' +import { isRangeModel } from '../_utils/type-validate' + +interface StateModel { + searchTerms: SearchTermsModel + errOptions: (keyof SearchTermsModel)[] | null +} + +interface ActionModel { + setAlgorithm: (algorithm: AlgorithmItemType) => void + setPanelItem: (key: keyof SearchTermsModel, item: string) => void + setRangeValue: (key: keyof SearchTermsModel, type: keyof RangeModel, value: number) => void + resetState: () => void + validateRangeValues: () => void +} + +interface ActionsModel { + actions: ActionModel +} + +const initialState = { + searchWord: null, + tradeTypeNames: null, + operationCycles: null, + stockTypeNames: null, + durations: null, + profitRanges: null, + principalRange: null, + mddRange: null, + smScoreRange: null, + algorithmType: null, +} + +const useSearchingItemStore = create<StateModel & ActionsModel>((set, get) => ({ + searchTerms: { + ...initialState, + }, + errOptions: [], + + actions: { + setAlgorithm: (algorithm) => + set((state) => ({ + searchTerms: { ...state.searchTerms, algorithmType: algorithm }, + })), + + setPanelItem: (key, item) => + set((state) => { + const currentItems = state.searchTerms[key] + if (Array.isArray(currentItems)) { + const updatedItems = currentItems.includes(item) + ? currentItems.filter((i) => i !== item) + : [...currentItems, item] + return { searchTerms: { ...state.searchTerms, [key]: [...updatedItems] } } + } + return { searchTerms: { ...state.searchTerms, [key]: [item] } } + }), + + setRangeValue: (key, type, value) => + set((state) => ({ + searchTerms: { + ...state.searchTerms, + [key]: { ...(state.searchTerms[key] as RangeModel), [type]: value }, + }, + })), + + resetState: () => set(() => ({ searchTerms: { ...initialState } })), + + validateRangeValues: () => { + const { searchTerms } = get() + const rangeOptions: (keyof SearchTermsModel)[] = [ + 'principalRange', + 'mddRange', + 'smScoreRange', + ] + const errOptions = rangeOptions.filter((option) => { + const value = searchTerms[option] + if (value !== null && isRangeModel(value)) { + return value.min > value.max + } + return false + }) + set({ errOptions }) + }, + }, +})) + +export default useSearchingItemStore diff --git a/app/(dashboard)/strategies/_ui/search-bar/_type/search.ts b/app/(dashboard)/strategies/_ui/search-bar/_type/search.ts new file mode 100644 index 00000000..3bacc688 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_type/search.ts @@ -0,0 +1,19 @@ +export type AlgorithmItemType = 'EFFICIENT_STRATEGY' | 'ATTACK_STRATEGY' | 'DEFENSIVE_STRATE' + +export interface SearchTermsModel { + searchWord: string | null + tradeTypeNames: string[] | null + operationCycles: string[] | null + stockTypeNames: string[] | null + durations: string[] | null + profitRanges: string[] | null + principalRange: RangeModel | null + mddRange: RangeModel | null + smScoreRange: RangeModel | null + algorithmType: AlgorithmItemType | null +} + +export interface RangeModel { + min: number + max: number +} diff --git a/app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts b/app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts new file mode 100644 index 00000000..7f71ad9f --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts @@ -0,0 +1,12 @@ +import { RangeModel } from '../_type/search' + +export const isRangeModel = (value: unknown): value is RangeModel => { + return ( + typeof value === 'object' && + value !== null && + 'min' in value && + 'max' in value && + typeof (value as RangeModel).min === 'number' && + typeof (value as RangeModel).max === 'number' + ) +} diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx new file mode 100644 index 00000000..73a5db4d --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx @@ -0,0 +1,48 @@ +'use client' + +import { useContext } from 'react' + +import { CloseIcon, OpenIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import { AccordionContext } from './accordion-container' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + optionId: keyof SearchTermsModel + title: string + size?: number +} + +const AccordionButton = ({ optionId, title, size }: Props) => { + const { openIds, handleButtonIds } = useContext(AccordionContext) + const searchTerms = useSearchingItemStore((state) => state.searchTerms) + + const clickedValue = searchTerms[optionId] + + return ( + <div className={cx('accordion-button', { active: openIds?.[optionId] })}> + <button onClick={() => handleButtonIds(optionId, !openIds?.[optionId])}> + <p> + {title} + {Array.isArray(clickedValue) && + clickedValue?.length !== 0 && + (clickedValue.length !== size ? ( + <span> + ({clickedValue.length}/{size}) + </span> + ) : ( + <span>(All)</span> + ))} + </p> + {openIds?.[optionId] ? <CloseIcon /> : <OpenIcon />} + </button> + </div> + ) +} + +export default AccordionButton diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx new file mode 100644 index 00000000..53b6246c --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx @@ -0,0 +1,43 @@ +'use client' + +import { createContext } from 'react' + +import useAccordionButton, { ButtonIdStateModel } from './_hooks/use-accordion-button' +import { SearchTermsModel } from './_type/search' +import AccordionButton from './accordion-button' +import AccordionPanel from './accordion-panel' + +interface AccordionContextModel { + panelRef: React.RefObject<HTMLDivElement> + openIds: ButtonIdStateModel | null + handleButtonIds: (id: string, open: boolean) => void +} + +const initialState: AccordionContextModel = { + panelRef: { current: null }, + openIds: null, + handleButtonIds: () => {}, +} + +export const AccordionContext = createContext(initialState) + +interface Props { + optionId: keyof SearchTermsModel + title: string + panels?: string[] +} + +const AccordionContainer = ({ optionId, title, panels }: Props) => { + const { openIds, panelRef, handleButtonIds } = useAccordionButton() + + return ( + <AccordionContext.Provider value={{ openIds, panelRef, handleButtonIds }}> + <div> + <AccordionButton optionId={optionId} title={title} size={panels?.length} /> + <AccordionPanel optionId={optionId} panels={panels} /> + </div> + </AccordionContext.Provider> + ) +} + +export default AccordionContainer diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx new file mode 100644 index 00000000..9d8fbb29 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx @@ -0,0 +1,75 @@ +import { useContext, useEffect, useState } from 'react' + +import { CheckedCircleIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import { AccordionContext } from './accordion-container' +import RangeContainer from './range-container' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + optionId: keyof SearchTermsModel + panels?: string[] +} + +const AccordionPanel = ({ optionId, panels }: Props) => { + const { openIds, panelRef } = useContext(AccordionContext) + const [panelHeight, setPanelHeight] = useState<number | null>(null) + const [isClose, setIsClose] = useState(false) + const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const { setPanelItem } = useSearchingItemStore((state) => state.actions) + + useEffect(() => { + if (panelRef.current && hasOpenId) { + const panelHeight = panelRef.current.clientHeight + 32 * (panels?.length || 1) + setPanelHeight(panelHeight) + panelRef.current.style.setProperty('--panel-height', `${panelHeight}px`) + } + + if (!hasOpenId) { + setIsClose(true) + const timeout = setTimeout(() => { + setIsClose(false) + setPanelHeight(null) + }, 300) + return () => clearTimeout(timeout) + } + }, [openIds, panelRef, optionId]) + + const hasOpenId = openIds?.[optionId] + const clickedValue = searchTerms[optionId] + + return ( + <> + {hasOpenId !== undefined && ( + <div + className={cx('panel-wrapper', { open: hasOpenId, close: isClose })} + style={{ '--panel-height': `${panelHeight}px` || '0px' } as React.CSSProperties} + ref={panelRef} + > + {panels + ? hasOpenId && + panels?.map((panel, idx) => ( + <button + key={`${panel}-${idx}`} + onClick={() => setPanelItem(optionId, panel)} + className={cx({ + active: Array.isArray(clickedValue) && clickedValue?.includes(panel), + })} + > + <p>{panel}</p> + <CheckedCircleIcon /> + </button> + )) + : hasOpenId && <RangeContainer optionId={optionId} />} + </div> + )} + </> + ) +} + +export default AccordionPanel diff --git a/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx b/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx new file mode 100644 index 00000000..10606e92 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx @@ -0,0 +1,27 @@ +'use client' + +import classNames from 'classnames/bind' + +import { AlgorithmItemType } from './_type/search' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + name: AlgorithmItemType + clickedAlgorithm: AlgorithmItemType | null + onChange: (algorithm: AlgorithmItemType) => void +} + +const AlgorithmItem = ({ name, clickedAlgorithm, onChange }: Props) => { + return ( + <button + className={cx('algorithm-button', { active: clickedAlgorithm === name })} + onClick={() => onChange(name)} + > + {name} + </button> + ) +} + +export default AlgorithmItem diff --git a/app/(dashboard)/strategies/_ui/search-bar/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/index.tsx new file mode 100644 index 00000000..b3305a74 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/index.tsx @@ -0,0 +1,94 @@ +'use client' + +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import { SearchInput } from '@/shared/ui/search-input' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import AccordionContainer from './accordion-container' +import AlgorithmItem from './algorithm-item' +import SearchBarTap from './search-bar-tap' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const ALGORITHM_MENU = [ + { EFFICIENT_STRATEGY: '효율형 전략' }, + { ATTACK_STRATEGY: '공격형 전략' }, + { DEFENSIVE_STRATEGY: '방어형 전략' }, +] +interface AccordionMenuDataModel { + id: keyof SearchTermsModel + title: string + panels?: string[] +} +const SearchBarContainer = () => { + const [isMainTab, setIsMainTab] = useState(true) + const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const { setAlgorithm, resetState, validateRangeValues } = useSearchingItemStore( + (state) => state.actions + ) + + const ACCORDION_MENU: AccordionMenuDataModel[] = [ + { id: 'tradeTypeNames', title: '매매 유형', panels: ['수동', '자동', '반자동'] }, + { id: 'operationCycles', title: '운용 주기', panels: ['데이', '포지션'] }, + { id: 'stockTypeNames', title: '운영 종목', panels: ['선물', '해외', '국내'] }, + { id: 'durations', title: '기간', panels: ['1년 이하', '1년 ~ 2년', '2년 ~ 3년', '3년 이상'] }, + { + id: 'profitRanges', + title: '수익률', + panels: ['10% 이하', '10% ~ 20%', '20% ~ 30%', '30% 이상'], + }, + { id: 'principalRange', title: '원금' }, + { id: 'mddRange', title: 'MDD' }, + { id: 'smScoreRange', title: 'SM SCORE' }, + ] + + return ( + <> + <div className={cx('searchInput-wrapper')}> + <SearchInput placeholder="전략명을 검색하세요." /> + </div> + <div className={cx('searchInput-wrapper')}> + <SearchBarTap isMainTab={isMainTab} onChangeTab={setIsMainTab} /> + {isMainTab + ? ACCORDION_MENU.map((menu) => ( + <AccordionContainer + key={menu.id} + optionId={menu.id} + title={menu.title} + panels={menu.panels} + /> + )) + : ALGORITHM_MENU.map((menu) => { + return Object.entries(menu).map(([key, value]) => ( + <AlgorithmItem + key={key} + name={value} + clickedAlgorithm={searchTerms.algorithmType} + onChange={setAlgorithm} + /> + )) + })} + <div className={cx('search-button-wrapper')}> + <Button className={cx('button', 'initialize')} onClick={resetState}> + 초기화 + </Button> + <Button + variant="filled" + className={cx('button', 'searching')} + onClick={validateRangeValues} + > + 검색하기 + </Button> + </div> + </div> + </> + ) +} + +export default SearchBarContainer diff --git a/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx new file mode 100644 index 00000000..69f97719 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx @@ -0,0 +1,44 @@ +import classNames from 'classnames/bind' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + optionId: keyof SearchTermsModel +} + +const RangeContainer = ({ optionId }: Props) => { + const errOptions = useSearchingItemStore((state) => state.errOptions) + const { setRangeValue } = useSearchingItemStore((state) => state.actions) + + const handleRangeValue = (e: React.ChangeEvent<HTMLInputElement>, type: 'min' | 'max') => { + const value = Number(e.target.value) + setRangeValue(optionId, type, value) + } + + return ( + <div className={cx('range-container')}> + <div className={cx('range-wrapper')}> + <input + className={cx('range')} + type="number" + placeholder="0" + onChange={(e) => handleRangeValue(e, 'min')} + /> + <span>~</span> + <input + className={cx('range')} + type="number" + placeholder="0" + onChange={(e) => handleRangeValue(e, 'max')} + /> + </div> + {errOptions?.includes(optionId) && <p>최소 값은 최대 값보다 작아야합니다.</p>} + </div> + ) +} + +export default RangeContainer diff --git a/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx b/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx new file mode 100644 index 00000000..47f06070 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx @@ -0,0 +1,33 @@ +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isMainTab: boolean + onChangeTab: (isMainTab: boolean) => void +} + +const SearchBarTap = ({ isMainTab, onChangeTab }: Props) => { + return ( + <div className={cx('tab-container')}> + <Button + className={cx('button', isMainTab ? 'main-on' : 'main-off')} + onClick={() => onChangeTab(!isMainTab)} + > + 항목별 + </Button> + <Button + className={cx('button', isMainTab ? 'main-off' : 'main-on')} + onClick={() => onChangeTab(!isMainTab)} + > + 알고리즘별 + </Button> + </div> + ) +} + +export default SearchBarTap diff --git a/app/(dashboard)/strategies/_ui/search-bar/styles.module.scss b/app/(dashboard)/strategies/_ui/search-bar/styles.module.scss new file mode 100644 index 00000000..fe548f66 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/styles.module.scss @@ -0,0 +1,185 @@ +@mixin item-align { + display: flex; + justify-content: space-between; +} + +.searchInput-wrapper { + background-color: $color-white; + padding: 20px; + border: 5px; + margin-bottom: 10px; +} + +.search-button-wrapper { + @include item-align; + margin-top: 20px; + .button { + height: 40px; + &.initialize { + width: 90px; + padding: 0; + } + &.searching { + width: 140px; + } + } +} + +.tab-container { + @include item-align; + margin-bottom: 20px; + .button { + border: 0; + width: 118px; + height: 48px; + &.main-on { + background-color: $color-orange-500; + color: $color-white; + } + &.main-off { + background-color: transparent; + color: $color-gray-700; + } + } +} + +.algorithm-button { + width: 100%; + padding: 10.8px 20px; + margin-bottom: 5px; + border-radius: 5px; + background-color: transparent; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + @include typo-b3; + color: $color-gray-600; + &:hover { + background-color: $color-orange-100; + } + &.active { + background-color: $color-orange-600; + color: $color-white; + } +} + +.accordion-button, +.panel-wrapper { + padding: 2px 0; + margin-bottom: 5px; + border-radius: 5px; + overflow: hidden; + button { + @include item-align; + width: 100%; + padding: 4px 20px; + align-items: center; + background-color: transparent; + } +} + +.accordion-button { + border: 1px solid $color-gray-200; + background-color: $color-gray-100; + button { + p { + @include typo-c1; + color: $color-gray-800; + span { + color: $color-orange-500; + margin-left: 4px; + } + } + svg { + width: 26px; + path { + fill: #171717; + } + } + } + &:hover { + border: 1px solid $color-orange-300; + } + &.active { + border: 1px solid $color-orange-500; + box-shadow: 0px 0px 2px rgba(255, 119, 82, 1); + } +} + +.panel-wrapper { + display: none; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + button { + p { + @include typo-c1; + color: $color-gray-600; + } + svg, + svg circle { + width: 24px; + .checked { + fill: $color-orange-600; + } + } + &.active { + svg, + svg circle { + fill: $color-orange-600; + } + } + &:hover { + background-color: $color-orange-100; + } + } + &.open { + display: block; + animation: accordionDown 0.3s cubic-bezier(0.2, 0.2, 0.2, 0.6); + } + &.close { + display: block; + animation: accordionUp 0.3s cubic-bezier(0.2, 0.2, 0.2, 0.6); + } + .range-container { + padding: 4px 20px; + p { + @include typo-c1; + color: $color-orange-800; + margin-top: 2px; + } + .range-wrapper { + @include item-align; + @include typo-c1; + align-items: center; + .range { + width: 80px; + height: 24px; + border-radius: 2px; + border: 1px solid $color-gray-300; + padding: 0 4px; + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + display: none; + } + } + span { + color: $color-gray-600; + } + } + } +} + +@keyframes accordionDown { + from { + height: 0; + } + to { + height: var(--panel-height); + } +} + +@keyframes accordionUp { + from { + height: var(--panel-height); + } + to { + height: 0; + } +} diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index d8d768c7..eebaff44 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames/bind' import Title from '@/shared/ui/title' +import SearchBarContainer from './_ui/search-bar' import SideContainer from './_ui/side-container' import StrategyList from './_ui/strategy-list' import styles from './page.module.scss' @@ -18,7 +19,7 @@ const StrategiesPage = () => { <StrategyList /> </Suspense> <SideContainer> - <p className={cx('search-bar')}>Search-Bar</p> + <SearchBarContainer /> </SideContainer> </div> ) From ea785016a955decfbbb61f73ec047fad5bdc26f2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 28 Nov 2024 22:36:50 +0900 Subject: [PATCH 390/648] =?UTF-8?q?feat:=20store=20reset=EC=95=A1=EC=85=98?= =?UTF-8?q?=EC=97=90=20errOptions=EC=B4=88=EA=B8=B0=ED=99=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/search-bar/_store/use-searching-item-store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts index aa2f094a..a9353af0 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts +++ b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts @@ -65,7 +65,7 @@ const useSearchingItemStore = create<StateModel & ActionsModel>((set, get) => ({ }, })), - resetState: () => set(() => ({ searchTerms: { ...initialState } })), + resetState: () => set(() => ({ searchTerms: { ...initialState }, errOptions: [] })), validateRangeValues: () => { const { searchTerms } = get() From cb811593e3bacbe861db3decc1e1e935ea4605e3 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 23:20:29 +0900 Subject: [PATCH 391/648] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95=EB=90=9C=20a?= =?UTF-8?q?pi=20=EB=AA=85=EC=84=B8=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95=20(#151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/area-chart.tsx | 8 +- app/(dashboard)/_ui/strategies-item/index.tsx | 12 +- .../strategies/_api/get-strategies.ts | 16 +- .../strategies/_ui/strategy-list/index.tsx | 10 +- mocks/handlers/strategies.ts | 536 ++++++------------ shared/types/strategy-details-data.ts | 26 +- 6 files changed, 201 insertions(+), 407 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/_ui/strategies-item/area-chart.tsx index 6c61330f..ff6f106c 100644 --- a/app/(dashboard)/_ui/strategies-item/area-chart.tsx +++ b/app/(dashboard)/_ui/strategies-item/area-chart.tsx @@ -33,8 +33,8 @@ const AreaChart = ({ profitRateChartData: data }: Props) => { }, yAxis: { visible: false, - min: Math.min(...data.yAxis), - max: Math.max(...data.yAxis), + min: Math.min(...data.profitRates), + max: Math.max(...data.profitRates), }, legend: { enabled: false }, plotOptions: { @@ -56,9 +56,9 @@ const AreaChart = ({ profitRateChartData: data }: Props) => { series: [ { type: 'areaspline', - data: data.xAxis.map((x, idx) => ({ + data: data.dates.map((x, idx) => ({ x: new Date(x).getTime(), - y: data.yAxis[idx], + y: data.profitRates[idx], })), color: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index dc8f508d..8053e335 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -19,16 +19,16 @@ const StrategiesItem = ({ strategiesData: data }: Props) => { return ( <Link className={cx('container')} href={`/strategies/${data.strategyId}`}> <StrategiesSummary - iconUrls={[data.tradeTypeIconUrl, ...data.stockTypeIconUrls]} - iconNames={[data.tradeTypeName, ...data.stockTypeNames]} + iconUrls={[data.tradeTypeIconUrl, ...data.stockTypeInfo.stockTypeIconUrls]} + iconNames={[data.tradeTypeName, ...data.stockTypeInfo.stockTypeNames]} title={data.strategyName} profile={{ - traderImage: data.traderImage, + traderImage: data.traderImgUrl, nickname: data.nickname, }} - subscriptionCount={data.subscriptionCnt} + subscriptionCount={data.subscriptionCount} averageRating={data.averageRating} - totalReview={data.totalReview} + totalReview={data.totalReviews} /> <AreaChart profitRateChartData={data.profitRateChartData} /> <div className={cx('mdd')}> @@ -39,7 +39,7 @@ const StrategiesItem = ({ strategiesData: data }: Props) => { </div> <div className={cx('profit')}> <span>누적 수익률</span> - <p>{data.cumulativeProfitLossRate}%</p> + <p>{data.cumulativeProfitRate}%</p> <span>최근 1년 수익률</span> <p>{data.recentYearProfitLossRate ? data.recentYearProfitLossRate + '%' : '-'}</p> </div> diff --git a/app/(dashboard)/strategies/_api/get-strategies.ts b/app/(dashboard)/strategies/_api/get-strategies.ts index 66dde4b0..b9aeb870 100644 --- a/app/(dashboard)/strategies/_api/get-strategies.ts +++ b/app/(dashboard)/strategies/_api/get-strategies.ts @@ -1,3 +1,4 @@ +import { strategiesMockData } from '@/mocks/handlers/strategies' import axios from 'axios' import { StrategiesModel } from '@/shared/types/strategy-details-data' @@ -6,21 +7,22 @@ const getStrategiesData = async ( isReady: boolean, page: number, size: number -): Promise<{ strategiesData: StrategiesModel[]; totalCount: number } | undefined> => { - if (!isReady) return { strategiesData: [], totalCount: 0 } +): Promise<{ strategiesData: StrategiesModel[]; totalPages: number } | undefined> => { + if (!isReady) return { strategiesData: [], totalPages: 0 } try { const response = await axios.get(`/api/strategies?page=${page}&size=${size}`) if (!response.data) { + // 임시 목데이터 console.error('전략 목록 데이터 가져오기 실패') - return { strategiesData: [], totalCount: 0 } + return { strategiesData: strategiesMockData, totalPages: 2 } } const { - strategiesData, - totalCount, - }: { strategiesData: StrategiesModel[]; totalCount: number } = await response.data + content: strategiesData, + totalPages, + }: { content: StrategiesModel[]; totalPages: number } = await response.data.result - return { strategiesData, totalCount } + return { strategiesData, totalPages } } catch (err) { console.error(err) } diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index 7ae141ce..d15a8a0a 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -35,7 +35,7 @@ const StrategyList = () => { }, [searchParams]) const strategiesData = data?.strategiesData || [] - const totalCount = data?.totalCount || null + const totalPages = data?.totalPages || null const handlePageChange = (page: number) => { router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`) @@ -47,12 +47,8 @@ const StrategyList = () => { <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> ))} <div className={cx('pagination')}> - {totalCount && ( - <Pagination - currentPage={page} - maxPage={Math.ceil(totalCount / COUNT_PER_PAGE)} - onPageChange={handlePageChange} - /> + {totalPages && ( + <Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} /> )} </div> </> diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts index 66854190..73c75794 100644 --- a/mocks/handlers/strategies.ts +++ b/mocks/handlers/strategies.ts @@ -1,14 +1,19 @@ import { HttpResponse, http } from 'msw' -const strategiesData = [ +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +export const strategiesMockData: StrategiesModel[] = [ { - strategyId: '1', + strategyId: 1, strategyName: 'Dynamic ETF 전략', + traderImgUrl: '/images/trader1.png', nickname: 'AlphaTrader', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -19,27 +24,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-15,432,567', + mdd: -15432567, smScore: 72.1, - cumulativeProfitLossRate: 140.5, + cumulativeProfitRate: 140.5, recentYearProfitLossRate: 35.2, - subscriptionCnt: 45, - isSubscribed: true, + subscriptionCount: 45, averageRating: 4.9, - totalReview: 34, + totalReviews: 34, + isSubscribed: true, }, { - strategyId: '2', + strategyId: 2, strategyName: '고수익 ETF', + traderImgUrl: '/images/trader2.png', nickname: 'BetaTrader', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -50,27 +58,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-12,786,543', + mdd: -12786543, smScore: 65.4, - cumulativeProfitLossRate: 125.3, + cumulativeProfitRate: 125.3, recentYearProfitLossRate: 28.4, - subscriptionCnt: 67, - isSubscribed: false, + subscriptionCount: 67, averageRating: 4.6, - totalReview: 19, + totalReviews: 19, + isSubscribed: false, }, { - strategyId: '3', + strategyId: 3, strategyName: 'Futures Pro', + traderImgUrl: '/images/trader3.png', nickname: 'Gamma', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -81,27 +92,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-18,234,678', + mdd: -18234678, smScore: 79.6, - cumulativeProfitLossRate: 160.4, + cumulativeProfitRate: 160.4, recentYearProfitLossRate: 40.1, - subscriptionCnt: 89, - isSubscribed: true, + subscriptionCount: 89, averageRating: 4.7, - totalReview: 45, + totalReviews: 45, + isSubscribed: true, }, { - strategyId: '4', + strategyId: 4, strategyName: '월별 수익 전략', + traderImgUrl: '/images/trader4.png', nickname: 'TraderX', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -112,27 +126,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-11,456,789', + mdd: -11456789, smScore: 68.4, - cumulativeProfitLossRate: 134.5, + cumulativeProfitRate: 134.5, recentYearProfitLossRate: 32.7, - subscriptionCnt: 34, - isSubscribed: false, + subscriptionCount: 34, averageRating: 4.5, - totalReview: 29, + totalReviews: 29, + isSubscribed: false, }, { - strategyId: '5', + strategyId: 5, strategyName: '리스크 관리 전략', + traderImgUrl: '/images/trader5.png', nickname: 'DeltaOne', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -143,27 +160,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-14,345,678', + mdd: -14345678, smScore: 62.3, - cumulativeProfitLossRate: 120.3, + cumulativeProfitRate: 120.3, recentYearProfitLossRate: 25.7, - subscriptionCnt: 21, - isSubscribed: true, + subscriptionCount: 21, averageRating: 4.8, - totalReview: 22, + totalReviews: 22, + isSubscribed: true, }, { - strategyId: '6', + strategyId: 6, strategyName: 'Active LongShort', + traderImgUrl: '/images/trader6.png', nickname: 'Hedger', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -174,27 +194,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-17,890,234', + mdd: -17890234, smScore: 80.2, - cumulativeProfitLossRate: 175.4, + cumulativeProfitRate: 175.4, recentYearProfitLossRate: 45.8, - subscriptionCnt: 120, - isSubscribed: true, + subscriptionCount: 120, averageRating: 4.9, - totalReview: 52, + totalReviews: 52, + isSubscribed: true, }, { - strategyId: '7', + strategyId: 7, strategyName: '안정적 성장 전략', + traderImgUrl: '/images/trader7.png', nickname: 'SafeGrow', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -205,27 +228,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-13,567,890', + mdd: -13567890, smScore: 70.1, - cumulativeProfitLossRate: 138.2, + cumulativeProfitRate: 138.2, recentYearProfitLossRate: 30.9, - subscriptionCnt: 54, - isSubscribed: false, + subscriptionCount: 54, averageRating: 4.4, - totalReview: 18, + totalReviews: 18, + isSubscribed: false, }, { - strategyId: '8', + strategyId: 8, strategyName: 'High Risk High Return', + traderImgUrl: '/images/trader8.png', nickname: 'RiskyTrader', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -236,27 +262,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-25,678,345', + mdd: -25678345, smScore: 85.7, - cumulativeProfitLossRate: 200.5, + cumulativeProfitRate: 200.5, recentYearProfitLossRate: 50.4, - subscriptionCnt: 76, - isSubscribed: true, + subscriptionCount: 76, averageRating: 4.7, - totalReview: 37, + totalReviews: 37, + isSubscribed: true, }, { - strategyId: '9', + strategyId: 9, strategyName: 'ETF 분산 투자', + traderImgUrl: '/images/trader9.png', nickname: 'Diversifier', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -267,27 +296,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-9,876,543', + mdd: -9876543, smScore: 58.3, - cumulativeProfitLossRate: 112.4, + cumulativeProfitRate: 112.4, recentYearProfitLossRate: 20.7, - subscriptionCnt: 23, - isSubscribed: false, + subscriptionCount: 23, averageRating: 4.3, - totalReview: 11, + totalReviews: 11, + isSubscribed: false, }, { - strategyId: '10', + strategyId: 10, strategyName: 'Momentum 전략', + traderImgUrl: '/images/trader10.png', nickname: 'MomentumKing', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -298,27 +330,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-18,456,789', + mdd: -18456789, smScore: 75.6, - cumulativeProfitLossRate: 165.3, + cumulativeProfitRate: 165.3, recentYearProfitLossRate: 38.5, - subscriptionCnt: 89, - isSubscribed: true, + subscriptionCount: 89, averageRating: 4.9, - totalReview: 48, + totalReviews: 48, + isSubscribed: true, }, { - strategyId: '11', + strategyId: 11, strategyName: '소형주 집중 전략', + traderImgUrl: '/images/trader11.png', nickname: 'SmallCapPro', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -329,27 +364,30 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-14,345,678', + mdd: -14345678, smScore: 69.4, - cumulativeProfitLossRate: 130.7, + cumulativeProfitRate: 130.7, recentYearProfitLossRate: 28.2, - subscriptionCnt: 67, - isSubscribed: false, + subscriptionCount: 67, averageRating: 4.6, - totalReview: 26, + totalReviews: 26, + isSubscribed: false, }, { - strategyId: '12', + strategyId: 12, strategyName: 'Active Bond 전략', + traderImgUrl: '/images/trader12.png', nickname: 'BondExpert', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -360,268 +398,21 @@ const strategiesData = [ '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', - mdd: '-10,567,890', + mdd: -10567890, smScore: 64.3, - cumulativeProfitLossRate: 125.8, + cumulativeProfitRate: 125.8, recentYearProfitLossRate: 27.4, - subscriptionCnt: 42, - isSubscribed: true, - averageRating: 4.5, - totalReview: 21, - }, - { - strategyId: '13', - strategyName: 'Smart Index 전략', - nickname: 'IndexGuru', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-16,543,678', - smScore: 71.8, - cumulativeProfitLossRate: 145.6, - recentYearProfitLossRate: 32.7, - subscriptionCnt: 54, - isSubscribed: true, - averageRating: 4.6, - totalReview: 33, - }, - { - strategyId: '14', - strategyName: 'MACD Pro', - nickname: 'TrendRider', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-19,567,890', - smScore: 77.5, - cumulativeProfitLossRate: 175.8, - recentYearProfitLossRate: 42.3, - subscriptionCnt: 78, - isSubscribed: true, - averageRating: 4.8, - totalReview: 49, - }, - { - strategyId: '15', - strategyName: '균형 성장 전략', - nickname: 'BalancedGrow', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-12,567,890', - smScore: 62.8, - cumulativeProfitLossRate: 118.5, - recentYearProfitLossRate: 24.3, - subscriptionCnt: 31, - isSubscribed: false, - averageRating: 4.2, - totalReview: 16, - }, - { - strategyId: '16', - strategyName: '단기 스윙 전략', - nickname: 'SwingPro', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-22,345,678', - smScore: 80.3, - cumulativeProfitLossRate: 190.7, - recentYearProfitLossRate: 45.8, - subscriptionCnt: 85, - isSubscribed: true, - averageRating: 4.9, - totalReview: 42, - }, - { - strategyId: '17', - strategyName: '로우볼 전략', - nickname: 'LowVolKing', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-8,345,678', - smScore: 55.9, - cumulativeProfitLossRate: 102.4, - recentYearProfitLossRate: 18.5, - subscriptionCnt: 18, - isSubscribed: false, - averageRating: 4.1, - totalReview: 9, - }, - { - strategyId: '18', - strategyName: '인컴 중심 전략', - nickname: 'IncomeMaster', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-10,678,345', - smScore: 60.2, - cumulativeProfitLossRate: 115.9, - recentYearProfitLossRate: 23.6, - subscriptionCnt: 40, - isSubscribed: true, + subscriptionCount: 42, averageRating: 4.5, - totalReview: 20, - }, - { - strategyId: '19', - strategyName: '글로벌 주식 전략', - nickname: 'GlobalTrader', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-17,890,567', - smScore: 74.2, - cumulativeProfitLossRate: 153.6, - recentYearProfitLossRate: 36.4, - subscriptionCnt: 72, - isSubscribed: true, - averageRating: 4.7, - totalReview: 38, - }, - { - strategyId: '20', - strategyName: '테크 성장 전략', - nickname: 'TechGrow', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: '-23,567,890', - smScore: 88.3, - cumulativeProfitLossRate: 210.9, - recentYearProfitLossRate: 52.8, - subscriptionCnt: 95, + totalReviews: 21, isSubscribed: true, - averageRating: 5.0, - totalReview: 50, }, ] + export const strategiesHandlers = [ http.get('/api/strategies', ({ request }) => { const url = new URL(request.url) @@ -629,16 +420,19 @@ export const strategiesHandlers = [ const size = parseInt(url.searchParams.get('size') || '8') if (!isNaN(page) && !isNaN(size)) { - const croppedStrategiesData = strategiesData.slice( + const croppedStrategiesData = strategiesMockData.slice( size * (page - 1), size * (page - 1) + size ) return HttpResponse.json({ - strategiesData: croppedStrategiesData, - totalCount: strategiesData.length, + result: { + content: croppedStrategiesData, + totalPages: Math.ceil(strategiesMockData.length / size), + }, }) } + return HttpResponse.json( { isSuccess: false, diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts index 4e78b564..10a42157 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-details-data.ts @@ -20,28 +20,30 @@ export interface MonthlyAnalysisModel { } export interface ProfitRateChartDataModel { - xAxis: string[] - yAxis: number[] + dates: string[] + profitRates: number[] } export interface StrategiesModel { - strategyId: string + strategyId: number strategyName: string + traderImgUrl?: string nickname: string - traderImage?: string - stockTypeIconUrls: string[] - stockTypeNames: string[] - profitRateChartData: ProfitRateChartDataModel + stockTypeInfo: { + stockTypeIconUrls: string[] + stockTypeNames: string[] + } tradeTypeIconUrl: string tradeTypeName: string - mdd: string + profitRateChartData: ProfitRateChartDataModel + mdd: number smScore: number - cumulativeProfitLossRate: number + cumulativeProfitRate: number recentYearProfitLossRate?: number - subscriptionCnt: number - isSubscribed: boolean + subscriptionCount: number averageRating: number - totalReview: number + totalReviews: number + isSubscribed: boolean } export interface StrategyDetailsInformationModel { From 8e5ceecff03999ef4dbb642579b667565634c1dd Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 23:21:58 +0900 Subject: [PATCH 392/648] =?UTF-8?q?feat:=20=EA=B5=AC=EB=8F=85=ED=95=9C=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/_api/get-favorite-strategy-list.ts | 34 +++++++++++ .../query/use-get-favorite-strategy-list.ts | 17 ++++++ .../_ui/favorite-strategy-list/index.tsx | 56 +++++++++++++++++++ .../favorite-strategy-list/styles.module.scss | 3 + app/(dashboard)/my/favorites/page.module.scss | 5 ++ app/(dashboard)/my/favorites/page.tsx | 20 ++++++- 6 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 app/(dashboard)/my/_api/get-favorite-strategy-list.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-get-favorite-strategy-list.ts create mode 100644 app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx create mode 100644 app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss create mode 100644 app/(dashboard)/my/favorites/page.module.scss diff --git a/app/(dashboard)/my/_api/get-favorite-strategy-list.ts b/app/(dashboard)/my/_api/get-favorite-strategy-list.ts new file mode 100644 index 00000000..b30fb6d9 --- /dev/null +++ b/app/(dashboard)/my/_api/get-favorite-strategy-list.ts @@ -0,0 +1,34 @@ +import { strategiesMockData } from '@/mocks/handlers/strategies' + +import axiosInstance from '@/shared/api/axios' +import { StrategiesModel } from '@/shared/types/strategy-details-data' + +interface Props { + page: number + size: number +} + +const getFavoriteStrategyList = async ({ + page = 1, + size = 6, +}: Props): Promise<{ strategiesData: StrategiesModel[]; totalPages: number } | undefined> => { + try { + const response = await axiosInstance.get( + `/api/my-strategies/subscribed?page=${page}&size=${size}` + ) + + const { + content: strategiesData, + totalPages, + }: { content: StrategiesModel[]; totalPages: number } = await response.data.result + + return { strategiesData, totalPages } + } catch (err) { + console.error(err) + // throw new Error('구독한 전략 조회에 실패했습니다.') + // 임시 목데이터 + return { strategiesData: strategiesMockData.filter((data) => data.isSubscribed), totalPages: 1 } + } +} + +export default getFavoriteStrategyList diff --git a/app/(dashboard)/my/_hooks/query/use-get-favorite-strategy-list.ts b/app/(dashboard)/my/_hooks/query/use-get-favorite-strategy-list.ts new file mode 100644 index 00000000..7138a936 --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-get-favorite-strategy-list.ts @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query' + +import getFavoriteStrategyList from '../../_api/get-favorite-strategy-list' + +interface Props { + page: number + size: number +} + +const useGetFavoriteStrategyList = ({ page, size }: Props) => { + return useQuery({ + queryKey: ['favoriteStrategies', page, size], + queryFn: () => getFavoriteStrategyList({ page, size }), + }) +} + +export default useGetFavoriteStrategyList diff --git a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx new file mode 100644 index 00000000..319a5bea --- /dev/null +++ b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx @@ -0,0 +1,56 @@ +'use client' + +import { useEffect } from 'react' + +import { useRouter, useSearchParams } from 'next/navigation' + +import ListHeader from '@/app/(dashboard)/_ui/list-header' +import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import Pagination from '@/shared/ui/pagination' + +import useGetFavoriteStrategyList from '../../../_hooks/query/use-get-favorite-strategy-list' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const COUNT_PER_PAGE = 6 + +const FavoriteStrategyList = () => { + const searchParams = useSearchParams() + const router = useRouter() + const page = parseInt(searchParams?.get('page') || '1') + + const { data } = useGetFavoriteStrategyList({ page, size: COUNT_PER_PAGE }) + + useEffect(() => { + if (!searchParams.size) { + router.push(`${PATH.FAVORITES}?page=1&size=${COUNT_PER_PAGE}`) + } + }, [searchParams]) + + const strategiesData = data?.strategiesData || [] + const totalPages = data?.totalPages || null + + const handlePageChange = (page: number) => { + router.push(`${PATH.FAVORITES}?page=${page}&size=${COUNT_PER_PAGE}`) + } + + return ( + <> + <ListHeader /> + <div className={cx('pagination')}> + {strategiesData?.map((strategy) => ( + <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> + ))} + {totalPages && ( + <Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} /> + )} + </div> + </> + ) +} + +export default FavoriteStrategyList diff --git a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss new file mode 100644 index 00000000..f5081c9f --- /dev/null +++ b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss @@ -0,0 +1,3 @@ +.pagination { + margin-bottom: 24px; +} diff --git a/app/(dashboard)/my/favorites/page.module.scss b/app/(dashboard)/my/favorites/page.module.scss new file mode 100644 index 00000000..62016214 --- /dev/null +++ b/app/(dashboard)/my/favorites/page.module.scss @@ -0,0 +1,5 @@ +.container { + h1 { + margin: 80px 0 24px; + } +} diff --git a/app/(dashboard)/my/favorites/page.tsx b/app/(dashboard)/my/favorites/page.tsx index 6d98eaf1..e64c2537 100644 --- a/app/(dashboard)/my/favorites/page.tsx +++ b/app/(dashboard)/my/favorites/page.tsx @@ -1,5 +1,23 @@ +import { Suspense } from 'react' + +import classNames from 'classnames/bind' + +import Title from '@/shared/ui/title' + +import FavoriteStrategyList from './_ui/favorite-strategy-list' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + const MyFavoritesPage = () => { - return <></> + return ( + <div className={cx('container')}> + <Title label="구독한 전략" /> + <Suspense fallback={<div>Loading...</div>}> + <FavoriteStrategyList /> + </Suspense> + </div> + ) } export default MyFavoritesPage From f9e04b605ff59bbbe65bac6be9b87030a220dce2 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 23:34:14 +0900 Subject: [PATCH 393/648] =?UTF-8?q?refactor:=20usePagination=20=ED=9B=85?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC=20(#151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/favorite-strategy-list/index.tsx | 22 +++--------- .../strategies/_ui/strategy-list/index.tsx | 25 ++++--------- shared/hooks/custom/use-pagination.ts | 36 +++++++++++++++++++ 3 files changed, 47 insertions(+), 36 deletions(-) create mode 100644 shared/hooks/custom/use-pagination.ts diff --git a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx index 319a5bea..54f251e9 100644 --- a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx +++ b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx @@ -1,14 +1,11 @@ 'use client' -import { useEffect } from 'react' - -import { useRouter, useSearchParams } from 'next/navigation' - import ListHeader from '@/app/(dashboard)/_ui/list-header' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' +import { usePagination } from '@/shared/hooks/custom/use-pagination' import Pagination from '@/shared/ui/pagination' import useGetFavoriteStrategyList from '../../../_hooks/query/use-get-favorite-strategy-list' @@ -19,25 +16,16 @@ const cx = classNames.bind(styles) const COUNT_PER_PAGE = 6 const FavoriteStrategyList = () => { - const searchParams = useSearchParams() - const router = useRouter() - const page = parseInt(searchParams?.get('page') || '1') + const { page, handlePageChange } = usePagination({ + basePath: PATH.FAVORITES, + pageSize: COUNT_PER_PAGE, + }) const { data } = useGetFavoriteStrategyList({ page, size: COUNT_PER_PAGE }) - useEffect(() => { - if (!searchParams.size) { - router.push(`${PATH.FAVORITES}?page=1&size=${COUNT_PER_PAGE}`) - } - }, [searchParams]) - const strategiesData = data?.strategiesData || [] const totalPages = data?.totalPages || null - const handlePageChange = (page: number) => { - router.push(`${PATH.FAVORITES}?page=${page}&size=${COUNT_PER_PAGE}`) - } - return ( <> <ListHeader /> diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index d15a8a0a..f7a65019 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -1,45 +1,32 @@ 'use client' -import { useEffect } from 'react' - -import { useRouter, useSearchParams } from 'next/navigation' - import ListHeader from '@/app/(dashboard)/_ui/list-header' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' +import { usePagination } from '@/shared/hooks/custom/use-pagination' import { useMSWStore } from '@/shared/stores/msw' import Pagination from '@/shared/ui/pagination' import useGetStrategiesData from '../../_hooks/query/use-get-strategies-data' import styles from './styles.module.scss' -/* eslint-disable react-hooks/exhaustive-deps */ - const cx = classNames.bind(styles) const COUNT_PER_PAGE = 8 const StrategyList = () => { - const searchParams = useSearchParams() - const router = useRouter() const isReady = useMSWStore((state) => state.isReady) - const page = parseInt(searchParams?.get('page') || '1') - + const { page, handlePageChange } = usePagination({ + basePath: PATH.STRATEGIES, + pageSize: COUNT_PER_PAGE, + }) const { data } = useGetStrategiesData({ isReady, page, size: COUNT_PER_PAGE }) - useEffect(() => { - if (!searchParams.size) { - router.push(`/strategies?page=1&size=${COUNT_PER_PAGE}`) - } - }, [searchParams]) - const strategiesData = data?.strategiesData || [] const totalPages = data?.totalPages || null - const handlePageChange = (page: number) => { - router.push(`/strategies?page=${page}&size=${COUNT_PER_PAGE}`) - } return ( <> <ListHeader /> diff --git a/shared/hooks/custom/use-pagination.ts b/shared/hooks/custom/use-pagination.ts new file mode 100644 index 00000000..8f37ce49 --- /dev/null +++ b/shared/hooks/custom/use-pagination.ts @@ -0,0 +1,36 @@ +'use client' + +import { useEffect } from 'react' + +import { useRouter, useSearchParams } from 'next/navigation' + +interface Props { + basePath: string + pageSize: number +} + +interface UsePaginationReturnModel { + page: number + handlePageChange: (page: number) => void +} + +export const usePagination = ({ basePath, pageSize }: Props): UsePaginationReturnModel => { + const searchParams = useSearchParams() + const router = useRouter() + const page = parseInt(searchParams?.get('page') || '1') + + useEffect(() => { + if (!searchParams.size) { + router.push(`${basePath}?page=1&size=${pageSize}`) + } + }, [searchParams, router, basePath, pageSize]) + + const handlePageChange = (page: number) => { + router.push(`${basePath}?page=${page}&size=${pageSize}`) + } + + return { + page, + handlePageChange, + } +} From 7d889ec69e9e630fa3ebae62a362813b016f39a9 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 28 Nov 2024 23:51:08 +0900 Subject: [PATCH 394/648] =?UTF-8?q?fix:=20=EC=A0=84=EB=9E=B5=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies-item.stories.tsx | 70 ++++++++++--------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx index bf64c563..639c7884 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx @@ -22,13 +22,16 @@ export const Primary = strategy.bind({}) Primary.args = { strategiesData: [ { - strategyId: '123', - strategyName: '리치테크 FuturesDay', - nickname: 'MACS', - stockTypeIconUrls: [], - stockTypeNames: [], + strategyId: 1, + strategyName: 'Dynamic ETF 전략', + traderImgUrl: '/images/trader1.png', + nickname: 'AlphaTrader', + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -39,27 +42,30 @@ Primary.args = { '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', - mdd: '-20,580,856', - smScore: 60.6, - cumulativeProfitLossRate: 120.1, - recentYearProfitLossRate: 30.1, - subscriptionCnt: 23, + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', + mdd: -15432567, + smScore: 72.1, + cumulativeProfitRate: 140.5, + recentYearProfitLossRate: 35.2, + subscriptionCount: 45, + averageRating: 4.9, + totalReviews: 34, isSubscribed: true, - averageRating: 4.8, - totalReview: 12, }, { - strategyId: '12345', - strategyName: 'ETF 레버리지/인버', - nickname: '수밍', - stockTypeIconUrls: [], - stockTypeNames: [], + strategyId: 2, + strategyName: '고수익 ETF', + traderImgUrl: '/images/trader2.png', + nickname: 'BetaTrader', + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, profitRateChartData: { - xAxis: [ + dates: [ '2023-01-01', '2023-01-02', '2023-01-03', @@ -70,18 +76,18 @@ Primary.args = { '2023-01-08', '2023-01-09', ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], + profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], }, - tradeTypeIconUrl: '', - tradeTypeName: '', - mdd: '-20,580,856', - smScore: 60.6, - cumulativeProfitLossRate: 120.1, - recentYearProfitLossRate: 30.1, - subscriptionCnt: 23, + tradeTypeIconUrl: '/images/trade.png', + tradeTypeName: '자동', + mdd: -12786543, + smScore: 65.4, + cumulativeProfitRate: 125.3, + recentYearProfitLossRate: 28.4, + subscriptionCount: 67, + averageRating: 4.6, + totalReviews: 19, isSubscribed: false, - averageRating: 4.8, - totalReview: 12, }, ], } From 875983f4401993674b3e914f75abbf0efafdb0c5 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 09:49:24 +0900 Subject: [PATCH 395/648] =?UTF-8?q?design:=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EB=A1=9C=EA=B3=A0=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95=20(#149)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/logo/styles.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/logo/styles.module.scss b/shared/ui/logo/styles.module.scss index 3c3c453b..0723ec53 100644 --- a/shared/ui/logo/styles.module.scss +++ b/shared/ui/logo/styles.module.scss @@ -11,6 +11,6 @@ .text { width: 142px; - color: $color-gray-500; + color: $color-gray-800; line-height: normal; } From 73f4f3e4ad962d519dcc8b6d73a2ecb440fd22d0 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 09:55:28 +0900 Subject: [PATCH 396/648] =?UTF-8?q?design:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20zIndex=20=EC=88=98=EC=A0=95=20(#149)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_variables.scss | 1 + shared/ui/side-navigation/styles.module.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/shared/styles/base/_variables.scss b/shared/styles/base/_variables.scss index 59b6585b..445e9826 100644 --- a/shared/styles/base/_variables.scss +++ b/shared/styles/base/_variables.scss @@ -61,6 +61,7 @@ $breakpoint-xl: 1200px; /* Z-index */ $z-index: ( modal: 1000, + navbar: 200, header: 100, select: 10, base: 1, diff --git a/shared/ui/side-navigation/styles.module.scss b/shared/ui/side-navigation/styles.module.scss index 131f77ff..231e10f2 100644 --- a/shared/ui/side-navigation/styles.module.scss +++ b/shared/ui/side-navigation/styles.module.scss @@ -9,7 +9,7 @@ border-right: 1px solid $color-gray-200; background-color: $color-gray-100; transition: width 0.2s ease; - z-index: zIndex(base); + z-index: zIndex(navbar); cursor: pointer; From b9d35a229758d59e98df3440779153a474d2213a Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 10:02:26 +0900 Subject: [PATCH 397/648] =?UTF-8?q?design:=20select=20=EC=98=B5=EC=85=98?= =?UTF-8?q?=20=EC=B5=9C=EB=8C=80=20=EB=86=92=EC=9D=B4=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20(#149)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/dropdown/styles.module.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/ui/dropdown/styles.module.scss b/shared/ui/dropdown/styles.module.scss index 5ad792c0..15ccf8da 100644 --- a/shared/ui/dropdown/styles.module.scss +++ b/shared/ui/dropdown/styles.module.scss @@ -82,14 +82,16 @@ $color-selected-text: #171717; border-radius: 4px; background-color: $color-white; box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.25); - overflow: hidden; + overflow-y: auto; &.small { width: $small-width; + max-height: 180px; } &.large { width: $large-width; + max-height: 200px; } } From c4ab54f947dfb8691c817115d3e902cfd29307a8 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 10:03:55 +0900 Subject: [PATCH 398/648] =?UTF-8?q?fix:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20client=EC=84=A0=EC=96=B8=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_ui/search-bar/accordion-button.tsx | 7 ++++--- .../strategies/_ui/search-bar/accordion-panel.tsx | 4 ++++ app/(dashboard)/strategies/_ui/search-bar/index.tsx | 4 ++-- .../strategies/_ui/search-bar/range-container.tsx | 2 ++ .../search-bar/{search-bar-tap.tsx => search-bar-tab.tsx} | 4 ++-- 5 files changed, 14 insertions(+), 7 deletions(-) rename app/(dashboard)/strategies/_ui/search-bar/{search-bar-tap.tsx => search-bar-tab.tsx} (88%) diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx index 73a5db4d..78752772 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx @@ -22,11 +22,12 @@ const AccordionButton = ({ optionId, title, size }: Props) => { const { openIds, handleButtonIds } = useContext(AccordionContext) const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const hasOpenId = openIds?.[optionId] const clickedValue = searchTerms[optionId] return ( - <div className={cx('accordion-button', { active: openIds?.[optionId] })}> - <button onClick={() => handleButtonIds(optionId, !openIds?.[optionId])}> + <div className={cx('accordion-button', { active: hasOpenId })}> + <button onClick={() => handleButtonIds(optionId, !hasOpenId)}> <p> {title} {Array.isArray(clickedValue) && @@ -39,7 +40,7 @@ const AccordionButton = ({ optionId, title, size }: Props) => { <span>(All)</span> ))} </p> - {openIds?.[optionId] ? <CloseIcon /> : <OpenIcon />} + {hasOpenId ? <CloseIcon /> : <OpenIcon />} </button> </div> ) diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx index 9d8fbb29..249f9bad 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx @@ -1,3 +1,5 @@ +'use client' + import { useContext, useEffect, useState } from 'react' import { CheckedCircleIcon } from '@/public/icons' @@ -9,6 +11,8 @@ import { AccordionContext } from './accordion-container' import RangeContainer from './range-container' import styles from './styles.module.scss' +/* eslint-disable react-hooks/exhaustive-deps */ + const cx = classNames.bind(styles) interface Props { diff --git a/app/(dashboard)/strategies/_ui/search-bar/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/index.tsx index b3305a74..6633f981 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/index.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/index.tsx @@ -11,7 +11,7 @@ import useSearchingItemStore from './_store/use-searching-item-store' import { SearchTermsModel } from './_type/search' import AccordionContainer from './accordion-container' import AlgorithmItem from './algorithm-item' -import SearchBarTap from './search-bar-tap' +import SearchBarTab from './search-bar-tab' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -54,7 +54,7 @@ const SearchBarContainer = () => { <SearchInput placeholder="전략명을 검색하세요." /> </div> <div className={cx('searchInput-wrapper')}> - <SearchBarTap isMainTab={isMainTab} onChangeTab={setIsMainTab} /> + <SearchBarTab isMainTab={isMainTab} onChangeTab={setIsMainTab} /> {isMainTab ? ACCORDION_MENU.map((menu) => ( <AccordionContainer diff --git a/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx index 69f97719..f83ea5f5 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx @@ -1,3 +1,5 @@ +'use client' + import classNames from 'classnames/bind' import useSearchingItemStore from './_store/use-searching-item-store' diff --git a/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx b/app/(dashboard)/strategies/_ui/search-bar/search-bar-tab.tsx similarity index 88% rename from app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx rename to app/(dashboard)/strategies/_ui/search-bar/search-bar-tab.tsx index 47f06070..a2c98b6a 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/search-bar-tab.tsx @@ -11,7 +11,7 @@ interface Props { onChangeTab: (isMainTab: boolean) => void } -const SearchBarTap = ({ isMainTab, onChangeTab }: Props) => { +const SearchBarTab = ({ isMainTab, onChangeTab }: Props) => { return ( <div className={cx('tab-container')}> <Button @@ -30,4 +30,4 @@ const SearchBarTap = ({ isMainTab, onChangeTab }: Props) => { ) } -export default SearchBarTap +export default SearchBarTab From 50b225a1ab3b8eddd2b1b7121d9e01e4f862b97b Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 10:20:49 +0900 Subject: [PATCH 399/648] =?UTF-8?q?fix:=20=EC=A0=84=EB=9E=B5=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=AA=A9=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_api/get-strategies.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/strategies/_api/get-strategies.ts b/app/(dashboard)/strategies/_api/get-strategies.ts index b9aeb870..b7b882c5 100644 --- a/app/(dashboard)/strategies/_api/get-strategies.ts +++ b/app/(dashboard)/strategies/_api/get-strategies.ts @@ -13,9 +13,7 @@ const getStrategiesData = async ( try { const response = await axios.get(`/api/strategies?page=${page}&size=${size}`) if (!response.data) { - // 임시 목데이터 console.error('전략 목록 데이터 가져오기 실패') - return { strategiesData: strategiesMockData, totalPages: 2 } } const { content: strategiesData, @@ -25,6 +23,8 @@ const getStrategiesData = async ( return { strategiesData, totalPages } } catch (err) { console.error(err) + // 임시 목데이터 + return { strategiesData: strategiesMockData, totalPages: 2 } } } From 8283c0bc52c71713f406f41108b8df25a9c51756 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 10:22:30 +0900 Subject: [PATCH 400/648] =?UTF-8?q?fix:=20header=20=EB=8C=80=EC=8B=9C?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EB=A7=81=ED=81=AC=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#149)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/_ui/header-links/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/header/_ui/header-links/index.tsx b/shared/ui/header/_ui/header-links/index.tsx index 52658346..1757cebf 100644 --- a/shared/ui/header/_ui/header-links/index.tsx +++ b/shared/ui/header/_ui/header-links/index.tsx @@ -5,7 +5,7 @@ import { LinkButton } from '@/shared/ui/link-button' const HeaderLinks = () => { return ( <Button.ButtonGroup gap="24px"> - <LinkButton href={PATH.HOME} size="small"> + <LinkButton href={PATH.STRATEGIES} size="small"> 대시보드 </LinkButton> <LinkButton href={PATH.SIGN_IN} size="small"> From 639e35ee7a9ec22301e3844cabc59b5e15a7f646 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 10:39:01 +0900 Subject: [PATCH 401/648] =?UTF-8?q?design:=20=EA=B2=80=EC=83=89=EB=B0=94?= =?UTF-8?q?=20position=20=EC=88=98=EC=A0=95=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/[strategyId]/page.tsx | 2 +- app/(dashboard)/strategies/_ui/side-container/index.tsx | 5 +++-- .../strategies/_ui/side-container/styles.module.scss | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index e733e5bf..605d948a 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -34,7 +34,7 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { {detailsInformationData && <DetailsInformation information={detailsInformationData} />} <AnalysisContainer /> <ReviewContainer strategyId={params.strategyId} /> - <SideContainer> + <SideContainer isFixed={true}> <SubscriberItem subscribers={99} /> {hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( diff --git a/app/(dashboard)/strategies/_ui/side-container/index.tsx b/app/(dashboard)/strategies/_ui/side-container/index.tsx index 3bc6e425..59939156 100644 --- a/app/(dashboard)/strategies/_ui/side-container/index.tsx +++ b/app/(dashboard)/strategies/_ui/side-container/index.tsx @@ -5,11 +5,12 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { + isFixed?: boolean children: React.ReactNode } -const SideContainer = ({ children }: Props) => { - return <aside className={cx('side-bar')}>{children}</aside> +const SideContainer = ({ isFixed = false, children }: Props) => { + return <aside className={cx('side-bar', { fixed: isFixed })}>{children}</aside> } export default SideContainer diff --git a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss index 092744ff..160baba4 100644 --- a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss +++ b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss @@ -1,6 +1,9 @@ .side-bar { width: $strategy-sidebar-width; - position: fixed; - right: 28px; + position: absolute; + right: 0px; top: 130px; + &.fixed { + position: fixed; + } } From d054859431760a33940469b03a1451632ad4c881 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 10:49:52 +0900 Subject: [PATCH 402/648] =?UTF-8?q?design:=20fixed=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_ui/side-container/styles.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss index 160baba4..294c8355 100644 --- a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss +++ b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss @@ -5,5 +5,6 @@ top: 130px; &.fixed { position: fixed; + right: 28px; } } From 166d06a3659c374aaac80d0e731bf52e173c7e46 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 29 Nov 2024 12:53:32 +0900 Subject: [PATCH 403/648] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index 2f1204bc..b4eaf01c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -22,13 +22,7 @@ const nextConfig = { { source: '/api/:path*', destination: 'http://15.164.90.102:8081/api/:path*', - has: [ - { - type: 'header', - key: 'host' - } - ] - } + }, ] }, webpack: (config, { isServer }) => { From 2dd9ad91a398f164263a78f56aa0a58e6a64f827 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 29 Nov 2024 12:54:02 +0900 Subject: [PATCH 404/648] =?UTF-8?q?feat:=20axios=20instance=20baseURL=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/axios.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/shared/api/axios.ts b/shared/api/axios.ts index 4de5f879..79c49a4d 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -8,7 +8,17 @@ import { getUserFromToken, isTokenExpired, refreshToken } from '@/shared/utils/t import { isAdmin } from '../types/auth' export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { - const instance = axios.create() + const instance = axios.create({ baseURL: 'http://15.164.90.102:8081' }) + + instance.interceptors.request.use((config) => { + if ( + config.url?.includes('/api/users/login') || + config.url?.includes('/api/users/reissue/refreshtoken') + ) { + config.baseURL = '' + } + return config + }) if (options.withInterceptors && typeof window !== 'undefined') { instance.interceptors.request.use( From b284bd543991aa59951148c8f7466fb0a2603cfb Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 29 Nov 2024 12:54:36 +0900 Subject: [PATCH 405/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/path.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/constants/path.ts b/shared/constants/path.ts index a1b8b98f..3e244fa3 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -20,6 +20,7 @@ export const PATH = { FAVORITES: '/my/favorites', MY_STRATEGIES: '/my/strategies', STRATEGIES_MANAGE: '/my/strategies/manage', + ADD_STRATEGY: '/my/strategies/add', MY_QUESTIONS: '/my/questions', // Admin From 90686060ecf6e3a5ef4e0c5ee1978618eb98b6c1 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 29 Nov 2024 12:55:42 +0900 Subject: [PATCH 406/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94?= =?UTF-8?q?=EB=A6=AC=EC=8B=B1=20(#150)=20-=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-information/index.tsx | 15 +++-- .../_ui/introduction/styles.module.scss | 1 + .../my/_api/get-my-strategy-list.ts | 2 +- .../strategies/manage/[strategyId]/page.tsx | 60 ++++++++++++++++++- .../manage/[strategyId]/styles.module.scss | 17 ++++++ app/(dashboard)/my/strategies/page.tsx | 17 +++++- .../my/strategies/styles.module.scss | 7 +++ .../analysis-container/account-content.tsx | 14 ++++- .../analysis-container/analysis-content.tsx | 27 ++++++++- .../_ui/analysis-container/index.tsx | 49 ++++++++------- .../_ui/analysis-container/styles.module.scss | 26 ++++++++ .../analysis-container/tabs-width-table.tsx | 8 ++- .../strategies/_ui/side-container/index.tsx | 16 ++++- .../_ui/side-container/styles.module.scss | 9 +++ shared/ui/table/vertical/index.tsx | 21 ++++++- shared/ui/table/vertical/styles.module.scss | 19 ++++++ 16 files changed, 268 insertions(+), 40 deletions(-) create mode 100644 app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss diff --git a/app/(dashboard)/_ui/details-information/index.tsx b/app/(dashboard)/_ui/details-information/index.tsx index b1a3f095..54798185 100644 --- a/app/(dashboard)/_ui/details-information/index.tsx +++ b/app/(dashboard)/_ui/details-information/index.tsx @@ -12,9 +12,10 @@ const cx = classNames.bind(styles) interface Props { information: StrategyDetailsInformationModel + type?: 'default' | 'my' } -const DetailsInformation = ({ information }: Props) => { +const DetailsInformation = ({ information, type = 'default' }: Props) => { const percentageToArray = [ { percent: information.cumulativeProfitRate, label: '누적 수익률' }, { percent: information.maxDrawdownRate, label: '최대 자본 인하율' }, @@ -38,11 +39,13 @@ const DetailsInformation = ({ information }: Props) => { /> </div> <StrategyIntroduction content={information.strategyDescription} /> - <div className={cx('percentage-container')}> - {percentageToArray.map((data) => ( - <Percentage key={data.label} percent={data.percent} label={data.label} /> - ))} - </div> + {type === 'default' && ( + <div className={cx('percentage-container')}> + {percentageToArray.map((data) => ( + <Percentage key={data.label} percent={data.percent} label={data.label} /> + ))} + </div> + )} </> ) } diff --git a/app/(dashboard)/_ui/introduction/styles.module.scss b/app/(dashboard)/_ui/introduction/styles.module.scss index 502c9827..773a3369 100644 --- a/app/(dashboard)/_ui/introduction/styles.module.scss +++ b/app/(dashboard)/_ui/introduction/styles.module.scss @@ -3,6 +3,7 @@ background-color: $color-white; border-radius: 5px; padding: 20px; + margin-bottom: 20px; .title { @include typo-b2; margin-bottom: 15px; diff --git a/app/(dashboard)/my/_api/get-my-strategy-list.ts b/app/(dashboard)/my/_api/get-my-strategy-list.ts index 3c072bcc..ca079c0c 100644 --- a/app/(dashboard)/my/_api/get-my-strategy-list.ts +++ b/app/(dashboard)/my/_api/get-my-strategy-list.ts @@ -13,7 +13,7 @@ interface StrategiesResponseModel { export const getMyStrategyList = async ({ page = 1, size = 4 }: { page: number; size: number }) => { const response = await axiosInstance.get<StrategiesResponseModel>( - `/api/my-strategies/page=${page}&size=${size}` + `/api/my-strategies?page=${page}&size=${size}` ) return response.data.result } diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index f3022b99..25d86971 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -1,5 +1,59 @@ -const StrategyManagePage = () => { - return <></> -} +'use client' + +import DetailsInformation from '@/app/(dashboard)/_ui/details-information' +import DetailsSideItem, { + InformationModel, + TitleType, +} from '@/app/(dashboard)/_ui/details-side-item' +import useGetDetailsInformationData from '@/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data' +import AnalysisContainer from '@/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container' +import SubscriberItem from '@/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item' +import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' +import classNames from 'classnames/bind' + +import { useMSWStore } from '@/shared/stores/msw' +import BackHeader from '@/shared/ui/header/back-header' +import Title from '@/shared/ui/title' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) +export type InformationType = { title: TitleType; data: string | number } | InformationModel[] + +const StrategyManagePage = ({ params }: { params: { strategyId: string } }) => { + const isReady = useMSWStore((state) => state.isReady) + const { data } = useGetDetailsInformationData({ + isReady, + strategyId: params.strategyId, + }) + + const { detailsSideData, detailsInformationData } = data || {} + + const hasDetailsSideData = detailsSideData?.map((data) => { + if (!Array.isArray(data)) return data.data !== undefined + }) + + return ( + <> + <BackHeader label={'나의 전략으로 돌아가기'} /> + <Title label={'나의 전략 관리'} /> + <div className={cx('container')}> + {detailsInformationData && ( + <DetailsInformation information={detailsInformationData} type="my" /> + )} + <AnalysisContainer type="my" /> + <SideContainer hasButton={true}> + <SubscriberItem subscribers={99} /> + {hasDetailsSideData?.[0] && + detailsSideData?.map((data, idx) => ( + <div key={`${data}_${idx}`}> + <DetailsSideItem information={data} /> + </div> + ))} + </SideContainer> + </div> + </> + ) +} export default StrategyManagePage diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss b/app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss new file mode 100644 index 00000000..9593e6ea --- /dev/null +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss @@ -0,0 +1,17 @@ +.container { + width: calc(100% - $strategy-sidebar-width); + max-width: $max-width; + padding-right: 10px; + padding-top: 10px; +} + +.wrapper { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: -5px; + button { + width: $strategy-sidebar-width; + height: 35px; + } +} diff --git a/app/(dashboard)/my/strategies/page.tsx b/app/(dashboard)/my/strategies/page.tsx index 7010a8a5..9b5e093c 100644 --- a/app/(dashboard)/my/strategies/page.tsx +++ b/app/(dashboard)/my/strategies/page.tsx @@ -1,7 +1,13 @@ +'use client' + import { Suspense } from 'react' +import { useRouter } from 'next/navigation' + import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' +import { Button } from '@/shared/ui/button' import Title from '@/shared/ui/title' import ListHeader from '../../_ui/list-header' @@ -11,9 +17,18 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const MyStrategiesPage = () => { + const router = useRouter() + const handleClick = () => { + router.push(PATH.ADD_STRATEGY) + } return ( <div className={cx('container')}> - <Title label={'나의 전략'} /> + <div className={cx('wrapper')}> + <Title label={'나의 전략'} /> + <Button size="small" variant="filled" onClick={handleClick}> + 전략 등록하기 + </Button> + </div> <ListHeader type="my" /> <Suspense fallback={<div>Loading...</div>}> <MyStrategyList /> diff --git a/app/(dashboard)/my/strategies/styles.module.scss b/app/(dashboard)/my/strategies/styles.module.scss index f481a8ff..ed53fc58 100644 --- a/app/(dashboard)/my/strategies/styles.module.scss +++ b/app/(dashboard)/my/strategies/styles.module.scss @@ -1,3 +1,10 @@ .container { margin-top: 80px; } + +.wrapper { + display: flex; + justify-content: space-between; + align-items: center; + padding-right: 20px; +} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/account-content.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/account-content.tsx index 5cd51bf4..09718df7 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/account-content.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/account-content.tsx @@ -4,6 +4,7 @@ import Image from 'next/image' import classNames from 'classnames/bind' +import { Button } from '@/shared/ui/button' import Pagination from '@/shared/ui/pagination' import styles from './styles.module.scss' @@ -21,9 +22,10 @@ interface Props { imagesData?: ImageDataModel[] currentPage: number onPageChange: (page: number) => void + isEditable?: boolean } -const AccountContent = ({ imagesData, currentPage, onPageChange }: Props) => { +const AccountContent = ({ imagesData, currentPage, onPageChange, isEditable = false }: Props) => { const croppedImagesData = imagesData?.slice( COUNT_PER_PAGE * (currentPage - 1), COUNT_PER_PAGE * (currentPage - 1) + COUNT_PER_PAGE @@ -33,6 +35,16 @@ const AccountContent = ({ imagesData, currentPage, onPageChange }: Props) => { return ( <div className={cx('table-wrapper')}> + {isEditable && ( + <div className={cx('edit-button-container')}> + <Button size="small" className={cx('edit-button')} variant="filled"> + 실계좌 이미지 업로드 + </Button> + <Button size="small" className={cx('delete-button')} variant="outline"> + 선택 삭제 + </Button> + </div> + )} {croppedImagesData && croppedImagesData.length !== 0 ? ( <> <div className={cx('account-images-container', isTwoLines && 'line')}> diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx index ea5bd137..afd3726e 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx @@ -36,6 +36,7 @@ interface AnalysisContentProps { analysisData: DailyAnalysisModel[] | MonthlyAnalysisModel[] currentPage: number onPageChange: (page: number) => void + isEditable?: boolean } const AnalysisContent = ({ @@ -43,19 +44,39 @@ const AnalysisContent = ({ analysisData, currentPage, onPageChange, + isEditable = false, }: AnalysisContentProps) => { const tableHeader = type === 'daily' ? DAILY_TABLE_HEADER : MONTHLY_TABLE_HEADER return ( <div className={cx('table-wrapper', 'analysis')}> - <Button size="small" className={cx('excel-button')} variant="filled"> - 엑셀 다운받기 - </Button> + {!isEditable && ( + <Button size="small" className={cx('excel-button')} variant="filled"> + 엑셀 다운받기 + </Button> + )} + {isEditable && ( + <div className={cx('button-container')}> + <div className={cx('button-wrapper')}> + <Button size="small" className={cx('upload-button')} variant="filled"> + 엑셀 업로드 + </Button> + <Button size="small" className={cx('upload-button')} variant="outline"> + 직접 입력 + </Button> + </div> + <Button size="small" variant="filled"> + 전체 삭제 + </Button> + </div> + )} + <VerticalTable tableHead={tableHeader} tableBody={analysisData} currentPage={currentPage} countPerPage={COUNT_PER_PAGE} + isEditable={isEditable} /> <Pagination currentPage={currentPage} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx index 44b78ec1..7308bd17 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx @@ -16,7 +16,11 @@ const cx = classNames.bind(styles) type OptionsType = (typeof YAXIS_OPTIONS)[keyof typeof YAXIS_OPTIONS] -const AnalysisContainer = () => { +interface Props { + type?: 'default' | 'my' +} + +const AnalysisContainer = ({ type = 'default' }: Props) => { const [firstValue, setFirstValue] = useState<OptionsType>('잔고') const [secondValue, setSecondValue] = useState<OptionsType>('잔고') @@ -30,27 +34,30 @@ const AnalysisContainer = () => { return ( <div className={cx('container')}> <div className={cx('analysis-header')}> - <p>분석</p> - <div> - <Select - size="large" - options={options} - value={firstValue} - onChange={(newValue) => setFirstValue(newValue as OptionsType)} - /> - <Select - size="large" - options={options} - value={secondValue} - onChange={(newValue) => setSecondValue(newValue as OptionsType)} - /> - </div> + <p className={cx({ my: type === 'my' })}>분석</p> + {type === 'default' && ( + <div> + <Select + size="large" + options={options} + value={firstValue} + onChange={(newValue) => setFirstValue(newValue as OptionsType)} + /> + <Select + size="large" + options={options} + value={secondValue} + onChange={(newValue) => setSecondValue(newValue as OptionsType)} + /> + </div> + )} </div> - <div className={cx('chart-wrapper')}> - <AnalysisChart analysisChartData={analysisChartData} /> - </div> - - <TabsWithTable /> + {type === 'default' && ( + <div className={cx('chart-wrapper')}> + <AnalysisChart analysisChartData={analysisChartData} /> + </div> + )} + <TabsWithTable isEditable={type === 'my' ? true : false} /> </div> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss index c538dcd5..c46c5823 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss @@ -8,6 +8,9 @@ p { @include typo-h4; color: $color-gray-600; + &.my { + margin-bottom: 40px; + } } div { display: flex; @@ -26,6 +29,15 @@ .table-wrapper { margin-top: 20px; position: relative; + .edit-button-container { + display: flex; + justify-content: space-between; + + .edit-button, + .delete-button { + padding: 7px 18px; + } + } &.analysis { margin-top: 40px; } @@ -73,3 +85,17 @@ color: $color-gray-600; @include typo-b2; } + +.button-container { + display: flex; + justify-content: space-between; + height: 30px; + margin-top: -20px; + margin-bottom: 10px; + + button.upload-button { + height: 100%; + padding: 7px 18px; + margin-right: 10px; + } +} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx index 38cb6fcf..0586aba8 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx @@ -16,7 +16,11 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -const TabsWithTable = () => { +interface Props { + isEditable?: boolean +} + +const TabsWithTable = ({ isEditable = false }: Props) => { const [activeTab, setActiveTab] = useState('statistics') const [currentPage, setCurrentPage] = useState(1) @@ -43,6 +47,7 @@ const TabsWithTable = () => { analysisData={tableBody} currentPage={currentPage} onPageChange={handlePageChange} + isEditable={isEditable} /> ), }, @@ -69,6 +74,7 @@ const TabsWithTable = () => { imagesData={[]} currentPage={currentPage} onPageChange={handlePageChange} + isEditable={isEditable} /> </div> ), diff --git a/app/(dashboard)/strategies/_ui/side-container/index.tsx b/app/(dashboard)/strategies/_ui/side-container/index.tsx index 3bc6e425..a2b59437 100644 --- a/app/(dashboard)/strategies/_ui/side-container/index.tsx +++ b/app/(dashboard)/strategies/_ui/side-container/index.tsx @@ -1,15 +1,27 @@ import classNames from 'classnames/bind' +import { Button } from '@/shared/ui/button' + import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { children: React.ReactNode + hasButton?: boolean } -const SideContainer = ({ children }: Props) => { - return <aside className={cx('side-bar')}>{children}</aside> +const SideContainer = ({ children, hasButton = false }: Props) => { + return ( + <aside className={cx('side-bar', { 'has-button': hasButton })}> + {hasButton && ( + <Button size="small" variant="filled" className={cx('edit-button')}> + 정보 수정하기 + </Button> + )} + {children} + </aside> + ) } export default SideContainer diff --git a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss index 092744ff..567d3654 100644 --- a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss +++ b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss @@ -3,4 +3,13 @@ position: fixed; right: 28px; top: 130px; + &.has-button { + top: 85px; + } + + .edit-button { + width: $strategy-sidebar-width; + height: 40px; + margin-bottom: 15px; + } } diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 580adee3..3fc262f0 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -1,6 +1,7 @@ import classNames from 'classnames/bind' import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' +import { Button } from '@/shared/ui/button' import styles from './styles.module.scss' @@ -13,9 +14,16 @@ interface Props { tableBody: TableBodyDataType[] countPerPage: number currentPage: number + isEditable?: boolean } -const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Props) => { +const VerticalTable = ({ + tableHead, + tableBody, + countPerPage, + currentPage, + isEditable = false, +}: Props) => { const croppedTableBody = tableBody.slice( countPerPage * (currentPage - 1), countPerPage * (currentPage - 1) + countPerPage @@ -29,6 +37,7 @@ const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Prop {tableHead.map((head) => ( <td key={head}>{head}</td> ))} + {isEditable && <td></td>} </tr> </thead> <tbody> @@ -37,6 +46,16 @@ const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Prop {Object.entries(row).map((rowData, idx) => ( <td key={idx}>{rowData[1]}</td> ))} + {isEditable && ( + <td className={cx('button-container')}> + <Button size="small" variant="outline" className={cx('edit-button')}> + 수정 + </Button> + <Button size="small" variant="filled" className={cx('delete-button')}> + 삭제 + </Button> + </td> + )} </tr> ))} </tbody> diff --git a/shared/ui/table/vertical/styles.module.scss b/shared/ui/table/vertical/styles.module.scss index cbc413ca..9ac3f8e5 100644 --- a/shared/ui/table/vertical/styles.module.scss +++ b/shared/ui/table/vertical/styles.module.scss @@ -1,12 +1,15 @@ .container { width: 100%; padding: 20px; + table { width: 100%; font-size: $text-c1; + thead { background-color: $color-gray-100; } + td { padding: 0 20px; height: 40px; @@ -15,5 +18,21 @@ border-top: 1px solid $color-gray-200; border-bottom: 1px solid $color-gray-200; } + + .button-container { + padding: 0; + text-align: right; + } + + .edit-button, + .delete-button { + min-width: 60px; + height: 24px; + padding: 7px 16px; + } + .edit-button { + border: 1px solid $color-gray-300; + margin-right: 8px; + } } } From 9103be2d422a361394ead0ba87413f4862d672e4 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 29 Nov 2024 13:09:47 +0900 Subject: [PATCH 407/648] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=ED=9B=84?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx | 2 +- .../strategies/_ui/side-container/styles.module.scss | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index 25d86971..34e4d0b2 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -43,7 +43,7 @@ const StrategyManagePage = ({ params }: { params: { strategyId: string } }) => { <DetailsInformation information={detailsInformationData} type="my" /> )} <AnalysisContainer type="my" /> - <SideContainer hasButton={true}> + <SideContainer hasButton={true} isFixed={true}> <SubscriberItem subscribers={99} /> {hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( diff --git a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss index ff1b5a66..feabb494 100644 --- a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss +++ b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss @@ -3,7 +3,7 @@ position: absolute; right: 0px; top: 130px; - + &.has-button { top: 85px; } @@ -12,7 +12,8 @@ width: $strategy-sidebar-width; height: 40px; margin-bottom: 15px; - + } + &.fixed { position: fixed; right: 28px; From 2f6f47e3870d4a09ffd3e426e595602fecfb41d7 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 29 Nov 2024 14:19:07 +0900 Subject: [PATCH 408/648] =?UTF-8?q?feat:=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=82=B4=EC=9D=98=20?= =?UTF-8?q?=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC=20(#111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/table/vertical/index.tsx | 7 ++++--- shared/utils/slice-array.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 shared/utils/slice-array.ts diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index e5b1aad4..8e658582 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -4,8 +4,8 @@ import { ReactNode } from 'react' import classNames from 'classnames/bind' import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' +import sliceArray from '@/shared/utils/slice-array' -import useVerticalTable from './hooks/use-vertical-table' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -23,7 +23,8 @@ export interface VerticalTableProps { } const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: VerticalTableProps) => { - const { hasData, croppedTableBody } = useVerticalTable({ tableBody, countPerPage, currentPage }) + const hasData = tableBody.length > 0 + const slicedTableBody = sliceArray(tableBody, countPerPage, currentPage) return ( <div className={cx('container')}> @@ -37,7 +38,7 @@ const VerticalTable = ({ tableHead, tableBody, countPerPage, currentPage }: Vert </thead> {hasData && ( <tbody> - {croppedTableBody.map((row) => ( + {slicedTableBody.map((row) => ( <tr key={Object.values(row)[0]}> {Object.values(row).map((data, idx) => ( <td key={data + idx}>{data}</td> diff --git a/shared/utils/slice-array.ts b/shared/utils/slice-array.ts new file mode 100644 index 00000000..9e96d6f8 --- /dev/null +++ b/shared/utils/slice-array.ts @@ -0,0 +1,8 @@ +const sliceArray = <T>(array: T[], countPerPage: number, currentPage: number) => { + return array.slice( + countPerPage * (currentPage - 1), + countPerPage * (currentPage - 1) + countPerPage + ) +} + +export default sliceArray From 0d8485e082a93f21b222c5b82d768a66cfaee4f1 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 29 Nov 2024 14:56:10 +0900 Subject: [PATCH 409/648] =?UTF-8?q?feat:=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=9B=85=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?(#111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vertical/hooks/use-vertical-table.ts | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 shared/ui/table/vertical/hooks/use-vertical-table.ts diff --git a/shared/ui/table/vertical/hooks/use-vertical-table.ts b/shared/ui/table/vertical/hooks/use-vertical-table.ts deleted file mode 100644 index 9661e9de..00000000 --- a/shared/ui/table/vertical/hooks/use-vertical-table.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { VerticalTableProps } from '..' - -type ArgsType = Pick<VerticalTableProps, 'tableBody' | 'countPerPage' | 'currentPage'> - -const useVerticalTable = ({ tableBody, countPerPage, currentPage }: ArgsType) => { - const hasData = tableBody.length > 0 - - const croppedTableBody = tableBody.slice( - countPerPage * (currentPage - 1), - countPerPage * (currentPage - 1) + countPerPage - ) - - return { - hasData, - croppedTableBody, - } -} - -export default useVerticalTable From 2ae09aacbf4d5f3aa5a2e2ba702b108f51cd438c Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 29 Nov 2024 15:02:58 +0900 Subject: [PATCH 410/648] =?UTF-8?q?feat:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EB=B0=B0=ED=8F=AC=20workflow=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=20(#158)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/chromatic.yml | 38 --------------------------------- README.md | 11 ++++++++++ 2 files changed, 11 insertions(+), 38 deletions(-) delete mode 100644 .github/workflows/chromatic.yml diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml deleted file mode 100644 index fce223c4..00000000 --- a/.github/workflows/chromatic.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: 'Chromatic' -on: - pull_request: -jobs: - chromatic: - name: Run Chromatic - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: cache dependencies - id: cache - uses: actions/cache@v4 - with: - path: '**/node_modules' - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-storybook - - - name: Install dependencies - run: npm ci - - - name: Publish to Chromatic - id: chromatic - uses: chromaui/action@latest - with: - projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} - - - name: comment on PR - uses: thollander/actions-comment-pull-request@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - message: '🚀 Storybook이 배포 됐어요! 확인하러 가기 => ${{ steps.chromatic.outputs.storybookUrl }}' diff --git a/README.md b/README.md index b798611d..488fbc69 100644 --- a/README.md +++ b/README.md @@ -1 +1,12 @@ # InvestMetic + +<p align="center"> + <a href="https://6729e72ee61d8f57ca4790fb-aepxabjrdg.chromatic.com/"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/263385/199832481-bbbf5961-6a26-481d-8224-51258cce9b33.png"> + <img src="https://user-images.githubusercontent.com/321738/63501763-88dbf600-c4cc-11e9-96cd-94adadc2fd72.png" alt="Storybook" width="400" /> + </picture> + + </a> + +</p> From 8084d466c7d86da2b31cd90cab473db82e98c8f5 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 29 Nov 2024 15:03:30 +0900 Subject: [PATCH 411/648] =?UTF-8?q?feat:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EB=B0=B0=ED=8F=AC=20script=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#158)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ba32b40..e6a61fa9 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "prettier": "prettier --write .", "storybook": "storybook dev -p 6006", "type-check": "tsc --noEmit -p ./tsconfig.json", - "build-storybook": "storybook build" + "build-storybook": "storybook build", + "deploy-storybook": "npx chromatic --project-token $CHROMATIC_PROJECT_TOKEN" }, "dependencies": { "@tanstack/react-query": "^5.59.19", From 6ce1f505e04888f838048e136127226d7978f54a Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 29 Nov 2024 15:56:17 +0900 Subject: [PATCH 412/648] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#150)=20-=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=EB=B0=94=EC=97=90=EC=84=9C=20isFixed=20pr?= =?UTF-8?q?op=20=EC=82=AD=EC=A0=9C=20-=20next.config.mjs=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/manage/[strategyId]/page.tsx | 16 +++++++--- .../manage/[strategyId]/styles.module.scss | 21 ++++++------ .../strategies/[strategyId]/page.tsx | 2 +- .../strategies/_ui/side-container/index.tsx | 16 ++-------- .../_ui/side-container/styles.module.scss | 15 --------- next.config.mjs | 32 +++++++++---------- 6 files changed, 42 insertions(+), 60 deletions(-) diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index 34e4d0b2..74049332 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -12,6 +12,7 @@ import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' import classNames from 'classnames/bind' import { useMSWStore } from '@/shared/stores/msw' +import { Button } from '@/shared/ui/button' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' @@ -35,15 +36,20 @@ const StrategyManagePage = ({ params }: { params: { strategyId: string } }) => { }) return ( - <> + <div className={cx('container')}> <BackHeader label={'나의 전략으로 돌아가기'} /> - <Title label={'나의 전략 관리'} /> - <div className={cx('container')}> + <div className={cx('header')}> + <Title label={'나의 전략 관리'} /> + <Button size="small" variant="filled" className={cx('edit-button')}> + 정보 수정하기 + </Button> + </div> + <div className={cx('strategy-container')}> {detailsInformationData && ( <DetailsInformation information={detailsInformationData} type="my" /> )} <AnalysisContainer type="my" /> - <SideContainer hasButton={true} isFixed={true}> + <SideContainer hasButton={true}> <SubscriberItem subscribers={99} /> {hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( @@ -53,7 +59,7 @@ const StrategyManagePage = ({ params }: { params: { strategyId: string } }) => { ))} </SideContainer> </div> - </> + </div> ) } export default StrategyManagePage diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss b/app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss index 9593e6ea..77611d8a 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/styles.module.scss @@ -1,17 +1,20 @@ .container { - width: calc(100% - $strategy-sidebar-width); - max-width: $max-width; - padding-right: 10px; - padding-top: 10px; + position: relative; } -.wrapper { +.header { display: flex; justify-content: space-between; - align-items: center; - margin-bottom: -5px; - button { + + .edit-button { width: $strategy-sidebar-width; - height: 35px; + height: 40px; } } + +.strategy-container { + width: calc(100% - $strategy-sidebar-width); + max-width: $max-width; + padding-right: 10px; + margin-top: -10px; +} diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 605d948a..e733e5bf 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -34,7 +34,7 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { {detailsInformationData && <DetailsInformation information={detailsInformationData} />} <AnalysisContainer /> <ReviewContainer strategyId={params.strategyId} /> - <SideContainer isFixed={true}> + <SideContainer> <SubscriberItem subscribers={99} /> {hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( diff --git a/app/(dashboard)/strategies/_ui/side-container/index.tsx b/app/(dashboard)/strategies/_ui/side-container/index.tsx index 2c80d431..394d5e06 100644 --- a/app/(dashboard)/strategies/_ui/side-container/index.tsx +++ b/app/(dashboard)/strategies/_ui/side-container/index.tsx @@ -1,28 +1,16 @@ import classNames from 'classnames/bind' -import { Button } from '@/shared/ui/button' - import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - isFixed?: boolean children: React.ReactNode hasButton?: boolean } -const SideContainer = ({ children, isFixed = false, hasButton = false }: Props) => { - return ( - <aside className={cx('side-bar', { fixed: isFixed, 'has-button': hasButton })}> - {hasButton && ( - <Button size="small" variant="filled" className={cx('edit-button')}> - 정보 수정하기 - </Button> - )} - {children} - </aside> - ) +const SideContainer = ({ children }: Props) => { + return <aside className={cx('side-bar')}>{children}</aside> } export default SideContainer diff --git a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss index feabb494..9a600262 100644 --- a/app/(dashboard)/strategies/_ui/side-container/styles.module.scss +++ b/app/(dashboard)/strategies/_ui/side-container/styles.module.scss @@ -3,19 +3,4 @@ position: absolute; right: 0px; top: 130px; - - &.has-button { - top: 85px; - } - - .edit-button { - width: $strategy-sidebar-width; - height: 40px; - margin-bottom: 15px; - } - - &.fixed { - position: fixed; - right: 28px; - } } diff --git a/next.config.mjs b/next.config.mjs index b4eaf01c..969da5ce 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -9,22 +9,22 @@ const nextConfig = { @import "@/shared/styles/base/functions"; `, }, - async rewrites() { - return [ - { - source: '/api/users/reissue/refreshtoken', - destination: '/api/users/reissue/refreshtoken', - }, - { - source: '/api/users/login', - destination: '/api/users/login', - }, - { - source: '/api/:path*', - destination: 'http://15.164.90.102:8081/api/:path*', - }, - ] - }, + // async rewrites() { + // return [ + // { + // source: '/api/users/reissue/refreshtoken', + // destination: '/api/users/reissue/refreshtoken', + // }, + // { + // source: '/api/users/login', + // destination: '/api/users/login', + // }, + // { + // source: '/api/:path*', + // destination: 'http://15.164.90.102:8081/api/:path*', + // }, + // ] + // }, webpack: (config, { isServer }) => { if (isServer) { if (Array.isArray(config.resolve.alias)) { From ac36fa12098157d9ef0405c70004f8b04c274cf2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 16:22:38 +0900 Subject: [PATCH 413/648] =?UTF-8?q?rename:=20=EC=A0=84=EB=9E=B5=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B2=94=EC=9A=A9=EC=A0=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=EC=88=98=EC=A0=95=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/area-chart.tsx | 2 +- app/(dashboard)/_ui/strategies-item/index.tsx | 2 +- .../strategies-item.stories.tsx | 2 +- .../my/_api/get-favorite-strategy-list.ts | 2 +- .../my/_api/get-my-strategy-list.ts | 2 +- .../_hooks/query/use-get-my-strategy-list.ts | 2 +- .../analysis-container/analysis-content.tsx | 2 +- .../strategies/_api/get-strategies.ts | 2 +- mocks/handlers/strategies.ts | 2 +- ...ategy-details-data.ts => strategy-data.ts} | 32 +++++++++---------- shared/ui/table/vertical/index.tsx | 3 +- 11 files changed, 25 insertions(+), 28 deletions(-) rename shared/types/{strategy-details-data.ts => strategy-data.ts} (77%) diff --git a/app/(dashboard)/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/_ui/strategies-item/area-chart.tsx index ff6f106c..7f3c04de 100644 --- a/app/(dashboard)/_ui/strategies-item/area-chart.tsx +++ b/app/(dashboard)/_ui/strategies-item/area-chart.tsx @@ -5,7 +5,7 @@ import dynamic from 'next/dynamic' import classNames from 'classnames/bind' import Highcharts from 'highcharts' -import { ProfitRateChartDataModel } from '@/shared/types/strategy-details-data' +import { ProfitRateChartDataModel } from '@/shared/types/strategy-data' import styles from './styles.module.scss' diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 8053e335..8952d938 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -2,7 +2,7 @@ import Link from 'next/link' import classNames from 'classnames/bind' -import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { StrategiesModel } from '@/shared/types/strategy-data' import AreaChart from './area-chart' import StrategiesSummary from './strategies-summary' diff --git a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx index 639c7884..7211a056 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryFn } from '@storybook/react' -import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { StrategiesModel } from '@/shared/types/strategy-data' import StrategiesItem from './index' diff --git a/app/(dashboard)/my/_api/get-favorite-strategy-list.ts b/app/(dashboard)/my/_api/get-favorite-strategy-list.ts index b30fb6d9..a25ef50d 100644 --- a/app/(dashboard)/my/_api/get-favorite-strategy-list.ts +++ b/app/(dashboard)/my/_api/get-favorite-strategy-list.ts @@ -1,7 +1,7 @@ import { strategiesMockData } from '@/mocks/handlers/strategies' import axiosInstance from '@/shared/api/axios' -import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { StrategiesModel } from '@/shared/types/strategy-data' interface Props { page: number diff --git a/app/(dashboard)/my/_api/get-my-strategy-list.ts b/app/(dashboard)/my/_api/get-my-strategy-list.ts index ca079c0c..fb7608af 100644 --- a/app/(dashboard)/my/_api/get-my-strategy-list.ts +++ b/app/(dashboard)/my/_api/get-my-strategy-list.ts @@ -1,5 +1,5 @@ import axiosInstance from '@/shared/api/axios' -import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { StrategiesModel } from '@/shared/types/strategy-data' // 실제 api 나오면 수정 필요함 // totalElements 사용해서 hasmore값 계산해야될 것 같음 diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts index 8b87cc7c..f0f096e2 100644 --- a/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts +++ b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts @@ -1,7 +1,7 @@ import { getMyStrategyList } from '@/app/(dashboard)/my/_api/get-my-strategy-list' import { useInfiniteQuery } from '@tanstack/react-query' -import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { StrategiesModel } from '@/shared/types/strategy-data' interface StrategiesPageModel { strategies: StrategiesModel[] diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx index afd3726e..a0966522 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames/bind' -import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' +import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-data' import { Button } from '@/shared/ui/button' import Pagination from '@/shared/ui/pagination' import VerticalTable from '@/shared/ui/table/vertical' diff --git a/app/(dashboard)/strategies/_api/get-strategies.ts b/app/(dashboard)/strategies/_api/get-strategies.ts index b7b882c5..7bcf855e 100644 --- a/app/(dashboard)/strategies/_api/get-strategies.ts +++ b/app/(dashboard)/strategies/_api/get-strategies.ts @@ -1,7 +1,7 @@ import { strategiesMockData } from '@/mocks/handlers/strategies' import axios from 'axios' -import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { StrategiesModel } from '@/shared/types/strategy-data' const getStrategiesData = async ( isReady: boolean, diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts index 73c75794..4d0ed4b5 100644 --- a/mocks/handlers/strategies.ts +++ b/mocks/handlers/strategies.ts @@ -1,6 +1,6 @@ import { HttpResponse, http } from 'msw' -import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { StrategiesModel } from '@/shared/types/strategy-data' export const strategiesMockData: StrategiesModel[] = [ { diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-data.ts similarity index 77% rename from shared/types/strategy-details-data.ts rename to shared/types/strategy-data.ts index 10a42157..e8da9f71 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-data.ts @@ -24,10 +24,9 @@ export interface ProfitRateChartDataModel { profitRates: number[] } -export interface StrategiesModel { +interface BaseStrategyModel { strategyId: number strategyName: string - traderImgUrl?: string nickname: string stockTypeInfo: { stockTypeIconUrls: string[] @@ -35,32 +34,31 @@ export interface StrategiesModel { } tradeTypeIconUrl: string tradeTypeName: string + cumulativeProfitRate: number + smScore: number + subscriptionCount: number + isSubscribed: boolean + traderImgUrl?: string +} + +export interface StrategiesModel extends BaseStrategyModel { profitRateChartData: ProfitRateChartDataModel mdd: number - smScore: number - cumulativeProfitRate: number recentYearProfitLossRate?: number - subscriptionCount: number averageRating: number totalReviews: number - isSubscribed: boolean } -export interface StrategyDetailsInformationModel { - strategyId: string - strategyName: string - stockTypeIconUrls: string[] - tradeTypeIconUrl: string - stockTypeNames: string[] - tradeTypeName: string +export interface StrategyDetailsInformationModel extends BaseStrategyModel { operationCycle: string strategyDescription: string - cumulativeProfitRate: number maxDrawdownRate: number averageProfitLossRate: number profitFactor: number winRate: number - subscriptionCount: number - traderImgUrl: string - subscribed: boolean + minimumInvestmentAmount: string + initialInvestment: number | string + kpRatio: number + finalProfitLossDate: string + createdAt: string } diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 2f4456fc..785e61fc 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -3,7 +3,7 @@ import { ReactNode } from 'react' import classNames from 'classnames/bind' -import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-details-data' +import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-data' import { Button } from '@/shared/ui/button' import sliceArray from '@/shared/utils/slice-array' @@ -77,4 +77,3 @@ const VerticalTable = ({ } export default VerticalTable - From e28baf79851b36323cb640e1a60a5e97244c5427 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 16:24:07 +0900 Subject: [PATCH 414/648] =?UTF-8?q?fix:=20api=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EB=AA=A9=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=B0=8F=20props=20=EC=88=98=EC=A0=95=20(?= =?UTF-8?q?#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-information/index.tsx | 8 +-- .../_api/get-details-information.ts | 46 ++++++++----- mocks/handlers/strategy-details.ts | 64 +++++++++++-------- 3 files changed, 72 insertions(+), 46 deletions(-) diff --git a/app/(dashboard)/_ui/details-information/index.tsx b/app/(dashboard)/_ui/details-information/index.tsx index 54798185..9225357b 100644 --- a/app/(dashboard)/_ui/details-information/index.tsx +++ b/app/(dashboard)/_ui/details-information/index.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames/bind' -import { StrategyDetailsInformationModel } from '@/shared/types/strategy-details-data' +import { StrategyDetailsInformationModel } from '@/shared/types/strategy-data' import StrategyIntroduction from '../introduction' import InvestInformation from './invest-information' @@ -28,12 +28,12 @@ const DetailsInformation = ({ information, type = 'default' }: Props) => { <> <div className={cx('information-top')}> <StrategyNameBox - iconUrls={[information.tradeTypeIconUrl, ...information.stockTypeIconUrls]} - iconNames={[information.tradeTypeName, ...information.stockTypeNames]} + iconUrls={[information.tradeTypeIconUrl, ...information.stockTypeInfo.stockTypeIconUrls]} + iconNames={[information.tradeTypeName, ...information.stockTypeInfo.stockTypeNames]} name={information.strategyName} /> <InvestInformation - stock={information.stockTypeNames} + stock={information.stockTypeInfo.stockTypeNames} trade={information.tradeTypeName} cycle={information.operationCycle} /> diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index feecbfa3..f17e537a 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -1,8 +1,9 @@ +import { strategiesDetailsInformationMockData } from '@/mocks/handlers/strategy-details' import axios from 'axios' import { InformationType } from '../page' -const getDetailsInformation = async (isReady: boolean, strategyId: string) => { +const getDetailsInformation = async (isReady: boolean, strategyId: number) => { if (!isReady || !strategyId) return try { @@ -28,26 +29,37 @@ const getDetailsInformation = async (isReady: boolean, strategyId: string) => { ], ] const detailsInformationData = { - strategyId: data.strategyId, - strategyName: data.strategyName, - stockTypeIconUrls: data.stockTypeIconUrls, - tradeTypeIconUrl: data.tradeTypeIconUrl, - stockTypeNames: data.stockTypeNames, - tradeTypeName: data.tradeTypeName, - operationCycle: data.operationCycle, - strategyDescription: data.strategyDescription, - cumulativeProfitRate: data.cumulativeProfitRate, - maxDrawdownRate: data.maxDrawdownRate, - averageProfitLossRate: data.averageProfitLossRate, - profitFactor: data.profitFactor, - winRate: data.winRate, - subscriptionCount: data.subscriptionCount, - traderImgUrl: data.traderImgUrl, - subscribed: data.subscribed, + ...data, } return { detailsSideData, detailsInformationData } } catch (err) { console.error(err) + // 임시 데이터 + const detailsSideData: InformationType[] = [ + { title: '트레이더', data: strategiesDetailsInformationMockData[0].nickname }, + { + title: '최소 투자 금액', + data: strategiesDetailsInformationMockData[0].minimumInvestmentAmount, + }, + { title: '투자 원금', data: strategiesDetailsInformationMockData[0].initialInvestment }, + + [ + { title: 'KP Ratio', data: strategiesDetailsInformationMockData[0].kpRatio }, + { title: 'SM SCORE', data: strategiesDetailsInformationMockData[0].smScore }, + ], + + [ + { + title: '최종손익입력일자', + data: strategiesDetailsInformationMockData[0].finalProfitLossDate, + }, + { title: '등록일', data: strategiesDetailsInformationMockData[0].createdAt }, + ], + ] + const detailsInformationData = { + ...strategiesDetailsInformationMockData[0], + } + return { detailsSideData, detailsInformationData } } } diff --git a/mocks/handlers/strategy-details.ts b/mocks/handlers/strategy-details.ts index 7ad5cdbe..7c627830 100644 --- a/mocks/handlers/strategy-details.ts +++ b/mocks/handlers/strategy-details.ts @@ -1,11 +1,15 @@ import { HttpResponse, http } from 'msw' -const data = [ +import { StrategyDetailsInformationModel } from '@/shared/types/strategy-data' + +export const strategiesDetailsInformationMockData: StrategyDetailsInformationModel[] = [ { - strategyId: '1', + strategyId: 1, strategyName: 'Dynamic ETF 전략', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', operationCycle: 'DAY', @@ -24,13 +28,15 @@ const data = [ smScore: 72.1, finalProfitLossDate: '2024.03.11', createdAt: '2024.01.11', - subscribed: true, + isSubscribed: true, }, { - strategyId: '2', + strategyId: 2, strategyName: '고수익 ETF', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', operationCycle: 'DAY', @@ -49,13 +55,15 @@ const data = [ smScore: 65.4, finalProfitLossDate: '2024.03.11', createdAt: '2024.01.11', - subscribed: true, + isSubscribed: true, }, { - strategyId: '3', + strategyId: 3, strategyName: 'Futures Pro', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', operationCycle: 'DAY', @@ -74,13 +82,15 @@ const data = [ smScore: 79.6, finalProfitLossDate: '2024.03.11', createdAt: '2024.01.11', - subscribed: true, + isSubscribed: true, }, { - strategyId: '4', + strategyId: 4, strategyName: '월별 수익 전략', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', operationCycle: 'DAY', @@ -99,13 +109,15 @@ const data = [ smScore: 68.4, finalProfitLossDate: '2024.03.11', createdAt: '2024.01.11', - subscribed: true, + isSubscribed: true, }, { - strategyId: '5', + strategyId: 5, strategyName: '리스크 관리 전략', - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], + stockTypeInfo: { + stockTypeIconUrls: ['/images/stock.png'], + stockTypeNames: ['해외지수선물'], + }, tradeTypeIconUrl: '/images/trade.png', tradeTypeName: '자동', operationCycle: 'DAY', @@ -124,13 +136,13 @@ const data = [ smScore: 62.3, finalProfitLossDate: '2024.03.11', createdAt: '2024.01.11', - subscribed: true, + isSubscribed: true, }, ] const reviewData = [ { - strategyId: '1', + strategyId: 1, averageRating: 3.8, reviewCount: 4, reviews: { @@ -203,7 +215,7 @@ const reviewData = [ }, }, { - strategyId: '2', + strategyId: 2, averageRating: 3.8, reviewCount: 4, reviews: { @@ -253,7 +265,9 @@ const reviewData = [ export const strategyDetailsHandlers = [ http.get('/api/strategies/:strategyId', ({ params }) => { const { strategyId } = params - const strategyData = data.find((item) => item.strategyId === strategyId) + const strategyData = strategiesDetailsInformationMockData.find( + (item) => item.strategyId === Number(strategyId) + ) if (strategyData) { return HttpResponse.json({ ...strategyData }) } @@ -273,7 +287,7 @@ export const strategyDetailsHandlers = [ const page = parseInt(url.searchParams.get('page') || '1') const size = parseInt(url.searchParams.get('size') || '4') - const reviewDataForStrategy = reviewData.find((item) => item.strategyId === strategyId) + const reviewDataForStrategy = reviewData.find((item) => item.strategyId === Number(strategyId)) if (!isNaN(page) && !isNaN(size) && reviewDataForStrategy) { const { content, totalElements } = reviewDataForStrategy.reviews const slicedContent = content.slice(size * (page - 1), size * (page - 1) + size) From 5ad126cd2c90a21c2c2efd55af4d5e99087b6b95 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 16:25:11 +0900 Subject: [PATCH 415/648] =?UTF-8?q?fix:=20primary=20id=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=88=98=EC=A0=95=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx | 2 +- app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts | 2 +- .../_hooks/query/use-get-details-information-data.ts | 4 ++-- .../[strategyId]/_hooks/query/use-get-reviews-data.ts | 2 +- .../strategies/[strategyId]/_ui/review-container/index.tsx | 2 +- app/(dashboard)/strategies/[strategyId]/page.tsx | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index 74049332..a10e5094 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -22,7 +22,7 @@ const cx = classNames.bind(styles) export type InformationType = { title: TitleType; data: string | number } | InformationModel[] -const StrategyManagePage = ({ params }: { params: { strategyId: string } }) => { +const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { const isReady = useMSWStore((state) => state.isReady) const { data } = useGetDetailsInformationData({ isReady, diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts index bdde39b8..50519dad 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { COUNT_PER_PAGE } from '../_ui/review-container/review-list' -const getReviews = async (strategyId: string, page: number | undefined) => { +const getReviews = async (strategyId: number, page: number | undefined) => { if (!strategyId && !page) return try { diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts index 419646cf..0d0ebe35 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts @@ -1,13 +1,13 @@ import { useQuery } from '@tanstack/react-query' -import { StrategyDetailsInformationModel } from '@/shared/types/strategy-details-data' +import { StrategyDetailsInformationModel } from '@/shared/types/strategy-data' import getDetailsInformation from '../../_api/get-details-information' import { InformationType } from '../../page' interface Props { isReady: boolean - strategyId: string + strategyId: number } const useGetDetailsInformationData = ({ diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts index b2f7f6ed..48431ef0 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts @@ -4,7 +4,7 @@ import getReviews from '../../_api/get-reviews' interface Props { isReady: boolean - strategyId: string + strategyId: number page: number | undefined } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx index b8a8a437..5e4e926d 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx @@ -15,7 +15,7 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - strategyId: string + strategyId: number } const ReviewContainer = ({ strategyId }: Props) => { diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index e733e5bf..f09d9875 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -14,7 +14,7 @@ import SubscriberItem from './_ui/subscriber-item' export type InformationType = { title: TitleType; data: string | number } | InformationModel[] -const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { +const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { const isReady = useMSWStore((state) => state.isReady) const { data } = useGetDetailsInformationData({ isReady, From 5b5467c6cad00f45bae1544fefa2956073aae389 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 16:40:20 +0900 Subject: [PATCH 416/648] =?UTF-8?q?fix:=20size=EB=8F=84=20searchParams?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_ui/strategy-list/index.tsx | 4 ++-- shared/hooks/custom/use-pagination.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index f7a65019..1e628dd6 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -18,11 +18,11 @@ const COUNT_PER_PAGE = 8 const StrategyList = () => { const isReady = useMSWStore((state) => state.isReady) - const { page, handlePageChange } = usePagination({ + const { size, page, handlePageChange } = usePagination({ basePath: PATH.STRATEGIES, pageSize: COUNT_PER_PAGE, }) - const { data } = useGetStrategiesData({ isReady, page, size: COUNT_PER_PAGE }) + const { data } = useGetStrategiesData({ isReady, page, size }) const strategiesData = data?.strategiesData || [] const totalPages = data?.totalPages || null diff --git a/shared/hooks/custom/use-pagination.ts b/shared/hooks/custom/use-pagination.ts index 8f37ce49..e4822f61 100644 --- a/shared/hooks/custom/use-pagination.ts +++ b/shared/hooks/custom/use-pagination.ts @@ -11,6 +11,7 @@ interface Props { interface UsePaginationReturnModel { page: number + size: number handlePageChange: (page: number) => void } @@ -18,6 +19,7 @@ export const usePagination = ({ basePath, pageSize }: Props): UsePaginationRetur const searchParams = useSearchParams() const router = useRouter() const page = parseInt(searchParams?.get('page') || '1') + const size = parseInt(searchParams?.get('size') || '1') useEffect(() => { if (!searchParams.size) { @@ -31,6 +33,7 @@ export const usePagination = ({ basePath, pageSize }: Props): UsePaginationRetur return { page, + size, handlePageChange, } } From cf43474df2ae6034acf7397aa08933136a246cb1 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 18:19:19 +0900 Subject: [PATCH 417/648] =?UTF-8?q?refactor:=20searchInput=20prop=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/search-input/index.tsx | 6 +++--- shared/ui/search-input/search-input.stories.tsx | 2 +- shared/ui/search-input/styles.module.scss | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/shared/ui/search-input/index.tsx b/shared/ui/search-input/index.tsx index 27940503..3ad2a59c 100644 --- a/shared/ui/search-input/index.tsx +++ b/shared/ui/search-input/index.tsx @@ -11,11 +11,11 @@ const cx = classNames.bind(styles) interface Props extends ComponentPropsWithoutRef<'input'> { placeholder?: string - handleSearchIconClick?: () => void + onSearchIconClick?: () => void } export const SearchInput = forwardRef<HTMLInputElement, Props>( - ({ placeholder = '', handleSearchIconClick, value, onChange, ...props }: Props, ref) => { + ({ placeholder = '', onSearchIconClick, value, onChange, ...props }: Props, ref) => { return ( <div className={cx('search-input-container')}> <input @@ -26,7 +26,7 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>( className={cx('search-input')} {...props} /> - <SearchIcon className={cx('search-icon')} onClick={handleSearchIconClick} /> + <SearchIcon className={cx('search-icon')} onClick={onSearchIconClick} /> </div> ) } diff --git a/shared/ui/search-input/search-input.stories.tsx b/shared/ui/search-input/search-input.stories.tsx index ced99a3e..1d6e163c 100644 --- a/shared/ui/search-input/search-input.stories.tsx +++ b/shared/ui/search-input/search-input.stories.tsx @@ -9,7 +9,7 @@ const meta: Meta<typeof SearchInput> = { placeholder: 'Search...', }, argTypes: { - handleSearchIconClick: { action: 'clicked' }, + onSearchIconClick: { action: 'clicked' }, }, tags: ['autodocs'], } diff --git a/shared/ui/search-input/styles.module.scss b/shared/ui/search-input/styles.module.scss index e1a18e25..f7570495 100644 --- a/shared/ui/search-input/styles.module.scss +++ b/shared/ui/search-input/styles.module.scss @@ -31,6 +31,7 @@ right: 12px; top: 50%; transform: translateY(-50%); + width: 24px; color: $color-gray-500; cursor: pointer; } From 8588536953ce6132107f973529defb4f6d1aa0c0 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 18:22:59 +0900 Subject: [PATCH 418/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94?= =?UTF-8?q?=EB=A6=AC=EC=8B=B1=20(#155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/questions/_ui/question-card/index.tsx | 31 +++-- .../_ui/questions-tab-content/index.tsx | 128 ++++++++++++++++++ .../questions-tab-content/styles.module.scss | 16 +++ .../my/questions/_ui/questions-tab/index.tsx | 33 +++++ app/(dashboard)/my/questions/page.module.scss | 12 ++ app/(dashboard)/my/questions/page.tsx | 56 +++++++- shared/types/questions.ts | 13 ++ 7 files changed, 276 insertions(+), 13 deletions(-) create mode 100644 app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx create mode 100644 app/(dashboard)/my/questions/_ui/questions-tab-content/styles.module.scss create mode 100644 app/(dashboard)/my/questions/_ui/questions-tab/index.tsx create mode 100644 app/(dashboard)/my/questions/page.module.scss diff --git a/app/(dashboard)/my/questions/_ui/question-card/index.tsx b/app/(dashboard)/my/questions/_ui/question-card/index.tsx index 18addfdb..2c4012e8 100644 --- a/app/(dashboard)/my/questions/_ui/question-card/index.tsx +++ b/app/(dashboard)/my/questions/_ui/question-card/index.tsx @@ -1,5 +1,8 @@ +import Link from 'next/link' + import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' import type { QuestionStatusType } from '@/shared/types/questions' import Avatar from '@/shared/ui/avatar' import Label from '@/shared/ui/label' @@ -17,12 +20,14 @@ export interface QuestionCardProps { } interface Props extends QuestionCardProps { + qnaId: number strategyName: string title: string status: QuestionStatusType } const QuestionCard = ({ + qnaId, strategyName, title, contents, @@ -33,19 +38,21 @@ const QuestionCard = ({ }: Props) => { return ( <div className={cx('card-container')}> - <div className={cx('top-wrapper')}> - <strong className={cx('strategy-name')}>{strategyName}</strong> - <span className={cx('created-at')}>{formatDateTime(createdAt)}</span> - </div> - <h2 className={cx('title')}>{title}</h2> - <p className={cx('contents')}>{contents}</p> - <div className={cx('bottom-wrapper')}> - <div className={cx('avatar-wrapper')}> - <Avatar src={profileImage} size="medium" /> - <span>{nickname}</span> + <Link href={`${PATH.MY_QUESTIONS}/${qnaId}`}> + <div className={cx('top-wrapper')}> + <strong className={cx('strategy-name')}>{strategyName}</strong> + <span className={cx('created-at')}>{formatDateTime(createdAt)}</span> + </div> + <h2 className={cx('title')}>{title}</h2> + <p className={cx('contents')}>{contents}</p> + <div className={cx('bottom-wrapper')}> + <div className={cx('avatar-wrapper')}> + <Avatar src={profileImage} size="medium" /> + <span>{nickname}</span> + </div> + <Label color={status === '답변 완료' ? 'indigo' : 'orange'}>{status}</Label> </div> - <Label color={status === '답변 완료' ? 'indigo' : 'orange'}>{status}</Label> - </div> + </Link> </div> ) } diff --git a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx new file mode 100644 index 00000000..0f5d3816 --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx @@ -0,0 +1,128 @@ +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import { usePagination } from '@/shared/hooks/custom/use-pagination' +import { QuestionModel } from '@/shared/types/questions' +import Pagination from '@/shared/ui/pagination' + +import QuestionCard from '../question-card' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const COUNT_PER_PAGE = 4 + +type QuestionTabType = 'all' | 'waiting' | 'complete' + +interface Props { + options: QuestionTabType +} + +const QuestionsTabContent = ({ options }: Props) => { + const { page, handlePageChange } = usePagination({ + basePath: PATH.MY_QUESTIONS, + pageSize: COUNT_PER_PAGE, + }) + // TODO: options에 따른 questions 데이터 불러오기 + // 검색 조건 등 추가될 수 있어서 option 대신 options로 사용 + + // 임시 + const totalPages = 2 + + const questionsData: QuestionModel[] = [ + { + qnaId: 1, + userId: 101, + strategyId: 1, + title: 'Dynamic ETF 전략 진입 시점 문의드립니다', + questionContent: + '현재 시장 상황에서 Dynamic ETF 전략을 시작하려고 하는데, 적절한 진입 시점인지 고민됩니다. MDD 관리는 어떻게 하시는지도 궁금합니다.', + status: '답변 완료', + strategyName: 'Dynamic ETF 전략', + createdAt: '2024-03-15T09:30:00Z', + profileImageUrl: '/images/profile1.png', + nickname: '투자초보123', + }, + { + qnaId: 2, + userId: 102, + strategyId: 6, + title: 'Active LongShort 전략 레버리지 문의', + questionContent: + 'Active LongShort 전략에서 사용하시는 레버리지 비율이 궁금합니다. 리스크 관리는 어떻게 하시나요?', + status: '답변 대기', + strategyName: 'Active LongShort', + createdAt: '2024-03-14T15:45:00Z', + profileImageUrl: '/images/profile2.png', + nickname: '실전투자자', + }, + { + qnaId: 3, + userId: 103, + strategyId: 3, + title: 'Futures Pro 전략 업데이트 일정', + questionContent: + '다음 전략 업데이트 일정이 궁금합니다. 현재 수익률이 조금 정체되어 있는 것 같은데 어떻게 보시나요?', + status: '답변 완료', + strategyName: 'Futures Pro', + createdAt: '2024-03-13T11:20:00Z', + profileImageUrl: '/images/profile3.png', + nickname: '퓨처스마스터', + }, + { + qnaId: 4, + userId: 104, + strategyId: 8, + title: 'High Risk High Return 전략 백테스트 결과 문의', + questionContent: + '백테스트 기간과 구체적인 결과가 궁금합니다. 특히 2023년 변동성 장세에서의 성과는 어땠나요?', + status: '답변 완료', + strategyName: 'High Risk High Return', + createdAt: '2024-03-12T16:15:00Z', + profileImageUrl: '/images/profile4.png', + nickname: '데이터분석가', + }, + { + qnaId: 5, + userId: 105, + strategyId: 2, + title: '고수익 ETF 전략 종목 선정 기준', + questionContent: + 'ETF 선정 기준과 리밸런싱 주기가 궁금합니다. 또한 현재 포트폴리오에서 가장 비중이 높은 ETF는 무엇인가요?', + status: '답변 대기', + strategyName: '고수익 ETF', + createdAt: '2024-03-11T13:50:00Z', + profileImageUrl: '/images/profile5.png', + nickname: 'ETF러버', + }, + ] + + return ( + <> + <ul className={cx('question-list')}> + {questionsData && + questionsData.length && + questionsData.map((question) => ( + <li key={question.qnaId}> + <QuestionCard + qnaId={question.qnaId} + strategyName={question.strategyName} + title={question.title} + status={question.status} + contents={question.questionContent} + nickname={question.nickname} + createdAt={question.createdAt} + /> + </li> + ))} + </ul> + {!questionsData || + (!questionsData.length && <p className={cx('empty-message')}>문의 내역이 없습니다.</p>)} + <div className={cx('pagination-wrapper')}> + <Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} /> + </div> + </> + ) +} + +export default QuestionsTabContent diff --git a/app/(dashboard)/my/questions/_ui/questions-tab-content/styles.module.scss b/app/(dashboard)/my/questions/_ui/questions-tab-content/styles.module.scss new file mode 100644 index 00000000..3517a7ec --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/questions-tab-content/styles.module.scss @@ -0,0 +1,16 @@ +.question-list { + padding-top: 24px; + + li { + margin-bottom: 24px; + } +} + +.empty-message { + margin: 12px 0; + @include typo-b2; +} + +.pagination-wrapper { + margin-bottom: 24px; +} diff --git a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx new file mode 100644 index 00000000..dae6d76b --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx @@ -0,0 +1,33 @@ +'use client' + +import { useState } from 'react' + +import Tabs from '@/shared/ui/tabs' + +import QuestionsTabContent from '../questions-tab-content' + +const QuestionsTab = () => { + const [activeTab, setActiveTab] = useState('all') + + const TABS = [ + { + id: 'all', + label: '모든 질문', + content: <QuestionsTabContent options="all" />, + }, + { + id: 'waiting', + label: '답변 대기', + content: <QuestionsTabContent options="waiting" />, + }, + { + id: 'complete', + label: '답변 완료', + content: <QuestionsTabContent options="complete" />, + }, + ] + + return <Tabs activeTab={activeTab} onTabChange={setActiveTab} tabs={TABS} /> +} + +export default QuestionsTab diff --git a/app/(dashboard)/my/questions/page.module.scss b/app/(dashboard)/my/questions/page.module.scss new file mode 100644 index 00000000..a745f035 --- /dev/null +++ b/app/(dashboard)/my/questions/page.module.scss @@ -0,0 +1,12 @@ +.title-wrapper { + margin: 80px 0 32px; + + display: flex; + align-items: center; + justify-content: space-between; +} + +.search-wrapper { + display: flex; + gap: 24px; +} diff --git a/app/(dashboard)/my/questions/page.tsx b/app/(dashboard)/my/questions/page.tsx index 5f164eef..61de4c34 100644 --- a/app/(dashboard)/my/questions/page.tsx +++ b/app/(dashboard)/my/questions/page.tsx @@ -1,5 +1,59 @@ +'use client' + +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import { DropdownValueType } from '@/shared/ui/dropdown/types' +import { SearchInput } from '@/shared/ui/search-input' +import Select from '@/shared/ui/select' +import Title from '@/shared/ui/title' + +import QuestionsTab from './_ui/questions-tab' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const searchSelectOptions = [ + { + value: 'all', + label: '전체', + }, + { + value: 'trader', + label: '트레이더', + }, + { + value: 'title', + label: '제목', + }, + { + value: 'strategy', + label: '전략명', + }, +] + const MyQuestionsPage = () => { - return <></> + const [selectedOption, setSelectedOption] = useState<DropdownValueType>('최신순') + + return ( + <div className={cx('container')}> + <div className={cx('title-wrapper')}> + <Title label="문의 내역" /> + <div className={cx('search-wrapper')}> + <Select + size="small" + value={selectedOption} + placeholder="검색 조건" + onChange={setSelectedOption} + options={searchSelectOptions} + /> + <SearchInput /> + </div> + </div> + <QuestionsTab /> + </div> + ) } export default MyQuestionsPage diff --git a/shared/types/questions.ts b/shared/types/questions.ts index a77df7bd..2fded165 100644 --- a/shared/types/questions.ts +++ b/shared/types/questions.ts @@ -1 +1,14 @@ export type QuestionStatusType = '답변 대기' | '답변 완료' + +export interface QuestionModel { + qnaId: number + userId: number + strategyId: number + title: string + questionContent: string + status: QuestionStatusType + strategyName: string + createdAt: string + profileImageUrl: string + nickname: string +} From 3c40bc619b0a776f1f5daaa754f83e3e76fd2141 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 19:03:23 +0900 Subject: [PATCH 419/648] =?UTF-8?q?feat:=20Suspense=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/questions-tab-content/index.tsx | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx index 0f5d3816..ec829332 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx @@ -1,3 +1,5 @@ +import { Suspense } from 'react' + import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -99,23 +101,25 @@ const QuestionsTabContent = ({ options }: Props) => { return ( <> - <ul className={cx('question-list')}> - {questionsData && - questionsData.length && - questionsData.map((question) => ( - <li key={question.qnaId}> - <QuestionCard - qnaId={question.qnaId} - strategyName={question.strategyName} - title={question.title} - status={question.status} - contents={question.questionContent} - nickname={question.nickname} - createdAt={question.createdAt} - /> - </li> - ))} - </ul> + <Suspense> + <ul className={cx('question-list')}> + {questionsData && + questionsData.length && + questionsData.map((question) => ( + <li key={question.qnaId}> + <QuestionCard + qnaId={question.qnaId} + strategyName={question.strategyName} + title={question.title} + status={question.status} + contents={question.questionContent} + nickname={question.nickname} + createdAt={question.createdAt} + /> + </li> + ))} + </ul> + </Suspense> {!questionsData || (!questionsData.length && <p className={cx('empty-message')}>문의 내역이 없습니다.</p>)} <div className={cx('pagination-wrapper')}> From 313f4bdb77e689ada10ef3252f6b5d6e4c87d7da Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 19:08:20 +0900 Subject: [PATCH 420/648] =?UTF-8?q?fix:=20Suspense=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95=20(#155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/questions-tab-content/index.tsx | 39 +++++++++---------- .../my/questions/_ui/questions-tab/index.tsx | 20 ++++++++-- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx index ec829332..dd5ae743 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx @@ -1,5 +1,3 @@ -import { Suspense } from 'react' - import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -101,25 +99,24 @@ const QuestionsTabContent = ({ options }: Props) => { return ( <> - <Suspense> - <ul className={cx('question-list')}> - {questionsData && - questionsData.length && - questionsData.map((question) => ( - <li key={question.qnaId}> - <QuestionCard - qnaId={question.qnaId} - strategyName={question.strategyName} - title={question.title} - status={question.status} - contents={question.questionContent} - nickname={question.nickname} - createdAt={question.createdAt} - /> - </li> - ))} - </ul> - </Suspense> + <ul className={cx('question-list')}> + {questionsData && + questionsData.length && + questionsData.map((question) => ( + <li key={question.qnaId}> + <QuestionCard + qnaId={question.qnaId} + strategyName={question.strategyName} + title={question.title} + status={question.status} + contents={question.questionContent} + nickname={question.nickname} + createdAt={question.createdAt} + /> + </li> + ))} + </ul> + {!questionsData || (!questionsData.length && <p className={cx('empty-message')}>문의 내역이 없습니다.</p>)} <div className={cx('pagination-wrapper')}> diff --git a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx index dae6d76b..4d68b960 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { Suspense, useState } from 'react' import Tabs from '@/shared/ui/tabs' @@ -13,17 +13,29 @@ const QuestionsTab = () => { { id: 'all', label: '모든 질문', - content: <QuestionsTabContent options="all" />, + content: ( + <Suspense> + <QuestionsTabContent options="all" /> + </Suspense> + ), }, { id: 'waiting', label: '답변 대기', - content: <QuestionsTabContent options="waiting" />, + content: ( + <Suspense> + <QuestionsTabContent options="waiting" /> + </Suspense> + ), }, { id: 'complete', label: '답변 완료', - content: <QuestionsTabContent options="complete" />, + content: ( + <Suspense> + <QuestionsTabContent options="complete" /> + </Suspense> + ), }, ] From 88d05dd8af1ed366e038899d6f02b5e985d72d71 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 29 Nov 2024 19:50:31 +0900 Subject: [PATCH 421/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=EC=9C=A0=ED=98=95=20=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20/=20=EB=B9=A0=EB=A5=B8=20api=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=B5=9C=EC=86=8C=ED=95=9C=EC=9D=98=20ui=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/stock/stock-manage/index.tsx | 112 ++++++++++++++++++ .../_ui/trade/trade-manage/index.tsx | 18 +++ app/admin/strategies/page.tsx | 46 ++++++- 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 app/admin/strategies/_ui/stock/stock-manage/index.tsx create mode 100644 app/admin/strategies/_ui/trade/trade-manage/index.tsx diff --git a/app/admin/strategies/_ui/stock/stock-manage/index.tsx b/app/admin/strategies/_ui/stock/stock-manage/index.tsx new file mode 100644 index 00000000..56f22c42 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/index.tsx @@ -0,0 +1,112 @@ +import { useState } from 'react' + +import useModal from '@/shared/hooks/custom/use-modal' +import { Button } from '@/shared/ui/button' +import { Input } from '@/shared/ui/input' + +const StockManage = () => { + const { isModalOpen, openModal, closeModal } = useModal() + const onClick = () => openModal() + + const [imagePreview, setImagePreview] = useState<string | null>(null) + + const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => { + const file = event.target.files?.[0] + + if (file) { + if (!file.type.startsWith('image/')) { + alert('Please upload a valid image file.') + return + } + + console.log(file) + console.log(file.size) + + const reader = new FileReader() + reader.onload = (e) => { + setImagePreview(e.target?.result as string) + } + reader.readAsDataURL(file) + } else { + setImagePreview(null) // 파일이 없으면 미리보기 초기화 + } + } + + const onSubmit = () => {} + + // const [tra] + + const fetchTrade = async () => { + // const res = await fetch( + // 'http://15.164.90.102:8081/api/strategies/trade-type?activateState=true' + // ) + // const res = await fetch('http://15.164.90.102:8081/api/admin/strategies/stock-type', { + // method: 'POST', + // body: JSON.stringify({ + // stockTypeName: '주식', + // stockTypeIconURL: 'StockIconTest.png', + // size: 456, + // // tradeTypeName: '자동', + // // tradeTypeIconURL: 'IconTest.svg', + // // size: 456, + // }), + // }) + const res = await fetch( + 'http://15.164.90.102:8081/api/admin/strategies/trade-types/{trade_type_id}', + { + method: 'PATCH', + } + ) + + const result = await res.json() + + console.log(result) + } + + return ( + <div> + <Button onClick={fetchTrade} variant="filled"> + fetch + </Button> + {/* <img + src="https://fastcampus-team3.s3.ap-northeast-2.amazonaws.com/strategy/image/07421e08/icons/sampleTrade-icon2.png" + alt="img" + /> */} + {/* <Modal icon={<RegisterIcon />} message="종목 등록" isOpen={isModalOpen}> */} + <form + onSubmit={onSubmit} + style={{ marginTop: '24px', display: 'flex', flexDirection: 'column', gap: '12px' }} + > + <div style={{ display: 'flex', gap: '12px' }}> + 종목 : <Input placeholder="종목명을 입력하세요." inputSize="small" /> + </div> + <Input type="file" accept="image/*" onChange={handleImageChange} /> + {imagePreview && ( + <img src={imagePreview} alt="Preview" style={{ maxWidth: '200px', maxHeight: '200px' }} /> + )} + <Button.ButtonGroup gap="24px"> + <Button onClick={closeModal}>취소</Button> + <Button + onClick={() => { + alert('123') + }} + variant="filled" + > + 등록 + </Button> + </Button.ButtonGroup> + </form> + + {/* </Modal> */} + {/* <VerticalTable + tableHead={['No.', '종목명', '분류', '상태']} + tableBody={[]} + countPerPage={8} + currentPage={1} + /> + <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> */} + </div> + ) +} + +export default StockManage diff --git a/app/admin/strategies/_ui/trade/trade-manage/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/index.tsx new file mode 100644 index 00000000..eaf032b8 --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/index.tsx @@ -0,0 +1,18 @@ +import Pagination from '@/shared/ui/pagination' +import VerticalTable from '@/shared/ui/table/vertical' + +const TradeManage = () => { + return ( + <div> + <VerticalTable + tableHead={['No.', '종목명', '분류', '상태']} + tableBody={[]} + countPerPage={8} + currentPage={1} + /> + <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + </div> + ) +} + +export default TradeManage diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index fca16be7..f7e019bc 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -1,5 +1,49 @@ +'use client' + +// TODO: ssr +import { useState } from 'react' + +import Tabs from '@/shared/ui/tabs' +import Title from '@/shared/ui/title' + +import StockManage from './_ui/stock/stock-manage' +import TradeManage from './_ui/trade/trade-manage' + +// import styles from './page.module.scss' + +// const cx = classNames.bind(styles) + +const tabs = ['종목', '매매유형'] + +type TabType = '종목' | '매매유형' + const AdminStrategiesPage = () => { - return <></> + const [activeTab, setActiveTab] = useState<TabType>('종목') + + return ( + <> + {/* TODO: inline css 제거 */} + <Title label="종목 및 매매 유형 관리" style={{ margin: '80px 0 26px 12.6px' }} /> + <div + style={{ + padding: '0 45px 37px', + borderRadius: '8px', + marginBottom: '42px', + backgroundColor: 'aliceblue', + }} + > + <Tabs + tabs={tabs.map((tab) => ({ + id: tab, + label: tab, + content: tab === '종목' ? <StockManage /> : <TradeManage />, + }))} + activeTab={activeTab} + onTabChange={(tab) => setActiveTab(tab)} + /> + </div> + </> + ) } export default AdminStrategiesPage From c753360af2d51c1babef3740088e332bd9be8f49 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 29 Nov 2024 22:14:00 +0900 Subject: [PATCH 422/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-container/index.tsx | 46 +++++++++++++++++++ .../_ui/question-container/styles.module.scss | 17 +++++++ .../my/questions/[questionId]/page.tsx | 16 +++++++ 3 files changed, 79 insertions(+) create mode 100644 app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx create mode 100644 app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss create mode 100644 app/(dashboard)/my/questions/[questionId]/page.tsx diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx new file mode 100644 index 00000000..1c321056 --- /dev/null +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -0,0 +1,46 @@ +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import QuestionDetailCard from '../question-detail-card' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const QuestionContainer = () => { + // 임시 + const hasAnswer = true + + return ( + <> + <div className={cx('container')}> + <QuestionDetailCard + isAuthor={true} + strategyName="ETF 레버리지 /인버" + title="미국발 경제악화가 한국 증시에 미치는 영향은 무엇인가요?" + contents="안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.........." + nickname="홍길동" + createdAt="2024-11-03T15:00:00" + status="답변 완료" + /> + {hasAnswer ? ( + <QuestionDetailCard + type="answer" + isAuthor={false} + contents="저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다..." + nickname="전문가" + createdAt="2024-11-03T15:00:00" + /> + ) : ( + <p className={cx('empty-message')}>아직 답변이 없습니다</p> + )} + + <Button variant="filled" className={cx('button')}> + 추가 질문하기 + </Button> + </div> + </> + ) +} + +export default QuestionContainer diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss new file mode 100644 index 00000000..0702276c --- /dev/null +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss @@ -0,0 +1,17 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; + margin-top: 86px; +} + +.button { + margin: 48px 0 72px; +} + +.empty-message { + margin-top: 90px; + color: $color-gray-600; + @include typo-b1; +} diff --git a/app/(dashboard)/my/questions/[questionId]/page.tsx b/app/(dashboard)/my/questions/[questionId]/page.tsx new file mode 100644 index 00000000..4f452c26 --- /dev/null +++ b/app/(dashboard)/my/questions/[questionId]/page.tsx @@ -0,0 +1,16 @@ +import BackHeader from '@/shared/ui/header/back-header' +import Title from '@/shared/ui/title' + +import QuestionContainer from './_ui/question-container' + +const QuestionDetailPage = () => { + return ( + <> + <BackHeader label={'문의 내역으로 돌아가기'} /> + <Title label="문의 내역" /> + <QuestionContainer /> + </> + ) +} + +export default QuestionDetailPage From 7491d1c0b71e296b33e69ee1ad5d900a84ef2abc Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 22:46:34 +0900 Subject: [PATCH 423/648] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EB=B6=80=EB=A0=8C=EB=8D=94=EB=A7=81=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-side-item/index.tsx | 10 ++++++-- .../_ui/details-side-item/side-item.tsx | 11 +++++---- .../_ui/review-container/review-item.tsx | 17 +++++++++----- .../_ui/review-container/review-list.tsx | 5 +++- .../strategies/[strategyId]/page.tsx | 23 ++++++++++++++----- 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/app/(dashboard)/_ui/details-side-item/index.tsx b/app/(dashboard)/_ui/details-side-item/index.tsx index 3e7138ea..7eb90ce4 100644 --- a/app/(dashboard)/_ui/details-side-item/index.tsx +++ b/app/(dashboard)/_ui/details-side-item/index.tsx @@ -22,9 +22,10 @@ export interface InformationModel { interface Props { information: InformationModel | InformationModel[] profileImage?: string + isMyStrategy?: boolean } -const DetailsSideItem = ({ information, profileImage }: Props) => { +const DetailsSideItem = ({ information, profileImage, isMyStrategy = true }: Props) => { const isArray = Array.isArray(information) return ( <> @@ -40,7 +41,12 @@ const DetailsSideItem = ({ information, profileImage }: Props) => { ))} </div> ) : ( - <SideItem title={information.title} data={information.data} profileImage={profileImage} /> + <SideItem + title={information.title} + data={information.data} + profileImage={profileImage} + isMyStrategy={isMyStrategy} + /> )} </> ) diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index 2444aa15..cf452b20 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -13,9 +13,10 @@ interface Props { title: Omit<TitleType, '트레이더' | '최소 투자 금액' | '투자 원금'> data: number | string profileImage?: string + isMyStrategy?: boolean } -const SideItem = ({ title, data, profileImage }: Props) => { +const SideItem = ({ title, data, profileImage, isMyStrategy = false }: Props) => { return ( <div className={cx('side-item')}> <div className={cx('title')}>{title}</div> @@ -26,9 +27,11 @@ const SideItem = ({ title, data, profileImage }: Props) => { <Avatar src={profileImage} /> <p>{data}</p> </div> - <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> - 문의하기 - </LinkButton> + {!isMyStrategy && ( + <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> + 문의하기 + </LinkButton> + )} </> ) : ( <p>{data}</p> diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx index a2b93e1d..7bfff652 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx @@ -14,6 +14,7 @@ interface Props { createdAt: string starRating: number isReviewer: boolean + isAdmin: boolean } const ReviewItem = ({ @@ -23,6 +24,7 @@ const ReviewItem = ({ starRating, content, isReviewer, + isAdmin, }: Props) => { const editedCreatedAt = createdAt.slice(0, -3) @@ -36,12 +38,15 @@ const ReviewItem = ({ <span>{editedCreatedAt}</span> <StarRating starRating={starRating} /> </div> - {isReviewer && ( - <div className={cx('button-wrapper')}> - <button>수정</button> - <button>삭제</button> - </div> - )} + <div className={cx('button-wrapper')}> + {isReviewer && ( + <> + <button>수정</button> + <button>삭제</button> + </> + )} + {!isReviewer && isAdmin && <button>삭제</button>} + </div> </div> <div className={cx('content')}>{content}</div> </li> diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx index d8baccbc..81c29cdc 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx @@ -1,5 +1,6 @@ import classNames from 'classnames/bind' +import { useAuthStore } from '@/shared/stores/use-auth-store' import Pagination from '@/shared/ui/pagination' import ReviewItem from './review-item' @@ -27,6 +28,7 @@ interface Props { const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props) => { const handlePageChange = (page: number) => setCurrentPage(page) + const user = useAuthStore((state) => state.user) return ( <> @@ -39,7 +41,8 @@ const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props createdAt={review.createdAt} starRating={review.starRating} content={review.content} - isReviewer={'' === review.nickname} + isReviewer={user?.nickname === review.nickname} + isAdmin={user?.role.includes('admin') ?? false} /> ))} </ul> diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index f09d9875..8526db4d 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,6 +1,7 @@ 'use client' import { useMSWStore } from '@/shared/stores/msw' +import { useAuthStore } from '@/shared/stores/use-auth-store' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' @@ -16,12 +17,13 @@ export type InformationType = { title: TitleType; data: string | number } | Info const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { const isReady = useMSWStore((state) => state.isReady) + const user = useAuthStore((state) => state.user) const { data } = useGetDetailsInformationData({ isReady, strategyId: params.strategyId, }) - const { detailsSideData, detailsInformationData } = data || {} + const { detailsSideData, detailsInformationData: information } = data || {} const hasDetailsSideData = detailsSideData?.map((data) => { if (!Array.isArray(data)) return data.data !== undefined @@ -31,15 +33,24 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { <div> <BackHeader label={'목록으로 돌아가기'} /> <Title label={'전략 상세보기'} /> - {detailsInformationData && <DetailsInformation information={detailsInformationData} />} - <AnalysisContainer /> + {information && <DetailsInformation information={information} />} + <AnalysisContainer strategyId={params.strategyId} /> <ReviewContainer strategyId={params.strategyId} /> <SideContainer> - <SubscriberItem subscribers={99} /> - {hasDetailsSideData?.[0] && + {information && ( + <SubscriberItem + isMyStrategy={user?.nickname === information.nickname} + subscribers={information?.subscriptionCount} + /> + )} + {information && + hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( <div key={`${data}_${idx}`}> - <DetailsSideItem information={data} /> + <DetailsSideItem + information={data} + isMyStrategy={user?.nickname === information.nickname} + /> </div> ))} </SideContainer> From 53daeb74e571623c43131eaad43438bb8a566af3 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 22:47:40 +0900 Subject: [PATCH 424/648] =?UTF-8?q?design:=20=EB=94=94=EC=9E=90=EC=9D=B8?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/styles.module.scss | 4 +--- .../_ui/subscriber-item/index.tsx | 16 ++++++++------ .../_ui/subscriber-item/styles.module.scss | 22 +++++++------------ 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index ca870fea..326a24e0 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -47,12 +47,10 @@ display: flex; align-items: center; height: 20px; + gap: 6px; p { @include typo-c1; padding-top: 10px; - &:first-child { - margin-right: 6px; - } } } } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/index.tsx index 4196613b..f7a2863c 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/index.tsx @@ -17,14 +17,16 @@ interface Props { const SubscriberItem = ({ isMyStrategy = false, subscribers, onClick }: Props) => { return ( <div className={cx('container')}> - <div className={cx('wrapper')}> - <div className={cx('contents')}>구독 | {subscribers}</div> - {!isMyStrategy && ( - <Button size="small" variant="filled" onClick={onClick}> - 구독하기 - </Button> - )} + <div> + <span>구독 </span> + <span>| </span> + <span>{subscribers}</span> </div> + {!isMyStrategy && ( + <Button size="small" variant="filled" onClick={onClick}> + 구독하기 + </Button> + )} </div> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/styles.module.scss index e6f0efed..4d49f1e8 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/styles.module.scss @@ -1,22 +1,16 @@ .container { display: flex; align-items: center; - width: 276px; + justify-content: space-between; height: 83px; - padding: 22px 20px 22px 40px; - flex-shrink: 0; + padding: 0 30px; border-radius: 5px; background-color: $color-white; margin-bottom: 18px; -} - -.wrapper { - display: flex; - align-items: center; - gap: 45px; -} - -.contents { - color: $color-gray-800; - @include typo-b1; + span { + font-size: 18px; + font-weight: $text-semibold; + color: $color-gray-800; + margin-left: 4px; + } } From dd420b3e36d3d30121743d80cf5b4f9be503977c Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 22:55:17 +0900 Subject: [PATCH 425/648] =?UTF-8?q?feat:=20=EB=B6=84=EC=84=9D=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20api=20=EC=B6=94=EA=B0=80=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/manage/[strategyId]/page.tsx | 2 +- .../[strategyId]/_api/get-analysis-chart.ts | 20 ++++++++++++ .../_hooks/query/use-get-analysis-chart.ts | 19 +++++++++++ .../_ui/analysis-container/analysis-chart.tsx | 2 +- .../_ui/analysis-container/index.tsx | 23 ++++++------- next.config.mjs | 32 +++++++++---------- 6 files changed, 69 insertions(+), 29 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index a10e5094..4684c2bd 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -48,7 +48,7 @@ const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { {detailsInformationData && ( <DetailsInformation information={detailsInformationData} type="my" /> )} - <AnalysisContainer type="my" /> + <AnalysisContainer type="my" strategyId={params.strategyId} /> <SideContainer hasButton={true}> <SubscriberItem subscribers={99} /> {hasDetailsSideData?.[0] && diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts new file mode 100644 index 00000000..e6986d33 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts @@ -0,0 +1,20 @@ +import axios from 'axios' + +import { OptionsType } from '../_ui/analysis-container' + +const getAnalysisChart = async ( + strategyId: number, + firstOption: OptionsType, + secondOption: OptionsType +) => { + try { + const response = await axios.get( + `/api/strategies/${strategyId}/analysis?option1=${firstOption}&option2=${secondOption}` + ) + return response.data.result + } catch (err) { + console.error(err) + } +} + +export default getAnalysisChart diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts new file mode 100644 index 00000000..82b1948a --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts @@ -0,0 +1,19 @@ +import { useQuery } from '@tanstack/react-query' + +import getAnalysisChart from '../../_api/get-analysis-chart' +import { OptionsType } from '../../_ui/analysis-container' + +interface Props { + strategyId: number + firstOption: OptionsType + secondOption: OptionsType +} + +const useGetAnalysisChart = ({ strategyId, firstOption, secondOption }: Props) => { + return useQuery({ + queryKey: ['analysisChart', strategyId, firstOption, secondOption], + queryFn: () => getAnalysisChart(strategyId, firstOption, secondOption), + }) +} + +export default useGetAnalysisChart diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx index 7d8e40dc..4c1e73f5 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx @@ -32,7 +32,7 @@ const AnalysisChart = ({ analysisChartData: data }: Props) => { const key = Object.keys(data.data)[sequence] as YAxisType | undefined return key ? YAXIS_OPTIONS[key] : '' } - + if (!data) return null const chartOptions: Highcharts.Options = { chart: { type: 'areaspline', diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx index 7308bd17..144276d1 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx @@ -6,24 +6,25 @@ import classNames from 'classnames/bind' import Select from '@/shared/ui/select' +import useGetAnalysisChart from '../../_hooks/query/use-get-analysis-chart' import AnalysisChart from './analysis-chart' -import { analysisChartData } from './example' import styles from './styles.module.scss' import TabsWithTable from './tabs-width-table' import { YAXIS_OPTIONS } from './yaxis-options' const cx = classNames.bind(styles) -type OptionsType = (typeof YAXIS_OPTIONS)[keyof typeof YAXIS_OPTIONS] +export type OptionsType = keyof typeof YAXIS_OPTIONS interface Props { + strategyId: number type?: 'default' | 'my' } -const AnalysisContainer = ({ type = 'default' }: Props) => { - const [firstValue, setFirstValue] = useState<OptionsType>('잔고') - const [secondValue, setSecondValue] = useState<OptionsType>('잔고') - +const AnalysisContainer = ({ strategyId, type = 'default' }: Props) => { + const [firstOption, setFirstOption] = useState<OptionsType>('PRINCIPAL') + const [secondOption, setSecondOption] = useState<OptionsType>('CUMULATIVE_PROFIT_LOSS') + const { data: chartData } = useGetAnalysisChart({ strategyId, firstOption, secondOption }) const optionsToArray = Object.entries(YAXIS_OPTIONS) const options: { value: string; label: string }[] = [] @@ -40,21 +41,21 @@ const AnalysisContainer = ({ type = 'default' }: Props) => { <Select size="large" options={options} - value={firstValue} - onChange={(newValue) => setFirstValue(newValue as OptionsType)} + value={firstOption} + onChange={(newValue) => setFirstOption(newValue as OptionsType)} /> <Select size="large" options={options} - value={secondValue} - onChange={(newValue) => setSecondValue(newValue as OptionsType)} + value={secondOption} + onChange={(newValue) => setSecondOption(newValue as OptionsType)} /> </div> )} </div> {type === 'default' && ( <div className={cx('chart-wrapper')}> - <AnalysisChart analysisChartData={analysisChartData} /> + <AnalysisChart analysisChartData={chartData} /> </div> )} <TabsWithTable isEditable={type === 'my' ? true : false} /> diff --git a/next.config.mjs b/next.config.mjs index 969da5ce..e8631f38 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -9,22 +9,22 @@ const nextConfig = { @import "@/shared/styles/base/functions"; `, }, - // async rewrites() { - // return [ - // { - // source: '/api/users/reissue/refreshtoken', - // destination: '/api/users/reissue/refreshtoken', - // }, - // { - // source: '/api/users/login', - // destination: '/api/users/login', - // }, - // { - // source: '/api/:path*', - // destination: 'http://15.164.90.102:8081/api/:path*', - // }, - // ] - // }, + async rewrites() { + return [ + // { + // source: '/api/users/reissue/refreshtoken', + // destination: '/api/users/reissue/refreshtoken', + // }, + // { + // source: '/api/users/login', + // destination: '/api/users/login', + // }, + { + source: '/api/strategies/:path*', + destination: 'http://15.164.90.102:8081/api/strategies/:path*', + }, + ] + }, webpack: (config, { isServer }) => { if (isServer) { if (Array.isArray(config.resolve.alias)) { From b45cb529d728c92eedb5daa49631f7280a59df53 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 23:33:56 +0900 Subject: [PATCH 426/648] =?UTF-8?q?fix:=20=EC=B0=A8=ED=8A=B8=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/analysis-container/analysis-chart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx index 4c1e73f5..68012785 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx @@ -54,7 +54,7 @@ const AnalysisChart = ({ analysisChartData: data }: Props) => { verticalAlign: 'top', layout: 'vertical', x: 10, - y: 10, + y: 0, itemStyle: { color: '#4D4D4D', fontSize: '12px', From 851ffb21d4660a4b85a88c8df3978aab8690dc5a Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 29 Nov 2024 23:34:17 +0900 Subject: [PATCH 427/648] =?UTF-8?q?fix:=20=ED=83=80=EC=9E=85=EB=AA=85=20?= =?UTF-8?q?=EB=AA=85=ED=99=95=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95=20(#1?= =?UTF-8?q?60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_api/get-analysis-chart.ts | 6 +++--- .../_hooks/query/use-get-analysis-chart.ts | 6 +++--- .../[strategyId]/_ui/analysis-container/index.tsx | 11 ++++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts index e6986d33..4fe6fe6b 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts @@ -1,11 +1,11 @@ import axios from 'axios' -import { OptionsType } from '../_ui/analysis-container' +import { AnalysisChartOptionsType } from '../_ui/analysis-container' const getAnalysisChart = async ( strategyId: number, - firstOption: OptionsType, - secondOption: OptionsType + firstOption: AnalysisChartOptionsType, + secondOption: AnalysisChartOptionsType ) => { try { const response = await axios.get( diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts index 82b1948a..0998b15e 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts @@ -1,12 +1,12 @@ import { useQuery } from '@tanstack/react-query' import getAnalysisChart from '../../_api/get-analysis-chart' -import { OptionsType } from '../../_ui/analysis-container' +import { AnalysisChartOptionsType } from '../../_ui/analysis-container' interface Props { strategyId: number - firstOption: OptionsType - secondOption: OptionsType + firstOption: AnalysisChartOptionsType + secondOption: AnalysisChartOptionsType } const useGetAnalysisChart = ({ strategyId, firstOption, secondOption }: Props) => { diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx index 144276d1..c15033d1 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx @@ -14,7 +14,7 @@ import { YAXIS_OPTIONS } from './yaxis-options' const cx = classNames.bind(styles) -export type OptionsType = keyof typeof YAXIS_OPTIONS +export type AnalysisChartOptionsType = keyof typeof YAXIS_OPTIONS interface Props { strategyId: number @@ -22,8 +22,9 @@ interface Props { } const AnalysisContainer = ({ strategyId, type = 'default' }: Props) => { - const [firstOption, setFirstOption] = useState<OptionsType>('PRINCIPAL') - const [secondOption, setSecondOption] = useState<OptionsType>('CUMULATIVE_PROFIT_LOSS') + const [firstOption, setFirstOption] = useState<AnalysisChartOptionsType>('PRINCIPAL') + const [secondOption, setSecondOption] = + useState<AnalysisChartOptionsType>('CUMULATIVE_PROFIT_LOSS') const { data: chartData } = useGetAnalysisChart({ strategyId, firstOption, secondOption }) const optionsToArray = Object.entries(YAXIS_OPTIONS) const options: { value: string; label: string }[] = [] @@ -42,13 +43,13 @@ const AnalysisContainer = ({ strategyId, type = 'default' }: Props) => { size="large" options={options} value={firstOption} - onChange={(newValue) => setFirstOption(newValue as OptionsType)} + onChange={(newValue) => setFirstOption(newValue as AnalysisChartOptionsType)} /> <Select size="large" options={options} value={secondOption} - onChange={(newValue) => setSecondOption(newValue as OptionsType)} + onChange={(newValue) => setSecondOption(newValue as AnalysisChartOptionsType)} /> </div> )} From b4196de635c48b23a250e632c2f033d56858e3b2 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 30 Nov 2024 14:24:39 +0900 Subject: [PATCH 428/648] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8B=A4=EC=A0=9C=20?= =?UTF-8?q?api=20=EC=97=B0=EB=8F=99=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/strategies-item/index.tsx | 27 ++++++++++++++----- .../strategies-item.stories.tsx | 2 ++ .../my/_api/get-my-strategy-list.ts | 24 +++++++++++------ .../strategies/_ui/my-strategy-list/index.tsx | 3 +-- mocks/handlers/strategies.ts | 12 +++++++++ shared/api/axios.ts | 4 ++- shared/types/strategy-details-data.ts | 1 + shared/ui/table/vertical/index.tsx | 1 - 8 files changed, 56 insertions(+), 18 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 8053e335..735b0039 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -3,6 +3,7 @@ import Link from 'next/link' import classNames from 'classnames/bind' import { StrategiesModel } from '@/shared/types/strategy-details-data' +import { Button } from '@/shared/ui/button' import AreaChart from './area-chart' import StrategiesSummary from './strategies-summary' @@ -13,14 +14,15 @@ const cx = classNames.bind(styles) interface Props { strategiesData: StrategiesModel + type?: 'default' | 'my' } -const StrategiesItem = ({ strategiesData: data }: Props) => { +const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { return ( - <Link className={cx('container')} href={`/strategies/${data.strategyId}`}> + <Link className={cx('container', type)} href={`/strategies/${data.strategyId}`}> <StrategiesSummary - iconUrls={[data.tradeTypeIconUrl, ...data.stockTypeInfo.stockTypeIconUrls]} - iconNames={[data.tradeTypeName, ...data.stockTypeInfo.stockTypeNames]} + // iconUrls={[data.tradeTypeIconUrl, ...data.stockTypeInfo.stockTypeIconUrls]} + // iconNames={[data.tradeTypeName, ...data.stockTypeInfo.stockTypeNames]} title={data.strategyName} profile={{ traderImage: data.traderImgUrl, @@ -43,8 +45,21 @@ const StrategiesItem = ({ strategiesData: data }: Props) => { <span>최근 1년 수익률</span> <p>{data.recentYearProfitLossRate ? data.recentYearProfitLossRate + '%' : '-'}</p> </div> - <div className={cx('subscribe')}> - <Subscribe subscriptionStatus={data.isSubscribed} /> + {type === 'default' && ( + <div className={cx('subscribe')}> + <Subscribe subscriptionStatus={data.isSubscribed} /> + </div> + )} + <div className={cx('public')}> + <p>{data.isPublic === 'PUBLIC' ? '공개' : '비공개'}</p> + </div> + <div className={cx('manage-buttons')}> + <Button size="small" variant="filled"> + 관리 + </Button> + <Button size="small" variant="outline"> + 삭제 + </Button> </div> </Link> ) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx index 639c7884..2d7ffdcd 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx @@ -54,6 +54,7 @@ Primary.args = { averageRating: 4.9, totalReviews: 34, isSubscribed: true, + isPublic: '', }, { strategyId: 2, @@ -88,6 +89,7 @@ Primary.args = { averageRating: 4.6, totalReviews: 19, isSubscribed: false, + isPublic: '', }, ], } diff --git a/app/(dashboard)/my/_api/get-my-strategy-list.ts b/app/(dashboard)/my/_api/get-my-strategy-list.ts index ca079c0c..cb55d468 100644 --- a/app/(dashboard)/my/_api/get-my-strategy-list.ts +++ b/app/(dashboard)/my/_api/get-my-strategy-list.ts @@ -1,19 +1,27 @@ import axiosInstance from '@/shared/api/axios' import { StrategiesModel } from '@/shared/types/strategy-details-data' -// 실제 api 나오면 수정 필요함 -// totalElements 사용해서 hasmore값 계산해야될 것 같음 - interface StrategiesResponseModel { + isSuccess: boolean + message: string result: { - strategies: StrategiesModel[] - hasMore: boolean + content: StrategiesModel[] + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean } } -export const getMyStrategyList = async ({ page = 1, size = 4 }: { page: number; size: number }) => { +export const getMyStrategyList = async ({ page = 1, size }: { page: number; size: number }) => { const response = await axiosInstance.get<StrategiesResponseModel>( - `/api/my-strategies?page=${page}&size=${size}` + `/api/my-strategies?userId=1&page=${page}&size=${size}` ) - return response.data.result + const { content, totalElements, page: page_, size: size_ } = response.data.result + return { + strategies: content, + hasMore: totalElements > page_ * size_, + } } diff --git a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx index 29e5edc1..e7833987 100644 --- a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx +++ b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx @@ -27,11 +27,10 @@ const MyStrategyList = () => { }) const strategies = data?.pages.flatMap((page) => page.strategies) || [] - return ( <> {strategies.map((strategy) => ( - <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> + <StrategiesItem key={strategy.strategyId} strategiesData={strategy} type="my" /> ))} <div ref={loadMoreRef} /> {isFetchingNextPage && <div>로딩 중...</div>} diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts index 73c75794..81e6c943 100644 --- a/mocks/handlers/strategies.ts +++ b/mocks/handlers/strategies.ts @@ -36,6 +36,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.9, totalReviews: 34, isSubscribed: true, + isPublic: '', }, { strategyId: 2, @@ -70,6 +71,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.6, totalReviews: 19, isSubscribed: false, + isPublic: '', }, { strategyId: 3, @@ -104,6 +106,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.7, totalReviews: 45, isSubscribed: true, + isPublic: '', }, { strategyId: 4, @@ -138,6 +141,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.5, totalReviews: 29, isSubscribed: false, + isPublic: '', }, { strategyId: 5, @@ -172,6 +176,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.8, totalReviews: 22, isSubscribed: true, + isPublic: '', }, { strategyId: 6, @@ -206,6 +211,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.9, totalReviews: 52, isSubscribed: true, + isPublic: '', }, { strategyId: 7, @@ -240,6 +246,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.4, totalReviews: 18, isSubscribed: false, + isPublic: '', }, { strategyId: 8, @@ -274,6 +281,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.7, totalReviews: 37, isSubscribed: true, + isPublic: '', }, { strategyId: 9, @@ -308,6 +316,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.3, totalReviews: 11, isSubscribed: false, + isPublic: '', }, { strategyId: 10, @@ -342,6 +351,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.9, totalReviews: 48, isSubscribed: true, + isPublic: '', }, { strategyId: 11, @@ -376,6 +386,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.6, totalReviews: 26, isSubscribed: false, + isPublic: '', }, { strategyId: 12, @@ -410,6 +421,7 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.5, totalReviews: 21, isSubscribed: true, + isPublic: '', }, ] diff --git a/shared/api/axios.ts b/shared/api/axios.ts index 79c49a4d..d63d8326 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -8,7 +8,9 @@ import { getUserFromToken, isTokenExpired, refreshToken } from '@/shared/utils/t import { isAdmin } from '../types/auth' export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { - const instance = axios.create({ baseURL: 'http://15.164.90.102:8081' }) + const instance = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_HOST, + }) instance.interceptors.request.use((config) => { if ( diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts index 10a42157..0ac4c576 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-details-data.ts @@ -44,6 +44,7 @@ export interface StrategiesModel { averageRating: number totalReviews: number isSubscribed: boolean + isPublic: string } export interface StrategyDetailsInformationModel { diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 2f4456fc..2b19a30a 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -77,4 +77,3 @@ const VerticalTable = ({ } export default VerticalTable - From 0c2edabb71146b3b1ce1a2e68d97c6a311e7c248 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 30 Nov 2024 14:25:05 +0900 Subject: [PATCH 429/648] =?UTF-8?q?design:=20=EB=82=98=EC=9D=98=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/list-header/styles.module.scss | 2 +- .../_ui/strategies-item/styles.module.scss | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/_ui/list-header/styles.module.scss b/app/(dashboard)/_ui/list-header/styles.module.scss index ee5053ef..b7f9ddd2 100644 --- a/app/(dashboard)/_ui/list-header/styles.module.scss +++ b/app/(dashboard)/_ui/list-header/styles.module.scss @@ -6,7 +6,7 @@ margin: 20px 0 10px; &.my { - grid-template-columns: 3fr 1.5fr 1.7fr 1.3fr 1.5fr 1.2fr 1.2fr; + grid-template-columns: 2.5fr 2.4fr 1.5fr 1.1fr 1.3fr 1fr 1fr; } .category { diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index ca870fea..f4b61780 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -6,12 +6,27 @@ width: 100%; align-items: center; background-color: $color-white; + &.my { + grid-template-columns: 2.5fr 2.4fr 1.5fr 1.1fr 1.3fr 1fr 1fr; + } .mdd, .sm-score, .profit, - .subscribe { + .subscribe, + .public, + .manage-buttons { place-items: center center; } + .manage-buttons { + display: flex; + flex-direction: column; + gap: 8px; + button { + width: 74px; + height: 30px; + padding: 7px 16px; + } + } .mdd, .sm-score { @include typo-b2; From 41a38ea8c012f5eca539a33d7e27610e2342800b Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 30 Nov 2024 14:49:53 +0900 Subject: [PATCH 430/648] =?UTF-8?q?fix:=20strategies-item=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=93=B1=20=EC=98=B5=EC=85=94=EB=84=90=20=EB=9E=9C?= =?UTF-8?q?=EB=8D=94=EB=A7=81=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/strategies-item/index.tsx | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 735b0039..10f803e6 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -50,17 +50,21 @@ const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { <Subscribe subscriptionStatus={data.isSubscribed} /> </div> )} - <div className={cx('public')}> - <p>{data.isPublic === 'PUBLIC' ? '공개' : '비공개'}</p> - </div> - <div className={cx('manage-buttons')}> - <Button size="small" variant="filled"> - 관리 - </Button> - <Button size="small" variant="outline"> - 삭제 - </Button> - </div> + {type === 'my' && ( + <> + <div className={cx('public')}> + <p>{data.isPublic === 'PUBLIC' ? '공개' : '비공개'}</p> + </div> + <div className={cx('manage-buttons')}> + <Button size="small" variant="filled"> + 관리 + </Button> + <Button size="small" variant="outline"> + 삭제 + </Button> + </div> + </> + )} </Link> ) } From 48c20f908137c25e66f91cfe1f1dc24cb393822d Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 30 Nov 2024 14:50:19 +0900 Subject: [PATCH 431/648] =?UTF-8?q?fix:=20StrategiesModel=20isPublic=20?= =?UTF-8?q?=EC=98=B5=EC=85=94=EB=84=90=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#1?= =?UTF-8?q?63)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/types/strategy-details-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/types/strategy-details-data.ts b/shared/types/strategy-details-data.ts index 0ac4c576..509c0925 100644 --- a/shared/types/strategy-details-data.ts +++ b/shared/types/strategy-details-data.ts @@ -44,7 +44,7 @@ export interface StrategiesModel { averageRating: number totalReviews: number isSubscribed: boolean - isPublic: string + isPublic?: string } export interface StrategyDetailsInformationModel { From ddcf29c199e5a268cd74dd2e71fe77aea5ed69e0 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 30 Nov 2024 15:23:04 +0900 Subject: [PATCH 432/648] =?UTF-8?q?feat:=20=EA=B5=AC=EB=8F=85=EC=9E=90=20?= =?UTF-8?q?=EC=83=81=EC=9C=84=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EC=97=B0=EA=B2=B0=20(#169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_api/top-strategies.ts | 11 +++ .../_hooks/query/use-get-top-ranking.ts | 14 ++++ .../(home)/_ui/top-favorite-section/index.tsx | 71 +++++-------------- next.config.mjs | 4 ++ shared/types/strategy-data.ts | 13 ++++ 5 files changed, 61 insertions(+), 52 deletions(-) create mode 100644 app/(landing)/(home)/_api/top-strategies.ts create mode 100644 app/(landing)/(home)/_hooks/query/use-get-top-ranking.ts diff --git a/app/(landing)/(home)/_api/top-strategies.ts b/app/(landing)/(home)/_api/top-strategies.ts new file mode 100644 index 00000000..02b0e00b --- /dev/null +++ b/app/(landing)/(home)/_api/top-strategies.ts @@ -0,0 +1,11 @@ +import axios from 'axios' + +export const getTopRanking = async () => { + try { + const response = await axios.get('/api/main/top-ranking') + return response.data.result + } catch (err) { + console.error(err) + throw new Error('구독수 상위 전략 조회에 실패했습니다.') + } +} diff --git a/app/(landing)/(home)/_hooks/query/use-get-top-ranking.ts b/app/(landing)/(home)/_hooks/query/use-get-top-ranking.ts new file mode 100644 index 00000000..2f8f3429 --- /dev/null +++ b/app/(landing)/(home)/_hooks/query/use-get-top-ranking.ts @@ -0,0 +1,14 @@ +import { useQuery } from '@tanstack/react-query' + +import { StrategyCardModel } from '@/shared/types/strategy-data' + +import { getTopRanking } from '../../_api/top-strategies' + +const useGetTopRanking = () => { + return useQuery<StrategyCardModel[]>({ + queryKey: ['topRanking'], + queryFn: getTopRanking, + }) +} + +export default useGetTopRanking diff --git a/app/(landing)/(home)/_ui/top-favorite-section/index.tsx b/app/(landing)/(home)/_ui/top-favorite-section/index.tsx index e4645dc8..51d3321d 100644 --- a/app/(landing)/(home)/_ui/top-favorite-section/index.tsx +++ b/app/(landing)/(home)/_ui/top-favorite-section/index.tsx @@ -1,8 +1,11 @@ +'use client' + import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { LinkButton } from '@/shared/ui/link-button' +import useGetTopRanking from '../../_hooks/query/use-get-top-ranking' import HomeSubtitle from '../home-subtitle' import TopFavoriteCard from '../top-strategy-card/top-favorite-card' import styles from './styles.module.scss' @@ -10,44 +13,7 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const TopFavoriteSection = () => { - const favoriteStrategies = [ - { - strategyId: 3, - strategyName: '내 전략은 엄청나', - traderImgUrl: '', - nickname: '엄청난 트레이더', - profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], - smScore: 75.0, - cumulativeProfitRate: 60.63, - subscriptionCount: 1234, - averageRating: 5.0, - totalReviews: 32, - }, - { - strategyId: 1, - strategyName: '내 전략은 꽤나 대단해', - traderImgUrl: '', - nickname: '대단한 트레이더', - profitRateChartData: [15.0, 32.0, 98.0, 29.0, 1.0, 59.0], - smScore: 80.0, - cumulativeProfitRate: 50.0, - subscriptionCount: 759, - averageRating: 4.9, - totalReviews: 62, - }, - { - strategyId: 2, - strategyName: '나 유명한데 한번 믿어봐', - traderImgUrl: '', - nickname: '엄청난 트레이더', - profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], - smScore: 70.0, - cumulativeProfitRate: 57.0, - subscriptionCount: 777, - averageRating: 5.0, - totalReviews: 20, - }, - ] + const { data: favoriteStrategies } = useGetTopRanking() return ( <section className={cx('section-container')}> @@ -57,20 +23,21 @@ const TopFavoriteSection = () => { </HomeSubtitle> <ul className={cx('strategy-wrapper')}> - {favoriteStrategies.map((strategy, idx) => ( - <li key={strategy.strategyId}> - <TopFavoriteCard - ranking={idx + 1} - nickname={strategy.nickname} - title={strategy.strategyName} - chartData={strategy.profitRateChartData} - percentageChange={strategy.cumulativeProfitRate} - subscriptionCount={strategy.subscriptionCount} - averageRating={strategy.averageRating} - reviewCount={strategy.totalReviews} - /> - </li> - ))} + {favoriteStrategies && + favoriteStrategies.map((strategy, idx) => ( + <li key={strategy.strategyId}> + <TopFavoriteCard + ranking={idx + 1} + nickname={strategy.nickname} + title={strategy.strategyName} + chartData={strategy.profitRateChartData} + percentageChange={strategy.cumulativeProfitRate} + subscriptionCount={strategy.subscriptionCount} + averageRating={strategy.averageRating} + reviewCount={strategy.totalReviews} + /> + </li> + ))} </ul> <LinkButton href={PATH.STRATEGIES} variant="filled"> diff --git a/next.config.mjs b/next.config.mjs index e8631f38..2ac2cd7d 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -23,6 +23,10 @@ const nextConfig = { source: '/api/strategies/:path*', destination: 'http://15.164.90.102:8081/api/strategies/:path*', }, + { + source: '/api/main/:path*', + destination: 'http://15.164.90.102:8081/api/main/:path*', + }, ] }, webpack: (config, { isServer }) => { diff --git a/shared/types/strategy-data.ts b/shared/types/strategy-data.ts index e8da9f71..62d9791d 100644 --- a/shared/types/strategy-data.ts +++ b/shared/types/strategy-data.ts @@ -62,3 +62,16 @@ export interface StrategyDetailsInformationModel extends BaseStrategyModel { finalProfitLossDate: string createdAt: string } + +export interface StrategyCardModel { + strategyId: number + strategyName: string + TraderImgUrl: string + nickname: string + profitRateChartData: number[] + smScore: number + cumulativeProfitRate: number + subscriptionCount: number + averageRating: number + totalReviews: number +} From 8d67fbd2d5e408900745084f38bb9c6b97b5708c Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 30 Nov 2024 15:24:08 +0900 Subject: [PATCH 433/648] =?UTF-8?q?feat:=20smscore=20=EC=83=81=EC=9C=84=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20api=20=EC=97=B0=EA=B2=B0=20(#169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_api/top-strategies.ts | 10 ++ .../query/use-get-top-ranking-smscore.ts | 14 +++ .../(home)/_ui/top-sm-score-section/index.tsx | 93 ++++--------------- 3 files changed, 42 insertions(+), 75 deletions(-) create mode 100644 app/(landing)/(home)/_hooks/query/use-get-top-ranking-smscore.ts diff --git a/app/(landing)/(home)/_api/top-strategies.ts b/app/(landing)/(home)/_api/top-strategies.ts index 02b0e00b..f67aaffd 100644 --- a/app/(landing)/(home)/_api/top-strategies.ts +++ b/app/(landing)/(home)/_api/top-strategies.ts @@ -9,3 +9,13 @@ export const getTopRanking = async () => { throw new Error('구독수 상위 전략 조회에 실패했습니다.') } } + +export const getTopRankingSmScore = async () => { + try { + const response = await axios.get('/api/main/top-ranking-smscore') + return response.data.result + } catch (err) { + console.error(err) + throw new Error('SM Score 상위 전략 조회에 실패했습니다.') + } +} diff --git a/app/(landing)/(home)/_hooks/query/use-get-top-ranking-smscore.ts b/app/(landing)/(home)/_hooks/query/use-get-top-ranking-smscore.ts new file mode 100644 index 00000000..1b632f85 --- /dev/null +++ b/app/(landing)/(home)/_hooks/query/use-get-top-ranking-smscore.ts @@ -0,0 +1,14 @@ +import { useQuery } from '@tanstack/react-query' + +import { StrategyCardModel } from '@/shared/types/strategy-data' + +import { getTopRankingSmScore } from '../../_api/top-strategies' + +const useGetTopRankingSmScore = () => { + return useQuery<StrategyCardModel[]>({ + queryKey: ['topRankingSmScore'], + queryFn: getTopRankingSmScore, + }) +} + +export default useGetTopRankingSmScore diff --git a/app/(landing)/(home)/_ui/top-sm-score-section/index.tsx b/app/(landing)/(home)/_ui/top-sm-score-section/index.tsx index f36dbf99..52b699df 100644 --- a/app/(landing)/(home)/_ui/top-sm-score-section/index.tsx +++ b/app/(landing)/(home)/_ui/top-sm-score-section/index.tsx @@ -1,5 +1,8 @@ +'use client' + import classNames from 'classnames/bind' +import useGetTopRankingSmScore from '../../_hooks/query/use-get-top-ranking-smscore' import HomeSubtitle from '../home-subtitle' import TopSmScoreCard from '../top-strategy-card/top-sm-score-card' import styles from './styles.module.scss' @@ -7,87 +10,27 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const TopSmScoreSection = () => { - const topSmScoreStrategies = [ - { - strategyId: 3, - strategyName: '내 전략은 엄청나', - traderImgUrl: '', - nickname: '엄청난 트레이더', - profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], - smScore: 95.01, - cumulativeProfitRate: 60.6, - subscriptionCount: 1234, - averageRating: 5.0, - totalReviews: 32, - }, - { - strategyId: 1, - strategyName: '내 전략은 꽤나 대단해', - traderImgUrl: '', - nickname: '대단한 트레이더', - profitRateChartData: [15.0, 32.0, 98.0, 29.0, 1.0, 59.0], - smScore: 80.02, - cumulativeProfitRate: 50.0, - subscriptionCount: 759, - averageRating: 4.9, - totalReviews: 62, - }, - { - strategyId: 2, - strategyName: '나 유명한데 한번 믿어봐', - traderImgUrl: '', - nickname: '엄청난 트레이더', - profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], - smScore: 70.01, - cumulativeProfitRate: 57.0, - subscriptionCount: 777, - averageRating: 5.0, - totalReviews: 20, - }, - { - strategyId: 4, - strategyName: '나 유명한데 한번 믿어봐', - traderImgUrl: '', - nickname: '엄청난 트레이더', - profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], - smScore: 70.0, - cumulativeProfitRate: 57.0, - subscriptionCount: 777, - averageRating: 5.0, - totalReviews: 20, - }, - { - strategyId: 5, - strategyName: '나 유명한데 한번 믿어봐', - traderImgUrl: '', - nickname: '엄청난 트레이더', - profitRateChartData: [10, 9, 7, 12, 15, 13, 18, 34, 27, 40], - smScore: 70.0, - cumulativeProfitRate: 57.0, - subscriptionCount: 777, - averageRating: 5.0, - totalReviews: 20, - }, - ] + const { data: topSmScoreStrategies } = useGetTopRankingSmScore() return ( <section className={cx('section-container')}> <HomeSubtitle>높은 SM 스코어별로 전략을 확인해보세요!</HomeSubtitle> <ul className={cx('strategy-wrapper')}> - {topSmScoreStrategies.map((strategy, idx) => ( - <li key={strategy.strategyId}> - <TopSmScoreCard - size={idx > 0 ? 'small' : 'large'} - ranking={idx + 1} - nickname={strategy.nickname} - title={strategy.strategyName} - chartData={strategy.profitRateChartData} - percentageChange={strategy.cumulativeProfitRate} - score={strategy.smScore} - /> - </li> - ))} + {topSmScoreStrategies && + topSmScoreStrategies.map((strategy, idx) => ( + <li key={strategy.strategyId}> + <TopSmScoreCard + size={idx > 0 ? 'small' : 'large'} + ranking={idx + 1} + nickname={strategy.nickname} + title={strategy.strategyName} + chartData={strategy.profitRateChartData} + percentageChange={strategy.cumulativeProfitRate} + score={strategy.smScore} + /> + </li> + ))} </ul> </section> ) From 7679aa060e50c19c159e6b2a2eb5879f34396bb3 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Sat, 30 Nov 2024 15:28:44 +0900 Subject: [PATCH 434/648] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/strategies-item.stories.tsx | 2 -- mocks/handlers/strategies.ts | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx index d7f0c296..7211a056 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-item.stories.tsx @@ -54,7 +54,6 @@ Primary.args = { averageRating: 4.9, totalReviews: 34, isSubscribed: true, - isPublic: '', }, { strategyId: 2, @@ -89,7 +88,6 @@ Primary.args = { averageRating: 4.6, totalReviews: 19, isSubscribed: false, - isPublic: '', }, ], } diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts index 739a380a..4d0ed4b5 100644 --- a/mocks/handlers/strategies.ts +++ b/mocks/handlers/strategies.ts @@ -36,7 +36,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.9, totalReviews: 34, isSubscribed: true, - isPublic: '', }, { strategyId: 2, @@ -71,7 +70,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.6, totalReviews: 19, isSubscribed: false, - isPublic: '', }, { strategyId: 3, @@ -106,7 +104,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.7, totalReviews: 45, isSubscribed: true, - isPublic: '', }, { strategyId: 4, @@ -141,7 +138,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.5, totalReviews: 29, isSubscribed: false, - isPublic: '', }, { strategyId: 5, @@ -176,7 +172,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.8, totalReviews: 22, isSubscribed: true, - isPublic: '', }, { strategyId: 6, @@ -211,7 +206,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.9, totalReviews: 52, isSubscribed: true, - isPublic: '', }, { strategyId: 7, @@ -246,7 +240,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.4, totalReviews: 18, isSubscribed: false, - isPublic: '', }, { strategyId: 8, @@ -281,7 +274,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.7, totalReviews: 37, isSubscribed: true, - isPublic: '', }, { strategyId: 9, @@ -316,7 +308,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.3, totalReviews: 11, isSubscribed: false, - isPublic: '', }, { strategyId: 10, @@ -351,7 +342,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.9, totalReviews: 48, isSubscribed: true, - isPublic: '', }, { strategyId: 11, @@ -386,7 +376,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.6, totalReviews: 26, isSubscribed: false, - isPublic: '', }, { strategyId: 12, @@ -421,7 +410,6 @@ export const strategiesMockData: StrategiesModel[] = [ averageRating: 4.5, totalReviews: 21, isSubscribed: true, - isPublic: '', }, ] From 52ad2b6ddb763a1d7cc9fffd9a7e7afe0894a09b Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Sat, 30 Nov 2024 15:36:53 +0900 Subject: [PATCH 435/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20=ED=85=9D=20=ED=8B=80?= =?UTF-8?q?=20=EC=99=84=EC=84=B1=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/manage-table/index.tsx | 34 ++++++++++++++ .../shared/manage-table/styles.module.scss | 11 +++++ .../manage-table/utils/add-index-to-data.tsx | 26 +++++++++++ .../_ui/trade/trade-manage/index.tsx | 45 ++++++++++++++----- .../_ui/trade/trade-manage/styles.module.scss | 7 +++ shared/ui/table/vertical/index.tsx | 1 - 6 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 app/admin/strategies/_ui/shared/manage-table/index.tsx create mode 100644 app/admin/strategies/_ui/shared/manage-table/styles.module.scss create mode 100644 app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx create mode 100644 app/admin/strategies/_ui/trade/trade-manage/styles.module.scss diff --git a/app/admin/strategies/_ui/shared/manage-table/index.tsx b/app/admin/strategies/_ui/shared/manage-table/index.tsx new file mode 100644 index 00000000..68387341 --- /dev/null +++ b/app/admin/strategies/_ui/shared/manage-table/index.tsx @@ -0,0 +1,34 @@ +import { ReactNode } from 'react' + +import classNames from 'classnames/bind' + +import Pagination from '@/shared/ui/pagination' +import VerticalTable from '@/shared/ui/table/vertical' + +import styles from './styles.module.scss' +import addIndexToData from './utils/add-index-to-data' + +const cx = classNames.bind(styles) + +interface Props { + active?: boolean + domain: '종목' | '매매 유형' + data: (ReactNode | string | number)[][] +} + +const ManageTable = ({ active, domain, data }: Props) => { + return ( + <div className={cx('container')}> + <span className={cx('title')}>{active ? '활성화' : '비활성화'}</span> + <VerticalTable + tableHead={['No.', domain === '종목' ? '종목명' : '매매 유형', '분류', '상태']} + tableBody={addIndexToData(data, active)} + countPerPage={8} + currentPage={1} + /> + <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + </div> + ) +} + +export default ManageTable diff --git a/app/admin/strategies/_ui/shared/manage-table/styles.module.scss b/app/admin/strategies/_ui/shared/manage-table/styles.module.scss new file mode 100644 index 00000000..e94f4a1d --- /dev/null +++ b/app/admin/strategies/_ui/shared/manage-table/styles.module.scss @@ -0,0 +1,11 @@ +.container { + width: 625px; + padding: 21px 20px 52px; + background-color: $color-white; +} + +.title { + display: block; + margin-left: 20px; + margin-bottom: 45px; +} diff --git a/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx b/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx new file mode 100644 index 00000000..caa3feea --- /dev/null +++ b/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx @@ -0,0 +1,26 @@ +import { CSSProperties } from 'react' + +import { Button } from '@/shared/ui/button' + +const addIndexAndButton = (data: any[][], active?: boolean) => { + return data?.map((d, idx) => [ + idx + 1, + ...d, + <Button + variant={active ? 'outline' : 'filled'} + size="small" + style={buttonStyles} + key={idx} + > + {active ? '비활성화' : '활성화'} + </Button>, + ]) +} + +const buttonStyles: CSSProperties = { + height: '30px', + padding: '7px 16px', + margin: '15px 0', +} + +export default addIndexAndButton diff --git a/app/admin/strategies/_ui/trade/trade-manage/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/index.tsx index eaf032b8..d091b4c3 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/index.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/index.tsx @@ -1,18 +1,43 @@ -import Pagination from '@/shared/ui/pagination' -import VerticalTable from '@/shared/ui/table/vertical' +import { CSSProperties } from 'react' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import ManageTable from '../../shared/manage-table' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +// const activeMock = [ +// ['K100 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K200 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K50 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K200 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K100 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K50 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K200 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K100 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ['K50 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], +// ] const TradeManage = () => { return ( - <div> - <VerticalTable - tableHead={['No.', '종목명', '분류', '상태']} - tableBody={[]} - countPerPage={8} - currentPage={1} - /> - <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + <div className={cx('container')}> + <Button variant="filled" size="small" style={buttonStyles}> + 매매 유형 등록하기 + </Button> + <ManageTable data={[]} active domain="매매 유형" /> + <ManageTable data={[]} domain="매매 유형" /> </div> ) } +const buttonStyles: CSSProperties = { + position: 'absolute', + top: '-144px', + right: 0, + padding: '12px 24px', +} + export default TradeManage diff --git a/app/admin/strategies/_ui/trade/trade-manage/styles.module.scss b/app/admin/strategies/_ui/trade/trade-manage/styles.module.scss new file mode 100644 index 00000000..1a20c271 --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/styles.module.scss @@ -0,0 +1,7 @@ +.container { + position: relative; + margin-top: 32px; + display: flex; + justify-content: space-between; + gap: 50px; +} diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 2f4456fc..2b19a30a 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -77,4 +77,3 @@ const VerticalTable = ({ } export default VerticalTable - From 99da6973be85b374cd924e05b164955941e8967c Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 30 Nov 2024 18:38:54 +0900 Subject: [PATCH 436/648] =?UTF-8?q?feat:=20=EC=83=81=EC=84=B8=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=B0=A8=ED=8A=B8=20=EC=A4=8C=20=EC=98=B5=EC=85=98?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/analysis-container/analysis-chart.tsx | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 app/(dashboard)/_ui/analysis-container/analysis-chart.tsx diff --git a/app/(dashboard)/_ui/analysis-container/analysis-chart.tsx b/app/(dashboard)/_ui/analysis-container/analysis-chart.tsx new file mode 100644 index 00000000..bbc5e72f --- /dev/null +++ b/app/(dashboard)/_ui/analysis-container/analysis-chart.tsx @@ -0,0 +1,183 @@ +'use client' + +import dynamic from 'next/dynamic' + +import classNames from 'classnames/bind' +import Highcharts, { SeriesOptionsType } from 'highcharts' + +import styles from './styles.module.scss' +import { YAXIS_OPTIONS } from './yaxis-options' + +const HighchartsReact = dynamic(() => import('highcharts-react-official'), { + ssr: false, +}) + +const cx = classNames.bind(styles) + +type YAxisType = keyof typeof YAXIS_OPTIONS + +interface AnalysisChartDataModel { + dates: string[] + data: { + [key in YAxisType]?: number[] + } +} + +interface Props { + analysisChartData: AnalysisChartDataModel +} + +const AnalysisChart = ({ analysisChartData: data }: Props) => { + const getOptionName = (sequence: number) => { + const key = Object.keys(data.data)[sequence] as YAxisType | undefined + return key ? YAXIS_OPTIONS[key] : '' + } + if (!data) return <p>등록된 데이터가 없습니다.</p> + const chartOptions: Highcharts.Options = { + chart: { + type: 'areaspline', + height: 367, + backgroundColor: 'transparent', + margin: [10, 50, 10, 50], + zoomType: 'x', + } as Highcharts.ChartOptions, + title: { text: undefined }, + xAxis: { + visible: false, + categories: data.dates, + min: data.dates.length > 30 ? data.dates.length - 30 : 0, + max: data.dates.length - 1, + }, + yAxis: [ + { + title: { + text: getOptionName(0), + style: { + color: '#797979', + fontSize: '10px', + }, + }, + labels: { + style: { + color: '#797979', + fontSize: '10px', + }, + }, + }, + { + title: { + text: getOptionName(1), + style: { + color: '#797979', + fontSize: '10px', + }, + }, + opposite: true, + labels: { + style: { + color: '#797979', + fontSize: '10px', + }, + }, + }, + ], + legend: { + enabled: true, + align: 'left', + verticalAlign: 'top', + layout: 'vertical', + x: 40, + y: -10, + itemStyle: { + color: '#4D4D4D', + fontSize: '12px', + }, + backgroundColor: '#FFFFFF', + borderColor: '#A7A7A7', + borderRadius: 4, + borderWidth: 1, + padding: 5, + }, + tooltip: { + useHTML: true, + headerFormat: '<div style="margin-bottom: 5px;">{point.key}</div>', + pointFormat: '<b>{point.y:.2f}</b>', + footerFormat: '', + borderColor: '#ECECEC', + borderWidth: 1, + shadow: false, + backgroundColor: '#FFFFFF', + style: { + padding: '10px', + }, + }, + plotOptions: { + areaspline: { + fillOpacity: 0.5, + lineWidth: 1, + marker: { + enabled: false, + }, + fillColor: { + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, + stops: [ + [0, '#ffbfad'], + [1, '#FFFFFF'], + ], + }, + }, + spline: { + lineWidth: 1, + marker: { + enabled: false, + }, + }, + }, + series: [ + { + type: 'areaspline', + name: getOptionName(0), + data: Object.values(data.data)[0], + color: '#ff5f33', + yAxis: 0, + stickyTracking: false, + pointPlacement: 'on', + }, + ...(Object.values(data.data)[1] + ? [ + { + type: 'spline', + name: getOptionName(1), + data: Object.values(data.data)[1], + color: '#6877FF', + yAxis: 1, + stickyTracking: false, + pointPlacement: 'on', + }, + ] + : []), + ] as SeriesOptionsType[], + responsive: { + rules: [ + { + condition: { + maxWidth: 960, + }, + chartOptions: { + chart: { + width: null, + }, + }, + }, + ], + }, + credits: { enabled: false }, + } + return ( + <div className={cx('chart')}> + <HighchartsReact highcharts={Highcharts} options={chartOptions} /> + </div> + ) +} + +export default AnalysisChart From 9023e7f8ff53354d2591ad9da937982550e02d85 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 30 Nov 2024 18:40:30 +0900 Subject: [PATCH 437/648] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=8B=B9=20=EA=B0=AF=EC=88=98=20=ED=95=9C=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B4=80=EB=A6=AC=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20constant=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/constants/count-per-page.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 shared/constants/count-per-page.ts diff --git a/shared/constants/count-per-page.ts b/shared/constants/count-per-page.ts new file mode 100644 index 00000000..59a0bf32 --- /dev/null +++ b/shared/constants/count-per-page.ts @@ -0,0 +1,7 @@ +export const STRATEGIES_PAGE_COUNT = 8 + +export const REVIEW_PAGE_COUNT = 4 + +export const ANALYSIS_PAGE_COUNT = 5 + +export const ACCOUNT_PAGE_COUNT = 10 From 9b0b1b0eb58162551df624c9fd78a7d91207b7b6 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 30 Nov 2024 18:41:50 +0900 Subject: [PATCH 438/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=A6=AC=EB=B7=B0&=EB=B6=84=EC=84=9D=20api=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=ED=95=A8=EC=88=98=20=EB=B0=8F=20tanstack=20q?= =?UTF-8?q?uery=20=EC=B6=94=EA=B0=80=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_api/get-analysis-chart.ts | 3 +-- .../[strategyId]/_api/get-analysis.ts | 21 ++++++++++++++++ .../[strategyId]/_api/get-reviews.ts | 6 ++--- .../[strategyId]/_api/get-statistics.ts | 12 +++++++++ .../[strategyId]/_api/post-review.ts | 16 ++++++++++++ .../_hooks/query/use-get-analysis-chart.ts | 2 +- .../_hooks/query/use-get-analysis.ts | 13 ++++++++++ .../_hooks/query/use-get-reviews-data.ts | 6 ++--- .../_hooks/query/use-get-statistics.ts | 12 +++++++++ .../_hooks/query/use-post-review.ts | 25 +++++++++++++++++++ 10 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/post-review.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-statistics.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts index 4fe6fe6b..7fa168a4 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts @@ -1,7 +1,6 @@ +import { AnalysisChartOptionsType } from '@/app/(dashboard)/_ui/analysis-container' import axios from 'axios' -import { AnalysisChartOptionsType } from '../_ui/analysis-container' - const getAnalysisChart = async ( strategyId: number, firstOption: AnalysisChartOptionsType, diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts new file mode 100644 index 00000000..810b4c2b --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts @@ -0,0 +1,21 @@ +import { AnalysisTabType } from '@/app/(dashboard)/_ui/analysis-container/tabs-width-table' +import axios from 'axios' + +const getAnalysis = async ( + strategyId: number, + type: AnalysisTabType, + page: number, + size: number +) => { + if (type !== 'daily' && type !== 'monthly') return null + try { + const response = await axios.get( + `/api/strategies/${strategyId}/${type}-analysis?page=${page}&size=${size}` + ) + return response.data.result + } catch (err) { + console.error(err, `${type} 분석 조회 실패`) + } +} + +export default getAnalysis diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts index 50519dad..99299d33 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts @@ -1,15 +1,15 @@ import axios from 'axios' -import { COUNT_PER_PAGE } from '../_ui/review-container/review-list' +import { REVIEW_PAGE_COUNT } from '@/shared/constants/count-per-page' const getReviews = async (strategyId: number, page: number | undefined) => { if (!strategyId && !page) return try { const response = await axios.get( - `/api/strategies/${strategyId}/reviews?page=${page}&size=${COUNT_PER_PAGE}` + `/api/strategies/${strategyId}/reviews?userId=1&page=${page}&size=${REVIEW_PAGE_COUNT}` ) - const data = await response.data + const data = await response.data.result return data } catch (err) { console.error(err, '리뷰 데이터 가져오기 실패') diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts new file mode 100644 index 00000000..60ab2291 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts @@ -0,0 +1,12 @@ +import axios from 'axios' + +const getStatistics = async (strategyId: number) => { + try { + const response = await axios.get(`/api/strategies/${strategyId}/statistics`) + return response.data.result + } catch (err) { + console.error(err) + } +} + +export default getStatistics diff --git a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts new file mode 100644 index 00000000..eef87d02 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts @@ -0,0 +1,16 @@ +import axios from 'axios' + +const postReview = async ( + strategyId: number, + content: { content: string; starRating: number } +): Promise<boolean | null> => { + try { + const response = await axios.post(`/api/strategies/${strategyId}/reviews?userId=1`, content) + return response.data.isSuccess + } catch (err) { + console.error(err, '리뷰 등록 실패') + return null + } +} + +export default postReview diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts index 0998b15e..4f913c85 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-chart.ts @@ -1,7 +1,7 @@ +import { AnalysisChartOptionsType } from '@/app/(dashboard)/_ui/analysis-container' import { useQuery } from '@tanstack/react-query' import getAnalysisChart from '../../_api/get-analysis-chart' -import { AnalysisChartOptionsType } from '../../_ui/analysis-container' interface Props { strategyId: number diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts new file mode 100644 index 00000000..24e53447 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts @@ -0,0 +1,13 @@ +import { AnalysisTabType } from '@/app/(dashboard)/_ui/analysis-container/tabs-width-table' +import { useQuery } from '@tanstack/react-query' + +import getAnalysis from '../../_api/get-analysis' + +const useGetAnalysis = (strategyId: number, type: AnalysisTabType, page: number, size: number) => { + return useQuery({ + queryKey: ['analysis', strategyId], + queryFn: () => getAnalysis(strategyId, type, page, size), + }) +} + +export default useGetAnalysis diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts index 48431ef0..dec69f32 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-reviews-data.ts @@ -3,16 +3,14 @@ import { useQuery } from '@tanstack/react-query' import getReviews from '../../_api/get-reviews' interface Props { - isReady: boolean strategyId: number page: number | undefined } -const useGetReviewsData = ({ isReady, strategyId, page }: Props) => { +const useGetReviewsData = ({ strategyId, page }: Props) => { return useQuery({ - queryKey: ['reviews', strategyId, page], + queryKey: ['reviews', strategyId], queryFn: () => getReviews(strategyId, page), - enabled: isReady, }) } diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-statistics.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-statistics.ts new file mode 100644 index 00000000..b633062d --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-statistics.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import getStatistics from '../../_api/get-statistics' + +const useGetStatistics = (strategyId: number) => { + return useQuery({ + queryKey: ['statistics', strategyId], + queryFn: () => getStatistics(strategyId), + }) +} + +export default useGetStatistics diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts new file mode 100644 index 00000000..c1f5ab3b --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts @@ -0,0 +1,25 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import postReview from '../../_api/post-review' + +const usePostReview = (strategyId: number) => { + const queryClient = useQueryClient() + return useMutation< + boolean | null, + undefined, + { strategyId: number; content: { content: string; starRating: number } } + >({ + mutationFn: ({ + strategyId, + content, + }: { + strategyId: number + content: { content: string; starRating: number } + }) => postReview(strategyId, content), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['reviews', strategyId] }) + }, + }) +} + +export default usePostReview From ec483fa1e4338d5a3c36a0591372d9d2e41c5ff8 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 30 Nov 2024 18:43:57 +0900 Subject: [PATCH 439/648] =?UTF-8?q?feat:=20tanstack=20query=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/analysis-content.tsx | 93 ++++++++++++++++++ .../_ui/analysis-container/index.tsx | 68 +++++++++++++ .../analysis-container/tabs-width-table.tsx | 96 +++++++++++++++++++ .../_ui/review-container/add-review.tsx | 39 +++++++- .../_ui/review-container/index.tsx | 6 +- 5 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 app/(dashboard)/_ui/analysis-container/analysis-content.tsx create mode 100644 app/(dashboard)/_ui/analysis-container/index.tsx create mode 100644 app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx diff --git a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx new file mode 100644 index 00000000..eb57120f --- /dev/null +++ b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx @@ -0,0 +1,93 @@ +import classNames from 'classnames/bind' + +import { ANALYSIS_PAGE_COUNT } from '@/shared/constants/count-per-page' +import { Button } from '@/shared/ui/button' +import Pagination from '@/shared/ui/pagination' +import VerticalTable from '@/shared/ui/table/vertical' + +import useGetAnalysis from '../../strategies/[strategyId]/_hooks/query/use-get-analysis' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const DAILY_TABLE_HEADER = [ + '날짜', + '원금', + '입출금', + '일 손익', + '일 손익률', + '누적 손익', + '누적 수익률', +] + +const MONTHLY_TABLE_HEADER = [ + '날짜', + '원금', + '입출금', + '월 손익', + '월 손익률', + '누적 손익', + '누적 수익률', +] + +interface Props { + type: 'daily' | 'monthly' + strategyId: number + currentPage: number + onPageChange: (page: number) => void + isEditable?: boolean +} + +const AnalysisContent = ({ + type, + strategyId, + currentPage, + onPageChange, + isEditable = false, +}: Props) => { + const { data: analysisData } = useGetAnalysis(strategyId, type, currentPage, ANALYSIS_PAGE_COUNT) + + const tableHeader = type === 'daily' ? DAILY_TABLE_HEADER : MONTHLY_TABLE_HEADER + + if (analysisData?.content === null || analysisData?.content === undefined) return null + + return ( + <div className={cx('table-wrapper', 'analysis')}> + {!isEditable && ( + <Button size="small" className={cx('excel-button')} variant="filled"> + 엑셀 다운받기 + </Button> + )} + {isEditable && ( + <div className={cx('button-container')}> + <div className={cx('button-wrapper')}> + <Button size="small" className={cx('upload-button')} variant="filled"> + 엑셀 업로드 + </Button> + <Button size="small" className={cx('upload-button')} variant="outline"> + 직접 입력 + </Button> + </div> + <Button size="small" variant="filled"> + 전체 삭제 + </Button> + </div> + )} + + <VerticalTable + tableHead={tableHeader} + tableBody={analysisData?.content} + currentPage={currentPage} + countPerPage={ANALYSIS_PAGE_COUNT} + isEditable={isEditable} + /> + <Pagination + currentPage={currentPage} + maxPage={analysisData?.totalPages} + onPageChange={onPageChange} + /> + </div> + ) +} + +export default AnalysisContent diff --git a/app/(dashboard)/_ui/analysis-container/index.tsx b/app/(dashboard)/_ui/analysis-container/index.tsx new file mode 100644 index 00000000..c6422eff --- /dev/null +++ b/app/(dashboard)/_ui/analysis-container/index.tsx @@ -0,0 +1,68 @@ +'use client' + +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import Select from '@/shared/ui/select' + +import useGetAnalysisChart from '../../strategies/[strategyId]/_hooks/query/use-get-analysis-chart' +import AnalysisChart from './analysis-chart' +import styles from './styles.module.scss' +import TabsWithTable from './tabs-width-table' +import { YAXIS_OPTIONS } from './yaxis-options' + +const cx = classNames.bind(styles) + +export type AnalysisChartOptionsType = keyof typeof YAXIS_OPTIONS + +interface Props { + strategyId: number + type?: 'default' | 'my' +} + +const AnalysisContainer = ({ strategyId, type = 'default' }: Props) => { + const [firstOption, setFirstOption] = useState<AnalysisChartOptionsType>('PRINCIPAL') + const [secondOption, setSecondOption] = + useState<AnalysisChartOptionsType>('CUMULATIVE_PROFIT_LOSS') + const { data: chartData } = useGetAnalysisChart({ strategyId, firstOption, secondOption }) + + const optionsToArray = Object.entries(YAXIS_OPTIONS) + const options: { value: string; label: string }[] = [] + + for (const [key, value] of optionsToArray) { + options.push({ value: key, label: value }) + } + + return ( + <div className={cx('container')}> + <div className={cx('analysis-header')}> + <p className={cx({ my: type === 'my' })}>분석</p> + {type === 'default' && ( + <div> + <Select + size="large" + options={options} + value={firstOption} + onChange={(newValue) => setFirstOption(newValue as AnalysisChartOptionsType)} + /> + <Select + size="large" + options={options} + value={secondOption} + onChange={(newValue) => setSecondOption(newValue as AnalysisChartOptionsType)} + /> + </div> + )} + </div> + {type === 'default' && ( + <div className={cx('chart-wrapper')}> + <AnalysisChart analysisChartData={chartData} /> + </div> + )} + <TabsWithTable strategyId={strategyId} isEditable={type === 'my' ? true : false} /> + </div> + ) +} + +export default AnalysisContainer diff --git a/app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx b/app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx new file mode 100644 index 00000000..b8fbeb08 --- /dev/null +++ b/app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx @@ -0,0 +1,96 @@ +'use client' + +import { useEffect, useState } from 'react' +import React from 'react' + +import { DailyGraphIcon, MoneyIcon, MonthlyGraphIcon, StatisticsIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Tabs from '@/shared/ui/tabs' + +import useGetStatistics from '../../strategies/[strategyId]/_hooks/query/use-get-statistics' +import AccountContent from './account-content' +import AnalysisContent from './analysis-content' +import StatisticsContent from './statistics-content' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +export type AnalysisTabType = 'statistics' | 'daily' | 'monthly' | 'account-images' +interface Props { + strategyId: number + isEditable?: boolean +} + +const TabsWithTable = ({ strategyId, isEditable = false }: Props) => { + const [activeTab, setActiveTab] = useState<AnalysisTabType>('statistics') + const [currentPage, setCurrentPage] = useState(1) + const { data: statisticsData } = useGetStatistics(strategyId) + + useEffect(() => { + setCurrentPage(1) + }, [activeTab]) + + const handlePageChange = (page: number) => setCurrentPage(page) + + const TABS = [ + { + id: 'statistics', + label: '통계', + icon: StatisticsIcon, + content: <StatisticsContent statisticsData={statisticsData} />, + }, + { + id: 'daily', + label: '일간분석', + icon: DailyGraphIcon, + content: ( + <AnalysisContent + type="daily" + strategyId={strategyId} + currentPage={currentPage} + onPageChange={handlePageChange} + isEditable={isEditable} + /> + ), + }, + { + id: 'monthly', + label: '월간분석', + icon: MonthlyGraphIcon, + content: ( + <AnalysisContent + type="monthly" + strategyId={strategyId} + currentPage={currentPage} + onPageChange={handlePageChange} + /> + ), + }, + { + id: 'account-images', + label: '실거래계좌', + icon: MoneyIcon, + content: ( + <div className={cx('table-wrapper')}> + <AccountContent + imagesData={[]} + currentPage={currentPage} + onPageChange={handlePageChange} + isEditable={isEditable} + /> + </div> + ), + }, + ] + + return ( + <Tabs + tabs={TABS} + activeTab={activeTab} + onTabChange={(id) => setActiveTab(id as AnalysisTabType)} + /> + ) +} + +export default TabsWithTable diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx index 6933368b..120453d4 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx @@ -5,28 +5,59 @@ import { useRef, useState } from 'react' import classNames from 'classnames/bind' import { Button } from '@/shared/ui/button' +import { ErrorMessage } from '@/shared/ui/error-message' import { Textarea } from '@/shared/ui/textarea' +import usePostReview from '../../_hooks/query/use-post-review' import StarRating from '../star-rating/index' import styles from './styles.module.scss' const cx = classNames.bind(styles) -const AddReview = () => { +interface Props { + strategyId: number +} + +const AddReview = ({ strategyId }: Props) => { const [starRatingValue, setStarRatingValue] = useState(0) + const [isEmpty, setIsEmpty] = useState(false) const textareaRef = useRef<HTMLTextAreaElement>(null) - + const { mutate } = usePostReview(strategyId) const handleStarRating = (idx: number) => setStarRatingValue(idx + 1) - const handleAddReview = () => {} + const handleAddReview = () => { + if (textareaRef.current) { + const review = textareaRef.current.value.trim() + if (review && starRatingValue > 0) { + const content = { + content: review, + starRating: starRatingValue, + } + mutate( + { strategyId, content }, + { + onSuccess: () => { + if (textareaRef.current) { + textareaRef.current.value = '' + setStarRatingValue(0) + } + }, + } + ) + } else { + setIsEmpty(true) + } + } + } return ( <div className={cx('add-review-wrapper')}> <div className={cx('textarea-wrapper')}> <Textarea rows={5} placeholder="리뷰를 작성해주세요." ref={textareaRef} /> + {isEmpty && <ErrorMessage errorMessage="리뷰 작성 또는 별점을 선택해주세요." />} </div> <div className={cx('add-button-wrapper')}> - <p>전략이 어땠나요?</p> + <p className={cx('strategy')}>전략이 어땠나요?</p> <StarRating starRatingValue={starRatingValue} onRatingChange={handleStarRating} /> <Button variant="filled" diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx index 5e4e926d..00eaeac3 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx @@ -4,7 +4,6 @@ import { useState } from 'react' import classNames from 'classnames/bind' -import { useMSWStore } from '@/shared/stores/msw' import TotalStar from '@/shared/ui/total-star' import useGetReviewsData from '../../_hooks/query/use-get-reviews-data' @@ -20,8 +19,7 @@ interface Props { const ReviewContainer = ({ strategyId }: Props) => { const [currentPage, setCurrentPage] = useState(1) - const isReady = useMSWStore((state) => state.isReady) - const { data: reviewData } = useGetReviewsData({ isReady, strategyId, page: currentPage }) + const { data: reviewData } = useGetReviewsData({ strategyId, page: currentPage }) return ( <div className={cx('container')}> @@ -33,7 +31,7 @@ const ReviewContainer = ({ strategyId }: Props) => { totalElements={reviewData?.reviews.totalElements} /> </div> - <AddReview /> + <AddReview strategyId={strategyId} /> {reviewData ? ( <ReviewList reviews={reviewData.reviews.content} From e5f4f9f7eb0d7d440cb1778a4105216e2eacb44b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 30 Nov 2024 18:45:00 +0900 Subject: [PATCH 440/648] =?UTF-8?q?fix:=20constant=20page=20count=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_ui/review-container/review-list.tsx | 5 ++--- app/(dashboard)/strategies/_ui/strategy-list/index.tsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx index 81c29cdc..f63f9aa2 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx @@ -1,5 +1,6 @@ import classNames from 'classnames/bind' +import { REVIEW_PAGE_COUNT } from '@/shared/constants/count-per-page' import { useAuthStore } from '@/shared/stores/use-auth-store' import Pagination from '@/shared/ui/pagination' @@ -8,8 +9,6 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -export const COUNT_PER_PAGE = 4 - interface ReviewContentModel { reviewId: number nickname: string @@ -48,7 +47,7 @@ const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props </ul> <Pagination currentPage={currentPage} - maxPage={Math.ceil(totalReview / COUNT_PER_PAGE)} + maxPage={Math.ceil(totalReview / REVIEW_PAGE_COUNT)} onPageChange={handlePageChange} /> </> diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index 1e628dd6..64f80a7f 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -4,6 +4,7 @@ import ListHeader from '@/app/(dashboard)/_ui/list-header' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import classNames from 'classnames/bind' +import { STRATEGIES_PAGE_COUNT } from '@/shared/constants/count-per-page' import { PATH } from '@/shared/constants/path' import { usePagination } from '@/shared/hooks/custom/use-pagination' import { useMSWStore } from '@/shared/stores/msw' @@ -14,13 +15,11 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -const COUNT_PER_PAGE = 8 - const StrategyList = () => { const isReady = useMSWStore((state) => state.isReady) const { size, page, handlePageChange } = usePagination({ basePath: PATH.STRATEGIES, - pageSize: COUNT_PER_PAGE, + pageSize: STRATEGIES_PAGE_COUNT, }) const { data } = useGetStrategiesData({ isReady, page, size }) From 8265f4919e95ea022bb63aadea2b19c774a6690a Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 30 Nov 2024 18:45:55 +0900 Subject: [PATCH 441/648] =?UTF-8?q?remove:=20=EC=8A=A4=EC=9B=A8=EA=B1=B0?= =?UTF-8?q?=20api=20=EC=99=80=20=EC=B6=A9=EB=8F=8C=EB=B0=A9=EC=A7=80?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=B4=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mocks/handlers/strategy-details.ts | 154 ----------------------------- 1 file changed, 154 deletions(-) diff --git a/mocks/handlers/strategy-details.ts b/mocks/handlers/strategy-details.ts index 7c627830..7b6c6e8c 100644 --- a/mocks/handlers/strategy-details.ts +++ b/mocks/handlers/strategy-details.ts @@ -140,128 +140,6 @@ export const strategiesDetailsInformationMockData: StrategyDetailsInformationMod }, ] -const reviewData = [ - { - strategyId: 1, - averageRating: 3.8, - reviewCount: 4, - reviews: { - content: [ - { - reviewId: 85, - nickname: '김아무개씨', - content: 'good strategy!', - imageUrl: '', - createdAt: '2024-11-17 19:31:44', - starRating: 4, - isOwner: false, - }, - { - reviewId: 84, - nickname: 'user2', - content: 'good strategy!', - imageUrl: '', - createdAt: '2024-11-17 19:24:55', - starRating: 5, - isOwner: false, - }, - { - reviewId: 83, - nickname: 'user3', - content: 'good strategy!', - imageUrl: '', - createdAt: '2024-11-17 19:24:38', - starRating: 5, - isOwner: false, - }, - { - reviewId: 82, - nickname: 'user4', - content: 'bad strategy!', - imageUrl: '', - createdAt: '2024-11-17 19:24:09', - starRating: 1, - isOwner: false, - }, - { - reviewId: 81, - nickname: 'user4', - content: 'bad strategy!', - imageUrl: '', - createdAt: '2024-11-17 19:24:09', - starRating: 1, - isOwner: false, - }, - { - reviewId: 80, - nickname: 'user4', - content: 'bad strategy!', - imageUrl: '', - createdAt: '2024-11-17 19:24:09', - starRating: 1, - isOwner: false, - }, - { - reviewId: 79, - nickname: 'user4', - content: 'bad strategy!', - imageUrl: '', - createdAt: '2024-11-17 19:24:09', - starRating: 1, - isOwner: false, - }, - ], - totalElements: 7, - }, - }, - { - strategyId: 2, - averageRating: 3.8, - reviewCount: 4, - reviews: { - content: [ - { - reviewId: 85, - nickname: '김아무개씨', - content: 'good strategy!', - imageUrl: '/images/profiles/user1.jpg', - createdAt: '2024-11-17 19:31:44', - starRating: 4, - isOwner: false, - }, - { - reviewId: 84, - nickname: 'user2', - content: 'good strategy!', - imageUrl: '/images/profiles/user1.jpg', - createdAt: '2024-11-17 19:24:55', - starRating: 5, - isOwner: false, - }, - { - reviewId: 83, - nickname: 'user3', - content: 'good strategy!', - imageUrl: '/images/profiles/user1.jpg', - createdAt: '2024-11-17 19:24:38', - starRating: 5, - isOwner: false, - }, - { - reviewId: 82, - nickname: 'user4', - content: 'bad strategy!', - imageUrl: '/images/profiles/user1.jpg', - createdAt: '2024-11-17 19:24:09', - starRating: 1, - isOwner: false, - }, - ], - totalElements: 4, - }, - }, -] - export const strategyDetailsHandlers = [ http.get('/api/strategies/:strategyId', ({ params }) => { const { strategyId } = params @@ -280,36 +158,4 @@ export const strategyDetailsHandlers = [ { status: 400 } ) }), - - http.get('/api/strategies/:strategyId/reviews', ({ params, request }) => { - const { strategyId } = params - const url = new URL(request.url) - const page = parseInt(url.searchParams.get('page') || '1') - const size = parseInt(url.searchParams.get('size') || '4') - - const reviewDataForStrategy = reviewData.find((item) => item.strategyId === Number(strategyId)) - if (!isNaN(page) && !isNaN(size) && reviewDataForStrategy) { - const { content, totalElements } = reviewDataForStrategy.reviews - const slicedContent = content.slice(size * (page - 1), size * (page - 1) + size) - - return HttpResponse.json({ - strategyId, - averageRating: reviewDataForStrategy.averageRating, - reviewCount: reviewDataForStrategy.reviewCount, - reviews: { - content: slicedContent, - totalElements, - }, - }) - } - - return HttpResponse.json( - { - isSuccess: false, - message: '전략의 리뷰를 찾을 수 없습니다.', - code: 4002, - }, - { status: 400 } - ) - }), ] From f742f74711d5dba35947c51696d09d3e547aeb9b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Sat, 30 Nov 2024 18:46:27 +0900 Subject: [PATCH 442/648] =?UTF-8?q?rename:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=EB=8F=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EC=96=B4=20=ED=8F=B4=EB=8D=94=20=EC=83=81?= =?UTF-8?q?=EC=9C=84=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/account-content.tsx | 7 +- .../analysis-chart.stories.tsx | 0 .../_ui/analysis-container/example.ts | 0 .../analysis-container/statistics-content.tsx | 2 + .../_ui/analysis-container/styles.module.scss | 0 .../_ui/analysis-container/yaxis-options.ts | 0 .../_ui/subscriber-item/index.tsx | 0 .../_ui/subscriber-item/styles.module.scss | 0 .../strategies/manage/[strategyId]/page.tsx | 4 +- .../_ui/analysis-container/analysis-chart.tsx | 150 ------------------ .../analysis-container/analysis-content.tsx | 90 ----------- .../_ui/analysis-container/index.tsx | 67 -------- .../analysis-container/tabs-width-table.tsx | 87 ---------- .../_ui/review-container/styles.module.scss | 2 +- .../strategies/[strategyId]/page.tsx | 5 +- 15 files changed, 11 insertions(+), 403 deletions(-) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/analysis-container/account-content.tsx (91%) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/analysis-container/analysis-chart.stories.tsx (100%) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/analysis-container/example.ts (100%) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/analysis-container/statistics-content.tsx (91%) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/analysis-container/styles.module.scss (100%) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/analysis-container/yaxis-options.ts (100%) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/subscriber-item/index.tsx (100%) rename app/(dashboard)/{strategies/[strategyId] => }/_ui/subscriber-item/styles.module.scss (100%) delete mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx delete mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx delete mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx delete mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/account-content.tsx b/app/(dashboard)/_ui/analysis-container/account-content.tsx similarity index 91% rename from app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/account-content.tsx rename to app/(dashboard)/_ui/analysis-container/account-content.tsx index 09718df7..fb298fad 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/account-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/account-content.tsx @@ -4,6 +4,7 @@ import Image from 'next/image' import classNames from 'classnames/bind' +import { ACCOUNT_PAGE_COUNT } from '@/shared/constants/count-per-page' import { Button } from '@/shared/ui/button' import Pagination from '@/shared/ui/pagination' @@ -11,8 +12,6 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -const COUNT_PER_PAGE = 10 - interface ImageDataModel { imageUrl: string title: string @@ -27,8 +26,8 @@ interface Props { const AccountContent = ({ imagesData, currentPage, onPageChange, isEditable = false }: Props) => { const croppedImagesData = imagesData?.slice( - COUNT_PER_PAGE * (currentPage - 1), - COUNT_PER_PAGE * (currentPage - 1) + COUNT_PER_PAGE + ACCOUNT_PAGE_COUNT * (currentPage - 1), + ACCOUNT_PAGE_COUNT * (currentPage - 1) + ACCOUNT_PAGE_COUNT ) const isTwoLines = croppedImagesData?.length || 0 >= 5 diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.stories.tsx b/app/(dashboard)/_ui/analysis-container/analysis-chart.stories.tsx similarity index 100% rename from app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.stories.tsx rename to app/(dashboard)/_ui/analysis-container/analysis-chart.stories.tsx diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/example.ts b/app/(dashboard)/_ui/analysis-container/example.ts similarity index 100% rename from app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/example.ts rename to app/(dashboard)/_ui/analysis-container/example.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/statistics-content.tsx b/app/(dashboard)/_ui/analysis-container/statistics-content.tsx similarity index 91% rename from app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/statistics-content.tsx rename to app/(dashboard)/_ui/analysis-container/statistics-content.tsx index c093d437..e9811c43 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/statistics-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/statistics-content.tsx @@ -18,6 +18,8 @@ interface Props { } const StatisticsContent = ({ statisticsData }: Props) => { + if (statisticsData === null || statisticsData === undefined) return null + const statisticsDataToArray = Object.entries(statisticsData) return ( diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss b/app/(dashboard)/_ui/analysis-container/styles.module.scss similarity index 100% rename from app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/styles.module.scss rename to app/(dashboard)/_ui/analysis-container/styles.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/yaxis-options.ts b/app/(dashboard)/_ui/analysis-container/yaxis-options.ts similarity index 100% rename from app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/yaxis-options.ts rename to app/(dashboard)/_ui/analysis-container/yaxis-options.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/index.tsx b/app/(dashboard)/_ui/subscriber-item/index.tsx similarity index 100% rename from app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/index.tsx rename to app/(dashboard)/_ui/subscriber-item/index.tsx diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/styles.module.scss b/app/(dashboard)/_ui/subscriber-item/styles.module.scss similarity index 100% rename from app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item/styles.module.scss rename to app/(dashboard)/_ui/subscriber-item/styles.module.scss diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index 4684c2bd..58aa872a 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -1,13 +1,13 @@ 'use client' +import AnalysisContainer from '@/app/(dashboard)/_ui/analysis-container' import DetailsInformation from '@/app/(dashboard)/_ui/details-information' import DetailsSideItem, { InformationModel, TitleType, } from '@/app/(dashboard)/_ui/details-side-item' +import SubscriberItem from '@/app/(dashboard)/_ui/subscriber-item' import useGetDetailsInformationData from '@/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data' -import AnalysisContainer from '@/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container' -import SubscriberItem from '@/app/(dashboard)/strategies/[strategyId]/_ui/subscriber-item' import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' import classNames from 'classnames/bind' diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx deleted file mode 100644 index 68012785..00000000 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-chart.tsx +++ /dev/null @@ -1,150 +0,0 @@ -'use client' - -import dynamic from 'next/dynamic' - -import classNames from 'classnames/bind' -import Highcharts, { SeriesOptionsType } from 'highcharts' - -import styles from './styles.module.scss' -import { YAXIS_OPTIONS } from './yaxis-options' - -const HighchartsReact = dynamic(() => import('highcharts-react-official'), { - ssr: false, -}) - -const cx = classNames.bind(styles) - -type YAxisType = keyof typeof YAXIS_OPTIONS - -interface AnalysisChartDataModel { - dates: string[] - data: { - [key in YAxisType]?: number[] - } -} - -interface Props { - analysisChartData: AnalysisChartDataModel -} - -const AnalysisChart = ({ analysisChartData: data }: Props) => { - const getOptionName = (sequence: number) => { - const key = Object.keys(data.data)[sequence] as YAxisType | undefined - return key ? YAXIS_OPTIONS[key] : '' - } - if (!data) return null - const chartOptions: Highcharts.Options = { - chart: { - type: 'areaspline', - height: 367, - backgroundColor: 'transparent', - margin: [0, 0, 0, 0], - }, - title: { text: undefined }, - xAxis: { - visible: false, - categories: data.dates, - startOnTick: true, - endOnTick: true, - }, - yAxis: [{ visible: false }, { visible: false }], - legend: { - enabled: true, - align: 'left', - verticalAlign: 'top', - layout: 'vertical', - x: 10, - y: 0, - itemStyle: { - color: '#4D4D4D', - fontSize: '12px', - }, - backgroundColor: '#FFFFFF', - borderColor: '#A7A7A7', - borderRadius: 4, - borderWidth: 1, - padding: 5, - }, - tooltip: { - useHTML: true, - headerFormat: '<div style="margin-bottom: 5px;">{point.key}</div>', - pointFormat: '<b>{point.y:.2f}</b>', - footerFormat: '', - borderColor: '#ECECEC', - borderWidth: 1, - shadow: false, - backgroundColor: '#FFFFFF', - style: { - padding: '10px', - }, - }, - plotOptions: { - areaspline: { - fillOpacity: 0.5, - lineWidth: 2, - marker: { - enabled: false, - }, - fillColor: { - linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, - stops: [ - [0, '#ffbfad'], - [1, '#FFFFFF'], - ], - }, - }, - spline: { - lineWidth: 2, - marker: { - enabled: false, - }, - }, - }, - series: [ - { - type: 'areaspline', - name: getOptionName(0), - data: Object.values(data.data)[0], - color: '#ff5f33', - yAxis: 0, - stickyTracking: false, - pointPlacement: 'on', - }, - ...(Object.values(data.data)[1] - ? [ - { - type: 'spline', - name: getOptionName(1), - data: Object.values(data.data)[1], - color: '#6877FF', - yAxis: 1, - stickyTracking: false, - pointPlacement: 'on', - }, - ] - : []), - ] as SeriesOptionsType[], - responsive: { - rules: [ - { - condition: { - maxWidth: 960, - }, - chartOptions: { - chart: { - width: null, - }, - }, - }, - ], - }, - credits: { enabled: false }, - } - return ( - <div className={cx('chart')}> - <HighchartsReact highcharts={Highcharts} options={chartOptions} /> - </div> - ) -} - -export default AnalysisChart diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx deleted file mode 100644 index a0966522..00000000 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/analysis-content.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import classNames from 'classnames/bind' - -import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-data' -import { Button } from '@/shared/ui/button' -import Pagination from '@/shared/ui/pagination' -import VerticalTable from '@/shared/ui/table/vertical' - -import styles from './styles.module.scss' - -const cx = classNames.bind(styles) - -const DAILY_TABLE_HEADER = [ - '날짜', - '원금', - '입출금', - '일 손익', - '일 손익률', - '누적 손익', - '누적 수익률', -] - -const MONTHLY_TABLE_HEADER = [ - '날짜', - '원금', - '입출금', - '월 손익', - '월 손익률', - '누적 손익', - '누적 수익률', -] - -const COUNT_PER_PAGE = 5 - -interface AnalysisContentProps { - type: 'daily' | 'monthly' - analysisData: DailyAnalysisModel[] | MonthlyAnalysisModel[] - currentPage: number - onPageChange: (page: number) => void - isEditable?: boolean -} - -const AnalysisContent = ({ - type, - analysisData, - currentPage, - onPageChange, - isEditable = false, -}: AnalysisContentProps) => { - const tableHeader = type === 'daily' ? DAILY_TABLE_HEADER : MONTHLY_TABLE_HEADER - - return ( - <div className={cx('table-wrapper', 'analysis')}> - {!isEditable && ( - <Button size="small" className={cx('excel-button')} variant="filled"> - 엑셀 다운받기 - </Button> - )} - {isEditable && ( - <div className={cx('button-container')}> - <div className={cx('button-wrapper')}> - <Button size="small" className={cx('upload-button')} variant="filled"> - 엑셀 업로드 - </Button> - <Button size="small" className={cx('upload-button')} variant="outline"> - 직접 입력 - </Button> - </div> - <Button size="small" variant="filled"> - 전체 삭제 - </Button> - </div> - )} - - <VerticalTable - tableHead={tableHeader} - tableBody={analysisData} - currentPage={currentPage} - countPerPage={COUNT_PER_PAGE} - isEditable={isEditable} - /> - <Pagination - currentPage={currentPage} - maxPage={Math.ceil(analysisData.length / 5)} - onPageChange={onPageChange} - /> - </div> - ) -} - -export default AnalysisContent diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx deleted file mode 100644 index c15033d1..00000000 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/index.tsx +++ /dev/null @@ -1,67 +0,0 @@ -'use client' - -import { useState } from 'react' - -import classNames from 'classnames/bind' - -import Select from '@/shared/ui/select' - -import useGetAnalysisChart from '../../_hooks/query/use-get-analysis-chart' -import AnalysisChart from './analysis-chart' -import styles from './styles.module.scss' -import TabsWithTable from './tabs-width-table' -import { YAXIS_OPTIONS } from './yaxis-options' - -const cx = classNames.bind(styles) - -export type AnalysisChartOptionsType = keyof typeof YAXIS_OPTIONS - -interface Props { - strategyId: number - type?: 'default' | 'my' -} - -const AnalysisContainer = ({ strategyId, type = 'default' }: Props) => { - const [firstOption, setFirstOption] = useState<AnalysisChartOptionsType>('PRINCIPAL') - const [secondOption, setSecondOption] = - useState<AnalysisChartOptionsType>('CUMULATIVE_PROFIT_LOSS') - const { data: chartData } = useGetAnalysisChart({ strategyId, firstOption, secondOption }) - const optionsToArray = Object.entries(YAXIS_OPTIONS) - const options: { value: string; label: string }[] = [] - - for (const [key, value] of optionsToArray) { - options.push({ value: key, label: value }) - } - - return ( - <div className={cx('container')}> - <div className={cx('analysis-header')}> - <p className={cx({ my: type === 'my' })}>분석</p> - {type === 'default' && ( - <div> - <Select - size="large" - options={options} - value={firstOption} - onChange={(newValue) => setFirstOption(newValue as AnalysisChartOptionsType)} - /> - <Select - size="large" - options={options} - value={secondOption} - onChange={(newValue) => setSecondOption(newValue as AnalysisChartOptionsType)} - /> - </div> - )} - </div> - {type === 'default' && ( - <div className={cx('chart-wrapper')}> - <AnalysisChart analysisChartData={chartData} /> - </div> - )} - <TabsWithTable isEditable={type === 'my' ? true : false} /> - </div> - ) -} - -export default AnalysisContainer diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx deleted file mode 100644 index 0586aba8..00000000 --- a/app/(dashboard)/strategies/[strategyId]/_ui/analysis-container/tabs-width-table.tsx +++ /dev/null @@ -1,87 +0,0 @@ -'use client' - -import { useEffect, useState } from 'react' -import React from 'react' - -import { DailyGraphIcon, MoneyIcon, MonthlyGraphIcon, StatisticsIcon } from '@/public/icons' -import classNames from 'classnames/bind' - -import Tabs from '@/shared/ui/tabs' - -import AccountContent from './account-content' -import AnalysisContent from './analysis-content' -import { statisticsData, tableBody } from './example' -import StatisticsContent from './statistics-content' -import styles from './styles.module.scss' - -const cx = classNames.bind(styles) - -interface Props { - isEditable?: boolean -} - -const TabsWithTable = ({ isEditable = false }: Props) => { - const [activeTab, setActiveTab] = useState('statistics') - const [currentPage, setCurrentPage] = useState(1) - - useEffect(() => { - setCurrentPage(1) - }, [activeTab]) - - const handlePageChange = (page: number) => setCurrentPage(page) - - const TABS = [ - { - id: 'statistics', - label: '통계', - icon: StatisticsIcon, - content: <StatisticsContent statisticsData={statisticsData} />, - }, - { - id: 'daily-analysis', - label: '일간분석', - icon: DailyGraphIcon, - content: ( - <AnalysisContent - type="daily" - analysisData={tableBody} - currentPage={currentPage} - onPageChange={handlePageChange} - isEditable={isEditable} - /> - ), - }, - { - id: 'monthly-analysis', - label: '월간분석', - icon: MonthlyGraphIcon, - content: ( - <AnalysisContent - type="monthly" - analysisData={tableBody} - currentPage={currentPage} - onPageChange={handlePageChange} - /> - ), - }, - { - id: 'account-images', - label: '실거래계좌', - icon: MoneyIcon, - content: ( - <div className={cx('table-wrapper')}> - <AccountContent - imagesData={[]} - currentPage={currentPage} - onPageChange={handlePageChange} - isEditable={isEditable} - /> - </div> - ), - }, - ] - - return <Tabs tabs={TABS} activeTab={activeTab} onTabChange={(id) => setActiveTab(id)} /> -} - -export default TabsWithTable diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss index cffd8a20..4db61f02 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss @@ -36,7 +36,7 @@ } .add-button-wrapper { width: 120px; - p { + & p { font-size: $text-c1; font-weight: $text-semibold; color: $color-gray-600; diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 8526db4d..c2181f2e 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,5 +1,8 @@ 'use client' +import AnalysisContainer from '@/app/(dashboard)/_ui/analysis-container' +import SubscriberItem from '@/app/(dashboard)/_ui/subscriber-item' + import { useMSWStore } from '@/shared/stores/msw' import { useAuthStore } from '@/shared/stores/use-auth-store' import BackHeader from '@/shared/ui/header/back-header' @@ -9,9 +12,7 @@ import DetailsInformation from '../../_ui/details-information' import DetailsSideItem, { InformationModel, TitleType } from '../../_ui/details-side-item' import SideContainer from '../_ui/side-container' import useGetDetailsInformationData from './_hooks/query/use-get-details-information-data' -import AnalysisContainer from './_ui/analysis-container' import ReviewContainer from './_ui/review-container' -import SubscriberItem from './_ui/subscriber-item' export type InformationType = { title: TitleType; data: string | number } | InformationModel[] From 99fc5ba27db4bfbf4d9f44956cb97c58152ec2ec Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 1 Dec 2024 03:58:08 +0900 Subject: [PATCH 443/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20inline=20style=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/strategies/page.module.scss | 5 +++++ app/admin/strategies/page.tsx | 24 +++++++----------------- 2 files changed, 12 insertions(+), 17 deletions(-) create mode 100644 app/admin/strategies/page.module.scss diff --git a/app/admin/strategies/page.module.scss b/app/admin/strategies/page.module.scss new file mode 100644 index 00000000..5c303257 --- /dev/null +++ b/app/admin/strategies/page.module.scss @@ -0,0 +1,5 @@ +.container { + padding: 0 45px 37px; + border-radius: 8px; + margin-bottom: 42px; +} diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index f7e019bc..462458ee 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -1,37 +1,27 @@ 'use client' -// TODO: ssr import { useState } from 'react' +import classNames from 'classnames/bind' + import Tabs from '@/shared/ui/tabs' import Title from '@/shared/ui/title' import StockManage from './_ui/stock/stock-manage' import TradeManage from './_ui/trade/trade-manage' +import styles from './page.module.scss' -// import styles from './page.module.scss' - -// const cx = classNames.bind(styles) - -const tabs = ['종목', '매매유형'] +const cx = classNames.bind(styles) -type TabType = '종목' | '매매유형' +const tabs = ['종목', '매매유형'] as const const AdminStrategiesPage = () => { - const [activeTab, setActiveTab] = useState<TabType>('종목') + const [activeTab, setActiveTab] = useState<(typeof tabs)[number]>('매매유형') return ( <> - {/* TODO: inline css 제거 */} <Title label="종목 및 매매 유형 관리" style={{ margin: '80px 0 26px 12.6px' }} /> - <div - style={{ - padding: '0 45px 37px', - borderRadius: '8px', - marginBottom: '42px', - backgroundColor: 'aliceblue', - }} - > + <div className={cx('container')}> <Tabs tabs={tabs.map((tab) => ({ id: tab, From c1ec17caa31e969eddae0b388a3e64ae182880ca Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 1 Dec 2024 04:18:48 +0900 Subject: [PATCH 444/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20Get=20api=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/manage-table/index.tsx | 14 +++++++++++ .../manage-table/utils/add-index-to-data.tsx | 7 +----- .../active-trade-manage-table.tsx | 24 +++++++++++++++++++ .../inactive-trade-manage-table.tsx | 24 +++++++++++++++++++ .../_ui/trade/trade-manage/index.tsx | 23 +++++------------- .../trade/trade-manage/trade-post-button.tsx | 22 +++++++++++++++++ .../_ui/trade/trade-manage/types.ts | 10 ++++++++ shared/ui/table/vertical/index.tsx | 18 ++++++++++++++ shared/utils/with-suspense.tsx | 15 ++++++++++++ 9 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx create mode 100644 app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx create mode 100644 app/admin/strategies/_ui/trade/trade-manage/trade-post-button.tsx create mode 100644 app/admin/strategies/_ui/trade/trade-manage/types.ts create mode 100644 shared/utils/with-suspense.tsx diff --git a/app/admin/strategies/_ui/shared/manage-table/index.tsx b/app/admin/strategies/_ui/shared/manage-table/index.tsx index 68387341..4f0ab082 100644 --- a/app/admin/strategies/_ui/shared/manage-table/index.tsx +++ b/app/admin/strategies/_ui/shared/manage-table/index.tsx @@ -31,4 +31,18 @@ const ManageTable = ({ active, domain, data }: Props) => { ) } +const Skeleton = ({ active, domain }: Pick<Props, 'active' | 'domain'>) => { + return ( + <div className={cx('container')}> + <span className={cx('title')}>{active ? '활성화' : '비활성화'}</span> + <VerticalTable.Skeleton + tableHead={['No.', domain === '종목' ? '종목명' : '매매 유형', '분류', '상태']} + countPerPage={8} //TODO: 이거 디자인에 맞춰서 크기 조절 + /> + </div> + ) +} + +ManageTable.Skeleton = Skeleton + export default ManageTable diff --git a/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx b/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx index caa3feea..2bb1c7b3 100644 --- a/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx +++ b/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx @@ -6,12 +6,7 @@ const addIndexAndButton = (data: any[][], active?: boolean) => { return data?.map((d, idx) => [ idx + 1, ...d, - <Button - variant={active ? 'outline' : 'filled'} - size="small" - style={buttonStyles} - key={idx} - > + <Button variant={active ? 'outline' : 'filled'} size="small" style={buttonStyles} key={idx}> {active ? '비활성화' : '활성화'} </Button>, ]) diff --git a/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx new file mode 100644 index 00000000..92a757b3 --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx @@ -0,0 +1,24 @@ +import axios from 'axios' + +import withSuspense from '@/shared/utils/with-suspense' + +import ManageTable from '../../shared/manage-table' +import { TradeResponseModel } from './types' + +const ActiveTradeManageTable = async () => { + const res = await axios<TradeResponseModel>('/api/admin/strategies/trade-type', { + params: { + activateState: true, + }, + }) + + const { result } = res.data + const tableData = result.map(({ tradeName, tradeTypeIconUrl }) => [ + tradeName, + <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, + ]) + + return <ManageTable data={tableData} active domain="매매 유형" /> +} + +export default withSuspense(ActiveTradeManageTable, <ManageTable.Skeleton domain="매매 유형" />) diff --git a/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx new file mode 100644 index 00000000..fe213253 --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx @@ -0,0 +1,24 @@ +import axios from 'axios' + +import withSuspense from '@/shared/utils/with-suspense' + +import ManageTable from '../../shared/manage-table' +import { TradeResponseModel } from './types' + +const InactiveTradeManageTable = async () => { + const res = await axios<TradeResponseModel>('/api/admin/strategies/trade-type', { + params: { + activateState: false, + }, + }) + + const { result } = res.data + const tableData = result.map(({ tradeName, tradeTypeIconUrl }) => [ + tradeName, + <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, + ]) + + return <ManageTable data={tableData} domain="매매 유형" /> +} + +export default withSuspense(InactiveTradeManageTable, <ManageTable.Skeleton domain="매매 유형" />) diff --git a/app/admin/strategies/_ui/trade/trade-manage/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/index.tsx index d091b4c3..d34e4860 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/index.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/index.tsx @@ -1,11 +1,9 @@ -import { CSSProperties } from 'react' - import classNames from 'classnames/bind' -import { Button } from '@/shared/ui/button' - -import ManageTable from '../../shared/manage-table' +import ActiveTradeManageTable from './active-trade-manage-table' +import InactiveTradeManageTable from './inactive-trade-manage-table' import styles from './styles.module.scss' +import TradePostButton from './trade-post-button' const cx = classNames.bind(styles) @@ -24,20 +22,11 @@ const cx = classNames.bind(styles) const TradeManage = () => { return ( <div className={cx('container')}> - <Button variant="filled" size="small" style={buttonStyles}> - 매매 유형 등록하기 - </Button> - <ManageTable data={[]} active domain="매매 유형" /> - <ManageTable data={[]} domain="매매 유형" /> + <TradePostButton /> + <ActiveTradeManageTable /> + <InactiveTradeManageTable /> </div> ) } -const buttonStyles: CSSProperties = { - position: 'absolute', - top: '-144px', - right: 0, - padding: '12px 24px', -} - export default TradeManage diff --git a/app/admin/strategies/_ui/trade/trade-manage/trade-post-button.tsx b/app/admin/strategies/_ui/trade/trade-manage/trade-post-button.tsx new file mode 100644 index 00000000..55ff66fb --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/trade-post-button.tsx @@ -0,0 +1,22 @@ +'use client' + +import { CSSProperties } from 'react' + +import { Button } from '@/shared/ui/button' + +const TradePostButton = () => { + return ( + <Button variant="filled" size="small" style={buttonStyles}> + 매매 유형 등록하기 + </Button> + ) +} + +const buttonStyles: CSSProperties = { + position: 'absolute', + top: '-144px', + right: 0, + padding: '12px 24px', +} + +export default TradePostButton diff --git a/app/admin/strategies/_ui/trade/trade-manage/types.ts b/app/admin/strategies/_ui/trade/trade-manage/types.ts new file mode 100644 index 00000000..d674dfe7 --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/types.ts @@ -0,0 +1,10 @@ +export interface TradeResponseModel { + isSuccess: boolean + message: string + result: Array<{ + tradeTypeId: number + tradeName: string + activateState: boolean + tradeTypeIconUrl: string + }> +} diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 2b19a30a..19cd6228 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -76,4 +76,22 @@ const VerticalTable = ({ ) } +const Skeleton = ({ tableHead, countPerPage, isEditable = false }: Partial<VerticalTableProps>) => { + return ( + <div className={cx('container')}> + <table> + <thead> + <tr> + {tableHead?.map((head) => <td key={head}>{head}</td>)} + {isEditable && <td></td>} + </tr> + </thead> + </table> + <div className={cx('no-data')} style={{ height: `calc(40px * ${countPerPage}` }} /> + </div> + ) +} + +VerticalTable.Skeleton = Skeleton + export default VerticalTable diff --git a/shared/utils/with-suspense.tsx b/shared/utils/with-suspense.tsx new file mode 100644 index 00000000..e3cc8918 --- /dev/null +++ b/shared/utils/with-suspense.tsx @@ -0,0 +1,15 @@ +import { ComponentType, ReactNode, Suspense } from 'react' + +function withSuspense<P>(Component: ComponentType<P>, fallback: ReactNode = <div>Loading...</div>) { + const WrappedComponent = (props: P & JSX.IntrinsicAttributes) => ( + <Suspense fallback={fallback}> + <Component {...props} /> + </Suspense> + ) + + WrappedComponent.displayName = `withSuspense(${Component.displayName || Component.name || 'Component'})` + + return WrappedComponent +} + +export default withSuspense From aac1d5e54cb8e3fcde6cc04bb5761bcae7eed4d5 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 1 Dec 2024 04:21:49 +0900 Subject: [PATCH 445/648] =?UTF-8?q?feat:=20suspense=EB=A5=BC=20=EC=94=8C?= =?UTF-8?q?=EC=9B=8C=EC=A3=BC=EB=8A=94=20HOC=20=EC=B6=94=EA=B0=80=20(#176)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/utils/with-suspense.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 shared/utils/with-suspense.tsx diff --git a/shared/utils/with-suspense.tsx b/shared/utils/with-suspense.tsx new file mode 100644 index 00000000..e3cc8918 --- /dev/null +++ b/shared/utils/with-suspense.tsx @@ -0,0 +1,15 @@ +import { ComponentType, ReactNode, Suspense } from 'react' + +function withSuspense<P>(Component: ComponentType<P>, fallback: ReactNode = <div>Loading...</div>) { + const WrappedComponent = (props: P & JSX.IntrinsicAttributes) => ( + <Suspense fallback={fallback}> + <Component {...props} /> + </Suspense> + ) + + WrappedComponent.displayName = `withSuspense(${Component.displayName || Component.name || 'Component'})` + + return WrappedComponent +} + +export default withSuspense From af51d17a3fe2ec521838e326b75ea94706c1444e Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 1 Dec 2024 04:33:26 +0900 Subject: [PATCH 446/648] =?UTF-8?q?refactor:=20tab=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EA=B0=80=20content=EB=A5=BC=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B0=9B=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20onChange=20type=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20(#111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/tabs/index.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/shared/ui/tabs/index.tsx b/shared/ui/tabs/index.tsx index daad5c13..56721e97 100644 --- a/shared/ui/tabs/index.tsx +++ b/shared/ui/tabs/index.tsx @@ -10,29 +10,31 @@ export interface TabItemModel { id: string label: string icon?: React.FC<React.SVGProps<SVGSVGElement>> - content: React.ReactElement + content?: React.ReactElement } -interface Props { +interface Props<T extends string> { tabs: TabItemModel[] - activeTab: string - onTabChange: (id: string) => void + activeTab: T + onTabChange: (id: T) => void } -const Tabs = ({ tabs, activeTab, onTabChange }: Props) => { +const Tabs = <T extends string>({ tabs, activeTab, onTabChange }: Props<T>) => { + const activeTabContent = tabs.find((tab) => tab.id === activeTab)?.content + return ( <> <ul className={cx('tab-list')}> {tabs.map(({ id, label, icon: Icon }) => ( <li key={id}> - <TabButton isActive={id === activeTab} onClick={() => onTabChange(id)}> + <TabButton isActive={id === activeTab} onClick={() => onTabChange(id as T)}> {Icon && <Icon className={cx('icon')} />} {label} </TabButton> </li> ))} </ul> - <div>{tabs.find((tab) => tab.id === activeTab)?.content}</div> + {activeTabContent && <div>{activeTabContent}</div>} </> ) } From 344fd1168c1f4d57df78f1eaec34e37d58a694fd Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 30 Nov 2024 15:56:46 +0900 Subject: [PATCH 447/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20api=20=EC=9A=94=EC=B2=AD=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=88=98=EC=A0=95=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 인증코드 추가 - 프로필 이미지 데이터 삭제 --- app/(landing)/signup/_api/signup.ts | 6 +----- app/(landing)/signup/_hooks/custom/use-signup-form.ts | 1 + app/(landing)/signup/information/types.ts | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/(landing)/signup/_api/signup.ts b/app/(landing)/signup/_api/signup.ts index 84ced2c5..e3bb7729 100644 --- a/app/(landing)/signup/_api/signup.ts +++ b/app/(landing)/signup/_api/signup.ts @@ -11,12 +11,8 @@ export const signup = async (formData: SignupFormDataModel) => { password: formData.password, email: `${formData.email}@${formData.emailDomain}`, role: formData.role, + code: formData.code, infoAgreement: formData.infoAgreement, - imageMetadata: { - imageName: '', - extension: '', - size: 0, - }, } try { diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index cab61d20..f60a57a9 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -129,6 +129,7 @@ const useSignupForm = () => { birthYear: form.birthYear, birthMonth: form.birthMonth, birthDay: form.birthDay, + code: form.verificationCode, emailDomain: form.emailDomain, } diff --git a/app/(landing)/signup/information/types.ts b/app/(landing)/signup/information/types.ts index 29cee026..1dacfbfa 100644 --- a/app/(landing)/signup/information/types.ts +++ b/app/(landing)/signup/information/types.ts @@ -54,6 +54,7 @@ export interface SignupFormDataModel { email: string emailDomain: string role: UserType + code: string infoAgreement: boolean } From 2192bc4193a5e6695ec5bba03ffaa66911681c30 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sat, 30 Nov 2024 16:58:36 +0900 Subject: [PATCH 448/648] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EB=AC=B8=EA=B5=AC=20=ED=91=9C=EC=8B=9C=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_hooks/custom/use-signup-form.ts | 2 ++ app/(landing)/signup/information/page.module.scss | 6 ++++++ app/(landing)/signup/information/page.tsx | 3 +++ 3 files changed, 11 insertions(+) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index f60a57a9..0c4c3dd1 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -69,6 +69,8 @@ const useSignupForm = () => { } const handleNicknameCheck = async () => { + setFormState((prev) => ({ ...prev, isNicknameVerified: false })) + try { const response = await checkNicknameDuplicate(form.nickname) if (response.result.isAvailable) { diff --git a/app/(landing)/signup/information/page.module.scss b/app/(landing)/signup/information/page.module.scss index ffa53870..32a148a4 100644 --- a/app/(landing)/signup/information/page.module.scss +++ b/app/(landing)/signup/information/page.module.scss @@ -30,6 +30,12 @@ margin-top: 8px; @include typo-c1; color: $color-gray-600; + + &.nickname-verified { + margin-top: 0; + @include typo-b3; + color: $color-indigo; + } } .wrapper { diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index e0848731..95b641fc 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -111,6 +111,9 @@ const InformationPage = () => { 중복확인 </Button> </div> + {formState.isNicknameVerified && ( + <small className={cx('nickname-verified')}>사용할 수 있는 닉네임입니다.</small> + )} <small>* 사이트 이용 시 사용할 닉네임을 입력해주세요.</small> </div> </div> From ab83925ec725ffcdf309e4863603ca17c6791a83 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 15:41:45 +0900 Subject: [PATCH 449/648] =?UTF-8?q?remove:=20msw=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 3 +- app/test/client/page.tsx | 48 --- app/test/server/page.tsx | 36 -- app/test/ui/email-check-button/index.tsx | 25 -- app/test/ui/signup-button/index.tsx | 29 -- app/test/ui/styels.ts | 7 - app/test/ui/title/index.tsx | 18 - app/test/ui/withdraw-button/index.tsx | 27 -- mocks/browser.ts | 5 - mocks/handlers.ts | 17 - mocks/handlers/auth.ts | 94 ----- mocks/handlers/index.ts | 6 - mocks/handlers/post.ts | 19 - mocks/handlers/strategies.ts | 445 ----------------------- mocks/handlers/user.ts | 71 ---- mocks/index.ts | 10 - mocks/server.ts | 6 - public/mockServiceWorker.js | 292 --------------- shared/providers/index.ts | 2 - shared/providers/msw-Initializer.tsx | 31 -- 20 files changed, 1 insertion(+), 1190 deletions(-) delete mode 100644 app/test/client/page.tsx delete mode 100644 app/test/server/page.tsx delete mode 100644 app/test/ui/email-check-button/index.tsx delete mode 100644 app/test/ui/signup-button/index.tsx delete mode 100644 app/test/ui/styels.ts delete mode 100644 app/test/ui/title/index.tsx delete mode 100644 app/test/ui/withdraw-button/index.tsx delete mode 100644 mocks/browser.ts delete mode 100644 mocks/handlers.ts delete mode 100644 mocks/handlers/auth.ts delete mode 100644 mocks/handlers/index.ts delete mode 100644 mocks/handlers/post.ts delete mode 100644 mocks/handlers/strategies.ts delete mode 100644 mocks/handlers/user.ts delete mode 100644 mocks/index.ts delete mode 100644 mocks/server.ts delete mode 100644 public/mockServiceWorker.js delete mode 100644 shared/providers/msw-Initializer.tsx diff --git a/app/layout.tsx b/app/layout.tsx index f1411d18..1e9d7aeb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,6 @@ import type { Metadata } from 'next' -import { MSWInitializer, QueryProvider } from '@/shared/providers' +import { QueryProvider } from '@/shared/providers' import '@/shared/styles/global.scss' import { pretendard } from './fonts' @@ -15,7 +15,6 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { <html lang="ko" className={pretendard.variable}> <body> <QueryProvider> - <MSWInitializer /> {children} <div id="modal-root"></div> </QueryProvider> diff --git a/app/test/client/page.tsx b/app/test/client/page.tsx deleted file mode 100644 index 27c5a2b5..00000000 --- a/app/test/client/page.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client' - -import { useEffect, useState } from 'react' - -import { useMSWStore } from '@/shared/stores/msw' - -import EmailCheckButton from '../ui/email-check-button' -import SignupButton from '../ui/signup-button' -import WithdrawButton from '../ui/withdraw-button' - -const Sub = () => { - const [user, setUser] = useState<{ title: string }>() - const isReady = useMSWStore((state) => state.isReady) - - useEffect(() => { - const fetchUsers = async () => { - if (!isReady) return - - try { - const res = await fetch('/api/posts') - if (res.ok) { - const data = await res.json() - setUser(data) - } - } catch (err) { - console.error('Fetch error:', err) - } - } - - fetchUsers() - }, [isReady]) - - if (!isReady) { - return <div>로딩 중..</div> - } - - return ( - <div> - Title : {user?.title} - <br /> - <EmailCheckButton /> - <SignupButton /> - <WithdrawButton /> - </div> - ) -} - -export default Sub diff --git a/app/test/server/page.tsx b/app/test/server/page.tsx deleted file mode 100644 index 3474246f..00000000 --- a/app/test/server/page.tsx +++ /dev/null @@ -1,36 +0,0 @@ -// 서버 컴포넌트 test -import '@/mocks/server' - -import EmailCheckButton from '../ui/email-check-button' -import SignupButton from '../ui/signup-button' -import Title from '../ui/title' -import WithdrawButton from '../ui/withdraw-button' - -const Home = async () => { - const fetchPost = async () => { - try { - const res = await fetch(`http://localhost:3000/api/posts`) - if (!res.ok) { - throw new Error('Failed to fetch post') - } - return res.json() - } catch (e) { - console.log('error', e) - return { title: '잘못된 제목이라니까' } - } - } - - const user = await fetchPost() - return ( - <div> - Title : {user.title} - <Title /> - <br /> - <EmailCheckButton /> - <SignupButton /> - <WithdrawButton /> - </div> - ) -} - -export default Home diff --git a/app/test/ui/email-check-button/index.tsx b/app/test/ui/email-check-button/index.tsx deleted file mode 100644 index f5a2a292..00000000 --- a/app/test/ui/email-check-button/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -'use client' - -import { styles } from '../styels' - -const EmailCheckButton = () => { - const checkEmail = async (email: string) => { - const res = await fetch(`/api/users/check/email?email=${email}`) - if (res.ok) { - const result = await res.json() - alert(result.message) - return console.log(result) - } - - console.log('not ok') - return null - } - - return ( - <button onClick={() => checkEmail('kbhoon@gmail.com')} style={styles}> - email check (get) - </button> - ) -} - -export default EmailCheckButton diff --git a/app/test/ui/signup-button/index.tsx b/app/test/ui/signup-button/index.tsx deleted file mode 100644 index 81f43bc3..00000000 --- a/app/test/ui/signup-button/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -'use client' - -import { styles } from '../styels' - -const SignupButton = () => { - const signup = async (userName: string, password: string) => { - const res = await fetch(`/api/users/signup`, { - method: 'POST', - body: JSON.stringify({ userName, password }), - }) - - if (res.ok) { - const result = await res.json() - alert(result.message) - return console.log(result) - } - - console.log('not ok') - return null - } - - return ( - <button onClick={() => signup('name', 'pw')} style={styles}> - signup (post) - </button> - ) -} - -export default SignupButton diff --git a/app/test/ui/styels.ts b/app/test/ui/styels.ts deleted file mode 100644 index af4f0ed2..00000000 --- a/app/test/ui/styels.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { CSSProperties } from 'react' - -export const styles: CSSProperties = { - padding: '6px 12px', - border: '1px solid black', - margin: '12px', -} diff --git a/app/test/ui/title/index.tsx b/app/test/ui/title/index.tsx deleted file mode 100644 index dccd784a..00000000 --- a/app/test/ui/title/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -// 서버 컴포넌트 -const Title = async () => { - const fetchPost = async () => { - try { - const res = await fetch('http://localhost:3000/api/posts') - if (!res.ok) throw new Error('Fetch 요청 실패~') - return res.json() - } catch (e) { - console.error(e) - return { title: '잘못된 제목입니다' } - } - } - - const post = await fetchPost() - return <h1>{post.title}</h1> -} - -export default Title diff --git a/app/test/ui/withdraw-button/index.tsx b/app/test/ui/withdraw-button/index.tsx deleted file mode 100644 index 453fd747..00000000 --- a/app/test/ui/withdraw-button/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -'use client' - -import { styles } from '../styels' - -const WithdrawButton = () => { - const 탈퇴 = async (userId: string) => { - const res = await fetch(`/api/users/${userId}`, { - method: 'DELETE', - }) - - if (res.ok) { - const result = await res.json() - alert(result.message) - return console.log(result) - } - - console.log('not ok') - } - - return ( - <button onClick={() => 탈퇴('James')} style={styles}> - 회원 탈퇴 (delete) - </button> - ) -} - -export default WithdrawButton diff --git a/mocks/browser.ts b/mocks/browser.ts deleted file mode 100644 index d3824e65..00000000 --- a/mocks/browser.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { setupWorker } from 'msw/browser' - -import { handlers } from './handlers' - -export const worker = setupWorker(...handlers) diff --git a/mocks/handlers.ts b/mocks/handlers.ts deleted file mode 100644 index e326cd80..00000000 --- a/mocks/handlers.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - authHandlers, - postHandlers, - strategiesHandlers, - strategyDetailsHandlers, - userHandlers, -} from './handlers/' - -const handlers = [ - ...userHandlers, - ...postHandlers, - ...authHandlers, - ...strategyDetailsHandlers, - ...strategiesHandlers, -] - -export { handlers } diff --git a/mocks/handlers/auth.ts b/mocks/handlers/auth.ts deleted file mode 100644 index 6d352241..00000000 --- a/mocks/handlers/auth.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { HttpResponse, http } from 'msw' - -import { ERROR_MESSAGES } from '@/shared/constants/error-messages' -import { LoginFormDataModel, TokenPayloadModel, UserModel, isAdmin } from '@/shared/types/auth' - -const MOCK_USERS: Record<string, UserModel> = { - 'test@example.com': { - id: '1', - email: 'test@example.com', - name: '김다은', - nickname: 'devdeun', - role: 'TRADER', - createdAt: '2024-01-01T00:00:00Z', - imageUrl: 'https://randomuser.me/api', - }, - 'admin@example.com': { - id: '2', - email: 'admin@example.com', - name: '권혁준', - nickname: 'redhero', - role: 'TRADER_ADMIN', - createdAt: '2024-01-01T00:00:00Z', - imageUrl: 'https://randomuser.me/api', - }, -} - -const createToken = (user: UserModel, isAdmin: boolean) => { - const now = Math.floor(Date.now() / 1000) - const exp = now + (isAdmin ? 1800 : 7 * 24 * 60 * 60) - const payload = { user, exp, iat: now } - return `mock_${btoa(encodeURIComponent(JSON.stringify(payload)))}` -} - -const decodeToken = (token: string) => { - const payload = token.replace('mock_', '') - return JSON.parse(decodeURIComponent(atob(payload))) as TokenPayloadModel -} - -export const authHandlers = [ - http.post('/api/users/login', async ({ request }) => { - const { email, password } = (await request.json()) as LoginFormDataModel - const user = MOCK_USERS[email] - - if (user && password === 'password123') { - const accessToken = createToken(user, isAdmin(user)) - const refreshToken = createToken(user, isAdmin(user)) - - return HttpResponse.json({ - isSuccess: true, - message: '로그인 성공', - data: { accessToken, refreshToken, user }, - }) - } - - return HttpResponse.json( - { - isSuccess: false, - message: ERROR_MESSAGES.AUTH.INVALID_CREDENTIALS, - code: 'INVALID_CREDENTIALS', - }, - { status: 400 } - ) - }), - - http.post('/api/users/reissue/refreshtoken', async ({ request }) => { - const authHeader = request.headers.get('Authorization') - const refreshToken = authHeader?.replace('Bearer ', '') - - if (!refreshToken) { - return HttpResponse.json( - { isSuccess: false, message: ERROR_MESSAGES.AUTH.INVALID_TOKEN }, - { status: 401 } - ) - } - - try { - const decoded = decodeToken(refreshToken) - - const accessToken = createToken(decoded.user, isAdmin(decoded.user)) - const newRefreshToken = createToken(decoded.user, isAdmin(decoded.user)) - - return HttpResponse.json({ - isSuccess: true, - message: '토큰 갱신 성공', - data: { accessToken, refreshToken: newRefreshToken }, - }) - } catch { - return HttpResponse.json( - { isSuccess: false, message: ERROR_MESSAGES.AUTH.REFRESH_FAILED }, - { status: 401 } - ) - } - }), -] diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts deleted file mode 100644 index ce66c0ff..00000000 --- a/mocks/handlers/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { postHandlers } from './post' -export { userHandlers } from './user' - -export { strategyDetailsHandlers } from './strategy-details' -export { strategiesHandlers } from './strategies' -export { authHandlers } from './auth' diff --git a/mocks/handlers/post.ts b/mocks/handlers/post.ts deleted file mode 100644 index af7d95f5..00000000 --- a/mocks/handlers/post.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { HttpResponse, http } from 'msw' - -const allPosts = new Map() -allPosts.set(123, { title: 'How to be with X-mas with girl-friend' }) - -// const { API_HOST } = process.env - -export const postHandlers = [ - http.get('/api/posts', () => { - return HttpResponse.json({ - title: '오늘 점심은 뭐가 좋을까', - }) - }), - http.get('http://localhost:3000/api/posts', () => { - return HttpResponse.json({ - title: '역시 제육볶음이지ㅋ', - }) - }), -] diff --git a/mocks/handlers/strategies.ts b/mocks/handlers/strategies.ts deleted file mode 100644 index 4d0ed4b5..00000000 --- a/mocks/handlers/strategies.ts +++ /dev/null @@ -1,445 +0,0 @@ -import { HttpResponse, http } from 'msw' - -import { StrategiesModel } from '@/shared/types/strategy-data' - -export const strategiesMockData: StrategiesModel[] = [ - { - strategyId: 1, - strategyName: 'Dynamic ETF 전략', - traderImgUrl: '/images/trader1.png', - nickname: 'AlphaTrader', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -15432567, - smScore: 72.1, - cumulativeProfitRate: 140.5, - recentYearProfitLossRate: 35.2, - subscriptionCount: 45, - averageRating: 4.9, - totalReviews: 34, - isSubscribed: true, - }, - { - strategyId: 2, - strategyName: '고수익 ETF', - traderImgUrl: '/images/trader2.png', - nickname: 'BetaTrader', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -12786543, - smScore: 65.4, - cumulativeProfitRate: 125.3, - recentYearProfitLossRate: 28.4, - subscriptionCount: 67, - averageRating: 4.6, - totalReviews: 19, - isSubscribed: false, - }, - { - strategyId: 3, - strategyName: 'Futures Pro', - traderImgUrl: '/images/trader3.png', - nickname: 'Gamma', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -18234678, - smScore: 79.6, - cumulativeProfitRate: 160.4, - recentYearProfitLossRate: 40.1, - subscriptionCount: 89, - averageRating: 4.7, - totalReviews: 45, - isSubscribed: true, - }, - { - strategyId: 4, - strategyName: '월별 수익 전략', - traderImgUrl: '/images/trader4.png', - nickname: 'TraderX', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -11456789, - smScore: 68.4, - cumulativeProfitRate: 134.5, - recentYearProfitLossRate: 32.7, - subscriptionCount: 34, - averageRating: 4.5, - totalReviews: 29, - isSubscribed: false, - }, - { - strategyId: 5, - strategyName: '리스크 관리 전략', - traderImgUrl: '/images/trader5.png', - nickname: 'DeltaOne', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -14345678, - smScore: 62.3, - cumulativeProfitRate: 120.3, - recentYearProfitLossRate: 25.7, - subscriptionCount: 21, - averageRating: 4.8, - totalReviews: 22, - isSubscribed: true, - }, - { - strategyId: 6, - strategyName: 'Active LongShort', - traderImgUrl: '/images/trader6.png', - nickname: 'Hedger', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -17890234, - smScore: 80.2, - cumulativeProfitRate: 175.4, - recentYearProfitLossRate: 45.8, - subscriptionCount: 120, - averageRating: 4.9, - totalReviews: 52, - isSubscribed: true, - }, - { - strategyId: 7, - strategyName: '안정적 성장 전략', - traderImgUrl: '/images/trader7.png', - nickname: 'SafeGrow', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -13567890, - smScore: 70.1, - cumulativeProfitRate: 138.2, - recentYearProfitLossRate: 30.9, - subscriptionCount: 54, - averageRating: 4.4, - totalReviews: 18, - isSubscribed: false, - }, - { - strategyId: 8, - strategyName: 'High Risk High Return', - traderImgUrl: '/images/trader8.png', - nickname: 'RiskyTrader', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -25678345, - smScore: 85.7, - cumulativeProfitRate: 200.5, - recentYearProfitLossRate: 50.4, - subscriptionCount: 76, - averageRating: 4.7, - totalReviews: 37, - isSubscribed: true, - }, - { - strategyId: 9, - strategyName: 'ETF 분산 투자', - traderImgUrl: '/images/trader9.png', - nickname: 'Diversifier', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -9876543, - smScore: 58.3, - cumulativeProfitRate: 112.4, - recentYearProfitLossRate: 20.7, - subscriptionCount: 23, - averageRating: 4.3, - totalReviews: 11, - isSubscribed: false, - }, - { - strategyId: 10, - strategyName: 'Momentum 전략', - traderImgUrl: '/images/trader10.png', - nickname: 'MomentumKing', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -18456789, - smScore: 75.6, - cumulativeProfitRate: 165.3, - recentYearProfitLossRate: 38.5, - subscriptionCount: 89, - averageRating: 4.9, - totalReviews: 48, - isSubscribed: true, - }, - { - strategyId: 11, - strategyName: '소형주 집중 전략', - traderImgUrl: '/images/trader11.png', - nickname: 'SmallCapPro', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -14345678, - smScore: 69.4, - cumulativeProfitRate: 130.7, - recentYearProfitLossRate: 28.2, - subscriptionCount: 67, - averageRating: 4.6, - totalReviews: 26, - isSubscribed: false, - }, - { - strategyId: 12, - strategyName: 'Active Bond 전략', - traderImgUrl: '/images/trader12.png', - nickname: 'BondExpert', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - profitRateChartData: { - dates: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - profitRates: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - mdd: -10567890, - smScore: 64.3, - cumulativeProfitRate: 125.8, - recentYearProfitLossRate: 27.4, - subscriptionCount: 42, - averageRating: 4.5, - totalReviews: 21, - isSubscribed: true, - }, -] - -export const strategiesHandlers = [ - http.get('/api/strategies', ({ request }) => { - const url = new URL(request.url) - const page = parseInt(url.searchParams.get('page') || '1') - const size = parseInt(url.searchParams.get('size') || '8') - - if (!isNaN(page) && !isNaN(size)) { - const croppedStrategiesData = strategiesMockData.slice( - size * (page - 1), - size * (page - 1) + size - ) - - return HttpResponse.json({ - result: { - content: croppedStrategiesData, - totalPages: Math.ceil(strategiesMockData.length / size), - }, - }) - } - - return HttpResponse.json( - { - isSuccess: false, - message: '전략 목록을 찾을 수 없습니다.', - code: 1004, - }, - { status: 400 } - ) - }), -] diff --git a/mocks/handlers/user.ts b/mocks/handlers/user.ts deleted file mode 100644 index 645c5005..00000000 --- a/mocks/handlers/user.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { HttpResponse, http } from 'msw' - -// const { API_HOST } = process.env -export const userHandlers = [ - http.get('/api/users/check/email', ({ request }) => { - const url = new URL(request.url) - const email = url.searchParams.get('email') - - const emailList = new Set(['kbhoon@gmail.com']) - - if (!email || emailList.has(email)) { - return HttpResponse.json({ - isSuccess: false, - message: '사용할 수 없는 이메일입니다.', - code: 2204, - }) - } - - return HttpResponse.json({ - isSuccess: true, - message: '사용 가능한 이메일입니다.', - }) - }), - - http.post('/api/users/signup', async ({ request }) => { - const newUser = (await request.json()) as { userName: string; password: string } - const { userName, password } = newUser - - if (!userName || !password) { - return HttpResponse.json( - { - isSuccess: false, - message: '회원가입에 실패하였습니다.', - code: 2003, - }, - { status: 400 } - ) - } - - return HttpResponse.json( - { - isSuccess: true, - message: '회원가입이 완료되었습니다. 로그인 화면으로 이동합니다.', - }, - { status: 201 } - ) - }), - - http.delete('/api/users/:userId', ({ params }) => { - const { userId } = params - - if (!userId) { - return HttpResponse.json( - { - isSuccess: false, - message: '회원 탈퇴에 실패하였습니다.', - code: 2004, - }, - { status: 400 } - ) - } - - return HttpResponse.json( - { - isSuccess: true, - message: '탈퇴가 완료되었습니다. 그 동안 서비스를 이용해주셔서 감사합니다.', - }, - { status: 201 } - ) - }), -] diff --git a/mocks/index.ts b/mocks/index.ts deleted file mode 100644 index 31d1add1..00000000 --- a/mocks/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -async function initMSW() { - const { worker } = await import('./browser') - await worker.start({ - serviceWorker: { - url: '/mockServiceWorker.js', - }, - }) -} - -export default initMSW diff --git a/mocks/server.ts b/mocks/server.ts deleted file mode 100644 index 45deee17..00000000 --- a/mocks/server.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { setupServer } from 'msw/node' - -import { handlers } from './handlers' - -export const server = setupServer(...handlers) -server.listen() diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js deleted file mode 100644 index 64c9b1cb..00000000 --- a/public/mockServiceWorker.js +++ /dev/null @@ -1,292 +0,0 @@ -/* eslint-disable */ -/* tslint:disable */ - -/** - * Mock Service Worker. - * @see https://github.com/mswjs/msw - * - Please do NOT modify this file. - * - Please do NOT serve this file on production. - */ - -const PACKAGE_VERSION = '2.6.6' -const INTEGRITY_CHECKSUM = 'ca7800994cc8bfb5eb961e037c877074' -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') -const activeClientIds = new Set() - -self.addEventListener('install', function () { - self.skipWaiting() -}) - -self.addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()) -}) - -self.addEventListener('message', async function (event) { - const clientId = event.source.id - - if (!clientId || !self.clients) { - return - } - - const client = await self.clients.get(clientId) - - if (!client) { - return - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }) - - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break - } - - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: { - packageVersion: PACKAGE_VERSION, - checksum: INTEGRITY_CHECKSUM, - }, - }) - break - } - - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) - - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: { - client: { - id: client.id, - frameType: client.frameType, - }, - }, - }) - break - } - - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId) - break - } - - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister() - } - - break - } - } -}) - -self.addEventListener('fetch', function (event) { - const { request } = event - - // Bypass navigation requests. - if (request.mode === 'navigate') { - return - } - - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return - } - - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return - } - - // Generate unique request ID. - const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId)) -}) - -async function handleRequest(event, requestId) { - const client = await resolveMainClient(event) - const response = await getResponse(event, client, requestId) - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - ;(async function () { - const responseClone = response.clone() - - sendToClient( - client, - { - type: 'RESPONSE', - payload: { - requestId, - isMockedResponse: IS_MOCKED_RESPONSE in response, - type: responseClone.type, - status: responseClone.status, - statusText: responseClone.statusText, - body: responseClone.body, - headers: Object.fromEntries(responseClone.headers.entries()), - }, - }, - [responseClone.body] - ) - })() - } - - return response -} - -// Resolve the main client for the given event. -// Client that issues a request doesn't necessarily equal the client -// that registered the worker. It's with the latter the worker should -// communicate with during the response resolving phase. -async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) - - if (activeClientIds.has(event.clientId)) { - return client - } - - if (client?.frameType === 'top-level') { - return client - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }) - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible' - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) -} - -async function getResponse(event, client, requestId) { - const { request } = event - - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const requestClone = request.clone() - - function passthrough() { - // Cast the request headers to a new Headers instance - // so the headers can be manipulated with. - const headers = new Headers(requestClone.headers) - - // Remove the "accept" header value that marked this request as passthrough. - // This prevents request alteration and also keeps it compliant with the - // user-defined CORS policies. - headers.delete('accept', 'msw/passthrough') - - return fetch(requestClone, { headers }) - } - - // Bypass mocking when the client is not active. - if (!client) { - return passthrough() - } - - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return passthrough() - } - - // Notify the client that a request has been intercepted. - const requestBuffer = await request.arrayBuffer() - const clientMessage = await sendToClient( - client, - { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - mode: request.mode, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: requestBuffer, - keepalive: request.keepalive, - }, - }, - [requestBuffer] - ) - - switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data) - } - - case 'PASSTHROUGH': { - return passthrough() - } - } - - return passthrough() -} - -function sendToClient(client, message, transferrables = []) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel() - - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error) - } - - resolve(event.data) - } - - client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean))) - }) -} - -async function respondWithMock(response) { - // Setting response status code to 0 is a no-op. - // However, when responding with a "Response.error()", the produced Response - // instance will have status code set to 0. Since it's not possible to create - // a Response instance with status code 0, handle that use-case separately. - if (response.status === 0) { - return Response.error() - } - - const mockedResponse = new Response(response.body, response) - - Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { - value: true, - enumerable: true, - }) - - return mockedResponse -} diff --git a/shared/providers/index.ts b/shared/providers/index.ts index 25979743..ac2203f8 100644 --- a/shared/providers/index.ts +++ b/shared/providers/index.ts @@ -1,3 +1 @@ export { QueryProvider } from './react-query/query-provider' - -export { MSWInitializer } from './msw-Initializer' diff --git a/shared/providers/msw-Initializer.tsx b/shared/providers/msw-Initializer.tsx deleted file mode 100644 index a31ea417..00000000 --- a/shared/providers/msw-Initializer.tsx +++ /dev/null @@ -1,31 +0,0 @@ -'use client' - -import { useEffect } from 'react' - -import { useMSWStore } from '@/shared/stores/msw' -import { isDevelopment } from '@/shared/utils/env' - -export const MSWInitializer = () => { - const setReady = useMSWStore((state) => state.setReady) - - useEffect(() => { - const initMSW = async () => { - if (!isDevelopment) { - setReady(true) - return - } - - try { - const { default: init } = await import('@/mocks') - await init() - setReady(true) - } catch (err) { - console.error('MSW initialization failed:', err) - } - } - - initMSW() - }, [setReady]) - - return null -} From 8c9e84ed0f9f2f4bf6b914b7b60dfaccfd76edbe Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 15:44:09 +0900 Subject: [PATCH 450/648] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95=EB=90=9C=20?= =?UTF-8?q?=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_api/email-auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(landing)/signup/_api/email-auth.ts b/app/(landing)/signup/_api/email-auth.ts index 8c525ab5..68dbda83 100644 --- a/app/(landing)/signup/_api/email-auth.ts +++ b/app/(landing)/signup/_api/email-auth.ts @@ -4,7 +4,7 @@ export const requestEmailAuthentication = async (email: string, domain: string) const emailAddress = `${email}@${domain}` try { - const response = await axios.get(`/api/users/authenticate?email=${emailAddress}`) + const response = await axios.get(`/api/users/authenticate/signup?email=${emailAddress}`) return response.data } catch (err) { console.error(err) @@ -19,7 +19,7 @@ export const getEmailAuthenticationResult = async (email: string, domain: string } try { - const response = await axios.post(`/api/users/authenticate`, data) + const response = await axios.post(`/api/users/authenticate/signup`, data) return response.data } catch (err) { console.error(err) From 595448a9fcbd07564bad808336c62108dcecea84 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 15:44:59 +0900 Subject: [PATCH 451/648] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=B0=8F=20=EC=84=B1=EA=B3=B5=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/_hooks/custom/use-signup-email.ts | 13 +++--- .../signup/_hooks/custom/use-signup-form.ts | 11 ++++- app/(landing)/signup/information/page.tsx | 43 +++++++++++-------- app/(landing)/signup/information/types.ts | 1 + app/(landing)/signup/information/utils.ts | 4 +- next.config.mjs | 4 ++ 6 files changed, 52 insertions(+), 24 deletions(-) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-email.ts b/app/(landing)/signup/_hooks/custom/use-signup-email.ts index 31f0f411..5b25752f 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-email.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-email.ts @@ -79,19 +79,22 @@ const useSignupEmail = ({ form, errors, isValidated, setForm, setErrors, setForm form.verificationCode ) - if (response.result.isVerified) { + if (response.isSuccess) { setFormState((prev) => ({ ...prev, isEmailVerified: true })) - if (errors.email) { - setErrors((prev) => ({ ...prev, email: null })) + if (errors.emailConfirm) { + setErrors((prev) => ({ ...prev, emailConfirm: null })) } } else { - setErrors((prev) => ({ ...prev, email: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_MISMATCH })) + setErrors((prev) => ({ + ...prev, + emailConfirm: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_MISMATCH, + })) } } catch (err) { console.error('인증번호 확인 실패:', err) setErrors((prev) => ({ ...prev, - email: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_CHECK_FAILED, + emailConfirm: SIGNUP_ERROR_MESSAGES.VERIFICATION_CODE_CHECK_FAILED, })) } } diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index 0c4c3dd1..1ecceb74 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -62,6 +62,13 @@ const useSignupForm = () => { isNicknameVerified: false, })) } + + if (name === 'phone') { + setFormState((prev) => ({ + ...prev, + isPhoneVerified: false, + })) + } } const handleMarketingAgree = (checked: boolean) => { @@ -109,7 +116,8 @@ const useSignupForm = () => { const formErrors = validateSignupForm( form, formState.isEmailVerified, - formState.isNicknameVerified + formState.isNicknameVerified, + formState.isPhoneVerified ) setIsValidated(true) @@ -117,6 +125,7 @@ const useSignupForm = () => { setErrors(formErrors) return } + const role = getUserTypeCookie() try { diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index 95b641fc..3ef3b2ee 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -162,23 +162,29 @@ const InformationPage = () => { <small>* 사이트 이용 시 아이디로 사용됩니다.</small> </div> {formState.isEmailSent && ( - <div className={cx('wrapper', 'verification')}> - <Input - id="verificationCode" - name="verificationCode" - value={form.verificationCode} - onChange={handleInputChange} - placeholder="인증번호" - className={cx('input')} - /> - <Button - onClick={handleVerificationCodeCheck} - type="button" - className={cx('button')} - > - 인증번호 확인 - </Button> - </div> + <> + <div className={cx('wrapper', 'verification')}> + <Input + id="verificationCode" + name="verificationCode" + value={form.verificationCode} + onChange={handleInputChange} + placeholder="인증번호" + className={cx('input')} + errorMessage={errors.emailConfirm} + /> + <Button + onClick={handleVerificationCodeCheck} + type="button" + className={cx('button')} + > + 인증번호 확인 + </Button> + </div> + {formState.isEmailVerified && ( + <small className={cx('nickname-verified')}>이메일 인증에 성공했습니다.</small> + )} + </> )} </div> </div> @@ -231,6 +237,9 @@ const InformationPage = () => { 중복확인 </Button> </div> + {formState.isPhoneVerified && ( + <small className={cx('nickname-verified')}>사용할 수 있는 휴대전화 번호입니다.</small> + )} <small>* (-) 없이 숫자만 입력해주세요.</small> </div> </div> diff --git a/app/(landing)/signup/information/types.ts b/app/(landing)/signup/information/types.ts index 1dacfbfa..9638e939 100644 --- a/app/(landing)/signup/information/types.ts +++ b/app/(landing)/signup/information/types.ts @@ -25,6 +25,7 @@ export interface SignupFormErrorsModel { name?: SignupErrorMessageType | null nickname?: SignupErrorMessageType | null email?: SignupErrorMessageType | null + emailConfirm?: SignupErrorMessageType | null password?: SignupErrorMessageType | null passwordConfirm?: SignupErrorMessageType | null phone?: SignupErrorMessageType | null diff --git a/app/(landing)/signup/information/utils.ts b/app/(landing)/signup/information/utils.ts index 7d402589..ba340e43 100644 --- a/app/(landing)/signup/information/utils.ts +++ b/app/(landing)/signup/information/utils.ts @@ -52,7 +52,8 @@ export const validatePasswordMatch = ( export const validateSignupForm = ( form: SignupFormModel, isEmailVerified: boolean, - isNicknameVerified: boolean + isNicknameVerified: boolean, + isPhoneVerified: boolean ): Record<string, SignupErrorMessageType> => { const errors: Record<string, SignupErrorMessageType> = {} @@ -75,6 +76,7 @@ export const validateSignupForm = ( const phoneError = validateField('PHONE', form.phone) if (phoneError) errors.phone = phoneError + if (!isPhoneVerified) errors.phone = SIGNUP_ERROR_MESSAGES.PHONE_CHECK_REQUIRED if (!form.birthYear || !form.birthMonth || !form.birthDay) { errors.select = SIGNUP_ERROR_MESSAGES.SELECT_REQUIRED diff --git a/next.config.mjs b/next.config.mjs index 2ac2cd7d..a9c9cd86 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -27,6 +27,10 @@ const nextConfig = { source: '/api/main/:path*', destination: 'http://15.164.90.102:8081/api/main/:path*', }, + { + source: '/api/users/:path*', + destination: 'http://15.164.90.102:8081/api/users/:path*', + }, ] }, webpack: (config, { isServer }) => { From 17f0fe403cc412a7df54e251f01bcae12440aadd Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 1 Dec 2024 16:04:10 +0900 Subject: [PATCH 452/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20Get=20api=20tanstack?= =?UTF-8?q?=20query=EB=A1=9C=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/manage-table/constant/index.ts | 1 + .../_ui/shared/manage-table/index.tsx | 17 ++++++++++--- .../active-trade-manage-table.tsx | 23 +++++++----------- .../_ui/trade/trade-manage/api/get-trades.ts | 21 ++++++++++++++++ .../trade-manage/hooks/use-trades-data.ts | 16 +++++++++++++ .../inactive-trade-manage-table.tsx | 24 +++++++------------ .../_ui/trade/trade-manage/index.tsx | 12 ---------- 7 files changed, 68 insertions(+), 46 deletions(-) create mode 100644 app/admin/strategies/_ui/shared/manage-table/constant/index.ts create mode 100644 app/admin/strategies/_ui/trade/trade-manage/api/get-trades.ts create mode 100644 app/admin/strategies/_ui/trade/trade-manage/hooks/use-trades-data.ts diff --git a/app/admin/strategies/_ui/shared/manage-table/constant/index.ts b/app/admin/strategies/_ui/shared/manage-table/constant/index.ts new file mode 100644 index 00000000..5995e354 --- /dev/null +++ b/app/admin/strategies/_ui/shared/manage-table/constant/index.ts @@ -0,0 +1 @@ +export const COUNT_PER_PAGE = 8 diff --git a/app/admin/strategies/_ui/shared/manage-table/index.tsx b/app/admin/strategies/_ui/shared/manage-table/index.tsx index 4f0ab082..0a923115 100644 --- a/app/admin/strategies/_ui/shared/manage-table/index.tsx +++ b/app/admin/strategies/_ui/shared/manage-table/index.tsx @@ -1,10 +1,11 @@ -import { ReactNode } from 'react' +import { ReactNode, useState } from 'react' import classNames from 'classnames/bind' import Pagination from '@/shared/ui/pagination' import VerticalTable from '@/shared/ui/table/vertical' +import { COUNT_PER_PAGE } from './constant' import styles from './styles.module.scss' import addIndexToData from './utils/add-index-to-data' @@ -17,16 +18,26 @@ interface Props { } const ManageTable = ({ active, domain, data }: Props) => { + const [currentPage, setCurrentPage] = useState(1) + + const hasData = data?.length > 0 + return ( <div className={cx('container')}> <span className={cx('title')}>{active ? '활성화' : '비활성화'}</span> <VerticalTable tableHead={['No.', domain === '종목' ? '종목명' : '매매 유형', '분류', '상태']} tableBody={addIndexToData(data, active)} - countPerPage={8} + countPerPage={COUNT_PER_PAGE} currentPage={1} /> - <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + {hasData && domain === '종목' && ( + <Pagination + currentPage={currentPage} + maxPage={3} + onPageChange={(page) => setCurrentPage(page)} + /> + )} </div> ) } diff --git a/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx index 92a757b3..6b243ef3 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx @@ -1,22 +1,15 @@ -import axios from 'axios' - import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../shared/manage-table' -import { TradeResponseModel } from './types' - -const ActiveTradeManageTable = async () => { - const res = await axios<TradeResponseModel>('/api/admin/strategies/trade-type', { - params: { - activateState: true, - }, - }) +import useTradeData from './hooks/use-trades-data' - const { result } = res.data - const tableData = result.map(({ tradeName, tradeTypeIconUrl }) => [ - tradeName, - <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, - ]) +const ActiveTradeManageTable = () => { + const { data } = useTradeData('active') + const tableData = + data?.result?.map(({ tradeName, tradeTypeIconUrl, tradeTypeId }) => [ + tradeName, + <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, + ]) ?? [] return <ManageTable data={tableData} active domain="매매 유형" /> } diff --git a/app/admin/strategies/_ui/trade/trade-manage/api/get-trades.ts b/app/admin/strategies/_ui/trade/trade-manage/api/get-trades.ts new file mode 100644 index 00000000..f661fbca --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/api/get-trades.ts @@ -0,0 +1,21 @@ +import axios from 'axios' + +import { TradeResponseModel } from '../types' + +const getTrades = async (activateState: boolean) => { + try { + const res = await axios<TradeResponseModel>('/api/admin/strategies/trade-type', { + params: { + activateState, + }, + }) + + if (!res.data.isSuccess) throw new Error(res.data.message) + + return res.data + } catch (err) { + console.log('Error : ' + err) + } +} + +export default getTrades diff --git a/app/admin/strategies/_ui/trade/trade-manage/hooks/use-trades-data.ts b/app/admin/strategies/_ui/trade/trade-manage/hooks/use-trades-data.ts new file mode 100644 index 00000000..a4f1290e --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/hooks/use-trades-data.ts @@ -0,0 +1,16 @@ +import { useQuery } from '@tanstack/react-query' + +import getTrades from '../api/get-trades' + +type ArgType = 'active' | 'inactive' + +const useTradeData = (activateState: ArgType) => { + const isActive = activateState === 'active' ? true : false + + return useQuery({ + queryKey: ['strategies', activateState], + queryFn: () => getTrades(isActive), + }) +} + +export default useTradeData diff --git a/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx index fe213253..150b8484 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx @@ -1,23 +1,15 @@ -import axios from 'axios' - import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../shared/manage-table' -import { TradeResponseModel } from './types' - -const InactiveTradeManageTable = async () => { - const res = await axios<TradeResponseModel>('/api/admin/strategies/trade-type', { - params: { - activateState: false, - }, - }) - - const { result } = res.data - const tableData = result.map(({ tradeName, tradeTypeIconUrl }) => [ - tradeName, - <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, - ]) +import useTradeData from './hooks/use-trades-data' +const InactiveTradeManageTable = () => { + const { data } = useTradeData('inactive') + const tableData = + data?.result?.map(({ tradeName, tradeTypeIconUrl, tradeTypeId }) => [ + tradeName, + <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, + ]) ?? [] return <ManageTable data={tableData} domain="매매 유형" /> } diff --git a/app/admin/strategies/_ui/trade/trade-manage/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/index.tsx index d34e4860..795b20fa 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/index.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/index.tsx @@ -7,18 +7,6 @@ import TradePostButton from './trade-post-button' const cx = classNames.bind(styles) -// const activeMock = [ -// ['K100 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K200 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K50 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K200 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K100 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K50 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K200 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K100 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ['K50 선물', <div style={{ width: '20px', height: '20px', backgroundColor: 'red' }} />], -// ] - const TradeManage = () => { return ( <div className={cx('container')}> From 588fff4b760df41468e3fe1040eed98d49055c03 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 16:48:39 +0900 Subject: [PATCH 453/648] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=A4=ED=8C=A8=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_hooks/custom/use-signup-form.ts | 4 ++++ app/(landing)/signup/information/page.tsx | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index 1ecceb74..e35b756e 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -44,6 +44,7 @@ const useSignupForm = () => { const [errors, setErrors] = useState<SignupFormErrorsModel>({}) const [formState, setFormState] = useState<SignupFormStateModel>(initialFormState) const [isValidated, setIsValidated] = useState(false) + const [isModalOpen, setIsModalOpen] = useState(false) const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target @@ -150,6 +151,7 @@ const useSignupForm = () => { } catch (err) { console.error('회원가입 실패:', err) setErrors((prev) => ({ ...prev, submitError: '회원가입에 실패했습니다.' })) + setIsModalOpen(true) } } @@ -166,6 +168,8 @@ const useSignupForm = () => { handleFormSubmit, handleNicknameCheck, handlePhoneCheck, + isModalOpen, + setIsModalOpen, } } diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index 3ef3b2ee..68c038dd 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -2,6 +2,7 @@ import { ChangeEvent } from 'react' +import { ModalAlertIcon } from '@/public/icons' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -10,6 +11,7 @@ import Checkbox from '@/shared/ui/check-box' import { ErrorMessage } from '@/shared/ui/error-message' import { Input } from '@/shared/ui/input' import { LinkButton } from '@/shared/ui/link-button' +import Modal from '@/shared/ui/modal' import Select from '@/shared/ui/select' import useSignupEmail from '../_hooks/custom/use-signup-email' @@ -50,6 +52,8 @@ const InformationPage = () => { handleFormSubmit, handleNicknameCheck, handlePhoneCheck, + isModalOpen, + setIsModalOpen, } = useSignupForm() const { @@ -299,6 +303,9 @@ const InformationPage = () => { 다음 </Button> </div> + <Modal message="회원가입에 실패했습니다." icon={<ModalAlertIcon />} isOpen={isModalOpen}> + <Button onClick={() => setIsModalOpen(false)}>닫기</Button> + </Modal> </> ) } From 94648c97e8c86fbb88541527e7967dc2184d5aab Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 17:49:19 +0900 Subject: [PATCH 454/648] =?UTF-8?q?feat:=20=EC=83=9D=EB=85=84=EC=9B=94?= =?UTF-8?q?=EC=9D=BC=20select=20handler=20=ED=95=A8=EC=88=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_hooks/custom/use-signup-form.ts | 6 ++++++ app/(landing)/signup/information/page.tsx | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index e35b756e..84fff937 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -72,6 +72,11 @@ const useSignupForm = () => { } } + const handleBirthdayChange = (value: string, field: 'birthYear' | 'birthMonth' | 'birthDay') => { + setErrors((prev) => ({ ...prev, select: null })) + setForm((prev) => ({ ...prev, [field]: String(value) })) + } + const handleMarketingAgree = (checked: boolean) => { setForm((prev) => ({ ...prev, isMarketingAgreed: checked })) } @@ -164,6 +169,7 @@ const useSignupForm = () => { setFormState, isValidated, handleInputChange, + handleBirthdayChange, handleMarketingAgree, handleFormSubmit, handleNicknameCheck, diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index 68c038dd..30a81eb4 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -48,6 +48,7 @@ const InformationPage = () => { setFormState, isValidated, handleInputChange, + handleBirthdayChange, handleMarketingAgree, handleFormSubmit, handleNicknameCheck, @@ -254,7 +255,7 @@ const InformationPage = () => { <Select options={yearOptions} value={form.birthYear} - onChange={(value) => setForm((prev) => ({ ...prev, birthYear: String(value) }))} + onChange={(value) => handleBirthdayChange(String(value), 'birthYear')} size="small" placeholder="년" titleStyle={selectStyle} @@ -262,7 +263,7 @@ const InformationPage = () => { <Select options={monthOptions} value={form.birthMonth} - onChange={(value) => setForm((prev) => ({ ...prev, birthMonth: String(value) }))} + onChange={(value) => handleBirthdayChange(String(value), 'birthMonth')} size="small" placeholder="월" titleStyle={selectStyle} @@ -270,7 +271,7 @@ const InformationPage = () => { <Select options={dayOptions} value={form.birthDay} - onChange={(value) => setForm((prev) => ({ ...prev, birthDay: String(value) }))} + onChange={(value) => handleBirthdayChange(String(value), 'birthDay')} size="small" placeholder="일" titleStyle={selectStyle} From b1e1f38822c94ca1ae0b1d3a864aabc4820a4355 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 17:50:25 +0900 Subject: [PATCH 455/648] =?UTF-8?q?design:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=99=84=EB=A3=8C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_ui/signup-complete-message/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(landing)/signup/_ui/signup-complete-message/index.tsx b/app/(landing)/signup/_ui/signup-complete-message/index.tsx index ad801b81..36e9964f 100644 --- a/app/(landing)/signup/_ui/signup-complete-message/index.tsx +++ b/app/(landing)/signup/_ui/signup-complete-message/index.tsx @@ -22,10 +22,10 @@ interface Props { const SignupCompleteMessage = ({ nickname, userType }: Props) => { return ( <section className={cx('container')}> - <p>{nickname}의 회원가입이 완료되었습니다.</p> + <p>{nickname}님의 회원가입이 완료되었습니다.</p> <p> 지금 바로 인베스트메틱 - <Logo /> + <Logo width={42} /> 에서 <br /> {COMPLETE_MESSAGE[userType]} </p> From fbd2d23f1e93466e9518fc9af2e70852527e5540 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 18:02:52 +0900 Subject: [PATCH 456/648] =?UTF-8?q?fix:=20msw=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=A4=91=EC=9D=B8=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/_api/get-favorite-strategy-list.ts | 6 +----- .../strategies/_api/get-strategies.ts | 3 --- next.config.mjs | 20 ++----------------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/app/(dashboard)/my/_api/get-favorite-strategy-list.ts b/app/(dashboard)/my/_api/get-favorite-strategy-list.ts index a25ef50d..18a17cae 100644 --- a/app/(dashboard)/my/_api/get-favorite-strategy-list.ts +++ b/app/(dashboard)/my/_api/get-favorite-strategy-list.ts @@ -1,5 +1,3 @@ -import { strategiesMockData } from '@/mocks/handlers/strategies' - import axiosInstance from '@/shared/api/axios' import { StrategiesModel } from '@/shared/types/strategy-data' @@ -25,9 +23,7 @@ const getFavoriteStrategyList = async ({ return { strategiesData, totalPages } } catch (err) { console.error(err) - // throw new Error('구독한 전략 조회에 실패했습니다.') - // 임시 목데이터 - return { strategiesData: strategiesMockData.filter((data) => data.isSubscribed), totalPages: 1 } + throw new Error('구독한 전략 조회에 실패했습니다.') } } diff --git a/app/(dashboard)/strategies/_api/get-strategies.ts b/app/(dashboard)/strategies/_api/get-strategies.ts index 7bcf855e..29b7c54d 100644 --- a/app/(dashboard)/strategies/_api/get-strategies.ts +++ b/app/(dashboard)/strategies/_api/get-strategies.ts @@ -1,4 +1,3 @@ -import { strategiesMockData } from '@/mocks/handlers/strategies' import axios from 'axios' import { StrategiesModel } from '@/shared/types/strategy-data' @@ -23,8 +22,6 @@ const getStrategiesData = async ( return { strategiesData, totalPages } } catch (err) { console.error(err) - // 임시 목데이터 - return { strategiesData: strategiesMockData, totalPages: 2 } } } diff --git a/next.config.mjs b/next.config.mjs index a9c9cd86..901e632c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -11,25 +11,9 @@ const nextConfig = { }, async rewrites() { return [ - // { - // source: '/api/users/reissue/refreshtoken', - // destination: '/api/users/reissue/refreshtoken', - // }, - // { - // source: '/api/users/login', - // destination: '/api/users/login', - // }, { - source: '/api/strategies/:path*', - destination: 'http://15.164.90.102:8081/api/strategies/:path*', - }, - { - source: '/api/main/:path*', - destination: 'http://15.164.90.102:8081/api/main/:path*', - }, - { - source: '/api/users/:path*', - destination: 'http://15.164.90.102:8081/api/users/:path*', + source: '/api/:path*', + destination: 'http://15.164.90.102:8081/api/:path*', }, ] }, From 805d5588f71c77e75b8ced264f332a1c52a315bb Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Sun, 1 Dec 2024 18:39:24 +0900 Subject: [PATCH 457/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20patch=20api=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/manage-table/index.tsx | 2 +- .../manage-table/utils/add-index-to-data.tsx | 20 ++-------- .../trade-manage/{api => _api}/get-trades.ts | 0 .../_api/toggle-trade-active-state.ts | 19 +++++++++ .../_hooks/use-toggle-trade-active-state.ts | 21 ++++++++++ .../{hooks => _hooks}/use-trades-data.ts | 4 +- .../{ => _ui}/active-trade-manage-table.tsx | 6 ++- .../{ => _ui}/inactive-trade-manage-table.tsx | 7 +++- .../_ui/trade-active-state-toggle-button.tsx | 39 +++++++++++++++++++ .../{ => _ui}/trade-post-button.tsx | 0 .../_ui/trade/trade-manage/index.tsx | 6 +-- .../_ui/trade/trade-manage/types.ts | 11 +++++- next.config.mjs | 4 ++ 13 files changed, 110 insertions(+), 29 deletions(-) rename app/admin/strategies/_ui/trade/trade-manage/{api => _api}/get-trades.ts (100%) create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_hooks/use-toggle-trade-active-state.ts rename app/admin/strategies/_ui/trade/trade-manage/{hooks => _hooks}/use-trades-data.ts (77%) rename app/admin/strategies/_ui/trade/trade-manage/{ => _ui}/active-trade-manage-table.tsx (64%) rename app/admin/strategies/_ui/trade/trade-manage/{ => _ui}/inactive-trade-manage-table.tsx (65%) create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx rename app/admin/strategies/_ui/trade/trade-manage/{ => _ui}/trade-post-button.tsx (100%) diff --git a/app/admin/strategies/_ui/shared/manage-table/index.tsx b/app/admin/strategies/_ui/shared/manage-table/index.tsx index 0a923115..315034fd 100644 --- a/app/admin/strategies/_ui/shared/manage-table/index.tsx +++ b/app/admin/strategies/_ui/shared/manage-table/index.tsx @@ -27,7 +27,7 @@ const ManageTable = ({ active, domain, data }: Props) => { <span className={cx('title')}>{active ? '활성화' : '비활성화'}</span> <VerticalTable tableHead={['No.', domain === '종목' ? '종목명' : '매매 유형', '분류', '상태']} - tableBody={addIndexToData(data, active)} + tableBody={addIndexToData(data)} countPerPage={COUNT_PER_PAGE} currentPage={1} /> diff --git a/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx b/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx index 2bb1c7b3..5f59cf36 100644 --- a/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx +++ b/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx @@ -1,21 +1,7 @@ -import { CSSProperties } from 'react' +import { ReactNode } from 'react' -import { Button } from '@/shared/ui/button' - -const addIndexAndButton = (data: any[][], active?: boolean) => { - return data?.map((d, idx) => [ - idx + 1, - ...d, - <Button variant={active ? 'outline' : 'filled'} size="small" style={buttonStyles} key={idx}> - {active ? '비활성화' : '활성화'} - </Button>, - ]) -} - -const buttonStyles: CSSProperties = { - height: '30px', - padding: '7px 16px', - margin: '15px 0', +const addIndexAndButton = (data: (string | number | ReactNode)[][]) => { + return data?.map((d, idx) => [idx + 1, ...d]) } export default addIndexAndButton diff --git a/app/admin/strategies/_ui/trade/trade-manage/api/get-trades.ts b/app/admin/strategies/_ui/trade/trade-manage/_api/get-trades.ts similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/api/get-trades.ts rename to app/admin/strategies/_ui/trade/trade-manage/_api/get-trades.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts b/app/admin/strategies/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts new file mode 100644 index 00000000..75733a4c --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts @@ -0,0 +1,19 @@ +import axios from 'axios' + +import { ToggleTradeActiveStateResponseModel } from '../types' + +const ToggleTradeActiveState = async (tradeTypeId: number) => { + try { + const res = await axios.patch<ToggleTradeActiveStateResponseModel>( + `/api/admin/strategies/trade-type/${tradeTypeId}` + ) + + if (!res.data.isSuccess) throw new Error(res.data.message) + + return res.data + } catch (err) { + console.log('Error : ' + err) + } +} + +export default ToggleTradeActiveState diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-toggle-trade-active-state.ts b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-toggle-trade-active-state.ts new file mode 100644 index 00000000..d1dcba6d --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-toggle-trade-active-state.ts @@ -0,0 +1,21 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import ToggleTradeActiveState from '../_api/toggle-trade-active-state' + +const useToggoleTradeActiveState = (tradeTypeId: number) => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => ToggleTradeActiveState(tradeTypeId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ['adminTrades'], + }) + }, + onError: (err) => { + console.error('Error : ', err) + }, + }) +} + +export default useToggoleTradeActiveState diff --git a/app/admin/strategies/_ui/trade/trade-manage/hooks/use-trades-data.ts b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-trades-data.ts similarity index 77% rename from app/admin/strategies/_ui/trade/trade-manage/hooks/use-trades-data.ts rename to app/admin/strategies/_ui/trade/trade-manage/_hooks/use-trades-data.ts index a4f1290e..3063f44e 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/hooks/use-trades-data.ts +++ b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-trades-data.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query' -import getTrades from '../api/get-trades' +import getTrades from '../_api/get-trades' type ArgType = 'active' | 'inactive' @@ -8,7 +8,7 @@ const useTradeData = (activateState: ArgType) => { const isActive = activateState === 'active' ? true : false return useQuery({ - queryKey: ['strategies', activateState], + queryKey: ['adminTrades', activateState], queryFn: () => getTrades(isActive), }) } diff --git a/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx similarity index 64% rename from app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx rename to app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx index 6b243ef3..718ec18f 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/active-trade-manage-table.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx @@ -1,7 +1,8 @@ import withSuspense from '@/shared/utils/with-suspense' -import ManageTable from '../../shared/manage-table' -import useTradeData from './hooks/use-trades-data' +import ManageTable from '../../../shared/manage-table' +import useTradeData from '../_hooks/use-trades-data' +import TradeActiveStateToggleButton from './trade-active-state-toggle-button' const ActiveTradeManageTable = () => { const { data } = useTradeData('active') @@ -9,6 +10,7 @@ const ActiveTradeManageTable = () => { data?.result?.map(({ tradeName, tradeTypeIconUrl, tradeTypeId }) => [ tradeName, <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, + <TradeActiveStateToggleButton tradeTypeId={tradeTypeId} active key={tradeTypeId} />, ]) ?? [] return <ManageTable data={tableData} active domain="매매 유형" /> diff --git a/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx similarity index 65% rename from app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx rename to app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx index 150b8484..f775995e 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/inactive-trade-manage-table.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx @@ -1,7 +1,8 @@ import withSuspense from '@/shared/utils/with-suspense' -import ManageTable from '../../shared/manage-table' -import useTradeData from './hooks/use-trades-data' +import ManageTable from '../../../shared/manage-table' +import useTradeData from '../_hooks/use-trades-data' +import TradeActiveStateToggleButton from './trade-active-state-toggle-button' const InactiveTradeManageTable = () => { const { data } = useTradeData('inactive') @@ -9,7 +10,9 @@ const InactiveTradeManageTable = () => { data?.result?.map(({ tradeName, tradeTypeIconUrl, tradeTypeId }) => [ tradeName, <img src={tradeTypeIconUrl} alt={tradeName} key={tradeName} />, + <TradeActiveStateToggleButton tradeTypeId={tradeTypeId} key={tradeTypeId} />, ]) ?? [] + return <ManageTable data={tableData} domain="매매 유형" /> } diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx new file mode 100644 index 00000000..8f82af3e --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx @@ -0,0 +1,39 @@ +'use client' + +import { CSSProperties } from 'react' + +import { Button } from '@/shared/ui/button' + +import useToggoleTradeActiveState from '../_hooks/use-toggle-trade-active-state' + +interface Props { + active?: boolean + tradeTypeId: number +} + +const TradeActiveStateToggleButton = ({ active, tradeTypeId }: Props) => { + const { mutate, isPending } = useToggoleTradeActiveState(tradeTypeId) + const onButtonClick = () => { + mutate() + console.log('t', tradeTypeId) + } + + return ( + <Button + onClick={onButtonClick} + variant={active ? 'outline' : 'filled'} + size="small" + style={buttonStyles} + disabled={isPending} + > + {active ? '비활성화' : '활성화'} + </Button> + ) +} + +const buttonStyles: CSSProperties = { + height: '30px', + padding: '7px 16px', + margin: '15px 0', +} +export default TradeActiveStateToggleButton diff --git a/app/admin/strategies/_ui/trade/trade-manage/trade-post-button.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button.tsx similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/trade-post-button.tsx rename to app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button.tsx diff --git a/app/admin/strategies/_ui/trade/trade-manage/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/index.tsx index 795b20fa..3bff5ae0 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/index.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/index.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames/bind' -import ActiveTradeManageTable from './active-trade-manage-table' -import InactiveTradeManageTable from './inactive-trade-manage-table' +import ActiveTradeManageTable from './_ui/active-trade-manage-table' +import InactiveTradeManageTable from './_ui/inactive-trade-manage-table' +import TradePostButton from './_ui/trade-post-button' import styles from './styles.module.scss' -import TradePostButton from './trade-post-button' const cx = classNames.bind(styles) diff --git a/app/admin/strategies/_ui/trade/trade-manage/types.ts b/app/admin/strategies/_ui/trade/trade-manage/types.ts index d674dfe7..f81e71f3 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/types.ts +++ b/app/admin/strategies/_ui/trade/trade-manage/types.ts @@ -1,6 +1,10 @@ -export interface TradeResponseModel { - isSuccess: boolean +interface TradeResponseBaseModel<T extends boolean> { + isSuccess: T message: string + code: T extends false ? number : never +} + +export interface TradeResponseModel extends TradeResponseBaseModel<boolean> { result: Array<{ tradeTypeId: number tradeName: string @@ -8,3 +12,6 @@ export interface TradeResponseModel { tradeTypeIconUrl: string }> } + +// eslint-disable-next-line +export interface ToggleTradeActiveStateResponseModel extends TradeResponseBaseModel<boolean> {} diff --git a/next.config.mjs b/next.config.mjs index 2ac2cd7d..2741a47e 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -27,6 +27,10 @@ const nextConfig = { source: '/api/main/:path*', destination: 'http://15.164.90.102:8081/api/main/:path*', }, + { + source: '/api/:path*', + destination: 'http://15.164.90.102:8081/api/:path*', + }, ] }, webpack: (config, { isServer }) => { From c41eada368a492ef99bf583ad033be170bbef51c Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 22:04:20 +0900 Subject: [PATCH 458/648] =?UTF-8?q?remove:=20msw=20handler=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mocks/handlers/strategy-details.ts | 161 ----------------------------- 1 file changed, 161 deletions(-) delete mode 100644 mocks/handlers/strategy-details.ts diff --git a/mocks/handlers/strategy-details.ts b/mocks/handlers/strategy-details.ts deleted file mode 100644 index 7b6c6e8c..00000000 --- a/mocks/handlers/strategy-details.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { HttpResponse, http } from 'msw' - -import { StrategyDetailsInformationModel } from '@/shared/types/strategy-data' - -export const strategiesDetailsInformationMockData: StrategyDetailsInformationModel[] = [ - { - strategyId: 1, - strategyName: 'Dynamic ETF 전략', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - operationCycle: 'DAY', - strategyDescription: '전략 설명 예시', - cumulativeProfitRate: 4924.0, - maxDrawdownRate: -13068.0, - averageProfitLossRate: 600.0, - profitFactor: 148.0, - winRate: 0.69, - subscriptionCount: 100, - traderImgUrl: '', - nickname: 'AlphaTrader', - minimumInvestmentAmount: '1억~ 2억', - initialInvestment: '10,000,000', - kpRatio: 0.513, - smScore: 72.1, - finalProfitLossDate: '2024.03.11', - createdAt: '2024.01.11', - isSubscribed: true, - }, - { - strategyId: 2, - strategyName: '고수익 ETF', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - operationCycle: 'DAY', - strategyDescription: '전략 설명 예시', - cumulativeProfitRate: 4924.0, - maxDrawdownRate: -13068.0, - averageProfitLossRate: 600.0, - profitFactor: 148.0, - winRate: 0.69, - subscriptionCount: 100, - traderImgUrl: '', - nickname: 'BetaTrader', - minimumInvestmentAmount: '1억~ 2억', - initialInvestment: '10,000,000', - kpRatio: 0.513, - smScore: 65.4, - finalProfitLossDate: '2024.03.11', - createdAt: '2024.01.11', - isSubscribed: true, - }, - { - strategyId: 3, - strategyName: 'Futures Pro', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - operationCycle: 'DAY', - strategyDescription: '전략 설명 예시', - cumulativeProfitRate: 4924.0, - maxDrawdownRate: -13068.0, - averageProfitLossRate: 600.0, - profitFactor: 148.0, - winRate: 0.69, - subscriptionCount: 100, - traderImgUrl: '', - nickname: 'Gamma', - minimumInvestmentAmount: '1억~ 2억', - initialInvestment: '10,000,000', - kpRatio: 0.513, - smScore: 79.6, - finalProfitLossDate: '2024.03.11', - createdAt: '2024.01.11', - isSubscribed: true, - }, - { - strategyId: 4, - strategyName: '월별 수익 전략', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - operationCycle: 'DAY', - strategyDescription: '전략 설명 예시', - cumulativeProfitRate: 4924.0, - maxDrawdownRate: -13068.0, - averageProfitLossRate: 600.0, - profitFactor: 148.0, - winRate: 0.69, - subscriptionCount: 100, - traderImgUrl: '', - nickname: 'user1', - minimumInvestmentAmount: '1억~ 2억', - initialInvestment: '10,000,000', - kpRatio: 0.513, - smScore: 68.4, - finalProfitLossDate: '2024.03.11', - createdAt: '2024.01.11', - isSubscribed: true, - }, - { - strategyId: 5, - strategyName: '리스크 관리 전략', - stockTypeInfo: { - stockTypeIconUrls: ['/images/stock.png'], - stockTypeNames: ['해외지수선물'], - }, - tradeTypeIconUrl: '/images/trade.png', - tradeTypeName: '자동', - operationCycle: 'DAY', - strategyDescription: '전략 설명 예시', - cumulativeProfitRate: 4924.0, - maxDrawdownRate: -13068.0, - averageProfitLossRate: 600.0, - profitFactor: 148.0, - winRate: 0.69, - subscriptionCount: 100, - traderImgUrl: '', - nickname: 'DeltaOne', - minimumInvestmentAmount: '1억~ 2억', - initialInvestment: '10,000,000', - kpRatio: 0.513, - smScore: 62.3, - finalProfitLossDate: '2024.03.11', - createdAt: '2024.01.11', - isSubscribed: true, - }, -] - -export const strategyDetailsHandlers = [ - http.get('/api/strategies/:strategyId', ({ params }) => { - const { strategyId } = params - const strategyData = strategiesDetailsInformationMockData.find( - (item) => item.strategyId === Number(strategyId) - ) - if (strategyData) { - return HttpResponse.json({ ...strategyData }) - } - return HttpResponse.json( - { - isSuccess: false, - message: '전략을 찾을 수 없습니다.', - code: 4002, - }, - { status: 400 } - ) - }), -] From c461af8762183a89866e8acdfc76516f79188b20 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Sun, 1 Dec 2024 22:07:48 +0900 Subject: [PATCH 459/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=AA=A9=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_api/get-details-information.ts | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index f17e537a..4ef6fa78 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -1,4 +1,3 @@ -import { strategiesDetailsInformationMockData } from '@/mocks/handlers/strategy-details' import axios from 'axios' import { InformationType } from '../page' @@ -34,32 +33,6 @@ const getDetailsInformation = async (isReady: boolean, strategyId: number) => { return { detailsSideData, detailsInformationData } } catch (err) { console.error(err) - // 임시 데이터 - const detailsSideData: InformationType[] = [ - { title: '트레이더', data: strategiesDetailsInformationMockData[0].nickname }, - { - title: '최소 투자 금액', - data: strategiesDetailsInformationMockData[0].minimumInvestmentAmount, - }, - { title: '투자 원금', data: strategiesDetailsInformationMockData[0].initialInvestment }, - - [ - { title: 'KP Ratio', data: strategiesDetailsInformationMockData[0].kpRatio }, - { title: 'SM SCORE', data: strategiesDetailsInformationMockData[0].smScore }, - ], - - [ - { - title: '최종손익입력일자', - data: strategiesDetailsInformationMockData[0].finalProfitLossDate, - }, - { title: '등록일', data: strategiesDetailsInformationMockData[0].createdAt }, - ], - ] - const detailsInformationData = { - ...strategiesDetailsInformationMockData[0], - } - return { detailsSideData, detailsInformationData } } } From e5e6da3d928ebd06713f7a03cb95f88caaa05138 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 02:29:24 +0900 Subject: [PATCH 460/648] =?UTF-8?q?feat:=20file=20input=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/file-input/index.tsx | 38 ++++++++++++++++++ .../_ui/shared/file-input/styles.module.scss | 40 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 app/admin/strategies/_ui/shared/file-input/index.tsx create mode 100644 app/admin/strategies/_ui/shared/file-input/styles.module.scss diff --git a/app/admin/strategies/_ui/shared/file-input/index.tsx b/app/admin/strategies/_ui/shared/file-input/index.tsx new file mode 100644 index 00000000..29021b2b --- /dev/null +++ b/app/admin/strategies/_ui/shared/file-input/index.tsx @@ -0,0 +1,38 @@ +'use client' + +import { ComponentPropsWithoutRef } from 'react' + +import Image from 'next/image' + +import { FileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props extends ComponentPropsWithoutRef<'input'> { + accept?: string + preview?: string +} + +const FileInput = ({ preview, accept = '*', value, onChange, ...props }: Props) => { + return ( + <div className={cx('container')}> + {preview && ( + <Image width={24} height={24} src={preview} alt="Preview" className={cx('preview')} /> + )} + <input + type="file" + accept={accept} + value={value} + onChange={onChange} + className={cx('input')} + {...props} + /> + <FileIcon className={cx('icon')} /> + </div> + ) +} + +export default FileInput diff --git a/app/admin/strategies/_ui/shared/file-input/styles.module.scss b/app/admin/strategies/_ui/shared/file-input/styles.module.scss new file mode 100644 index 00000000..2aa3f5f3 --- /dev/null +++ b/app/admin/strategies/_ui/shared/file-input/styles.module.scss @@ -0,0 +1,40 @@ +.container { + position: relative; + display: inline-block; + width: 160px; + height: 40px; + border-radius: 4px; + border: 1px solid $color-gray-400; + color: $color-gray-400; + @include typo-c1; + + svg { + width: 24px; + } +} + +.input { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + z-index: 10; +} + +.preview { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); +} + +.icon { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + width: 24px; + color: $color-gray-500; +} From 4ce56d9cedbcb92fcbbe915243883136ec7f2ab7 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 02:35:04 +0900 Subject: [PATCH 461/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20patch=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20ui=20=EB=A7=88=EB=AC=B4=EB=A6=AC=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade-manage/_ui/trade-post-button.tsx | 22 --- .../_ui/trade-post-button/index.tsx | 134 ++++++++++++++++++ .../_ui/trade-post-button/styles.module.scss | 38 +++++ 3 files changed, 172 insertions(+), 22 deletions(-) delete mode 100644 app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button.tsx create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button.tsx deleted file mode 100644 index 55ff66fb..00000000 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button.tsx +++ /dev/null @@ -1,22 +0,0 @@ -'use client' - -import { CSSProperties } from 'react' - -import { Button } from '@/shared/ui/button' - -const TradePostButton = () => { - return ( - <Button variant="filled" size="small" style={buttonStyles}> - 매매 유형 등록하기 - </Button> - ) -} - -const buttonStyles: CSSProperties = { - position: 'absolute', - top: '-144px', - right: 0, - padding: '12px 24px', -} - -export default TradePostButton diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx new file mode 100644 index 00000000..919eb9ff --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx @@ -0,0 +1,134 @@ +'use client' + +import { CSSProperties, useState } from 'react' + +import FileInput from '@/app/admin/strategies/_ui/shared/file-input' +import { RegisterIcon } from '@/public/icons' +import axios from 'axios' +import classNames from 'classnames/bind' + +import useModal from '@/shared/hooks/custom/use-modal' +import { Button } from '@/shared/ui/button' +import { Input } from '@/shared/ui/input' +import Modal from '@/shared/ui/modal' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const TradePostButton = () => { + const { isModalOpen, openModal, closeModal } = useModal() + const onClick = () => openModal() + + const [imagePreview, setImagePreview] = useState<string | null>(null) + const [imageFile, setImageFile] = useState<File | null>(null) + + const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => { + const file = event.target.files?.[0] + + if (file) { + if (!file.type.startsWith('image/')) { + alert('Please upload a valid image file.') + return + } + + setImageFile(file) + + const reader = new FileReader() + reader.onload = (e) => { + setImagePreview(e.target?.result as string) + } + reader.readAsDataURL(file) + } else { + setImagePreview(null) + } + } + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!imageFile) { + alert('이미지를 선택하세요!') + return + } + + try { + console.log('f', imageFile) + + const presignedResponse = await axios.post('/api/admin/strategies/trade-type', { + tradeTypeName: '자동', + tradeTypeIconUrl: imageFile.name, + size: imageFile.size, + }) + + console.log('p', presignedResponse) + + const { presignedUrl } = presignedResponse.data.result + + await axios.put(presignedUrl, imageFile, { + headers: { + 'Content-Type': imageFile.type, + }, + }) + + alert('이미지가 성공적으로 업로드되었습니다!') + } catch (error) { + console.error('이미지 업로드 실패:', error) + alert('업로드 중 문제가 발생했습니다.') + } + } + + return ( + <> + <Button onClick={onClick} variant="filled" size="small" style={buttonStyles}> + 매매 유형 등록하기 + </Button> + <Modal icon={<RegisterIcon />} message="매매유형 등록" isOpen={isModalOpen}> + <form onSubmit={onSubmit} className={cx('form')}> + <div className={cx('input-container')}> + <div className={cx('input-field')}> + 유형 <Input placeholder="유형을 입력해주세요." className={cx('input')} /> + </div> + <div className={cx('input-field')}> + 아이콘{' '} + <FileInput + preview={imagePreview ?? ''} + accept="image/*" + onChange={handleImageChange} + /> + </div> + </div> + <div className={cx('button-group')}> + <Button onClick={closeModal} type="button" className={cx('button')}> + 취소 + </Button> + <Button variant="filled" className={cx('button')}> + 등록 + </Button> + </div> + </form> + </Modal> + </> + ) +} + +const buttonStyles: CSSProperties = { + position: 'absolute', + top: '-144px', + right: 0, + padding: '12px 24px', +} + +export default TradePostButton + +// const fetchTrade = async () => { +// const res = await fetch('http://15.164.90.102:8081/api/strategies/trade-type', { +// method: 'POST', +// headers: { +// 'Content-Type': 'application/json', +// }, +// body: JSON.stringify({ +// tradeTypeName: '자동', +// tradeTypeIconUrl: 'IconTest.png', +// size: 526, +// }), +// }) diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss new file mode 100644 index 00000000..9fea8077 --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss @@ -0,0 +1,38 @@ +.form { + width: 236px; //이거 고정값 맞나?... + margin-top: 24px; +} + +.input-container { + margin-top: 25px; + margin-bottom: 88px; + display: flex; + flex-direction: column; + gap: 18px; +} + +.input-field { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + + .input { + width: 160px; + height: 40px; + } +} + + +.button-group { + width: 216px; + margin: 0 auto; + display: flex; + justify-content: space-between; +} + +.button { + width: 90px; + height: 45px; +} From dd44460c179822ae7a3882fc0cc7d1fcf06908ba Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 03:49:50 +0900 Subject: [PATCH 462/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20=EB=B9=84=EC=A7=80?= =?UTF-8?q?=EB=8B=88=EC=8A=A4=EB=A1=9C=EC=A7=81=20=ED=9B=84=EA=B8=8D?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/file-input/styles.module.scss | 4 + .../_hooks/use-strategy-icon-post.ts | 89 ++++++++++++++ .../_ui/trade-post-button/index.tsx | 111 ++++-------------- .../_ui/trade-post-button/styles.module.scss | 8 +- 4 files changed, 120 insertions(+), 92 deletions(-) create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts diff --git a/app/admin/strategies/_ui/shared/file-input/styles.module.scss b/app/admin/strategies/_ui/shared/file-input/styles.module.scss index 2aa3f5f3..2a888cb1 100644 --- a/app/admin/strategies/_ui/shared/file-input/styles.module.scss +++ b/app/admin/strategies/_ui/shared/file-input/styles.module.scss @@ -21,6 +21,10 @@ height: 100%; opacity: 0; z-index: 10; + + &:hover { + cursor: pointer; + } } .preview { diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts new file mode 100644 index 00000000..42ef04e3 --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts @@ -0,0 +1,89 @@ +import { ChangeEvent, useState } from 'react' + +import axios from 'axios' + +interface FormDataModel { + imageFile: File | null + typeName: string +} + +const initailFormData = { imageFile: null, typeName: '' } + +const useStrategyIconPost = () => { + const [imagePreview, setImagePreview] = useState('') + const [formData, setFormData] = useState<FormDataModel>(initailFormData) + + const onTypeNameInputChange = (e: ChangeEvent<HTMLInputElement>) => { + const typeName = e.target.value + + setFormData((prev) => ({ ...prev, typeName })) + } + + const onImageInputChange = (e: ChangeEvent<HTMLInputElement>) => { + const iconImage = e.target.files?.[0] + + if (iconImage) { + if (!iconImage.type.startsWith('image/')) { + alert('이미지 파일만 업로드가 가능해요.') + return + } + + setFormData((prev) => ({ ...prev, imageFile: iconImage })) + + const reader = new FileReader() + reader.onload = (e) => { + setImagePreview(e.target?.result as string) + } + reader.readAsDataURL(iconImage) + } else { + setImagePreview('') + } + } + + const isSubmitable = Object.values(formData).every((value) => Boolean(value)) + + const onFormSubmit = async (e: React.FormEvent) => { + if (!isSubmitable) return + + e.preventDefault() + const { imageFile, typeName } = formData + + try { + console.log('imageFile', imageFile, 'typeName', typeName) + + const presignedResponse = await axios.post('/api/admin/strategies/trade-type', { + tradeTypeName: '자동', + tradeTypeIconUrl: imageFile?.name, + size: imageFile?.size, + }) + + console.log('p', presignedResponse) + + // const { presignedUrl } = presignedResponse.data.result + + // await axios.put(presignedUrl, imageFile, { + // headers: { + // 'Content-Type': imageFile.type, + // }, + // }) + + alert('이미지가 성공적으로 업로드되었습니다!') + setFormData(initailFormData) + } catch (error) { + console.error('이미지 업로드 실패:', error) + alert('업로드 중 문제가 발생했습니다.') + } + } + + return { + imagePreview, + setImagePreview, + onTypeNameInputChange, + onImageInputChange, + isSubmitable, + onFormSubmit, + formData, + } +} + +export default useStrategyIconPost diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx index 919eb9ff..1462f32f 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx @@ -1,10 +1,7 @@ 'use client' -import { CSSProperties, useState } from 'react' - import FileInput from '@/app/admin/strategies/_ui/shared/file-input' import { RegisterIcon } from '@/public/icons' -import axios from 'axios' import classNames from 'classnames/bind' import useModal from '@/shared/hooks/custom/use-modal' @@ -12,96 +9,48 @@ import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import Modal from '@/shared/ui/modal' +import useStrategyIconPost from '../../_hooks/use-strategy-icon-post' import styles from './styles.module.scss' const cx = classNames.bind(styles) const TradePostButton = () => { const { isModalOpen, openModal, closeModal } = useModal() - const onClick = () => openModal() - - const [imagePreview, setImagePreview] = useState<string | null>(null) - const [imageFile, setImageFile] = useState<File | null>(null) - - const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => { - const file = event.target.files?.[0] - - if (file) { - if (!file.type.startsWith('image/')) { - alert('Please upload a valid image file.') - return - } - - setImageFile(file) - - const reader = new FileReader() - reader.onload = (e) => { - setImagePreview(e.target?.result as string) - } - reader.readAsDataURL(file) - } else { - setImagePreview(null) - } - } - - const onSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!imageFile) { - alert('이미지를 선택하세요!') - return - } - - try { - console.log('f', imageFile) - - const presignedResponse = await axios.post('/api/admin/strategies/trade-type', { - tradeTypeName: '자동', - tradeTypeIconUrl: imageFile.name, - size: imageFile.size, - }) - - console.log('p', presignedResponse) - - const { presignedUrl } = presignedResponse.data.result - - await axios.put(presignedUrl, imageFile, { - headers: { - 'Content-Type': imageFile.type, - }, - }) - - alert('이미지가 성공적으로 업로드되었습니다!') - } catch (error) { - console.error('이미지 업로드 실패:', error) - alert('업로드 중 문제가 발생했습니다.') - } - } + const onPostButtonClick = () => openModal() + const { onFormSubmit, imagePreview, onImageInputChange, onTypeNameInputChange, isSubmitable } = + useStrategyIconPost() return ( <> - <Button onClick={onClick} variant="filled" size="small" style={buttonStyles}> + <Button + onClick={onPostButtonClick} + variant="filled" + size="small" + className={cx('post-button')} + > 매매 유형 등록하기 </Button> <Modal icon={<RegisterIcon />} message="매매유형 등록" isOpen={isModalOpen}> - <form onSubmit={onSubmit} className={cx('form')}> + <form onSubmit={onFormSubmit} className={cx('form')}> <div className={cx('input-container')}> <div className={cx('input-field')}> - 유형 <Input placeholder="유형을 입력해주세요." className={cx('input')} /> + 유형 + <Input + placeholder="유형을 입력해주세요." + className={cx('input')} + onChange={onTypeNameInputChange} + /> </div> <div className={cx('input-field')}> - 아이콘{' '} - <FileInput - preview={imagePreview ?? ''} - accept="image/*" - onChange={handleImageChange} - /> + 아이콘 + <FileInput preview={imagePreview} accept="image/*" onChange={onImageInputChange} /> </div> </div> <div className={cx('button-group')}> <Button onClick={closeModal} type="button" className={cx('button')}> 취소 </Button> - <Button variant="filled" className={cx('button')}> + <Button disabled={!isSubmitable} variant="filled" className={cx('button')}> 등록 </Button> </div> @@ -111,24 +60,4 @@ const TradePostButton = () => { ) } -const buttonStyles: CSSProperties = { - position: 'absolute', - top: '-144px', - right: 0, - padding: '12px 24px', -} - export default TradePostButton - -// const fetchTrade = async () => { -// const res = await fetch('http://15.164.90.102:8081/api/strategies/trade-type', { -// method: 'POST', -// headers: { -// 'Content-Type': 'application/json', -// }, -// body: JSON.stringify({ -// tradeTypeName: '자동', -// tradeTypeIconUrl: 'IconTest.png', -// size: 526, -// }), -// }) diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss index 9fea8077..2817e085 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss @@ -1,3 +1,10 @@ +.post-button { + position: absolute; + top: -144px; + right: 0; + padding: 12px 24px; +} + .form { width: 236px; //이거 고정값 맞나?... margin-top: 24px; @@ -24,7 +31,6 @@ } } - .button-group { width: 216px; margin: 0 auto; From 21c54f7a97a60932236f1662ea594afcabdfba4b Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 05:05:54 +0900 Subject: [PATCH 463/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EA=B5=AC=ED=98=84=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade-manage/_api/get-presigned-url.ts | 17 ++++++++++ .../_api/upload-file-with-presigned-url.ts | 11 +++++++ .../_hooks/use-strategy-icon-post.ts | 31 ++++++------------- .../_ui/trade/trade-manage/types.ts | 6 ++++ 4 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts create mode 100644 app/admin/strategies/_ui/trade/trade-manage/_api/upload-file-with-presigned-url.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts b/app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts new file mode 100644 index 00000000..ad0aaa5d --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts @@ -0,0 +1,17 @@ +import axios from 'axios' + +import { PresignedUrlResponseModel } from '../types' + +const getPresignedUrl = async (typeName: string, imageName: string, imageSize: number) => { + const res = await axios.post<PresignedUrlResponseModel>('/api/admin/strategies/trade-type', { + tradeTypeName: typeName, + tradeTypeIconUrl: imageName, + size: imageSize, + }) + + if (!res.data.isSuccess) throw new Error('presigned url Error : ' + res.data.message) + + return res.data.result.presignedUrl +} + +export default getPresignedUrl diff --git a/app/admin/strategies/_ui/trade/trade-manage/_api/upload-file-with-presigned-url.ts b/app/admin/strategies/_ui/trade/trade-manage/_api/upload-file-with-presigned-url.ts new file mode 100644 index 00000000..1dca39bf --- /dev/null +++ b/app/admin/strategies/_ui/trade/trade-manage/_api/upload-file-with-presigned-url.ts @@ -0,0 +1,11 @@ +import axios from 'axios' + +const uploadFileWithPresignedUrl = async (presignedUrl: string, file: File) => { + await axios.put(presignedUrl, file, { + headers: { + 'Content-Type': file.type, + }, + }) +} + +export default uploadFileWithPresignedUrl diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts index 42ef04e3..cb9fac2c 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts +++ b/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts @@ -1,6 +1,7 @@ import { ChangeEvent, useState } from 'react' -import axios from 'axios' +import getPresignedUrl from '../_api/get-presigned-url' +import uploadFileWithPresignedUrl from '../_api/upload-file-with-presigned-url' interface FormDataModel { imageFile: File | null @@ -43,46 +44,34 @@ const useStrategyIconPost = () => { const isSubmitable = Object.values(formData).every((value) => Boolean(value)) const onFormSubmit = async (e: React.FormEvent) => { - if (!isSubmitable) return - e.preventDefault() const { imageFile, typeName } = formData - try { - console.log('imageFile', imageFile, 'typeName', typeName) + if (!imageFile || !typeName) return - const presignedResponse = await axios.post('/api/admin/strategies/trade-type', { - tradeTypeName: '자동', - tradeTypeIconUrl: imageFile?.name, - size: imageFile?.size, - }) + try { + // console.log('imageFile', imageFile, 'typeName', typeName) - console.log('p', presignedResponse) + const presignedUrl = await getPresignedUrl(typeName, imageFile.name, imageFile.size) - // const { presignedUrl } = presignedResponse.data.result + // console.log('p', presignedUrl) - // await axios.put(presignedUrl, imageFile, { - // headers: { - // 'Content-Type': imageFile.type, - // }, - // }) + await uploadFileWithPresignedUrl(presignedUrl, imageFile) alert('이미지가 성공적으로 업로드되었습니다!') setFormData(initailFormData) - } catch (error) { - console.error('이미지 업로드 실패:', error) + } catch (err) { + console.error('이미지 업로드 실패:', err) alert('업로드 중 문제가 발생했습니다.') } } return { imagePreview, - setImagePreview, onTypeNameInputChange, onImageInputChange, isSubmitable, onFormSubmit, - formData, } } diff --git a/app/admin/strategies/_ui/trade/trade-manage/types.ts b/app/admin/strategies/_ui/trade/trade-manage/types.ts index f81e71f3..c3ce3092 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/types.ts +++ b/app/admin/strategies/_ui/trade/trade-manage/types.ts @@ -15,3 +15,9 @@ export interface TradeResponseModel extends TradeResponseBaseModel<boolean> { // eslint-disable-next-line export interface ToggleTradeActiveStateResponseModel extends TradeResponseBaseModel<boolean> {} + +export interface PresignedUrlResponseModel extends TradeResponseBaseModel<boolean> { + result: { + presignedUrl: string + } +} From c261993e2634d2047a7a8ec8a050444b7c6aa7f2 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 05:07:46 +0900 Subject: [PATCH 464/648] =?UTF-8?q?chore:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EB=A7=A4=20=EC=9C=A0=ED=98=95=20api=20=EC=A4=91=20que?= =?UTF-8?q?ry=EB=8A=94=20=EB=94=B0=EB=A1=9C=20=ED=8F=B4=EB=8D=94=EB=A1=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_hooks/{ => query}/use-toggle-trade-active-state.ts | 2 +- .../trade/trade-manage/_hooks/{ => query}/use-trades-data.ts | 2 +- .../_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx | 2 +- .../_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx | 2 +- .../trade/trade-manage/_ui/trade-active-state-toggle-button.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename app/admin/strategies/_ui/trade/trade-manage/_hooks/{ => query}/use-toggle-trade-active-state.ts (86%) rename app/admin/strategies/_ui/trade/trade-manage/_hooks/{ => query}/use-trades-data.ts (88%) diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-toggle-trade-active-state.ts b/app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts similarity index 86% rename from app/admin/strategies/_ui/trade/trade-manage/_hooks/use-toggle-trade-active-state.ts rename to app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts index d1dcba6d..c48878f5 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-toggle-trade-active-state.ts +++ b/app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts @@ -1,6 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' -import ToggleTradeActiveState from '../_api/toggle-trade-active-state' +import ToggleTradeActiveState from '../../_api/toggle-trade-active-state' const useToggoleTradeActiveState = (tradeTypeId: number) => { const queryClient = useQueryClient() diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-trades-data.ts b/app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts similarity index 88% rename from app/admin/strategies/_ui/trade/trade-manage/_hooks/use-trades-data.ts rename to app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts index 3063f44e..7d32530d 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-trades-data.ts +++ b/app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query' -import getTrades from '../_api/get-trades' +import getTrades from '../../_api/get-trades' type ArgType = 'active' | 'inactive' diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx index 718ec18f..d51da41f 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx @@ -1,7 +1,7 @@ import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' -import useTradeData from '../_hooks/use-trades-data' +import useTradeData from '../_hooks/query/use-trades-data' import TradeActiveStateToggleButton from './trade-active-state-toggle-button' const ActiveTradeManageTable = () => { diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx index f775995e..aa54973c 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx @@ -1,7 +1,7 @@ import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' -import useTradeData from '../_hooks/use-trades-data' +import useTradeData from '../_hooks/query/use-trades-data' import TradeActiveStateToggleButton from './trade-active-state-toggle-button' const InactiveTradeManageTable = () => { diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx index 8f82af3e..3ec95b75 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx @@ -4,7 +4,7 @@ import { CSSProperties } from 'react' import { Button } from '@/shared/ui/button' -import useToggoleTradeActiveState from '../_hooks/use-toggle-trade-active-state' +import useToggoleTradeActiveState from '../_hooks/query/use-toggle-trade-active-state' interface Props { active?: boolean From 5c0470ad2cbd2ce5998ac9a969aa991b99ec39c0 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 11:26:24 +0900 Subject: [PATCH 465/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=20=EA=B4=80=EB=A6=AC=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EA=B5=AC=ED=98=84=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/manage-table/index.tsx | 31 +++++++++++----- .../stock-manage/_api/get-presigned-url.ts | 17 +++++++++ .../_ui/stock/stock-manage/_api/get-stocks.ts | 23 ++++++++++++ .../_hooks/query/use-stocks-data.ts | 16 +++++++++ .../_ui/active-stock-manage-table.tsx | 36 +++++++++++++++++++ .../_ui/inactive-stock-manage-table.tsx | 34 ++++++++++++++++++ .../_ui/stock/stock-manage/types.ts | 31 ++++++++++++++++ app/admin/strategies/page.tsx | 2 +- 8 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx create mode 100644 app/admin/strategies/_ui/stock/stock-manage/types.ts diff --git a/app/admin/strategies/_ui/shared/manage-table/index.tsx b/app/admin/strategies/_ui/shared/manage-table/index.tsx index 315034fd..59b9d1d0 100644 --- a/app/admin/strategies/_ui/shared/manage-table/index.tsx +++ b/app/admin/strategies/_ui/shared/manage-table/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useState } from 'react' +import { Dispatch, ReactNode, SetStateAction } from 'react' import classNames from 'classnames/bind' @@ -15,10 +15,23 @@ interface Props { active?: boolean domain: '종목' | '매매 유형' data: (ReactNode | string | number)[][] + //TODO: domain이 종록일 때만 쓰는 속성들 컨트롤 하는 법 고민해보기 + size?: number + currentPage?: number + setCurrentPage?: Dispatch<SetStateAction<number>> + maxPage?: number } -const ManageTable = ({ active, domain, data }: Props) => { - const [currentPage, setCurrentPage] = useState(1) +const ManageTable = ({ + active, + domain, + data, + size = COUNT_PER_PAGE, + currentPage = 1, + maxPage, + setCurrentPage, +}: Props) => { + // const [currentPage, setCurrentPage] = useState(1) const hasData = data?.length > 0 @@ -28,27 +41,27 @@ const ManageTable = ({ active, domain, data }: Props) => { <VerticalTable tableHead={['No.', domain === '종목' ? '종목명' : '매매 유형', '분류', '상태']} tableBody={addIndexToData(data)} - countPerPage={COUNT_PER_PAGE} - currentPage={1} + countPerPage={size} + currentPage={1} // 공통 컴포넌트와의 호환성 문제... /> {hasData && domain === '종목' && ( <Pagination currentPage={currentPage} - maxPage={3} - onPageChange={(page) => setCurrentPage(page)} + maxPage={maxPage ?? 3} + onPageChange={(page) => setCurrentPage?.(page)} /> )} </div> ) } -const Skeleton = ({ active, domain }: Pick<Props, 'active' | 'domain'>) => { +const Skeleton = ({ active, domain, size }: Pick<Props, 'active' | 'domain' | 'size'>) => { return ( <div className={cx('container')}> <span className={cx('title')}>{active ? '활성화' : '비활성화'}</span> <VerticalTable.Skeleton tableHead={['No.', domain === '종목' ? '종목명' : '매매 유형', '분류', '상태']} - countPerPage={8} //TODO: 이거 디자인에 맞춰서 크기 조절 + countPerPage={size} //TODO: 이거 디자인에 맞춰서 크기 조절 /> </div> ) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts b/app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts new file mode 100644 index 00000000..ad0aaa5d --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts @@ -0,0 +1,17 @@ +import axios from 'axios' + +import { PresignedUrlResponseModel } from '../types' + +const getPresignedUrl = async (typeName: string, imageName: string, imageSize: number) => { + const res = await axios.post<PresignedUrlResponseModel>('/api/admin/strategies/trade-type', { + tradeTypeName: typeName, + tradeTypeIconUrl: imageName, + size: imageSize, + }) + + if (!res.data.isSuccess) throw new Error('presigned url Error : ' + res.data.message) + + return res.data.result.presignedUrl +} + +export default getPresignedUrl diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts b/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts new file mode 100644 index 00000000..d8b93512 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts @@ -0,0 +1,23 @@ +import axios from 'axios' + +import { StockResponseModel } from '../types' + +const getStocks = async (activateState: boolean, page: number, size: number) => { + try { + const res = await axios<StockResponseModel>('/api/admin/strategies/stock-type', { + params: { + activateState, + page, + size, + }, + }) + + if (!res.data.isSuccess) throw new Error(res.data.message) + + return res.data + } catch (err) { + console.log('Error : ' + err) + } +} + +export default getStocks diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts new file mode 100644 index 00000000..ed812a19 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts @@ -0,0 +1,16 @@ +import { useQuery } from '@tanstack/react-query' + +import getStocks from '../../_api/get-stocks' + +type ArgType = 'active' | 'inactive' + +const useStocksData = (activateState: ArgType, page: number, size: number) => { + const isActive = activateState === 'active' ? true : false + + return useQuery({ + queryKey: ['adminstocks', activateState, page, size], + queryFn: () => getStocks(isActive, page, size), + }) +} + +export default useStocksData diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx new file mode 100644 index 00000000..0d245213 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -0,0 +1,36 @@ +'use client' + +import { useState } from 'react' + +import withSuspense from '@/shared/utils/with-suspense' + +import ManageTable from '../../../shared/manage-table' +import useStocksData from '../_hooks/query/use-stocks-data' + +const TABLE_BODY_SIZE = 10 + +const ActiveStockManageTable = () => { + const [currentPage, setCurrentPage] = useState(1) + + const { data } = useStocksData('active', currentPage, TABLE_BODY_SIZE) + const tableData = + data?.result?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ + stockTypeName, + <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, + // <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, + ]) ?? [] + + return ( + <ManageTable + data={tableData} + size={TABLE_BODY_SIZE} + currentPage={currentPage} + setCurrentPage={setCurrentPage} + maxPage={data?.result.totalPages} + active + domain="종목" + /> + ) +} + +export default withSuspense(ActiveStockManageTable, <ManageTable.Skeleton domain="종목" />) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx new file mode 100644 index 00000000..63efbcc8 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react' + +import withSuspense from '@/shared/utils/with-suspense' + +import ManageTable from '../../../shared/manage-table' +import useStocksData from '../_hooks/query/use-stocks-data' + +const TABLE_BODY_SIZE = 10 + +const InactiveTradeManageTable = () => { + const [currentPage, setCurrentPage] = useState(1) + + const { data } = useStocksData('inactive', currentPage, TABLE_BODY_SIZE) + const tableData = + data?.result?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ + stockTypeName, + <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, + // <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, + ]) ?? [] + + return ( + <ManageTable + data={tableData} + size={TABLE_BODY_SIZE} + currentPage={currentPage} + setCurrentPage={setCurrentPage} + maxPage={data?.result.totalPages} + active + domain="종목" + /> + ) +} + +export default withSuspense(InactiveTradeManageTable, <ManageTable.Skeleton domain="종목" />) diff --git a/app/admin/strategies/_ui/stock/stock-manage/types.ts b/app/admin/strategies/_ui/stock/stock-manage/types.ts new file mode 100644 index 00000000..76a8be23 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/types.ts @@ -0,0 +1,31 @@ +interface StockResponseBaseModel<T extends boolean> { + isSuccess: T + message: string + code: T extends false ? number : never +} + +export interface StockResponseModel extends StockResponseBaseModel<boolean> { + result: { + content: Array<{ + stockTypeId: number + stockTypeName: string + activateState: boolean + stockTypeIconUrl: string + }> + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean + } +} + +// eslint-disable-next-line +export interface ToggleStockActiveStateResponseModel extends StockResponseBaseModel<boolean> {} + +export interface PresignedUrlResponseModel extends StockResponseBaseModel<boolean> { + result: { + presignedUrl: string + } +} diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index 462458ee..a7b382c3 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -16,7 +16,7 @@ const cx = classNames.bind(styles) const tabs = ['종목', '매매유형'] as const const AdminStrategiesPage = () => { - const [activeTab, setActiveTab] = useState<(typeof tabs)[number]>('매매유형') + const [activeTab, setActiveTab] = useState<(typeof tabs)[number]>('종목') return ( <> From 8e229322828b922ea4cee353c1cb8ee0df85ca41 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 11:41:31 +0900 Subject: [PATCH 466/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=20=EA=B4=80=EB=A6=AC=20patch=20api=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_api/toggle-stock-active-state.ts | 19 +++ .../query/use-toggle-trade-active-state.ts | 21 +++ .../_ui/active-stock-manage-table.tsx | 3 +- .../_ui/inactive-stock-manage-table.tsx | 3 +- .../_ui/stock-active-state-toggle-button.tsx | 38 +++++ .../_ui/stock/stock-manage/index.tsx | 159 ++++++++++-------- .../_ui/stock/stock-manage/styles.module.scss | 7 + 7 files changed, 175 insertions(+), 75 deletions(-) create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx create mode 100644 app/admin/strategies/_ui/stock/stock-manage/styles.module.scss diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts b/app/admin/strategies/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts new file mode 100644 index 00000000..16d1a16b --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts @@ -0,0 +1,19 @@ +import axios from 'axios' + +import { ToggleStockActiveStateResponseModel } from '../types' + +const ToggleStockActiveState = async (stockTypeId: number) => { + try { + const res = await axios.patch<ToggleStockActiveStateResponseModel>( + `/api/admin/strategies/stock-type/${stockTypeId}` + ) + + if (!res.data.isSuccess) throw new Error(res.data.message) + + return res.data + } catch (err) { + console.log('Error : ' + err) + } +} + +export default ToggleStockActiveState diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts new file mode 100644 index 00000000..d190e1dc --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts @@ -0,0 +1,21 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import ToggleStockActiveState from '../../_api/toggle-stock-active-state' + +const useToggoleStockActiveState = (stockTypeId: number) => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => ToggleStockActiveState(stockTypeId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ['adminStocks'], + }) + }, + onError: (err) => { + console.error('Error : ', err) + }, + }) +} + +export default useToggoleStockActiveState diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx index 0d245213..bc5dd51d 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -6,6 +6,7 @@ import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' import useStocksData from '../_hooks/query/use-stocks-data' +import StockActiveStateToggleButton from './stock-active-state-toggle-button' const TABLE_BODY_SIZE = 10 @@ -17,7 +18,7 @@ const ActiveStockManageTable = () => { data?.result?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, - // <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, + <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, ]) ?? [] return ( diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx index 63efbcc8..34e1c7ed 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx @@ -4,6 +4,7 @@ import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' import useStocksData from '../_hooks/query/use-stocks-data' +import StockActiveStateToggleButton from './stock-active-state-toggle-button' const TABLE_BODY_SIZE = 10 @@ -15,7 +16,7 @@ const InactiveTradeManageTable = () => { data?.result?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, - // <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, + <StockActiveStateToggleButton stockTypeId={stockTypeId} key={stockTypeId} />, ]) ?? [] return ( diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx new file mode 100644 index 00000000..4edd4539 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx @@ -0,0 +1,38 @@ +'use client' + +import { CSSProperties } from 'react' + +import { Button } from '@/shared/ui/button' + +import useToggoleStockActiveState from '../_hooks/query/use-toggle-trade-active-state' + +interface Props { + active?: boolean + stockTypeId: number +} + +const StockActiveStateToggleButton = ({ active, stockTypeId }: Props) => { + const { mutate, isPending } = useToggoleStockActiveState(stockTypeId) + const onButtonClick = () => { + mutate() + } + + return ( + <Button + onClick={onButtonClick} + variant={active ? 'outline' : 'filled'} + size="small" + style={buttonStyles} + disabled={isPending} + > + {active ? '비활성화' : '활성화'} + </Button> + ) +} + +const buttonStyles: CSSProperties = { + height: '30px', + padding: '7px 16px', + margin: '15px 0', +} +export default StockActiveStateToggleButton diff --git a/app/admin/strategies/_ui/stock/stock-manage/index.tsx b/app/admin/strategies/_ui/stock/stock-manage/index.tsx index 56f22c42..b2b1f0d0 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/index.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/index.tsx @@ -1,14 +1,22 @@ import { useState } from 'react' +import axios from 'axios' +import classNames from 'classnames/bind' + import useModal from '@/shared/hooks/custom/use-modal' -import { Button } from '@/shared/ui/button' -import { Input } from '@/shared/ui/input' + +import ActiveStockManageTable from './_ui/active-stock-manage-table' +import InactiveStockManageTable from './_ui/inactive-stock-manage-table' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) const StockManage = () => { const { isModalOpen, openModal, closeModal } = useModal() const onClick = () => openModal() const [imagePreview, setImagePreview] = useState<string | null>(null) + const [imageFile, setImageFile] = useState<File | null>(null) const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => { const file = event.target.files?.[0] @@ -19,8 +27,7 @@ const StockManage = () => { return } - console.log(file) - console.log(file.size) + setImageFile(file) const reader = new FileReader() reader.onload = (e) => { @@ -28,84 +35,90 @@ const StockManage = () => { } reader.readAsDataURL(file) } else { - setImagePreview(null) // 파일이 없으면 미리보기 초기화 + setImagePreview(null) } } - const onSubmit = () => {} - - // const [tra] - - const fetchTrade = async () => { - // const res = await fetch( - // 'http://15.164.90.102:8081/api/strategies/trade-type?activateState=true' - // ) - // const res = await fetch('http://15.164.90.102:8081/api/admin/strategies/stock-type', { - // method: 'POST', - // body: JSON.stringify({ - // stockTypeName: '주식', - // stockTypeIconURL: 'StockIconTest.png', - // size: 456, - // // tradeTypeName: '자동', - // // tradeTypeIconURL: 'IconTest.svg', - // // size: 456, - // }), - // }) - const res = await fetch( - 'http://15.164.90.102:8081/api/admin/strategies/trade-types/{trade_type_id}', - { - method: 'PATCH', - } - ) + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!imageFile) { + alert('이미지를 선택하세요!') + return + } - const result = await res.json() + try { + console.log('f', imageFile) - console.log(result) + const presignedResponse = await axios.post('/api/admin/strategies/trade-type', { + tradeTypeName: '자동', + tradeTypeIconUrl: imageFile.name, + size: imageFile.size, + }) + + console.log('p', presignedResponse) + + const { presignedUrl } = presignedResponse.data.result + + // Step 2: 이미지 업로드 + await axios.put(presignedUrl, imageFile, { + headers: { + 'Content-Type': imageFile.type, + }, + }) + + alert('이미지가 성공적으로 업로드되었습니다!') + } catch (error) { + console.error('이미지 업로드 실패:', error) + alert('업로드 중 문제가 발생했습니다.') + } } return ( - <div> - <Button onClick={fetchTrade} variant="filled"> - fetch - </Button> - {/* <img - src="https://fastcampus-team3.s3.ap-northeast-2.amazonaws.com/strategy/image/07421e08/icons/sampleTrade-icon2.png" - alt="img" - /> */} - {/* <Modal icon={<RegisterIcon />} message="종목 등록" isOpen={isModalOpen}> */} - <form - onSubmit={onSubmit} - style={{ marginTop: '24px', display: 'flex', flexDirection: 'column', gap: '12px' }} - > - <div style={{ display: 'flex', gap: '12px' }}> - 종목 : <Input placeholder="종목명을 입력하세요." inputSize="small" /> - </div> - <Input type="file" accept="image/*" onChange={handleImageChange} /> - {imagePreview && ( - <img src={imagePreview} alt="Preview" style={{ maxWidth: '200px', maxHeight: '200px' }} /> - )} - <Button.ButtonGroup gap="24px"> - <Button onClick={closeModal}>취소</Button> - <Button - onClick={() => { - alert('123') - }} - variant="filled" - > - 등록 - </Button> - </Button.ButtonGroup> - </form> - - {/* </Modal> */} - {/* <VerticalTable - tableHead={['No.', '종목명', '분류', '상태']} - tableBody={[]} - countPerPage={8} - currentPage={1} - /> - <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> */} + <div className={cx('container')}> + {/* <TradePostButton /> */} + <ActiveStockManageTable /> + <InactiveStockManageTable /> </div> + // <div> + // <Button onClick={onClick} variant="filled"> + // modal + // </Button> + // {/* <img + // src="https://fastcampus-team3.s3.ap-northeast-2.amazonaws.com/strategy/image/07421e08/icons/sampleTrade-icon2.png" + // alt="img" + // /> */} + // <Modal icon={<RegisterIcon />} message="종목 등록" isOpen={isModalOpen}> + // <form + // onSubmit={onSubmit} + // style={{ marginTop: '24px', display: 'flex', flexDirection: 'column', gap: '12px' }} + // > + // <div style={{ display: 'flex', gap: '12px' }}> + // 종목 : <Input placeholder="종목명을 입력하세요." inputSize="small" /> + // </div> + // <Input type="file" accept="image/*" onChange={handleImageChange} /> + // {imagePreview && ( + // <img + // src={imagePreview} + // alt="Preview" + // style={{ maxWidth: '200px', maxHeight: '200px' }} + // /> + // )} + // <Button.ButtonGroup gap="24px"> + // <Button onClick={closeModal} type="button"> + // 취소 + // </Button> + // <Button variant="filled">등록</Button> + // </Button.ButtonGroup> + // </form> + // </Modal> + // {/* <VerticalTable + // tableHead={['No.', '종목명', '분류', '상태']} + // tableBody={[]} + // countPerPage={8} + // currentPage={1} + // /> + // <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> */} + // </div> ) } diff --git a/app/admin/strategies/_ui/stock/stock-manage/styles.module.scss b/app/admin/strategies/_ui/stock/stock-manage/styles.module.scss new file mode 100644 index 00000000..1a20c271 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/styles.module.scss @@ -0,0 +1,7 @@ +.container { + position: relative; + margin-top: 32px; + display: flex; + justify-content: space-between; + gap: 50px; +} From 2d80aa933740f7562c999489a64cedfbbc26c410 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 11:45:59 +0900 Subject: [PATCH 467/648] =?UTF-8?q?fix:=20useQuery=EB=A5=BC=20useSuspenseQ?= =?UTF-8?q?uery=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts index ed812a19..6b748d5f 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts +++ b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' import getStocks from '../../_api/get-stocks' @@ -7,7 +7,7 @@ type ArgType = 'active' | 'inactive' const useStocksData = (activateState: ArgType, page: number, size: number) => { const isActive = activateState === 'active' ? true : false - return useQuery({ + return useSuspenseQuery({ queryKey: ['adminstocks', activateState, page, size], queryFn: () => getStocks(isActive, page, size), }) From e4a7024c68e535b3652ed606251b7a3923c1ae71 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 2 Dec 2024 12:38:47 +0900 Subject: [PATCH 468/648] =?UTF-8?q?feat:=20=EB=8B=B5=EB=B3=80=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20textarea=20=EC=B6=94=EA=B0=80=20(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-container/index.tsx | 42 ++++++++++++++++--- .../_ui/question-container/styles.module.scss | 18 ++++++++ shared/ui/textarea/styles.module.scss | 2 +- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 1c321056..5b0b8e8e 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -1,6 +1,11 @@ +'use client' + +import { useState } from 'react' + import classNames from 'classnames/bind' import { Button } from '@/shared/ui/button' +import { Textarea } from '@/shared/ui/textarea' import QuestionDetailCard from '../question-detail-card' import styles from './styles.module.scss' @@ -8,8 +13,21 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const QuestionContainer = () => { + const [isActiveAnswer, setIsActiveAnswer] = useState(false) // 임시 - const hasAnswer = true + const hasAnswer = false + const isTrader = true + + const handleQuestionAdd = () => {} + + const handleAnswerAdd = () => { + setIsActiveAnswer((prevState) => !prevState) + } + + // TODO: Trader, Investor에 따라 적절한 UI 표시 + // Trader이고 답변이 달리지 않았을 때: 답변하기 버튼 + // Trader이고 답변이 달렸을 때: 답변 삭제하기 버튼 + // Investor일 때: 추가 질문하기 버튼 return ( <> @@ -32,12 +50,26 @@ const QuestionContainer = () => { createdAt="2024-11-03T15:00:00" /> ) : ( - <p className={cx('empty-message')}>아직 답변이 없습니다</p> + <>{!isTrader && <p className={cx('empty-message')}>아직 답변이 없습니다</p>}</> )} - <Button variant="filled" className={cx('button')}> - 추가 질문하기 - </Button> + {isActiveAnswer ? ( + <div className={cx('answer-input-wrapper')}> + <div className={cx('title-wrapper')}> + <h2 className={cx('title')}>답변</h2> + <Button size="small">등록하기</Button> + </div> + <Textarea placeholder="내용을 입력하세요." /> + </div> + ) : ( + <Button + variant="filled" + className={cx('button')} + onClick={isTrader ? handleAnswerAdd : handleQuestionAdd} + > + {isTrader ? '답변하기' : '추가 질문하기'} + </Button> + )} </div> </> ) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss index 0702276c..0e982a35 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/styles.module.scss @@ -15,3 +15,21 @@ color: $color-gray-600; @include typo-b1; } + +.answer-input-wrapper { + width: 100%; + padding: 35px 40px; + margin-bottom: 120px; + background-color: $color-white; + + .title-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 18px; + } + + .title { + @include typo-b1; + } +} diff --git a/shared/ui/textarea/styles.module.scss b/shared/ui/textarea/styles.module.scss index c81af07c..ef86832a 100644 --- a/shared/ui/textarea/styles.module.scss +++ b/shared/ui/textarea/styles.module.scss @@ -1,7 +1,7 @@ .textarea { padding: 8px 12px; border-radius: 2px; - border: 1px solid $color-gray-400; + border: 1px solid $color-gray-300; color: $color-gray-400; width: 100%; height: 100%; From fea2d8357dd10eb6e5f5d491e10ded2990f8cf16 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 2 Dec 2024 09:45:43 +0900 Subject: [PATCH 469/648] =?UTF-8?q?feat:=20skeleton=20mixin=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/styles/base/_mixins.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index 339b00a1..5a13bca3 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -113,3 +113,14 @@ overflow: hidden; } } + +// skeleton +@mixin skeleton { + border-radius: 6px; + background-color: $color-gray-200; + + div { + border-radius: 6px; + background-color: $color-gray-300; + } +} From 0c1eac42838f019117a0906670046f4a68cdd0c3 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 13:26:23 +0900 Subject: [PATCH 470/648] =?UTF-8?q?feat:=20searchWord=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EC=95=A1=EC=85=98=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B0=B1=EC=97=94=EB=93=9C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95=ED=95=9C=20=EA=B2=80=EC=83=89=EC=96=B4=20enu?= =?UTF-8?q?m=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EC=83=81=ED=83=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20(#181)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_store/use-searching-item-store.ts | 21 ++++++++++++++----- .../_ui/search-bar/algorithm-item.tsx | 9 ++++---- .../_ui/search-bar/panel-mapping.ts | 18 ++++++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 app/(dashboard)/strategies/_ui/search-bar/panel-mapping.ts diff --git a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts index a9353af0..dbca850f 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts +++ b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts @@ -2,6 +2,7 @@ import { create } from 'zustand' import { AlgorithmItemType, RangeModel, SearchTermsModel } from '../_type/search' import { isRangeModel } from '../_utils/type-validate' +import { PANEL_MAPPING } from '../panel-mapping' interface StateModel { searchTerms: SearchTermsModel @@ -12,6 +13,7 @@ interface ActionModel { setAlgorithm: (algorithm: AlgorithmItemType) => void setPanelItem: (key: keyof SearchTermsModel, item: string) => void setRangeValue: (key: keyof SearchTermsModel, type: keyof RangeModel, value: number) => void + setSearchWord: (searchWord: string) => void resetState: () => void validateRangeValues: () => void } @@ -37,7 +39,7 @@ const useSearchingItemStore = create<StateModel & ActionsModel>((set, get) => ({ searchTerms: { ...initialState, }, - errOptions: [], + errOptions: null, actions: { setAlgorithm: (algorithm) => @@ -47,14 +49,15 @@ const useSearchingItemStore = create<StateModel & ActionsModel>((set, get) => ({ setPanelItem: (key, item) => set((state) => { + const mappingItem = PANEL_MAPPING[key]?.[item] || item const currentItems = state.searchTerms[key] if (Array.isArray(currentItems)) { - const updatedItems = currentItems.includes(item) - ? currentItems.filter((i) => i !== item) - : [...currentItems, item] + const updatedItems = currentItems.includes(mappingItem) + ? currentItems.filter((i) => i !== mappingItem) + : [...currentItems, mappingItem] return { searchTerms: { ...state.searchTerms, [key]: [...updatedItems] } } } - return { searchTerms: { ...state.searchTerms, [key]: [item] } } + return { searchTerms: { ...state.searchTerms, [key]: [mappingItem] } } }), setRangeValue: (key, type, value) => @@ -65,6 +68,14 @@ const useSearchingItemStore = create<StateModel & ActionsModel>((set, get) => ({ }, })), + setSearchWord: (searchWord) => + set((state) => ({ + searchTerms: { + ...state.searchTerms, + searchWord, + }, + })), + resetState: () => set(() => ({ searchTerms: { ...initialState }, errOptions: [] })), validateRangeValues: () => { diff --git a/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx b/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx index 10606e92..fc234356 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx @@ -8,16 +8,17 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - name: AlgorithmItemType + optionId: AlgorithmItemType + name: string clickedAlgorithm: AlgorithmItemType | null onChange: (algorithm: AlgorithmItemType) => void } -const AlgorithmItem = ({ name, clickedAlgorithm, onChange }: Props) => { +const AlgorithmItem = ({ optionId, name, clickedAlgorithm, onChange }: Props) => { return ( <button - className={cx('algorithm-button', { active: clickedAlgorithm === name })} - onClick={() => onChange(name)} + className={cx('algorithm-button', { active: clickedAlgorithm === optionId })} + onClick={() => onChange(optionId)} > {name} </button> diff --git a/app/(dashboard)/strategies/_ui/search-bar/panel-mapping.ts b/app/(dashboard)/strategies/_ui/search-bar/panel-mapping.ts new file mode 100644 index 00000000..e11ca03f --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/panel-mapping.ts @@ -0,0 +1,18 @@ +export const PANEL_MAPPING: { [key: string]: Record<string, string> } = { + operationCycles: { + 데이: 'DAY', + 포지션: 'POSITION', + }, + durations: { + '1년 이하': 'ONE_YEAR_OR_LESS', + '1년 ~ 2년': 'ONE_TO_TWO_YEARS', + '2년 ~ 3년': 'TWO_TO_THREE_YEARS', + '3년 이상': 'THREE_YEARS_OR_MORE', + }, + profitRanges: { + '10% 이하': 'UNDER_10_PERCENT', + '10% ~ 20%': 'BETWEEN_10_AND_20', + '20% ~ 30%': 'BETWEEN_20_AND_30', + '30% 이상': 'OVER_30_PERCENT', + }, +} From 429654ead6ddb3ccd2994dee9cbf46569255788b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 13:27:36 +0900 Subject: [PATCH 471/648] =?UTF-8?q?feat:=20msw=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0api=EB=A1=9C=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B2=80=EC=83=89=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/manage/[strategyId]/page.tsx | 3 - .../_api/get-details-information.ts | 10 +-- .../query/use-get-details-information-data.ts | 5 +- .../strategies/[strategyId]/page.tsx | 3 - .../strategies/_api/get-strategies-search.ts | 12 +++ .../strategies/_api/get-strategies.ts | 28 ------- .../strategies/_api/post-strategies.ts | 17 ++++ .../_hooks/query/use-get-strategies-data.ts | 19 ----- .../_hooks/query/use-get-strategies-search.ts | 12 +++ .../_hooks/query/use-post-strategies.ts | 21 +++++ .../_ui/search-bar/accordion-container.tsx | 3 + .../_ui/search-bar/accordion-panel.tsx | 5 +- .../strategies/_ui/search-bar/index.tsx | 83 +++++++++++++------ .../strategies/_ui/strategy-list/index.tsx | 14 ++-- 14 files changed, 136 insertions(+), 99 deletions(-) create mode 100644 app/(dashboard)/strategies/_api/get-strategies-search.ts delete mode 100644 app/(dashboard)/strategies/_api/get-strategies.ts create mode 100644 app/(dashboard)/strategies/_api/post-strategies.ts delete mode 100644 app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts create mode 100644 app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts create mode 100644 app/(dashboard)/strategies/_hooks/query/use-post-strategies.ts diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index 58aa872a..7ff3fffc 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -11,7 +11,6 @@ import useGetDetailsInformationData from '@/app/(dashboard)/strategies/[strategy import SideContainer from '@/app/(dashboard)/strategies/_ui/side-container' import classNames from 'classnames/bind' -import { useMSWStore } from '@/shared/stores/msw' import { Button } from '@/shared/ui/button' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' @@ -23,9 +22,7 @@ const cx = classNames.bind(styles) export type InformationType = { title: TitleType; data: string | number } | InformationModel[] const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { - const isReady = useMSWStore((state) => state.isReady) const { data } = useGetDetailsInformationData({ - isReady, strategyId: params.strategyId, }) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index 4ef6fa78..36f29ffc 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -2,15 +2,11 @@ import axios from 'axios' import { InformationType } from '../page' -const getDetailsInformation = async (isReady: boolean, strategyId: number) => { - if (!isReady || !strategyId) return +const getDetailsInformation = async (strategyId: number) => { + if (!strategyId) return try { - const response = await axios.get(`/api/strategies/${strategyId}`) - if (!response.data) { - console.error('전략 상세 데이터 가져오기 실패') - return - } + const response = await axios.get(`/api/strategies/${strategyId}/detail`) const data = await response.data const detailsSideData: InformationType[] = [ { title: '트레이더', data: data.nickname }, diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts index 0d0ebe35..c06f4980 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts @@ -6,12 +6,10 @@ import getDetailsInformation from '../../_api/get-details-information' import { InformationType } from '../../page' interface Props { - isReady: boolean strategyId: number } const useGetDetailsInformationData = ({ - isReady, strategyId, }: Props): { data: @@ -23,8 +21,7 @@ const useGetDetailsInformationData = ({ } => { return useQuery({ queryKey: ['strategyDetails', strategyId], - queryFn: () => getDetailsInformation(isReady, strategyId), - enabled: isReady, + queryFn: () => getDetailsInformation(strategyId), }) } diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index c2181f2e..08949eeb 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -3,7 +3,6 @@ import AnalysisContainer from '@/app/(dashboard)/_ui/analysis-container' import SubscriberItem from '@/app/(dashboard)/_ui/subscriber-item' -import { useMSWStore } from '@/shared/stores/msw' import { useAuthStore } from '@/shared/stores/use-auth-store' import BackHeader from '@/shared/ui/header/back-header' import Title from '@/shared/ui/title' @@ -17,10 +16,8 @@ import ReviewContainer from './_ui/review-container' export type InformationType = { title: TitleType; data: string | number } | InformationModel[] const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { - const isReady = useMSWStore((state) => state.isReady) const user = useAuthStore((state) => state.user) const { data } = useGetDetailsInformationData({ - isReady, strategyId: params.strategyId, }) diff --git a/app/(dashboard)/strategies/_api/get-strategies-search.ts b/app/(dashboard)/strategies/_api/get-strategies-search.ts new file mode 100644 index 00000000..84a238db --- /dev/null +++ b/app/(dashboard)/strategies/_api/get-strategies-search.ts @@ -0,0 +1,12 @@ +import axios from 'axios' + +const getStrategiesSearch = async () => { + try { + const response = await axios.get('api/strategies/search') + return response.data.result + } catch (err) { + console.error(err) + } +} + +export default getStrategiesSearch diff --git a/app/(dashboard)/strategies/_api/get-strategies.ts b/app/(dashboard)/strategies/_api/get-strategies.ts deleted file mode 100644 index 29b7c54d..00000000 --- a/app/(dashboard)/strategies/_api/get-strategies.ts +++ /dev/null @@ -1,28 +0,0 @@ -import axios from 'axios' - -import { StrategiesModel } from '@/shared/types/strategy-data' - -const getStrategiesData = async ( - isReady: boolean, - page: number, - size: number -): Promise<{ strategiesData: StrategiesModel[]; totalPages: number } | undefined> => { - if (!isReady) return { strategiesData: [], totalPages: 0 } - - try { - const response = await axios.get(`/api/strategies?page=${page}&size=${size}`) - if (!response.data) { - console.error('전략 목록 데이터 가져오기 실패') - } - const { - content: strategiesData, - totalPages, - }: { content: StrategiesModel[]; totalPages: number } = await response.data.result - - return { strategiesData, totalPages } - } catch (err) { - console.error(err) - } -} - -export default getStrategiesData diff --git a/app/(dashboard)/strategies/_api/post-strategies.ts b/app/(dashboard)/strategies/_api/post-strategies.ts new file mode 100644 index 00000000..47118e52 --- /dev/null +++ b/app/(dashboard)/strategies/_api/post-strategies.ts @@ -0,0 +1,17 @@ +import axios from 'axios' + +import { SearchTermsModel } from '../_ui/search-bar/_type/search' + +const postStrategies = async (page: number, size: number, searchTerms: SearchTermsModel) => { + try { + const response = await axios.post( + `/api/strategies/search?page=${page}&size=${size}`, + searchTerms + ) + return response.data.result + } catch (err) { + console.error(err) + } +} + +export default postStrategies diff --git a/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts deleted file mode 100644 index 508b7ab1..00000000 --- a/app/(dashboard)/strategies/_hooks/query/use-get-strategies-data.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useQuery } from '@tanstack/react-query' - -import getStrategiesData from '../../_api/get-strategies' - -interface Props { - isReady: boolean - page: number - size: number -} - -const useGetStrategiesData = ({ isReady, page, size }: Props) => { - return useQuery({ - queryKey: ['strategies', page, size], - queryFn: () => getStrategiesData(isReady, page, size), - enabled: isReady, - }) -} - -export default useGetStrategiesData diff --git a/app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts new file mode 100644 index 00000000..77452fe8 --- /dev/null +++ b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import getStrategiesSearch from '../../_api/get-strategies-search' + +const useGetStrategiesSearch = () => { + return useQuery({ + queryKey: ['strategiesSearch'], + queryFn: () => getStrategiesSearch(), + }) +} + +export default useGetStrategiesSearch diff --git a/app/(dashboard)/strategies/_hooks/query/use-post-strategies.ts b/app/(dashboard)/strategies/_hooks/query/use-post-strategies.ts new file mode 100644 index 00000000..c9297832 --- /dev/null +++ b/app/(dashboard)/strategies/_hooks/query/use-post-strategies.ts @@ -0,0 +1,21 @@ +import { useQuery } from '@tanstack/react-query' + +import postStrategies from '../../_api/post-strategies' +import { SearchTermsModel } from '../../_ui/search-bar/_type/search' + +const usePostStrategies = ({ + page, + size, + searchTerms, +}: { + page: number + size: number + searchTerms: SearchTermsModel +}) => { + return useQuery({ + queryKey: ['strategies'], + queryFn: () => postStrategies(page, size, searchTerms), + }) +} + +export default usePostStrategies diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx index 53b6246c..7c5c4acf 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx @@ -30,6 +30,9 @@ interface Props { const AccordionContainer = ({ optionId, title, panels }: Props) => { const { openIds, panelRef, handleButtonIds } = useAccordionButton() + if (optionId === 'tradeTypeNames' && panels?.length === 0) return null + if (optionId === 'stockTypeNames' && panels?.length === 0) return null + return ( <AccordionContext.Provider value={{ openIds, panelRef, handleButtonIds }}> <div> diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx index 249f9bad..d7bd0212 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx @@ -8,6 +8,7 @@ import classNames from 'classnames/bind' import useSearchingItemStore from './_store/use-searching-item-store' import { SearchTermsModel } from './_type/search' import { AccordionContext } from './accordion-container' +import { PANEL_MAPPING } from './panel-mapping' import RangeContainer from './range-container' import styles from './styles.module.scss' @@ -62,7 +63,9 @@ const AccordionPanel = ({ optionId, panels }: Props) => { key={`${panel}-${idx}`} onClick={() => setPanelItem(optionId, panel)} className={cx({ - active: Array.isArray(clickedValue) && clickedValue?.includes(panel), + active: + Array.isArray(clickedValue) && + clickedValue?.includes(PANEL_MAPPING[optionId]?.[panel] ?? panel), })} > <p>{panel}</p> diff --git a/app/(dashboard)/strategies/_ui/search-bar/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/index.tsx index 6633f981..3becbbef 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/index.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/index.tsx @@ -1,14 +1,16 @@ 'use client' -import { useState } from 'react' +import { useRef, useState } from 'react' import classNames from 'classnames/bind' import { Button } from '@/shared/ui/button' import { SearchInput } from '@/shared/ui/search-input' +import useGetStrategiesSearch from '../../_hooks/query/use-get-strategies-search' +import usePostStrategies from '../../_hooks/query/use-post-strategies' import useSearchingItemStore from './_store/use-searching-item-store' -import { SearchTermsModel } from './_type/search' +import { AlgorithmItemType, SearchTermsModel } from './_type/search' import AccordionContainer from './accordion-container' import AlgorithmItem from './algorithm-item' import SearchBarTab from './search-bar-tab' @@ -16,27 +18,54 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -const ALGORITHM_MENU = [ - { EFFICIENT_STRATEGY: '효율형 전략' }, - { ATTACK_STRATEGY: '공격형 전략' }, - { DEFENSIVE_STRATEGY: '방어형 전략' }, -] interface AccordionMenuDataModel { id: keyof SearchTermsModel title: string panels?: string[] } + const SearchBarContainer = () => { const [isMainTab, setIsMainTab] = useState(true) const searchTerms = useSearchingItemStore((state) => state.searchTerms) - const { setAlgorithm, resetState, validateRangeValues } = useSearchingItemStore( + const errOptions = useSearchingItemStore((state) => state.errOptions) + const { setSearchWord, setAlgorithm, resetState, validateRangeValues } = useSearchingItemStore( (state) => state.actions ) + const searchRef = useRef<HTMLInputElement>(null) + const { data } = useGetStrategiesSearch() + const { refetch } = usePostStrategies({ page: 1, size: 8, searchTerms }) + + const handleSearchWord = () => { + if (searchRef.current) { + setSearchWord(searchRef.current.value) + } + } + + const onReset = () => { + resetState() + if (searchRef.current) { + searchRef.current.value = '' + } + refetch() + } + + const onSearch = () => { + validateRangeValues() + if (errOptions === null) { + refetch() + } + } + + const ALGORITHM_MENU = [ + { id: 'EFFICIENT_STRATEGY', name: '효율형 전략' }, + { id: 'ATTACK_STRATEGY', name: '공격형 전략' }, + { id: 'DEFENSIVE_STRATEGY', name: '방어형 전략' }, + ] const ACCORDION_MENU: AccordionMenuDataModel[] = [ - { id: 'tradeTypeNames', title: '매매 유형', panels: ['수동', '자동', '반자동'] }, + { id: 'tradeTypeNames', title: '매매 유형', panels: data?.tradeTypeNames }, { id: 'operationCycles', title: '운용 주기', panels: ['데이', '포지션'] }, - { id: 'stockTypeNames', title: '운영 종목', panels: ['선물', '해외', '국내'] }, + { id: 'stockTypeNames', title: '운영 종목', panels: data?.stockTypeNames }, { id: 'durations', title: '기간', panels: ['1년 이하', '1년 ~ 2년', '2년 ~ 3년', '3년 이상'] }, { id: 'profitRanges', @@ -51,7 +80,12 @@ const SearchBarContainer = () => { return ( <> <div className={cx('searchInput-wrapper')}> - <SearchInput placeholder="전략명을 검색하세요." /> + <SearchInput + ref={searchRef} + placeholder="전략명을 검색하세요." + onChange={handleSearchWord} + onSearchIconClick={onSearch} + /> </div> <div className={cx('searchInput-wrapper')}> <SearchBarTab isMainTab={isMainTab} onChangeTab={setIsMainTab} /> @@ -64,25 +98,20 @@ const SearchBarContainer = () => { panels={menu.panels} /> )) - : ALGORITHM_MENU.map((menu) => { - return Object.entries(menu).map(([key, value]) => ( - <AlgorithmItem - key={key} - name={value} - clickedAlgorithm={searchTerms.algorithmType} - onChange={setAlgorithm} - /> - )) - })} + : ALGORITHM_MENU.map((menu) => ( + <AlgorithmItem + key={menu.id} + optionId={menu.id as AlgorithmItemType} + name={menu.name} + clickedAlgorithm={searchTerms.algorithmType} + onChange={setAlgorithm} + /> + ))} <div className={cx('search-button-wrapper')}> - <Button className={cx('button', 'initialize')} onClick={resetState}> + <Button className={cx('button', 'initialize')} onClick={onReset}> 초기화 </Button> - <Button - variant="filled" - className={cx('button', 'searching')} - onClick={validateRangeValues} - > + <Button variant="filled" className={cx('button', 'searching')} onClick={onSearch}> 검색하기 </Button> </div> diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index 64f80a7f..e61ba297 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -7,24 +7,24 @@ import classNames from 'classnames/bind' import { STRATEGIES_PAGE_COUNT } from '@/shared/constants/count-per-page' import { PATH } from '@/shared/constants/path' import { usePagination } from '@/shared/hooks/custom/use-pagination' -import { useMSWStore } from '@/shared/stores/msw' +import { StrategiesModel } from '@/shared/types/strategy-data' import Pagination from '@/shared/ui/pagination' -import useGetStrategiesData from '../../_hooks/query/use-get-strategies-data' +import usePostStrategies from '../../_hooks/query/use-post-strategies' +import useSearchingItemStore from '../search-bar/_store/use-searching-item-store' import styles from './styles.module.scss' const cx = classNames.bind(styles) const StrategyList = () => { - const isReady = useMSWStore((state) => state.isReady) const { size, page, handlePageChange } = usePagination({ basePath: PATH.STRATEGIES, pageSize: STRATEGIES_PAGE_COUNT, }) - const { data } = useGetStrategiesData({ isReady, page, size }) - - const strategiesData = data?.strategiesData || [] - const totalPages = data?.totalPages || null + const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const { data } = usePostStrategies({ page, size, searchTerms }) + const strategiesData = data?.content as StrategiesModel[] + const totalPages = data?.totalPages as number return ( <> From e7e26e27e8c8c7945de55b41da3d0f2ab625e2bc Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 13:48:39 +0900 Subject: [PATCH 472/648] =?UTF-8?q?fix:=20querykey=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EA=B5=90=EC=A0=95,=20inactive=20table=EC=97=90=20active=20?= =?UTF-8?q?=EC=86=8D=EC=84=B1=20=EC=88=98=EC=A0=95=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts | 2 +- .../_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts index 6b748d5f..c0a17ff5 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts +++ b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts @@ -8,7 +8,7 @@ const useStocksData = (activateState: ArgType, page: number, size: number) => { const isActive = activateState === 'active' ? true : false return useSuspenseQuery({ - queryKey: ['adminstocks', activateState, page, size], + queryKey: ['adminStocks', activateState, page, size], queryFn: () => getStocks(isActive, page, size), }) } diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx index 34e1c7ed..201a8775 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx @@ -26,7 +26,6 @@ const InactiveTradeManageTable = () => { currentPage={currentPage} setCurrentPage={setCurrentPage} maxPage={data?.result.totalPages} - active domain="종목" /> ) From 37b4e8cd512188500a17bd5f1ca7ff05f75e6687 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 14:11:48 +0900 Subject: [PATCH 473/648] =?UTF-8?q?fix:=20=EC=BF=BC=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A7=81=20size=EA=B0=92=20=EC=B6=94=EA=B0=80=20(#181?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/hooks/custom/use-pagination.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/hooks/custom/use-pagination.ts b/shared/hooks/custom/use-pagination.ts index e4822f61..7e517f3f 100644 --- a/shared/hooks/custom/use-pagination.ts +++ b/shared/hooks/custom/use-pagination.ts @@ -19,13 +19,13 @@ export const usePagination = ({ basePath, pageSize }: Props): UsePaginationRetur const searchParams = useSearchParams() const router = useRouter() const page = parseInt(searchParams?.get('page') || '1') - const size = parseInt(searchParams?.get('size') || '1') + const size = parseInt(searchParams?.get('size') || `${pageSize}`) useEffect(() => { if (!searchParams.size) { - router.push(`${basePath}?page=1&size=${pageSize}`) + router.push(`${basePath}?page=1&size=${size ?? pageSize}`) } - }, [searchParams, router, basePath, pageSize]) + }, [searchParams, router, basePath, pageSize, size]) const handlePageChange = (page: number) => { router.push(`${basePath}?page=${page}&size=${pageSize}`) From 0326ff6eee64a8e192bd1741ccb9f895f2a76758 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 14:45:38 +0900 Subject: [PATCH 474/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=20=EA=B4=80=EB=A6=AC=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/_api/get-presigned-url.ts | 35 ++++++ .../_api/upload-file-with-presigned-url.ts | 0 .../_hooks/use-strategy-icon-post.ts | 6 +- .../_ui/shared/manage-table/index.tsx | 2 - .../stock-manage/_api/get-presigned-url.ts | 17 --- .../_ui/stock/stock-manage/_api/get-stocks.ts | 1 + .../_ui/stock-post-button/index.tsx | 63 ++++++++++ .../_ui/stock-post-button/styles.module.scss | 44 +++++++ .../_ui/stock/stock-manage/index.tsx | 109 +----------------- .../trade-manage/_api/get-presigned-url.ts | 17 --- .../_ui/trade-post-button/index.tsx | 4 +- 11 files changed, 151 insertions(+), 147 deletions(-) create mode 100644 app/admin/strategies/_ui/shared/_api/get-presigned-url.ts rename app/admin/strategies/_ui/{trade/trade-manage => shared}/_api/upload-file-with-presigned-url.ts (100%) rename app/admin/strategies/_ui/{trade/trade-manage => shared}/_hooks/use-strategy-icon-post.ts (93%) delete mode 100644 app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx create mode 100644 app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss delete mode 100644 app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts diff --git a/app/admin/strategies/_ui/shared/_api/get-presigned-url.ts b/app/admin/strategies/_ui/shared/_api/get-presigned-url.ts new file mode 100644 index 00000000..217ca203 --- /dev/null +++ b/app/admin/strategies/_ui/shared/_api/get-presigned-url.ts @@ -0,0 +1,35 @@ +import axios from 'axios' + +import { PresignedUrlResponseModel } from '../../trade/trade-manage/types' +import { DomainType } from '../_hooks/use-strategy-icon-post' + +const getPresignedUrl = async ( + typeName: string, + imageName: string, + imageSize: number, + domain: DomainType +) => { + const body = + domain === 'trade' + ? { + tradeTypeName: typeName, + tradeTypeIconUrl: imageName, + size: imageSize, + } + : { + stockTypeName: typeName, + stockTypeIconUrl: imageName, + size: imageSize, + } + + const res = await axios.post<PresignedUrlResponseModel>( + `/api/admin/strategies/${domain}-type`, + body + ) + + if (!res.data.isSuccess) throw new Error('presigned url Error : ' + res.data.message) + + return res.data.result.presignedUrl +} + +export default getPresignedUrl diff --git a/app/admin/strategies/_ui/trade/trade-manage/_api/upload-file-with-presigned-url.ts b/app/admin/strategies/_ui/shared/_api/upload-file-with-presigned-url.ts similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_api/upload-file-with-presigned-url.ts rename to app/admin/strategies/_ui/shared/_api/upload-file-with-presigned-url.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts b/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts similarity index 93% rename from app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts rename to app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts index cb9fac2c..97883baf 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_hooks/use-strategy-icon-post.ts +++ b/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts @@ -8,9 +8,11 @@ interface FormDataModel { typeName: string } +export type DomainType = 'trade' | 'stock' + const initailFormData = { imageFile: null, typeName: '' } -const useStrategyIconPost = () => { +const useStrategyIconPost = (domain: DomainType) => { const [imagePreview, setImagePreview] = useState('') const [formData, setFormData] = useState<FormDataModel>(initailFormData) @@ -52,7 +54,7 @@ const useStrategyIconPost = () => { try { // console.log('imageFile', imageFile, 'typeName', typeName) - const presignedUrl = await getPresignedUrl(typeName, imageFile.name, imageFile.size) + const presignedUrl = await getPresignedUrl(typeName, imageFile.name, imageFile.size, domain) // console.log('p', presignedUrl) diff --git a/app/admin/strategies/_ui/shared/manage-table/index.tsx b/app/admin/strategies/_ui/shared/manage-table/index.tsx index 59b9d1d0..f7c3dd62 100644 --- a/app/admin/strategies/_ui/shared/manage-table/index.tsx +++ b/app/admin/strategies/_ui/shared/manage-table/index.tsx @@ -31,8 +31,6 @@ const ManageTable = ({ maxPage, setCurrentPage, }: Props) => { - // const [currentPage, setCurrentPage] = useState(1) - const hasData = data?.length > 0 return ( diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts b/app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts deleted file mode 100644 index ad0aaa5d..00000000 --- a/app/admin/strategies/_ui/stock/stock-manage/_api/get-presigned-url.ts +++ /dev/null @@ -1,17 +0,0 @@ -import axios from 'axios' - -import { PresignedUrlResponseModel } from '../types' - -const getPresignedUrl = async (typeName: string, imageName: string, imageSize: number) => { - const res = await axios.post<PresignedUrlResponseModel>('/api/admin/strategies/trade-type', { - tradeTypeName: typeName, - tradeTypeIconUrl: imageName, - size: imageSize, - }) - - if (!res.data.isSuccess) throw new Error('presigned url Error : ' + res.data.message) - - return res.data.result.presignedUrl -} - -export default getPresignedUrl diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts b/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts index d8b93512..17e7e530 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts +++ b/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts @@ -17,6 +17,7 @@ const getStocks = async (activateState: boolean, page: number, size: number) => return res.data } catch (err) { console.log('Error : ' + err) + throw err } } diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx new file mode 100644 index 00000000..36c6594c --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx @@ -0,0 +1,63 @@ +'use client' + +import FileInput from '@/app/admin/strategies/_ui/shared/file-input' +import { RegisterIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import useModal from '@/shared/hooks/custom/use-modal' +import { Button } from '@/shared/ui/button' +import { Input } from '@/shared/ui/input' +import Modal from '@/shared/ui/modal' + +import useStrategyIconPost from '../../../../shared/_hooks/use-strategy-icon-post' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const StockPostButton = () => { + const { isModalOpen, openModal, closeModal } = useModal() + const onPostButtonClick = () => openModal() + const { onFormSubmit, imagePreview, onImageInputChange, onTypeNameInputChange, isSubmitable } = + useStrategyIconPost('stock') + + return ( + <> + <Button + onClick={onPostButtonClick} + variant="filled" + size="small" + className={cx('post-button')} + > + 종목 등록하기 + </Button> + <Modal icon={<RegisterIcon />} message="종목 등록" isOpen={isModalOpen}> + <form onSubmit={onFormSubmit} className={cx('form')}> + <div className={cx('input-container')}> + <div className={cx('input-field')}> + 종목 + <Input + placeholder="종목명을 입력해주세요." + className={cx('input')} + onChange={onTypeNameInputChange} + /> + </div> + <div className={cx('input-field')}> + 아이콘 + <FileInput preview={imagePreview} accept="image/*" onChange={onImageInputChange} /> + </div> + </div> + <div className={cx('button-group')}> + <Button onClick={closeModal} type="button" className={cx('button')}> + 취소 + </Button> + <Button disabled={!isSubmitable} variant="filled" className={cx('button')}> + 등록 + </Button> + </div> + </form> + </Modal> + </> + ) +} + +export default StockPostButton diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss new file mode 100644 index 00000000..2817e085 --- /dev/null +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss @@ -0,0 +1,44 @@ +.post-button { + position: absolute; + top: -144px; + right: 0; + padding: 12px 24px; +} + +.form { + width: 236px; //이거 고정값 맞나?... + margin-top: 24px; +} + +.input-container { + margin-top: 25px; + margin-bottom: 88px; + display: flex; + flex-direction: column; + gap: 18px; +} + +.input-field { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + + .input { + width: 160px; + height: 40px; + } +} + +.button-group { + width: 216px; + margin: 0 auto; + display: flex; + justify-content: space-between; +} + +.button { + width: 90px; + height: 45px; +} diff --git a/app/admin/strategies/_ui/stock/stock-manage/index.tsx b/app/admin/strategies/_ui/stock/stock-manage/index.tsx index b2b1f0d0..7e771db6 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/index.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/index.tsx @@ -1,124 +1,19 @@ -import { useState } from 'react' - -import axios from 'axios' import classNames from 'classnames/bind' -import useModal from '@/shared/hooks/custom/use-modal' - import ActiveStockManageTable from './_ui/active-stock-manage-table' import InactiveStockManageTable from './_ui/inactive-stock-manage-table' +import StockPostButton from './_ui/stock-post-button' import styles from './styles.module.scss' const cx = classNames.bind(styles) const StockManage = () => { - const { isModalOpen, openModal, closeModal } = useModal() - const onClick = () => openModal() - - const [imagePreview, setImagePreview] = useState<string | null>(null) - const [imageFile, setImageFile] = useState<File | null>(null) - - const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => { - const file = event.target.files?.[0] - - if (file) { - if (!file.type.startsWith('image/')) { - alert('Please upload a valid image file.') - return - } - - setImageFile(file) - - const reader = new FileReader() - reader.onload = (e) => { - setImagePreview(e.target?.result as string) - } - reader.readAsDataURL(file) - } else { - setImagePreview(null) - } - } - - const onSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!imageFile) { - alert('이미지를 선택하세요!') - return - } - - try { - console.log('f', imageFile) - - const presignedResponse = await axios.post('/api/admin/strategies/trade-type', { - tradeTypeName: '자동', - tradeTypeIconUrl: imageFile.name, - size: imageFile.size, - }) - - console.log('p', presignedResponse) - - const { presignedUrl } = presignedResponse.data.result - - // Step 2: 이미지 업로드 - await axios.put(presignedUrl, imageFile, { - headers: { - 'Content-Type': imageFile.type, - }, - }) - - alert('이미지가 성공적으로 업로드되었습니다!') - } catch (error) { - console.error('이미지 업로드 실패:', error) - alert('업로드 중 문제가 발생했습니다.') - } - } - return ( <div className={cx('container')}> - {/* <TradePostButton /> */} + <StockPostButton /> <ActiveStockManageTable /> <InactiveStockManageTable /> </div> - // <div> - // <Button onClick={onClick} variant="filled"> - // modal - // </Button> - // {/* <img - // src="https://fastcampus-team3.s3.ap-northeast-2.amazonaws.com/strategy/image/07421e08/icons/sampleTrade-icon2.png" - // alt="img" - // /> */} - // <Modal icon={<RegisterIcon />} message="종목 등록" isOpen={isModalOpen}> - // <form - // onSubmit={onSubmit} - // style={{ marginTop: '24px', display: 'flex', flexDirection: 'column', gap: '12px' }} - // > - // <div style={{ display: 'flex', gap: '12px' }}> - // 종목 : <Input placeholder="종목명을 입력하세요." inputSize="small" /> - // </div> - // <Input type="file" accept="image/*" onChange={handleImageChange} /> - // {imagePreview && ( - // <img - // src={imagePreview} - // alt="Preview" - // style={{ maxWidth: '200px', maxHeight: '200px' }} - // /> - // )} - // <Button.ButtonGroup gap="24px"> - // <Button onClick={closeModal} type="button"> - // 취소 - // </Button> - // <Button variant="filled">등록</Button> - // </Button.ButtonGroup> - // </form> - // </Modal> - // {/* <VerticalTable - // tableHead={['No.', '종목명', '분류', '상태']} - // tableBody={[]} - // countPerPage={8} - // currentPage={1} - // /> - // <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> */} - // </div> ) } diff --git a/app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts b/app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts deleted file mode 100644 index ad0aaa5d..00000000 --- a/app/admin/strategies/_ui/trade/trade-manage/_api/get-presigned-url.ts +++ /dev/null @@ -1,17 +0,0 @@ -import axios from 'axios' - -import { PresignedUrlResponseModel } from '../types' - -const getPresignedUrl = async (typeName: string, imageName: string, imageSize: number) => { - const res = await axios.post<PresignedUrlResponseModel>('/api/admin/strategies/trade-type', { - tradeTypeName: typeName, - tradeTypeIconUrl: imageName, - size: imageSize, - }) - - if (!res.data.isSuccess) throw new Error('presigned url Error : ' + res.data.message) - - return res.data.result.presignedUrl -} - -export default getPresignedUrl diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx index 1462f32f..e04e99eb 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx @@ -9,7 +9,7 @@ import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import Modal from '@/shared/ui/modal' -import useStrategyIconPost from '../../_hooks/use-strategy-icon-post' +import useStrategyIconPost from '../../../../shared/_hooks/use-strategy-icon-post' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -18,7 +18,7 @@ const TradePostButton = () => { const { isModalOpen, openModal, closeModal } = useModal() const onPostButtonClick = () => openModal() const { onFormSubmit, imagePreview, onImageInputChange, onTypeNameInputChange, isSubmitable } = - useStrategyIconPost() + useStrategyIconPost('trade') return ( <> From 0b71b8d56cf402313e8b167c7dff2b69ea04bb83 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 15:11:23 +0900 Subject: [PATCH 475/648] =?UTF-8?q?fix:=20queryFn=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EC=88=98=EC=A0=95=20(#181)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_hooks/query/use-get-strategies-search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts index 77452fe8..77574220 100644 --- a/app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts +++ b/app/(dashboard)/strategies/_hooks/query/use-get-strategies-search.ts @@ -5,7 +5,7 @@ import getStrategiesSearch from '../../_api/get-strategies-search' const useGetStrategiesSearch = () => { return useQuery({ queryKey: ['strategiesSearch'], - queryFn: () => getStrategiesSearch(), + queryFn: getStrategiesSearch, }) } From 67fbecf8a1751d48f9e46a3f18fa4e3832427ba5 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 15:25:32 +0900 Subject: [PATCH 476/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=20=EA=B4=80=EB=A6=AC=20suspense=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shared/manage-table/file-input/index.tsx | 32 +++++++++++++++++++ .../_ui/stock/stock-manage/_api/get-stocks.ts | 2 +- .../_hooks/query/use-stocks-data.ts | 4 +-- .../_ui/active-stock-manage-table.tsx | 10 +++--- .../_ui/inactive-stock-manage-table.tsx | 4 +-- 5 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 app/admin/strategies/_ui/shared/manage-table/file-input/index.tsx diff --git a/app/admin/strategies/_ui/shared/manage-table/file-input/index.tsx b/app/admin/strategies/_ui/shared/manage-table/file-input/index.tsx new file mode 100644 index 00000000..335a833a --- /dev/null +++ b/app/admin/strategies/_ui/shared/manage-table/file-input/index.tsx @@ -0,0 +1,32 @@ +'use client' + +import { ComponentPropsWithoutRef } from 'react' + +import { FileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props extends ComponentPropsWithoutRef<'input'> { + placeholder?: string + onSearchIconClick?: () => void +} + +const FileInput = ({ placeholder = '', value, onChange, ...props }: Props) => { + return ( + <div className={cx('search-input-container')}> + <input + value={value} + onChange={onChange} + placeholder={placeholder} + className={cx('search-input')} + {...props} + /> + <FileIcon className={cx('search-icon')} /> + </div> + ) +} + +export default FileInput diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts b/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts index 17e7e530..5da57c6c 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts +++ b/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts @@ -14,7 +14,7 @@ const getStocks = async (activateState: boolean, page: number, size: number) => if (!res.data.isSuccess) throw new Error(res.data.message) - return res.data + return res.data.result } catch (err) { console.log('Error : ' + err) throw err diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts index c0a17ff5..1922a706 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts +++ b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts @@ -1,4 +1,4 @@ -import { useSuspenseQuery } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' import getStocks from '../../_api/get-stocks' @@ -7,7 +7,7 @@ type ArgType = 'active' | 'inactive' const useStocksData = (activateState: ArgType, page: number, size: number) => { const isActive = activateState === 'active' ? true : false - return useSuspenseQuery({ + return useQuery({ queryKey: ['adminStocks', activateState, page, size], queryFn: () => getStocks(isActive, page, size), }) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx index bc5dd51d..d7e2ea03 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -13,25 +13,27 @@ const TABLE_BODY_SIZE = 10 const ActiveStockManageTable = () => { const [currentPage, setCurrentPage] = useState(1) - const { data } = useStocksData('active', currentPage, TABLE_BODY_SIZE) + const { isLoading, data } = useStocksData('active', currentPage, TABLE_BODY_SIZE) const tableData = - data?.result?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ + data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, ]) ?? [] + if (isLoading) return <ManageTable.Skeleton domain="종목" /> + return ( <ManageTable data={tableData} size={TABLE_BODY_SIZE} currentPage={currentPage} setCurrentPage={setCurrentPage} - maxPage={data?.result.totalPages} + maxPage={data?.totalPages} active domain="종목" /> ) } -export default withSuspense(ActiveStockManageTable, <ManageTable.Skeleton domain="종목" />) +export default ActiveStockManageTable diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx index 201a8775..66e614d1 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx @@ -13,7 +13,7 @@ const InactiveTradeManageTable = () => { const { data } = useStocksData('inactive', currentPage, TABLE_BODY_SIZE) const tableData = - data?.result?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ + data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, <StockActiveStateToggleButton stockTypeId={stockTypeId} key={stockTypeId} />, @@ -25,7 +25,7 @@ const InactiveTradeManageTable = () => { size={TABLE_BODY_SIZE} currentPage={currentPage} setCurrentPage={setCurrentPage} - maxPage={data?.result.totalPages} + maxPage={data?.totalPages} domain="종목" /> ) From 5fac0728885d27a16e9b9b33d461f51a1ffd7b93 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 2 Dec 2024 15:29:34 +0900 Subject: [PATCH 477/648] =?UTF-8?q?feat:=20api=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EB=B3=B4=ED=98=B8=20=EB=A1=9C=EC=A7=81=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/middleware.ts b/middleware.ts index 1f818883..3575db4e 100644 --- a/middleware.ts +++ b/middleware.ts @@ -8,6 +8,14 @@ import { PATH } from '@/shared/constants/path' export const middleware = (request: NextRequest) => { const path = request.nextUrl.pathname + //TODO: api 요청 보호 + // if (path.startsWith('/api/')) { + // const authHeader = request.headers.get('access-token') + // if (!authHeader) { + // return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + // } + // } + if (path.startsWith(PATH.SIGN_UP)) { const response = handleSignupMiddleware(request) if (response) return response @@ -17,5 +25,5 @@ export const middleware = (request: NextRequest) => { } export const config = { - matcher: ['/signup/:path*'], + matcher: ['/api/:path*', '/signup/:path*'], } From ebbaaa5e24f42035add560abcec335f2a8003e20 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 2 Dec 2024 15:30:28 +0900 Subject: [PATCH 478/648] =?UTF-8?q?feat:=20auth-provider=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20msw=20initializer=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 6 ++-- shared/providers/auth-provider.tsx | 49 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 shared/providers/auth-provider.tsx diff --git a/app/layout.tsx b/app/layout.tsx index f1411d18..8e9e0247 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from 'next' -import { MSWInitializer, QueryProvider } from '@/shared/providers' +import { QueryProvider } from '@/shared/providers' +import { AuthProvider } from '@/shared/providers/auth-provider' import '@/shared/styles/global.scss' import { pretendard } from './fonts' @@ -15,8 +16,7 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { <html lang="ko" className={pretendard.variable}> <body> <QueryProvider> - <MSWInitializer /> - {children} + <AuthProvider>{children}</AuthProvider> <div id="modal-root"></div> </QueryProvider> </body> diff --git a/shared/providers/auth-provider.tsx b/shared/providers/auth-provider.tsx new file mode 100644 index 00000000..42ee7642 --- /dev/null +++ b/shared/providers/auth-provider.tsx @@ -0,0 +1,49 @@ +'use client' + +import { ReactNode, createContext, useEffect } from 'react' + +import { usePathname, useRouter } from 'next/navigation' + +import { PATH } from '@/shared/constants/path' +import { getAccessToken } from '@/shared/lib/auth-tokens' + +interface AuthProviderProps { + children: ReactNode +} + +const AuthContext = createContext<null>(null) + +export const AuthProvider = ({ children }: AuthProviderProps) => { + const pathname = usePathname() + const router = useRouter() + + const authRequiredPatterns = ['/my/', `${PATH.ADMIN}`, `${PATH.TRADERS}`, `${PATH.STRATEGIES}/`] + + const nonAuthPages = [ + PATH.SIGN_IN, + PATH.SIGN_UP, + PATH.SIGN_UP_USER_TYPE, + PATH.SIGN_UP_TERMS_OF_USE, + PATH.SIGN_UP_INFORMATION, + PATH.SIGN_UP_COMPLETE, + ] + + useEffect(() => { + const accessToken = getAccessToken() + + const isAuthRequired = authRequiredPatterns.some((pattern) => pathname.startsWith(pattern)) + const isNonAuthPage = nonAuthPages.some((page) => pathname.startsWith(page)) + + if (isAuthRequired && !accessToken) { + router.replace(`${PATH.SIGN_IN}?returnUrl=${pathname}`) + return + } + + if (isNonAuthPage && accessToken) { + router.replace(PATH.STRATEGIES) + return + } + }, [pathname]) + + return <AuthContext.Provider value={null}>{children}</AuthContext.Provider> +} From 0b7b73fc15d2377e2fb05a4d6f14518788eb4b1f Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 2 Dec 2024 15:31:20 +0900 Subject: [PATCH 479/648] =?UTF-8?q?feat:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20msw=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C=20(#1?= =?UTF-8?q?74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mocks/handlers.ts | 2 - mocks/handlers/auth.ts | 94 ----------------------------------------- mocks/handlers/index.ts | 1 - 3 files changed, 97 deletions(-) delete mode 100644 mocks/handlers/auth.ts diff --git a/mocks/handlers.ts b/mocks/handlers.ts index e326cd80..6e106498 100644 --- a/mocks/handlers.ts +++ b/mocks/handlers.ts @@ -1,5 +1,4 @@ import { - authHandlers, postHandlers, strategiesHandlers, strategyDetailsHandlers, @@ -9,7 +8,6 @@ import { const handlers = [ ...userHandlers, ...postHandlers, - ...authHandlers, ...strategyDetailsHandlers, ...strategiesHandlers, ] diff --git a/mocks/handlers/auth.ts b/mocks/handlers/auth.ts deleted file mode 100644 index 6d352241..00000000 --- a/mocks/handlers/auth.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { HttpResponse, http } from 'msw' - -import { ERROR_MESSAGES } from '@/shared/constants/error-messages' -import { LoginFormDataModel, TokenPayloadModel, UserModel, isAdmin } from '@/shared/types/auth' - -const MOCK_USERS: Record<string, UserModel> = { - 'test@example.com': { - id: '1', - email: 'test@example.com', - name: '김다은', - nickname: 'devdeun', - role: 'TRADER', - createdAt: '2024-01-01T00:00:00Z', - imageUrl: 'https://randomuser.me/api', - }, - 'admin@example.com': { - id: '2', - email: 'admin@example.com', - name: '권혁준', - nickname: 'redhero', - role: 'TRADER_ADMIN', - createdAt: '2024-01-01T00:00:00Z', - imageUrl: 'https://randomuser.me/api', - }, -} - -const createToken = (user: UserModel, isAdmin: boolean) => { - const now = Math.floor(Date.now() / 1000) - const exp = now + (isAdmin ? 1800 : 7 * 24 * 60 * 60) - const payload = { user, exp, iat: now } - return `mock_${btoa(encodeURIComponent(JSON.stringify(payload)))}` -} - -const decodeToken = (token: string) => { - const payload = token.replace('mock_', '') - return JSON.parse(decodeURIComponent(atob(payload))) as TokenPayloadModel -} - -export const authHandlers = [ - http.post('/api/users/login', async ({ request }) => { - const { email, password } = (await request.json()) as LoginFormDataModel - const user = MOCK_USERS[email] - - if (user && password === 'password123') { - const accessToken = createToken(user, isAdmin(user)) - const refreshToken = createToken(user, isAdmin(user)) - - return HttpResponse.json({ - isSuccess: true, - message: '로그인 성공', - data: { accessToken, refreshToken, user }, - }) - } - - return HttpResponse.json( - { - isSuccess: false, - message: ERROR_MESSAGES.AUTH.INVALID_CREDENTIALS, - code: 'INVALID_CREDENTIALS', - }, - { status: 400 } - ) - }), - - http.post('/api/users/reissue/refreshtoken', async ({ request }) => { - const authHeader = request.headers.get('Authorization') - const refreshToken = authHeader?.replace('Bearer ', '') - - if (!refreshToken) { - return HttpResponse.json( - { isSuccess: false, message: ERROR_MESSAGES.AUTH.INVALID_TOKEN }, - { status: 401 } - ) - } - - try { - const decoded = decodeToken(refreshToken) - - const accessToken = createToken(decoded.user, isAdmin(decoded.user)) - const newRefreshToken = createToken(decoded.user, isAdmin(decoded.user)) - - return HttpResponse.json({ - isSuccess: true, - message: '토큰 갱신 성공', - data: { accessToken, refreshToken: newRefreshToken }, - }) - } catch { - return HttpResponse.json( - { isSuccess: false, message: ERROR_MESSAGES.AUTH.REFRESH_FAILED }, - { status: 401 } - ) - } - }), -] diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts index ce66c0ff..66d9626f 100644 --- a/mocks/handlers/index.ts +++ b/mocks/handlers/index.ts @@ -3,4 +3,3 @@ export { userHandlers } from './user' export { strategyDetailsHandlers } from './strategy-details' export { strategiesHandlers } from './strategies' -export { authHandlers } from './auth' From f45b6cb11c62b58db0c22b49bbc05fd0e31afabf Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 2 Dec 2024 15:31:41 +0900 Subject: [PATCH 480/648] =?UTF-8?q?feat:=20=EB=8B=B5=EB=B3=80=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=20=EC=A1=B0=EA=B1=B4=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/questions/[questionId]/_ui/question-container/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 5b0b8e8e..95fe8264 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -50,7 +50,7 @@ const QuestionContainer = () => { createdAt="2024-11-03T15:00:00" /> ) : ( - <>{!isTrader && <p className={cx('empty-message')}>아직 답변이 없습니다</p>}</> + <>{!isActiveAnswer && <p className={cx('empty-message')}>아직 답변이 없습니다</p>}</> )} {isActiveAnswer ? ( From 397e56dec3efa5e9a78cbe6b155cfdc04350e89b Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 2 Dec 2024 15:32:09 +0900 Subject: [PATCH 481/648] =?UTF-8?q?feat:=20=ED=97=A4=EB=8D=94=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/layout.tsx | 6 ++--- shared/ui/header/_ui/header-links/index.tsx | 28 +++++++++++++++------ shared/ui/header/logo-header/index.tsx | 5 ++-- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/(landing)/layout.tsx b/app/(landing)/layout.tsx index 2668bf4c..31af9c37 100644 --- a/app/(landing)/layout.tsx +++ b/app/(landing)/layout.tsx @@ -2,6 +2,7 @@ import { usePathname } from 'next/navigation' +import { useAuthStore } from '@/shared/stores/use-auth-store' import Footer from '@/shared/ui/footer' import LogoHeader from '@/shared/ui/header/logo-header' @@ -11,12 +12,11 @@ interface Props { const LandingLayout = ({ children }: Props) => { const pathname = usePathname() - + const isAuthenticated = useAuthStore((state) => state.isAuthenticated) const hasFooter = !pathname.includes('/signin') && !pathname.includes('/signup') - return ( <> - <LogoHeader hasText={true} hasLinks={true} /> + <LogoHeader hasText={true} hasLinks={true} isLoggedIn={isAuthenticated ? true : false} /> <main className="landing-main">{children}</main> {hasFooter && <Footer />} </> diff --git a/shared/ui/header/_ui/header-links/index.tsx b/shared/ui/header/_ui/header-links/index.tsx index 1757cebf..2098605a 100644 --- a/shared/ui/header/_ui/header-links/index.tsx +++ b/shared/ui/header/_ui/header-links/index.tsx @@ -1,19 +1,33 @@ import { PATH } from '@/shared/constants/path' +import { useAuth } from '@/shared/hooks/custom/use-auth' import { Button } from '@/shared/ui/button' import { LinkButton } from '@/shared/ui/link-button' -const HeaderLinks = () => { +interface Props { + isLoggedIn?: boolean +} + +const HeaderLinks = ({ isLoggedIn }: Props) => { + const { logout } = useAuth() return ( <Button.ButtonGroup gap="24px"> <LinkButton href={PATH.STRATEGIES} size="small"> 대시보드 </LinkButton> - <LinkButton href={PATH.SIGN_IN} size="small"> - 로그인 - </LinkButton> - <LinkButton href={PATH.SIGN_UP_USER_TYPE} size="small" variant="filled"> - 회원가입 - </LinkButton> + {!isLoggedIn && ( + <LinkButton href={PATH.SIGN_IN} size="small"> + 로그인 + </LinkButton> + )} + {isLoggedIn ? ( + <Button onClick={logout} size="small" variant="filled"> + 로그아웃 + </Button> + ) : ( + <LinkButton href={PATH.SIGN_UP_USER_TYPE} size="small" variant="filled"> + 회원가입 + </LinkButton> + )} </Button.ButtonGroup> ) } diff --git a/shared/ui/header/logo-header/index.tsx b/shared/ui/header/logo-header/index.tsx index 94a23ebc..8ac8b42b 100644 --- a/shared/ui/header/logo-header/index.tsx +++ b/shared/ui/header/logo-header/index.tsx @@ -12,13 +12,14 @@ const headerStyles = { interface Props { hasLinks?: boolean hasText?: boolean + isLoggedIn?: boolean } -const LogoHeader = ({ hasLinks = false, hasText = false }: Props) => { +const LogoHeader = ({ hasLinks = false, hasText = false, isLoggedIn = false }: Props) => { return ( <Header Left={<Logo hasText={hasText} />} - Right={hasLinks && <HeaderLinks />} + Right={hasLinks && <HeaderLinks isLoggedIn={isLoggedIn} />} styles={headerStyles} /> ) From eff119b6de20d6bc2d2d41728d99fd3bc2cf18b0 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 2 Dec 2024 15:35:15 +0900 Subject: [PATCH 482/648] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EA=B4=80=EB=A0=A8=20api?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B4=80=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/auth.ts | 14 +++- shared/api/axios.ts | 25 ++---- shared/constants/auth.ts | 12 ++- shared/hooks/custom/use-auth.ts | 122 +++++++++++------------------ shared/hooks/query/auth-queries.ts | 81 ++++++++++++++++--- shared/lib/auth-tokens.ts | 20 ++--- shared/types/auth.ts | 31 ++++---- shared/utils/token-utils.ts | 40 ++++------ 8 files changed, 180 insertions(+), 165 deletions(-) diff --git a/shared/api/auth.ts b/shared/api/auth.ts index e39c85be..7b3bbe12 100644 --- a/shared/api/auth.ts +++ b/shared/api/auth.ts @@ -3,6 +3,16 @@ import type { LoginFormDataModel, LoginResponseType } from '@/shared/types/auth' import axiosInstance from './axios' export const login = async (credentials: LoginFormDataModel): Promise<LoginResponseType> => { - const response = await axiosInstance.post('/api/users/login', credentials) - return response.data + const response = await axiosInstance.post('/login', credentials) + return response +} + +export const refreshAccessToken = async (): Promise<LoginResponseType> => { + const response = await axiosInstance.post('/api/users/reissue/refreshtoken') + return response +} + +export const logout = async () => { + const response = await axiosInstance.post('/api/users/logout') + return response } diff --git a/shared/api/axios.ts b/shared/api/axios.ts index d63d8326..e9e92ba3 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -3,23 +3,12 @@ import { AxiosError, InternalAxiosRequestConfig } from 'axios' import { getAccessToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' -import { getUserFromToken, isTokenExpired, refreshToken } from '@/shared/utils/token-utils' - -import { isAdmin } from '../types/auth' +import { isTokenExpired, refreshToken } from '@/shared/utils/token-utils' export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_HOST, - }) - - instance.interceptors.request.use((config) => { - if ( - config.url?.includes('/api/users/login') || - config.url?.includes('/api/users/reissue/refreshtoken') - ) { - config.baseURL = '' - } - return config + withCredentials: true, }) if (options.withInterceptors && typeof window !== 'undefined') { @@ -33,9 +22,7 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} } try { - const user = getUserFromToken(accessToken) - - if (isAdmin(user) && isTokenExpired(accessToken)) { + if (isTokenExpired(accessToken)) { useAuthStore.getState().setAuthState({ isAuthenticated: false, user: null, @@ -45,9 +32,9 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} return config } - config.headers.Authorization = `Bearer ${accessToken}` + config.headers['access-token'] = `Bearer ${accessToken}` } catch (err) { - console.error('토큰 디코딩 실패:', err) + console.error('토큰 검증 실패:', err) useAuthStore.getState().setAuthState({ isAuthenticated: false, user: null, @@ -77,7 +64,7 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} try { const newToken = await refreshToken() if (newToken) { - originalRequest.headers.Authorization = `Bearer ${newToken}` + originalRequest.headers['access-token'] = `Bearer ${newToken}` return instance(originalRequest) } } catch (refreshError) { diff --git a/shared/constants/auth.ts b/shared/constants/auth.ts index 4fe8b0fe..3f0f3709 100644 --- a/shared/constants/auth.ts +++ b/shared/constants/auth.ts @@ -1,4 +1,12 @@ +export const STORAGE_PREFIX = 'investment_platform_' +export const STORAGE_KEYS = { + ACCESS_TOKEN: `${STORAGE_PREFIX}access_token`, + REFRESH_TOKEN: `${STORAGE_PREFIX}refresh_token`, + USER: `${STORAGE_PREFIX}user`, + SESSION: `${STORAGE_PREFIX}session`, +} as const + export const AUTH_TIME = { - TOKEN_CHECK_INTERVAL: 10 * 60 * 1000, // 10분 - ADMIN_EXPIRY_WARNING: 10 * 60 * 1000, // 10분 + TOKEN_CHECK_INTERVAL: 60000, // 1분 + ADMIN_EXPIRY_WARNING: 300000, // 5분 } as const diff --git a/shared/hooks/custom/use-auth.ts b/shared/hooks/custom/use-auth.ts index 624e20e3..73c0a100 100644 --- a/shared/hooks/custom/use-auth.ts +++ b/shared/hooks/custom/use-auth.ts @@ -1,125 +1,97 @@ import { useEffect } from 'react' -import { useRouter } from 'next/navigation' - -import { AUTH_TIME } from '@/shared/constants/auth' -import { PATH } from '@/shared/constants/path' -import { - getAccessToken, - getRefreshToken, - removeAccessToken, - removeRefreshToken, -} from '@/shared/lib/auth-tokens' +import { AUTH_TIME, STORAGE_KEYS } from '@/shared/constants/auth' +import { useLogoutMutation } from '@/shared/hooks/query/auth-queries' +import { getAccessToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' import { TokenStatusModel, isAdmin } from '@/shared/types/auth' -import { getTimeUntilExpiry, getUserFromToken, isTokenExpired } from '@/shared/utils/token-utils' +import { getTimeUntilExpiry, isTokenExpired } from '@/shared/utils/token-utils' export const useAuth = () => { - const router = useRouter() - const { user, isKeepLoggedIn } = useAuthStore() - - const logout = () => { - try { - removeAccessToken() - removeRefreshToken() - sessionStorage.removeItem('sessionToken') - - useAuthStore.getState().setAuthState({ - isAuthenticated: false, - user: null, - isKeepLoggedIn: false, - isLoggedOut: true, - }) - - router.replace(PATH.SIGN_IN) - } catch (err) { - console.error('로그아웃 실패:', err) - } - } + const { isKeepLoggedIn, isLoggedOut } = useAuthStore() + const { mutate: logout } = useLogoutMutation() const checkTokenStatus = (): TokenStatusModel | null => { const accessToken = getAccessToken() - const refreshToken = getRefreshToken() - - if (!accessToken || !refreshToken) { - logout() - return null - } + const userStr = localStorage.getItem(STORAGE_KEYS.USER) - const tokenUser = getUserFromToken(accessToken) - if (!tokenUser) { + if (!accessToken || !userStr) { logout() return null } - const isExpired = isTokenExpired(accessToken) - const timeUntilExpiry = getTimeUntilExpiry(accessToken) + try { + const user = JSON.parse(userStr) + const isExpired = isTokenExpired(accessToken) + const timeUntilExpiry = getTimeUntilExpiry(accessToken) - if (isAdmin(tokenUser)) { if (isExpired) { logout() return null } + + if (!isAdmin(user)) { + const sessionToken = sessionStorage.getItem(STORAGE_KEYS.SESSION) + if (!isKeepLoggedIn && !sessionToken) { + logout() + return null + } + } + return { isValid: !isExpired, timeUntilExpiry, isNearExpiry: timeUntilExpiry < AUTH_TIME.ADMIN_EXPIRY_WARNING, } - } - - const sessionToken = sessionStorage.getItem('sessionToken') - if (!isKeepLoggedIn && !sessionToken) { + } catch (error) { + console.error('Failed to parse user data:', error) logout() return null } - - return { - isValid: !isExpired, - timeUntilExpiry, - isNearExpiry: false, - } } useEffect(() => { const initializeAuth = () => { const accessToken = getAccessToken() - const refreshToken = getRefreshToken() + const userStr = localStorage.getItem(STORAGE_KEYS.USER) + if (isLoggedOut !== false) return - if (!accessToken || !refreshToken) return - - const tokenUser = getUserFromToken(accessToken) - if (!tokenUser) return - - if (isAdmin(tokenUser) && isTokenExpired(accessToken)) { + if (!accessToken || !userStr) { logout() return } - if (!isAdmin(tokenUser)) { - const sessionToken = sessionStorage.getItem('sessionToken') - if (!isKeepLoggedIn && !sessionToken) { + try { + const user = JSON.parse(userStr) + + if (isTokenExpired(accessToken)) { logout() return } - } - if (!user || user.id !== tokenUser.id) { + if (!isAdmin(user)) { + const sessionToken = sessionStorage.getItem(STORAGE_KEYS.SESSION) + if (!isKeepLoggedIn && !sessionToken) { + logout() + return + } + } + useAuthStore.getState().setAuthState({ isAuthenticated: true, - user: tokenUser, + user, isLoggedOut: false, }) + } catch (error) { + console.error('Failed to parse user data:', error) + logout() } } - // initializeAuth() - - // const interval = setInterval(checkTokenStatus, AUTH_TIME.TOKEN_CHECK_INTERVAL) - // return () => clearInterval(interval) - }, [isKeepLoggedIn, router, user]) + initializeAuth() + const interval = setInterval(checkTokenStatus, AUTH_TIME.TOKEN_CHECK_INTERVAL) + return () => clearInterval(interval) + }, [isKeepLoggedIn, logout]) - return { - logout, - checkTokenStatus, - } + return { logout, checkTokenStatus } } diff --git a/shared/hooks/query/auth-queries.ts b/shared/hooks/query/auth-queries.ts index 169a6c60..b5740d21 100644 --- a/shared/hooks/query/auth-queries.ts +++ b/shared/hooks/query/auth-queries.ts @@ -1,27 +1,84 @@ +import { useRouter } from 'next/navigation' + import { useMutation } from '@tanstack/react-query' -import { login } from '@/shared/api/auth' -import { setAccessToken, setRefreshToken } from '@/shared/lib/auth-tokens' +import { login, logout } from '@/shared/api/auth' +import axiosInstance from '@/shared/api/axios' +import { STORAGE_KEYS } from '@/shared/constants/auth' +import { PATH } from '@/shared/constants/path' +import { setAccessToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' -import { LoginResponseType, isAdmin } from '@/shared/types/auth' +import { isAdmin } from '@/shared/types/auth' +import { getEmailFromToken } from '@/shared/utils/token-utils' export const useLoginMutation = () => { return useMutation({ mutationFn: login, - onSuccess: (response: LoginResponseType) => { - const { user, accessToken, refreshToken } = response.data - setAccessToken(accessToken) - setRefreshToken(refreshToken) + onSuccess: async (response) => { + const accessToken = response.headers['access-token']?.replace('Bearer ', '') + if (accessToken) { + setAccessToken(accessToken) + + try { + const userEmail = getEmailFromToken(accessToken) + if (!userEmail) throw new Error('Invalid token') + + const userResponse = await axiosInstance.get( + `/api/users/mypage/profile?email=${userEmail}` + ) + if (userResponse.data.isSuccess) { + localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(userResponse.data.result)) + + if (!isAdmin(userResponse.data.result)) { + sessionStorage.setItem(STORAGE_KEYS.SESSION, 'true') + } - if (!isAdmin(user)) { - sessionStorage.setItem('sessionToken', 'true') + useAuthStore.getState().setAuthState({ + isAuthenticated: true, + user: userResponse.data.result, + isLoggedOut: false, + }) + } + } catch (error) { + console.error('Failed to fetch user info:', error) + throw error + } } + }, + }) +} + +export const useLogoutMutation = () => { + const router = useRouter() + + return useMutation({ + mutationFn: logout, + onSuccess: () => { + localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + localStorage.removeItem(STORAGE_KEYS.USER) + sessionStorage.removeItem(STORAGE_KEYS.SESSION) + + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + isLoggedOut: true, + }) + + router.replace(PATH.SIGN_IN) + }, + onError: (error) => { + console.error('Logout failed:', error) + localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + localStorage.removeItem(STORAGE_KEYS.USER) + sessionStorage.removeItem(STORAGE_KEYS.SESSION) useAuthStore.getState().setAuthState({ - isAuthenticated: true, - user, - isLoggedOut: false, + isAuthenticated: false, + user: null, + isLoggedOut: true, }) + + router.replace(PATH.SIGN_IN) }, }) } diff --git a/shared/lib/auth-tokens.ts b/shared/lib/auth-tokens.ts index 9f88d0a0..da4859a5 100644 --- a/shared/lib/auth-tokens.ts +++ b/shared/lib/auth-tokens.ts @@ -1,23 +1,13 @@ -export const setAccessToken = (token: string) => { - localStorage.setItem('accessToken', token) -} +import { STORAGE_KEYS } from '../constants/auth' -export const setRefreshToken = (token: string) => { - localStorage.setItem('refreshToken', token) +export const setAccessToken = (token: string) => { + localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token) } export const getAccessToken = () => { - return localStorage.getItem('accessToken') -} - -export const getRefreshToken = () => { - return localStorage.getItem('refreshToken') + return localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN) } export const removeAccessToken = () => { - localStorage.removeItem('accessToken') -} - -export const removeRefreshToken = () => { - localStorage.removeItem('refreshToken') + localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) } diff --git a/shared/types/auth.ts b/shared/types/auth.ts index 6b24d3cf..4b6e549e 100644 --- a/shared/types/auth.ts +++ b/shared/types/auth.ts @@ -1,15 +1,23 @@ -export type UserType = 'TRADER' | 'INVESTOR' +import { AxiosResponse } from 'axios' +export type UserType = 'TRADER' | 'INVESTOR' export type RoleType = UserType | `${UserType}_ADMIN` export interface UserModel { - id: string + userId: number + userName: string email: string - name: string + imageUrl: string nickname: string + phone: string + infoAgreement: boolean role: RoleType - createdAt?: string - imageUrl: string +} + +export interface LoginResponseModel { + isSuccess: boolean + message: string + email: string } export interface LoginFormDataModel { @@ -22,13 +30,6 @@ export interface TokenDataModel { refreshToken: string } -export interface AuthResponseModel { - accessToken: string - refreshToken: string - user: UserModel - keepLoggedIn?: boolean -} - export interface ApiResponseModel<T> { isSuccess: boolean message: string @@ -36,11 +37,13 @@ export interface ApiResponseModel<T> { code?: string } -export type LoginResponseType = ApiResponseModel<AuthResponseModel> +export type LoginResponseType = AxiosResponse<LoginResponseModel> export type TokenResponseType = ApiResponseModel<TokenDataModel> export interface TokenPayloadModel { - user: UserModel + category: string + email: string + role: RoleType exp: number iat: number } diff --git a/shared/utils/token-utils.ts b/shared/utils/token-utils.ts index 29fe3591..3559978b 100644 --- a/shared/utils/token-utils.ts +++ b/shared/utils/token-utils.ts @@ -1,17 +1,14 @@ import { jwtDecode } from 'jwt-decode' -import { getRefreshToken, setAccessToken, setRefreshToken } from '@/shared/lib/auth-tokens' -import type { TokenPayloadModel, TokenResponseType } from '@/shared/types/auth' +import { setAccessToken } from '@/shared/lib/auth-tokens' +import { TokenPayloadModel } from '@/shared/types/auth' -import axiosInstance from '../api/axios' +import { refreshAccessToken } from '../api/auth' let isRefreshing = false let refreshSubscribers: ((token: string) => void)[] = [] export const refreshToken = async (): Promise<string | null> => { - const refreshToken = getRefreshToken() - if (!refreshToken) return null - if (isRefreshing) { return new Promise((resolve) => { refreshSubscribers.push((token) => resolve(token)) @@ -21,25 +18,16 @@ export const refreshToken = async (): Promise<string | null> => { try { isRefreshing = true - const response = await axiosInstance.post<TokenResponseType>( - '/api/users/reissue/refreshtoken', - {}, - { - headers: { - Authorization: `Bearer ${refreshToken}`, - }, - } - ) + const response = await refreshAccessToken() - if (!response.data.data) { + if (!response.data.isSuccess) { return null } - const { accessToken: newAccessToken, refreshToken: newRefreshToken } = response.data.data + const newAccessToken = response.headers['access-token']?.replace('Bearer ', '') - if (newAccessToken && newRefreshToken) { + if (newAccessToken) { setAccessToken(newAccessToken) - setRefreshToken(newRefreshToken) refreshSubscribers.forEach((cb) => cb(newAccessToken)) return newAccessToken } @@ -63,21 +51,21 @@ export const isTokenExpired = (token: string): boolean => { } } -export const getTimeUntilExpiry = (token: string): number => { +export const getEmailFromToken = (token: string | null): string | null => { + if (!token) return null try { const decoded = jwtDecode<TokenPayloadModel>(token) - return decoded.exp * 1000 - Date.now() + return decoded.email } catch { - return 0 + return null } } -export const getUserFromToken = (token: string | null): TokenPayloadModel['user'] | null => { - if (!token) return null +export const getTimeUntilExpiry = (token: string): number => { try { const decoded = jwtDecode<TokenPayloadModel>(token) - return decoded.user + return decoded.exp * 1000 - Date.now() } catch { - return null + return 0 } } From e622073d2ce0b703f6d3b60807626986c040effb Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 2 Dec 2024 15:36:13 +0900 Subject: [PATCH 483/648] =?UTF-8?q?feat:=20rewrite=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20cx=20=EC=82=AC=EC=9A=A9=ED=95=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81,=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 35 +++++++++---------- app/(landing)/signin/styles.module.scss | 24 +++++-------- next.config.mjs | 16 ++++----- shared/constants/path.ts | 4 --- shared/ui/side-navigation/nav-button-item.tsx | 2 +- 5 files changed, 34 insertions(+), 47 deletions(-) diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index 7eb525f8..b67f0236 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -30,7 +30,6 @@ const SignInPage = () => { const setKeepLoggedIn = useAuthStore((state) => state.setKeepLoggedIn) const isKeepLoggedIn = useAuthStore((state) => state.isKeepLoggedIn) - const [formData, setFormData] = useState<LoginFormDataModel>({ email: '', password: '', @@ -65,10 +64,10 @@ const SignInPage = () => { } try { - const { data } = await loginMutation.mutateAsync(formData) + const response = await loginMutation.mutateAsync(formData) - if (!data?.accessToken || !data?.refreshToken || !data?.user) { - setErrors(ERROR_MESSAGES.AUTH.LOGIN_FAILED) + if (!response.data.isSuccess) { + setErrors(response.data.message || ERROR_MESSAGES.AUTH.LOGIN_FAILED) return } @@ -115,11 +114,11 @@ const SignInPage = () => { const isFormDisabled = isSubmitting || loginMutation.isPending return ( - <div className={styles.container}> - <div className={styles.loginBox}> - <h1 className={styles.title}>로그인</h1> - <form onSubmit={handleSubmit} className={styles.form}> - <div className={styles.inputGroup}> + <div className={cx('container')}> + <div className={cx('loginBox')}> + <h1 className={cx('title')}>로그인</h1> + <form onSubmit={handleSubmit} className={cx('form')}> + <div className={cx('inputGroup')}> <label htmlFor="email">이메일</label> <Input type="email" @@ -129,13 +128,13 @@ const SignInPage = () => { value={formData.email} onChange={handleInputChange} placeholder="이메일을 입력하세요." - className={styles.input} + className={cx('input')} disabled={isFormDisabled} required autoComplete="email" /> </div> - <div className={styles.inputGroup}> + <div className={cx('inputGroup')}> <label htmlFor="password">비밀번호</label> <Input type="password" @@ -145,7 +144,7 @@ const SignInPage = () => { value={formData.password} onChange={handleInputChange} placeholder="비밀번호를 입력하세요." - className={styles.input} + className={cx('input')} disabled={isFormDisabled} required autoComplete="current-password" @@ -161,8 +160,8 @@ const SignInPage = () => { {errors} </p> </div> - <div className={styles.options}> - <label className={styles.remember}> + <div className={cx('options')}> + <label className={cx('remember')}> <Checkbox isChecked={isKeepLoggedIn} onChange={handleKeepLoggedInChange} @@ -170,10 +169,10 @@ const SignInPage = () => { textColor="gray600" /> </label> - <div className={styles.links}> - <Link href={PATH.FIND_ID}>아이디 찾기</Link> - <span className={styles.divider}>|</span> - <Link href={PATH.FIND_PASSWORD}>비밀번호 찾기</Link> + <div className={cx('find-buttons')}> + <Link href={'/'}>아이디 찾기</Link> + <span className={cx('divider')}>|</span> + <Link href={'/'}>비밀번호 찾기</Link> </div> </div> <Button type="submit" size="large" variant="filled" disabled={isFormDisabled}> diff --git a/app/(landing)/signin/styles.module.scss b/app/(landing)/signin/styles.module.scss index 9be85e8b..6d404244 100644 --- a/app/(landing)/signin/styles.module.scss +++ b/app/(landing)/signin/styles.module.scss @@ -66,6 +66,14 @@ align-items: center; margin-bottom: 30px; margin-top: -25px; + + .find-buttons { + display: flex; + align-items: center; + gap: 8px; + color: $color-gray-600; + @include typo-c1; + } } .remember { @@ -75,22 +83,6 @@ gap: 8px; } -.links { - display: flex; - align-items: center; - gap: 8px; - - a { - color: $color-gray-600; - @include typo-c1; - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } -} - .divider { color: #ddd; } diff --git a/next.config.mjs b/next.config.mjs index 2ac2cd7d..97265100 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -11,14 +11,6 @@ const nextConfig = { }, async rewrites() { return [ - // { - // source: '/api/users/reissue/refreshtoken', - // destination: '/api/users/reissue/refreshtoken', - // }, - // { - // source: '/api/users/login', - // destination: '/api/users/login', - // }, { source: '/api/strategies/:path*', destination: 'http://15.164.90.102:8081/api/strategies/:path*', @@ -27,6 +19,14 @@ const nextConfig = { source: '/api/main/:path*', destination: 'http://15.164.90.102:8081/api/main/:path*', }, + { + source: '/api/users/:path*', + destination: 'http://15.164.90.102:8081/api/users/:path*', + }, + { + source: '/login', + destination: 'http://15.164.90.102:8081/login', + }, ] }, webpack: (config, { isServer }) => { diff --git a/shared/constants/path.ts b/shared/constants/path.ts index 3e244fa3..2e5ec512 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -30,8 +30,4 @@ export const PATH = { ADMIN_NOTICES: '/admin/notices', ADMIN_STRATEGIES: '/admin/strategies', ADMIN_QUESTIONS: '/admin/questions', - - // Etc - FIND_ID: '/find-id', - FIND_PASSWORD: '/find-password', } as const diff --git a/shared/ui/side-navigation/nav-button-item.tsx b/shared/ui/side-navigation/nav-button-item.tsx index 3c2ce0a3..cc43e1b9 100644 --- a/shared/ui/side-navigation/nav-button-item.tsx +++ b/shared/ui/side-navigation/nav-button-item.tsx @@ -8,7 +8,7 @@ const cx = classNames.bind(styles) interface Props { icon: React.ElementType - onClick: React.MouseEventHandler<HTMLButtonElement> + onClick: () => void children: React.ReactNode } From 48c1c528b2db855873c3be750c2c5072d82ba869 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 2 Dec 2024 16:14:13 +0900 Subject: [PATCH 484/648] =?UTF-8?q?feat:=20spinner=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/spinner/index.tsx | 15 +++++++++++++++ shared/ui/spinner/styles.module.scss | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 shared/ui/spinner/index.tsx create mode 100644 shared/ui/spinner/styles.module.scss diff --git a/shared/ui/spinner/index.tsx b/shared/ui/spinner/index.tsx new file mode 100644 index 00000000..1d366cc6 --- /dev/null +++ b/shared/ui/spinner/index.tsx @@ -0,0 +1,15 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + className?: string +} + +const Spinner = ({ className }: Props) => { + return <div className={cx('spinner', className)} /> +} + +export default Spinner diff --git a/shared/ui/spinner/styles.module.scss b/shared/ui/spinner/styles.module.scss new file mode 100644 index 00000000..792878bd --- /dev/null +++ b/shared/ui/spinner/styles.module.scss @@ -0,0 +1,17 @@ +.spinner { + width: 32px; + height: 32px; + border: 4px solid $color-gray-200; + border-top: 4px solid $color-orange-500; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} From 6456b95586a27d3097ec7c86e97bed74c848b8de Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Mon, 2 Dec 2024 16:14:54 +0900 Subject: [PATCH 485/648] =?UTF-8?q?feat:=20loading=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/loading.module.scss | 9 +++++++++ app/loading.tsx | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 app/loading.module.scss create mode 100644 app/loading.tsx diff --git a/app/loading.module.scss b/app/loading.module.scss new file mode 100644 index 00000000..e3c1edc5 --- /dev/null +++ b/app/loading.module.scss @@ -0,0 +1,9 @@ +.container { + display: flex; + align-items: center; + justify-content: center; + gap: 16px; + height: 100vh; + @include typo-b1; + color: $color-gray-700; +} diff --git a/app/loading.tsx b/app/loading.tsx new file mode 100644 index 00000000..be3e6582 --- /dev/null +++ b/app/loading.tsx @@ -0,0 +1,20 @@ +'use client' + +import classNames from 'classnames/bind' + +import Spinner from '@/shared/ui/spinner' + +import styles from './loading.module.scss' + +const cx = classNames.bind(styles) + +const LoadingPage = () => { + return ( + <div className={cx('container')}> + <p>Loading...</p> + <Spinner /> + </div> + ) +} + +export default LoadingPage From 860546e5206f5cbe053fee437ca9848d004655ff Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 17:34:48 +0900 Subject: [PATCH 486/648] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EA=B5=AC=EB=8F=85=20=ED=99=95=EC=9D=B8=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#187)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/index.tsx | 6 +-- shared/ui/modal/modal.stories.tsx | 20 ++++++---- shared/ui/modal/signin-check-modal.tsx | 36 +++++++++++++++++ shared/ui/modal/styles.module.scss | 12 ++++-- shared/ui/modal/subscribe-check-modal.tsx | 48 +++++++++++++++++++++++ 5 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 shared/ui/modal/signin-check-modal.tsx create mode 100644 shared/ui/modal/subscribe-check-modal.tsx diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index 628509e1..2589d2a4 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -8,14 +8,14 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - icon?: React.ReactNode + icon?: React.FC<React.SVGProps<SVGSVGElement>> message?: string children?: React.ReactNode isOpen: boolean className?: string } -const Modal = ({ icon, message, children, isOpen = false, className }: Props) => { +const Modal = ({ icon: Icon, message, children, isOpen = false, className }: Props) => { if (!isOpen) return null const modalRoot = document.getElementById('modal-root') @@ -26,7 +26,7 @@ const Modal = ({ icon, message, children, isOpen = false, className }: Props) => <> <div className={cx('overlay')}></div> <div className={cx('modal', className)}> - <div className={cx('icon')}>{icon}</div> + <div className={cx('icon')}>{Icon && <Icon className={cx('icon')} />}</div> <p className={cx('message')}>{message}</p> {children} </div> diff --git a/shared/ui/modal/modal.stories.tsx b/shared/ui/modal/modal.stories.tsx index aebb1ced..970ee44e 100644 --- a/shared/ui/modal/modal.stories.tsx +++ b/shared/ui/modal/modal.stories.tsx @@ -21,7 +21,13 @@ export default meta type StoryType = StoryObj<typeof Modal> -const ModalStory = ({ message, icon }: { message?: string; icon?: React.ReactNode }) => { +const ModalStory = ({ + message, + icon, +}: { + message?: string + icon?: React.FC<React.SVGProps<SVGSVGElement>> +}) => { const [isOpen, setIsOpen] = React.useState(true) React.useEffect(() => { @@ -43,23 +49,21 @@ const ModalStory = ({ message, icon }: { message?: string; icon?: React.ReactNod } export const Default: StoryType = { - render: () => <ModalStory message="기본모달" icon={<ModalAlertIcon />} />, + render: () => <ModalStory message="기본모달" icon={ModalAlertIcon} />, } export const AlertIcon: StoryType = { - render: () => <ModalStory message="이것은 알림아이콘 모달입니다." icon={<ModalAlertIcon />} />, + render: () => <ModalStory message="이것은 알림아이콘 모달입니다." icon={ModalAlertIcon} />, } export const CheckIcon: StoryType = { - render: () => <ModalStory message="이것은 체크아이콘 모달입니다." icon={<ModalCheckIcon />} />, + render: () => <ModalStory message="이것은 체크아이콘 모달입니다." icon={ModalCheckIcon} />, } export const SubscribeIcon: StoryType = { - render: () => ( - <ModalStory message="이것은 구독아이콘 모달입니다." icon={<ModalSubscribeIcon />} /> - ), + render: () => <ModalStory message="이것은 구독아이콘 모달입니다." icon={ModalSubscribeIcon} />, } export const PlusIcon: StoryType = { - render: () => <ModalStory message="이것은 등록아이콘 모달입니다." icon={<RegisterIcon />} />, + render: () => <ModalStory message="이것은 등록아이콘 모달입니다." icon={RegisterIcon} />, } diff --git a/shared/ui/modal/signin-check-modal.tsx b/shared/ui/modal/signin-check-modal.tsx new file mode 100644 index 00000000..34c78aec --- /dev/null +++ b/shared/ui/modal/signin-check-modal.tsx @@ -0,0 +1,36 @@ +'use client' + +import React from 'react' + +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Modal from '.' +import { Button } from '../button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isModalOpen: boolean + closeModal: () => void + onChange?: () => void +} + +const SigninCheckModal = ({ isModalOpen, closeModal, onChange }: Props) => { + return ( + <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> + <span className={cx('message')}> + 로그인이 필요합니다. <br /> 로그인 하시겠습니까? + </span> + <div className={cx('two-button')}> + <Button onClick={closeModal}>아니오</Button> + <Button onClick={onChange} variant="filled" className={cx('button')}> + 예 + </Button> + </div> + </Modal> + ) +} + +export default SigninCheckModal diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index 007089c4..3d510751 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -15,7 +15,7 @@ border: 1px solid $color-gray-200; border-radius: 8px; z-index: zIndex(modal); - padding: 80px 40px; + padding: 40px; width: 360px; display: flex; flex-direction: column; @@ -27,7 +27,6 @@ display: flex; justify-content: center; align-items: center; - margin-bottom: 12px; svg { width: 40px; @@ -37,9 +36,16 @@ .message { @include typo-b1; - font-weight: bold; text-align: center; white-space: pre-line; color: $color-gray-800; margin-bottom: 30px; } + +.two-button { + display: flex; + gap: 10px; + & .button { + width: 90px; + } +} diff --git a/shared/ui/modal/subscribe-check-modal.tsx b/shared/ui/modal/subscribe-check-modal.tsx new file mode 100644 index 00000000..1976a3c5 --- /dev/null +++ b/shared/ui/modal/subscribe-check-modal.tsx @@ -0,0 +1,48 @@ +'use client' + +import React from 'react' + +import { ModalAlertIcon, ModalSubscribeIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Modal from '.' +import { Button } from '../button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isModalOpen: boolean + isSubscribing: boolean + closeModal: () => void + onChange?: () => void +} + +const SubscribeCheckModal = ({ isModalOpen, isSubscribing, closeModal, onChange }: Props) => { + return ( + <Modal isOpen={isModalOpen} icon={isSubscribing ? ModalAlertIcon : ModalSubscribeIcon}> + {isSubscribing ? ( + <> + <span className={cx('message')}>구독을 취소하시겠습니까?</span> + <div className={cx('two-button')}> + <Button onClick={closeModal}>아니오</Button> + <Button onClick={onChange} variant="filled" className={cx('button')}> + 예 + </Button> + </div> + </> + ) : ( + <> + <span className={cx('message')}> + 전략을 구독합니다 <br /> + 구독한 전략은 나의 관심전략 <br /> + 페이지에서 확인 가능합니다. + </span> + <Button onClick={closeModal}>닫기</Button> + </> + )} + </Modal> + ) +} + +export default SubscribeCheckModal From ca6892b4f1ebaf8661a1b83ea962faecceeaa339 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 17:35:22 +0900 Subject: [PATCH 487/648] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20props=20=EC=88=98=EC=A0=95=20(#187)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/information/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(landing)/signup/information/page.tsx b/app/(landing)/signup/information/page.tsx index 30a81eb4..fca24b32 100644 --- a/app/(landing)/signup/information/page.tsx +++ b/app/(landing)/signup/information/page.tsx @@ -304,7 +304,7 @@ const InformationPage = () => { 다음 </Button> </div> - <Modal message="회원가입에 실패했습니다." icon={<ModalAlertIcon />} isOpen={isModalOpen}> + <Modal message="회원가입에 실패했습니다." icon={ModalAlertIcon} isOpen={isModalOpen}> <Button onClick={() => setIsModalOpen(false)}>닫기</Button> </Modal> </> From 2c247cf3e71eab36c9e033c9fd3446b882d2af1e Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 17:35:51 +0900 Subject: [PATCH 488/648] =?UTF-8?q?fix:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=EA=B2=BD=EC=9A=B0=20null=EB=A1=9C=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94=20(#187)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_ui/strategy-list/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index e61ba297..12f33b5c 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -24,7 +24,7 @@ const StrategyList = () => { const searchTerms = useSearchingItemStore((state) => state.searchTerms) const { data } = usePostStrategies({ page, size, searchTerms }) const strategiesData = data?.content as StrategiesModel[] - const totalPages = data?.totalPages as number + const totalPages = (data?.totalPages as number) || null return ( <> From c71d1bea6f1cf69f868869bc816a2b4d871773b6 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Mon, 2 Dec 2024 19:11:18 +0900 Subject: [PATCH 489/648] =?UTF-8?q?feat:=20number=20type=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EB=A1=9C=20=ED=8F=AC=EB=A7=B7=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-side-item/side-item.tsx | 3 ++- app/(dashboard)/_ui/strategies-item/index.tsx | 3 ++- shared/ui/table/statistics/index.tsx | 6 ++++-- shared/ui/table/vertical/index.tsx | 3 ++- shared/utils/format.ts | 14 ++++++++++++++ 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index cf452b20..7a91c142 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -3,6 +3,7 @@ import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import Avatar from '@/shared/ui/avatar' import { LinkButton } from '@/shared/ui/link-button' +import { formatNumber } from '@/shared/utils/format' import { TitleType } from '.' import styles from './styles.module.scss' @@ -34,7 +35,7 @@ const SideItem = ({ title, data, profileImage, isMyStrategy = false }: Props) => )} </> ) : ( - <p>{data}</p> + <p>{typeof data === 'number' ? formatNumber(data) : data}</p> )} </div> </div> diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index f82d4103..8fe9c7e9 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames/bind' import { StrategiesModel } from '@/shared/types/strategy-data' import { Button } from '@/shared/ui/button' +import { formatNumber } from '@/shared/utils/format' import AreaChart from './area-chart' import StrategiesSummary from './strategies-summary' @@ -34,7 +35,7 @@ const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { /> <AreaChart profitRateChartData={data.profitRateChartData} /> <div className={cx('mdd')}> - <p>{data.mdd}</p> + <p>{formatNumber(data.mdd)}</p> </div> <div className={cx('sm-score')}> <p>{data.smScore}</p> diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index 6c422b4f..b0dc9895 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -1,5 +1,7 @@ import classNames from 'classnames/bind' +import { formatNumber } from '@/shared/utils/format' + import { inKoreanData } from './in-korean' import styles from './styles.module.scss' @@ -48,11 +50,11 @@ const StatisticsTable = ({ title, statisticsData }: Props) => { {groupedData.map((row, idx) => ( <tr key={idx}> <td>{row[0]}</td> - <td>{row[1]}</td> + <td>{typeof row[1] === 'number' ? formatNumber(row[1]) : row[1]}</td> {row[2] && ( <> <td>{row[2]}</td> - <td>{row[3]}</td> + <td>{typeof row[3] === 'number' ? formatNumber(row[3]) : row[3]}</td> </> )} </tr> diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 785e61fc..218cf111 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -5,6 +5,7 @@ import classNames from 'classnames/bind' import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-data' import { Button } from '@/shared/ui/button' +import { formatNumber } from '@/shared/utils/format' import sliceArray from '@/shared/utils/slice-array' import styles from './styles.module.scss' @@ -50,7 +51,7 @@ const VerticalTable = ({ {slicedTableBody.map((row) => ( <tr key={Object.values(row)[0]}> {Object.values(row).map((data, idx) => ( - <td key={data + idx}>{data}</td> + <td key={data + idx}>{typeof data === 'number' ? formatNumber(data) : data}</td> ))} {isEditable && ( <td className={cx('button-container')}> diff --git a/shared/utils/format.ts b/shared/utils/format.ts index a6b11d19..5301e583 100644 --- a/shared/utils/format.ts +++ b/shared/utils/format.ts @@ -13,3 +13,17 @@ export const formatDateTime = (dateString: string) => { return `${year}.${month}.${day} ${hours}:${minutes}` } + +export const arrayFormatNumbers = (arrayData: number[]): (string | number)[] | null => { + if (Array.isArray(arrayData)) { + return arrayData.map((data) => formatNumber(data)) + } + return null +} + +export const formatNumber = (data: number): string | number => { + if (data.toFixed(0).toString().length > 3) { + return data.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + } + return data +} From 9c34a604c26941cad6b9045bc928176f6c5702f3 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 21:43:05 +0900 Subject: [PATCH 490/648] =?UTF-8?q?config:=20s3=EC=9D=98=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=EB=A5=BC=20=EB=B0=9B=EC=9D=84=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.mjs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/next.config.mjs b/next.config.mjs index 06ec54d7..2cc08497 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -51,6 +51,16 @@ const nextConfig = { return config }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'fastcampus-team3.s3.ap-northeast-2.amazonaws.com', + port: '', + pathname: '/**', + }, + ], + }, } export default nextConfig From cc510ffd2a1327eddb34fe815653eecd2a40101b Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 21:57:45 +0900 Subject: [PATCH 491/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=20=EA=B4=80=EB=A6=AC=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EC=97=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=81=AC?= =?UTF-8?q?=EA=B8=B0=20=EC=A7=80=EC=A0=95=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stock-manage/_ui/active-stock-manage-table.tsx | 10 ++++++++-- .../stock-manage/_ui/inactive-stock-manage-table.tsx | 10 +++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx index d7e2ea03..7484f3ff 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' -import withSuspense from '@/shared/utils/with-suspense' +import Image from 'next/image' import ManageTable from '../../../shared/manage-table' import useStocksData from '../_hooks/query/use-stocks-data' @@ -17,7 +17,13 @@ const ActiveStockManageTable = () => { const tableData = data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, - <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, + <Image + src={stockTypeIconUrl} + alt={stockTypeName} + width={24} + height={24} + key={stockTypeName} + />, <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, ]) ?? [] diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx index 66e614d1..a96eb468 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx @@ -1,5 +1,7 @@ import { useState } from 'react' +import Image from 'next/image' + import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' @@ -15,7 +17,13 @@ const InactiveTradeManageTable = () => { const tableData = data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, - <img src={stockTypeIconUrl} alt={stockTypeName} key={stockTypeName} />, + <Image + src={stockTypeIconUrl} + alt={stockTypeName} + width={24} + height={24} + key={stockTypeName} + />, <StockActiveStateToggleButton stockTypeId={stockTypeId} key={stockTypeId} />, ]) ?? [] From 1ff1a5139ef2fac7bf62ec6595240e52e7b2e0c5 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 21:58:58 +0900 Subject: [PATCH 492/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=20file=20input=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=A7=88=EB=AC=B4=EB=A6=AC=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shared/_hooks/use-strategy-icon-post.ts | 45 +++++++++---------- .../_ui/shared/file-input/index.tsx | 9 +--- .../_ui/stock-post-button/index.tsx | 26 ++++++++++- .../_ui/trade-post-button/index.tsx | 26 ++++++++++- 4 files changed, 71 insertions(+), 35 deletions(-) diff --git a/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts b/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts index 97883baf..ba73d488 100644 --- a/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts +++ b/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts @@ -3,23 +3,17 @@ import { ChangeEvent, useState } from 'react' import getPresignedUrl from '../_api/get-presigned-url' import uploadFileWithPresignedUrl from '../_api/upload-file-with-presigned-url' -interface FormDataModel { - imageFile: File | null - typeName: string -} - export type DomainType = 'trade' | 'stock' -const initailFormData = { imageFile: null, typeName: '' } - const useStrategyIconPost = (domain: DomainType) => { const [imagePreview, setImagePreview] = useState('') - const [formData, setFormData] = useState<FormDataModel>(initailFormData) + const [typeName, setTypeName] = useState('') + const [image, setImage] = useState<File | null>(null) const onTypeNameInputChange = (e: ChangeEvent<HTMLInputElement>) => { const typeName = e.target.value - setFormData((prev) => ({ ...prev, typeName })) + setTypeName(typeName) } const onImageInputChange = (e: ChangeEvent<HTMLInputElement>) => { @@ -31,8 +25,9 @@ const useStrategyIconPost = (domain: DomainType) => { return } - setFormData((prev) => ({ ...prev, imageFile: iconImage })) + setImage(iconImage) + // preview const reader = new FileReader() reader.onload = (e) => { setImagePreview(e.target?.result as string) @@ -43,37 +38,41 @@ const useStrategyIconPost = (domain: DomainType) => { } } - const isSubmitable = Object.values(formData).every((value) => Boolean(value)) + const isSubmitable = typeName !== '' && image !== null - const onFormSubmit = async (e: React.FormEvent) => { + const resetFormData = () => { + setImage(null) + setTypeName('') + setImagePreview('') + } + + const onSubmit = async (e: React.FormEvent) => { e.preventDefault() - const { imageFile, typeName } = formData - if (!imageFile || !typeName) return + if (!isSubmitable) return try { - // console.log('imageFile', imageFile, 'typeName', typeName) - - const presignedUrl = await getPresignedUrl(typeName, imageFile.name, imageFile.size, domain) - - // console.log('p', presignedUrl) + const presignedUrl = await getPresignedUrl(typeName, image.name, image.size, domain) - await uploadFileWithPresignedUrl(presignedUrl, imageFile) + await uploadFileWithPresignedUrl(presignedUrl, image) alert('이미지가 성공적으로 업로드되었습니다!') - setFormData(initailFormData) + resetFormData() } catch (err) { console.error('이미지 업로드 실패:', err) - alert('업로드 중 문제가 발생했습니다.') + throw new Error('업로드 중 문제가 발생했습니다.') } } return { + ImageData, + typeName, + resetFormData, imagePreview, onTypeNameInputChange, onImageInputChange, isSubmitable, - onFormSubmit, + onSubmit, } } diff --git a/app/admin/strategies/_ui/shared/file-input/index.tsx b/app/admin/strategies/_ui/shared/file-input/index.tsx index 29021b2b..78d8f630 100644 --- a/app/admin/strategies/_ui/shared/file-input/index.tsx +++ b/app/admin/strategies/_ui/shared/file-input/index.tsx @@ -22,14 +22,7 @@ const FileInput = ({ preview, accept = '*', value, onChange, ...props }: Props) {preview && ( <Image width={24} height={24} src={preview} alt="Preview" className={cx('preview')} /> )} - <input - type="file" - accept={accept} - value={value} - onChange={onChange} - className={cx('input')} - {...props} - /> + <input type="file" accept={accept} onChange={onChange} className={cx('input')} {...props} /> <FileIcon className={cx('icon')} /> </div> ) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx index 36c6594c..f9790bd3 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx @@ -1,7 +1,10 @@ 'use client' +import { FormEvent } from 'react' + import FileInput from '@/app/admin/strategies/_ui/shared/file-input' import { RegisterIcon } from '@/public/icons' +import { useQueryClient } from '@tanstack/react-query' import classNames from 'classnames/bind' import useModal from '@/shared/hooks/custom/use-modal' @@ -17,8 +20,26 @@ const cx = classNames.bind(styles) const StockPostButton = () => { const { isModalOpen, openModal, closeModal } = useModal() const onPostButtonClick = () => openModal() - const { onFormSubmit, imagePreview, onImageInputChange, onTypeNameInputChange, isSubmitable } = - useStrategyIconPost('stock') + const queryClient = useQueryClient() + + const { + typeName, + onSubmit, + imagePreview, + onImageInputChange, + onTypeNameInputChange, + isSubmitable, + } = useStrategyIconPost('stock') + + const onFormSubmit = async (e: FormEvent) => { + try { + await onSubmit(e) + closeModal() + queryClient.invalidateQueries({ queryKey: ['adminStocks'] }) + } catch (err) { + console.error('Error : ' + err) + } + } return ( <> @@ -38,6 +59,7 @@ const StockPostButton = () => { <Input placeholder="종목명을 입력해주세요." className={cx('input')} + value={typeName} onChange={onTypeNameInputChange} /> </div> diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx index e04e99eb..7e48b965 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx +++ b/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx @@ -1,7 +1,10 @@ 'use client' +import { FormEvent } from 'react' + import FileInput from '@/app/admin/strategies/_ui/shared/file-input' import { RegisterIcon } from '@/public/icons' +import { useQueryClient } from '@tanstack/react-query' import classNames from 'classnames/bind' import useModal from '@/shared/hooks/custom/use-modal' @@ -17,8 +20,27 @@ const cx = classNames.bind(styles) const TradePostButton = () => { const { isModalOpen, openModal, closeModal } = useModal() const onPostButtonClick = () => openModal() - const { onFormSubmit, imagePreview, onImageInputChange, onTypeNameInputChange, isSubmitable } = - useStrategyIconPost('trade') + + const queryClient = useQueryClient() + + const { + typeName, + onSubmit, + imagePreview, + onImageInputChange, + onTypeNameInputChange, + isSubmitable, + } = useStrategyIconPost('trade') + + const onFormSubmit = async (e: FormEvent) => { + try { + await onSubmit(e) + closeModal() + queryClient.invalidateQueries({ queryKey: ['adminTrades'] }) + } catch (err) { + console.error('Error : ' + err) + } + } return ( <> From fb3702565fe610e28d72b9eb241df9675791da3e Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Mon, 2 Dec 2024 22:39:39 +0900 Subject: [PATCH 493/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=AA=A9=EC=97=90=EC=84=9C=20mutation=20=20=ED=9B=84?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EA=B0=80=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0,?= =?UTF-8?q?=20=EC=A0=84=EC=B2=B4=20=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EA=B0=80?= =?UTF-8?q?=20=EC=97=86=EB=8A=94=20=EA=B2=83=EC=B2=98=EB=9F=BC=20=EB=B3=B4?= =?UTF-8?q?=EC=9D=B4=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_hooks/query/use-stocks-data.ts | 3 ++- .../_ui/active-stock-manage-table.tsx | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts index 1922a706..1af8f245 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts +++ b/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts @@ -4,12 +4,13 @@ import getStocks from '../../_api/get-stocks' type ArgType = 'active' | 'inactive' -const useStocksData = (activateState: ArgType, page: number, size: number) => { +const useStocksData = (activateState: ArgType, page: number, size: number, enabled?: boolean) => { const isActive = activateState === 'active' ? true : false return useQuery({ queryKey: ['adminStocks', activateState, page, size], queryFn: () => getStocks(isActive, page, size), + enabled, }) } diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx index 7484f3ff..5d9ec006 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx +++ b/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -13,7 +13,24 @@ const TABLE_BODY_SIZE = 10 const ActiveStockManageTable = () => { const [currentPage, setCurrentPage] = useState(1) - const { isLoading, data } = useStocksData('active', currentPage, TABLE_BODY_SIZE) + const { data: initialData, isLoading: isLoadingInitial } = useStocksData( + 'active', + currentPage, + TABLE_BODY_SIZE + ) + + const isUnderflow = initialData?.content.length === 0 && !initialData?.first + + if (isUnderflow) setCurrentPage((prev) => prev - 1) + + const { data: fallbackData, isLoading: isLoadingFallback } = useStocksData( + 'active', + currentPage - 1, + TABLE_BODY_SIZE, + isUnderflow + ) + + const data = initialData ?? fallbackData const tableData = data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, @@ -27,7 +44,7 @@ const ActiveStockManageTable = () => { <StockActiveStateToggleButton stockTypeId={stockTypeId} active key={stockTypeId} />, ]) ?? [] - if (isLoading) return <ManageTable.Skeleton domain="종목" /> + if (isLoadingInitial || isLoadingFallback) return <ManageTable.Skeleton domain="종목" /> return ( <ManageTable From b5bac269b80a5e5fd15d95fadd9aed33d79720b2 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Mon, 2 Dec 2024 23:59:06 +0900 Subject: [PATCH 494/648] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#196)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/api/auth.ts b/shared/api/auth.ts index 7b3bbe12..a548b216 100644 --- a/shared/api/auth.ts +++ b/shared/api/auth.ts @@ -3,7 +3,7 @@ import type { LoginFormDataModel, LoginResponseType } from '@/shared/types/auth' import axiosInstance from './axios' export const login = async (credentials: LoginFormDataModel): Promise<LoginResponseType> => { - const response = await axiosInstance.post('/login', credentials) + const response = await axiosInstance.post('/api/users/login', credentials) return response } From 6f4c064571c8b9fdec4b33d5696281b09800a9e5 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 10:19:28 +0900 Subject: [PATCH 495/648] =?UTF-8?q?fix:=20=ED=8F=AC=EB=A7=B7=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=88=98=EC=A0=95=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/utils/format.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/shared/utils/format.ts b/shared/utils/format.ts index 5301e583..dd3789e6 100644 --- a/shared/utils/format.ts +++ b/shared/utils/format.ts @@ -21,9 +21,4 @@ export const arrayFormatNumbers = (arrayData: number[]): (string | number)[] | n return null } -export const formatNumber = (data: number): string | number => { - if (data.toFixed(0).toString().length > 3) { - return data.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',') - } - return data -} +export const formatNumber = (data: number): string | number => data.toLocaleString() From a462c2f1563e87ef46b9a4e1457f4a2d18b55025 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 10:30:05 +0900 Subject: [PATCH 496/648] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/details-side-item/side-item.tsx | 2 +- shared/ui/table/statistics/index.tsx | 4 ++-- shared/ui/table/vertical/index.tsx | 2 +- shared/utils/format.ts | 7 ++++++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index 7a91c142..621a509a 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -35,7 +35,7 @@ const SideItem = ({ title, data, profileImage, isMyStrategy = false }: Props) => )} </> ) : ( - <p>{typeof data === 'number' ? formatNumber(data) : data}</p> + <p>{formatNumber(data)}</p> )} </div> </div> diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index b0dc9895..b5fb1113 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -50,11 +50,11 @@ const StatisticsTable = ({ title, statisticsData }: Props) => { {groupedData.map((row, idx) => ( <tr key={idx}> <td>{row[0]}</td> - <td>{typeof row[1] === 'number' ? formatNumber(row[1]) : row[1]}</td> + <td>{formatNumber(row[1])}</td> {row[2] && ( <> <td>{row[2]}</td> - <td>{typeof row[3] === 'number' ? formatNumber(row[3]) : row[3]}</td> + <td>{formatNumber(row[3])}</td> </> )} </tr> diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 218cf111..c4dff1ba 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -51,7 +51,7 @@ const VerticalTable = ({ {slicedTableBody.map((row) => ( <tr key={Object.values(row)[0]}> {Object.values(row).map((data, idx) => ( - <td key={data + idx}>{typeof data === 'number' ? formatNumber(data) : data}</td> + <td key={data + idx}>{formatNumber(data)}</td> ))} {isEditable && ( <td className={cx('button-container')}> diff --git a/shared/utils/format.ts b/shared/utils/format.ts index dd3789e6..164869e9 100644 --- a/shared/utils/format.ts +++ b/shared/utils/format.ts @@ -21,4 +21,9 @@ export const arrayFormatNumbers = (arrayData: number[]): (string | number)[] | n return null } -export const formatNumber = (data: number): string | number => data.toLocaleString() +export const formatNumber = (data: number | string): string | number => { + if (typeof data === 'number') { + return data.toLocaleString() + } + return data +} From e98f1269304f7f8c631e59c55389530b852074ab Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 10:51:11 +0900 Subject: [PATCH 497/648] =?UTF-8?q?feat:=20=EA=B5=AC=EB=8F=85=20api=20&=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=AA=A8=EB=8B=AC=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-information/index.tsx | 14 +++- app/(dashboard)/_ui/strategies-item/index.tsx | 2 +- .../_ui/strategies-item/subscribe.tsx | 43 ++++++++-- app/(dashboard)/_ui/subscriber-item/index.tsx | 5 +- .../query/use-get-details-information-data.ts | 14 ++-- .../strategies/[strategyId]/page.tsx | 83 +++++++++++++------ .../strategies/_api/get-subscribe.ts | 13 +++ .../_hooks/query/use-get-subscribe.ts | 11 +++ 8 files changed, 136 insertions(+), 49 deletions(-) create mode 100644 app/(dashboard)/strategies/_api/get-subscribe.ts create mode 100644 app/(dashboard)/strategies/_hooks/query/use-get-subscribe.ts diff --git a/app/(dashboard)/_ui/details-information/index.tsx b/app/(dashboard)/_ui/details-information/index.tsx index 9225357b..cd754124 100644 --- a/app/(dashboard)/_ui/details-information/index.tsx +++ b/app/(dashboard)/_ui/details-information/index.tsx @@ -23,17 +23,23 @@ const DetailsInformation = ({ information, type = 'default' }: Props) => { { percent: information.profitFactor, label: 'Profit Factor' }, { percent: information.winRate, label: '승률' }, ] - + if (!information) return null return ( <> <div className={cx('information-top')}> <StrategyNameBox - iconUrls={[information.tradeTypeIconUrl, ...information.stockTypeInfo.stockTypeIconUrls]} - iconNames={[information.tradeTypeName, ...information.stockTypeInfo.stockTypeNames]} + iconUrls={[ + information.tradeTypeIconUrl, + ...(information.stockTypeInfo?.stockTypeIconUrls || []), + ]} + iconNames={[ + information.tradeTypeName, + ...(information.stockTypeInfo?.stockTypeNames || []), + ]} name={information.strategyName} /> <InvestInformation - stock={information.stockTypeInfo.stockTypeNames} + stock={information.stockTypeInfo?.stockTypeNames || []} trade={information.tradeTypeName} cycle={information.operationCycle} /> diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 8fe9c7e9..dc9bcf01 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -48,7 +48,7 @@ const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { </div> {type === 'default' && ( <div className={cx('subscribe')}> - <Subscribe subscriptionStatus={data.isSubscribed} /> + <Subscribe subscriptionStatus={data.isSubscribed} strategyId={data.strategyId} /> </div> )} {type === 'my' && ( diff --git a/app/(dashboard)/_ui/strategies-item/subscribe.tsx b/app/(dashboard)/_ui/strategies-item/subscribe.tsx index 183595ac..36592df8 100644 --- a/app/(dashboard)/_ui/strategies-item/subscribe.tsx +++ b/app/(dashboard)/_ui/strategies-item/subscribe.tsx @@ -2,19 +2,32 @@ import { useEffect, useState } from 'react' +import { useRouter } from 'next/navigation' + import { BookmarkIcon, BookmarkOutlineIcon } from '@/public/icons' import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' +import useModal from '@/shared/hooks/custom/use-modal' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import SigninCheckModal from '@/shared/ui/modal/signin-check-modal' + +import useGetSubscribe from '../../strategies/_hooks/query/use-get-subscribe' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { + strategyId: number subscriptionStatus: boolean } -const Subscribe = ({ subscriptionStatus }: Props) => { +const Subscribe = ({ strategyId, subscriptionStatus }: Props) => { const [isSubscribed, setIsSubscribed] = useState(false) + const router = useRouter() + const user = useAuthStore((state) => state.user) + const { isModalOpen, openModal, closeModal } = useModal() + const { mutate } = useGetSubscribe() useEffect(() => { if (subscriptionStatus) { @@ -24,15 +37,31 @@ const Subscribe = ({ subscriptionStatus }: Props) => { const handleSubscribe = (e: React.MouseEvent) => { e.preventDefault() - setIsSubscribed(!isSubscribed) + if (!user) { + openModal() + } + mutate(strategyId, { + onSuccess: () => setIsSubscribed(!isSubscribed), + }) } + const handleRoute = () => router.push(PATH.SIGN_IN) + return ( - <div className={cx('subscribe-icon')}> - <button onClick={handleSubscribe}> - {isSubscribed ? <BookmarkIcon /> : <BookmarkOutlineIcon />} - </button> - </div> + <> + <div className={cx('subscribe-icon')}> + <button onClick={handleSubscribe}> + {isSubscribed ? <BookmarkIcon /> : <BookmarkOutlineIcon />} + </button> + </div> + {isModalOpen && ( + <SigninCheckModal + isModalOpen={isModalOpen} + closeModal={closeModal} + onChange={handleRoute} + /> + )} + </> ) } diff --git a/app/(dashboard)/_ui/subscriber-item/index.tsx b/app/(dashboard)/_ui/subscriber-item/index.tsx index f7a2863c..68c73bd0 100644 --- a/app/(dashboard)/_ui/subscriber-item/index.tsx +++ b/app/(dashboard)/_ui/subscriber-item/index.tsx @@ -10,11 +10,12 @@ const cx = classNames.bind(styles) interface Props { isMyStrategy?: boolean + isSubscribed?: boolean subscribers: number onClick?: () => void } -const SubscriberItem = ({ isMyStrategy = false, subscribers, onClick }: Props) => { +const SubscriberItem = ({ isSubscribed, isMyStrategy = false, subscribers, onClick }: Props) => { return ( <div className={cx('container')}> <div> @@ -24,7 +25,7 @@ const SubscriberItem = ({ isMyStrategy = false, subscribers, onClick }: Props) = </div> {!isMyStrategy && ( <Button size="small" variant="filled" onClick={onClick}> - 구독하기 + {isSubscribed ? '구독취소' : '구독하기'} </Button> )} </div> diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts index c06f4980..87f4d668 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-details-information-data.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query' +import { UseQueryResult, useQuery } from '@tanstack/react-query' import { StrategyDetailsInformationModel } from '@/shared/types/strategy-data' @@ -11,14 +11,10 @@ interface Props { const useGetDetailsInformationData = ({ strategyId, -}: Props): { - data: - | { - detailsSideData: InformationType[] - detailsInformationData: StrategyDetailsInformationModel - } - | undefined -} => { +}: Props): UseQueryResult<{ + detailsSideData: InformationType[] + detailsInformationData: StrategyDetailsInformationModel +}> => { return useQuery({ queryKey: ['strategyDetails', strategyId], queryFn: () => getDetailsInformation(strategyId), diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 08949eeb..1221f87a 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -3,12 +3,15 @@ import AnalysisContainer from '@/app/(dashboard)/_ui/analysis-container' import SubscriberItem from '@/app/(dashboard)/_ui/subscriber-item' +import useModal from '@/shared/hooks/custom/use-modal' import { useAuthStore } from '@/shared/stores/use-auth-store' import BackHeader from '@/shared/ui/header/back-header' +import SubscribeCheckModal from '@/shared/ui/modal/subscribe-check-modal' import Title from '@/shared/ui/title' import DetailsInformation from '../../_ui/details-information' import DetailsSideItem, { InformationModel, TitleType } from '../../_ui/details-side-item' +import useGetSubscribe from '../_hooks/query/use-get-subscribe' import SideContainer from '../_ui/side-container' import useGetDetailsInformationData from './_hooks/query/use-get-details-information-data' import ReviewContainer from './_ui/review-container' @@ -17,42 +20,70 @@ export type InformationType = { title: TitleType; data: string | number } | Info const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { const user = useAuthStore((state) => state.user) - const { data } = useGetDetailsInformationData({ + const { isModalOpen, openModal, closeModal } = useModal() + const { mutate } = useGetSubscribe() + const { refetch, data } = useGetDetailsInformationData({ strategyId: params.strategyId, }) const { detailsSideData, detailsInformationData: information } = data || {} + const handleSubscribe = () => { + mutate(params.strategyId, { + onSuccess: () => refetch(), + }) + } + const handleUnsubscribe = () => { + mutate(params.strategyId, { + onSuccess: () => { + closeModal() + refetch() + }, + }) + } + const hasDetailsSideData = detailsSideData?.map((data) => { if (!Array.isArray(data)) return data.data !== undefined }) return ( - <div> - <BackHeader label={'목록으로 돌아가기'} /> - <Title label={'전략 상세보기'} /> - {information && <DetailsInformation information={information} />} - <AnalysisContainer strategyId={params.strategyId} /> - <ReviewContainer strategyId={params.strategyId} /> - <SideContainer> - {information && ( - <SubscriberItem - isMyStrategy={user?.nickname === information.nickname} - subscribers={information?.subscriptionCount} - /> - )} - {information && - hasDetailsSideData?.[0] && - detailsSideData?.map((data, idx) => ( - <div key={`${data}_${idx}`}> - <DetailsSideItem - information={data} - isMyStrategy={user?.nickname === information.nickname} - /> - </div> - ))} - </SideContainer> - </div> + <> + <div> + <BackHeader label={'목록으로 돌아가기'} /> + <Title label={'전략 상세보기'} /> + {information && <DetailsInformation information={information} />} + <AnalysisContainer strategyId={params.strategyId} /> + <ReviewContainer strategyId={params.strategyId} /> + <SideContainer> + {information && ( + <SubscriberItem + isMyStrategy={user?.nickname === information.nickname} + isSubscribed={information?.isSubscribed} + subscribers={information?.subscriptionCount} + onClick={openModal} + /> + )} + {information && + hasDetailsSideData?.[0] && + detailsSideData?.map((data, idx) => ( + <div key={`${data}_${idx}`}> + <DetailsSideItem + information={data} + isMyStrategy={user?.nickname === information.nickname} + /> + </div> + ))} + </SideContainer> + </div> + {isModalOpen && information && ( + <SubscribeCheckModal + isSubscribing={information?.isSubscribed} + isModalOpen={isModalOpen} + closeModal={handleUnsubscribe} + onChange={handleSubscribe} + /> + )} + </> ) } export default StrategyDetailPage diff --git a/app/(dashboard)/strategies/_api/get-subscribe.ts b/app/(dashboard)/strategies/_api/get-subscribe.ts new file mode 100644 index 00000000..0191cc15 --- /dev/null +++ b/app/(dashboard)/strategies/_api/get-subscribe.ts @@ -0,0 +1,13 @@ +import axios from 'axios' + +const getSubscribe = async (strategyId: number) => { + try { + const response = await axios.get(`/api/strategies/${strategyId}/subscribe?userId=3`) + return response.data.isSuccess + } catch (err) { + console.error(err) + throw err + } +} + +export default getSubscribe diff --git a/app/(dashboard)/strategies/_hooks/query/use-get-subscribe.ts b/app/(dashboard)/strategies/_hooks/query/use-get-subscribe.ts new file mode 100644 index 00000000..c50532f4 --- /dev/null +++ b/app/(dashboard)/strategies/_hooks/query/use-get-subscribe.ts @@ -0,0 +1,11 @@ +import { useMutation } from '@tanstack/react-query' + +import getSubscribe from '../../_api/get-subscribe' + +const useGetSubscribe = () => { + return useMutation({ + mutationFn: (strategyId: number) => getSubscribe(strategyId), + }) +} + +export default useGetSubscribe From 09eafa1c0a08ae2130017750428aa8458b0396d0 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 10:51:54 +0900 Subject: [PATCH 498/648] =?UTF-8?q?fix:=20api=20axiosInstance=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_api/get-analysis-chart.ts | 5 +++-- .../strategies/[strategyId]/_api/get-analysis.ts | 5 +++-- .../[strategyId]/_api/get-details-information.ts | 4 ++-- .../strategies/[strategyId]/_api/get-reviews.ts | 5 ++--- .../strategies/[strategyId]/_api/get-statistics.ts | 4 ++-- .../strategies/[strategyId]/_api/post-review.ts | 7 +++++-- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts index 7fa168a4..09551448 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-chart.ts @@ -1,5 +1,6 @@ import { AnalysisChartOptionsType } from '@/app/(dashboard)/_ui/analysis-container' -import axios from 'axios' + +import axiosInstance from '@/shared/api/axios' const getAnalysisChart = async ( strategyId: number, @@ -7,7 +8,7 @@ const getAnalysisChart = async ( secondOption: AnalysisChartOptionsType ) => { try { - const response = await axios.get( + const response = await axiosInstance.get( `/api/strategies/${strategyId}/analysis?option1=${firstOption}&option2=${secondOption}` ) return response.data.result diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts index 810b4c2b..38a8711c 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis.ts @@ -1,5 +1,6 @@ import { AnalysisTabType } from '@/app/(dashboard)/_ui/analysis-container/tabs-width-table' -import axios from 'axios' + +import axiosInstance from '@/shared/api/axios' const getAnalysis = async ( strategyId: number, @@ -9,7 +10,7 @@ const getAnalysis = async ( ) => { if (type !== 'daily' && type !== 'monthly') return null try { - const response = await axios.get( + const response = await axiosInstance.get( `/api/strategies/${strategyId}/${type}-analysis?page=${page}&size=${size}` ) return response.data.result diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index 36f29ffc..3c84357f 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -1,4 +1,4 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { InformationType } from '../page' @@ -6,7 +6,7 @@ const getDetailsInformation = async (strategyId: number) => { if (!strategyId) return try { - const response = await axios.get(`/api/strategies/${strategyId}/detail`) + const response = await axiosInstance.get(`/api/strategies/${strategyId}/detail`) const data = await response.data const detailsSideData: InformationType[] = [ { title: '트레이더', data: data.nickname }, diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts index 99299d33..b91c21ef 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-reviews.ts @@ -1,12 +1,11 @@ -import axios from 'axios' - +import axiosInstance from '@/shared/api/axios' import { REVIEW_PAGE_COUNT } from '@/shared/constants/count-per-page' const getReviews = async (strategyId: number, page: number | undefined) => { if (!strategyId && !page) return try { - const response = await axios.get( + const response = await axiosInstance.get( `/api/strategies/${strategyId}/reviews?userId=1&page=${page}&size=${REVIEW_PAGE_COUNT}` ) const data = await response.data.result diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts index 60ab2291..0a8bd487 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-statistics.ts @@ -1,8 +1,8 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' const getStatistics = async (strategyId: number) => { try { - const response = await axios.get(`/api/strategies/${strategyId}/statistics`) + const response = await axiosInstance.get(`/api/strategies/${strategyId}/statistics`) return response.data.result } catch (err) { console.error(err) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts index eef87d02..c7337ad4 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts @@ -1,11 +1,14 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' const postReview = async ( strategyId: number, content: { content: string; starRating: number } ): Promise<boolean | null> => { try { - const response = await axios.post(`/api/strategies/${strategyId}/reviews?userId=1`, content) + const response = await axiosInstance.post( + `/api/strategies/${strategyId}/reviews?userId=1`, + content + ) return response.data.isSuccess } catch (err) { console.error(err, '리뷰 등록 실패') From f5377e75c01cbb4daa4f1c4c93f7c0d7b50b5a6c Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 11:49:45 +0900 Subject: [PATCH 499/648] =?UTF-8?q?fix:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=ACclose=20=EC=B6=94=EA=B0=80=20(#186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/details-information/index.tsx | 8 ++++---- .../[strategyId]/_api/get-details-information.ts | 2 +- app/(dashboard)/strategies/[strategyId]/page.tsx | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/_ui/details-information/index.tsx b/app/(dashboard)/_ui/details-information/index.tsx index cd754124..50bf7e2d 100644 --- a/app/(dashboard)/_ui/details-information/index.tsx +++ b/app/(dashboard)/_ui/details-information/index.tsx @@ -28,10 +28,10 @@ const DetailsInformation = ({ information, type = 'default' }: Props) => { <> <div className={cx('information-top')}> <StrategyNameBox - iconUrls={[ - information.tradeTypeIconUrl, - ...(information.stockTypeInfo?.stockTypeIconUrls || []), - ]} + // iconUrls={[ + // information.tradeTypeIconUrl, + // ...(information.stockTypeInfo?.stockTypeIconUrls || []), + // ]} iconNames={[ information.tradeTypeName, ...(information.stockTypeInfo?.stockTypeNames || []), diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts index 3c84357f..7cb39730 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-details-information.ts @@ -7,7 +7,7 @@ const getDetailsInformation = async (strategyId: number) => { try { const response = await axiosInstance.get(`/api/strategies/${strategyId}/detail`) - const data = await response.data + const data = await response.data.result const detailsSideData: InformationType[] = [ { title: '트레이더', data: data.nickname }, { title: '최소 투자 금액', data: data.minimumInvestmentAmount }, diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 1221f87a..101afc68 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -30,7 +30,10 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { const handleSubscribe = () => { mutate(params.strategyId, { - onSuccess: () => refetch(), + onSuccess: () => { + closeModal() + refetch() + }, }) } const handleUnsubscribe = () => { From 55cd15a50932713f9562eff506cb0ab004f0e68f Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 11:56:41 +0900 Subject: [PATCH 500/648] =?UTF-8?q?fix:=20api=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=88=98=EC=A0=95=20(#186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/[strategyId]/_api/post-review.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts index c7337ad4..1b13282e 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts @@ -5,10 +5,7 @@ const postReview = async ( content: { content: string; starRating: number } ): Promise<boolean | null> => { try { - const response = await axiosInstance.post( - `/api/strategies/${strategyId}/reviews?userId=1`, - content - ) + const response = await axiosInstance.post(`/api/strategies/${strategyId}/reviews`, content) return response.data.isSuccess } catch (err) { console.error(err, '리뷰 등록 실패') From 5c77aa2590a33389a34a05cc56623efe176f2d74 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 12:35:24 +0900 Subject: [PATCH 501/648] =?UTF-8?q?design:=20=EB=9E=9C=EB=94=A9=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B0=98=EC=9D=91=ED=98=95=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=81=EC=9A=A9=20(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../average-metrics-chart.tsx | 15 +++++++- .../_ui/average-metrics-section/index.tsx | 4 +- .../styles.module.scss | 4 ++ .../(home)/_ui/hero-section/index.tsx | 2 +- .../_ui/hero-section/styles.module.scss | 8 ++++ .../top-favorite-section/styles.module.scss | 4 ++ .../top-sm-score-section/styles.module.scss | 6 +++ .../user-metrics-section/styles.module.scss | 13 +++++++ shared/styles/base/_mixins.scss | 38 +++++++++++++++++-- 9 files changed, 88 insertions(+), 6 deletions(-) diff --git a/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx b/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx index b2b0b155..a4d9e512 100644 --- a/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx +++ b/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx @@ -26,7 +26,6 @@ const AverageMetricsChart = ({ data }: Props) => { const chartOptions: Highcharts.Options = { chart: { type: 'areaspline', - width: 1000, height: 450, backgroundColor: '#FFFFFF', zooming: { @@ -176,6 +175,20 @@ const AverageMetricsChart = ({ data }: Props) => { pointPlacement: 'on', }, ], + responsive: { + rules: [ + { + condition: { + maxWidth: 1000, + }, + chartOptions: { + chart: { + width: null, + }, + }, + }, + ], + }, credits: { enabled: false }, } diff --git a/app/(landing)/(home)/_ui/average-metrics-section/index.tsx b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx index 622195cc..e375000d 100644 --- a/app/(landing)/(home)/_ui/average-metrics-section/index.tsx +++ b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx @@ -47,7 +47,9 @@ const AverageMetricsSection = () => { FROM <span className={cx('date')}>{startDate}</span>TO <span className={cx('date')}>{endDate}</span> </div> - <AverageMetricsChart data={chartData} /> + <div className={cx('chart-wrapper')}> + <AverageMetricsChart data={chartData} /> + </div> </div> </div> </section> diff --git a/app/(landing)/(home)/_ui/average-metrics-section/styles.module.scss b/app/(landing)/(home)/_ui/average-metrics-section/styles.module.scss index 2da855b3..1350af68 100644 --- a/app/(landing)/(home)/_ui/average-metrics-section/styles.module.scss +++ b/app/(landing)/(home)/_ui/average-metrics-section/styles.module.scss @@ -31,3 +31,7 @@ background-color: $color-gray-200; } } + +.chart-wrapper { + width: 100%; +} diff --git a/app/(landing)/(home)/_ui/hero-section/index.tsx b/app/(landing)/(home)/_ui/hero-section/index.tsx index 1e8cb8a1..8934b0a2 100644 --- a/app/(landing)/(home)/_ui/hero-section/index.tsx +++ b/app/(landing)/(home)/_ui/hero-section/index.tsx @@ -18,7 +18,7 @@ const HeroSection = () => { <br /> 참고하거나 공유하고 싶다면 <br /> - <Logo className={cx('logo')} width="65" height="39" /> 인베스트메틱에서! + <Logo className={cx('logo')} /> 인베스트메틱에서! </h1> <LinkButton href={PATH.SIGN_UP_USER_TYPE} variant="filled" className={cx('button')}> 바로 함께하기 diff --git a/app/(landing)/(home)/_ui/hero-section/styles.module.scss b/app/(landing)/(home)/_ui/hero-section/styles.module.scss index 9379f456..875f2978 100644 --- a/app/(landing)/(home)/_ui/hero-section/styles.module.scss +++ b/app/(landing)/(home)/_ui/hero-section/styles.module.scss @@ -12,6 +12,14 @@ display: inline-block; width: 65px; height: auto; + + @include tablet-md { + width: 42px; + } + + @include mobile { + width: 34px; + } } .button { diff --git a/app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss b/app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss index c32ecf71..f6cefe03 100644 --- a/app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss +++ b/app/(landing)/(home)/_ui/top-favorite-section/styles.module.scss @@ -9,4 +9,8 @@ display: flex; justify-content: center; gap: 20px; + + @include tablet-md { + flex-direction: column; + } } diff --git a/app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss b/app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss index eeca3b16..2de03be0 100644 --- a/app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss +++ b/app/(landing)/(home)/_ui/top-sm-score-section/styles.module.scss @@ -18,4 +18,10 @@ grid-row: 1 / 3; } } + + @include tablet-md { + display: flex; + flex-direction: column; + align-items: center; + } } diff --git a/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss b/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss index 53ef6847..493905b3 100644 --- a/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss +++ b/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss @@ -3,4 +3,17 @@ gap: 20px; justify-content: center; margin-top: 36px; + + @include tablet-md { + & > div { + padding: 10px; + } + } + + @include mobile { + flex-wrap: wrap; + & > div { + width: calc(50% - 10px); + } + } } diff --git a/shared/styles/base/_mixins.scss b/shared/styles/base/_mixins.scss index 5a13bca3..ec8ca507 100644 --- a/shared/styles/base/_mixins.scss +++ b/shared/styles/base/_mixins.scss @@ -5,19 +5,19 @@ } @mixin tablet-sm { - @media (min-width: #{$breakpoint-sm}) and (max-width: #{$breakpoint-md - 1}) { + @media (max-width: #{$breakpoint-md - 1}) { @content; } } @mixin tablet-md { - @media (min-width: #{$breakpoint-md}) and (max-width: #{$breakpoint-lg - 1}) { + @media (max-width: #{$breakpoint-lg - 1}) { @content; } } @mixin desktop { - @media (min-width: #{$breakpoint-lg}) and (max-width: #{$breakpoint-xl - 1}) { + @media (max-width: #{$breakpoint-xl - 1}) { @content; } } @@ -39,24 +39,52 @@ @extend %base-headline; font-size: $text-h1; font-weight: $text-bold; + + @include tablet-md { + font-size: $text-h2; + } + + @include mobile { + font-size: $text-h3; + } } @mixin typo-h2 { @extend %base-headline; font-size: $text-h2; font-weight: $text-bold; + + @include tablet-md { + font-size: $text-h3; + } + + @include mobile { + font-size: $text-h4; + } } @mixin typo-h3 { @extend %base-headline; font-size: $text-h3; font-weight: $text-semibold; + + @include tablet-md { + font-size: $text-h4; + } + + @include mobile { + font-size: $text-b1; + } } @mixin typo-h4 { @extend %base-headline; font-size: $text-h4; font-weight: $text-semibold; + + @include tablet-md { + font-size: $text-b1; + } } // body @@ -70,6 +98,10 @@ @mixin typo-b1 { @extend %base-body; font-size: $text-b1; + + @include tablet-md { + font-size: $text-b2; + } } @mixin typo-b2 { From b8cb8f3d08bb7275ec8af9cd012c133e837c1d46 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 13:00:35 +0900 Subject: [PATCH 502/648] =?UTF-8?q?design:=20footer=20=EB=B0=98=EC=9D=91?= =?UTF-8?q?=ED=98=95=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/footer/styles.module.scss | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/shared/ui/footer/styles.module.scss b/shared/ui/footer/styles.module.scss index 6648cf7a..c3800af9 100644 --- a/shared/ui/footer/styles.module.scss +++ b/shared/ui/footer/styles.module.scss @@ -8,9 +8,16 @@ justify-content: space-between; align-items: flex-end; max-width: $max-width; + height: 100%; padding: 40px; margin: 0 auto; + @include tablet-md { + flex-direction: column; + align-items: baseline; + padding: 40px 20px; + } + strong { display: block; color: $color-gray-100; @@ -22,6 +29,14 @@ display: flex; gap: 60px; + @include tablet-md { + margin-top: 24px; + } + + @include mobile { + flex-direction: column; + } + .contents { display: flex; flex-direction: column; @@ -35,6 +50,10 @@ @include typo-b2; color: $color-gray-800; background-color: $color-gray-100; + + @include tablet-md { + height: 100%; + } } .copyright-wrapper { @@ -46,6 +65,12 @@ margin: 0 auto; padding: 0 40px; + @include tablet-md { + flex-direction: column; + gap: 12px; + padding: 16px 0; + } + .links { display: flex; gap: 40px; @@ -54,6 +79,11 @@ text-decoration: underline; } } + + .copyright { + padding: 0 12px; + text-align: center; + } } .logo-container { From 1342f4bd5ea81d9261820ce9d1ff192daf381e83 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 3 Dec 2024 13:44:15 +0900 Subject: [PATCH 503/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EA=B4=80=EB=A6=AC=20get=20api=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20(#109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/_ui/admin-header/index.tsx | 7 +- app/admin/users/_api/get-admin-users.ts | 34 +++++++++ .../users/_hooks/query/use-admin-users.ts | 20 ++++++ app/admin/users/_hooks/use-user-search.ts | 16 +++++ app/admin/users/page.module.scss | 10 +++ app/admin/users/page.tsx | 72 +++++++++++-------- app/admin/users/types.ts | 32 +++++++++ shared/ui/title/index.tsx | 5 +- 8 files changed, 158 insertions(+), 38 deletions(-) create mode 100644 app/admin/users/_api/get-admin-users.ts create mode 100644 app/admin/users/_hooks/query/use-admin-users.ts create mode 100644 app/admin/users/_hooks/use-user-search.ts create mode 100644 app/admin/users/types.ts diff --git a/app/admin/_ui/admin-header/index.tsx b/app/admin/_ui/admin-header/index.tsx index a45bbd16..dfa1c8d5 100644 --- a/app/admin/_ui/admin-header/index.tsx +++ b/app/admin/_ui/admin-header/index.tsx @@ -1,7 +1,6 @@ import { CSSProperties, ReactNode } from 'react' import Header from '@/shared/ui/header' -import { SearchInput } from '@/shared/ui/search-input' interface Props { Left?: ReactNode @@ -24,8 +23,4 @@ const AdminContentsHeader = ({ Left, Right }: Props) => { return <Header as="div" Left={Left} Right={Right} styles={headerStyles} /> } -const Search = () => { - return <SearchInput placeholder="제목을 검색하세요." /> -} - -export default Object.assign(AdminContentsHeader, { Search }) +export default AdminContentsHeader diff --git a/app/admin/users/_api/get-admin-users.ts b/app/admin/users/_api/get-admin-users.ts new file mode 100644 index 00000000..d5f94102 --- /dev/null +++ b/app/admin/users/_api/get-admin-users.ts @@ -0,0 +1,34 @@ +import axiosInstance from '@/shared/api/axios' + +import { AdminUsersResponeseModel } from '../types' + +interface ArgModel { + role: string + condition: string + keyword: string | null + page?: number + size?: number +} + +const getAdminUsers = async ({ role, condition, keyword, page = 1, size }: ArgModel) => { + try { + const res = await axiosInstance<AdminUsersResponeseModel>('/api/admin/users', { + params: { + role, + condition, + keyword, + page, + size, + }, + }) + + if (!res.data.isSuccess) throw new Error('Error with code' + res.data.code) + + return res.data.result + } catch (err) { + console.error(err) + throw err + } +} + +export default getAdminUsers diff --git a/app/admin/users/_hooks/query/use-admin-users.ts b/app/admin/users/_hooks/query/use-admin-users.ts new file mode 100644 index 00000000..37847a67 --- /dev/null +++ b/app/admin/users/_hooks/query/use-admin-users.ts @@ -0,0 +1,20 @@ +import { useQuery } from '@tanstack/react-query' + +import getAdminUsers from '../../_api/get-admin-users' + +interface ArgModel { + role: string + condition: string + keyword: string | null + page?: number + size?: number +} + +const useAdminUsers = ({ role, condition, keyword, page, size }: ArgModel) => { + return useQuery({ + queryKey: ['adminUsers', [role, condition, keyword, page, size]], + queryFn: () => getAdminUsers({ role, condition, keyword, page, size }), + }) +} + +export default useAdminUsers diff --git a/app/admin/users/_hooks/use-user-search.ts b/app/admin/users/_hooks/use-user-search.ts new file mode 100644 index 00000000..eed85d33 --- /dev/null +++ b/app/admin/users/_hooks/use-user-search.ts @@ -0,0 +1,16 @@ +import { DropdownOptionModel } from '@/shared/ui/dropdown/types' + +const useUserSearch = () => { + const searchOptions: DropdownOptionModel[] = [ + { label: '닉네임', value: 'NICKNAME' }, + { label: '이메일', value: 'EMAIL' }, + { label: '이름', value: 'NAME' }, + { label: '핸드폰 번호', value: 'PHONE' }, + ] + + return { + searchOptions, + } +} + +export default useUserSearch diff --git a/app/admin/users/page.module.scss b/app/admin/users/page.module.scss index 9dfc496a..7dd1306d 100644 --- a/app/admin/users/page.module.scss +++ b/app/admin/users/page.module.scss @@ -1,3 +1,13 @@ +.title { + margin: $header-height 0 26px 12.6px; +} + +.container { + padding: 0 45px 37px; + border-radius: 8px; + margin-bottom: 42px; +} + .color-primary-500 { color: $color-orange-500; } diff --git a/app/admin/users/page.tsx b/app/admin/users/page.tsx index 0e3f1ee7..03a1a995 100644 --- a/app/admin/users/page.tsx +++ b/app/admin/users/page.tsx @@ -5,75 +5,87 @@ import { useState } from 'react' import classNames from 'classnames/bind' +import Avatar from '@/shared/ui/avatar' import Pagination from '@/shared/ui/pagination' import { SearchInput } from '@/shared/ui/search-input' import Select from '@/shared/ui/select' import VerticalTable from '@/shared/ui/table/vertical' -import Tabs from '@/shared/ui/tabs' +import Tabs, { TabItemModel } from '@/shared/ui/tabs' import Title from '@/shared/ui/title' import AdminContentsHeader from '../_ui/admin-header' +import useAdminUsers from './_hooks/query/use-admin-users' +import useUserSearch from './_hooks/use-user-search' import styles from './page.module.scss' const cx = classNames.bind(styles) - -const tabs = ['모든 회원', '일반', '트레이더', '관리자'] +const tabs: Array<TabItemModel> = [ + { label: '모든 회원', id: 'ALL' }, + { label: '일반', id: 'INVESTOR' }, + { label: '트레이더', id: 'TRADER' }, + { label: '관리자', id: 'ADMIN' }, +] const AdminUsersPage = () => { - const [select, setSelect] = useState(tabs[0]) + const { searchOptions } = useUserSearch() + const [select, setSelect] = useState(searchOptions[0].value) + const [activeTab, setActiveTab] = useState(tabs[0].id) + const [keyword, setKeyword] = useState('') + const { isLoading, data } = useAdminUsers({ role: activeTab, condition: select, keyword }) + + if (isLoading || !data) return null + + console.log('data', data) + + const tableBody = data.content.map((data, idx) => [ + idx + 1, + <Avatar src={data?.imageUrl ?? undefined} key={data.userId} />, + data.userName, + data.nickname, + data.email, + data.phone, + data.role, + <button key={data.userId}>123</button>, + ]) + return ( <> - {/* TODO: inline css 제거 */} - <Title label="회원 관리" style={{ margin: '80px 0 26px 12.6px' }} /> - <div - style={{ - padding: '0 45px 37px', - borderRadius: '8px', - marginBottom: '42px', - backgroundColor: 'aliceblue', - }} - > - <Tabs - tabs={tabs.map((tab) => ({ - id: tab, - label: tab, - }))} - activeTab={tabs[0]} - onTabChange={(id) => alert(id)} - /> + <Title label="회원 관리" className={cx('title')} /> + <section className={cx('container')}> + <Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} /> <AdminContentsHeader // TODO: 실제 개수로 바인딩 Left={ <span> - 총 <span className={cx('color-primary-500')}>30</span>명 + 총 <span className={cx('color-primary-500')}>{data?.totalElements}</span>명 </span> } Right={ <div style={{ display: 'flex', gap: '24px' }}> <Select size="small" - options={tabs.map((tab) => ({ - label: tab, - value: tab, + options={searchOptions.map((option) => ({ + label: option.label, + value: option.value, }))} value={select} onChange={(v) => { setSelect(v as string) }} /> - <SearchInput /> + <SearchInput value={keyword} onChange={(e) => setKeyword(e.target.value)} /> </div> } /> <VerticalTable tableHead={['No.', '프로필', '이름', '닉네임', '이메일', '전화번호', '회원분류', '탈퇴']} - tableBody={[]} + tableBody={data?.content ? tableBody : []} countPerPage={10} currentPage={1} /> {/* TODO: 실제 값으로 추가 */} - <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> - </div> + <Pagination currentPage={1} maxPage={data?.totalPages} onPageChange={() => {}} /> + </section> </> ) } diff --git a/app/admin/users/types.ts b/app/admin/users/types.ts new file mode 100644 index 00000000..4ead871d --- /dev/null +++ b/app/admin/users/types.ts @@ -0,0 +1,32 @@ +interface UsersResponseBaseModel<T extends boolean> { + isSuccess: T + message: string + code: T extends false ? number : null +} + +type RoleType = 'TRADER' | 'TRADER_ADMIN' | 'INVESTOR' | 'INVESTOR' + +interface UserInfoModel { + userId: number + userName: string + email: string + imageUrl: string | null + nickname: string + phone: string + infoAgreement: boolean + role: RoleType +} + +export interface AdminUsersResponeseModel extends UsersResponseBaseModel<boolean> { + result: { + content: Array<UserInfoModel> + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean + } +} + +export type UserRoleType = '모든 회원' | '일반' | '트레이더' | '관리자' diff --git a/shared/ui/title/index.tsx b/shared/ui/title/index.tsx index 1f5c8800..f73e2ebe 100644 --- a/shared/ui/title/index.tsx +++ b/shared/ui/title/index.tsx @@ -10,12 +10,13 @@ interface Props { label: string subtitle?: string marginLeft?: CSSProperties['marginLeft'] + className?: string style?: CSSProperties } -const Title = ({ label, subtitle, marginLeft, style }: Props) => { +const Title = ({ label, subtitle, marginLeft, className, style }: Props) => { return ( - <div className={cx('container')} style={{ ...style, marginLeft }}> + <div className={cx('container', className)} style={{ ...style, marginLeft }}> <h1 className={cx('title')}>{label}</h1> {subtitle && <p className={cx('sub-title')}>{subtitle}</p>} </div> From d977282acbb21d491c0b790eda1812f0151c37ae Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 16:10:49 +0900 Subject: [PATCH 504/648] =?UTF-8?q?fix:=20=EB=9E=9C=EB=94=A9=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B0=94=EB=A1=9C=20=ED=95=A8=EA=BB=98?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20(#207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_ui/hero-section/index.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/(landing)/(home)/_ui/hero-section/index.tsx b/app/(landing)/(home)/_ui/hero-section/index.tsx index 8934b0a2..8c2a4af0 100644 --- a/app/(landing)/(home)/_ui/hero-section/index.tsx +++ b/app/(landing)/(home)/_ui/hero-section/index.tsx @@ -4,6 +4,7 @@ import Logo from '@/public/images/logo.svg' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' +import { useAuthStore } from '@/shared/stores/use-auth-store' import { LinkButton } from '@/shared/ui/link-button' import styles from './styles.module.scss' @@ -11,6 +12,8 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const HeroSection = () => { + const user = useAuthStore((state) => state.user) + return ( <section className={cx('section')}> <h1 className={cx('title')}> @@ -20,9 +23,11 @@ const HeroSection = () => { <br /> <Logo className={cx('logo')} /> 인베스트메틱에서! </h1> - <LinkButton href={PATH.SIGN_UP_USER_TYPE} variant="filled" className={cx('button')}> - 바로 함께하기 - </LinkButton> + {!user?.userId && ( + <LinkButton href={PATH.SIGN_UP_USER_TYPE} variant="filled" className={cx('button')}> + 바로 함께하기 + </LinkButton> + )} </section> ) } From db7598e25541f0d6be965a8e7897f21d66570680 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 16:21:15 +0900 Subject: [PATCH 505/648] =?UTF-8?q?rename:=20check-duplicate=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20shared=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/_hooks/custom/use-signup-email.ts | 3 ++- app/(landing)/signup/_hooks/custom/use-signup-form.ts | 2 +- shared/api/.gitkeep | 0 {app/(landing)/signup/_api => shared/api}/check-duplicate.ts | 0 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 shared/api/.gitkeep rename {app/(landing)/signup/_api => shared/api}/check-duplicate.ts (100%) diff --git a/app/(landing)/signup/_hooks/custom/use-signup-email.ts b/app/(landing)/signup/_hooks/custom/use-signup-email.ts index 5b25752f..a4e45aa3 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-email.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-email.ts @@ -1,6 +1,7 @@ import { Dispatch, SetStateAction, useState } from 'react' -import { checkEmailDuplicate } from '../../_api/check-duplicate' +import { checkEmailDuplicate } from '@/shared/api/check-duplicate' + import { getEmailAuthenticationResult, requestEmailAuthentication } from '../../_api/email-auth' import { SIGNUP_ERROR_MESSAGES } from '../../_constants/signup' import { diff --git a/app/(landing)/signup/_hooks/custom/use-signup-form.ts b/app/(landing)/signup/_hooks/custom/use-signup-form.ts index 84fff937..9e82f61e 100644 --- a/app/(landing)/signup/_hooks/custom/use-signup-form.ts +++ b/app/(landing)/signup/_hooks/custom/use-signup-form.ts @@ -2,9 +2,9 @@ import { ChangeEvent, useState } from 'react' import { useRouter } from 'next/navigation' +import { checkNicknameDuplicate, checkPhoneDuplicate } from '@/shared/api/check-duplicate' import { PATH } from '@/shared/constants/path' -import { checkNicknameDuplicate, checkPhoneDuplicate } from '../../_api/check-duplicate' import { signup } from '../../_api/signup' import { SIGNUP_ERROR_MESSAGES } from '../../_constants/signup' import { getUserTypeCookie, setNicknameCookie } from '../../_lib/cookies' diff --git a/shared/api/.gitkeep b/shared/api/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/(landing)/signup/_api/check-duplicate.ts b/shared/api/check-duplicate.ts similarity index 100% rename from app/(landing)/signup/_api/check-duplicate.ts rename to shared/api/check-duplicate.ts From b5f3783096d36201336185fb5cbcde8e19ee6dd0 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 16:26:25 +0900 Subject: [PATCH 506/648] =?UTF-8?q?design:=20=EB=AA=A9=EB=A1=9D=20grid=20c?= =?UTF-8?q?olumn=20=EC=82=AC=EC=9D=B4=EC=A6=88=20=EC=88=98=EC=A0=95=20(#20?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/list-header/styles.module.scss | 4 ++-- app/(dashboard)/_ui/strategies-item/styles.module.scss | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/_ui/list-header/styles.module.scss b/app/(dashboard)/_ui/list-header/styles.module.scss index b7f9ddd2..5efb4b05 100644 --- a/app/(dashboard)/_ui/list-header/styles.module.scss +++ b/app/(dashboard)/_ui/list-header/styles.module.scss @@ -1,10 +1,10 @@ .container { display: grid; - grid-template-columns: 1.6fr 1.2fr repeat(3, 0.6fr) 0.4fr; + grid-template-columns: 1.5fr 1.2fr 0.8fr repeat(2, 0.6fr) 0.4fr; width: 100%; height: 42px; margin: 20px 0 10px; - + grid-gap: 4px; &.my { grid-template-columns: 2.5fr 2.4fr 1.5fr 1.1fr 1.3fr 1fr 1fr; } diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index 12278382..5374efc8 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -1,11 +1,12 @@ .container { margin-bottom: 24px; display: grid; - grid-template-columns: 1.6fr 1.2fr repeat(3, 0.6fr) 0.4fr; + grid-template-columns: 1.5fr 1.2fr 0.8fr repeat(2, 0.6fr) 0.4fr; height: 158px; width: 100%; align-items: center; background-color: $color-white; + grid-gap: 4px; &.my { grid-template-columns: 2.5fr 2.4fr 1.5fr 1.1fr 1.3fr 1fr 1fr; } @@ -44,11 +45,13 @@ .summary { padding-left: 30px; + overflow: hidden; * { margin: 4px 0; } .title { @include typo-h4; + @include ellipsis(1); } .trader-profile { display: flex; From 65bdca11adbfa0509a9a9a38d97e70e0f3851497 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 16:27:31 +0900 Subject: [PATCH 507/648] =?UTF-8?q?fix:=20axiosInstance=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_api/get-strategies-search.ts | 4 ++-- app/(dashboard)/strategies/_api/get-subscribe.ts | 4 ++-- app/(dashboard)/strategies/_api/post-strategies.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/strategies/_api/get-strategies-search.ts b/app/(dashboard)/strategies/_api/get-strategies-search.ts index 84a238db..2646df0d 100644 --- a/app/(dashboard)/strategies/_api/get-strategies-search.ts +++ b/app/(dashboard)/strategies/_api/get-strategies-search.ts @@ -1,8 +1,8 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' const getStrategiesSearch = async () => { try { - const response = await axios.get('api/strategies/search') + const response = await axiosInstance.get('api/strategies/search') return response.data.result } catch (err) { console.error(err) diff --git a/app/(dashboard)/strategies/_api/get-subscribe.ts b/app/(dashboard)/strategies/_api/get-subscribe.ts index 0191cc15..61153b01 100644 --- a/app/(dashboard)/strategies/_api/get-subscribe.ts +++ b/app/(dashboard)/strategies/_api/get-subscribe.ts @@ -1,8 +1,8 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' const getSubscribe = async (strategyId: number) => { try { - const response = await axios.get(`/api/strategies/${strategyId}/subscribe?userId=3`) + const response = await axiosInstance.get(`/api/strategies/${strategyId}/subscribe?userId=3`) return response.data.isSuccess } catch (err) { console.error(err) diff --git a/app/(dashboard)/strategies/_api/post-strategies.ts b/app/(dashboard)/strategies/_api/post-strategies.ts index 47118e52..3cc5563b 100644 --- a/app/(dashboard)/strategies/_api/post-strategies.ts +++ b/app/(dashboard)/strategies/_api/post-strategies.ts @@ -1,10 +1,10 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { SearchTermsModel } from '../_ui/search-bar/_type/search' const postStrategies = async (page: number, size: number, searchTerms: SearchTermsModel) => { try { - const response = await axios.post( + const response = await axiosInstance.post( `/api/strategies/search?page=${page}&size=${size}`, searchTerms ) From cf6afde3205e7c316013ff4efbc962ef4ffa31dc Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Tue, 3 Dec 2024 16:28:28 +0900 Subject: [PATCH 508/648] =?UTF-8?q?bug:=20=EC=A0=84=EB=9E=B5=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EB=B0=94=20=EC=83=81=ED=83=9C=20=EB=B0=8F=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20=ED=83=80=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/search-bar/_store/use-searching-item-store.ts | 4 +++- app/(dashboard)/strategies/_ui/search-bar/index.tsx | 10 +++++----- app/(dashboard)/strategies/_ui/strategy-list/index.tsx | 10 ++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts index dbca850f..8f78db4c 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts +++ b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts @@ -76,7 +76,9 @@ const useSearchingItemStore = create<StateModel & ActionsModel>((set, get) => ({ }, })), - resetState: () => set(() => ({ searchTerms: { ...initialState }, errOptions: [] })), + resetState: () => { + set(() => ({ searchTerms: { ...initialState }, errOptions: null })) + }, validateRangeValues: () => { const { searchTerms } = get() diff --git a/app/(dashboard)/strategies/_ui/search-bar/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/index.tsx index 3becbbef..0b6055bf 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/index.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/index.tsx @@ -41,17 +41,17 @@ const SearchBarContainer = () => { } } - const onReset = () => { - resetState() + const onReset = async () => { + await resetState() if (searchRef.current) { searchRef.current.value = '' } refetch() } - const onSearch = () => { - validateRangeValues() - if (errOptions === null) { + const onSearch = async () => { + await validateRangeValues() + if (errOptions === null || errOptions.length === 0) { refetch() } } diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index 12f33b5c..70c7a9ea 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -1,5 +1,7 @@ 'use client' +import { useEffect } from 'react' + import ListHeader from '@/app/(dashboard)/_ui/list-header' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import classNames from 'classnames/bind' @@ -14,6 +16,8 @@ import usePostStrategies from '../../_hooks/query/use-post-strategies' import useSearchingItemStore from '../search-bar/_store/use-searching-item-store' import styles from './styles.module.scss' +/* eslint-disable react-hooks/exhaustive-deps */ + const cx = classNames.bind(styles) const StrategyList = () => { @@ -22,7 +26,13 @@ const StrategyList = () => { pageSize: STRATEGIES_PAGE_COUNT, }) const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const { resetState } = useSearchingItemStore((state) => state.actions) const { data } = usePostStrategies({ page, size, searchTerms }) + + useEffect(() => { + resetState() + }, []) + const strategiesData = data?.content as StrategiesModel[] const totalPages = (data?.totalPages as number) || null From c43d508ac2c7669d8730e3981015340b88b1563e Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 3 Dec 2024 17:09:37 +0900 Subject: [PATCH 509/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EA=B4=80=EB=A6=AC=20patch=20api=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20(#109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/users/_api/get-admin-users.ts | 2 +- app/admin/users/_api/patch-user-role.ts | 23 ++++++ app/admin/users/_api/set-table-body.tsx | 20 +++++ .../users/_hooks/query/use-admin-users.ts | 2 +- .../users/_hooks/query/use-patch-user-role.ts | 17 ++++ .../users/_hooks/use-user-search-page.ts | 34 ++++++++ app/admin/users/_hooks/use-user-search.ts | 16 ---- app/admin/users/_ui/role-select.tsx | 37 +++++++++ app/admin/users/constants.ts | 26 ++++++ app/admin/users/page.tsx | 79 ++++++++++--------- app/admin/users/types.ts | 12 ++- 11 files changed, 209 insertions(+), 59 deletions(-) create mode 100644 app/admin/users/_api/patch-user-role.ts create mode 100644 app/admin/users/_api/set-table-body.tsx create mode 100644 app/admin/users/_hooks/query/use-patch-user-role.ts create mode 100644 app/admin/users/_hooks/use-user-search-page.ts delete mode 100644 app/admin/users/_hooks/use-user-search.ts create mode 100644 app/admin/users/_ui/role-select.tsx create mode 100644 app/admin/users/constants.ts diff --git a/app/admin/users/_api/get-admin-users.ts b/app/admin/users/_api/get-admin-users.ts index d5f94102..c6e3bb02 100644 --- a/app/admin/users/_api/get-admin-users.ts +++ b/app/admin/users/_api/get-admin-users.ts @@ -4,7 +4,7 @@ import { AdminUsersResponeseModel } from '../types' interface ArgModel { role: string - condition: string + condition: string | null keyword: string | null page?: number size?: number diff --git a/app/admin/users/_api/patch-user-role.ts b/app/admin/users/_api/patch-user-role.ts new file mode 100644 index 00000000..993ebd5d --- /dev/null +++ b/app/admin/users/_api/patch-user-role.ts @@ -0,0 +1,23 @@ +import axiosInstance from '@/shared/api/axios' + +import { AdminPatchUserRoleType, PatchUserRoleResponeseModel } from '../types' + +const patchAdminUserRole = async (userId: number, newRole: AdminPatchUserRoleType) => { + try { + const res = await axiosInstance.patch<PatchUserRoleResponeseModel>( + `/api/admin/users/${userId}/role`, + { + newRole, + } + ) + + if (!res.data.isSuccess) throw new Error('Error with code' + res.data.code) + + return res.data + } catch (err) { + console.error(err) + throw err + } +} + +export default patchAdminUserRole diff --git a/app/admin/users/_api/set-table-body.tsx b/app/admin/users/_api/set-table-body.tsx new file mode 100644 index 00000000..5fd06ef6 --- /dev/null +++ b/app/admin/users/_api/set-table-body.tsx @@ -0,0 +1,20 @@ +import Avatar from '@/shared/ui/avatar' + +import RoleSelect from '../_ui/role-select' +import { AdminUserInfoModel } from '../types' + +const setTableBody = (data: AdminUserInfoModel[]) => + data.map((data, idx) => { + return [ + idx + 1, + <Avatar src={data?.imageUrl ?? undefined} key={data.userId} />, + data.userName, + data.nickname, + data.email, + data.phone, + <RoleSelect data={data} key={data.userId} />, + <button key={data.userId}>123</button>, + ] + }) + +export default setTableBody diff --git a/app/admin/users/_hooks/query/use-admin-users.ts b/app/admin/users/_hooks/query/use-admin-users.ts index 37847a67..a6771370 100644 --- a/app/admin/users/_hooks/query/use-admin-users.ts +++ b/app/admin/users/_hooks/query/use-admin-users.ts @@ -4,7 +4,7 @@ import getAdminUsers from '../../_api/get-admin-users' interface ArgModel { role: string - condition: string + condition: string | null keyword: string | null page?: number size?: number diff --git a/app/admin/users/_hooks/query/use-patch-user-role.ts b/app/admin/users/_hooks/query/use-patch-user-role.ts new file mode 100644 index 00000000..b163f174 --- /dev/null +++ b/app/admin/users/_hooks/query/use-patch-user-role.ts @@ -0,0 +1,17 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import patchAdminUserRole from '../../_api/patch-user-role' +import { AdminPatchUserRoleType } from '../../types' + +const usePatchUserRole = (userId: number, newRole: AdminPatchUserRoleType) => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => patchAdminUserRole(userId, newRole), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['adminUsers'] }) + }, + }) +} + +export default usePatchUserRole diff --git a/app/admin/users/_hooks/use-user-search-page.ts b/app/admin/users/_hooks/use-user-search-page.ts new file mode 100644 index 00000000..0335a970 --- /dev/null +++ b/app/admin/users/_hooks/use-user-search-page.ts @@ -0,0 +1,34 @@ +import { useState } from 'react' + +import { searchOptions, tabs } from '../constants' + +const useUserSearchPage = () => { + const [select, setSelect] = useState(searchOptions[0].value) + const [activeTab, setActiveTab] = useState(tabs[0].id) + const [inputValue, setInputValue] = useState('') + const [keyword, setKeyword] = useState('') + const [condition, setCondition] = useState<string | null>(null) + + const setConditionAndKeyword = () => { + setKeyword(inputValue) + setCondition(select) + } + + return { + searchOptions, + tabs, + select, + setSelect, + activeTab, + setActiveTab, + inputValue, + setInputValue, + keyword, + setKeyword, + condition, + setCondition, + setConditionAndKeyword, + } +} + +export default useUserSearchPage diff --git a/app/admin/users/_hooks/use-user-search.ts b/app/admin/users/_hooks/use-user-search.ts deleted file mode 100644 index eed85d33..00000000 --- a/app/admin/users/_hooks/use-user-search.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { DropdownOptionModel } from '@/shared/ui/dropdown/types' - -const useUserSearch = () => { - const searchOptions: DropdownOptionModel[] = [ - { label: '닉네임', value: 'NICKNAME' }, - { label: '이메일', value: 'EMAIL' }, - { label: '이름', value: 'NAME' }, - { label: '핸드폰 번호', value: 'PHONE' }, - ] - - return { - searchOptions, - } -} - -export default useUserSearch diff --git a/app/admin/users/_ui/role-select.tsx b/app/admin/users/_ui/role-select.tsx new file mode 100644 index 00000000..7a0c870a --- /dev/null +++ b/app/admin/users/_ui/role-select.tsx @@ -0,0 +1,37 @@ +'use client' + +import { useState } from 'react' + +import Select from '@/shared/ui/select' + +import usePatchUserRole from '../_hooks/query/use-patch-user-role' +import { InvestorOptions, TraderOptions } from '../constants' +import { AdminPatchUserRoleType, AdminUserInfoModel, AdminUserRoleType } from '../types' + +interface Props { + data: AdminUserInfoModel +} + +const RoleSelect = ({ data }: Props) => { + const { userId, role } = data + const castAdmin = (role: AdminUserRoleType) => + role === 'INVESTOR_ADMIN' || role === 'TRADER_ADMIN' ? 'ADMIN' : role + const [value, setValue] = useState<AdminPatchUserRoleType>(castAdmin(role)) + const options = role.includes('TRADER') ? TraderOptions : InvestorOptions + + const { mutate } = usePatchUserRole(userId, value) + + return ( + <Select + value={role} + onChange={(v) => { + setValue(castAdmin(v as AdminUserRoleType)) + mutate() + }} + options={options} + key={userId} + /> + ) +} + +export default RoleSelect diff --git a/app/admin/users/constants.ts b/app/admin/users/constants.ts new file mode 100644 index 00000000..71354f18 --- /dev/null +++ b/app/admin/users/constants.ts @@ -0,0 +1,26 @@ +import { DropdownOptionModel } from '@/shared/ui/dropdown/types' +import { TabItemModel } from '@/shared/ui/tabs' + +export const searchOptions: DropdownOptionModel[] = [ + { label: '닉네임', value: 'NICKNAME' }, + { label: '이메일', value: 'EMAIL' }, + { label: '이름', value: 'NAME' }, + { label: '핸드폰 번호', value: 'PHONE' }, +] + +export const tabs: Array<TabItemModel> = [ + { label: '모든 회원', id: 'ALL' }, + { label: '일반', id: 'INVESTOR' }, + { label: '트레이더', id: 'TRADER' }, + { label: '관리자', id: 'ADMIN' }, +] + +export const InvestorOptions: DropdownOptionModel[] = [ + { label: '일반', value: 'INVESTOR' }, + { label: '관리자', value: 'INVESTOR_ADMIN' }, +] + +export const TraderOptions: DropdownOptionModel[] = [ + { label: '트레이더', value: 'TRADER' }, + { label: '관리자', value: 'TRADER_ADMIN' }, +] diff --git a/app/admin/users/page.tsx b/app/admin/users/page.tsx index 03a1a995..84155f8e 100644 --- a/app/admin/users/page.tsx +++ b/app/admin/users/page.tsx @@ -1,60 +1,51 @@ 'use client' -// TODO: ssr -import { useState } from 'react' +import { Suspense } from 'react' import classNames from 'classnames/bind' -import Avatar from '@/shared/ui/avatar' import Pagination from '@/shared/ui/pagination' import { SearchInput } from '@/shared/ui/search-input' import Select from '@/shared/ui/select' import VerticalTable from '@/shared/ui/table/vertical' -import Tabs, { TabItemModel } from '@/shared/ui/tabs' +import Tabs from '@/shared/ui/tabs' import Title from '@/shared/ui/title' import AdminContentsHeader from '../_ui/admin-header' +import setTableBody from './_api/set-table-body' import useAdminUsers from './_hooks/query/use-admin-users' -import useUserSearch from './_hooks/use-user-search' +import useUserSearch from './_hooks/use-user-search-page' import styles from './page.module.scss' const cx = classNames.bind(styles) -const tabs: Array<TabItemModel> = [ - { label: '모든 회원', id: 'ALL' }, - { label: '일반', id: 'INVESTOR' }, - { label: '트레이더', id: 'TRADER' }, - { label: '관리자', id: 'ADMIN' }, -] const AdminUsersPage = () => { - const { searchOptions } = useUserSearch() - const [select, setSelect] = useState(searchOptions[0].value) - const [activeTab, setActiveTab] = useState(tabs[0].id) - const [keyword, setKeyword] = useState('') - const { isLoading, data } = useAdminUsers({ role: activeTab, condition: select, keyword }) + const { + searchOptions, + tabs, + select, + setSelect, + activeTab, + setActiveTab, + inputValue, + setInputValue, + keyword, + condition, + setConditionAndKeyword, + } = useUserSearch() + + const { isLoading, data } = useAdminUsers({ role: activeTab, condition, keyword }) if (isLoading || !data) return null console.log('data', data) - const tableBody = data.content.map((data, idx) => [ - idx + 1, - <Avatar src={data?.imageUrl ?? undefined} key={data.userId} />, - data.userName, - data.nickname, - data.email, - data.phone, - data.role, - <button key={data.userId}>123</button>, - ]) - return ( <> <Title label="회원 관리" className={cx('title')} /> <section className={cx('container')}> <Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} /> <AdminContentsHeader - // TODO: 실제 개수로 바인딩 Left={ <span> 총 <span className={cx('color-primary-500')}>{data?.totalElements}</span>명 @@ -73,18 +64,32 @@ const AdminUsersPage = () => { setSelect(v as string) }} /> - <SearchInput value={keyword} onChange={(e) => setKeyword(e.target.value)} /> + <SearchInput + value={inputValue} + onChange={(e) => setInputValue(e.target.value)} + onSearchIconClick={setConditionAndKeyword} + /> </div> } /> - <VerticalTable - tableHead={['No.', '프로필', '이름', '닉네임', '이메일', '전화번호', '회원분류', '탈퇴']} - tableBody={data?.content ? tableBody : []} - countPerPage={10} - currentPage={1} - /> - {/* TODO: 실제 값으로 추가 */} - <Pagination currentPage={1} maxPage={data?.totalPages} onPageChange={() => {}} /> + <Suspense fallback={<div>loaad...</div>}> + <VerticalTable + tableHead={[ + 'No.', + '프로필', + '이름', + '닉네임', + '이메일', + '전화번호', + '회원분류', + '탈퇴', + ]} + tableBody={data?.content ? setTableBody(data.content) : []} + countPerPage={10} + currentPage={1} + /> + <Pagination currentPage={1} maxPage={data?.totalPages} onPageChange={() => {}} /> + </Suspense> </section> </> ) diff --git a/app/admin/users/types.ts b/app/admin/users/types.ts index 4ead871d..4c2ca097 100644 --- a/app/admin/users/types.ts +++ b/app/admin/users/types.ts @@ -4,9 +4,11 @@ interface UsersResponseBaseModel<T extends boolean> { code: T extends false ? number : null } -type RoleType = 'TRADER' | 'TRADER_ADMIN' | 'INVESTOR' | 'INVESTOR' +export type AdminUserRoleType = 'TRADER' | 'TRADER_ADMIN' | 'INVESTOR' | 'INVESTOR_ADMIN' -interface UserInfoModel { +export type AdminPatchUserRoleType = 'ADMIN' | 'TRADER' | 'INVESTOR' + +export interface AdminUserInfoModel { userId: number userName: string email: string @@ -14,12 +16,12 @@ interface UserInfoModel { nickname: string phone: string infoAgreement: boolean - role: RoleType + role: AdminUserRoleType } export interface AdminUsersResponeseModel extends UsersResponseBaseModel<boolean> { result: { - content: Array<UserInfoModel> + content: Array<AdminUserInfoModel> page: number size: number totalElements: number @@ -28,5 +30,7 @@ export interface AdminUsersResponeseModel extends UsersResponseBaseModel<boolean last: boolean } } +// eslint-disable-next-line +export interface PatchUserRoleResponeseModel extends UsersResponseBaseModel<boolean> {} export type UserRoleType = '모든 회원' | '일반' | '트레이더' | '관리자' From 6ce88a3a030484fd735c461706a13a1b628aeaa5 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 3 Dec 2024 17:53:39 +0900 Subject: [PATCH 510/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EA=B4=80=EB=A6=AC=20=20(=EB=AC=B4?= =?UTF-8?q?=EC=9D=98=EB=AF=B8=ED=95=9C=20suspense=20=EC=A0=9C=EA=B1=B0=20(?= =?UTF-8?q?#109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/users/page.tsx | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/app/admin/users/page.tsx b/app/admin/users/page.tsx index 84155f8e..2d1e2cbe 100644 --- a/app/admin/users/page.tsx +++ b/app/admin/users/page.tsx @@ -1,7 +1,5 @@ 'use client' -import { Suspense } from 'react' - import classNames from 'classnames/bind' import Pagination from '@/shared/ui/pagination' @@ -72,24 +70,13 @@ const AdminUsersPage = () => { </div> } /> - <Suspense fallback={<div>loaad...</div>}> - <VerticalTable - tableHead={[ - 'No.', - '프로필', - '이름', - '닉네임', - '이메일', - '전화번호', - '회원분류', - '탈퇴', - ]} - tableBody={data?.content ? setTableBody(data.content) : []} - countPerPage={10} - currentPage={1} - /> - <Pagination currentPage={1} maxPage={data?.totalPages} onPageChange={() => {}} /> - </Suspense> + <VerticalTable + tableHead={['No.', '프로필', '이름', '닉네임', '이메일', '전화번호', '회원분류', '탈퇴']} + tableBody={data?.content ? setTableBody(data.content) : []} + countPerPage={10} + currentPage={1} + /> + <Pagination currentPage={1} maxPage={data?.totalPages} onPageChange={() => {}} /> </section> </> ) From e7e03c36e350732d9690999254e447c045c69e9d Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 3 Dec 2024 18:29:35 +0900 Subject: [PATCH 511/648] fix: page path name (#162) --- .../_ui/shared/_api/get-presigned-url.ts | 2 +- .../_api/upload-file-with-presigned-url.ts | 0 .../shared/_hooks/use-category-icon-post.ts} | 4 +- .../_ui/shared/file-input/index.tsx | 0 .../_ui/shared/file-input/styles.module.scss | 0 .../_ui/shared/manage-table/constant/index.ts | 0 .../shared/manage-table/file-input/index.tsx | 0 .../_ui/shared/manage-table/index.tsx | 0 .../shared/manage-table/styles.module.scss | 0 .../manage-table/utils/add-index-to-data.tsx | 0 .../_ui/stock/stock-manage/_api/get-stocks.ts | 0 .../_api/toggle-stock-active-state.ts | 0 .../_hooks/query/use-stocks-data.ts | 0 .../query/use-toggle-trade-active-state.ts | 0 .../_ui/active-stock-manage-table.tsx | 0 .../_ui/inactive-stock-manage-table.tsx | 0 .../_ui/stock-active-state-toggle-button.tsx | 0 .../_ui/stock-post-button/index.tsx | 4 +- .../_ui/stock-post-button/styles.module.scss | 0 .../_ui/stock/stock-manage/index.tsx | 0 .../_ui/stock/stock-manage/styles.module.scss | 0 .../_ui/stock/stock-manage/types.ts | 0 .../_ui/trade/trade-manage/_api/get-trades.ts | 0 .../_api/toggle-trade-active-state.ts | 0 .../query/use-toggle-trade-active-state.ts | 0 .../_hooks/query/use-trades-data.ts | 0 .../_ui/active-trade-manage-table.tsx | 0 .../_ui/inactive-trade-manage-table.tsx | 0 .../_ui/trade-active-state-toggle-button.tsx | 0 .../_ui/trade-post-button/index.tsx | 4 +- .../_ui/trade-post-button/styles.module.scss | 0 .../_ui/trade/trade-manage/index.tsx | 0 .../_ui/trade/trade-manage/styles.module.scss | 0 .../_ui/trade/trade-manage/types.ts | 0 .../{strategies => category}/page.module.scss | 0 app/admin/category/page.tsx | 36 ++++++++++++++++- app/admin/strategies/page.tsx | 40 ++----------------- 37 files changed, 45 insertions(+), 45 deletions(-) rename app/admin/{strategies => category}/_ui/shared/_api/get-presigned-url.ts (92%) rename app/admin/{strategies => category}/_ui/shared/_api/upload-file-with-presigned-url.ts (100%) rename app/admin/{strategies/_ui/shared/_hooks/use-strategy-icon-post.ts => category/_ui/shared/_hooks/use-category-icon-post.ts} (95%) rename app/admin/{strategies => category}/_ui/shared/file-input/index.tsx (100%) rename app/admin/{strategies => category}/_ui/shared/file-input/styles.module.scss (100%) rename app/admin/{strategies => category}/_ui/shared/manage-table/constant/index.ts (100%) rename app/admin/{strategies => category}/_ui/shared/manage-table/file-input/index.tsx (100%) rename app/admin/{strategies => category}/_ui/shared/manage-table/index.tsx (100%) rename app/admin/{strategies => category}/_ui/shared/manage-table/styles.module.scss (100%) rename app/admin/{strategies => category}/_ui/shared/manage-table/utils/add-index-to-data.tsx (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_api/get-stocks.ts (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx (93%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/index.tsx (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/styles.module.scss (100%) rename app/admin/{strategies => category}/_ui/stock/stock-manage/types.ts (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_api/get-trades.ts (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx (93%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/index.tsx (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/styles.module.scss (100%) rename app/admin/{strategies => category}/_ui/trade/trade-manage/types.ts (100%) rename app/admin/{strategies => category}/page.module.scss (100%) diff --git a/app/admin/strategies/_ui/shared/_api/get-presigned-url.ts b/app/admin/category/_ui/shared/_api/get-presigned-url.ts similarity index 92% rename from app/admin/strategies/_ui/shared/_api/get-presigned-url.ts rename to app/admin/category/_ui/shared/_api/get-presigned-url.ts index 217ca203..ab9b70a5 100644 --- a/app/admin/strategies/_ui/shared/_api/get-presigned-url.ts +++ b/app/admin/category/_ui/shared/_api/get-presigned-url.ts @@ -1,7 +1,7 @@ import axios from 'axios' import { PresignedUrlResponseModel } from '../../trade/trade-manage/types' -import { DomainType } from '../_hooks/use-strategy-icon-post' +import { DomainType } from '../_hooks/use-category-icon-post' const getPresignedUrl = async ( typeName: string, diff --git a/app/admin/strategies/_ui/shared/_api/upload-file-with-presigned-url.ts b/app/admin/category/_ui/shared/_api/upload-file-with-presigned-url.ts similarity index 100% rename from app/admin/strategies/_ui/shared/_api/upload-file-with-presigned-url.ts rename to app/admin/category/_ui/shared/_api/upload-file-with-presigned-url.ts diff --git a/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts b/app/admin/category/_ui/shared/_hooks/use-category-icon-post.ts similarity index 95% rename from app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts rename to app/admin/category/_ui/shared/_hooks/use-category-icon-post.ts index c8ee9582..53f457ba 100644 --- a/app/admin/strategies/_ui/shared/_hooks/use-strategy-icon-post.ts +++ b/app/admin/category/_ui/shared/_hooks/use-category-icon-post.ts @@ -5,7 +5,7 @@ import uploadFileWithPresignedUrl from '../_api/upload-file-with-presigned-url' export type DomainType = 'trade' | 'stock' -const useStrategyIconPost = (domain: DomainType) => { +const useCategoryIconPost = (domain: DomainType) => { const [imagePreview, setImagePreview] = useState('') const [typeName, setTypeName] = useState('') const [image, setImage] = useState<File | null>(null) @@ -75,4 +75,4 @@ const useStrategyIconPost = (domain: DomainType) => { } } -export default useStrategyIconPost +export default useCategoryIconPost diff --git a/app/admin/strategies/_ui/shared/file-input/index.tsx b/app/admin/category/_ui/shared/file-input/index.tsx similarity index 100% rename from app/admin/strategies/_ui/shared/file-input/index.tsx rename to app/admin/category/_ui/shared/file-input/index.tsx diff --git a/app/admin/strategies/_ui/shared/file-input/styles.module.scss b/app/admin/category/_ui/shared/file-input/styles.module.scss similarity index 100% rename from app/admin/strategies/_ui/shared/file-input/styles.module.scss rename to app/admin/category/_ui/shared/file-input/styles.module.scss diff --git a/app/admin/strategies/_ui/shared/manage-table/constant/index.ts b/app/admin/category/_ui/shared/manage-table/constant/index.ts similarity index 100% rename from app/admin/strategies/_ui/shared/manage-table/constant/index.ts rename to app/admin/category/_ui/shared/manage-table/constant/index.ts diff --git a/app/admin/strategies/_ui/shared/manage-table/file-input/index.tsx b/app/admin/category/_ui/shared/manage-table/file-input/index.tsx similarity index 100% rename from app/admin/strategies/_ui/shared/manage-table/file-input/index.tsx rename to app/admin/category/_ui/shared/manage-table/file-input/index.tsx diff --git a/app/admin/strategies/_ui/shared/manage-table/index.tsx b/app/admin/category/_ui/shared/manage-table/index.tsx similarity index 100% rename from app/admin/strategies/_ui/shared/manage-table/index.tsx rename to app/admin/category/_ui/shared/manage-table/index.tsx diff --git a/app/admin/strategies/_ui/shared/manage-table/styles.module.scss b/app/admin/category/_ui/shared/manage-table/styles.module.scss similarity index 100% rename from app/admin/strategies/_ui/shared/manage-table/styles.module.scss rename to app/admin/category/_ui/shared/manage-table/styles.module.scss diff --git a/app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx b/app/admin/category/_ui/shared/manage-table/utils/add-index-to-data.tsx similarity index 100% rename from app/admin/strategies/_ui/shared/manage-table/utils/add-index-to-data.tsx rename to app/admin/category/_ui/shared/manage-table/utils/add-index-to-data.tsx diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts b/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_api/get-stocks.ts rename to app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts diff --git a/app/admin/strategies/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts b/app/admin/category/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts rename to app/admin/category/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts rename to app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts diff --git a/app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts b/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts rename to app/admin/category/_ui/stock/stock-manage/_hooks/query/use-toggle-trade-active-state.ts diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx rename to app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx rename to app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx rename to app/admin/category/_ui/stock/stock-manage/_ui/stock-active-state-toggle-button.tsx diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx similarity index 93% rename from app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx rename to app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx index 203b4530..44419b91 100644 --- a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx +++ b/app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx @@ -2,7 +2,7 @@ import { FormEvent } from 'react' -import FileInput from '@/app/admin/strategies/_ui/shared/file-input' +import FileInput from '@/app/admin/category/_ui/shared/file-input' import { RegisterIcon } from '@/public/icons' import { useQueryClient } from '@tanstack/react-query' import classNames from 'classnames/bind' @@ -12,7 +12,7 @@ import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import Modal from '@/shared/ui/modal' -import useStrategyIconPost from '../../../../shared/_hooks/use-strategy-icon-post' +import useStrategyIconPost from '../../../../shared/_hooks/use-category-icon-post' import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss b/app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss rename to app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/styles.module.scss diff --git a/app/admin/strategies/_ui/stock/stock-manage/index.tsx b/app/admin/category/_ui/stock/stock-manage/index.tsx similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/index.tsx rename to app/admin/category/_ui/stock/stock-manage/index.tsx diff --git a/app/admin/strategies/_ui/stock/stock-manage/styles.module.scss b/app/admin/category/_ui/stock/stock-manage/styles.module.scss similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/styles.module.scss rename to app/admin/category/_ui/stock/stock-manage/styles.module.scss diff --git a/app/admin/strategies/_ui/stock/stock-manage/types.ts b/app/admin/category/_ui/stock/stock-manage/types.ts similarity index 100% rename from app/admin/strategies/_ui/stock/stock-manage/types.ts rename to app/admin/category/_ui/stock/stock-manage/types.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_api/get-trades.ts b/app/admin/category/_ui/trade/trade-manage/_api/get-trades.ts similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_api/get-trades.ts rename to app/admin/category/_ui/trade/trade-manage/_api/get-trades.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts b/app/admin/category/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts rename to app/admin/category/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts b/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts rename to app/admin/category/_ui/trade/trade-manage/_hooks/query/use-toggle-trade-active-state.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts b/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts rename to app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx rename to app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx rename to app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx rename to app/admin/category/_ui/trade/trade-manage/_ui/trade-active-state-toggle-button.tsx diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx similarity index 93% rename from app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx rename to app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx index 6dbce0c1..4ac7abc0 100644 --- a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx @@ -2,7 +2,7 @@ import { FormEvent } from 'react' -import FileInput from '@/app/admin/strategies/_ui/shared/file-input' +import FileInput from '@/app/admin/category/_ui/shared/file-input' import { RegisterIcon } from '@/public/icons' import { useQueryClient } from '@tanstack/react-query' import classNames from 'classnames/bind' @@ -12,7 +12,7 @@ import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import Modal from '@/shared/ui/modal' -import useStrategyIconPost from '../../../../shared/_hooks/use-strategy-icon-post' +import useStrategyIconPost from '../../../../shared/_hooks/use-category-icon-post' import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss b/app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss rename to app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/styles.module.scss diff --git a/app/admin/strategies/_ui/trade/trade-manage/index.tsx b/app/admin/category/_ui/trade/trade-manage/index.tsx similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/index.tsx rename to app/admin/category/_ui/trade/trade-manage/index.tsx diff --git a/app/admin/strategies/_ui/trade/trade-manage/styles.module.scss b/app/admin/category/_ui/trade/trade-manage/styles.module.scss similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/styles.module.scss rename to app/admin/category/_ui/trade/trade-manage/styles.module.scss diff --git a/app/admin/strategies/_ui/trade/trade-manage/types.ts b/app/admin/category/_ui/trade/trade-manage/types.ts similarity index 100% rename from app/admin/strategies/_ui/trade/trade-manage/types.ts rename to app/admin/category/_ui/trade/trade-manage/types.ts diff --git a/app/admin/strategies/page.module.scss b/app/admin/category/page.module.scss similarity index 100% rename from app/admin/strategies/page.module.scss rename to app/admin/category/page.module.scss diff --git a/app/admin/category/page.tsx b/app/admin/category/page.tsx index 6c9b9f84..6cfc8fff 100644 --- a/app/admin/category/page.tsx +++ b/app/admin/category/page.tsx @@ -1,5 +1,39 @@ +'use client' + +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import Tabs from '@/shared/ui/tabs' +import Title from '@/shared/ui/title' + +import StockManage from './_ui/stock/stock-manage' +import TradeManage from './_ui/trade/trade-manage' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const tabs = ['종목', '매매유형'] as const + const AdminCategoryPage = () => { - return <></> + const [activeTab, setActiveTab] = useState<(typeof tabs)[number]>('종목') + + return ( + <> + <Title label="종목 및 매매 유형 관리" style={{ margin: '80px 0 26px 12.6px' }} /> + <div className={cx('container')}> + <Tabs + tabs={tabs.map((tab) => ({ + id: tab, + label: tab, + content: tab === '종목' ? <StockManage /> : <TradeManage />, + }))} + activeTab={activeTab} + onTabChange={(tab) => setActiveTab(tab)} + /> + </div> + </> + ) } export default AdminCategoryPage diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index a7b382c3..278e9531 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -1,39 +1,5 @@ -'use client' - -import { useState } from 'react' - -import classNames from 'classnames/bind' - -import Tabs from '@/shared/ui/tabs' -import Title from '@/shared/ui/title' - -import StockManage from './_ui/stock/stock-manage' -import TradeManage from './_ui/trade/trade-manage' -import styles from './page.module.scss' - -const cx = classNames.bind(styles) - -const tabs = ['종목', '매매유형'] as const - -const AdminStrategiesPage = () => { - const [activeTab, setActiveTab] = useState<(typeof tabs)[number]>('종목') - - return ( - <> - <Title label="종목 및 매매 유형 관리" style={{ margin: '80px 0 26px 12.6px' }} /> - <div className={cx('container')}> - <Tabs - tabs={tabs.map((tab) => ({ - id: tab, - label: tab, - content: tab === '종목' ? <StockManage /> : <TradeManage />, - }))} - activeTab={activeTab} - onTabChange={(tab) => setActiveTab(tab)} - /> - </div> - </> - ) +const AdminStrategyPage = () => { + return <></> } -export default AdminStrategiesPage +export default AdminStrategyPage From e47db787f3b9ac52d112bec5f790ead19ad0d120 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Tue, 3 Dec 2024 18:37:13 +0900 Subject: [PATCH 512/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EA=B4=80=EB=A6=AC=20=20(delete=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20api=20=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94=20=ED=9B=84=20=EC=97=B0=EB=8F=99=20=EC=98=88?= =?UTF-8?q?=EC=A0=95=20(#109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/users/_api/delete-user.ts | 18 ++++++++++++ app/admin/users/_api/set-table-body.tsx | 3 +- .../users/_hooks/query/use-delete-user.ts | 16 +++++++++++ app/admin/users/_ui/user-delete-button.tsx | 28 +++++++++++++++++++ app/admin/users/types.ts | 3 ++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 app/admin/users/_api/delete-user.ts create mode 100644 app/admin/users/_hooks/query/use-delete-user.ts create mode 100644 app/admin/users/_ui/user-delete-button.tsx diff --git a/app/admin/users/_api/delete-user.ts b/app/admin/users/_api/delete-user.ts new file mode 100644 index 00000000..255b338c --- /dev/null +++ b/app/admin/users/_api/delete-user.ts @@ -0,0 +1,18 @@ +import axiosInstance from '@/shared/api/axios' + +import { DeleteUserResponeseModel } from '../types' + +const deleteUser = async (userId: number) => { + try { + const res = await axiosInstance.delete<DeleteUserResponeseModel>(`/api/admin/users/${userId}`) + + if (!res.data.isSuccess) throw new Error('Error with code' + res.data.code) + + return res.data + } catch (err) { + console.error(err) + throw err + } +} + +export default deleteUser diff --git a/app/admin/users/_api/set-table-body.tsx b/app/admin/users/_api/set-table-body.tsx index 5fd06ef6..0fdff9fe 100644 --- a/app/admin/users/_api/set-table-body.tsx +++ b/app/admin/users/_api/set-table-body.tsx @@ -1,6 +1,7 @@ import Avatar from '@/shared/ui/avatar' import RoleSelect from '../_ui/role-select' +import UserDeleteButton from '../_ui/user-delete-button' import { AdminUserInfoModel } from '../types' const setTableBody = (data: AdminUserInfoModel[]) => @@ -13,7 +14,7 @@ const setTableBody = (data: AdminUserInfoModel[]) => data.email, data.phone, <RoleSelect data={data} key={data.userId} />, - <button key={data.userId}>123</button>, + <UserDeleteButton userId={data.userId} key={data.userId} />, ] }) diff --git a/app/admin/users/_hooks/query/use-delete-user.ts b/app/admin/users/_hooks/query/use-delete-user.ts new file mode 100644 index 00000000..964563a2 --- /dev/null +++ b/app/admin/users/_hooks/query/use-delete-user.ts @@ -0,0 +1,16 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import deleteUser from '../../_api/delete-user' + +const useDeleteUser = (userId: number) => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => deleteUser(userId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['adminUsers'] }) + }, + }) +} + +export default useDeleteUser diff --git a/app/admin/users/_ui/user-delete-button.tsx b/app/admin/users/_ui/user-delete-button.tsx new file mode 100644 index 00000000..18cf57b5 --- /dev/null +++ b/app/admin/users/_ui/user-delete-button.tsx @@ -0,0 +1,28 @@ +'use client' + +import { Button } from '@/shared/ui/button' + +import useDeleteUser from '../_hooks/query/use-delete-user' + +interface Props { + userId: number +} + +const UserDeleteButton = ({ userId }: Props) => { + // TODO : api 완성되면 연결하기 + // const { mutate, isPending } = useDeleteUser(userId) + + return ( + <Button + variant="filled" + // onClick={() => mutate()} + // disabled={isPending} + size="small" + style={{ padding: '7px 16px' }} + > + 강제삭제 + </Button> + ) +} + +export default UserDeleteButton diff --git a/app/admin/users/types.ts b/app/admin/users/types.ts index 4c2ca097..40cc9fee 100644 --- a/app/admin/users/types.ts +++ b/app/admin/users/types.ts @@ -33,4 +33,7 @@ export interface AdminUsersResponeseModel extends UsersResponseBaseModel<boolean // eslint-disable-next-line export interface PatchUserRoleResponeseModel extends UsersResponseBaseModel<boolean> {} +// eslint-disable-next-line +export interface DeleteUserResponeseModel extends UsersResponseBaseModel<boolean> {} + export type UserRoleType = '모든 회원' | '일반' | '트레이더' | '관리자' From 912482904cc143d4c8eee2bb0b3f9c8d183f922f Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 18:48:10 +0900 Subject: [PATCH 513/648] =?UTF-8?q?design:=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EB=B0=98=EC=9D=91=ED=98=95=20=EC=9E=91=EC=97=85=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/header/index.tsx | 14 ++++- shared/ui/header/styles.module.scss | 98 +++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/shared/ui/header/index.tsx b/shared/ui/header/index.tsx index 0428d2bb..8a1a2bf1 100644 --- a/shared/ui/header/index.tsx +++ b/shared/ui/header/index.tsx @@ -16,7 +16,19 @@ const Header = ({ Left, Right, styles }: Props) => { return ( <header className={cx('container')} style={styles}> {Left} - {Right} + {Right && ( + <> + <div className={cx('right-wrapper')}> + <input type="checkbox" id="menu-trigger" /> + <label htmlFor="menu-trigger"> + <span></span> + <span></span> + <span></span> + </label> + <div className={cx('right-contents-wrapper')}>{Right}</div> + </div> + </> + )} </header> ) } diff --git a/shared/ui/header/styles.module.scss b/shared/ui/header/styles.module.scss index 6fbb1f74..1bfb8fae 100644 --- a/shared/ui/header/styles.module.scss +++ b/shared/ui/header/styles.module.scss @@ -8,4 +8,102 @@ justify-content: space-between; align-items: center; padding: 0 40px; + + @include tablet-md { + padding: 0 20px; + background-color: $color-white; + } +} + +.right-wrapper { + input { + display: none; + + &:checked { + & + label { + span { + &:nth-child(1) { + top: 50%; + transform: rotate(45deg); + } + + &:nth-child(2) { + opacity: 0; + } + + &:nth-child(3) { + top: 50%; + transform: rotate(-45deg); + } + } + } + + & ~ .right-contents-wrapper { + @include tablet-md { + display: flex; + } + } + } + } + + label { + position: relative; + display: none; + width: 32px; + height: 24px; + cursor: pointer; + + @include tablet-md { + display: block; + } + + span { + position: absolute; + left: 0; + width: 100%; + height: 2px; + background-color: $color-black; + transition: 0.3s; + + &:nth-child(1) { + top: 0%; + } + + &:nth-child(2) { + top: 50%; + } + + &:nth-child(3) { + top: 100%; + } + } + } +} + +.right-contents-wrapper { + display: flex; + + @include tablet-md { + position: fixed; + top: 80px; + left: 0; + display: none; + justify-content: center; + flex-direction: column; + width: 100%; + padding: 20px 0; + background-color: $color-white; + transition: transform 0.3s ease; + + div { + flex-direction: column; + align-items: center; + padding: 0 20px; + + a { + width: 100%; + max-width: 400px; + } + } + } } From 37b1fd3b27d323a7c6448c541051d7dde9398a5d Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Wed, 4 Dec 2024 02:13:59 +0900 Subject: [PATCH 514/648] =?UTF-8?q?feat:=20UserInfo,=20UserProfile,=20User?= =?UTF-8?q?Withdraw=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20(#127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/profile/_ui/user-info/index.tsx | 180 ++++++++++++++++++ .../profile/_ui/user-info/styles.module.scss | 135 +++++++++++++ .../my/profile/_ui/user-profile/index.tsx | 34 ++++ .../_ui/user-profile/styles.module.scss | 56 ++++++ .../my/profile/_ui/user-withdraw/index.tsx | 39 ++++ .../_ui/user-withdraw/styles.module.scss | 68 +++++++ app/(dashboard)/my/profile/edit/page.tsx | 11 ++ app/(dashboard)/my/profile/page.tsx | 8 +- public/icons/camera.svg | 3 + public/icons/index.tsx | 1 + shared/constants/path.ts | 1 + shared/styles/stories/icons.stories.tsx | 2 + shared/ui/avatar/avatar.stories.tsx | 4 +- shared/ui/avatar/index.tsx | 2 +- shared/ui/avatar/styles.module.scss | 24 +++ shared/ui/button/button.stories.tsx | 2 +- shared/ui/input/index.tsx | 23 ++- shared/ui/input/input.stories.tsx | 32 +++- shared/ui/input/styles.module.scss | 16 ++ shared/ui/link-button/link-button.stories.tsx | 2 +- shared/ui/page-not-found/index.tsx | 2 +- 21 files changed, 633 insertions(+), 12 deletions(-) create mode 100644 app/(dashboard)/my/profile/_ui/user-info/index.tsx create mode 100644 app/(dashboard)/my/profile/_ui/user-info/styles.module.scss create mode 100644 app/(dashboard)/my/profile/_ui/user-profile/index.tsx create mode 100644 app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss create mode 100644 app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx create mode 100644 app/(dashboard)/my/profile/_ui/user-withdraw/styles.module.scss create mode 100644 app/(dashboard)/my/profile/edit/page.tsx create mode 100644 public/icons/camera.svg diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx new file mode 100644 index 00000000..70baa39d --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -0,0 +1,180 @@ +'use client' + +import { useRouter } from 'next/navigation' + +import { CameraIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import Avatar from '@/shared/ui/avatar' +import { Button } from '@/shared/ui/button' +import { Input } from '@/shared/ui/input' +import { LinkButton } from '@/shared/ui/link-button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isEditable?: boolean +} + +const UserInfo = ({ isEditable = false }: Props) => { + const router = useRouter() + const handleChange = () => {} + const handleDelete = () => {} + const handleNicknameConfirm = () => {} + const handlePhoneConfirm = () => {} + const handleBack = () => { + router.back() + } + const handleSave = () => {} + return ( + <div className={cx('container')}> + <p className={cx('text')}>개인 정보</p> + <div className={cx('line')}></div> + + <div className={cx('content')}> + <div className={cx('content-wrapper')}> + <div className={cx('left-wrapper')}> + <div className={cx('avatar-wrapper')}> + <Avatar size="xxlarge" /> + <div className={cx('camera-wrapper')}> + <CameraIcon className={cx('camera-icon')} onClick={handleChange} /> + </div> + </div> + {isEditable && <Button onClick={handleDelete}>프로필 사진 삭제</Button>} + </div> + + <div className={cx('right-wrapper')}> + {!isEditable && ( + <LinkButton variant="filled" className={cx('edit-button')} href={PATH.EDIT_PROFILE}> + 개인 정보 수정 + </LinkButton> + )} + <div className={cx('first-row')}> + <p className={cx('title')}>이름</p> + <Input + id="name" + name="name" + value={'고양이'} + inputSize="compact" + className={cx('input')} + isWhiteDisabled={!isEditable ? true : undefined} + disabled={isEditable} + /> + </div> + + <div className={cx('row')}> + <div> + <p className={cx('title')}>이메일</p> + <Input + inputSize="compact" + value={'hello@example.com'} + className={cx('input')} + isWhiteDisabled={!isEditable ? true : undefined} + disabled={isEditable} + /> + </div> + <div> + <p className={cx('title')}>휴대전화</p> + <div className={cx('position')}> + <Input + id="phone" + name="phone" + value={'01012345678'} + onChange={() => {}} + className={cx('input')} + inputSize="compact" + isWhiteDisabled={!isEditable ? true : undefined} + /> + + {isEditable && <Button onClick={handlePhoneConfirm}>확인</Button>} + </div> + </div> + </div> + + <div className={cx('row')}> + <div> + <p className={cx('title')}>생년월일</p> + <Input + inputSize="compact" + value={'2004.05.04'} + className={cx('input')} + isWhiteDisabled={!isEditable ? true : undefined} + disabled={isEditable} + /> + </div> + <div> + <p className={cx('title')}>닉네임</p> + <div className={cx('position')}> + <Input + id="nickname" + name="nickname" + inputSize="compact" + value={'고양이귀여워'} + onChange={() => {}} + className={cx('input')} + isWhiteDisabled={!isEditable ? true : undefined} + /> + + {isEditable && <Button onClick={handleNicknameConfirm}>확인</Button>} + </div> + </div> + </div> + + {isEditable && ( + <div className={cx('password-row')}> + <div> + <p className={cx('title')}>비밀번호</p> + <Input + id="password" + name="password" + type="password" + inputSize="compact" + onChange={() => {}} + placeholder="비밀번호를 입력하세요" + className={cx('input')} + errorMessage={''} + /> + </div> + <div> + <p className={cx('title')}>비밀번호 확인</p> + + <Input + id="passwordConfirm" + name="passwordConfirm" + type="password" + onChange={() => {}} + placeholder="한 번 더 입력하세요" + className={cx('input')} + inputSize="compact" + /> + </div> + </div> + )} + {isEditable && ( + <div> + <p className={cx('notification')}> + * 비밀번호는 문자, 숫자 포함 6~20자로 구성되어야 합니다. + </p> + </div> + )} + </div> + </div> + + {isEditable && ( + <div className={cx('button-wrapper')}> + <Button className={cx('left-button')} onClick={handleBack}> + 뒤로가기 + </Button> + <Button className={cx('right-button')} variant="filled" onClick={handleSave}> + 저장하기 + </Button> + </div> + )} + </div> + </div> + ) +} +export default UserInfo diff --git a/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss b/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss new file mode 100644 index 00000000..bcb7d74b --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss @@ -0,0 +1,135 @@ +.container { + background-color: $color-white; + width: 897px; + height: 854px; + padding: 44px 40px; +} + +.text { + @include typo-b1; + margin-bottom: 22px; + color: $color-gray-700; +} + +.line { + width: 100%; + height: 1px; + background-color: $color-gray-300; +} + +.content { + display: flex; + flex-direction: column; + align-items: center; +} + +.content-wrapper { + display: flex; + max-width: 820px; + justify-content: space-between; + margin-top: 44px; +} + +.left-wrapper { + display: flex; + flex-direction: column; + flex: 1; + margin-right: 50px; +} + +.avatar-wrapper { + position: relative; + display: inline-block; + margin-bottom: 35px; + + .camera-wrapper { + position: absolute; + bottom: 40px; + right: 10px; + width: 36px; + height: 36px; + transform: translate(50%, 50%); + background: $color-orange-500; + border-radius: 50%; + border: 4px solid $color-white; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + + .camera-icon { + fill: $color-white; + width: 18px; + height: 18px; + } + } +} + +.right-wrapper { + display: flex; + flex-direction: column; + + .edit-button { + align-self: flex-end; + margin-top: 20px; + } + + .first-row { + display: flex; + flex-direction: column; + margin-bottom: 36px; + } + + .title { + color: $color-gray-800; + margin-bottom: 15px; + @include typo-b3; + } + + .row { + display: flex; + gap: 27px; + align-items: center; + margin-bottom: 36px; + + .input { + flex: 1; + } + } + + .password-row { + display: flex; + gap: 27px; + align-items: center; + } +} + +.button-wrapper { + margin-top: 103px; + display: flex; + justify-content: center; + gap: 32px; + width: 100%; + height: 40px; + + .left-button { + width: 112px; + } + + .right-button { + width: 112px; + } +} + +.notification { + font-size: 12px; + color: $color-gray-600; + margin-top: 8px; + @include typo-c1; +} + +.position { + display: flex; + align-items: center; + gap: 27px; +} diff --git a/app/(dashboard)/my/profile/_ui/user-profile/index.tsx b/app/(dashboard)/my/profile/_ui/user-profile/index.tsx new file mode 100644 index 00000000..4bfd1e9b --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-profile/index.tsx @@ -0,0 +1,34 @@ +import classNames from 'classnames/bind' + +import Avatar from '@/shared/ui/avatar' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + userType: string + name: string + email: string +} + +const UserProfile = ({ userType, name, email }: Props) => { + return ( + <div className={cx('container')}> + <p className={cx('profile-info')}>프로필 정보</p> + <div className={cx('line')}></div> + <div className={cx('content')}> + <div className={cx('left-wrapper')}> + <p className={cx('type')}>{userType}</p> + <p className={cx('name')}>{name}</p> + <p className={cx('email')}>{email}</p> + </div> + <div className={cx('right-wrapper')}> + <Avatar size="xlarge" /> + </div> + </div> + </div> + ) +} + +export default UserProfile diff --git a/app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss b/app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss new file mode 100644 index 00000000..a3aa6eb6 --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss @@ -0,0 +1,56 @@ +.container { + background-color: $color-white; + width: 375px; + height: 280px; + padding: 44px 40px; +} + +.profile-info { + @include typo-b1; + color: $color-gray-700; + margin-bottom: 22px; +} + +.line { + width: 100%; + height: 1px; + background-color: $color-gray-300; +} + +.content { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 44px; +} + +.left-wrapper { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; + gap: 12px; + + .type { + @include typo-b2; + color: $color-orange-500; + } + + .name { + @include typo-h4; + font-weight: bold; + color: $color-gray-800; + } + + .email { + @include typo-b2; + color: $color-gray-400; + } +} + +.right-wrapper { + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; +} diff --git a/app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx b/app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx new file mode 100644 index 00000000..54640f4b --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx @@ -0,0 +1,39 @@ +import { useRouter } from 'next/navigation' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const UserWithdraw = () => { + const router = useRouter() + const handleWithdraw = () => {} + const handleBack = () => { + router.back() + } + return ( + <div className={cx('container')}> + <p className={cx('title')}>회원탈퇴</p> + <div className={cx('line')}></div> + <p className={cx('message')}>회원 탈퇴 메세지</p> + <div className={cx('content')}> + <div className={cx('message-wrapper')}> + <p>ㆍ탈퇴 즉시 모든 개인정보가 삭제됩니다.</p> + <p>ㆍ구독한 전략에 대한 모든 내용이 삭제됩니다.</p> + </div> + </div> + <div className={cx('button-wrapper')}> + <Button className={cx('left-button')} onClick={handleBack}> + 뒤로가기 + </Button> + <Button className={cx('right-button')} variant="filled" onClick={handleWithdraw}> + 탈퇴 + </Button> + </div> + </div> + ) +} +export default UserWithdraw diff --git a/app/(dashboard)/my/profile/_ui/user-withdraw/styles.module.scss b/app/(dashboard)/my/profile/_ui/user-withdraw/styles.module.scss new file mode 100644 index 00000000..35e2f626 --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-withdraw/styles.module.scss @@ -0,0 +1,68 @@ +.container { + background-color: $color-white; + width: 897px; + height: 854px; + padding: 44px 40px; +} + +.title { + @include typo-b1; + color: $color-gray-700; + margin-bottom: 22px; +} + +.line { + width: 100%; + height: 1px; + background-color: $color-gray-300; +} + +.message { + margin-top: 69px; + text-align: center; + @include typo-h4; + color: $color-gray-700; +} + +.content { + display: flex; + justify-content: center; + align-items: center; +} + +.message-wrapper { + margin-top: 51px; + border: 1px solid $color-gray-600; + border-radius: 6px; + width: 569px; + height: 206px; + color: $color-gray-700; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; + + p { + @include typo-b2; + margin: 0; + line-height: 1.5; + } +} + +.button-wrapper { + margin-top: 58px; + display: flex; + justify-content: center; + gap: 32px; + width: 100%; + height: 40px; + + .left-button { + width: 112px; + } + + .right-button { + width: 112px; + } +} diff --git a/app/(dashboard)/my/profile/edit/page.tsx b/app/(dashboard)/my/profile/edit/page.tsx new file mode 100644 index 00000000..8fae3268 --- /dev/null +++ b/app/(dashboard)/my/profile/edit/page.tsx @@ -0,0 +1,11 @@ +import UserInfo from '../_ui/user-info' + +const MyProfileEditPage = () => { + return ( + <> + <UserInfo isEditable={true} /> + </> + ) +} + +export default MyProfileEditPage diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx index c0cd893f..1e1e244b 100644 --- a/app/(dashboard)/my/profile/page.tsx +++ b/app/(dashboard)/my/profile/page.tsx @@ -1,5 +1,11 @@ +import UserInfo from './_ui/user-info' + const MyProfilePage = () => { - return <></> + return ( + <> + <UserInfo /> + </> + ) } export default MyProfilePage diff --git a/public/icons/camera.svg b/public/icons/camera.svg new file mode 100644 index 00000000..6dc8a916 --- /dev/null +++ b/public/icons/camera.svg @@ -0,0 +1,3 @@ +<svg width="25" height="25" viewBox="0 0 25 25" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M8.62409 4.56017L6.67448 7.15966C6.57223 7.29599 6.43964 7.40665 6.28721 7.48286C6.13478 7.55908 5.9667 7.59876 5.79627 7.59876H5.35717C4.71666 7.59876 4.10238 7.8532 3.64946 8.30611C3.19655 8.75903 2.94211 9.37331 2.94211 10.0138V18.7959C2.94211 19.4364 3.19655 20.0507 3.64946 20.5036C4.10238 20.9565 4.71666 21.2109 5.35717 21.2109H19.4085C20.049 21.2109 20.6632 20.9565 21.1162 20.5036C21.5691 20.0507 21.8235 19.4364 21.8235 18.7959V10.0138C21.8235 9.37331 21.5691 8.75903 21.1162 8.30611C20.6632 7.8532 20.049 7.59876 19.4085 7.59876H18.9694C18.7989 7.59876 18.6308 7.55908 18.4784 7.48286C18.326 7.40665 18.1934 7.29599 18.0911 7.15966L16.1415 4.56017C16.0311 4.41292 15.8879 4.29341 15.7233 4.2111C15.5587 4.12879 15.3771 4.08594 15.1931 4.08594H9.57256C9.19932 4.08594 8.84804 4.26158 8.62409 4.56017ZM12.3828 9.35517C11.835 9.35517 11.2926 9.46307 10.7865 9.6727C10.2804 9.88234 9.82049 10.1896 9.43314 10.577C9.04578 10.9643 8.73851 11.4242 8.52887 11.9303C8.31924 12.4364 8.21134 12.9788 8.21134 13.5266C8.21134 14.0744 8.31924 14.6169 8.52887 15.123C8.73851 15.6291 9.04578 16.089 9.43314 16.4763C9.82049 16.8637 10.2804 17.1709 10.7865 17.3806C11.2926 17.5902 11.835 17.6981 12.3828 17.6981C13.4892 17.6981 14.5502 17.2586 15.3325 16.4763C16.1148 15.694 16.5543 14.633 16.5543 13.5266C16.5543 12.4203 16.1148 11.3593 15.3325 10.577C14.5502 9.79466 13.4892 9.35517 12.3828 9.35517Z" fill="currentColor"/> +</svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 8537e2c5..1f8d546d 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -33,3 +33,4 @@ export { default as CircleIcon } from './circle.svg' export { default as CheckedCircleIcon } from './checked-circle.svg' export { default as BackIcon } from './back.svg' export { default as RegisterIcon } from './register.svg' +export { default as CameraIcon } from './camera.svg' diff --git a/shared/constants/path.ts b/shared/constants/path.ts index 2e5ec512..b0a6a0d5 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -17,6 +17,7 @@ export const PATH = { // My PROFILE: '/my/profile', + EDIT_PROFILE: '/my/profile/edit', FAVORITES: '/my/favorites', MY_STRATEGIES: '/my/strategies', STRATEGIES_MANAGE: '/my/strategies/manage', diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index fe422a74..b7b579fd 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -3,6 +3,7 @@ import { BarsIcon, BookmarkIcon, BookmarkOutlineIcon, + CameraIcon, CategoryIcon, ChangeIcon, CheckIcon, @@ -73,6 +74,7 @@ const icons = [ { name: 'ModalSubscribeIcon', icon: ModalSubscribeIcon }, { name: 'ModalCheckIcon', icon: ModalCheckIcon }, { name: 'RegisterIcon', icon: RegisterIcon }, + { name: 'CameraIcon', icon: CameraIcon }, ] export const Icons: Story = { diff --git a/shared/ui/avatar/avatar.stories.tsx b/shared/ui/avatar/avatar.stories.tsx index 9bb902eb..95749bb6 100644 --- a/shared/ui/avatar/avatar.stories.tsx +++ b/shared/ui/avatar/avatar.stories.tsx @@ -12,7 +12,7 @@ const meta = { argTypes: { size: { control: 'radio', - options: ['small', 'medium', 'large'], + options: ['small', 'medium', 'large', 'xlarge', 'xxlarge'], defaultValue: 'small', }, src: { @@ -39,6 +39,8 @@ export const Sizes: StoryType = { <Avatar size="small" /> <Avatar size="medium" /> <Avatar size="large" /> + <Avatar size="xlarge" /> + <Avatar size="xxlarge" /> </div> ), } diff --git a/shared/ui/avatar/index.tsx b/shared/ui/avatar/index.tsx index 7cc8d830..349991d7 100644 --- a/shared/ui/avatar/index.tsx +++ b/shared/ui/avatar/index.tsx @@ -11,7 +11,7 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -type AvatarSizeType = 'small' | 'medium' | 'large' +type AvatarSizeType = 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge' interface Props { src?: string | StaticImageData diff --git a/shared/ui/avatar/styles.module.scss b/shared/ui/avatar/styles.module.scss index 5c069d98..415511fa 100644 --- a/shared/ui/avatar/styles.module.scss +++ b/shared/ui/avatar/styles.module.scss @@ -1,10 +1,14 @@ $avatar-small: 24px; $avatar-medium: 32px; $avatar-large: 72px; +$avatar-xlarge: 100px; +$avatar-xxlarge: 148px; $svg-small: 20px; $svg-medium: 24px; $svg-large: 54px; +$svg-xlarge: 75px; +$svg-xxlarge: 110px; .avatar { position: relative; @@ -47,6 +51,26 @@ $svg-large: 54px; } } + &.xlarge { + width: $avatar-xlarge; + height: $avatar-xlarge; + + svg { + width: $svg-xlarge; + height: $svg-xlarge; + } + } + + &.xxlarge { + width: $avatar-xxlarge; + height: $avatar-xxlarge; + + svg { + width: $svg-xxlarge; + height: $svg-xxlarge; + } + } + svg { margin-bottom: 2px; fill: $color-gray-500; diff --git a/shared/ui/button/button.stories.tsx b/shared/ui/button/button.stories.tsx index 9a71d7b5..da8755e0 100644 --- a/shared/ui/button/button.stories.tsx +++ b/shared/ui/button/button.stories.tsx @@ -48,7 +48,7 @@ export const Small: StoryType = { export const Medium: StoryType = { args: { - children: 'meduim', + children: 'medium', size: 'medium', }, } diff --git a/shared/ui/input/index.tsx b/shared/ui/input/index.tsx index 6453cd0a..6c65a096 100644 --- a/shared/ui/input/index.tsx +++ b/shared/ui/input/index.tsx @@ -9,22 +9,39 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -export type InputSizeType = 'small' | 'medium' | 'large' | 'full' +export type InputSizeType = 'small' | 'compact' | 'medium' | 'large' | 'full' interface Props extends ComponentPropsWithoutRef<'input'> { inputSize?: InputSizeType errorMessage?: string | null + isWhiteDisabled?: boolean } export const Input = forwardRef<HTMLInputElement, Props>( - ({ inputSize = 'medium', className, value, errorMessage, onChange, ...props }, ref) => { + ( + { + inputSize = 'medium', + className, + value, + errorMessage, + isWhiteDisabled = false, + onChange, + ...props + }, + ref + ) => { return ( <div> <input ref={ref} value={value} onChange={onChange} - className={cx('input', inputSize, { error: !!errorMessage }, className)} + className={cx( + 'input', + inputSize, + { error: !!errorMessage, 'white-disabled': isWhiteDisabled }, + className + )} {...props} /> <ErrorMessage errorMessage={errorMessage} /> diff --git a/shared/ui/input/input.stories.tsx b/shared/ui/input/input.stories.tsx index d79ea475..ce01968d 100644 --- a/shared/ui/input/input.stories.tsx +++ b/shared/ui/input/input.stories.tsx @@ -13,16 +13,21 @@ const meta: Meta<typeof Input> = { argTypes: { inputSize: { control: { type: 'radio' }, - options: ['small', 'medium', 'large', 'full'], + options: ['small', 'compact', 'medium', 'large', 'full'], }, type: { control: { type: 'select' }, options: ['email', 'password', 'tel', 'text'], }, + isWhiteDisabled: { + control: { type: 'boolean' }, + description: 'White disabled input', + }, }, tags: ['autodocs'], } +export default meta type StoryType = StoryObj<typeof Input> export const Default: StoryType = {} @@ -34,6 +39,13 @@ export const Small: StoryType = { }, } +export const Compact: StoryType = { + args: { + inputSize: 'compact', + placeholder: 'Compact input', + }, +} + export const Medium: StoryType = { args: { inputSize: 'medium', @@ -51,8 +63,22 @@ export const Large: StoryType = { export const Full: StoryType = { args: { inputSize: 'full', - placeholder: 'full input', + placeholder: 'Full input', }, } -export default meta +export const Disabled: StoryType = { + args: { + inputSize: 'medium', + placeholder: 'Disabled input', + disabled: true, + }, +} + +export const WhiteDisabled: StoryType = { + args: { + inputSize: 'medium', + placeholder: 'White disabled input', + isWhiteDisabled: true, + }, +} diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss index 24e80ffb..d716d2f6 100644 --- a/shared/ui/input/styles.module.scss +++ b/shared/ui/input/styles.module.scss @@ -19,20 +19,36 @@ &.small { width: 132px; } + + &.compact { + width: 240px; + } + &.medium { width: 300px; } + &.large { width: 400px; } + &.full { width: 100%; height: 100%; } + &:disabled { color: $color-gray-500; border-color: $color-gray-400; background-color: $color-gray-200; cursor: not-allowed; } + + &.white-disabled { + background-color: $color-white; + color: $color-gray-500; + border-color: $color-gray-400; + cursor: not-allowed; + pointer-events: none; + } } diff --git a/shared/ui/link-button/link-button.stories.tsx b/shared/ui/link-button/link-button.stories.tsx index a0afe38c..6097fa48 100644 --- a/shared/ui/link-button/link-button.stories.tsx +++ b/shared/ui/link-button/link-button.stories.tsx @@ -47,7 +47,7 @@ export const Small: StoryType = { export const Medium: StoryType = { args: { - children: 'meduim', + children: 'medium', size: 'medium', }, } diff --git a/shared/ui/page-not-found/index.tsx b/shared/ui/page-not-found/index.tsx index 1f421278..0e24b4ca 100644 --- a/shared/ui/page-not-found/index.tsx +++ b/shared/ui/page-not-found/index.tsx @@ -17,7 +17,7 @@ export const PageNotFound = () => { <p className={cx('number')}>404</p> <p className={cx('text')}>페이지를 찾을 수 없습니다.</p> <div className={cx('button-wrapper')}> - <LinkButton href={`${PATH.HOME}`} size="medium" variant="filled"> + <LinkButton href={PATH.HOME} size="medium" variant="filled"> 홈으로 돌아가기 </LinkButton> </div> From 703c4b14afb306c7db419af30ef4e67ea50c402c Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Wed, 4 Dec 2024 02:44:31 +0900 Subject: [PATCH 515/648] =?UTF-8?q?feat:=20MyProfileWithdrawPage=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=9D=EC=84=B1=20(#127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/withdraw/page.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 app/(dashboard)/my/profile/withdraw/page.tsx diff --git a/app/(dashboard)/my/profile/withdraw/page.tsx b/app/(dashboard)/my/profile/withdraw/page.tsx new file mode 100644 index 00000000..d4c36b05 --- /dev/null +++ b/app/(dashboard)/my/profile/withdraw/page.tsx @@ -0,0 +1,11 @@ +import UserWithdraw from '../_ui/user-withdraw' + +const MyProfileWithdrawPage = () => { + return ( + <> + <UserWithdraw /> + </> + ) +} + +export default MyProfileWithdrawPage From e0f4de3b074865f56ba779e39b46276a88772051 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 10:14:21 +0900 Subject: [PATCH 516/648] =?UTF-8?q?fix:=20=EA=B2=80=EC=83=89=EB=B0=94=20in?= =?UTF-8?q?put=20value=EC=86=8D=EC=84=B1=20=EB=88=84=EB=9D=BD=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=B6=94=EA=B0=80=20(#200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_ui/search-bar/range-container.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx index f83ea5f5..44aa439b 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames/bind' import useSearchingItemStore from './_store/use-searching-item-store' -import { SearchTermsModel } from './_type/search' +import { RangeModel, SearchTermsModel } from './_type/search' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -14,6 +14,7 @@ interface Props { const RangeContainer = ({ optionId }: Props) => { const errOptions = useSearchingItemStore((state) => state.errOptions) + const searchTerms = useSearchingItemStore((state) => state.searchTerms) const { setRangeValue } = useSearchingItemStore((state) => state.actions) const handleRangeValue = (e: React.ChangeEvent<HTMLInputElement>, type: 'min' | 'max') => { @@ -21,11 +22,14 @@ const RangeContainer = ({ optionId }: Props) => { setRangeValue(optionId, type, value) } + const option = searchTerms?.[optionId] as RangeModel | null + return ( <div className={cx('range-container')}> <div className={cx('range-wrapper')}> <input className={cx('range')} + value={option?.min ?? ''} type="number" placeholder="0" onChange={(e) => handleRangeValue(e, 'min')} @@ -33,6 +37,7 @@ const RangeContainer = ({ optionId }: Props) => { <span>~</span> <input className={cx('range')} + value={option?.max ?? ''} type="number" placeholder="0" onChange={(e) => handleRangeValue(e, 'max')} From b41696ff1bac0b1bef940ff173c0685f1f73dd9e Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 10:15:12 +0900 Subject: [PATCH 517/648] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=82=AD=EC=A0=9C=20api=20=EC=B6=94=EA=B0=80=20(#?= =?UTF-8?q?200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_api/delete-review.ts | 12 ++++++++++ .../[strategyId]/_api/patch-review.ts | 19 +++++++++++++++ .../[strategyId]/_api/post-review.ts | 10 +++++--- .../_hooks/query/use-delete-review.ts | 16 +++++++++++++ .../_hooks/query/use-patch-review.ts | 23 +++++++++++++++++++ .../_hooks/query/use-post-review.ts | 4 ++-- 6 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/delete-review.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/patch-review.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-delete-review.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-patch-review.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_api/delete-review.ts b/app/(dashboard)/strategies/[strategyId]/_api/delete-review.ts new file mode 100644 index 00000000..1e3c6b31 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/delete-review.ts @@ -0,0 +1,12 @@ +import axiosInstance from '@/shared/api/axios' + +const deleteReview = async (strategyId: number, reviewId: number) => { + try { + const response = await axiosInstance.delete(`/api/strategies/${strategyId}/reviews/${reviewId}`) + return response.data.isSuccess + } catch (err) { + console.error(err) + } +} + +export default deleteReview diff --git a/app/(dashboard)/strategies/[strategyId]/_api/patch-review.ts b/app/(dashboard)/strategies/[strategyId]/_api/patch-review.ts new file mode 100644 index 00000000..3c218151 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/patch-review.ts @@ -0,0 +1,19 @@ +import axiosInstance from '@/shared/api/axios' + +const patchReview = async ( + strategyId: number, + reviewId: number, + content: { content: string; starRating: number } +) => { + try { + const response = await axiosInstance.patch( + `/api/strategies/${strategyId}/reviews/${reviewId}`, + content + ) + return response.data.result + } catch (err) { + console.error(err) + } +} + +export default patchReview diff --git a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts index 1b13282e..e253983e 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts @@ -1,15 +1,19 @@ +import axios from 'axios' + import axiosInstance from '@/shared/api/axios' const postReview = async ( strategyId: number, content: { content: string; starRating: number } -): Promise<boolean | null> => { +): Promise<boolean | undefined | { isSuccess: boolean; message: string; code: number }> => { try { const response = await axiosInstance.post(`/api/strategies/${strategyId}/reviews`, content) return response.data.isSuccess } catch (err) { - console.error(err, '리뷰 등록 실패') - return null + if (axios.isAxiosError(err) && err.response) { + throw err.response.data + } + console.error(err) } } diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-delete-review.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-delete-review.ts new file mode 100644 index 00000000..1b9db669 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-delete-review.ts @@ -0,0 +1,16 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import deleteReview from '../../_api/delete-review' + +const useDeleteReview = (strategyId: number) => { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: ({ strategyId, reviewId }: { strategyId: number; reviewId: number }) => + deleteReview(strategyId, reviewId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['reviews', strategyId] }) + }, + }) +} + +export default useDeleteReview diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-patch-review.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-patch-review.ts new file mode 100644 index 00000000..cc4f9b3a --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-patch-review.ts @@ -0,0 +1,23 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import patchReview from '../../_api/patch-review' + +const usePatchReview = (strategyId: number) => { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: ({ + strategyId, + reviewId, + content, + }: { + strategyId: number + reviewId: number + content: { content: string; starRating: number } + }) => patchReview(strategyId, reviewId, content), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['reviews', strategyId] }) + }, + }) +} + +export default usePatchReview diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts index c1f5ab3b..9dd4f722 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts @@ -5,8 +5,8 @@ import postReview from '../../_api/post-review' const usePostReview = (strategyId: number) => { const queryClient = useQueryClient() return useMutation< - boolean | null, - undefined, + boolean | undefined | { isSuccess: boolean; message: string; code: number }, + { isSuccess: boolean; message: string; code: number }, { strategyId: number; content: { content: string; starRating: number } } >({ mutationFn: ({ From cf5876194de2e2c50779212295b34c44c85441b1 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 10:16:14 +0900 Subject: [PATCH 518/648] =?UTF-8?q?feat:=20api=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EC=95=88=EB=82=B4=EB=AA=A8=EB=8B=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/review-container/add-review.tsx | 105 +++++++++++++----- .../_ui/review-container/index.tsx | 3 +- .../review-container/review-guide-modal.tsx | 48 ++++++++ .../_ui/review-container/review-item.tsx | 56 +++++++++- .../_ui/review-container/review-list.tsx | 5 +- .../_ui/review-container/styles.module.scss | 44 ++++++-- 6 files changed, 217 insertions(+), 44 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx index 120453d4..58cbaf73 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx @@ -4,28 +4,44 @@ import { useRef, useState } from 'react' import classNames from 'classnames/bind' +import useModal from '@/shared/hooks/custom/use-modal' import { Button } from '@/shared/ui/button' import { ErrorMessage } from '@/shared/ui/error-message' import { Textarea } from '@/shared/ui/textarea' +import usePatchReview from '../../_hooks/query/use-patch-review' import usePostReview from '../../_hooks/query/use-post-review' import StarRating from '../star-rating/index' +import ReviewGuideModal from './review-guide-modal' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { strategyId: number + reviewId?: number + isEdit?: boolean + content?: string + starRating?: number + onCancel?: () => void } -const AddReview = ({ strategyId }: Props) => { - const [starRatingValue, setStarRatingValue] = useState(0) +const AddReview = ({ + strategyId, + reviewId, + isEdit = false, + content, + starRating, + onCancel, +}: Props) => { + const [starRatingValue, setStarRatingValue] = useState(starRating ?? 0) const [isEmpty, setIsEmpty] = useState(false) + const { isModalOpen, openModal, closeModal } = useModal() const textareaRef = useRef<HTMLTextAreaElement>(null) - const { mutate } = usePostReview(strategyId) - const handleStarRating = (idx: number) => setStarRatingValue(idx + 1) + const { mutate: postMutate, isError } = usePostReview(strategyId) + const { mutate: patchMutate } = usePatchReview(strategyId) - const handleAddReview = () => { + const handleFetchReview = (type: 'add' | 'edit') => { if (textareaRef.current) { const review = textareaRef.current.value.trim() if (review && starRatingValue > 0) { @@ -33,41 +49,74 @@ const AddReview = ({ strategyId }: Props) => { content: review, starRating: starRatingValue, } - mutate( - { strategyId, content }, - { - onSuccess: () => { - if (textareaRef.current) { - textareaRef.current.value = '' - setStarRatingValue(0) - } - }, - } - ) + if (type === 'add') { + postMutate( + { strategyId, content }, + { + onSuccess: () => { + if (textareaRef.current) { + textareaRef.current.value = '' + setStarRatingValue(0) + } + }, + onError: (err) => { + if (err && !err.isSuccess) { + openModal() + } + }, + } + ) + } else if (type === 'edit' && reviewId && onCancel) { + patchMutate( + { strategyId, reviewId, content }, + { + onSuccess: () => { + onCancel() + }, + } + ) + } } else { setIsEmpty(true) } } } + const handleStarRating = (idx: number) => setStarRatingValue(idx + 1) + return ( - <div className={cx('add-review-wrapper')}> + <div className={cx('add-review-wrapper', { edit: isEdit })}> <div className={cx('textarea-wrapper')}> - <Textarea rows={5} placeholder="리뷰를 작성해주세요." ref={textareaRef} /> + <Textarea + defaultValue={content && content} + rows={5} + placeholder="리뷰를 작성해주세요." + ref={textareaRef} + /> {isEmpty && <ErrorMessage errorMessage="리뷰 작성 또는 별점을 선택해주세요." />} </div> - <div className={cx('add-button-wrapper')}> - <p className={cx('strategy')}>전략이 어땠나요?</p> + <div className={cx('add-button-wrapper', { edit: isEdit })}> + {!isEdit && <p className={cx('strategy')}>전략이 어땠나요?</p>} <StarRating starRatingValue={starRatingValue} onRatingChange={handleStarRating} /> - <Button - variant="filled" - size="small" - className={cx('review-button')} - onClick={handleAddReview} - > - 리뷰 등록하기 - </Button> + {isEdit ? ( + <div className={cx('button-wrapper', { edit: isEdit })}> + <button onClick={() => handleFetchReview('edit')}>저장</button> + <button onClick={onCancel}>취소</button> + </div> + ) : ( + <Button + variant="filled" + size="small" + className={cx('review-button')} + onClick={() => handleFetchReview('add')} + > + 리뷰 등록하기 + </Button> + )} </div> + {isModalOpen && ( + <ReviewGuideModal isModalOpen={isModalOpen} isErr={isError} closeModal={closeModal} /> + )} </div> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx index 00eaeac3..7d3e881f 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/index.tsx @@ -32,8 +32,9 @@ const ReviewContainer = ({ strategyId }: Props) => { /> </div> <AddReview strategyId={strategyId} /> - {reviewData ? ( + {reviewData && reviewData.reviews.content.length !== 0 ? ( <ReviewList + strategyId={strategyId} reviews={reviewData.reviews.content} totalReview={reviewData.reviews.totalElements} currentPage={currentPage} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx new file mode 100644 index 00000000..417f27e1 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx @@ -0,0 +1,48 @@ +'use client' + +import React from 'react' + +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import Modal from '@/shared/ui/modal' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isModalOpen: boolean + isErr: boolean + closeModal: () => void + onChange?: () => void +} + +const ReviewGuideModal = ({ isModalOpen, isErr, closeModal, onChange }: Props) => { + return ( + <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> + <span className={cx('message')}> + {isErr ? ( + <> + 이미 등록된 리뷰가 있습니다. <br /> 리뷰는 한 번만 등록 가능합니다. + </> + ) : ( + <>리뷰를 삭제하시겠습니까?</> + )} + </span> + {isErr && !onChange ? ( + <Button onClick={closeModal}>닫기</Button> + ) : ( + <div className={cx('two-button')}> + <Button onClick={closeModal}>아니오</Button> + <Button onClick={onChange} variant="filled" className={cx('button')}> + 예 + </Button> + </div> + )} + </Modal> + ) +} + +export default ReviewGuideModal diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx index 7bfff652..de3b1542 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx @@ -1,13 +1,23 @@ +'use client' + +import { useState } from 'react' + import classNames from 'classnames/bind' +import useModal from '@/shared/hooks/custom/use-modal' import Avatar from '@/shared/ui/avatar' +import useDeleteReview from '../../_hooks/query/use-delete-review' import StarRating from '../star-rating/index' +import AddReview from './add-review' +import ReviewGuideModal from './review-guide-modal' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { + reviewId: number + strategyId: number nickname: string content: string profileImage?: string @@ -18,6 +28,8 @@ interface Props { } const ReviewItem = ({ + reviewId, + strategyId, nickname, profileImage, createdAt, @@ -26,6 +38,21 @@ const ReviewItem = ({ isReviewer, isAdmin, }: Props) => { + const [isEdit, setIsEdit] = useState(false) + const { isModalOpen, openModal, closeModal } = useModal() + const { mutate } = useDeleteReview(strategyId) + + const handleDelete = () => { + mutate( + { strategyId, reviewId }, + { + onSuccess: () => { + closeModal() + }, + } + ) + } + const editedCreatedAt = createdAt.slice(0, -3) return ( @@ -36,19 +63,38 @@ const ReviewItem = ({ <p className={cx('nickname')}>{nickname}</p> <span>|</span> <span>{editedCreatedAt}</span> - <StarRating starRating={starRating} /> + {!isEdit && <StarRating starRating={starRating} />} </div> <div className={cx('button-wrapper')}> - {isReviewer && ( + {isReviewer && !isEdit && ( <> - <button>수정</button> - <button>삭제</button> + <button onClick={() => setIsEdit(true)}>수정</button> + <button onClick={openModal}>삭제</button> </> )} {!isReviewer && isAdmin && <button>삭제</button>} </div> </div> - <div className={cx('content')}>{content}</div> + {isEdit ? ( + <AddReview + strategyId={strategyId} + reviewId={reviewId} + isEdit={isEdit} + content={content} + starRating={starRating} + onCancel={() => setIsEdit(false)} + /> + ) : ( + <div className={cx('content')}>{content}</div> + )} + {isModalOpen && ( + <ReviewGuideModal + isModalOpen={isModalOpen} + isErr={false} + closeModal={closeModal} + onChange={handleDelete} + /> + )} </li> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx index f63f9aa2..1e05cef3 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-list.tsx @@ -19,13 +19,14 @@ interface ReviewContentModel { } interface Props { + strategyId: number reviews: ReviewContentModel[] totalReview: number currentPage: number setCurrentPage: (page: number) => void } -const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props) => { +const ReviewList = ({ strategyId, reviews, totalReview, currentPage, setCurrentPage }: Props) => { const handlePageChange = (page: number) => setCurrentPage(page) const user = useAuthStore((state) => state.user) @@ -35,6 +36,8 @@ const ReviewList = ({ reviews, totalReview, currentPage, setCurrentPage }: Props {reviews.map((review) => ( <ReviewItem key={review.reviewId} + reviewId={review.reviewId} + strategyId={strategyId} nickname={review.nickname} profileImage={review.imageUrl} createdAt={review.createdAt} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss index 4db61f02..02376d20 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/styles.module.scss @@ -45,6 +45,12 @@ .review-button { margin: 20px 0 0 4px; } + &.edit { + padding: 20px 0; + } + } + &.edit { + margin: 10px 0; } } @@ -72,15 +78,6 @@ margin-right: 5px; } } - .button-wrapper { - button { - font-weight: $text-bold; - font-size: $text-c1; - color: $color-gray-500; - background-color: transparent; - margin-left: 20px; - } - } } .content { margin: 10px 0; @@ -88,3 +85,32 @@ font-weight: $text-medium; } } + +.button-wrapper { + button { + font-weight: $text-bold; + font-size: $text-c1; + color: $color-gray-500; + background-color: transparent; + margin-left: 20px; + } + &.edit { + margin-top: 20px; + padding-left: 10px; + } +} + +.message { + @include typo-b1; + text-align: center; + color: $color-gray-800; + margin-bottom: 30px; +} + +.two-button { + display: flex; + gap: 10px; + & .button { + width: 90px; + } +} From 95b7872c60b8756050c9da22c4f133694ddd6c60 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 4 Dec 2024 10:34:54 +0900 Subject: [PATCH 519/648] =?UTF-8?q?feat:=20=EB=8C=80=ED=91=9C=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=ED=86=B5=ED=95=A9=20=ED=8F=89=EA=B7=A0=20=EC=A7=80?= =?UTF-8?q?=ED=91=9C=20api=20=EC=97=B0=EA=B2=B0=20(#211)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(home)/_api/strategies-metrics.ts | 11 ++++++++ .../query/use-get-strategies-metrics.ts | 13 ++++++++++ .../average-metrics-chart.tsx | 14 +++++----- .../_ui/average-metrics-section/index.tsx | 26 +++++-------------- 4 files changed, 39 insertions(+), 25 deletions(-) create mode 100644 app/(landing)/(home)/_api/strategies-metrics.ts create mode 100644 app/(landing)/(home)/_hooks/query/use-get-strategies-metrics.ts diff --git a/app/(landing)/(home)/_api/strategies-metrics.ts b/app/(landing)/(home)/_api/strategies-metrics.ts new file mode 100644 index 00000000..8f55f2bd --- /dev/null +++ b/app/(landing)/(home)/_api/strategies-metrics.ts @@ -0,0 +1,11 @@ +import axios from 'axios' + +export const getStrategiesMetrics = async () => { + try { + const response = await axios.get('/api/main/total-strategies-metrics') + return response.data.result + } catch (err) { + console.error(err) + throw new Error('대표 전략 통합 지표 조회에 실패했습니다.') + } +} diff --git a/app/(landing)/(home)/_hooks/query/use-get-strategies-metrics.ts b/app/(landing)/(home)/_hooks/query/use-get-strategies-metrics.ts new file mode 100644 index 00000000..34e94629 --- /dev/null +++ b/app/(landing)/(home)/_hooks/query/use-get-strategies-metrics.ts @@ -0,0 +1,13 @@ +import { useQuery } from '@tanstack/react-query' + +import { getStrategiesMetrics } from '../../_api/strategies-metrics' +import { AverageMetricsChartDataModel } from '../../_ui/average-metrics-section/average-metrics-chart' + +const useGetStrategiesMetrics = () => { + return useQuery<AverageMetricsChartDataModel>({ + queryKey: ['totalStrategiesMetrics'], + queryFn: getStrategiesMetrics, + }) +} + +export default useGetStrategiesMetrics diff --git a/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx b/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx index a4d9e512..bbe0af43 100644 --- a/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx +++ b/app/(landing)/(home)/_ui/average-metrics-section/average-metrics-chart.tsx @@ -13,9 +13,11 @@ const HighchartsReact = dynamic(() => import('highcharts-react-official'), { export interface AverageMetricsChartDataModel { dates: string[] - averagePrice: number[] - topSmScore: number[] - topStrategyPrice: number[] + data: { + avgReferencePrice: number[] + highestSmScoreReferencePrice: number[] + highestSubscribeScoreReferencePrice: number[] + } } interface Props { @@ -150,7 +152,7 @@ const AverageMetricsChart = ({ data }: Props) => { { type: 'areaspline', name: '평균', - data: data.averagePrice, + data: data.data.avgReferencePrice, color: '#FF4F1F', yAxis: 0, stickyTracking: false, @@ -159,7 +161,7 @@ const AverageMetricsChart = ({ data }: Props) => { { type: 'spline', name: 'SM SCORE 1위', - data: data.topSmScore, + data: data.data.highestSmScoreReferencePrice, color: '#6877FF', yAxis: 1, stickyTracking: false, @@ -168,7 +170,7 @@ const AverageMetricsChart = ({ data }: Props) => { { type: 'spline', name: '구독 1위', - data: data.topStrategyPrice, + data: data.data.highestSubscribeScoreReferencePrice, color: '#FFE070', yAxis: 1, stickyTracking: false, diff --git a/app/(landing)/(home)/_ui/average-metrics-section/index.tsx b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx index e375000d..5d2156d0 100644 --- a/app/(landing)/(home)/_ui/average-metrics-section/index.tsx +++ b/app/(landing)/(home)/_ui/average-metrics-section/index.tsx @@ -1,9 +1,11 @@ +'use client' + import dynamic from 'next/dynamic' import classNames from 'classnames/bind' +import useGetStrategiesMetrics from '../../_hooks/query/use-get-strategies-metrics' import HomeSubtitle from '../home-subtitle' -import { AverageMetricsChartDataModel } from './average-metrics-chart' import styles from './styles.module.scss' const AverageMetricsChart = dynamic(() => import('./average-metrics-chart'), { @@ -14,24 +16,10 @@ const AverageMetricsChart = dynamic(() => import('./average-metrics-chart'), { const cx = classNames.bind(styles) const AverageMetricsSection = () => { - const chartData: AverageMetricsChartDataModel = { - dates: [ - 'Jan 1, 2023', - 'Feb 1, 2023', - 'Mar 1, 2023', - 'Apr 1, 2023', - 'May 1, 2023', - 'Jun 1, 2023', - 'Jul 1, 2023', - 'Aug 1, 2023', - 'Sep 1, 2023', - 'Oct 1, 2023', - 'Nov 1, 2023', - 'Dec 1, 2023', - ], - averagePrice: [200, 220, 260, 310, 290, 340, 400, 380, 450, 530, 510, 580], - topSmScore: [240, 270, 320, 330, 350, 420, 450, 470, 550, 530, 600, 680], - topStrategyPrice: [350, 330, 380, 450, 430, 500, 580, 560, 630, 710, 690, 760], + const { data: chartData } = useGetStrategiesMetrics() + + if (!chartData) { + return <p>차트 조회에 실패했습니다.</p> } const startDate = chartData.dates[0] From 343037998a664ce4f289d061d960b67c8c222e29 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 4 Dec 2024 11:20:49 +0900 Subject: [PATCH 520/648] =?UTF-8?q?design:=20=EC=95=BD=EA=B4=80=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=97=AC=EB=B0=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=ED=8F=B0=ED=8A=B8=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EC=A6=88=20=EC=88=98=EC=A0=95=20(#224)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/privacy-terms/page.module.scss | 8 ++++++-- app/(landing)/terms-of-use/page.module.scss | 12 ++++++++---- app/(landing)/terms-of-use/page.tsx | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/(landing)/privacy-terms/page.module.scss b/app/(landing)/privacy-terms/page.module.scss index 466e324e..efc640e4 100644 --- a/app/(landing)/privacy-terms/page.module.scss +++ b/app/(landing)/privacy-terms/page.module.scss @@ -1,9 +1,13 @@ .section-container { - margin-bottom: 80px; + margin: 0 100px 80px; + + @include mobile { + margin: 0 0 80px; + } h3 { margin: 16px 0; - @include typo-h3; + @include typo-h4; font-weight: $text-bold; } } diff --git a/app/(landing)/terms-of-use/page.module.scss b/app/(landing)/terms-of-use/page.module.scss index 9c0ec141..9d3d316a 100644 --- a/app/(landing)/terms-of-use/page.module.scss +++ b/app/(landing)/terms-of-use/page.module.scss @@ -1,13 +1,17 @@ .title { - margin: 16px 0; - @include typo-h3; + margin: 16px 0 32px; + @include typo-h4; font-weight: $text-bold; } .section-container { - margin-bottom: 80px; + margin: 0 100px 80px; + + @include mobile { + margin: 0 0 80px; + } h3 { - @include typo-b1; + @include typo-b2; } } diff --git a/app/(landing)/terms-of-use/page.tsx b/app/(landing)/terms-of-use/page.tsx index 5fb414a9..702fca84 100644 --- a/app/(landing)/terms-of-use/page.tsx +++ b/app/(landing)/terms-of-use/page.tsx @@ -10,8 +10,8 @@ const cx = classNames.bind(styles) const TermsOfUsePage = () => { return ( <> - <h1 className={cx('title')}>이용약관</h1> <section className={cx('section-container')}> + <h1 className={cx('title')}>이용약관</h1> <InvestorTerms /> </section> <section className={cx('section-container')}> From 4e65006819dcbba337a1fb4a580204338672974b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 11:42:01 +0900 Subject: [PATCH 521/648] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_api/post-review.ts | 4 +++- .../[strategyId]/_hooks/query/use-post-review.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts index e253983e..f6447366 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/post-review.ts @@ -2,10 +2,12 @@ import axios from 'axios' import axiosInstance from '@/shared/api/axios' +import { PostReviewErrModel } from '../_hooks/query/use-post-review' + const postReview = async ( strategyId: number, content: { content: string; starRating: number } -): Promise<boolean | undefined | { isSuccess: boolean; message: string; code: number }> => { +): Promise<boolean | undefined | PostReviewErrModel> => { try { const response = await axiosInstance.post(`/api/strategies/${strategyId}/reviews`, content) return response.data.isSuccess diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts index 9dd4f722..668e16b6 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-review.ts @@ -2,11 +2,17 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import postReview from '../../_api/post-review' +export interface PostReviewErrModel { + isSuccess: boolean + message: string + code: number +} + const usePostReview = (strategyId: number) => { const queryClient = useQueryClient() return useMutation< - boolean | undefined | { isSuccess: boolean; message: string; code: number }, - { isSuccess: boolean; message: string; code: number }, + boolean | undefined | PostReviewErrModel, + PostReviewErrModel, { strategyId: number; content: { content: string; starRating: number } } >({ mutationFn: ({ From 6f9306ad06fb53ba480005adc5a1c3bda9d361df Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 11:42:47 +0900 Subject: [PATCH 522/648] =?UTF-8?q?fix:=20=EC=83=81=ED=83=9C=EB=AA=85=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/review-container/add-review.tsx | 18 ++++++------ .../_ui/review-container/review-item.tsx | 28 +++++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx index 58cbaf73..f6d0fc3b 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx @@ -20,7 +20,7 @@ const cx = classNames.bind(styles) interface Props { strategyId: number reviewId?: number - isEdit?: boolean + isEditable?: boolean content?: string starRating?: number onCancel?: () => void @@ -29,7 +29,7 @@ interface Props { const AddReview = ({ strategyId, reviewId, - isEdit = false, + isEditable = false, content, starRating, onCancel, @@ -85,7 +85,7 @@ const AddReview = ({ const handleStarRating = (idx: number) => setStarRatingValue(idx + 1) return ( - <div className={cx('add-review-wrapper', { edit: isEdit })}> + <div className={cx('add-review-wrapper', { edit: isEditable })}> <div className={cx('textarea-wrapper')}> <Textarea defaultValue={content && content} @@ -95,11 +95,11 @@ const AddReview = ({ /> {isEmpty && <ErrorMessage errorMessage="리뷰 작성 또는 별점을 선택해주세요." />} </div> - <div className={cx('add-button-wrapper', { edit: isEdit })}> - {!isEdit && <p className={cx('strategy')}>전략이 어땠나요?</p>} + <div className={cx('add-button-wrapper', { edit: isEditable })}> + {!isEditable && <p className={cx('strategy')}>전략이 어땠나요?</p>} <StarRating starRatingValue={starRatingValue} onRatingChange={handleStarRating} /> - {isEdit ? ( - <div className={cx('button-wrapper', { edit: isEdit })}> + {isEditable ? ( + <div className={cx('button-wrapper', { edit: isEditable })}> <button onClick={() => handleFetchReview('edit')}>저장</button> <button onClick={onCancel}>취소</button> </div> @@ -114,9 +114,7 @@ const AddReview = ({ </Button> )} </div> - {isModalOpen && ( - <ReviewGuideModal isModalOpen={isModalOpen} isErr={isError} closeModal={closeModal} /> - )} + <ReviewGuideModal isModalOpen={isModalOpen} isErr={isError} closeModal={closeModal} /> </div> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx index de3b1542..2c04eb57 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx @@ -38,7 +38,7 @@ const ReviewItem = ({ isReviewer, isAdmin, }: Props) => { - const [isEdit, setIsEdit] = useState(false) + const [isEditable, setIsEditable] = useState(false) const { isModalOpen, openModal, closeModal } = useModal() const { mutate } = useDeleteReview(strategyId) @@ -63,38 +63,36 @@ const ReviewItem = ({ <p className={cx('nickname')}>{nickname}</p> <span>|</span> <span>{editedCreatedAt}</span> - {!isEdit && <StarRating starRating={starRating} />} + {!isEditable && <StarRating starRating={starRating} />} </div> <div className={cx('button-wrapper')}> - {isReviewer && !isEdit && ( + {isReviewer && !isEditable && ( <> - <button onClick={() => setIsEdit(true)}>수정</button> + <button onClick={() => setIsEditable(true)}>수정</button> <button onClick={openModal}>삭제</button> </> )} {!isReviewer && isAdmin && <button>삭제</button>} </div> </div> - {isEdit ? ( + {isEditable ? ( <AddReview strategyId={strategyId} reviewId={reviewId} - isEdit={isEdit} + isEditable={isEditable} content={content} starRating={starRating} - onCancel={() => setIsEdit(false)} + onCancel={() => setIsEditable(false)} /> ) : ( <div className={cx('content')}>{content}</div> )} - {isModalOpen && ( - <ReviewGuideModal - isModalOpen={isModalOpen} - isErr={false} - closeModal={closeModal} - onChange={handleDelete} - /> - )} + <ReviewGuideModal + isModalOpen={isModalOpen} + isErr={false} + closeModal={closeModal} + onChange={handleDelete} + /> </li> ) } From 1629f96795dbab116136eb4e8a9c9f05ab375811 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 4 Dec 2024 12:01:10 +0900 Subject: [PATCH 523/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=84=B8=EC=85=98=20=EB=A7=8C=EB=A3=8C=20=EC=A0=84=20=EA=B2=BD?= =?UTF-8?q?=EA=B3=A0=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84=20(#226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/providers/auth-provider.tsx | 38 +++++++------ shared/stores/use-modal-store.ts | 13 +++++ shared/stores/use-session-store.ts | 20 +++++++ .../modal/session-extension-modal/index.tsx | 54 +++++++++++++++++++ .../styles.module.scss | 51 ++++++++++++++++++ 5 files changed, 159 insertions(+), 17 deletions(-) create mode 100644 shared/stores/use-modal-store.ts create mode 100644 shared/stores/use-session-store.ts create mode 100644 shared/ui/modal/session-extension-modal/index.tsx create mode 100644 shared/ui/modal/session-extension-modal/styles.module.scss diff --git a/shared/providers/auth-provider.tsx b/shared/providers/auth-provider.tsx index 42ee7642..2e29e219 100644 --- a/shared/providers/auth-provider.tsx +++ b/shared/providers/auth-provider.tsx @@ -7,6 +7,11 @@ import { usePathname, useRouter } from 'next/navigation' import { PATH } from '@/shared/constants/path' import { getAccessToken } from '@/shared/lib/auth-tokens' +import { useAuth } from '../hooks/custom/use-auth' +import { useSessionExpiryWarning } from '../hooks/custom/use-session-expiry-warning' +import { useAuthStore } from '../stores/use-auth-store' +import { isAuthRequiredPath, isNonAuthPage } from '../utils/auth-path' + interface AuthProviderProps { children: ReactNode } @@ -16,34 +21,33 @@ const AuthContext = createContext<null>(null) export const AuthProvider = ({ children }: AuthProviderProps) => { const pathname = usePathname() const router = useRouter() + const { initializeAuthState } = useAuthStore() - const authRequiredPatterns = ['/my/', `${PATH.ADMIN}`, `${PATH.TRADERS}`, `${PATH.STRATEGIES}/`] + useAuth() + const SessionExpiryWarningModal = useSessionExpiryWarning() - const nonAuthPages = [ - PATH.SIGN_IN, - PATH.SIGN_UP, - PATH.SIGN_UP_USER_TYPE, - PATH.SIGN_UP_TERMS_OF_USE, - PATH.SIGN_UP_INFORMATION, - PATH.SIGN_UP_COMPLETE, - ] + useEffect(() => { + initializeAuthState() + }, [initializeAuthState]) useEffect(() => { const accessToken = getAccessToken() - const isAuthRequired = authRequiredPatterns.some((pattern) => pathname.startsWith(pattern)) - const isNonAuthPage = nonAuthPages.some((page) => pathname.startsWith(page)) - - if (isAuthRequired && !accessToken) { + if (isAuthRequiredPath(pathname) && !accessToken) { router.replace(`${PATH.SIGN_IN}?returnUrl=${pathname}`) return } - if (isNonAuthPage && accessToken) { + if (isNonAuthPage(pathname) && accessToken) { router.replace(PATH.STRATEGIES) return } - }, [pathname]) - - return <AuthContext.Provider value={null}>{children}</AuthContext.Provider> + }, [pathname, router]) + + return ( + <AuthContext.Provider value={null}> + {children} + {SessionExpiryWarningModal} + </AuthContext.Provider> + ) } diff --git a/shared/stores/use-modal-store.ts b/shared/stores/use-modal-store.ts new file mode 100644 index 00000000..e68ce238 --- /dev/null +++ b/shared/stores/use-modal-store.ts @@ -0,0 +1,13 @@ +import { create } from 'zustand' + +interface ModalStoreModel { + isModalOpen: boolean + openModal: () => void + closeModal: () => void +} + +export const useModalStore = create<ModalStoreModel>((set) => ({ + isModalOpen: false, + openModal: () => set({ isModalOpen: true }), + closeModal: () => set({ isModalOpen: false }), +})) diff --git a/shared/stores/use-session-store.ts b/shared/stores/use-session-store.ts new file mode 100644 index 00000000..f8dd0b72 --- /dev/null +++ b/shared/stores/use-session-store.ts @@ -0,0 +1,20 @@ +import { create } from 'zustand' + +interface SessionStoreModel { + isWarningShown: boolean + minutesLeft: number + setWarningShown: (shown: boolean) => void + setMinutesLeft: (minutes: number | ((prev: number) => number)) => void +} + +export const useSessionStore = create<SessionStoreModel>()((set) => ({ + isWarningShown: false, + minutesLeft: 0, + + setWarningShown: (shown) => set({ isWarningShown: shown }), + + setMinutesLeft: (minutes) => + set((state) => ({ + minutesLeft: typeof minutes === 'function' ? minutes(state.minutesLeft) : minutes, + })), +})) diff --git a/shared/ui/modal/session-extension-modal/index.tsx b/shared/ui/modal/session-extension-modal/index.tsx new file mode 100644 index 00000000..dcaee74e --- /dev/null +++ b/shared/ui/modal/session-extension-modal/index.tsx @@ -0,0 +1,54 @@ +'use client' + +import React from 'react' + +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import Modal from '..' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface SessionExtensionModalProps { + isModalOpen: boolean + closeModal: () => void + onExtend: () => void + minutesLeft: number +} + +const SessionExtensionModal = ({ + isModalOpen, + closeModal, + onExtend, + minutesLeft, +}: SessionExtensionModalProps) => { + const getMessage = () => { + if (minutesLeft > 0) { + return `${minutesLeft}분 후에 자동 로그아웃됩니다.` + } + return '잠시 후 자동 로그아웃됩니다.' + } + + return ( + <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> + <span className={cx('message')}> + 세션이 곧 만료됩니다. + <br /> + {getMessage()} + <br /> + 로그인을 연장하시겠습니까? + </span> + <div className={cx('two-button')}> + <Button onClick={closeModal}>아니오</Button> + <Button onClick={onExtend} variant="filled" className={cx('button')}> + 예 + </Button> + </div> + </Modal> + ) +} + +export default SessionExtensionModal diff --git a/shared/ui/modal/session-extension-modal/styles.module.scss b/shared/ui/modal/session-extension-modal/styles.module.scss new file mode 100644 index 00000000..3d510751 --- /dev/null +++ b/shared/ui/modal/session-extension-modal/styles.module.scss @@ -0,0 +1,51 @@ +.overlay { + position: fixed; + inset: 0; + background-color: $color-black; + opacity: 0.2; + z-index: zIndex(overlay); +} + +.modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: $color-gray-100; + border: 1px solid $color-gray-200; + border-radius: 8px; + z-index: zIndex(modal); + padding: 40px; + width: 360px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.icon { + display: flex; + justify-content: center; + align-items: center; + + svg { + width: 40px; + flex-shrink: 0; + } +} + +.message { + @include typo-b1; + text-align: center; + white-space: pre-line; + color: $color-gray-800; + margin-bottom: 30px; +} + +.two-button { + display: flex; + gap: 10px; + & .button { + width: 90px; + } +} From 6209ad9edc3a818186cfb9df00ca7933bbc1e6c1 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 4 Dec 2024 12:04:07 +0900 Subject: [PATCH 524/648] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20(#226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 3 +- app/layout.tsx | 2 +- shared/api/axios.ts | 31 ++-- shared/constants/auth.ts | 23 ++- shared/hooks/custom/use-auth.ts | 88 ++++------- .../custom/use-session-expiry-warning.tsx | 81 ++++++++++ shared/hooks/query/auth-queries.ts | 115 +++++++++----- shared/lib/auth-tokens.ts | 23 ++- shared/stores/use-auth-store.ts | 145 ++++++++++++++++-- shared/utils/auth-path.ts | 9 ++ shared/utils/token-utils.ts | 70 ++++++--- 11 files changed, 431 insertions(+), 159 deletions(-) create mode 100644 shared/hooks/custom/use-session-expiry-warning.tsx create mode 100644 shared/utils/auth-path.ts diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index b67f0236..169914ca 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -27,9 +27,9 @@ const SignInPage = () => { const router = useRouter() const searchParams = useSearchParams() const loginMutation = useLoginMutation() - const setKeepLoggedIn = useAuthStore((state) => state.setKeepLoggedIn) const isKeepLoggedIn = useAuthStore((state) => state.isKeepLoggedIn) + const [formData, setFormData] = useState<LoginFormDataModel>({ email: '', password: '', @@ -83,6 +83,7 @@ const SignInPage = () => { console.error('Invalid return URL:', err) } } + router.replace(PATH.STRATEGIES) } catch (err) { if (err instanceof AxiosError) { diff --git a/app/layout.tsx b/app/layout.tsx index 8e9e0247..7212214b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -16,8 +16,8 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { <html lang="ko" className={pretendard.variable}> <body> <QueryProvider> - <AuthProvider>{children}</AuthProvider> <div id="modal-root"></div> + <AuthProvider>{children}</AuthProvider> </QueryProvider> </body> </html> diff --git a/shared/api/axios.ts b/shared/api/axios.ts index e9e92ba3..be96c478 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -6,6 +6,7 @@ import { useAuthStore } from '@/shared/stores/use-auth-store' import { isTokenExpired, refreshToken } from '@/shared/utils/token-utils' export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { + const { user, setAuthState } = useAuthStore.getState() const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_HOST, withCredentials: true, @@ -14,36 +15,31 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} if (options.withInterceptors && typeof window !== 'undefined') { instance.interceptors.request.use( async (config: InternalAxiosRequestConfig) => { - const { isLoggedOut } = useAuthStore.getState() const accessToken = getAccessToken() - if (isLoggedOut || !accessToken) { + if (!user || !accessToken) { return config } try { if (isTokenExpired(accessToken)) { - useAuthStore.getState().setAuthState({ + setAuthState({ isAuthenticated: false, user: null, - isKeepLoggedIn: false, - isLoggedOut: true, }) return config } config.headers['access-token'] = `Bearer ${accessToken}` + return config } catch (err) { - console.error('토큰 검증 실패:', err) - useAuthStore.getState().setAuthState({ + console.error('Token validation failed:', err) + setAuthState({ isAuthenticated: false, user: null, - isKeepLoggedIn: false, - isLoggedOut: true, }) + return config } - - return config }, (err) => Promise.reject(err) ) @@ -51,15 +47,12 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} instance.interceptors.response.use( (response) => response, async (err: AxiosError) => { - if (!err.config) return Promise.reject(err) - - const originalRequest = err.config - const { isLoggedOut } = useAuthStore.getState() - - if (isLoggedOut) { + if (!err.config || !user) { return Promise.reject(err) } + const originalRequest = err.config + if (err.response?.status === 401) { try { const newToken = await refreshToken() @@ -71,11 +64,9 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} console.error('Token refresh failed:', refreshError) } - useAuthStore.getState().setAuthState({ + setAuthState({ isAuthenticated: false, user: null, - isKeepLoggedIn: false, - isLoggedOut: true, }) } diff --git a/shared/constants/auth.ts b/shared/constants/auth.ts index 3f0f3709..475d9813 100644 --- a/shared/constants/auth.ts +++ b/shared/constants/auth.ts @@ -1,12 +1,25 @@ -export const STORAGE_PREFIX = 'investment_platform_' +import { PATH } from './path' + +const STORAGE_PREFIX = 'investment_platform_' + export const STORAGE_KEYS = { ACCESS_TOKEN: `${STORAGE_PREFIX}access_token`, - REFRESH_TOKEN: `${STORAGE_PREFIX}refresh_token`, USER: `${STORAGE_PREFIX}user`, - SESSION: `${STORAGE_PREFIX}session`, } as const export const AUTH_TIME = { - TOKEN_CHECK_INTERVAL: 60000, // 1분 - ADMIN_EXPIRY_WARNING: 300000, // 5분 + TOKEN_CHECK_INTERVAL: 120 * 1000, + ADMIN_EXPIRY_WARNING: 600 * 1000, + SAFETY_MARGIN: 5 * 1000, } as const + +export const AUTH_REQUIRED_PATTERNS = ['/my/', '/admin', '/traders', '/strategies/'] as const + +export const NON_AUTH_PAGES = [ + PATH.SIGN_IN, + PATH.SIGN_UP, + PATH.SIGN_UP_USER_TYPE, + PATH.SIGN_UP_TERMS_OF_USE, + PATH.SIGN_UP_INFORMATION, + PATH.SIGN_UP_COMPLETE, +] as const diff --git a/shared/hooks/custom/use-auth.ts b/shared/hooks/custom/use-auth.ts index 73c0a100..a5f9d87a 100644 --- a/shared/hooks/custom/use-auth.ts +++ b/shared/hooks/custom/use-auth.ts @@ -1,97 +1,77 @@ -import { useEffect } from 'react' +import { useCallback, useEffect } from 'react' -import { AUTH_TIME, STORAGE_KEYS } from '@/shared/constants/auth' +import { usePathname } from 'next/navigation' + +import { AUTH_TIME } from '@/shared/constants/auth' import { useLogoutMutation } from '@/shared/hooks/query/auth-queries' import { getAccessToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' import { TokenStatusModel, isAdmin } from '@/shared/types/auth' -import { getTimeUntilExpiry, isTokenExpired } from '@/shared/utils/token-utils' +import { isAuthRequiredPath } from '@/shared/utils/auth-path' +import { getTimeUntilExpiry } from '@/shared/utils/token-utils' export const useAuth = () => { - const { isKeepLoggedIn, isLoggedOut } = useAuthStore() + const { user } = useAuthStore() const { mutate: logout } = useLogoutMutation() + const pathname = usePathname() - const checkTokenStatus = (): TokenStatusModel | null => { + const checkTokenStatus = useCallback((): TokenStatusModel | null => { const accessToken = getAccessToken() - const userStr = localStorage.getItem(STORAGE_KEYS.USER) + const { user } = useAuthStore.getState() - if (!accessToken || !userStr) { - logout() + if (!accessToken || !user) { return null } try { - const user = JSON.parse(userStr) - const isExpired = isTokenExpired(accessToken) const timeUntilExpiry = getTimeUntilExpiry(accessToken) - if (isExpired) { + if (timeUntilExpiry <= 0) { logout() return null } - if (!isAdmin(user)) { - const sessionToken = sessionStorage.getItem(STORAGE_KEYS.SESSION) - if (!isKeepLoggedIn && !sessionToken) { + if (isAdmin(user)) { + if (localStorage.getItem('access_token')) { logout() return null } } return { - isValid: !isExpired, + isValid: true, timeUntilExpiry, isNearExpiry: timeUntilExpiry < AUTH_TIME.ADMIN_EXPIRY_WARNING, } } catch (error) { - console.error('Failed to parse user data:', error) + console.error('Token status check failed:', error) logout() return null } - } + }, [logout]) useEffect(() => { - const initializeAuth = () => { - const accessToken = getAccessToken() - const userStr = localStorage.getItem(STORAGE_KEYS.USER) - if (isLoggedOut !== false) return - - if (!accessToken || !userStr) { - logout() - return - } - - try { - const user = JSON.parse(userStr) - - if (isTokenExpired(accessToken)) { - logout() - return - } + if (!isAuthRequiredPath(pathname)) { + return + } - if (!isAdmin(user)) { - const sessionToken = sessionStorage.getItem(STORAGE_KEYS.SESSION) - if (!isKeepLoggedIn && !sessionToken) { - logout() - return - } - } + const status = checkTokenStatus() + if (!status) { + return + } - useAuthStore.getState().setAuthState({ - isAuthenticated: true, - user, - isLoggedOut: false, - }) - } catch (error) { - console.error('Failed to parse user data:', error) - logout() + const interval = setInterval(() => { + const currentStatus = checkTokenStatus() + if (!currentStatus) { + clearInterval(interval) } - } + }, AUTH_TIME.TOKEN_CHECK_INTERVAL) - initializeAuth() - const interval = setInterval(checkTokenStatus, AUTH_TIME.TOKEN_CHECK_INTERVAL) return () => clearInterval(interval) - }, [isKeepLoggedIn, logout]) + }, [pathname, checkTokenStatus]) - return { logout, checkTokenStatus } + return { + logout, + checkTokenStatus, + } } diff --git a/shared/hooks/custom/use-session-expiry-warning.tsx b/shared/hooks/custom/use-session-expiry-warning.tsx new file mode 100644 index 00000000..c4e9d762 --- /dev/null +++ b/shared/hooks/custom/use-session-expiry-warning.tsx @@ -0,0 +1,81 @@ +import { useCallback, useEffect, useRef } from 'react' + +import { AUTH_TIME } from '@/shared/constants/auth' +import { useRefreshTokenMutation } from '@/shared/hooks/query/auth-queries' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import { useModalStore } from '@/shared/stores/use-modal-store' +import { useSessionStore } from '@/shared/stores/use-session-store' +import { isAdmin } from '@/shared/types/auth' +import SessionExtensionModal from '@/shared/ui/modal/session-extension-modal' + +import { useAuth } from './use-auth' + +export const useSessionExpiryWarning = () => { + const { checkTokenStatus } = useAuth() + const { user } = useAuthStore() + const { mutate: refreshToken } = useRefreshTokenMutation() + const { isModalOpen, openModal, closeModal } = useModalStore() + const { setWarningShown, setMinutesLeft, minutesLeft } = useSessionStore() + const warningShownRef = useRef(false) + + useEffect(() => { + if (!isModalOpen) return + + const timer = setInterval(() => { + setMinutesLeft((prev) => { + const newMinutes = Math.max(0, prev - 1) + if (newMinutes === 0) { + closeModal() + warningShownRef.current = false + setWarningShown(false) + } + return newMinutes + }) + }, 60000) + + return () => clearInterval(timer) + }, [isModalOpen, closeModal, setMinutesLeft, setWarningShown]) + + const handleRefreshToken = useCallback(() => { + refreshToken(undefined, { + onSuccess: () => { + closeModal() + warningShownRef.current = false + setWarningShown(false) + }, + onError: (error) => { + console.error('Token refresh failed in session warning:', error) + }, + }) + }, [refreshToken, closeModal, setWarningShown]) + + useEffect(() => { + if (!user || !isAdmin(user)) return + + const checkAndNotify = () => { + const tokenStatus = checkTokenStatus() + + if (tokenStatus?.isNearExpiry && !warningShownRef.current) { + const minutes = Math.floor(tokenStatus.timeUntilExpiry / 60000) + setMinutesLeft(minutes) + setWarningShown(true) + warningShownRef.current = true + openModal() + } + } + + checkAndNotify() + const interval = setInterval(checkAndNotify, AUTH_TIME.TOKEN_CHECK_INTERVAL) + + return () => clearInterval(interval) + }, [checkTokenStatus, openModal, setMinutesLeft, setWarningShown, user]) + + return ( + <SessionExtensionModal + isModalOpen={isModalOpen} + closeModal={closeModal} + onExtend={handleRefreshToken} + minutesLeft={minutesLeft} + /> + ) +} diff --git a/shared/hooks/query/auth-queries.ts b/shared/hooks/query/auth-queries.ts index b5740d21..5a361f6f 100644 --- a/shared/hooks/query/auth-queries.ts +++ b/shared/hooks/query/auth-queries.ts @@ -2,13 +2,11 @@ import { useRouter } from 'next/navigation' import { useMutation } from '@tanstack/react-query' -import { login, logout } from '@/shared/api/auth' +import { login, logout, refreshAccessToken } from '@/shared/api/auth' import axiosInstance from '@/shared/api/axios' -import { STORAGE_KEYS } from '@/shared/constants/auth' import { PATH } from '@/shared/constants/path' -import { setAccessToken } from '@/shared/lib/auth-tokens' +import { removeAccessToken, setAccessToken } from '@/shared/lib/auth-tokens' import { useAuthStore } from '@/shared/stores/use-auth-store' -import { isAdmin } from '@/shared/types/auth' import { getEmailFromToken } from '@/shared/utils/token-utils' export const useLoginMutation = () => { @@ -16,33 +14,32 @@ export const useLoginMutation = () => { mutationFn: login, onSuccess: async (response) => { const accessToken = response.headers['access-token']?.replace('Bearer ', '') - if (accessToken) { - setAccessToken(accessToken) + if (!accessToken) { + throw new Error('No access token received') + } - try { - const userEmail = getEmailFromToken(accessToken) - if (!userEmail) throw new Error('Invalid token') + try { + const userEmail = getEmailFromToken(accessToken) + if (!userEmail) { + throw new Error('Invalid token') + } - const userResponse = await axiosInstance.get( - `/api/users/mypage/profile?email=${userEmail}` - ) - if (userResponse.data.isSuccess) { - localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(userResponse.data.result)) + const userResponse = await axiosInstance.get(`/api/users/mypage/profile?email=${userEmail}`) + if (!userResponse.data.isSuccess) { + throw new Error('Failed to fetch user profile') + } - if (!isAdmin(userResponse.data.result)) { - sessionStorage.setItem(STORAGE_KEYS.SESSION, 'true') - } + const user = userResponse.data.result + setAccessToken(accessToken, user) - useAuthStore.getState().setAuthState({ - isAuthenticated: true, - user: userResponse.data.result, - isLoggedOut: false, - }) - } - } catch (error) { - console.error('Failed to fetch user info:', error) - throw error - } + useAuthStore.getState().setAuthState({ + isAuthenticated: true, + user, + }) + } catch (error) { + console.error('Login process failed:', error) + removeAccessToken() + throw error } }, }) @@ -54,30 +51,72 @@ export const useLogoutMutation = () => { return useMutation({ mutationFn: logout, onSuccess: () => { - localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) - localStorage.removeItem(STORAGE_KEYS.USER) - sessionStorage.removeItem(STORAGE_KEYS.SESSION) - + removeAccessToken() useAuthStore.getState().setAuthState({ isAuthenticated: false, user: null, - isLoggedOut: true, }) - router.replace(PATH.SIGN_IN) }, onError: (error) => { console.error('Logout failed:', error) - localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) - localStorage.removeItem(STORAGE_KEYS.USER) - sessionStorage.removeItem(STORAGE_KEYS.SESSION) - + removeAccessToken() useAuthStore.getState().setAuthState({ isAuthenticated: false, user: null, - isLoggedOut: true, }) + router.replace(PATH.SIGN_IN) + }, + }) +} + +export const useRefreshTokenMutation = () => { + const router = useRouter() + + return useMutation({ + mutationFn: refreshAccessToken, + onSuccess: async (response) => { + const accessToken = response.headers['access-token']?.replace('Bearer ', '') + if (!accessToken) { + throw new Error('No access token received') + } + + try { + const userEmail = getEmailFromToken(accessToken) + if (!userEmail) { + throw new Error('Invalid token after refresh') + } + + const userResponse = await axiosInstance.get(`/api/users/mypage/profile?email=${userEmail}`) + if (!userResponse.data.isSuccess) { + throw new Error('Failed to fetch user profile') + } + + const user = userResponse.data.result + setAccessToken(accessToken, user) + useAuthStore.getState().setAuthState({ + isAuthenticated: true, + user, + }) + } catch (error) { + console.error('Token refresh process failed:', error) + removeAccessToken() + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + }) + router.replace(PATH.SIGN_IN) + throw error + } + }, + onError: (error) => { + console.error('Token refresh mutation failed:', error) + removeAccessToken() + useAuthStore.getState().setAuthState({ + isAuthenticated: false, + user: null, + }) router.replace(PATH.SIGN_IN) }, }) diff --git a/shared/lib/auth-tokens.ts b/shared/lib/auth-tokens.ts index da4859a5..7b6bc6a9 100644 --- a/shared/lib/auth-tokens.ts +++ b/shared/lib/auth-tokens.ts @@ -1,13 +1,28 @@ import { STORAGE_KEYS } from '../constants/auth' +import { useAuthStore } from '../stores/use-auth-store' +import { UserModel, isAdmin } from '../types/auth' -export const setAccessToken = (token: string) => { - localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token) +export const setAccessToken = (token: string, user: UserModel | null) => { + if (!user || !token) return + + const { isKeepLoggedIn } = useAuthStore.getState() + const storage = isAdmin(user) ? sessionStorage : isKeepLoggedIn ? localStorage : sessionStorage + + storage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token) } -export const getAccessToken = () => { - return localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN) +export const getAccessToken = (): string | null => { + if (typeof window === 'undefined') return null + + return ( + sessionStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN) || + localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN) + ) } export const removeAccessToken = () => { + if (typeof window === 'undefined') return + localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + sessionStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) } diff --git a/shared/stores/use-auth-store.ts b/shared/stores/use-auth-store.ts index 7b3eecd8..e53e348e 100644 --- a/shared/stores/use-auth-store.ts +++ b/shared/stores/use-auth-store.ts @@ -1,17 +1,18 @@ import { create } from 'zustand' -import type { UserModel } from '../types/auth' +import { STORAGE_KEYS } from '../constants/auth' +import { type UserModel, isAdmin } from '../types/auth' interface AuthStateModel { isAuthenticated: boolean user: UserModel | null isKeepLoggedIn: boolean - isLoggedOut: boolean } interface AuthActionsModel { setKeepLoggedIn: (value: boolean) => void setAuthState: (state: Partial<AuthStateModel>) => void + initializeAuthState: () => void } type AuthStoreType = AuthStateModel & AuthActionsModel @@ -20,21 +21,135 @@ const initialState: AuthStateModel = { isAuthenticated: false, user: null, isKeepLoggedIn: false, - isLoggedOut: false, } -export const useAuthStore = create<AuthStoreType>((set) => ({ +export const useAuthStore = create<AuthStoreType>()((set) => ({ ...initialState, - setKeepLoggedIn: (value) => - set((state) => ({ - ...state, - isKeepLoggedIn: value, - })), - - setAuthState: (newState) => - set((state) => ({ - ...state, - ...newState, - })), + setKeepLoggedIn: (value) => { + set((state) => { + const updatedState = { + ...state, + isKeepLoggedIn: value, + } + + if (state.isAuthenticated && state.user) { + if (isAdmin(state.user)) { + sessionStorage.setItem( + STORAGE_KEYS.USER, + JSON.stringify({ + isAuthenticated: state.isAuthenticated, + user: state.user, + isKeepLoggedIn: false, + }) + ) + localStorage.removeItem(STORAGE_KEYS.USER) + localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + } else { + const storage = value ? localStorage : sessionStorage + storage.setItem( + STORAGE_KEYS.USER, + JSON.stringify({ + isAuthenticated: state.isAuthenticated, + user: state.user, + isKeepLoggedIn: value, + }) + ) + const otherStorage = value ? sessionStorage : localStorage + otherStorage.removeItem(STORAGE_KEYS.USER) + otherStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + } + } + + return updatedState + }) + }, + + setAuthState: (newState) => { + set((state) => { + const updatedState = { ...state, ...newState } + + if (updatedState.isAuthenticated && updatedState.user) { + if (isAdmin(updatedState.user)) { + sessionStorage.setItem( + STORAGE_KEYS.USER, + JSON.stringify({ + isAuthenticated: updatedState.isAuthenticated, + user: updatedState.user, + isKeepLoggedIn: false, + }) + ) + localStorage.removeItem(STORAGE_KEYS.USER) + localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + } else { + const storage = updatedState.isKeepLoggedIn ? localStorage : sessionStorage + storage.setItem( + STORAGE_KEYS.USER, + JSON.stringify({ + isAuthenticated: updatedState.isAuthenticated, + user: updatedState.user, + isKeepLoggedIn: updatedState.isKeepLoggedIn, + }) + ) + const otherStorage = updatedState.isKeepLoggedIn ? sessionStorage : localStorage + otherStorage.removeItem(STORAGE_KEYS.USER) + otherStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + } + } else { + localStorage.removeItem(STORAGE_KEYS.USER) + localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + sessionStorage.removeItem(STORAGE_KEYS.USER) + sessionStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN) + } + + return updatedState + }) + }, + + initializeAuthState: () => { + if (typeof window === 'undefined') return + + const sessionAuth = sessionStorage.getItem(STORAGE_KEYS.USER) + const localAuth = localStorage.getItem(STORAGE_KEYS.USER) + + if (sessionAuth) { + try { + const authData = JSON.parse(sessionAuth) + if (isAdmin(authData.user)) { + set({ + isAuthenticated: true, + user: authData.user, + isKeepLoggedIn: false, + }) + return + } else { + set({ + isAuthenticated: true, + user: authData.user, + isKeepLoggedIn: false, + }) + return + } + } catch (error) { + console.error('Failed to parse session user data:', error) + sessionStorage.removeItem(STORAGE_KEYS.USER) + } + } + + if (localAuth) { + try { + const authData = JSON.parse(localAuth) + if (!isAdmin(authData.user)) { + set({ + isAuthenticated: true, + user: authData.user, + isKeepLoggedIn: true, + }) + } + } catch (error) { + console.error('Failed to parse local user data:', error) + localStorage.removeItem(STORAGE_KEYS.USER) + } + } + }, })) diff --git a/shared/utils/auth-path.ts b/shared/utils/auth-path.ts new file mode 100644 index 00000000..ced0c131 --- /dev/null +++ b/shared/utils/auth-path.ts @@ -0,0 +1,9 @@ +import { AUTH_REQUIRED_PATTERNS, NON_AUTH_PAGES } from '../constants/auth' + +export const isAuthRequiredPath = (pathname: string): boolean => { + return AUTH_REQUIRED_PATTERNS.some((pattern) => pathname.startsWith(pattern)) +} + +export const isNonAuthPage = (pathname: string): boolean => { + return NON_AUTH_PAGES.some((page) => pathname.startsWith(page)) +} diff --git a/shared/utils/token-utils.ts b/shared/utils/token-utils.ts index 3559978b..2f795560 100644 --- a/shared/utils/token-utils.ts +++ b/shared/utils/token-utils.ts @@ -1,23 +1,24 @@ import { jwtDecode } from 'jwt-decode' import { setAccessToken } from '@/shared/lib/auth-tokens' +import { useAuthStore } from '@/shared/stores/use-auth-store' import { TokenPayloadModel } from '@/shared/types/auth' import { refreshAccessToken } from '../api/auth' +import { AUTH_TIME } from '../constants/auth' -let isRefreshing = false -let refreshSubscribers: ((token: string) => void)[] = [] +let isRefreshInProgress = false +let pendingRefreshRequests: ((token: string) => void)[] = [] export const refreshToken = async (): Promise<string | null> => { - if (isRefreshing) { + if (isRefreshInProgress) { return new Promise((resolve) => { - refreshSubscribers.push((token) => resolve(token)) + pendingRefreshRequests.push((token) => resolve(token)) }) } try { - isRefreshing = true - + isRefreshInProgress = true const response = await refreshAccessToken() if (!response.data.isSuccess) { @@ -25,38 +26,61 @@ export const refreshToken = async (): Promise<string | null> => { } const newAccessToken = response.headers['access-token']?.replace('Bearer ', '') + if (!newAccessToken) { + return null + } - if (newAccessToken) { - setAccessToken(newAccessToken) - refreshSubscribers.forEach((cb) => cb(newAccessToken)) - return newAccessToken + const currentUser = useAuthStore.getState().user + if (!currentUser) { + return null } - return null - } catch (err) { - console.error('Token refresh failed:', err) + setAccessToken(newAccessToken, currentUser) + pendingRefreshRequests.forEach((callback) => callback(newAccessToken)) + return newAccessToken + } catch (error) { + console.error('Token refresh failed:', error) return null } finally { - isRefreshing = false - refreshSubscribers = [] + isRefreshInProgress = false + pendingRefreshRequests = [] } } export const isTokenExpired = (token: string): boolean => { try { const decoded = jwtDecode<TokenPayloadModel>(token) - return decoded.exp * 1000 < Date.now() - } catch { + const currentTime = Date.now() + const expiryTime = decoded.exp * 1000 + + return expiryTime - currentTime <= AUTH_TIME.SAFETY_MARGIN + } catch (error) { + console.error('Token expiry check failed:', error) + return true + } +} + +export const isNearExpiry = (token: string): boolean => { + try { + const decoded = jwtDecode<TokenPayloadModel>(token) + const currentTime = Date.now() + const expiryTime = decoded.exp * 1000 + + return expiryTime - currentTime < AUTH_TIME.ADMIN_EXPIRY_WARNING + } catch (error) { + console.error('Near expiry check failed:', error) return true } } export const getEmailFromToken = (token: string | null): string | null => { if (!token) return null + try { const decoded = jwtDecode<TokenPayloadModel>(token) - return decoded.email - } catch { + return decoded.email || null + } catch (error) { + console.error('Email extraction failed:', error) return null } } @@ -64,8 +88,12 @@ export const getEmailFromToken = (token: string | null): string | null => { export const getTimeUntilExpiry = (token: string): number => { try { const decoded = jwtDecode<TokenPayloadModel>(token) - return decoded.exp * 1000 - Date.now() - } catch { + const currentTime = Date.now() + const expiryTime = decoded.exp * 1000 + + return Math.max(0, expiryTime - currentTime) + } catch (error) { + console.error('Time until expiry calculation failed:', error) return 0 } } From 1cf49718240e8b1817008caee9447a1c43b89467 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 4 Dec 2024 12:12:02 +0900 Subject: [PATCH 525/648] =?UTF-8?q?design:=20=EA=B3=B5=EC=A7=80=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/_ui/admin-header/index.tsx | 34 ++++------ app/admin/_ui/admin-header/styles.module.scss | 9 +++ .../notices/_ui/notice-post-button/index.tsx | 27 ++++++++ .../_ui/notice-post-button/styles.module.scss | 6 ++ app/admin/notices/page.module.scss | 20 +++++- app/admin/notices/page.tsx | 68 +++++++++++-------- app/admin/notices/tabledata.tsx | 46 +++++++++++-- shared/ui/header/styles.module.scss | 2 +- 8 files changed, 154 insertions(+), 58 deletions(-) create mode 100644 app/admin/_ui/admin-header/styles.module.scss create mode 100644 app/admin/notices/_ui/notice-post-button/index.tsx create mode 100644 app/admin/notices/_ui/notice-post-button/styles.module.scss diff --git a/app/admin/_ui/admin-header/index.tsx b/app/admin/_ui/admin-header/index.tsx index a45bbd16..cac6b4e7 100644 --- a/app/admin/_ui/admin-header/index.tsx +++ b/app/admin/_ui/admin-header/index.tsx @@ -1,31 +1,25 @@ import { CSSProperties, ReactNode } from 'react' -import Header from '@/shared/ui/header' -import { SearchInput } from '@/shared/ui/search-input' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) interface Props { Left?: ReactNode Right?: ReactNode styles?: CSSProperties + className?: string } -const headerStyles: CSSProperties = { - position: 'static', - padding: '0', - height: 'calc(40px * 2 + 18px)', - // font B3 - fontSize: '14px', - fontWeight: '600', - lineHeight: '132%', - letterSpacing: '-0.28px', -} - -const AdminContentsHeader = ({ Left, Right }: Props) => { - return <Header as="div" Left={Left} Right={Right} styles={headerStyles} /> -} - -const Search = () => { - return <SearchInput placeholder="제목을 검색하세요." /> +const AdminContentsHeader = ({ Left, Right, className }: Props) => { + return ( + <div className={cx('container', className)}> + {Left} + {Right} + </div> + ) } -export default Object.assign(AdminContentsHeader, { Search }) +export default AdminContentsHeader diff --git a/app/admin/_ui/admin-header/styles.module.scss b/app/admin/_ui/admin-header/styles.module.scss new file mode 100644 index 00000000..5feb4570 --- /dev/null +++ b/app/admin/_ui/admin-header/styles.module.scss @@ -0,0 +1,9 @@ +.container { + z-index: zIndex(header); + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 40px 0; + @include typo-b3; +} diff --git a/app/admin/notices/_ui/notice-post-button/index.tsx b/app/admin/notices/_ui/notice-post-button/index.tsx new file mode 100644 index 00000000..780020da --- /dev/null +++ b/app/admin/notices/_ui/notice-post-button/index.tsx @@ -0,0 +1,27 @@ +'use client' + +import { useRouter } from 'next/navigation' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const NoticePostButton = () => { + const router = useRouter() + + const onClick = () => { + router.push('/admin/notices/post') + } + + return ( + <Button onClick={onClick} variant="filled" size="small" className={cx('post-button')}> + 공지 등록하기 + </Button> + ) +} + +export default NoticePostButton diff --git a/app/admin/notices/_ui/notice-post-button/styles.module.scss b/app/admin/notices/_ui/notice-post-button/styles.module.scss new file mode 100644 index 00000000..f8fba06a --- /dev/null +++ b/app/admin/notices/_ui/notice-post-button/styles.module.scss @@ -0,0 +1,6 @@ +.post-button { + position: absolute; + top: -55px; + right: 0; + padding: 12px 24px; +} diff --git a/app/admin/notices/page.module.scss b/app/admin/notices/page.module.scss index 9dfc496a..02684f1a 100644 --- a/app/admin/notices/page.module.scss +++ b/app/admin/notices/page.module.scss @@ -1,3 +1,21 @@ -.color-primary-500 { +.container { + position: relative; + padding: 0 25px 37px; // table 기본 padding 때문에 20 뺌 + border-radius: 8px; + margin-bottom: 42px; + background-color: $color-white; +} + +.header { + padding: 40px 20px 20px; +} + +.colored { color: $color-orange-500; } + +.button { + width: 74px; + height: 30px; + border: 1px solid $color-gray-300; +} diff --git a/app/admin/notices/page.tsx b/app/admin/notices/page.tsx index 05830056..e3e3175c 100644 --- a/app/admin/notices/page.tsx +++ b/app/admin/notices/page.tsx @@ -1,65 +1,75 @@ 'use client' -// TODO: ssr import classNames from 'classnames/bind' +import { Button } from '@/shared/ui/button' import Pagination from '@/shared/ui/pagination' import { SearchInput } from '@/shared/ui/search-input' import VerticalTable from '@/shared/ui/table/vertical' import Title from '@/shared/ui/title' import AdminContentsHeader from '../_ui/admin-header' +import NoticePostButton from './_ui/notice-post-button' import styles from './page.module.scss' +import { RES } from './tabledata' const cx = classNames.bind(styles) +const TOTAL_NOTICE = RES.data.content.length + const AdminNoticesPage = () => { return ( <> - {/* TODO: inline css 제거 */} <Title label="공지사항" style={{ margin: '80px 0 26px 12.6px' }} /> - <div - style={{ - padding: '0 45px 37px', - borderRadius: '8px', - marginBottom: '42px', - backgroundColor: 'aliceblue', - }} - > + <div className={cx('container')}> + <NoticePostButton /> <AdminContentsHeader - // TODO: 실제 개수로 바인딩 Left={ <span> - 총 <span className={cx('color-primary-500')}>30</span>명 + 총 <span className={cx('colored')}>{TOTAL_NOTICE}</span>개 </span> } - Right={<SearchInput placeholder="제목을 검색하세요." />} + Right={<SearchInput placeholder="제목을 입력하세요." />} + className={cx('header')} /> <VerticalTable tableHead={['No.', '제목', '등록일', '작성일', '']} tableBody={ - [] - // TABLE_DATA.map((d) => ({ - // ...d, - // content: ( - // <Button.ButtonGroup> - // {/* TODO: onclick 로직 정의 */} - // <Button size="small" onClick={() => {}}> - // 수정 - // </Button> - // <Button size="small" onClick={() => {}} variant="filled"> - // 삭제 - // </Button> - // </Button.ButtonGroup> - // ), - // })) + // [] + RES.data.content.map((d, idx) => [ + idx + 1, + d.title, + d.publishedAt.slice(0, 10), + d.createdAt.slice(0, 10), + <Button.ButtonGroup key={d.content}> + {/* TODO: onclick 로직 정의 */} + + <Button + onClick={() => {}} + size="small" + className={cx('button')} + style={{ padding: '16px 7px' }} + > + 수정 + </Button> + <Button + size="small" + onClick={() => {}} + variant="filled" + className={cx('button')} + style={{ padding: '16px 7px' }} + > + 삭제 + </Button> + </Button.ButtonGroup>, + ]) } // TODO: 실제 값으로 추가 countPerPage={10} currentPage={1} /> {/* TODO: 실제 값으로 추가 */} - <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + <Pagination currentPage={1} maxPage={1} onPageChange={() => {}} /> </div> </> ) diff --git a/app/admin/notices/tabledata.tsx b/app/admin/notices/tabledata.tsx index 124113d9..c474022d 100644 --- a/app/admin/notices/tabledata.tsx +++ b/app/admin/notices/tabledata.tsx @@ -1,7 +1,39 @@ -export const TABLE_DATA = Array.from({ length: 30 }, (_, i) => ({ - idx: i, - title: `제목 ${i + 1}`, - createdAt: new Intl.DateTimeFormat('ko-KR').format(new Date(2024, 11, i + 1)), - updatedAt: new Intl.DateTimeFormat('ko-KR').format(new Date(2024, 11, i + 2)), - content: <div>내용 ${i + 1}</div>, -})) +export const RES = { + isSuccess: true, + message: '공지사항 목록 조회 성공', + data: { + content: [ + { + noticeId: 1, + user: { + id: 2, + nickname: '관리자', + }, + title: '서비스 점검 안내', //공지사항 제목 + content: '점검예정.', //공지사항 내용 + createdAt: '2024-01-01 12:00:00', // 등록일 + publishedAt: '2024-11-15T09:00:00', //공개일 + createdBy: '관리자', // 등록자 + }, + { + noticeId: 2, + user: { + id: 3, + nickname: '관리자', + }, + title: '업데이트 안내', + content: '이번 업데이트 내용은', + nickname: '관리자', + createdAt: '2024-01-02 12:00:00', + publishedAt: '2024-11-15T09:00:00', + createdBy: '관리자', + }, + ], + pageable: { + pageNumber: 0, + pageSize: 20, + }, + totalPages: 5, + totalElements: 50, + }, +} diff --git a/shared/ui/header/styles.module.scss b/shared/ui/header/styles.module.scss index 1bfb8fae..cffd0a03 100644 --- a/shared/ui/header/styles.module.scss +++ b/shared/ui/header/styles.module.scss @@ -16,7 +16,7 @@ } .right-wrapper { - input { + input[type='checkbox'] { display: none; &:checked { From a72a1ed57c8563cd2d9bc1d2a9bcd5b323b89a61 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 4 Dec 2024 12:35:00 +0900 Subject: [PATCH 526/648] =?UTF-8?q?fix:=20closeModal=20=3D>=20onCloseModal?= =?UTF-8?q?=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/hooks/custom/use-session-expiry-warning.tsx | 12 ++++++------ shared/stores/use-modal-store.ts | 4 ++-- shared/ui/modal/session-extension-modal/index.tsx | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/shared/hooks/custom/use-session-expiry-warning.tsx b/shared/hooks/custom/use-session-expiry-warning.tsx index c4e9d762..34ed1afc 100644 --- a/shared/hooks/custom/use-session-expiry-warning.tsx +++ b/shared/hooks/custom/use-session-expiry-warning.tsx @@ -14,7 +14,7 @@ export const useSessionExpiryWarning = () => { const { checkTokenStatus } = useAuth() const { user } = useAuthStore() const { mutate: refreshToken } = useRefreshTokenMutation() - const { isModalOpen, openModal, closeModal } = useModalStore() + const { isModalOpen, openModal, onCloseModal } = useModalStore() const { setWarningShown, setMinutesLeft, minutesLeft } = useSessionStore() const warningShownRef = useRef(false) @@ -25,7 +25,7 @@ export const useSessionExpiryWarning = () => { setMinutesLeft((prev) => { const newMinutes = Math.max(0, prev - 1) if (newMinutes === 0) { - closeModal() + onCloseModal() warningShownRef.current = false setWarningShown(false) } @@ -34,12 +34,12 @@ export const useSessionExpiryWarning = () => { }, 60000) return () => clearInterval(timer) - }, [isModalOpen, closeModal, setMinutesLeft, setWarningShown]) + }, [isModalOpen, onCloseModal, setMinutesLeft, setWarningShown]) const handleRefreshToken = useCallback(() => { refreshToken(undefined, { onSuccess: () => { - closeModal() + onCloseModal() warningShownRef.current = false setWarningShown(false) }, @@ -47,7 +47,7 @@ export const useSessionExpiryWarning = () => { console.error('Token refresh failed in session warning:', error) }, }) - }, [refreshToken, closeModal, setWarningShown]) + }, [refreshToken, onCloseModal, setWarningShown]) useEffect(() => { if (!user || !isAdmin(user)) return @@ -73,7 +73,7 @@ export const useSessionExpiryWarning = () => { return ( <SessionExtensionModal isModalOpen={isModalOpen} - closeModal={closeModal} + onCloseModal={onCloseModal} onExtend={handleRefreshToken} minutesLeft={minutesLeft} /> diff --git a/shared/stores/use-modal-store.ts b/shared/stores/use-modal-store.ts index e68ce238..fd78f6bc 100644 --- a/shared/stores/use-modal-store.ts +++ b/shared/stores/use-modal-store.ts @@ -3,11 +3,11 @@ import { create } from 'zustand' interface ModalStoreModel { isModalOpen: boolean openModal: () => void - closeModal: () => void + onCloseModal: () => void } export const useModalStore = create<ModalStoreModel>((set) => ({ isModalOpen: false, openModal: () => set({ isModalOpen: true }), - closeModal: () => set({ isModalOpen: false }), + onCloseModal: () => set({ isModalOpen: false }), })) diff --git a/shared/ui/modal/session-extension-modal/index.tsx b/shared/ui/modal/session-extension-modal/index.tsx index dcaee74e..58dd8f8c 100644 --- a/shared/ui/modal/session-extension-modal/index.tsx +++ b/shared/ui/modal/session-extension-modal/index.tsx @@ -14,14 +14,14 @@ const cx = classNames.bind(styles) interface SessionExtensionModalProps { isModalOpen: boolean - closeModal: () => void + onCloseModal: () => void onExtend: () => void minutesLeft: number } const SessionExtensionModal = ({ isModalOpen, - closeModal, + onCloseModal, onExtend, minutesLeft, }: SessionExtensionModalProps) => { @@ -42,7 +42,7 @@ const SessionExtensionModal = ({ 로그인을 연장하시겠습니까? </span> <div className={cx('two-button')}> - <Button onClick={closeModal}>아니오</Button> + <Button onClick={onCloseModal}>아니오</Button> <Button onClick={onExtend} variant="filled" className={cx('button')}> 예 </Button> From 27e24f55547c2c867df555563bdce0b985536a87 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 4 Dec 2024 12:35:43 +0900 Subject: [PATCH 527/648] =?UTF-8?q?fix:=20axios=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EB=A7=88=EB=8B=A4=20=EC=B5=9C=EC=8B=A0=20store=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A5=BC=20=EC=B0=B8=EC=A1=B0=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/axios.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/shared/api/axios.ts b/shared/api/axios.ts index be96c478..cd7d98a7 100644 --- a/shared/api/axios.ts +++ b/shared/api/axios.ts @@ -6,7 +6,6 @@ import { useAuthStore } from '@/shared/stores/use-auth-store' import { isTokenExpired, refreshToken } from '@/shared/utils/token-utils' export const createAxiosInstance = (options: { withInterceptors?: boolean } = {}) => { - const { user, setAuthState } = useAuthStore.getState() const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_HOST, withCredentials: true, @@ -15,6 +14,7 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} if (options.withInterceptors && typeof window !== 'undefined') { instance.interceptors.request.use( async (config: InternalAxiosRequestConfig) => { + const { user, setAuthState } = useAuthStore.getState() const accessToken = getAccessToken() if (!user || !accessToken) { @@ -47,7 +47,11 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} instance.interceptors.response.use( (response) => response, async (err: AxiosError) => { - if (!err.config || !user) { + if (!err.config) return Promise.reject(err) + + const { user } = useAuthStore.getState() + + if (!user) { return Promise.reject(err) } @@ -64,7 +68,7 @@ export const createAxiosInstance = (options: { withInterceptors?: boolean } = {} console.error('Token refresh failed:', refreshError) } - setAuthState({ + useAuthStore.getState().setAuthState({ isAuthenticated: false, user: null, }) From b3acb65fd19b08432376dc84ca4c484991efd54c Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Wed, 4 Dec 2024 12:39:27 +0900 Subject: [PATCH 528/648] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 7212214b..5e14ea54 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -16,8 +16,10 @@ const RootLayout = ({ children }: { children: React.ReactNode }) => { <html lang="ko" className={pretendard.variable}> <body> <QueryProvider> - <div id="modal-root"></div> - <AuthProvider>{children}</AuthProvider> + <AuthProvider> + {children} + <div id="modal-root" /> + </AuthProvider> </QueryProvider> </body> </html> From b0cc6026ad4f18f362334992558102fb21206e62 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 12:44:19 +0900 Subject: [PATCH 529/648] =?UTF-8?q?design:=20=EB=94=94=EC=9E=90=EC=9D=B8?= =?UTF-8?q?=EA=B3=BC=20=EB=8B=A4=EB=A5=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#221)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/input/styles.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/input/styles.module.scss b/shared/ui/input/styles.module.scss index 24e80ffb..0ea5e540 100644 --- a/shared/ui/input/styles.module.scss +++ b/shared/ui/input/styles.module.scss @@ -1,6 +1,6 @@ .input { color: $color-gray-400; - border: 1px solid $color-gray-400; + border: 1px solid $color-gray-300; border-radius: 2px; height: 48px; padding: 14px 24px; From a8da621baa17fbeb892e273f52f7c62621d6250b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 12:45:34 +0900 Subject: [PATCH 530/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20&=20=EB=AC=B8=EC=9D=98?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=EC=84=B1=EA=B3=B5=20=EC=95=88=EB=82=B4=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80=20(#221)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/add-question-modal.tsx | 74 ++++++++++++++++++++++++ shared/ui/modal/question-guide-modal.tsx | 34 +++++++++++ shared/ui/modal/styles.module.scss | 34 +++++++++++ 3 files changed, 142 insertions(+) create mode 100644 shared/ui/modal/add-question-modal.tsx create mode 100644 shared/ui/modal/question-guide-modal.tsx diff --git a/shared/ui/modal/add-question-modal.tsx b/shared/ui/modal/add-question-modal.tsx new file mode 100644 index 00000000..5cfc5945 --- /dev/null +++ b/shared/ui/modal/add-question-modal.tsx @@ -0,0 +1,74 @@ +'use client' + +import React from 'react' + +import { RegisterIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Modal from '.' +import { Button } from '../button' +import { ErrorMessage } from '../error-message' +import { Input } from '../input' +import { Textarea } from '../textarea' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isModalOpen: boolean + isEmpty: boolean + strategyName: string + title?: string + content?: string + titleRef?: React.RefObject<HTMLInputElement> + contentRef?: React.RefObject<HTMLTextAreaElement> + closeModal: () => void + onChange?: () => void +} + +const AddQuestionModal = ({ + isModalOpen, + isEmpty = false, + strategyName, + title, + content, + titleRef, + contentRef, + closeModal, + onChange, +}: Props) => { + return ( + <Modal + isOpen={isModalOpen} + message={title ? '추가 문의하기' : '문의하기'} + icon={RegisterIcon} + className="question" + > + <span className={cx('message', 'strategy-name')}>{strategyName}</span> + <div className={cx('question-title')}> + <p>제목</p> + <div> + <Input + ref={titleRef} + value={title && title} + inputSize="full" + placeholder="제목을 입력하세요" + /> + </div> + </div> + <div className={cx('question-content')}> + <p>내용</p> + <Textarea ref={contentRef} value={content && content} placeholder="내용을 입력하세요." /> + </div> + {isEmpty && <ErrorMessage errorMessage="제목 또는 내용을 모두 입력해주세요." />} + <div className={cx('two-button', 'question')}> + <Button onClick={closeModal}>취소</Button> + <Button onClick={onChange} variant="filled" className={cx('button')}> + 문의 + </Button> + </div> + </Modal> + ) +} + +export default AddQuestionModal diff --git a/shared/ui/modal/question-guide-modal.tsx b/shared/ui/modal/question-guide-modal.tsx new file mode 100644 index 00000000..acc2cbb8 --- /dev/null +++ b/shared/ui/modal/question-guide-modal.tsx @@ -0,0 +1,34 @@ +'use client' + +import React from 'react' + +import { ModalCheckIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Modal from '.' +import { Button } from '../button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isModalOpen: boolean + closeModal: () => void +} + +const QuestionGuideModal = ({ isModalOpen, closeModal }: Props) => { + return ( + <Modal isOpen={isModalOpen} icon={ModalCheckIcon}> + <span className={cx('message')}> + 문의가 등록되었습니다. + <br /> + 문의 등록 내용 및 답변은 + <br /> + 문의 페이지에서 확인 가능합니다. + </span> + <Button onClick={closeModal}>닫기</Button> + </Modal> + ) +} + +export default QuestionGuideModal diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index 3d510751..fc5e53e2 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -21,6 +21,19 @@ flex-direction: column; align-items: center; justify-content: center; + &.question { + width: 600px; + .two-button { + gap: 40px; + } + .message { + margin: 30px 0; + &.strategy-name { + @include typo-h4; + margin: 0 0 30px 0; + } + } + } } .icon { @@ -48,4 +61,25 @@ & .button { width: 90px; } + &.question { + margin-top: 20px; + } +} + +.question-title, +.question-content { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 30px; + padding: 0 20px; + p { + width: 30px; + margin-right: 20px; + @include typo-b3; + } + div { + width: 100%; + } } From d57d551ec50eb3cbbd162b5a1db88d2bad288791 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 12:45:52 +0900 Subject: [PATCH 531/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20=EC=A0=81=EC=9A=A9=20(#221)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-side-item/index.tsx | 9 ++- .../_ui/details-side-item/side-item.tsx | 55 +++++++++++++++++-- .../strategies/[strategyId]/page.tsx | 1 + 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/_ui/details-side-item/index.tsx b/app/(dashboard)/_ui/details-side-item/index.tsx index 7eb90ce4..7ec97e00 100644 --- a/app/(dashboard)/_ui/details-side-item/index.tsx +++ b/app/(dashboard)/_ui/details-side-item/index.tsx @@ -23,9 +23,15 @@ interface Props { information: InformationModel | InformationModel[] profileImage?: string isMyStrategy?: boolean + strategyName?: string } -const DetailsSideItem = ({ information, profileImage, isMyStrategy = true }: Props) => { +const DetailsSideItem = ({ + information, + profileImage, + isMyStrategy = true, + strategyName, +}: Props) => { const isArray = Array.isArray(information) return ( <> @@ -46,6 +52,7 @@ const DetailsSideItem = ({ information, profileImage, isMyStrategy = true }: Pro data={information.data} profileImage={profileImage} isMyStrategy={isMyStrategy} + strategyName={strategyName} /> )} </> diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index 621a509a..eff0e139 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -1,8 +1,12 @@ +import { useRef, useState } from 'react' + import classNames from 'classnames/bind' -import { PATH } from '@/shared/constants/path' +import useModal from '@/shared/hooks/custom/use-modal' import Avatar from '@/shared/ui/avatar' -import { LinkButton } from '@/shared/ui/link-button' +import { Button } from '@/shared/ui/button' +import AddQuestionModal from '@/shared/ui/modal/add-question-modal' +import QuestionGuideModal from '@/shared/ui/modal/question-guide-modal' import { formatNumber } from '@/shared/utils/format' import { TitleType } from '.' @@ -15,9 +19,38 @@ interface Props { data: number | string profileImage?: string isMyStrategy?: boolean + strategyName?: string } -const SideItem = ({ title, data, profileImage, isMyStrategy = false }: Props) => { +const SideItem = ({ title, data, profileImage, isMyStrategy = false, strategyName }: Props) => { + const [isEmpty, setIsEmpty] = useState(false) + const { + isModalOpen: isAddQuestionModalOpen, + openModal: questionOpenModal, + closeModal: questionCloseModal, + } = useModal() + const { + isModalOpen: isQuestionGuideModalOpen, + openModal: guideOpenModal, + closeModal: guideCloseModal, + } = useModal() + const titleRef = useRef<HTMLInputElement | null>(null) + const contentRef = useRef<HTMLTextAreaElement | null>(null) + + const handleAddQuestion = () => { + if (titleRef.current && contentRef.current) { + const title = titleRef.current.value.trim() + const content = contentRef.current.value.trim() + if (title !== '' && content !== '') { + // TODO: 문의 추가 api 연결 + questionCloseModal() + guideOpenModal() + } else { + setIsEmpty(true) + } + } + } + return ( <div className={cx('side-item')}> <div className={cx('title')}>{title}</div> @@ -29,15 +62,27 @@ const SideItem = ({ title, data, profileImage, isMyStrategy = false }: Props) => <p>{data}</p> </div> {!isMyStrategy && ( - <LinkButton href={PATH.MY_QUESTIONS} size="small" style={{ height: '30px' }}> + <Button onClick={questionOpenModal} size="small" style={{ height: '30px' }}> 문의하기 - </LinkButton> + </Button> )} </> ) : ( <p>{formatNumber(data)}</p> )} </div> + {strategyName && ( + <AddQuestionModal + strategyName={strategyName} + isModalOpen={isAddQuestionModalOpen} + titleRef={titleRef} + contentRef={contentRef} + closeModal={questionCloseModal} + onChange={handleAddQuestion} + isEmpty={isEmpty} + /> + )} + <QuestionGuideModal isModalOpen={isQuestionGuideModalOpen} closeModal={guideCloseModal} /> </div> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 101afc68..131c7eb2 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -73,6 +73,7 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { <DetailsSideItem information={data} isMyStrategy={user?.nickname === information.nickname} + strategyName={information.strategyName} /> </div> ))} From 2c6175a21da563c9d87f031544bc47a07ff35b96 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 12:56:35 +0900 Subject: [PATCH 532/648] =?UTF-8?q?design:=20padding=EA=B0=92=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#221)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/add-question-modal.tsx | 7 ++++++- shared/ui/modal/styles.module.scss | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/shared/ui/modal/add-question-modal.tsx b/shared/ui/modal/add-question-modal.tsx index 5cfc5945..d23e93db 100644 --- a/shared/ui/modal/add-question-modal.tsx +++ b/shared/ui/modal/add-question-modal.tsx @@ -58,7 +58,12 @@ const AddQuestionModal = ({ </div> <div className={cx('question-content')}> <p>내용</p> - <Textarea ref={contentRef} value={content && content} placeholder="내용을 입력하세요." /> + <Textarea + ref={contentRef} + value={content && content} + placeholder="내용을 입력하세요." + className={cx('content')} + /> </div> {isEmpty && <ErrorMessage errorMessage="제목 또는 내용을 모두 입력해주세요." />} <div className={cx('two-button', 'question')}> diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index fc5e53e2..7b40476b 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -82,4 +82,7 @@ div { width: 100%; } + & .content { + padding: 14px 24px; + } } From 0dc26305d50c058e554c2a748ebbf5ab0c3ade9f Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 4 Dec 2024 14:40:00 +0900 Subject: [PATCH 533/648] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94?= =?UTF-8?q?=EB=A6=AC=EC=8B=B1=20(#230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/_ui/notice-table/index.tsx | 94 +++++++++++++++++++ .../_ui/notice-table/styles.module.scss | 48 ++++++++++ app/(landing)/notices/page.module.scss | 5 + app/(landing)/notices/page.tsx | 19 ++++ shared/ui/table/vertical/index.tsx | 6 +- 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 app/(landing)/notices/_ui/notice-table/index.tsx create mode 100644 app/(landing)/notices/_ui/notice-table/styles.module.scss create mode 100644 app/(landing)/notices/page.module.scss create mode 100644 app/(landing)/notices/page.tsx diff --git a/app/(landing)/notices/_ui/notice-table/index.tsx b/app/(landing)/notices/_ui/notice-table/index.tsx new file mode 100644 index 00000000..f9b10716 --- /dev/null +++ b/app/(landing)/notices/_ui/notice-table/index.tsx @@ -0,0 +1,94 @@ +'use client' + +import Link from 'next/link' + +import classNames from 'classnames/bind' + +import { PATH } from '@/shared/constants/path' +import { usePagination } from '@/shared/hooks/custom/use-pagination' +import Pagination from '@/shared/ui/pagination' +import VerticalTable from '@/shared/ui/table/vertical' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const COUNT_PER_PAGE = 7 + +export interface NoticeListContentModel { + no: number + title: React.ReactNode + createdAt: string +} + +const NoticeTable = () => { + const { page, handlePageChange } = usePagination({ + basePath: PATH.NOTICES, + pageSize: COUNT_PER_PAGE, + }) + + const data = { + content: [ + { + noticeId: 1, + user: { + userId: 1, + nickname: '투자왕', + }, + title: '오늘의 공지사항은 무엇일까요 궁금하면 클릭', + content: 'content3', + createdAt: '2024.12.04', + }, + { + noticeId: 2, + user: { + userId: 1, + nickname: '투자왕', + }, + title: '오늘의 공지사항은 무엇일까요 궁금하면 클릭', + content: 'content3', + createdAt: '2024.12.04', + }, + { + noticeId: 3, + user: { + userId: 1, + nickname: '투자왕', + }, + title: '오늘의 공지사항은 무엇일까요 궁금하면 클릭', + content: 'content3', + createdAt: '2024.12.04', + }, + ], + page: 1, + size: 3, + totalElements: 3, + totalPages: 1, + first: true, + last: true, + } + + const notices = data.content.map((values, idx) => ({ + no: (page - 1) * COUNT_PER_PAGE + (idx + 1), + title: <Link href={`${PATH.NOTICES}/${values.noticeId}`}>{values.title}</Link>, + createdAt: values.createdAt, + })) + + return ( + <div className={cx('table-wrapper')}> + <p className={cx('count')}> + 총 <span className={cx('highlight')}>{data.totalElements}</span>개 + </p> + <VerticalTable + className={cx('table')} + countPerPage={COUNT_PER_PAGE} + currentPage={page} + tableBody={notices} + tableHead={['No.', '제목', '등록일']} + /> + <Pagination currentPage={page} maxPage={data.totalPages} onPageChange={handlePageChange} /> + </div> + ) +} + +export default NoticeTable diff --git a/app/(landing)/notices/_ui/notice-table/styles.module.scss b/app/(landing)/notices/_ui/notice-table/styles.module.scss new file mode 100644 index 00000000..ab83d45d --- /dev/null +++ b/app/(landing)/notices/_ui/notice-table/styles.module.scss @@ -0,0 +1,48 @@ +.table-wrapper { + padding: 40px 0; + margin-bottom: 80px; + border-radius: 8px; + background-color: $color-white; +} + +.table { + min-height: 450px; + + table { + thead td { + height: 40px; + } + + td { + height: 60px; + + &:nth-child(1) { + width: 10%; + text-align: center; + } + + &:nth-child(2) { + width: 80%; + padding-left: 40px; + + @include mobile { + padding-left: 0; + } + } + + &:nth-child(3) { + width: 10%; + } + } + } +} + +.count { + padding-left: 20px; + margin-bottom: 20px; + @include typo-b3; + + .highlight { + color: $color-orange-500; + } +} diff --git a/app/(landing)/notices/page.module.scss b/app/(landing)/notices/page.module.scss new file mode 100644 index 00000000..9ffbde90 --- /dev/null +++ b/app/(landing)/notices/page.module.scss @@ -0,0 +1,5 @@ +.title { + margin: 70px 0 36px; + @include typo-h4; + color: $color-gray-700; +} diff --git a/app/(landing)/notices/page.tsx b/app/(landing)/notices/page.tsx new file mode 100644 index 00000000..0778e79c --- /dev/null +++ b/app/(landing)/notices/page.tsx @@ -0,0 +1,19 @@ +import classNames from 'classnames/bind' + +import VerticalTable from '@/shared/ui/table/vertical' + +import NoticeTable from './_ui/notice-table' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const NoticePage = () => { + return ( + <> + <h1 className={cx('title')}>공지사항</h1> + <NoticeTable /> + </> + ) +} + +export default NoticePage diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index ccc671cd..124a12a3 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -1,6 +1,7 @@ // import { TABLE_DATA } from '@/app/admin/notices/tabledata' import { ReactNode } from 'react' +import { NoticeListContentModel } from '@/app/(landing)/notices/_ui/notice-table' import classNames from 'classnames/bind' import { DailyAnalysisModel, MonthlyAnalysisModel } from '@/shared/types/strategy-data' @@ -15,6 +16,7 @@ const cx = classNames.bind(styles) type TableBodyDataType = | DailyAnalysisModel | MonthlyAnalysisModel + | NoticeListContentModel | Array<ReactNode | string | number> export interface VerticalTableProps { @@ -23,6 +25,7 @@ export interface VerticalTableProps { countPerPage: number currentPage: number isEditable?: boolean + className?: string } const VerticalTable = ({ @@ -31,12 +34,13 @@ const VerticalTable = ({ countPerPage, currentPage, isEditable = false, + className, }: VerticalTableProps) => { const hasData = tableBody.length > 0 const slicedTableBody = sliceArray(tableBody, countPerPage, currentPage) return ( - <div className={cx('container')}> + <div className={cx('container', className)}> <table> <thead> <tr> From ced6388ac399229ea1f3d4cff4d50d0c62822c3a Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 15:01:36 +0900 Subject: [PATCH 534/648] =?UTF-8?q?bug:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20(#2?= =?UTF-8?q?22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-information/index.tsx | 10 +++++----- app/(dashboard)/_ui/strategies-item/index.tsx | 4 ++-- .../_ui/strategies-item/strategies-icon.tsx | 19 +++++++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/(dashboard)/_ui/details-information/index.tsx b/app/(dashboard)/_ui/details-information/index.tsx index 50bf7e2d..85a27b07 100644 --- a/app/(dashboard)/_ui/details-information/index.tsx +++ b/app/(dashboard)/_ui/details-information/index.tsx @@ -28,13 +28,13 @@ const DetailsInformation = ({ information, type = 'default' }: Props) => { <> <div className={cx('information-top')}> <StrategyNameBox - // iconUrls={[ - // information.tradeTypeIconUrl, - // ...(information.stockTypeInfo?.stockTypeIconUrls || []), - // ]} + iconUrls={[ + information.tradeTypeIconUrl, + ...(information.stockTypeInfo?.stockTypeIconUrls ?? []), + ]} iconNames={[ information.tradeTypeName, - ...(information.stockTypeInfo?.stockTypeNames || []), + ...(information.stockTypeInfo?.stockTypeNames ?? []), ]} name={information.strategyName} /> diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index dc9bcf01..4dfff730 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -22,8 +22,8 @@ const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { return ( <Link className={cx('container', type)} href={`/strategies/${data.strategyId}`}> <StrategiesSummary - // iconUrls={[data.tradeTypeIconUrl, ...data.stockTypeInfo.stockTypeIconUrls]} - // iconNames={[data.tradeTypeName, ...data.stockTypeInfo.stockTypeNames]} + iconUrls={[data.tradeTypeIconUrl, ...(data.stockTypeInfo?.stockTypeIconUrls ?? [])]} + iconNames={[data.tradeTypeName, ...(data.stockTypeInfo?.stockTypeNames ?? [])]} title={data.strategyName} profile={{ traderImage: data.traderImgUrl, diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 0f740762..295eb72f 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -25,6 +25,7 @@ interface Props { const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { const [imageSizes, setImageSizes] = useState<{ [key: string]: ImageSizeModel }>({}) + const [validImages, setValidImages] = useState<{ [key: string]: boolean }>({}) useEffect(() => { const images: HTMLImageElement[] = [] @@ -53,6 +54,14 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { const getImageSize = (url: string) => imageSizes[url] || { width: 20, height: 20 } + const handleImageErr = (url: string) => { + setValidImages((prev) => ({ ...prev, [url]: false })) + } + + const handleImageLoad = (url: string) => { + setValidImages((prev) => ({ ...prev, [url]: true })) + } + if (iconUrls?.length === 0 || iconNames?.length === 0) return null if (iconUrls?.length !== iconNames?.length) return null @@ -60,7 +69,7 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { <div className={cx('icon-container')}> {iconUrls?.map((url, idx) => { const name = iconNames?.[idx] - if (!url || !name) return null + if (!url || !name || validImages[url] === false) return null const { width, height } = getImageSize(url) return ( @@ -75,7 +84,13 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { data-tooltip-content={name} data-tooltip-class-name="tooltip" > - <Image src={url} alt={name} fill /> + <Image + src={url} + alt={name} + fill + onLoadingComplete={() => handleImageLoad(url)} + onError={() => handleImageErr(url)} + /> </div> <Tooltip id={name} className={cx('tooltip')} classNameArrow={cx('arrow')} opacity={1}> {name} From b522afa2a76ef03c2d510fa04fabefca1ffa2bff Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 15:02:49 +0900 Subject: [PATCH 535/648] =?UTF-8?q?fix:=20=EC=B0=A8=ED=8A=B8=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC=20(#222)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/analysis-container/analysis-chart.tsx | 4 ++-- app/(dashboard)/_ui/strategies-item/area-chart.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/analysis-chart.tsx b/app/(dashboard)/_ui/analysis-container/analysis-chart.tsx index bbc5e72f..8eff7139 100644 --- a/app/(dashboard)/_ui/analysis-container/analysis-chart.tsx +++ b/app/(dashboard)/_ui/analysis-container/analysis-chart.tsx @@ -32,13 +32,13 @@ const AnalysisChart = ({ analysisChartData: data }: Props) => { const key = Object.keys(data.data)[sequence] as YAxisType | undefined return key ? YAXIS_OPTIONS[key] : '' } - if (!data) return <p>등록된 데이터가 없습니다.</p> + if (!data) return <div></div> const chartOptions: Highcharts.Options = { chart: { type: 'areaspline', height: 367, backgroundColor: 'transparent', - margin: [10, 50, 10, 50], + margin: [10, 60, 10, 60], zoomType: 'x', } as Highcharts.ChartOptions, title: { text: undefined }, diff --git a/app/(dashboard)/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/_ui/strategies-item/area-chart.tsx index 7f3c04de..6de0b814 100644 --- a/app/(dashboard)/_ui/strategies-item/area-chart.tsx +++ b/app/(dashboard)/_ui/strategies-item/area-chart.tsx @@ -20,6 +20,7 @@ interface Props { } const AreaChart = ({ profitRateChartData: data }: Props) => { + if (!data) return <div></div> const chartOptions: Highcharts.Options = { chart: { type: 'areaspline', From 7629b75e6deeda89decf0a0c6653380ae72d4c68 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 15:03:35 +0900 Subject: [PATCH 536/648] =?UTF-8?q?fix:=20=EA=B5=AC=EB=8F=85=20api?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=88=98=EC=A0=95=20(#2?= =?UTF-8?q?22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_api/get-subscribe.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/_api/get-subscribe.ts b/app/(dashboard)/strategies/_api/get-subscribe.ts index 61153b01..8a243a16 100644 --- a/app/(dashboard)/strategies/_api/get-subscribe.ts +++ b/app/(dashboard)/strategies/_api/get-subscribe.ts @@ -2,7 +2,7 @@ import axiosInstance from '@/shared/api/axios' const getSubscribe = async (strategyId: number) => { try { - const response = await axiosInstance.get(`/api/strategies/${strategyId}/subscribe?userId=3`) + const response = await axiosInstance.get(`/api/strategies/${strategyId}/subscribe`) return response.data.isSuccess } catch (err) { console.error(err) From d1f8f7f5bb53cad2308b3dcf2a4c7ffe963653b2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 15:04:50 +0900 Subject: [PATCH 537/648] =?UTF-8?q?design:=20=EC=B6=A9=EB=8F=8C=EB=90=9C?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=95=EB=A6=AC=20(#222)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/analysis-container/styles.module.scss | 2 +- app/(dashboard)/_ui/strategies-item/styles.module.scss | 9 +++++---- shared/ui/total-star/index.tsx | 4 ++-- shared/ui/total-star/styles.module.scss | 5 ++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/styles.module.scss b/app/(dashboard)/_ui/analysis-container/styles.module.scss index c46c5823..bd5c75cc 100644 --- a/app/(dashboard)/_ui/analysis-container/styles.module.scss +++ b/app/(dashboard)/_ui/analysis-container/styles.module.scss @@ -21,8 +21,8 @@ .chart { width: 100%; + height: 367px; border-radius: 5px; - overflow: hidden; margin: 20px 0 40px; } diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index 5374efc8..28266542 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -46,7 +46,7 @@ .summary { padding-left: 30px; overflow: hidden; - * { + :not(:first-child) { margin: 4px 0; } .title { @@ -56,7 +56,7 @@ .trader-profile { display: flex; align-items: center; - p { + & p { margin-left: 10px; @include typo-b2; } @@ -66,9 +66,9 @@ align-items: center; height: 20px; gap: 6px; - p { + padding-top: 10px; + & p { @include typo-c1; - padding-top: 10px; } } } @@ -89,6 +89,7 @@ .icon-container { display: flex; + align-items: center; gap: 4px; .icon-wrapper { .icon { diff --git a/shared/ui/total-star/index.tsx b/shared/ui/total-star/index.tsx index 7a9c2c5b..4af98681 100644 --- a/shared/ui/total-star/index.tsx +++ b/shared/ui/total-star/index.tsx @@ -26,8 +26,8 @@ const TotalStar = ({ <div className={cx('icon')}> <Star size={size} /> </div> - <p>{averageRating}</p> - <p>({totalElements})</p> + <p className={cx('text')}>{averageRating}</p> + <p className={cx('text')}>({totalElements})</p> </div> ) } diff --git a/shared/ui/total-star/styles.module.scss b/shared/ui/total-star/styles.module.scss index f5e4151e..639cb153 100644 --- a/shared/ui/total-star/styles.module.scss +++ b/shared/ui/total-star/styles.module.scss @@ -16,14 +16,13 @@ &.gray { color: $color-gray-400; } - p { - padding-top: 2px; + .text { margin-left: 2px; } } .icon-wrapper { display: block; - margin: -3px; + margin: -3px -3px 0 -3px; &.small { width: 20px; height: 20px; From 8262d9713257c8c4418bd8d8398c34db4a562df6 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 11:17:05 +0900 Subject: [PATCH 538/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20api=20=EC=B6=94=EA=B0=80=20(#191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/questions/_api/get-my-question-list.ts | 48 +++++++++++++++++++ .../_hooks/query/use-get-my-question-list.ts | 32 +++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 app/(dashboard)/my/questions/_api/get-my-question-list.ts create mode 100644 app/(dashboard)/my/questions/_hooks/query/use-get-my-question-list.ts diff --git a/app/(dashboard)/my/questions/_api/get-my-question-list.ts b/app/(dashboard)/my/questions/_api/get-my-question-list.ts new file mode 100644 index 00000000..41af71bc --- /dev/null +++ b/app/(dashboard)/my/questions/_api/get-my-question-list.ts @@ -0,0 +1,48 @@ +import axiosInstance from '@/shared/api/axios' +import { UserType } from '@/shared/types/auth' +import { + QuestionModel, + QuestionSearchConditionType, + QuestionSearchOptionsModel, + QuestionStateTapType, +} from '@/shared/types/questions' + +interface Props { + userType: UserType + page?: number + size?: number + keyword?: string + searchCondition?: QuestionSearchConditionType + stateCondition: QuestionStateTapType +} + +const getMyQuestionList = async ({ + userType, + page = 1, + size = 3, + keyword, + searchCondition, + stateCondition, +}: Props): Promise<QuestionModel[]> => { + try { + const data: QuestionSearchOptionsModel = { + keyword: undefined, + searchCondition: undefined, + stateCondition: stateCondition, + } + if (keyword) data.keyword = keyword + if (searchCondition) data.searchCondition = searchCondition + + const response = await axiosInstance.post( + `/api/${userType.toLowerCase()}/questions?page=${page}&size=${size}`, + data + ) + + return response.data.data + } catch (err) { + console.error(err) + throw new Error('문의 목록 조회에 실패했습니다.') + } +} + +export default getMyQuestionList diff --git a/app/(dashboard)/my/questions/_hooks/query/use-get-my-question-list.ts b/app/(dashboard)/my/questions/_hooks/query/use-get-my-question-list.ts new file mode 100644 index 00000000..2f84b48d --- /dev/null +++ b/app/(dashboard)/my/questions/_hooks/query/use-get-my-question-list.ts @@ -0,0 +1,32 @@ +import { useQuery } from '@tanstack/react-query' + +import { UserType } from '@/shared/types/auth' +import { QuestionSearchOptionsModel } from '@/shared/types/questions' + +import getMyQuestionList from '../../_api/get-my-question-list' + +interface Props { + page: number + size: number + options: QuestionSearchOptionsModel + userType: UserType +} + +const useGetMyQuestionList = ({ page, size, userType, options }: Props) => { + return useQuery({ + queryKey: ['questionList', page, size, options], + queryFn: () => { + const { keyword, searchCondition, stateCondition } = options + return getMyQuestionList({ + userType, + page, + size, + keyword, + searchCondition, + stateCondition, + }) + }, + }) +} + +export default useGetMyQuestionList From 4fd17e5d6600795f6d15dcf3bc79fcfeb4b9c5fd Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 11:17:46 +0900 Subject: [PATCH 539/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=83=81=EC=84=B8=20api=20=EC=B6=94=EA=B0=80=20(#1?= =?UTF-8?q?91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/questions/_api/get-question-details.ts | 18 ++++++++++++++++++ .../_hooks/query/use-get-question-details.ts | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 app/(dashboard)/my/questions/_api/get-question-details.ts create mode 100644 app/(dashboard)/my/questions/_hooks/query/use-get-question-details.ts diff --git a/app/(dashboard)/my/questions/_api/get-question-details.ts b/app/(dashboard)/my/questions/_api/get-question-details.ts new file mode 100644 index 00000000..8b605446 --- /dev/null +++ b/app/(dashboard)/my/questions/_api/get-question-details.ts @@ -0,0 +1,18 @@ +import axiosInstance from '@/shared/api/axios' +import { QuestionDetailsModel } from '@/shared/types/questions' + +interface Props { + questionId: number +} + +const getQuestionDetails = async ({ questionId }: Props): Promise<QuestionDetailsModel> => { + try { + const response = await axiosInstance.get(`/api/questions?questionId=${questionId}`) + return response.data.data + } catch (err) { + console.error(err) + throw new Error('문의 목록 조회에 실패했습니다.') + } +} + +export default getQuestionDetails diff --git a/app/(dashboard)/my/questions/_hooks/query/use-get-question-details.ts b/app/(dashboard)/my/questions/_hooks/query/use-get-question-details.ts new file mode 100644 index 00000000..06f737ae --- /dev/null +++ b/app/(dashboard)/my/questions/_hooks/query/use-get-question-details.ts @@ -0,0 +1,16 @@ +import { useQuery } from '@tanstack/react-query' + +import getQuestionDetails from '../../_api/get-question-details' + +interface Props { + questionId: number +} + +const useGetQuestionDetails = ({ questionId }: Props) => { + return useQuery({ + queryKey: ['questionDetails', questionId], + queryFn: () => getQuestionDetails({ questionId }), + }) +} + +export default useGetQuestionDetails From 8b37abf5f15753da721ba5840e9838382068b4d7 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 11:26:59 +0900 Subject: [PATCH 540/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20api=20=EC=A0=81=EC=9A=A9=20(#191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-container/index.tsx | 46 +++++---- .../my/questions/_ui/question-card/index.tsx | 16 ++-- .../_ui/questions-tab-content/index.tsx | 95 ++++--------------- .../my/questions/_ui/questions-tab/index.tsx | 20 +++- shared/types/questions.ts | 47 ++++++++- 5 files changed, 113 insertions(+), 111 deletions(-) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 6f73eb2f..e853c78b 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -2,11 +2,15 @@ import { useState } from 'react' +import { useParams } from 'next/navigation' + import classNames from 'classnames/bind' +import { useAuthStore } from '@/shared/stores/use-auth-store' import { Button } from '@/shared/ui/button' import { Textarea } from '@/shared/ui/textarea' +import useGetQuestionDetails from '../../../_hooks/query/use-get-question-details' import QuestionDetailCard from '../question-detail-card' import styles from './styles.module.scss' @@ -14,9 +18,14 @@ const cx = classNames.bind(styles) const QuestionContainer = () => { const [isActiveAnswer, setIsActiveAnswer] = useState(false) - // 임시 - const hasAnswer = false - const isTrader = true + const { questionId } = useParams() + + const user = useAuthStore((state) => state.user) + const isTrader = user?.role.includes('TRADER') || false + + const { data: questionDetails } = useGetQuestionDetails({ + questionId: parseInt(questionId as string), + }) const handleQuestionAdd = () => {} @@ -24,30 +33,29 @@ const QuestionContainer = () => { setIsActiveAnswer((prevState) => !prevState) } - // TODO: Trader, Investor에 따라 적절한 UI 표시 - // Trader이고 답변이 달리지 않았을 때: 답변하기 버튼 - // Trader이고 답변이 달렸을 때: 답변 삭제하기 버튼 - // Investor일 때: 추가 질문하기 버튼 + if (!questionDetails) { + return + } return ( <> <div className={cx('container')}> <QuestionDetailCard - isAuthor={true} - strategyName="ETF 레버리지 /인버" - title="미국발 경제악화가 한국 증시에 미치는 영향은 무엇인가요?" - contents="안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구..........안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.......... 안녕하세요 주식투자를 해보려고 하는데요 어쩌구... 저쩌구.........." - nickname="홍길동" - createdAt="2024-11-03T15:00:00" - status="답변 완료" + isAuthor={!isTrader} + strategyName={questionDetails.strategyName} + title={questionDetails.title} + contents={questionDetails.questionContent} + nickname={questionDetails.nickname} + createdAt={questionDetails.questionCreatedAt} + status={questionDetails.state === 'WAITING' ? '답변 대기' : '답변 완료'} /> - {hasAnswer ? ( + {questionDetails.answer ? ( <QuestionDetailCard type="answer" - isAuthor={false} - contents="저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다... 저는 이러쿵저러쿵 생각합니다...저는 이러쿵저러쿵 생각합니다..." - nickname="전문가" - createdAt="2024-11-03T15:00:00" + isAuthor={isTrader} + contents={questionDetails.answer.content} + nickname={questionDetails.answer.nickname} + createdAt={questionDetails.answer.createdAt} /> ) : ( <>{!isActiveAnswer && <p className={cx('empty-message')}>아직 답변이 없습니다</p>}</> diff --git a/app/(dashboard)/my/questions/_ui/question-card/index.tsx b/app/(dashboard)/my/questions/_ui/question-card/index.tsx index 2c4012e8..ddc67a9c 100644 --- a/app/(dashboard)/my/questions/_ui/question-card/index.tsx +++ b/app/(dashboard)/my/questions/_ui/question-card/index.tsx @@ -3,7 +3,7 @@ import Link from 'next/link' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' -import type { QuestionStatusType } from '@/shared/types/questions' +import { QuestionStateConditionType } from '@/shared/types/questions' import Avatar from '@/shared/ui/avatar' import Label from '@/shared/ui/label' import { formatDateTime } from '@/shared/utils/format' @@ -20,25 +20,27 @@ export interface QuestionCardProps { } interface Props extends QuestionCardProps { - qnaId: number + questionId: number strategyName: string title: string - status: QuestionStatusType + questionState: QuestionStateConditionType } const QuestionCard = ({ - qnaId, + questionId, strategyName, title, contents, nickname, profileImage, createdAt, - status, + questionState, }: Props) => { + const status = questionState === 'COMPLETED' ? '답변 완료' : '답변 대기' + return ( <div className={cx('card-container')}> - <Link href={`${PATH.MY_QUESTIONS}/${qnaId}`}> + <Link href={`${PATH.MY_QUESTIONS}/${questionId}`}> <div className={cx('top-wrapper')}> <strong className={cx('strategy-name')}>{strategyName}</strong> <span className={cx('created-at')}>{formatDateTime(createdAt)}</span> @@ -50,7 +52,7 @@ const QuestionCard = ({ <Avatar src={profileImage} size="medium" /> <span>{nickname}</span> </div> - <Label color={status === '답변 완료' ? 'indigo' : 'orange'}>{status}</Label> + <Label color={questionState === 'COMPLETED' ? 'indigo' : 'orange'}>{status}</Label> </div> </Link> </div> diff --git a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx index dd5ae743..3a0e6af3 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx @@ -2,20 +2,20 @@ import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { usePagination } from '@/shared/hooks/custom/use-pagination' -import { QuestionModel } from '@/shared/types/questions' +import { useAuthStore } from '@/shared/stores/use-auth-store' +import { QuestionSearchOptionsModel } from '@/shared/types/questions' import Pagination from '@/shared/ui/pagination' +import useGetMyQuestionList from '../../_hooks/query/use-get-my-question-list' import QuestionCard from '../question-card' import styles from './styles.module.scss' const cx = classNames.bind(styles) -const COUNT_PER_PAGE = 4 - -type QuestionTabType = 'all' | 'waiting' | 'complete' +const COUNT_PER_PAGE = 3 interface Props { - options: QuestionTabType + options: QuestionSearchOptionsModel } const QuestionsTabContent = ({ options }: Props) => { @@ -23,79 +23,20 @@ const QuestionsTabContent = ({ options }: Props) => { basePath: PATH.MY_QUESTIONS, pageSize: COUNT_PER_PAGE, }) - // TODO: options에 따른 questions 데이터 불러오기 - // 검색 조건 등 추가될 수 있어서 option 대신 options로 사용 + + const user = useAuthStore((state) => state.user) + + if (!user) return // 임시 const totalPages = 2 - const questionsData: QuestionModel[] = [ - { - qnaId: 1, - userId: 101, - strategyId: 1, - title: 'Dynamic ETF 전략 진입 시점 문의드립니다', - questionContent: - '현재 시장 상황에서 Dynamic ETF 전략을 시작하려고 하는데, 적절한 진입 시점인지 고민됩니다. MDD 관리는 어떻게 하시는지도 궁금합니다.', - status: '답변 완료', - strategyName: 'Dynamic ETF 전략', - createdAt: '2024-03-15T09:30:00Z', - profileImageUrl: '/images/profile1.png', - nickname: '투자초보123', - }, - { - qnaId: 2, - userId: 102, - strategyId: 6, - title: 'Active LongShort 전략 레버리지 문의', - questionContent: - 'Active LongShort 전략에서 사용하시는 레버리지 비율이 궁금합니다. 리스크 관리는 어떻게 하시나요?', - status: '답변 대기', - strategyName: 'Active LongShort', - createdAt: '2024-03-14T15:45:00Z', - profileImageUrl: '/images/profile2.png', - nickname: '실전투자자', - }, - { - qnaId: 3, - userId: 103, - strategyId: 3, - title: 'Futures Pro 전략 업데이트 일정', - questionContent: - '다음 전략 업데이트 일정이 궁금합니다. 현재 수익률이 조금 정체되어 있는 것 같은데 어떻게 보시나요?', - status: '답변 완료', - strategyName: 'Futures Pro', - createdAt: '2024-03-13T11:20:00Z', - profileImageUrl: '/images/profile3.png', - nickname: '퓨처스마스터', - }, - { - qnaId: 4, - userId: 104, - strategyId: 8, - title: 'High Risk High Return 전략 백테스트 결과 문의', - questionContent: - '백테스트 기간과 구체적인 결과가 궁금합니다. 특히 2023년 변동성 장세에서의 성과는 어땠나요?', - status: '답변 완료', - strategyName: 'High Risk High Return', - createdAt: '2024-03-12T16:15:00Z', - profileImageUrl: '/images/profile4.png', - nickname: '데이터분석가', - }, - { - qnaId: 5, - userId: 105, - strategyId: 2, - title: '고수익 ETF 전략 종목 선정 기준', - questionContent: - 'ETF 선정 기준과 리밸런싱 주기가 궁금합니다. 또한 현재 포트폴리오에서 가장 비중이 높은 ETF는 무엇인가요?', - status: '답변 대기', - strategyName: '고수익 ETF', - createdAt: '2024-03-11T13:50:00Z', - profileImageUrl: '/images/profile5.png', - nickname: 'ETF러버', - }, - ] + const { data: questionsData } = useGetMyQuestionList({ + page, + size: COUNT_PER_PAGE, + userType: user.role.includes('TRADER') ? 'TRADER' : 'INVESTOR', + options, + }) return ( <> @@ -103,12 +44,12 @@ const QuestionsTabContent = ({ options }: Props) => { {questionsData && questionsData.length && questionsData.map((question) => ( - <li key={question.qnaId}> + <li key={question.questionId}> <QuestionCard - qnaId={question.qnaId} + questionId={question.questionId} strategyName={question.strategyName} title={question.title} - status={question.status} + questionState={question.stateCondition} contents={question.questionContent} nickname={question.nickname} createdAt={question.createdAt} diff --git a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx index 4d68b960..4a0eab8b 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx @@ -15,7 +15,11 @@ const QuestionsTab = () => { label: '모든 질문', content: ( <Suspense> - <QuestionsTabContent options="all" /> + <QuestionsTabContent + options={{ + stateCondition: 'ALL', + }} + /> </Suspense> ), }, @@ -24,16 +28,24 @@ const QuestionsTab = () => { label: '답변 대기', content: ( <Suspense> - <QuestionsTabContent options="waiting" /> + <QuestionsTabContent + options={{ + stateCondition: 'WAITING', + }} + /> </Suspense> ), }, { - id: 'complete', + id: 'completed', label: '답변 완료', content: ( <Suspense> - <QuestionsTabContent options="complete" /> + <QuestionsTabContent + options={{ + stateCondition: 'COMPLETED', + }} + /> </Suspense> ), }, diff --git a/shared/types/questions.ts b/shared/types/questions.ts index 2fded165..7518ca09 100644 --- a/shared/types/questions.ts +++ b/shared/types/questions.ts @@ -1,14 +1,53 @@ +import { UserType } from './auth' + export type QuestionStatusType = '답변 대기' | '답변 완료' +export type QuestionStateConditionType = 'WAITING' | 'COMPLETED' +export type QuestionStateTapType = QuestionStateConditionType | 'ALL' + +export type QuestionSearchConditionType = + | 'TITLE' + | 'CONTENT' + | 'TITLE_OR_CONTENT' + | 'TRADER_NAME' + | 'INVESTOR_NAME' + | 'STRATEGY_NAME' + export interface QuestionModel { - qnaId: number - userId: number - strategyId: number + questionId: number title: string questionContent: string - status: QuestionStatusType strategyName: string + nickname: string + profileImageUrl: string + stateCondition: QuestionStateConditionType createdAt: string +} + +export interface AnswerModel { + answerId: number + content: string + nickname: string + role: UserType profileImageUrl: string + createdAt: string +} + +export interface QuestionDetailsModel { + questionId: number + title: string + questionContent: string + strategyName: string nickname: string + role: UserType + profileImageUrl: string + state: QuestionStateConditionType + questionCreatedAt: string + answer: AnswerModel | null +} + +export interface QuestionSearchOptionsModel { + keyword?: string + searchCondition?: QuestionSearchConditionType + stateCondition: QuestionStateTapType } From e1f195f6c09c412fe13767eda4bc1916298d7cdb Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Tue, 3 Dec 2024 11:37:21 +0900 Subject: [PATCH 541/648] =?UTF-8?q?fix:=20user=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95=20(#191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/questions/_ui/questions-tab-content/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx index 3a0e6af3..28a383c4 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx @@ -26,15 +26,13 @@ const QuestionsTabContent = ({ options }: Props) => { const user = useAuthStore((state) => state.user) - if (!user) return - // 임시 const totalPages = 2 const { data: questionsData } = useGetMyQuestionList({ page, size: COUNT_PER_PAGE, - userType: user.role.includes('TRADER') ? 'TRADER' : 'INVESTOR', + userType: user?.role.includes('TRADER') ? 'TRADER' : 'INVESTOR', options, }) From 1ab1bd390b2d01c3352f29e23d67fc0dcb736062 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 17:33:17 +0900 Subject: [PATCH 542/648] =?UTF-8?q?feat:=20=EC=8B=A4=EA=B3=84=EC=A2=8C?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20api=EC=B6=94=EA=B0=80=20(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_api/get-account-images.ts | 12 ++++++++++++ .../_hooks/query/use-get-account-images.ts | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-account-images.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts new file mode 100644 index 00000000..3d75a98a --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts @@ -0,0 +1,12 @@ +import axiosInstance from '@/shared/api/axios' + +const getAccountImages = async (strategyId: number) => { + try { + const response = await axiosInstance.get(`/api/strategies/${strategyId}/account-images`) + return response.data.result + } catch (err) { + console.error(err) + } +} + +export default getAccountImages diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-account-images.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-account-images.ts new file mode 100644 index 00000000..d84e32d6 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-account-images.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import getAccountImages from '../../_api/get-account-images' + +const useGetAccountImages = (strategyId: number) => { + return useQuery({ + queryKey: ['account-images', strategyId], + queryFn: () => getAccountImages(strategyId), + }) +} + +export default useGetAccountImages From a083fb013f30c90b02713bd4e61c304d458d3f03 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 17:34:54 +0900 Subject: [PATCH 543/648] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=81=AC=EA=B2=8C=20=EB=B3=BC=EC=88=98=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80=20(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/account-image-modal.tsx | 29 +++++++++++++++++++++++++ shared/ui/modal/styles.module.scss | 10 +++++++++ 2 files changed, 39 insertions(+) create mode 100644 shared/ui/modal/account-image-modal.tsx diff --git a/shared/ui/modal/account-image-modal.tsx b/shared/ui/modal/account-image-modal.tsx new file mode 100644 index 00000000..a38aaa60 --- /dev/null +++ b/shared/ui/modal/account-image-modal.tsx @@ -0,0 +1,29 @@ +import Image from 'next/image' + +import classNames from 'classnames/bind' + +import Modal from '.' +import { Button } from '../button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isOpen: boolean + onClose: () => void + title: string + url: string +} + +const AccountImageModal = ({ isOpen, title, url, onClose }: Props) => { + return ( + <Modal isOpen={isOpen} message={title} className={cx('account')}> + <div className={cx('image')}> + <Image src={url} alt={'imageData.title'} fill sizes="100%" /> + </div> + <Button onClick={onClose}>닫기</Button> + </Modal> + ) +} + +export default AccountImageModal diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index 3d510751..e3f28231 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -21,6 +21,9 @@ flex-direction: column; align-items: center; justify-content: center; + &.account { + width: 600px; + } } .icon { @@ -49,3 +52,10 @@ width: 90px; } } + +.image { + position: relative; + width: 100%; + height: 400px; + margin-bottom: 20px; +} From caaf67a2963a72904bc7c343217a6ca9273e3355 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 17:35:17 +0900 Subject: [PATCH 544/648] =?UTF-8?q?feat:=20api,=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/account-content.tsx | 52 +++++++++++++++---- .../_ui/analysis-container/styles.module.scss | 3 +- .../analysis-container/tabs-width-table.tsx | 2 +- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/account-content.tsx b/app/(dashboard)/_ui/analysis-container/account-content.tsx index fb298fad..830fc7ef 100644 --- a/app/(dashboard)/_ui/analysis-container/account-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/account-content.tsx @@ -1,36 +1,56 @@ 'use client' +import { useState } from 'react' + import Image from 'next/image' import classNames from 'classnames/bind' import { ACCOUNT_PAGE_COUNT } from '@/shared/constants/count-per-page' +import useModal from '@/shared/hooks/custom/use-modal' import { Button } from '@/shared/ui/button' +import AccountImageModal from '@/shared/ui/modal/account-image-modal' import Pagination from '@/shared/ui/pagination' +import sliceArray from '@/shared/utils/slice-array' +import useGetAccountImages from '../../strategies/[strategyId]/_hooks/query/use-get-account-images' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface ImageDataModel { + id: number imageUrl: string title: string } interface Props { - imagesData?: ImageDataModel[] + strategyId: number currentPage: number onPageChange: (page: number) => void isEditable?: boolean } -const AccountContent = ({ imagesData, currentPage, onPageChange, isEditable = false }: Props) => { - const croppedImagesData = imagesData?.slice( - ACCOUNT_PAGE_COUNT * (currentPage - 1), - ACCOUNT_PAGE_COUNT * (currentPage - 1) + ACCOUNT_PAGE_COUNT +const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = false }: Props) => { + const [selectedImage, setSelectedImage] = useState<ImageDataModel | null>(null) + const { isModalOpen, openModal, closeModal } = useModal() + const { data } = useGetAccountImages(strategyId) + + const handleOpenModal = (image: ImageDataModel) => { + setSelectedImage(image) + openModal() + } + + if (!data || !Array.isArray(data.content)) return <></> + + const imagesData = data.content + const croppedImagesData: ImageDataModel[] = sliceArray( + imagesData ?? [], + ACCOUNT_PAGE_COUNT, + currentPage ) - const isTwoLines = croppedImagesData?.length || 0 >= 5 + const isTwoLines = (croppedImagesData?.length || 0) > 4 return ( <div className={cx('table-wrapper')}> @@ -47,10 +67,14 @@ const AccountContent = ({ imagesData, currentPage, onPageChange, isEditable = fa {croppedImagesData && croppedImagesData.length !== 0 ? ( <> <div className={cx('account-images-container', isTwoLines && 'line')}> - {croppedImagesData?.map((imageData) => ( - <div key={imageData.imageUrl} className={cx('image-data')}> + {croppedImagesData?.map((imageData: ImageDataModel) => ( + <div + key={imageData.imageUrl} + className={cx('image-data')} + onClick={() => handleOpenModal(imageData)} + > <div className={cx('image')}> - <Image src={imageData.imageUrl} alt={imageData.title} fill /> + <Image src={imageData.imageUrl} alt={imageData.title} fill sizes="100%" /> </div> <span>{imageData.title}</span> </div> @@ -59,7 +83,7 @@ const AccountContent = ({ imagesData, currentPage, onPageChange, isEditable = fa {imagesData && ( <Pagination currentPage={currentPage} - maxPage={Math.ceil(imagesData.length / 5)} + maxPage={data.totalPages} onPageChange={onPageChange} /> )} @@ -69,6 +93,14 @@ const AccountContent = ({ imagesData, currentPage, onPageChange, isEditable = fa <p>업데이트 된 실거래계좌 이미지가 없습니다.</p> </div> )} + {selectedImage && ( + <AccountImageModal + isOpen={isModalOpen} + url={selectedImage.imageUrl} + title={selectedImage.title} + onClose={closeModal} + /> + )} </div> ) } diff --git a/app/(dashboard)/_ui/analysis-container/styles.module.scss b/app/(dashboard)/_ui/analysis-container/styles.module.scss index c46c5823..48d35bf5 100644 --- a/app/(dashboard)/_ui/analysis-container/styles.module.scss +++ b/app/(dashboard)/_ui/analysis-container/styles.module.scss @@ -65,8 +65,7 @@ align-items: center; .image { position: relative; - max-width: 165px; - min-width: 100px; + width: 100%; height: 100px; border-radius: 8px; overflow: hidden; diff --git a/app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx b/app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx index b8fbeb08..bab75655 100644 --- a/app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx +++ b/app/(dashboard)/_ui/analysis-container/tabs-width-table.tsx @@ -74,7 +74,7 @@ const TabsWithTable = ({ strategyId, isEditable = false }: Props) => { content: ( <div className={cx('table-wrapper')}> <AccountContent - imagesData={[]} + strategyId={strategyId} currentPage={currentPage} onPageChange={handlePageChange} isEditable={isEditable} From f239dda52e39192b5e457f3b717f4c907358ed28 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 22:10:32 +0900 Subject: [PATCH 545/648] =?UTF-8?q?feat:=20=EC=A0=9C=EC=95=88=EC=84=9C=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4,=20=EB=B6=84=EC=84=9D=20=EC=97=91=EC=85=80?= =?UTF-8?q?=20=EB=8B=A4=EC=9A=B4=20api=20=EC=B6=94=EA=B0=80=20(#232)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_api/get-analysis-download.ts | 26 +++++++++++++++++++ .../_api/get-proposal-download.ts | 24 +++++++++++++++++ .../[strategyId]/_api/helper-download-file.ts | 20 ++++++++++++++ .../_hooks/query/use-get-analysis-download.ts | 12 +++++++++ .../_hooks/query/use-get-proposal-download.ts | 12 +++++++++ 5 files changed, 94 insertions(+) create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/helper-download-file.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-download.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-proposal-download.ts diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts new file mode 100644 index 00000000..a74972e1 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts @@ -0,0 +1,26 @@ +import axiosInstance from '@/shared/api/axios' + +import { downloadFile } from './helper-download-file' + +const getAnalysisDownload = async (strategyId: number, type: 'daily' | 'monthly') => { + try { + const response = await axiosInstance.get( + `/api/strategies/${strategyId}/${type}-analysis/download`, + { + responseType: 'blob', + } + ) + const blob = new Blob([response.data], { type: response.headers['content-type'] }) + const url = window.URL.createObjectURL(blob) + + const link = document.createElement('a') + link.href = url + const contentDisposition = response.headers['content-disposition'] + + downloadFile(blob, contentDisposition, `${type}_분석자료`) + } catch (err) { + console.error(err) + } +} + +export default getAnalysisDownload diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts new file mode 100644 index 00000000..37e4f225 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts @@ -0,0 +1,24 @@ +import axiosInstance from '@/shared/api/axios' + +import { downloadFile } from './helper-download-file' + +const getProposalDownload = async (strategyId: number, name: string) => { + try { + const response = await axiosInstance.get(`/api/my-strategies/${strategyId}/download-proposal`, { + responseType: 'blob', + }) + const blob = new Blob([response.data], { type: response.headers['content-type'] }) + const url = window.URL.createObjectURL(blob) + + const link = document.createElement('a') + link.href = url + + const contentDisposition = response.headers['content-disposition'] + + downloadFile(blob, contentDisposition, `${name}_제안서`) + } catch (err) { + console.error(err) + } +} + +export default getProposalDownload diff --git a/app/(dashboard)/strategies/[strategyId]/_api/helper-download-file.ts b/app/(dashboard)/strategies/[strategyId]/_api/helper-download-file.ts new file mode 100644 index 00000000..3cfb1819 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/helper-download-file.ts @@ -0,0 +1,20 @@ +export const downloadFile = ( + blob: Blob, + contentDisposition: string | undefined, + defaultFileName: string +) => { + const url = window.URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + + const fileName = contentDisposition + ? decodeURIComponent(contentDisposition.split('filename=')[1]) + : defaultFileName + + link.download = fileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + + window.URL.revokeObjectURL(url) +} diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-download.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-download.ts new file mode 100644 index 00000000..c2c669fb --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis-download.ts @@ -0,0 +1,12 @@ +import { useMutation } from '@tanstack/react-query' + +import getAnalysisDownload from '../../_api/get-analysis-download' + +const useGetAnalysisDownload = () => { + return useMutation({ + mutationFn: ({ strategyId, type }: { strategyId: number; type: 'daily' | 'monthly' }) => + getAnalysisDownload(strategyId, type), + }) +} + +export default useGetAnalysisDownload diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-proposal-download.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-proposal-download.ts new file mode 100644 index 00000000..b14c58a2 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-proposal-download.ts @@ -0,0 +1,12 @@ +import { useMutation } from '@tanstack/react-query' + +import getProposalDownload from '../../_api/get-proposal-download' + +const useGetProposalDownload = () => { + return useMutation({ + mutationFn: ({ strategyId, name }: { strategyId: number; name: string }) => + getProposalDownload(strategyId, name), + }) +} + +export default useGetProposalDownload From 765bca1c136e6204cbef1e3bc4721d1672039b42 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 22:11:07 +0900 Subject: [PATCH 546/648] =?UTF-8?q?feat:=20=EC=A0=9C=EC=95=88=EC=84=9C=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4,=20=EB=B6=84=EC=84=9D=20=EC=97=91=EC=85=80?= =?UTF-8?q?=20=EB=8B=A4=EC=9A=B4=20api=20=EC=A0=81=EC=9A=A9=20(#232)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/analysis-container/analysis-content.tsx | 13 ++++++++++++- app/(dashboard)/_ui/details-information/index.tsx | 4 +++- .../_ui/details-information/strategy-name-box.tsx | 13 ++++++++++--- .../my/strategies/manage/[strategyId]/page.tsx | 6 +++++- app/(dashboard)/strategies/[strategyId]/page.tsx | 4 +++- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx index eb57120f..d232cd1c 100644 --- a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx @@ -6,6 +6,7 @@ import Pagination from '@/shared/ui/pagination' import VerticalTable from '@/shared/ui/table/vertical' import useGetAnalysis from '../../strategies/[strategyId]/_hooks/query/use-get-analysis' +import useGetAnalysisDownload from '../../strategies/[strategyId]/_hooks/query/use-get-analysis-download' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -46,6 +47,11 @@ const AnalysisContent = ({ isEditable = false, }: Props) => { const { data: analysisData } = useGetAnalysis(strategyId, type, currentPage, ANALYSIS_PAGE_COUNT) + const { mutate } = useGetAnalysisDownload() + + const handleDownload = () => { + mutate({ strategyId, type }) + } const tableHeader = type === 'daily' ? DAILY_TABLE_HEADER : MONTHLY_TABLE_HEADER @@ -54,7 +60,12 @@ const AnalysisContent = ({ return ( <div className={cx('table-wrapper', 'analysis')}> {!isEditable && ( - <Button size="small" className={cx('excel-button')} variant="filled"> + <Button + onClick={handleDownload} + size="small" + className={cx('excel-button')} + variant="filled" + > 엑셀 다운받기 </Button> )} diff --git a/app/(dashboard)/_ui/details-information/index.tsx b/app/(dashboard)/_ui/details-information/index.tsx index 85a27b07..3aae89cd 100644 --- a/app/(dashboard)/_ui/details-information/index.tsx +++ b/app/(dashboard)/_ui/details-information/index.tsx @@ -11,11 +11,12 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { + strategyId: number information: StrategyDetailsInformationModel type?: 'default' | 'my' } -const DetailsInformation = ({ information, type = 'default' }: Props) => { +const DetailsInformation = ({ strategyId, information, type = 'default' }: Props) => { const percentageToArray = [ { percent: information.cumulativeProfitRate, label: '누적 수익률' }, { percent: information.maxDrawdownRate, label: '최대 자본 인하율' }, @@ -37,6 +38,7 @@ const DetailsInformation = ({ information, type = 'default' }: Props) => { ...(information.stockTypeInfo?.stockTypeNames ?? []), ]} name={information.strategyName} + strategyId={strategyId} /> <InvestInformation stock={information.stockTypeInfo?.stockTypeNames || []} diff --git a/app/(dashboard)/_ui/details-information/strategy-name-box.tsx b/app/(dashboard)/_ui/details-information/strategy-name-box.tsx index c7e68de5..18cbd70b 100644 --- a/app/(dashboard)/_ui/details-information/strategy-name-box.tsx +++ b/app/(dashboard)/_ui/details-information/strategy-name-box.tsx @@ -3,23 +3,30 @@ import React from 'react' import StrategiesIcon from '@/app/(dashboard)/_ui/strategies-item/strategies-icon' import classNames from 'classnames/bind' +import useGetProposalDownload from '../../strategies/[strategyId]/_hooks/query/use-get-proposal-download' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { + strategyId: number iconUrls?: string[] iconNames?: string[] name: string - hasButton?: boolean } -const StrategyNameBox = ({ iconUrls, iconNames, name, hasButton = true }: Props) => { +const StrategyNameBox = ({ strategyId, iconUrls, iconNames, name }: Props) => { + const { mutate } = useGetProposalDownload() + + const handleDownload = () => { + mutate({ strategyId, name }) + } + return ( <div className={cx('name-container')}> <StrategiesIcon iconUrls={iconUrls} iconNames={iconNames} /> <p className={cx('name')}>{name}</p> - {hasButton && <button>제안서 다운로드</button>} + <button onClick={handleDownload}>제안서 다운로드</button> </div> ) } diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index 7ff3fffc..351aed37 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -43,7 +43,11 @@ const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { </div> <div className={cx('strategy-container')}> {detailsInformationData && ( - <DetailsInformation information={detailsInformationData} type="my" /> + <DetailsInformation + information={detailsInformationData} + strategyId={params.strategyId} + type="my" + /> )} <AnalysisContainer type="my" strategyId={params.strategyId} /> <SideContainer hasButton={true}> diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 131c7eb2..d478a6f2 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -54,7 +54,9 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { <div> <BackHeader label={'목록으로 돌아가기'} /> <Title label={'전략 상세보기'} /> - {information && <DetailsInformation information={information} />} + {information && ( + <DetailsInformation information={information} strategyId={params.strategyId} /> + )} <AnalysisContainer strategyId={params.strategyId} /> <ReviewContainer strategyId={params.strategyId} /> <SideContainer> From 79f4afe677e867c86ccc8fb1b27cb0a4ac62b9f6 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 22:15:07 +0900 Subject: [PATCH 547/648] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20(#232)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_api/get-analysis-download.ts | 4 ---- .../strategies/[strategyId]/_api/get-proposal-download.ts | 5 ----- 2 files changed, 9 deletions(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts index a74972e1..49d80032 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-analysis-download.ts @@ -11,10 +11,6 @@ const getAnalysisDownload = async (strategyId: number, type: 'daily' | 'monthly' } ) const blob = new Blob([response.data], { type: response.headers['content-type'] }) - const url = window.URL.createObjectURL(blob) - - const link = document.createElement('a') - link.href = url const contentDisposition = response.headers['content-disposition'] downloadFile(blob, contentDisposition, `${type}_분석자료`) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts index 37e4f225..7992f065 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-proposal-download.ts @@ -8,11 +8,6 @@ const getProposalDownload = async (strategyId: number, name: string) => { responseType: 'blob', }) const blob = new Blob([response.data], { type: response.headers['content-type'] }) - const url = window.URL.createObjectURL(blob) - - const link = document.createElement('a') - link.href = url - const contentDisposition = response.headers['content-disposition'] downloadFile(blob, contentDisposition, `${name}_제안서`) From 178ff18b73a92ae05b15f108fadf64fa6bbdfcc2 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 22:26:47 +0900 Subject: [PATCH 548/648] =?UTF-8?q?refactor:=20=ED=95=B8=EB=93=A4=EB=9F=AC?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=EB=AA=85=20on=EC=B6=94=EA=B0=80=20(#231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/details-side-item/side-item.tsx | 4 ++-- app/(dashboard)/_ui/strategies-item/subscribe.tsx | 2 +- .../[strategyId]/_ui/review-container/add-review.tsx | 2 +- .../_ui/review-container/review-guide-modal.tsx | 8 ++++---- .../[strategyId]/_ui/review-container/review-item.tsx | 2 +- app/(dashboard)/strategies/[strategyId]/page.tsx | 2 +- shared/ui/modal/add-question-modal.tsx | 6 +++--- shared/ui/modal/question-guide-modal.tsx | 6 +++--- shared/ui/modal/signin-check-modal.tsx | 6 +++--- shared/ui/modal/subscribe-check-modal.tsx | 8 ++++---- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index eff0e139..f9afb4b5 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -77,12 +77,12 @@ const SideItem = ({ title, data, profileImage, isMyStrategy = false, strategyNam isModalOpen={isAddQuestionModalOpen} titleRef={titleRef} contentRef={contentRef} - closeModal={questionCloseModal} + onCloseModal={questionCloseModal} onChange={handleAddQuestion} isEmpty={isEmpty} /> )} - <QuestionGuideModal isModalOpen={isQuestionGuideModalOpen} closeModal={guideCloseModal} /> + <QuestionGuideModal isModalOpen={isQuestionGuideModalOpen} onCloseModal={guideCloseModal} /> </div> ) } diff --git a/app/(dashboard)/_ui/strategies-item/subscribe.tsx b/app/(dashboard)/_ui/strategies-item/subscribe.tsx index 36592df8..e580b704 100644 --- a/app/(dashboard)/_ui/strategies-item/subscribe.tsx +++ b/app/(dashboard)/_ui/strategies-item/subscribe.tsx @@ -57,7 +57,7 @@ const Subscribe = ({ strategyId, subscriptionStatus }: Props) => { {isModalOpen && ( <SigninCheckModal isModalOpen={isModalOpen} - closeModal={closeModal} + onCloseModal={closeModal} onChange={handleRoute} /> )} diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx index f6d0fc3b..2aaf024f 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/add-review.tsx @@ -114,7 +114,7 @@ const AddReview = ({ </Button> )} </div> - <ReviewGuideModal isModalOpen={isModalOpen} isErr={isError} closeModal={closeModal} /> + <ReviewGuideModal isModalOpen={isModalOpen} isErr={isError} onCloseModal={closeModal} /> </div> ) } diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx index 417f27e1..92acec9e 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-guide-modal.tsx @@ -15,11 +15,11 @@ const cx = classNames.bind(styles) interface Props { isModalOpen: boolean isErr: boolean - closeModal: () => void + onCloseModal: () => void onChange?: () => void } -const ReviewGuideModal = ({ isModalOpen, isErr, closeModal, onChange }: Props) => { +const ReviewGuideModal = ({ isModalOpen, isErr, onCloseModal, onChange }: Props) => { return ( <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> <span className={cx('message')}> @@ -32,10 +32,10 @@ const ReviewGuideModal = ({ isModalOpen, isErr, closeModal, onChange }: Props) = )} </span> {isErr && !onChange ? ( - <Button onClick={closeModal}>닫기</Button> + <Button onClick={onCloseModal}>닫기</Button> ) : ( <div className={cx('two-button')}> - <Button onClick={closeModal}>아니오</Button> + <Button onClick={onCloseModal}>아니오</Button> <Button onClick={onChange} variant="filled" className={cx('button')}> 예 </Button> diff --git a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx index 2c04eb57..d9111cfd 100644 --- a/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/review-container/review-item.tsx @@ -90,7 +90,7 @@ const ReviewItem = ({ <ReviewGuideModal isModalOpen={isModalOpen} isErr={false} - closeModal={closeModal} + onCloseModal={closeModal} onChange={handleDelete} /> </li> diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 131c7eb2..e535a26b 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -83,7 +83,7 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { <SubscribeCheckModal isSubscribing={information?.isSubscribed} isModalOpen={isModalOpen} - closeModal={handleUnsubscribe} + onCloseModal={handleUnsubscribe} onChange={handleSubscribe} /> )} diff --git a/shared/ui/modal/add-question-modal.tsx b/shared/ui/modal/add-question-modal.tsx index d23e93db..9b51b850 100644 --- a/shared/ui/modal/add-question-modal.tsx +++ b/shared/ui/modal/add-question-modal.tsx @@ -22,7 +22,7 @@ interface Props { content?: string titleRef?: React.RefObject<HTMLInputElement> contentRef?: React.RefObject<HTMLTextAreaElement> - closeModal: () => void + onCloseModal: () => void onChange?: () => void } @@ -34,7 +34,7 @@ const AddQuestionModal = ({ content, titleRef, contentRef, - closeModal, + onCloseModal, onChange, }: Props) => { return ( @@ -67,7 +67,7 @@ const AddQuestionModal = ({ </div> {isEmpty && <ErrorMessage errorMessage="제목 또는 내용을 모두 입력해주세요." />} <div className={cx('two-button', 'question')}> - <Button onClick={closeModal}>취소</Button> + <Button onClick={onCloseModal}>취소</Button> <Button onClick={onChange} variant="filled" className={cx('button')}> 문의 </Button> diff --git a/shared/ui/modal/question-guide-modal.tsx b/shared/ui/modal/question-guide-modal.tsx index acc2cbb8..6d94ff05 100644 --- a/shared/ui/modal/question-guide-modal.tsx +++ b/shared/ui/modal/question-guide-modal.tsx @@ -13,10 +13,10 @@ const cx = classNames.bind(styles) interface Props { isModalOpen: boolean - closeModal: () => void + onCloseModal: () => void } -const QuestionGuideModal = ({ isModalOpen, closeModal }: Props) => { +const QuestionGuideModal = ({ isModalOpen, onCloseModal }: Props) => { return ( <Modal isOpen={isModalOpen} icon={ModalCheckIcon}> <span className={cx('message')}> @@ -26,7 +26,7 @@ const QuestionGuideModal = ({ isModalOpen, closeModal }: Props) => { <br /> 문의 페이지에서 확인 가능합니다. </span> - <Button onClick={closeModal}>닫기</Button> + <Button onClick={onCloseModal}>닫기</Button> </Modal> ) } diff --git a/shared/ui/modal/signin-check-modal.tsx b/shared/ui/modal/signin-check-modal.tsx index 34c78aec..d9942a84 100644 --- a/shared/ui/modal/signin-check-modal.tsx +++ b/shared/ui/modal/signin-check-modal.tsx @@ -13,18 +13,18 @@ const cx = classNames.bind(styles) interface Props { isModalOpen: boolean - closeModal: () => void + onCloseModal: () => void onChange?: () => void } -const SigninCheckModal = ({ isModalOpen, closeModal, onChange }: Props) => { +const SigninCheckModal = ({ isModalOpen, onCloseModal, onChange }: Props) => { return ( <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> <span className={cx('message')}> 로그인이 필요합니다. <br /> 로그인 하시겠습니까? </span> <div className={cx('two-button')}> - <Button onClick={closeModal}>아니오</Button> + <Button onClick={onCloseModal}>아니오</Button> <Button onClick={onChange} variant="filled" className={cx('button')}> 예 </Button> diff --git a/shared/ui/modal/subscribe-check-modal.tsx b/shared/ui/modal/subscribe-check-modal.tsx index 1976a3c5..ef80305b 100644 --- a/shared/ui/modal/subscribe-check-modal.tsx +++ b/shared/ui/modal/subscribe-check-modal.tsx @@ -14,18 +14,18 @@ const cx = classNames.bind(styles) interface Props { isModalOpen: boolean isSubscribing: boolean - closeModal: () => void + onCloseModal: () => void onChange?: () => void } -const SubscribeCheckModal = ({ isModalOpen, isSubscribing, closeModal, onChange }: Props) => { +const SubscribeCheckModal = ({ isModalOpen, isSubscribing, onCloseModal, onChange }: Props) => { return ( <Modal isOpen={isModalOpen} icon={isSubscribing ? ModalAlertIcon : ModalSubscribeIcon}> {isSubscribing ? ( <> <span className={cx('message')}>구독을 취소하시겠습니까?</span> <div className={cx('two-button')}> - <Button onClick={closeModal}>아니오</Button> + <Button onClick={onCloseModal}>아니오</Button> <Button onClick={onChange} variant="filled" className={cx('button')}> 예 </Button> @@ -38,7 +38,7 @@ const SubscribeCheckModal = ({ isModalOpen, isSubscribing, closeModal, onChange 구독한 전략은 나의 관심전략 <br /> 페이지에서 확인 가능합니다. </span> - <Button onClick={closeModal}>닫기</Button> + <Button onClick={onCloseModal}>닫기</Button> </> )} </Modal> From ae05817e0ec55602227c25d33764276758a4df63 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 4 Dec 2024 22:26:59 +0900 Subject: [PATCH 549/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20api=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20(#191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../question-detail-card/styles.module.scss | 1 + .../my/questions/_api/get-my-question-list.ts | 33 +++++++------ .../my/questions/_api/get-question-details.ts | 4 +- .../_ui/questions-tab-content/index.tsx | 26 ++++++---- .../my/questions/_ui/questions-tab/index.tsx | 16 ++++++- app/(dashboard)/my/questions/page.tsx | 48 ++++++++++++++----- 6 files changed, 89 insertions(+), 39 deletions(-) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss index 67f6e6b3..d7cb99ed 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/styles.module.scss @@ -2,6 +2,7 @@ .card-container { @include card-base; + width: 100%; padding: 35px 40px; } diff --git a/app/(dashboard)/my/questions/_api/get-my-question-list.ts b/app/(dashboard)/my/questions/_api/get-my-question-list.ts index 41af71bc..736bcc50 100644 --- a/app/(dashboard)/my/questions/_api/get-my-question-list.ts +++ b/app/(dashboard)/my/questions/_api/get-my-question-list.ts @@ -3,7 +3,6 @@ import { UserType } from '@/shared/types/auth' import { QuestionModel, QuestionSearchConditionType, - QuestionSearchOptionsModel, QuestionStateTapType, } from '@/shared/types/questions' @@ -16,29 +15,29 @@ interface Props { stateCondition: QuestionStateTapType } +interface QuestionReturnModel { + content: QuestionModel[] + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean +} + const getMyQuestionList = async ({ userType, page = 1, size = 3, - keyword, - searchCondition, + keyword = '', + searchCondition = 'CONTENT', stateCondition, -}: Props): Promise<QuestionModel[]> => { +}: Props): Promise<QuestionReturnModel> => { try { - const data: QuestionSearchOptionsModel = { - keyword: undefined, - searchCondition: undefined, - stateCondition: stateCondition, - } - if (keyword) data.keyword = keyword - if (searchCondition) data.searchCondition = searchCondition - - const response = await axiosInstance.post( - `/api/${userType.toLowerCase()}/questions?page=${page}&size=${size}`, - data + const response = await axiosInstance.get( + `/api/${userType.toLowerCase()}/questions?page=${page}&size=${size}&keyword=${keyword}&searchCondition=${searchCondition}&stateCondition=${stateCondition}` ) - - return response.data.data + return response.data.result } catch (err) { console.error(err) throw new Error('문의 목록 조회에 실패했습니다.') diff --git a/app/(dashboard)/my/questions/_api/get-question-details.ts b/app/(dashboard)/my/questions/_api/get-question-details.ts index 8b605446..f112c922 100644 --- a/app/(dashboard)/my/questions/_api/get-question-details.ts +++ b/app/(dashboard)/my/questions/_api/get-question-details.ts @@ -7,8 +7,8 @@ interface Props { const getQuestionDetails = async ({ questionId }: Props): Promise<QuestionDetailsModel> => { try { - const response = await axiosInstance.get(`/api/questions?questionId=${questionId}`) - return response.data.data + const response = await axiosInstance.get(`/api/questions/${questionId}`) + return response.data.result } catch (err) { console.error(err) throw new Error('문의 목록 조회에 실패했습니다.') diff --git a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx index 28a383c4..e7225d35 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab-content/index.tsx @@ -26,21 +26,24 @@ const QuestionsTabContent = ({ options }: Props) => { const user = useAuthStore((state) => state.user) - // 임시 - const totalPages = 2 - - const { data: questionsData } = useGetMyQuestionList({ + const { data } = useGetMyQuestionList({ page, size: COUNT_PER_PAGE, userType: user?.role.includes('TRADER') ? 'TRADER' : 'INVESTOR', options, }) + if (!data) { + return + } + + const questionsData = data.content + return ( <> <ul className={cx('question-list')}> {questionsData && - questionsData.length && + !!questionsData.length && questionsData.map((question) => ( <li key={question.questionId}> <QuestionCard @@ -56,10 +59,17 @@ const QuestionsTabContent = ({ options }: Props) => { ))} </ul> - {!questionsData || - (!questionsData.length && <p className={cx('empty-message')}>문의 내역이 없습니다.</p>)} + {(!questionsData || !questionsData.length) && ( + <p className={cx('empty-message')}>문의 내역이 없습니다.</p> + )} <div className={cx('pagination-wrapper')}> - <Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} /> + {data.totalElements > 0 && ( + <Pagination + currentPage={page} + maxPage={data.totalPages} + onPageChange={handlePageChange} + /> + )} </div> </> ) diff --git a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx index 4a0eab8b..ef6b7ea2 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx @@ -2,11 +2,19 @@ import { Suspense, useState } from 'react' +import { QuestionSearchConditionType } from '@/shared/types/questions' import Tabs from '@/shared/ui/tabs' import QuestionsTabContent from '../questions-tab-content' -const QuestionsTab = () => { +interface Props { + searchOptions: { + keyword: string + searchCondition: QuestionSearchConditionType + } +} + +const QuestionsTab = ({ searchOptions }: Props) => { const [activeTab, setActiveTab] = useState('all') const TABS = [ @@ -18,6 +26,8 @@ const QuestionsTab = () => { <QuestionsTabContent options={{ stateCondition: 'ALL', + searchCondition: searchOptions.searchCondition, + keyword: searchOptions.keyword, }} /> </Suspense> @@ -31,6 +41,8 @@ const QuestionsTab = () => { <QuestionsTabContent options={{ stateCondition: 'WAITING', + searchCondition: searchOptions.searchCondition, + keyword: searchOptions.keyword, }} /> </Suspense> @@ -44,6 +56,8 @@ const QuestionsTab = () => { <QuestionsTabContent options={{ stateCondition: 'COMPLETED', + searchCondition: searchOptions.searchCondition, + keyword: searchOptions.keyword, }} /> </Suspense> diff --git a/app/(dashboard)/my/questions/page.tsx b/app/(dashboard)/my/questions/page.tsx index 61de4c34..4c219c08 100644 --- a/app/(dashboard)/my/questions/page.tsx +++ b/app/(dashboard)/my/questions/page.tsx @@ -1,9 +1,10 @@ 'use client' -import { useState } from 'react' +import { useRef, useState } from 'react' import classNames from 'classnames/bind' +import { QuestionSearchConditionType } from '@/shared/types/questions' import { DropdownValueType } from '@/shared/ui/dropdown/types' import { SearchInput } from '@/shared/ui/search-input' import Select from '@/shared/ui/select' @@ -16,25 +17,46 @@ const cx = classNames.bind(styles) const searchSelectOptions = [ { - value: 'all', - label: '전체', + value: 'TITLE', + label: '제목', }, { - value: 'trader', - label: '트레이더', + value: 'CONTENT', + label: '내용', }, { - value: 'title', - label: '제목', + value: 'TITLE_OR_CONTENT', + label: '제목 또는 내용', }, { - value: 'strategy', + value: 'TRADER_NAME', + label: '트레이더명', + }, + { + value: 'INVESTOR_NAME', + label: '투자자명', + }, + { + value: 'STRATEGY_NAME', label: '전략명', }, ] const MyQuestionsPage = () => { - const [selectedOption, setSelectedOption] = useState<DropdownValueType>('최신순') + const [selectedOption, setSelectedOption] = useState<DropdownValueType>('TITLE') + const [searchOptions, setSearchOptions] = useState({ + keyword: '', + searchCondition: selectedOption as QuestionSearchConditionType, + }) + + const inputRef = useRef<HTMLInputElement | null>(null) + + const handleSearch = () => { + setSearchOptions({ + keyword: inputRef.current?.value || '', + searchCondition: selectedOption as QuestionSearchConditionType, + }) + } return ( <div className={cx('container')}> @@ -48,10 +70,14 @@ const MyQuestionsPage = () => { onChange={setSelectedOption} options={searchSelectOptions} /> - <SearchInput /> + <SearchInput + ref={inputRef} + placeholder="검색어를 입력하세요." + onSearchIconClick={handleSearch} + /> </div> </div> - <QuestionsTab /> + <QuestionsTab searchOptions={searchOptions} /> </div> ) } From 46085c602a59c32e20c13209dfb4a60e5e29522f Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Wed, 4 Dec 2024 22:32:26 +0900 Subject: [PATCH 550/648] =?UTF-8?q?API=20response=20base=20interface=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/types/response.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 shared/types/response.ts diff --git a/shared/types/response.ts b/shared/types/response.ts new file mode 100644 index 00000000..1585d465 --- /dev/null +++ b/shared/types/response.ts @@ -0,0 +1,5 @@ +export interface APIResponseBaseModel<T extends boolean> { + isSuccess: T + message: string + code: T extends false ? number : never +} From f42813d3f5ab3e72050b358284f6d23f0de2147f Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Wed, 4 Dec 2024 23:06:42 +0900 Subject: [PATCH 551/648] =?UTF-8?q?fix:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20props=20=EC=88=98=EC=A0=95=20(#191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-detail-card/question-detail-card.stories.tsx | 1 - .../my/questions/_ui/question-card/question-card.stories.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx index f778b621..65d61a95 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/question-detail-card.stories.tsx @@ -24,7 +24,6 @@ Question.args = { nickname: '투자초보', profileImage: '', createdAt: '2024-11-03T15:00:00', - status: '답변 대기', isAuthor: false, onDelete: () => alert('삭제 버튼 클릭'), } diff --git a/app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx b/app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx index a0f077ed..6f7c3eab 100644 --- a/app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx +++ b/app/(dashboard)/my/questions/_ui/question-card/question-card.stories.tsx @@ -23,13 +23,13 @@ Default.args = { nickname: '투자할래요', profileImage: '', createdAt: '2024-11-03T15:00:00', - status: '답변 대기', + questionState: 'WAITING', } export const Answered = Template.bind({}) Answered.args = { ...Default.args, - status: '답변 완료', + questionState: 'COMPLETED', } export default meta From b8a9edf137dc04957317c2dc4ebaa214c3d33da8 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 23:18:17 +0900 Subject: [PATCH 552/648] =?UTF-8?q?fix:=20return=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=AA=85=EC=8B=9C=20(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[strategyId]/_api/get-account-images.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts b/app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts index 3d75a98a..f21a5a45 100644 --- a/app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts +++ b/app/(dashboard)/strategies/[strategyId]/_api/get-account-images.ts @@ -1,6 +1,18 @@ +import { ImageDataModel } from '@/app/(dashboard)/_ui/analysis-container/account-content' + import axiosInstance from '@/shared/api/axios' -const getAccountImages = async (strategyId: number) => { +interface ResponseModel { + content: ImageDataModel + first: boolean + last: boolean + page: number + size: number + totalElements: number + totalPages: number +} + +const getAccountImages = async (strategyId: number): Promise<ResponseModel | null | undefined> => { try { const response = await axiosInstance.get(`/api/strategies/${strategyId}/account-images`) return response.data.result From 1cd37e219013d3cfdfe6679393868217c3b9c02a Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Wed, 4 Dec 2024 23:18:53 +0900 Subject: [PATCH 553/648] =?UTF-8?q?fix:=20useQuery=20=EB=A1=9C=EB=94=A9=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80=20(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/analysis-container/account-content.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/account-content.tsx b/app/(dashboard)/_ui/analysis-container/account-content.tsx index 830fc7ef..68443546 100644 --- a/app/(dashboard)/_ui/analysis-container/account-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/account-content.tsx @@ -18,7 +18,7 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -interface ImageDataModel { +export interface ImageDataModel { id: number imageUrl: string title: string @@ -34,14 +34,14 @@ interface Props { const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = false }: Props) => { const [selectedImage, setSelectedImage] = useState<ImageDataModel | null>(null) const { isModalOpen, openModal, closeModal } = useModal() - const { data } = useGetAccountImages(strategyId) + const { data, isLoading } = useGetAccountImages(strategyId) const handleOpenModal = (image: ImageDataModel) => { setSelectedImage(image) openModal() } - if (!data || !Array.isArray(data.content)) return <></> + if (!data || !Array.isArray(data.content) || isLoading) return null const imagesData = data.content const croppedImagesData: ImageDataModel[] = sliceArray( From 1f0a5df16960ef8c5430ce4e70ebc15395383f5a Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Wed, 4 Dec 2024 23:31:34 +0900 Subject: [PATCH 554/648] =?UTF-8?q?feat:=20NoticeDetail=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20?= =?UTF-8?q?download=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80=20(?= =?UTF-8?q?#217)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/[noticeId]/detail/page.tsx | 5 + .../notices/_ui/notice-detail/index.tsx | 63 +++++++++++++ .../_ui/notice-detail/styles.module.scss | 92 +++++++++++++++++++ .../_ui/notice-table/styles.module.scss | 2 + app/(landing)/notices/page.tsx | 2 - public/icons/download.svg | 3 + public/icons/index.tsx | 1 + shared/styles/stories/icons.stories.tsx | 2 + 8 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 app/(landing)/notices/[noticeId]/detail/page.tsx create mode 100644 app/(landing)/notices/_ui/notice-detail/index.tsx create mode 100644 app/(landing)/notices/_ui/notice-detail/styles.module.scss create mode 100644 public/icons/download.svg diff --git a/app/(landing)/notices/[noticeId]/detail/page.tsx b/app/(landing)/notices/[noticeId]/detail/page.tsx new file mode 100644 index 00000000..b6c54f06 --- /dev/null +++ b/app/(landing)/notices/[noticeId]/detail/page.tsx @@ -0,0 +1,5 @@ +const NoticeDetailPage = () => { + return <></> +} + +export default NoticeDetailPage diff --git a/app/(landing)/notices/_ui/notice-detail/index.tsx b/app/(landing)/notices/_ui/notice-detail/index.tsx new file mode 100644 index 00000000..27733c6e --- /dev/null +++ b/app/(landing)/notices/_ui/notice-detail/index.tsx @@ -0,0 +1,63 @@ +'use client' + +import { DownloadIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + title: React.ReactNode + date: string + children?: React.ReactNode + hasAttachFile?: boolean +} + +const NoticeDetail = ({ title, date, children, hasAttachFile = true }: Props) => { + const handleSave = () => {} + return ( + <div className={cx('container')}> + <div className={cx('title-wrapper')}> + <div className={cx('title')}>{title}</div> + <div className={cx('date')}>{date}</div> + </div> + <div className={cx('top-line')}></div> + <div className={cx('content')}>{children}</div> + <div className={cx('bottom-line')}></div> + + {hasAttachFile && ( + <div className={cx('attach-file')}> + <div className={cx('file-left-wrapper')}> + <p className={cx('file-count')}>첨부 {'5'}개</p>{' '} + </div> + <div className={cx('file-right-wrapper')}> + <div className={cx('file-wrapper')}> + <div className={cx('file-title')}>{'파일제목1.pdf'}</div> + <DownloadIcon className={cx('file-download')} onClick={handleSave} /> + </div> + + <div className={cx('file-wrapper')}> + <div className={cx('file-title')}>{'파일제목2.pdf'}</div> + <DownloadIcon className={cx('file-download')} onClick={handleSave} /> + </div> + <div className={cx('file-wrapper')}> + <div className={cx('file-title')}>{'파일제목3.pdf'}</div> + <DownloadIcon className={cx('file-download')} onClick={handleSave} /> + </div> + <div className={cx('file-wrapper')}> + <div className={cx('file-title')}>{'파일제목4.pdf'}</div> + <DownloadIcon className={cx('file-download')} onClick={handleSave} /> + </div> + <div className={cx('file-wrapper')}> + <div className={cx('file-title')}>{'파일제목5.pdf'}</div> + <DownloadIcon className={cx('file-download')} onClick={handleSave} /> + </div> + </div> + </div> + )} + </div> + ) +} + +export default NoticeDetail diff --git a/app/(landing)/notices/_ui/notice-detail/styles.module.scss b/app/(landing)/notices/_ui/notice-detail/styles.module.scss new file mode 100644 index 00000000..30ff9437 --- /dev/null +++ b/app/(landing)/notices/_ui/notice-detail/styles.module.scss @@ -0,0 +1,92 @@ +.container { + width: 1190px; + height: 813px; + padding: 52px 28px; + background-color: $color-white; +} + +.title-wrapper { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + padding: 0px 24px; +} + +.title { + @include typo-h4; +} + +.date { + color: $color-gray-400; +} + +.top-line { + width: 100%; + height: 1px; + background-color: $color-gray-300; + margin-bottom: 43px; +} + +.content { + padding: 0px 24px; + @include typo-b2; + line-height: 2; +} + +.bottom-line { + margin-top: 95px; + width: 100%; + height: 1px; + background-color: $color-gray-300; +} + +.attach-file { + padding: 0px 24px; + margin-top: 18px; + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 10px; +} + +.file-left-wrapper { + flex: 0 0 5%; + display: flex; + align-items: center; +} + +.file-count { + @include typo-b3; + margin-top: 10px; +} + +.file-right-wrapper { + flex: 0 0 95%; + display: flex; + flex-direction: column; + gap: 8px; +} + +.file-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 10px; + margin-left: 57px; +} + +.file-title { + @include typo-b2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; +} + +.file-download { + width: 14px; + height: 14px; + color: $color-gray-500; + cursor: pointer; +} diff --git a/app/(landing)/notices/_ui/notice-table/styles.module.scss b/app/(landing)/notices/_ui/notice-table/styles.module.scss index ab83d45d..86bfda66 100644 --- a/app/(landing)/notices/_ui/notice-table/styles.module.scss +++ b/app/(landing)/notices/_ui/notice-table/styles.module.scss @@ -7,6 +7,7 @@ .table { min-height: 450px; + color: $color-gray-600; table { thead td { @@ -15,6 +16,7 @@ td { height: 60px; + @include typo-b3; &:nth-child(1) { width: 10%; diff --git a/app/(landing)/notices/page.tsx b/app/(landing)/notices/page.tsx index 0778e79c..c24e3feb 100644 --- a/app/(landing)/notices/page.tsx +++ b/app/(landing)/notices/page.tsx @@ -1,7 +1,5 @@ import classNames from 'classnames/bind' -import VerticalTable from '@/shared/ui/table/vertical' - import NoticeTable from './_ui/notice-table' import styles from './page.module.scss' diff --git a/public/icons/download.svg b/public/icons/download.svg new file mode 100644 index 00000000..0344cb0e --- /dev/null +++ b/public/icons/download.svg @@ -0,0 +1,3 @@ +<svg width="15" height="15" viewBox="0 0 15 15" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path d="M7.38281 11.1465L3.00537 6.76904L4.23105 5.49959L6.50732 7.77585V0.640625H8.2583V7.77585L10.5346 5.49959L11.7603 6.76904L7.38281 11.1465ZM2.12988 14.6484C1.64836 14.6484 1.2363 14.4771 0.893693 14.1345C0.551085 13.7919 0.37949 13.3796 0.378906 12.8975V10.271H2.12988V12.8975H12.6357V10.271H14.3867V12.8975C14.3867 13.379 14.2154 13.7913 13.8728 14.1345C13.5302 14.4777 13.1178 14.649 12.6357 14.6484H2.12988Z" fill="currentColor"/> +</svg> diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 8537e2c5..f267c781 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -33,3 +33,4 @@ export { default as CircleIcon } from './circle.svg' export { default as CheckedCircleIcon } from './checked-circle.svg' export { default as BackIcon } from './back.svg' export { default as RegisterIcon } from './register.svg' +export { default as DownloadIcon } from './download.svg' diff --git a/shared/styles/stories/icons.stories.tsx b/shared/styles/stories/icons.stories.tsx index fe422a74..e3ad9072 100644 --- a/shared/styles/stories/icons.stories.tsx +++ b/shared/styles/stories/icons.stories.tsx @@ -13,6 +13,7 @@ import { ChevronUpIcon, CloseIcon, DailyGraphIcon, + DownloadIcon, FileIcon, ModalAlertIcon, ModalCheckIcon, @@ -73,6 +74,7 @@ const icons = [ { name: 'ModalSubscribeIcon', icon: ModalSubscribeIcon }, { name: 'ModalCheckIcon', icon: ModalCheckIcon }, { name: 'RegisterIcon', icon: RegisterIcon }, + { name: 'DownloadIcon', icon: DownloadIcon }, ] export const Icons: Story = { From 2671377f9a9501922d05118f9b2595acdc6d2b4c Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Wed, 4 Dec 2024 23:53:24 +0900 Subject: [PATCH 555/648] =?UTF-8?q?feat:=20=ED=8C=8C=EC=9D=BC=EC=B2=A8?= =?UTF-8?q?=EB=B6=80=20=ED=83=80=EC=9D=B4=ED=8B=80=20onClick=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20(#217)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/_ui/notice-detail/index.tsx | 20 ++++++++++++++----- .../_ui/notice-detail/styles.module.scss | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/(landing)/notices/_ui/notice-detail/index.tsx b/app/(landing)/notices/_ui/notice-detail/index.tsx index 27733c6e..b673e71e 100644 --- a/app/(landing)/notices/_ui/notice-detail/index.tsx +++ b/app/(landing)/notices/_ui/notice-detail/index.tsx @@ -33,24 +33,34 @@ const NoticeDetail = ({ title, date, children, hasAttachFile = true }: Props) => </div> <div className={cx('file-right-wrapper')}> <div className={cx('file-wrapper')}> - <div className={cx('file-title')}>{'파일제목1.pdf'}</div> + <div className={cx('file-title')} onClick={handleSave}> + {'파일제목1.pdf'} + </div> <DownloadIcon className={cx('file-download')} onClick={handleSave} /> </div> <div className={cx('file-wrapper')}> - <div className={cx('file-title')}>{'파일제목2.pdf'}</div> + <div className={cx('file-title')} onClick={handleSave}> + {'파일제목2.pdf'} + </div> <DownloadIcon className={cx('file-download')} onClick={handleSave} /> </div> <div className={cx('file-wrapper')}> - <div className={cx('file-title')}>{'파일제목3.pdf'}</div> + <div className={cx('file-title')} onClick={handleSave}> + {'파일제목3.pdf'} + </div> <DownloadIcon className={cx('file-download')} onClick={handleSave} /> </div> <div className={cx('file-wrapper')}> - <div className={cx('file-title')}>{'파일제목4.pdf'}</div> + <div className={cx('file-title')} onClick={handleSave}> + {'파일제목4.pdf'} + </div> <DownloadIcon className={cx('file-download')} onClick={handleSave} /> </div> <div className={cx('file-wrapper')}> - <div className={cx('file-title')}>{'파일제목5.pdf'}</div> + <div className={cx('file-title')} onClick={handleSave}> + {'파일제목5.pdf'} + </div> <DownloadIcon className={cx('file-download')} onClick={handleSave} /> </div> </div> diff --git a/app/(landing)/notices/_ui/notice-detail/styles.module.scss b/app/(landing)/notices/_ui/notice-detail/styles.module.scss index 30ff9437..007fea2e 100644 --- a/app/(landing)/notices/_ui/notice-detail/styles.module.scss +++ b/app/(landing)/notices/_ui/notice-detail/styles.module.scss @@ -82,6 +82,7 @@ overflow: hidden; text-overflow: ellipsis; flex: 1; + cursor: pointer; } .file-download { From e0c4fac5e5f8ec37f282c73e75494d064d721a0f Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 00:08:25 +0900 Subject: [PATCH 556/648] =?UTF-8?q?refactor:=20UserInfo=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20isWhiteDisabled=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?(#127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/_ui/user-info/index.tsx | 10 +++++----- app/(dashboard)/my/profile/page.tsx | 6 +----- app/(dashboard)/my/profile/withdraw/page.tsx | 6 +----- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index 70baa39d..ae6948c0 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -60,7 +60,7 @@ const UserInfo = ({ isEditable = false }: Props) => { value={'고양이'} inputSize="compact" className={cx('input')} - isWhiteDisabled={!isEditable ? true : undefined} + isWhiteDisabled={!isEditable} disabled={isEditable} /> </div> @@ -72,7 +72,7 @@ const UserInfo = ({ isEditable = false }: Props) => { inputSize="compact" value={'hello@example.com'} className={cx('input')} - isWhiteDisabled={!isEditable ? true : undefined} + isWhiteDisabled={!isEditable} disabled={isEditable} /> </div> @@ -86,7 +86,7 @@ const UserInfo = ({ isEditable = false }: Props) => { onChange={() => {}} className={cx('input')} inputSize="compact" - isWhiteDisabled={!isEditable ? true : undefined} + isWhiteDisabled={!isEditable} /> {isEditable && <Button onClick={handlePhoneConfirm}>확인</Button>} @@ -101,7 +101,7 @@ const UserInfo = ({ isEditable = false }: Props) => { inputSize="compact" value={'2004.05.04'} className={cx('input')} - isWhiteDisabled={!isEditable ? true : undefined} + isWhiteDisabled={!isEditable} disabled={isEditable} /> </div> @@ -115,7 +115,7 @@ const UserInfo = ({ isEditable = false }: Props) => { value={'고양이귀여워'} onChange={() => {}} className={cx('input')} - isWhiteDisabled={!isEditable ? true : undefined} + isWhiteDisabled={!isEditable} /> {isEditable && <Button onClick={handleNicknameConfirm}>확인</Button>} diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx index 1e1e244b..1d87202a 100644 --- a/app/(dashboard)/my/profile/page.tsx +++ b/app/(dashboard)/my/profile/page.tsx @@ -1,11 +1,7 @@ import UserInfo from './_ui/user-info' const MyProfilePage = () => { - return ( - <> - <UserInfo /> - </> - ) + return <UserInfo /> } export default MyProfilePage diff --git a/app/(dashboard)/my/profile/withdraw/page.tsx b/app/(dashboard)/my/profile/withdraw/page.tsx index d4c36b05..178574c8 100644 --- a/app/(dashboard)/my/profile/withdraw/page.tsx +++ b/app/(dashboard)/my/profile/withdraw/page.tsx @@ -1,11 +1,7 @@ import UserWithdraw from '../_ui/user-withdraw' const MyProfileWithdrawPage = () => { - return ( - <> - <UserWithdraw /> - </> - ) + return <UserWithdraw /> } export default MyProfileWithdrawPage From 469e3bf78090afa4508bde18111c4df7225425b7 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 00:13:53 +0900 Subject: [PATCH 557/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20%,=EC=9D=BC=20=EC=88=98=EC=B9=98=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EC=B6=94=EA=B0=80=20(#240)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/{in-korean.ts => constant.ts} | 21 +++++++++++++++++++ shared/ui/table/statistics/index.tsx | 14 ++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) rename shared/ui/table/statistics/{in-korean.ts => constant.ts} (76%) diff --git a/shared/ui/table/statistics/in-korean.ts b/shared/ui/table/statistics/constant.ts similarity index 76% rename from shared/ui/table/statistics/in-korean.ts rename to shared/ui/table/statistics/constant.ts index af999406..4b6fada2 100644 --- a/shared/ui/table/statistics/in-korean.ts +++ b/shared/ui/table/statistics/constant.ts @@ -34,3 +34,24 @@ export const inKoreanData = { ddMddInfo: 'DD&MDD 정보', tradingInfo: '거래 관련 정보', } as const + +export const STATISTICS_PERCENT = [ + '누적 수익률', + '최대 누적 수익률', + '평균 손익률', + '최대 일 수익률', + '최대 일 손실률', + '현재 자본 인하율', + '최대 자본 인하율', + '승률', +] + +export const STATISTICS_DATE = [ + '고점 갱신 후 경과일', + '총 거래 일수', + '총 이익 일수', + '총 손실 일수', + '현재 연속 손익 일수', + '최대 연속 이익 일수', + '최대 연속 손실 일수', +] diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index b5fb1113..be46135e 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames/bind' import { formatNumber } from '@/shared/utils/format' -import { inKoreanData } from './in-korean' +import { STATISTICS_DATE, STATISTICS_PERCENT, inKoreanData } from './constant' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -50,11 +50,19 @@ const StatisticsTable = ({ title, statisticsData }: Props) => { {groupedData.map((row, idx) => ( <tr key={idx}> <td>{row[0]}</td> - <td>{formatNumber(row[1])}</td> + <td> + {formatNumber(row[1])} + {STATISTICS_PERCENT.includes(row[0] as string) && '%'} + {STATISTICS_DATE.includes(row[0] as string) && '일'} + </td> {row[2] && ( <> <td>{row[2]}</td> - <td>{formatNumber(row[3])}</td> + <td> + {formatNumber(row[3])} + {STATISTICS_PERCENT.includes(row[2] as string) && '%'} + {STATISTICS_DATE.includes(row[2] as string) && '일'} + </td> </> )} </tr> From 9cfefdba9ff62174aeaa843004044ace4af44bab Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 00:18:29 +0900 Subject: [PATCH 558/648] =?UTF-8?q?feat:=20UserWithdraw=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20use=20client=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx b/app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx index 54640f4b..6e443ff4 100644 --- a/app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-withdraw/index.tsx @@ -1,3 +1,5 @@ +'use client' + import { useRouter } from 'next/navigation' import classNames from 'classnames/bind' From 07e91e22b769adef52b5b8f46a59b6be0bb9e5ae Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 00:38:20 +0900 Subject: [PATCH 559/648] =?UTF-8?q?settings:=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20XSLX=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=84=A4=EC=B9=98=20(#2?= =?UTF-8?q?28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 104 ++++++++++++++++++ package.json | 1 + .../{register.svg => modal-register.svg} | 0 3 files changed, 105 insertions(+) rename public/icons/{register.svg => modal-register.svg} (100%) diff --git a/package-lock.json b/package-lock.json index 77c48946..63ebbfdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "react": "^18", "react-dom": "^18", "react-tooltip": "^5.28.0", + "xlsx": "^0.18.5", "zustand": "^5.0.1" }, "devDependencies": { @@ -5923,6 +5924,15 @@ "node": ">=8.9.0" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -6991,6 +7001,19 @@ "node": ">=4" } }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chai": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", @@ -7219,6 +7242,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -7406,6 +7438,18 @@ } } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -9510,6 +9554,15 @@ "node": ">= 6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -13753,6 +13806,18 @@ "node": ">=0.10.0" } }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", @@ -15204,6 +15269,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -15303,6 +15386,27 @@ } } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index e6a61fa9..cb4a612d 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "react": "^18", "react-dom": "^18", "react-tooltip": "^5.28.0", + "xlsx": "^0.18.5", "zustand": "^5.0.1" }, "devDependencies": { diff --git a/public/icons/register.svg b/public/icons/modal-register.svg similarity index 100% rename from public/icons/register.svg rename to public/icons/modal-register.svg From 18fe1661c6ebdee0d7e35986e5263c04eab586fc Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 00:51:08 +0900 Subject: [PATCH 560/648] =?UTF-8?q?feat:=20=EC=9D=BC=EA=B0=84=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=97=91=EC=85=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC,=20=EC=A7=81=EC=A0=91=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84=20(#228)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/index.tsx | 2 +- shared/types/strategy-data.ts | 6 + .../form/direct-input-form.tsx | 202 ++++++++++++++++++ .../form/excel-upload-form.tsx | 94 ++++++++ .../form/styles.module.scss | 110 ++++++++++ .../ui/modal/analysis-upload-modal/index.tsx | 41 ++++ .../analysis-upload-modal/styles.module.scss | 85 ++++++++ shared/ui/modal/index.tsx | 12 +- shared/ui/modal/styles.module.scss | 12 +- shared/utils/excel-utils.ts | 61 ++++++ 10 files changed, 614 insertions(+), 11 deletions(-) create mode 100644 shared/ui/modal/analysis-upload-modal/form/direct-input-form.tsx create mode 100644 shared/ui/modal/analysis-upload-modal/form/excel-upload-form.tsx create mode 100644 shared/ui/modal/analysis-upload-modal/form/styles.module.scss create mode 100644 shared/ui/modal/analysis-upload-modal/index.tsx create mode 100644 shared/ui/modal/analysis-upload-modal/styles.module.scss create mode 100644 shared/utils/excel-utils.ts diff --git a/public/icons/index.tsx b/public/icons/index.tsx index 8537e2c5..32190161 100644 --- a/public/icons/index.tsx +++ b/public/icons/index.tsx @@ -32,4 +32,4 @@ export { default as ModalCheckIcon } from './modal-check.svg' export { default as CircleIcon } from './circle.svg' export { default as CheckedCircleIcon } from './checked-circle.svg' export { default as BackIcon } from './back.svg' -export { default as RegisterIcon } from './register.svg' +export { default as RegisterIcon } from './modal-register.svg' diff --git a/shared/types/strategy-data.ts b/shared/types/strategy-data.ts index cd2bb15b..d25c4ca1 100644 --- a/shared/types/strategy-data.ts +++ b/shared/types/strategy-data.ts @@ -76,3 +76,9 @@ export interface StrategyCardModel { averageRating: number totalReviews: number } + +export interface AnalysisDataModel { + date: string + transaction: number + dailyProfitLoss: number +} diff --git a/shared/ui/modal/analysis-upload-modal/form/direct-input-form.tsx b/shared/ui/modal/analysis-upload-modal/form/direct-input-form.tsx new file mode 100644 index 00000000..1c774192 --- /dev/null +++ b/shared/ui/modal/analysis-upload-modal/form/direct-input-form.tsx @@ -0,0 +1,202 @@ +'use client' + +import React, { useState } from 'react' + +import { useAnalysisUploadMutation } from '@/app/(dashboard)/my/_hooks/query/use-analysis-mutation' +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import { Input } from '@/shared/ui/input' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + strategyId: number + onClose: () => void +} + +interface EntryDataModel { + date: string + transaction: string + dailyProfitLoss: string +} + +const DirectInputForm = ({ strategyId, onClose }: Props) => { + const [currentEntry, setCurrentEntry] = useState<EntryDataModel>({ + date: '', + transaction: '', + dailyProfitLoss: '', + }) + const [savedEntries, setSavedEntries] = useState<EntryDataModel[]>([]) + const [error, setError] = useState<string>('') + const { uploadAnalysis, isLoading } = useAnalysisUploadMutation(strategyId) + + const handleCurrentEntryChange = (field: keyof EntryDataModel, value: string) => { + setCurrentEntry((prev) => ({ + ...prev, + [field]: value, + })) + setError('') + } + + const validateEntry = (entry: EntryDataModel) => { + if (!entry.date || !entry.transaction || !entry.dailyProfitLoss) { + setError('모든 필드를 입력해주세요.') + return false + } + + const dateRegex = /^\d{4}-\d{2}-\d{2}$/ + if (!dateRegex.test(entry.date)) { + setError('날짜 형식이 올바르지 않습니다. (YYYY-MM-DD)') + return false + } + + if (isNaN(Number(entry.transaction)) || isNaN(Number(entry.dailyProfitLoss))) { + setError('입출금과 일일손익은 숫자여야 합니다.') + return false + } + + return true + } + + const addEntry = () => { + if (!validateEntry(currentEntry)) return + if (savedEntries.length >= 4) { + setError('최대 5개까지만 입력할 수 있습니다.') + return + } + + setSavedEntries([...savedEntries, currentEntry]) + setCurrentEntry({ + date: '', + transaction: '', + dailyProfitLoss: '', + }) + } + + const removeEntry = (index: number) => { + setSavedEntries(savedEntries.filter((_, i) => i !== index)) + } + + const handleSubmit = () => { + const allEntries = [...savedEntries] + if (currentEntry.date || currentEntry.transaction || currentEntry.dailyProfitLoss) { + if (!validateEntry(currentEntry)) return + allEntries.push(currentEntry) + } + + if (allEntries.length === 0) { + setError('최소 하나의 데이터를 입력해주세요.') + return + } + + const data = allEntries.map((entry) => ({ + date: entry.date, + transaction: Number(entry.transaction), + dailyProfitLoss: Number(entry.dailyProfitLoss), + })) + + uploadAnalysis( + { data }, + { + onSuccess: () => { + onClose() + }, + onError: () => { + setError('데이터 업로드 중 오류가 발생했습니다.') + }, + } + ) + } + + return ( + <div className={cx('upload-container')}> + <div className={cx('form-grid')}> + <div className={cx('input-group')}> + <label className={cx('label')}>날짜</label> + <Input + className={cx('data-input')} + name="date" + value={currentEntry.date} + onChange={(e) => handleCurrentEntryChange('date', e.target.value)} + placeholder="날짜를 입력해주세요." + /> + </div> + + <div className={cx('input-group')}> + <label className={cx('label')}>입출금</label> + <Input + className={cx('data-input')} + name="transaction" + value={currentEntry.transaction} + onChange={(e) => handleCurrentEntryChange('transaction', e.target.value)} + placeholder="입출금을 입력해주세요." + /> + </div> + + <div className={cx('input-group')}> + <label className={cx('label')}>일 손익</label> + <Input + className={cx('data-input')} + name="dailyProfitLoss" + value={currentEntry.dailyProfitLoss} + onChange={(e) => handleCurrentEntryChange('dailyProfitLoss', e.target.value)} + placeholder="일손익금을 입력해주세요." + /> + </div> + + <div className={cx('input-actions')}> + {savedEntries.length < 5 && ( + <Button className={cx('add-button')} variant="filled" onClick={addEntry}> + 추가 + </Button> + )} + </div> + </div> + + {savedEntries.map((entry, index) => ( + <div key={index} className={cx('form-grid')}> + <div className={cx('input-group')}> + <label className={cx('label')}>날짜</label> + <Input className={cx('data-input')} value={entry.date} disabled /> + </div> + + <div className={cx('input-group')}> + <label className={cx('label')}>입출금</label> + <Input className={cx('data-input')} value={entry.transaction} disabled /> + </div> + + <div className={cx('input-group')}> + <label className={cx('label')}>일 손익</label> + <Input className={cx('data-input')} value={entry.dailyProfitLoss} disabled /> + </div> + + <div className={cx('input-actions')}> + <Button + variant="outline" + className={cx('delete-button')} + onClick={() => removeEntry(index)} + > + 삭제 + </Button> + </div> + </div> + ))} + + {error && <p className={cx('error-message')}>{error}</p>} + + <div className={cx('button-group')}> + <Button variant="outline" onClick={onClose} disabled={isLoading}> + 취소 + </Button> + <Button variant="filled" onClick={handleSubmit} disabled={isLoading}> + 등록 + </Button> + </div> + </div> + ) +} + +export default DirectInputForm diff --git a/shared/ui/modal/analysis-upload-modal/form/excel-upload-form.tsx b/shared/ui/modal/analysis-upload-modal/form/excel-upload-form.tsx new file mode 100644 index 00000000..cc54f5a2 --- /dev/null +++ b/shared/ui/modal/analysis-upload-modal/form/excel-upload-form.tsx @@ -0,0 +1,94 @@ +'use client' + +import React, { useState } from 'react' + +import { useAnalysisUploadMutation } from '@/app/(dashboard)/my/_hooks/query/use-analysis-mutation' +import { FileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import { processExcelFile } from '@/shared/utils/excel-utils' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + strategyId: number + onClose: () => void +} + +const ExcelUploadForm = ({ strategyId, onClose }: Props) => { + const [file, setFile] = useState<File | null>(null) + const [error, setError] = useState<string>('') + const { uploadAnalysis, isLoading } = useAnalysisUploadMutation(strategyId) + + const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => { + const selectedFile = event.target.files?.[0] + if (selectedFile) { + setFile(selectedFile) + setError('') + } + } + + const handleGuidDownload = () => { + //TODO 업로드 가이드 다운로드 api 없음 ;; + } + + const handleSubmit = async () => { + if (!file) return + + try { + const data = await processExcelFile(file) + uploadAnalysis( + { data }, + { + onSuccess: () => { + onClose() + }, + onError: () => { + setError('데이터 업로드 중 오류가 발생했습니다.') + }, + } + ) + } catch (err) { + setError(err instanceof Error ? err.message : '파일 처리 중 오류가 발생했습니다.') + } + } + + return ( + <div className={cx('upload-container')}> + <div className={cx('file-input-wrapper')}> + <input + type="file" + accept=".xlsx, .xls" + onChange={handleFileUpload} + className={cx('hidden-input')} + id="fileInput" + /> + <label htmlFor="fileInput" className={cx('custom-input')}> + <span className={cx('placeholder')}>{file ? file.name : '파일 찾아보기'}</span> + <button className={cx('file-button')}> + <FileIcon /> + </button> + </label> + <Button variant="outline" className={cx('guide-button')} onClick={handleGuidDownload}> + 업로드 가이드 다운 + </Button> + </div> + + {error && <p className={cx('error-message')}>{error}</p>} + + <div className={cx('button-group')}> + <Button onClick={onClose} variant="outline" disabled={isLoading}> + 취소 + </Button> + <Button variant="filled" disabled={!file || isLoading} onClick={handleSubmit}> + 업로드 + </Button> + </div> + </div> + ) +} + +export default ExcelUploadForm diff --git a/shared/ui/modal/analysis-upload-modal/form/styles.module.scss b/shared/ui/modal/analysis-upload-modal/form/styles.module.scss new file mode 100644 index 00000000..ffc52938 --- /dev/null +++ b/shared/ui/modal/analysis-upload-modal/form/styles.module.scss @@ -0,0 +1,110 @@ +.upload-container { + display: flex; + flex-direction: column; + gap: 16px; +} + +.file-input-wrapper { + display: flex; + gap: 24px; + position: relative; + width: 100%; +} + +.hidden-input { + display: none; +} + +.custom-input { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + border: 1px solid $color-gray-300; + border-radius: 8px; + background-color: transparent; + flex: 1; +} + +.placeholder { + color: $color-gray-400; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.file-icon { + flex-shrink: 0; + width: 20px; + height: 20px; +} + +.file-button { + display: flex; + align-items: center; + width: 24px; + height: 24px; + background-color: transparent; +} + +.guide-button { + height: 45px; + padding: 12px 24px; + @include typo-b3; + border: 1px solid #797979; +} + +.button-group { + display: flex; + justify-content: center; + gap: 24px; + margin-top: 100px; +} + +.error-message { + color: $color-orange-500; + font-size: 14px; +} + +.form-grid { + display: grid; + grid-template-columns: 170px 170px 160px auto; + gap: 24px; + align-items: flex-start; +} + +.input-group { + width: 170px; + display: flex; + flex-direction: column; + gap: 8px; + padding: 7px 12px; + .data-input { + width: 170px; + border-radius: 4px; + border: 1px solid $color-gray-200; + &::placeholder { + color: $color-gray-400; + @include typo-c1; + } + } +} + +.label { + @include typo-b3; + color: $color-gray-800; +} + +.input-actions { + display: flex; + align-items: flex-end; + gap: 8px; + height: 100%; + padding-bottom: 2px; +} + +.add-button, +.delete-button { + font-size: 12px !important; + margin: 0 0 8px 8px; +} diff --git a/shared/ui/modal/analysis-upload-modal/index.tsx b/shared/ui/modal/analysis-upload-modal/index.tsx new file mode 100644 index 00000000..21809f5f --- /dev/null +++ b/shared/ui/modal/analysis-upload-modal/index.tsx @@ -0,0 +1,41 @@ +'use client' + +import React from 'react' + +import { RegisterIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Modal from '@/shared/ui/modal' + +import DirectInputForm from './form/direct-input-form' +import ExcelUploadForm from './form/excel-upload-form' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface AnalysisUploadModalProps { + isOpen: boolean + onClose: () => void + strategyId: number + uploadType: 'excel' | 'direct' + message: string +} + +const AnalysisUploadModal = ({ + isOpen, + onClose, + strategyId, + uploadType, + message, +}: AnalysisUploadModalProps) => { + return ( + <Modal isOpen={isOpen} size="big" icon={RegisterIcon} message={message}> + <div className={cx('container')}> + {uploadType === 'excel' && <ExcelUploadForm strategyId={strategyId} onClose={onClose} />} + {uploadType === 'direct' && <DirectInputForm strategyId={strategyId} onClose={onClose} />} + </div> + </Modal> + ) +} + +export default AnalysisUploadModal diff --git a/shared/ui/modal/analysis-upload-modal/styles.module.scss b/shared/ui/modal/analysis-upload-modal/styles.module.scss new file mode 100644 index 00000000..bdaea2fc --- /dev/null +++ b/shared/ui/modal/analysis-upload-modal/styles.module.scss @@ -0,0 +1,85 @@ +.container { + margin-top: 24px; + padding: 24px; + width: 700px; +} + +.title { + font-size: 20px; + font-weight: 600; + margin-bottom: 24px; + text-align: center; +} + +.form-container { + display: flex; + flex-direction: column; + gap: 24px; +} + +.entries-container { + display: flex; + flex-direction: column; + gap: 16px; + max-height: 60vh; + overflow-y: auto; +} + +.entry-item { + position: relative; + background-color: #f8f9fa; + padding: 16px; + border-radius: 8px; +} + +.entry-inputs { + display: flex; + flex-direction: column; + gap: 12px; +} + +.remove-button { + position: absolute; + top: 8px; + right: 8px; + color: #6b7280; + cursor: pointer; + background: none; + border: none; + padding: 4px; + + &:hover { + color: #374151; + } +} + +.add-button { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 100%; + padding: 12px; + border: 2px dashed #e5e7eb; + border-radius: 8px; + color: #6b7280; + cursor: pointer; + + &:hover { + color: #374151; + border-color: #d1d5db; + } +} + +.button-group { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 8px; +} + +.error-message { + color: #ef4444; + font-size: 14px; + margin-top: 8px; +} diff --git a/shared/ui/modal/index.tsx b/shared/ui/modal/index.tsx index 2589d2a4..e4a13f9f 100644 --- a/shared/ui/modal/index.tsx +++ b/shared/ui/modal/index.tsx @@ -13,9 +13,17 @@ interface Props { children?: React.ReactNode isOpen: boolean className?: string + size?: 'default' | 'big' } -const Modal = ({ icon: Icon, message, children, isOpen = false, className }: Props) => { +const Modal = ({ + icon: Icon, + message, + children, + isOpen = false, + size = 'default', + className, +}: Props) => { if (!isOpen) return null const modalRoot = document.getElementById('modal-root') @@ -25,7 +33,7 @@ const Modal = ({ icon: Icon, message, children, isOpen = false, className }: Pro return createPortal( <> <div className={cx('overlay')}></div> - <div className={cx('modal', className)}> + <div className={cx('modal', size, className)}> <div className={cx('icon')}>{Icon && <Icon className={cx('icon')} />}</div> <p className={cx('message')}>{message}</p> {children} diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index 3d510751..aae6bf15 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -21,6 +21,10 @@ flex-direction: column; align-items: center; justify-content: center; + + &.big { + width: 800px; + } } .icon { @@ -41,11 +45,3 @@ color: $color-gray-800; margin-bottom: 30px; } - -.two-button { - display: flex; - gap: 10px; - & .button { - width: 90px; - } -} diff --git a/shared/utils/excel-utils.ts b/shared/utils/excel-utils.ts new file mode 100644 index 00000000..2b7d3a45 --- /dev/null +++ b/shared/utils/excel-utils.ts @@ -0,0 +1,61 @@ +import * as XLSX from 'xlsx' + +interface RowDataModel { + date: string + transaction: number + dailyProfitLoss: number +} + +export const processExcelFile = (file: File): Promise<RowDataModel[]> => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + + reader.onload = (e: ProgressEvent<FileReader>) => { + try { + if (!e.target?.result) throw new Error('파일을 읽을 수 없습니다.') + + const data = new Uint8Array(e.target.result as ArrayBuffer) + const workbook = XLSX.read(data, { type: 'array' }) + + const sheetName = workbook.SheetNames[0] + const worksheet = workbook.Sheets[sheetName] + + const jsonData: (string | number | null)[][] = XLSX.utils.sheet_to_json(worksheet, { + header: 1, + }) + + const header = jsonData[0] + if (!header || header[0] !== '일자' || header[1] !== '입출금' || header[2] !== '일일손익') { + throw new Error('올바른 형식의 엑셀 파일이 아닙니다.') + } + + const rows: RowDataModel[] = jsonData + .slice(1) + .filter((row) => row.length >= 3) + .map((row) => ({ + date: parseExcelDate(row[0] as number), + transaction: Number(row[1]), + dailyProfitLoss: Number(row[2]), + })) + + resolve(rows) + } catch (error) { + reject(error) + } + } + + reader.onerror = () => reject(new Error('파일 읽기 실패')) + reader.readAsArrayBuffer(file) + }) +} + +const parseExcelDate = (excelDate: number): string => { + if (typeof excelDate === 'number') { + const date = XLSX.SSF.parse_date_code(excelDate) + const year = date.y + const month = String(date.m).padStart(2, '0') + const day = String(date.d).padStart(2, '0') + return `${year}-${month}-${day}` + } + return String(excelDate) +} From 0da65a17230b3d08e4ebdccb84f0d90b5988d87c Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 00:53:32 +0900 Subject: [PATCH 561/648] =?UTF-8?q?feat:=20=EC=9D=BC=EA=B0=84=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?api=20=EC=97=B0=EA=B2=B0=20(#228)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/_api/get-my-daily-analysis.ts | 14 +++++ .../my/_api/post-daily-analysis.ts | 15 +++++ .../my/_hooks/query/use-analysis-mutation.ts | 60 +++++++++++++++++++ .../_hooks/query/use-get-my-daily-analysis.ts | 12 ++++ 4 files changed, 101 insertions(+) create mode 100644 app/(dashboard)/my/_api/get-my-daily-analysis.ts create mode 100644 app/(dashboard)/my/_api/post-daily-analysis.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-analysis-mutation.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts diff --git a/app/(dashboard)/my/_api/get-my-daily-analysis.ts b/app/(dashboard)/my/_api/get-my-daily-analysis.ts new file mode 100644 index 00000000..cf54e6c3 --- /dev/null +++ b/app/(dashboard)/my/_api/get-my-daily-analysis.ts @@ -0,0 +1,14 @@ +import axiosInstance from '@/shared/api/axios' + +const getMyDailyAnalysis = async (strategyId: number, page: number, size: number) => { + try { + const response = await axiosInstance.get( + `/api/my-strategies/${strategyId}/daily-analysis?page=${page}&size=${size}` + ) + return response.data.result + } catch (err) { + console.error(err, `일간 분석 조회 실패`) + } +} + +export default getMyDailyAnalysis diff --git a/app/(dashboard)/my/_api/post-daily-analysis.ts b/app/(dashboard)/my/_api/post-daily-analysis.ts new file mode 100644 index 00000000..9c9ec145 --- /dev/null +++ b/app/(dashboard)/my/_api/post-daily-analysis.ts @@ -0,0 +1,15 @@ +import axiosInstance from '@/shared/api/axios' +import { AnalysisDataModel } from '@/shared/types/strategy-data' + +export const uploadDailyAnalysis = async ( + strategyId: number, + data: AnalysisDataModel[] +): Promise<void> => { + const response = await axiosInstance.post(`/api/my-strategies/${strategyId}/daily-analysis`, data) + return response.data +} + +export const deleteAllAnalysis = async (strategyId: number): Promise<void> => { + const response = await axiosInstance.delete(`/api/my-strategies/${strategyId}/daily-analysis`) + return response.data +} diff --git a/app/(dashboard)/my/_hooks/query/use-analysis-mutation.ts b/app/(dashboard)/my/_hooks/query/use-analysis-mutation.ts new file mode 100644 index 00000000..fae29d08 --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-analysis-mutation.ts @@ -0,0 +1,60 @@ +import getMyDailyAnalysis from '@/app/(dashboard)/my/_api/get-my-daily-analysis' +import { + deleteAllAnalysis, + uploadDailyAnalysis, +} from '@/app/(dashboard)/my/_api/post-daily-analysis' +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { AnalysisDataModel } from '@/shared/types/strategy-data' + +interface UploadMutationParamsModel { + data: AnalysisDataModel[] +} + +export const useAnalysisUploadMutation = ( + strategyId: number, + page: number = 1, + size: number = 10 +) => { + const queryClient = useQueryClient() + + const uploadMutation = useMutation<void, Error, UploadMutationParamsModel>({ + mutationFn: ({ data }) => uploadDailyAnalysis(strategyId, data), + onSuccess: async () => { + queryClient.invalidateQueries({ + queryKey: ['myDailyAnalysis', strategyId], + }) + + try { + const newData = await getMyDailyAnalysis(strategyId, page, size) + queryClient.setQueryData(['myDailyAnalysis', strategyId], newData) + } catch (error) { + console.error('Failed to fetch updated my daily analysis data:', error) + } + }, + }) + + const deleteMutation = useMutation<void, Error, void>({ + mutationFn: () => deleteAllAnalysis(strategyId), + onSuccess: async () => { + queryClient.invalidateQueries({ + queryKey: ['myDailyAnalysis', strategyId], + }) + + try { + const newData = await getMyDailyAnalysis(strategyId, page, size) + queryClient.setQueryData(['myDailyAnalysis', strategyId], newData) + } catch (error) { + console.error('Failed to fetch updated my daily analysis data:', error) + } + }, + }) + + return { + uploadAnalysis: uploadMutation.mutate, + deleteAllAnalysis: deleteMutation.mutate, + isLoading: uploadMutation.status === 'pending' || deleteMutation.status === 'pending', + isError: uploadMutation.status === 'error' || deleteMutation.status === 'error', + error: uploadMutation.error || deleteMutation.error, + } +} diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts b/app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts new file mode 100644 index 00000000..9d17cffd --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import getMyDailyAnalysis from '../../_api/get-my-daily-analysis' + +const useGetAnalysis = (strategyId: number, page: number, size: number) => { + return useQuery({ + queryKey: ['myDailyAnalysis', strategyId], + queryFn: () => getMyDailyAnalysis(strategyId, page, size), + }) +} + +export default useGetAnalysis From c2cf306f8d9103caa9937835d99141c49d2f206a Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 00:55:19 +0900 Subject: [PATCH 562/648] =?UTF-8?q?refactor:=20NoticeDetail=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20Props=EC=A0=9C=EA=B1=B0=20(#217)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/_ui/notice-detail/index.tsx | 77 ++++++++----------- 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/app/(landing)/notices/_ui/notice-detail/index.tsx b/app/(landing)/notices/_ui/notice-detail/index.tsx index b673e71e..c7f34acb 100644 --- a/app/(landing)/notices/_ui/notice-detail/index.tsx +++ b/app/(landing)/notices/_ui/notice-detail/index.tsx @@ -7,65 +7,48 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -interface Props { - title: React.ReactNode - date: string - children?: React.ReactNode - hasAttachFile?: boolean -} +const NoticeDetail = () => { + // TODO: 임시데이터 제거 + const data = { + title: '공지사항 제목입니다.', + createdAd: '2024-12-05', + content: '이것은 공지사항의 내용입니다. 자세한 사항은 첨부 파일을 확인하세요.', + attachments: [ + { title: '파일제목1.pdf', url: '/path/to/file1.pdf' }, + { title: '파일제목2.pdf', url: '/path/to/file2.pdf' }, + { title: '파일제목3.pdf', url: '/path/to/file3.pdf' }, + { title: '파일제목4.pdf', url: '/path/to/file4.pdf' }, + { title: '파일제목5.pdf', url: '/path/to/file5.pdf' }, + ], + } -const NoticeDetail = ({ title, date, children, hasAttachFile = true }: Props) => { const handleSave = () => {} + return ( <div className={cx('container')}> <div className={cx('title-wrapper')}> - <div className={cx('title')}>{title}</div> - <div className={cx('date')}>{date}</div> + <div className={cx('title')}>{data.title}</div> + <div className={cx('date')}>{data.createdAd}</div> </div> <div className={cx('top-line')}></div> - <div className={cx('content')}>{children}</div> + <div className={cx('content')}>{data.content}</div> <div className={cx('bottom-line')}></div> - {hasAttachFile && ( - <div className={cx('attach-file')}> - <div className={cx('file-left-wrapper')}> - <p className={cx('file-count')}>첨부 {'5'}개</p>{' '} - </div> - <div className={cx('file-right-wrapper')}> - <div className={cx('file-wrapper')}> - <div className={cx('file-title')} onClick={handleSave}> - {'파일제목1.pdf'} - </div> - <DownloadIcon className={cx('file-download')} onClick={handleSave} /> - </div> - - <div className={cx('file-wrapper')}> - <div className={cx('file-title')} onClick={handleSave}> - {'파일제목2.pdf'} - </div> - <DownloadIcon className={cx('file-download')} onClick={handleSave} /> - </div> - <div className={cx('file-wrapper')}> - <div className={cx('file-title')} onClick={handleSave}> - {'파일제목3.pdf'} - </div> - <DownloadIcon className={cx('file-download')} onClick={handleSave} /> - </div> - <div className={cx('file-wrapper')}> - <div className={cx('file-title')} onClick={handleSave}> - {'파일제목4.pdf'} - </div> - <DownloadIcon className={cx('file-download')} onClick={handleSave} /> - </div> - <div className={cx('file-wrapper')}> - <div className={cx('file-title')} onClick={handleSave}> - {'파일제목5.pdf'} + <div className={cx('attach-file')}> + <div className={cx('file-left-wrapper')}> + <p className={cx('file-count')}>첨부 {data.attachments.length}개</p> + </div> + <div className={cx('file-right-wrapper')}> + {data.attachments.map((file, index) => ( + <div className={cx('file-wrapper')} key={index}> + <div className={cx('file-title')} onClick={() => handleSave}> + {file.title} </div> - <DownloadIcon className={cx('file-download')} onClick={handleSave} /> + <DownloadIcon className={cx('file-download')} onClick={() => handleSave} /> </div> - </div> + ))} </div> - )} + </div> </div> ) } From 8447223401b91ed0a75ce1a5dfd764884623b2c6 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 00:57:46 +0900 Subject: [PATCH 563/648] =?UTF-8?q?feat:=20=EA=B8=B0=ED=83=80=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EA=B2=BD=20=EC=82=AC=ED=95=AD=20(#228)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/analysis-content.tsx | 82 ++++++++++++++++++- app/(dashboard)/_ui/strategies-item/index.tsx | 12 ++- .../_ui/strategies-item/styles.module.scss | 2 +- .../strategies/_ui/my-strategy-list/index.tsx | 9 +- .../strategies/manage/[strategyId]/page.tsx | 14 ++-- 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx index eb57120f..998d07e7 100644 --- a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx @@ -1,10 +1,16 @@ +import { useState } from 'react' + import classNames from 'classnames/bind' import { ANALYSIS_PAGE_COUNT } from '@/shared/constants/count-per-page' +import useModal from '@/shared/hooks/custom/use-modal' import { Button } from '@/shared/ui/button' +import AnalysisUploadModal from '@/shared/ui/modal/analysis-upload-modal' import Pagination from '@/shared/ui/pagination' import VerticalTable from '@/shared/ui/table/vertical' +import { useAnalysisUploadMutation } from '../../my/_hooks/query/use-analysis-mutation' +import useGetMyDailyAnalysis from '../../my/_hooks/query/use-get-my-daily-analysis' import useGetAnalysis from '../../strategies/[strategyId]/_hooks/query/use-get-analysis' import styles from './styles.module.scss' @@ -45,10 +51,58 @@ const AnalysisContent = ({ onPageChange, isEditable = false, }: Props) => { - const { data: analysisData } = useGetAnalysis(strategyId, type, currentPage, ANALYSIS_PAGE_COUNT) + const [uploadType, setUploadType] = useState<'excel' | 'direct' | null>(null) + const { isModalOpen, openModal, closeModal } = useModal() + + //TODO 현재 나의 전략 일간분석 조회 권한이 없어서 안보임 + const { data: myAnalysisData } = useGetMyDailyAnalysis( + strategyId, + currentPage, + ANALYSIS_PAGE_COUNT + ) + const { data: publicAnalysisData } = useGetAnalysis( + strategyId, + type, + currentPage, + ANALYSIS_PAGE_COUNT + ) + + const analysisData = isEditable ? myAnalysisData : publicAnalysisData + + const { deleteAllAnalysis, isLoading } = useAnalysisUploadMutation( + strategyId, + currentPage, + ANALYSIS_PAGE_COUNT + ) const tableHeader = type === 'daily' ? DAILY_TABLE_HEADER : MONTHLY_TABLE_HEADER + const handleExcelUpload = () => { + setUploadType('excel') + openModal() + } + + const handleDirectInput = () => { + setUploadType('direct') + openModal() + } + + const handleCloseModal = () => { + closeModal() + setUploadType(null) + } + + const handleDeleteAll = async () => { + if (window.confirm('모든 데이터를 삭제하시겠습니까?')) { + try { + await deleteAllAnalysis() + } catch (error) { + console.error('Delete error:', error) + alert('데이터 삭제 중 오류가 발생했습니다.') + } + } + } + if (analysisData?.content === null || analysisData?.content === undefined) return null return ( @@ -61,14 +115,24 @@ const AnalysisContent = ({ {isEditable && ( <div className={cx('button-container')}> <div className={cx('button-wrapper')}> - <Button size="small" className={cx('upload-button')} variant="filled"> + <Button + size="small" + className={cx('upload-button')} + variant="filled" + onClick={handleExcelUpload} + > 엑셀 업로드 </Button> - <Button size="small" className={cx('upload-button')} variant="outline"> + <Button + size="small" + className={cx('upload-button')} + variant="filled" + onClick={handleDirectInput} + > 직접 입력 </Button> </div> - <Button size="small" variant="filled"> + <Button size="small" variant="filled" onClick={handleDeleteAll} disabled={isLoading}> 전체 삭제 </Button> </div> @@ -86,6 +150,16 @@ const AnalysisContent = ({ maxPage={analysisData?.totalPages} onPageChange={onPageChange} /> + + {uploadType && ( + <AnalysisUploadModal + isOpen={isModalOpen} + onClose={handleCloseModal} + strategyId={strategyId} + uploadType={uploadType} + message={uploadType === 'excel' ? '엑셀 업로드' : '일간분석 데이터 입력'} + /> + )} </div> ) } diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index dc9bcf01..41ef1897 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames/bind' import { StrategiesModel } from '@/shared/types/strategy-data' import { Button } from '@/shared/ui/button' +import { LinkButton } from '@/shared/ui/link-button' import { formatNumber } from '@/shared/utils/format' import AreaChart from './area-chart' @@ -57,10 +58,15 @@ const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { <p>{data.isPublic === 'PUBLIC' ? '공개' : '비공개'}</p> </div> <div className={cx('manage-buttons')}> - <Button size="small" variant="filled"> + <LinkButton + size="small" + variant="filled" + href={`/my/strategies/manage/${data.strategyId}`} + className={cx('manage-button')} + > 관리 - </Button> - <Button size="small" variant="outline"> + </LinkButton> + <Button size="small" variant="outline" className={cx('manage-button')}> 삭제 </Button> </div> diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index 5374efc8..29126b7b 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -22,7 +22,7 @@ display: flex; flex-direction: column; gap: 8px; - button { + .manage-button { width: 74px; height: 30px; padding: 7px 16px; diff --git a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx index e7833987..61254eda 100644 --- a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx +++ b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx @@ -8,7 +8,12 @@ import { useGetMyStrategyList } from '@/app/(dashboard)/my/_hooks/query/use-get- import { useIntersectionObserver } from '@/shared/hooks/custom/use-intersection-observer' const MyStrategyList = () => { - const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetMyStrategyList() + const { + data: strategyData, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + } = useGetMyStrategyList() const loadMoreRef = useRef<HTMLDivElement>(null) @@ -26,7 +31,7 @@ const MyStrategyList = () => { onIntersect, }) - const strategies = data?.pages.flatMap((page) => page.strategies) || [] + const strategies = strategyData?.pages.flatMap((page) => page.strategies) || [] return ( <> {strategies.map((strategy) => ( diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index 7ff3fffc..ec0ffed4 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -22,12 +22,14 @@ const cx = classNames.bind(styles) export type InformationType = { title: TitleType; data: string | number } | InformationModel[] const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { - const { data } = useGetDetailsInformationData({ + const { data: detailsInfoData } = useGetDetailsInformationData({ strategyId: params.strategyId, }) - - const { detailsSideData, detailsInformationData } = data || {} - + const { data: subscribeData } = useGetDetailsInformationData({ + strategyId: params.strategyId, + }) + const { detailsSideData, detailsInformationData } = detailsInfoData || {} + const { detailsInformationData: subscribeInfo } = subscribeData || {} const hasDetailsSideData = detailsSideData?.map((data) => { if (!Array.isArray(data)) return data.data !== undefined }) @@ -47,7 +49,9 @@ const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { )} <AnalysisContainer type="my" strategyId={params.strategyId} /> <SideContainer hasButton={true}> - <SubscriberItem subscribers={99} /> + {subscribeInfo && ( + <SubscriberItem subscribers={subscribeInfo?.subscriptionCount} isMyStrategy={true} /> + )} {hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( <div key={`${data}_${idx}`}> From db82097d98b78863931eafc6908cf6cd6e327033 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 01:07:42 +0900 Subject: [PATCH 564/648] =?UTF-8?q?feat:=20NoticeDetailPage=EC=97=90=20Not?= =?UTF-8?q?iceDetail=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#217)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/notices/[noticeId]/detail/page.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/(landing)/notices/[noticeId]/detail/page.tsx b/app/(landing)/notices/[noticeId]/detail/page.tsx index b6c54f06..f06aa3a2 100644 --- a/app/(landing)/notices/[noticeId]/detail/page.tsx +++ b/app/(landing)/notices/[noticeId]/detail/page.tsx @@ -1,5 +1,7 @@ +import NoticeDetail from '../../_ui/notice-detail' + const NoticeDetailPage = () => { - return <></> + return <NoticeDetail /> } export default NoticeDetailPage From 6cb2517f8c72207b7d961799a8fe8445f1472064 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 01:17:23 +0900 Subject: [PATCH 565/648] =?UTF-8?q?design:=20NoticeDetail=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20CSS=20=EB=B3=80=EA=B2=BD=20(#217)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/notices/_ui/notice-detail/styles.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(landing)/notices/_ui/notice-detail/styles.module.scss b/app/(landing)/notices/_ui/notice-detail/styles.module.scss index 007fea2e..1e1a741a 100644 --- a/app/(landing)/notices/_ui/notice-detail/styles.module.scss +++ b/app/(landing)/notices/_ui/notice-detail/styles.module.scss @@ -1,5 +1,5 @@ .container { - width: 1190px; + width: 100%; height: 813px; padding: 52px 28px; background-color: $color-white; From 02d80f363a8ce57e00d373f0d9eccdcf7d351a12 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Thu, 5 Dec 2024 03:50:30 +0900 Subject: [PATCH 566/648] =?UTF-8?q?design:=20=EA=B3=B5=EC=A7=80=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/notices/page.tsx | 57 +++++------ app/admin/notices/post/_api/post-notice.ts | 26 +++++ .../notices/post/_hooks/use-notice-form.ts | 25 +++++ .../notices/post/_hooks/use-notice-post.ts | 22 +++++ .../notices/post/_ui/file-input/index.tsx | 38 ++++++++ .../post/_ui/file-input/styles.module.scss | 44 +++++++++ .../notices/post/_ui/input-field/index.tsx | 18 +--- app/admin/notices/post/page.module.scss | 34 +++++++ app/admin/notices/post/page.tsx | 95 +++++++++++-------- app/admin/notices/post/types.ts | 24 +++++ shared/types/response.ts | 5 + .../modal/session-extension-modal/index.tsx | 54 +++++++++++ 12 files changed, 358 insertions(+), 84 deletions(-) create mode 100644 app/admin/notices/post/_api/post-notice.ts create mode 100644 app/admin/notices/post/_hooks/use-notice-form.ts create mode 100644 app/admin/notices/post/_hooks/use-notice-post.ts create mode 100644 app/admin/notices/post/_ui/file-input/index.tsx create mode 100644 app/admin/notices/post/_ui/file-input/styles.module.scss create mode 100644 app/admin/notices/post/page.module.scss create mode 100644 app/admin/notices/post/types.ts create mode 100644 shared/types/response.ts create mode 100644 shared/ui/modal/session-extension-modal/index.tsx diff --git a/app/admin/notices/page.tsx b/app/admin/notices/page.tsx index e3e3175c..3553e0ce 100644 --- a/app/admin/notices/page.tsx +++ b/app/admin/notices/page.tsx @@ -4,7 +4,6 @@ import classNames from 'classnames/bind' import { Button } from '@/shared/ui/button' import Pagination from '@/shared/ui/pagination' -import { SearchInput } from '@/shared/ui/search-input' import VerticalTable from '@/shared/ui/table/vertical' import Title from '@/shared/ui/title' @@ -29,41 +28,37 @@ const AdminNoticesPage = () => { 총 <span className={cx('colored')}>{TOTAL_NOTICE}</span>개 </span> } - Right={<SearchInput placeholder="제목을 입력하세요." />} className={cx('header')} /> <VerticalTable tableHead={['No.', '제목', '등록일', '작성일', '']} - tableBody={ - // [] - RES.data.content.map((d, idx) => [ - idx + 1, - d.title, - d.publishedAt.slice(0, 10), - d.createdAt.slice(0, 10), - <Button.ButtonGroup key={d.content}> - {/* TODO: onclick 로직 정의 */} + tableBody={RES.data.content.map((d, idx) => [ + idx + 1, + d.title, + d.publishedAt.slice(0, 10), + d.createdAt.slice(0, 10), + <Button.ButtonGroup key={d.content}> + {/* TODO: onclick 로직 정의 */} - <Button - onClick={() => {}} - size="small" - className={cx('button')} - style={{ padding: '16px 7px' }} - > - 수정 - </Button> - <Button - size="small" - onClick={() => {}} - variant="filled" - className={cx('button')} - style={{ padding: '16px 7px' }} - > - 삭제 - </Button> - </Button.ButtonGroup>, - ]) - } + <Button + onClick={() => {}} + size="small" + className={cx('button')} + style={{ padding: '16px 7px' }} + > + 수정 + </Button> + <Button + size="small" + onClick={() => {}} + variant="filled" + className={cx('button')} + style={{ padding: '16px 7px' }} + > + 삭제 + </Button> + </Button.ButtonGroup>, + ])} // TODO: 실제 값으로 추가 countPerPage={10} currentPage={1} diff --git a/app/admin/notices/post/_api/post-notice.ts b/app/admin/notices/post/_api/post-notice.ts new file mode 100644 index 00000000..8b32f275 --- /dev/null +++ b/app/admin/notices/post/_api/post-notice.ts @@ -0,0 +1,26 @@ +import axiosInstance from '@/shared/api/axios' + +import { NoticeFormModel, PostNoticeResopnseModel } from '../types' + +const postNotice = async (formData: NoticeFormModel) => { + const data = new FormData() + data.append('title', formData.title) + data.append('content', formData.content) + formData.files.forEach((file) => data.append('files', file)) + + try { + const res = await axiosInstance.post<PostNoticeResopnseModel>('/api/admin/notices', { + method: 'POST', + body: data, + }) + + if (!res.data.isSuccess) throw new Error('Error : ' + res.data.message) + + alert('공지 등록이 완료되었습니다.') + } catch (error) { + console.error(error) + alert('공지 등록 중 오류가 발생했습니다.') + } +} + +export default postNotice diff --git a/app/admin/notices/post/_hooks/use-notice-form.ts b/app/admin/notices/post/_hooks/use-notice-form.ts new file mode 100644 index 00000000..f169e3d0 --- /dev/null +++ b/app/admin/notices/post/_hooks/use-notice-form.ts @@ -0,0 +1,25 @@ +import { useState } from 'react' + +import { NoticeFormModel } from '../types' + +const useNoticeForm = () => { + const [formData, setFormData] = useState<NoticeFormModel>({ + title: '', + content: '', + files: [], + }) + + const onInputChange = (name: keyof NoticeFormModel, value: string | File[]) => { + setFormData((prev) => ({ + ...prev, + [name]: value, + })) + } + + return { + formData, + onInputChange, + } +} + +export default useNoticeForm diff --git a/app/admin/notices/post/_hooks/use-notice-post.ts b/app/admin/notices/post/_hooks/use-notice-post.ts new file mode 100644 index 00000000..76dc5bca --- /dev/null +++ b/app/admin/notices/post/_hooks/use-notice-post.ts @@ -0,0 +1,22 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import postNotice from '../_api/post-notice' +import { NoticeFormModel } from '../types' + +const usePostNotice = (formData: NoticeFormModel) => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => postNotice(formData), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ['notices'], + }) + }, + onError: (err) => { + console.error('Error : ', err) + }, + }) +} + +export default usePostNotice diff --git a/app/admin/notices/post/_ui/file-input/index.tsx b/app/admin/notices/post/_ui/file-input/index.tsx new file mode 100644 index 00000000..5b2ec3f9 --- /dev/null +++ b/app/admin/notices/post/_ui/file-input/index.tsx @@ -0,0 +1,38 @@ +'use client' + +import { ComponentPropsWithoutRef } from 'react' + +import Image from 'next/image' + +import { FileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props extends ComponentPropsWithoutRef<'input'> { + accept?: string + preview?: string +} + +const FileInput = ({ preview, accept = '*', onChange, ...props }: Props) => { + return ( + <div className={cx('container')}> + {preview && ( + <Image width={24} height={24} src={preview} alt="Preview" className={cx('preview')} /> + )} + <input + type="file" + multiple + accept={accept} + onChange={onChange} + className={cx('input')} + {...props} + /> + <FileIcon className={cx('icon')} /> + </div> + ) +} + +export default FileInput diff --git a/app/admin/notices/post/_ui/file-input/styles.module.scss b/app/admin/notices/post/_ui/file-input/styles.module.scss new file mode 100644 index 00000000..1437a7c1 --- /dev/null +++ b/app/admin/notices/post/_ui/file-input/styles.module.scss @@ -0,0 +1,44 @@ +.container { + position: relative; + display: inline-block; + width: 100%; + height: 40px; + border-radius: 4px; + border: 1px solid $color-gray-400; + color: $color-gray-400; + @include typo-c1; + + svg { + width: 24px; + } +} + +.input { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + z-index: 10; + + &:hover { + cursor: pointer; + } +} + +.preview { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); +} + +.icon { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + width: 24px; + color: $color-gray-500; +} diff --git a/app/admin/notices/post/_ui/input-field/index.tsx b/app/admin/notices/post/_ui/input-field/index.tsx index 145cf5f1..67b436ab 100644 --- a/app/admin/notices/post/_ui/input-field/index.tsx +++ b/app/admin/notices/post/_ui/input-field/index.tsx @@ -1,9 +1,8 @@ 'use client' -import classNames from 'classnames/bind' +import { ReactNode } from 'react' -import { Input } from '@/shared/ui/input' -import { Textarea } from '@/shared/ui/textarea' +import classNames from 'classnames/bind' import styles from './styles.module.scss' @@ -11,21 +10,14 @@ const cx = classNames.bind(styles) interface Props { label: string - inputType: JSX.IntrinsicElements['input']['type'] | 'textArea' - placeholder?: string + Input: ReactNode } -const InputField = ({ label, inputType, placeholder }: Props) => { +const InputField = ({ label, Input }: Props) => { return ( <label className={cx('container')}> <span className={cx('label')}>{label}</span> - <div className={cx('input')}> - {inputType !== 'textArea' ? ( - <Input type={inputType} inputSize="full" placeholder={placeholder} /> - ) : ( - <Textarea rows={26} placeholder={placeholder} /> - )} - </div> + <div className={cx('input')}>{Input}</div> </label> ) } diff --git a/app/admin/notices/post/page.module.scss b/app/admin/notices/post/page.module.scss new file mode 100644 index 00000000..59d57426 --- /dev/null +++ b/app/admin/notices/post/page.module.scss @@ -0,0 +1,34 @@ +.container { + padding: 86px 0 80px; + border-radius: 8px; + margin-bottom: 42px; + background-color: $color-white; +} + +.form { + display: flex; + flex-direction: column; + align-items: center; +} + +.input-field { + width: 795px; + margin-bottom: 64px; + display: flex; + flex-direction: column; + gap: 30px; +} + +.textarea { + height: 420px; + + &::placeholder { + vertical-align: middle; + } +} + +.button { + width: 74px; + height: 30px; + border: 1px solid $color-gray-300; +} diff --git a/app/admin/notices/post/page.tsx b/app/admin/notices/post/page.tsx index 349e3519..a37a1b7b 100644 --- a/app/admin/notices/post/page.tsx +++ b/app/admin/notices/post/page.tsx @@ -1,59 +1,74 @@ 'use client' +import classNames from 'classnames/bind' + import { Button } from '@/shared/ui/button' import BackHeader from '@/shared/ui/header/back-header' +import { Input } from '@/shared/ui/input' +import { Textarea } from '@/shared/ui/textarea' import Title from '@/shared/ui/title' +import useNoticeForm from './_hooks/use-notice-form' +import usePostNotice from './_hooks/use-notice-post' +import FileInput from './_ui/file-input' import InputField from './_ui/input-field' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) const AdminNoticePostPage = () => { + const { formData, onInputChange } = useNoticeForm() + const { mutate: postNotice } = usePostNotice(formData) + return ( <> <BackHeader label="공지사항으로 돌아가기" /> - {/* TODO: inline css 제거 */} <Title label="공지사항 등록" style={{ margin: '0 0 26px 12.6px' }} /> - <div - style={{ - padding: '0 45px 37px', - borderRadius: '8px', - marginBottom: '42px', - backgroundColor: 'aliceblue', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }} - > - {/* TODO: 디자인 계속 바뀌는 느낌이라서 일단 가운데 정렬 해둠 */} - <div - style={{ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', + <div className={cx('container')}> + <form + className={cx('form')} + onSubmit={(e) => { + e.preventDefault() + postNotice() }} > - <div - style={{ - width: '795px', - backgroundColor: 'aliceblue', - display: 'flex', - flexDirection: 'column', - gap: '30px', - }} - > - <InputField label="제목" inputType="text" placeholder="제목을 입력하세요." /> - <InputField label="내용" inputType="textArea" placeholder="내용을 입력하세요." /> - <InputField label="파일첨부" inputType="file" /> + <div className={cx('input-field')}> + <InputField + label="제목" + Input={ + <Input + type="text" + inputSize="full" + placeholder="제목을 입력하세요." + value={formData.title} + onChange={(e) => onInputChange('title', e.target.value)} + /> + } + /> + <InputField + label="내용" + Input={ + <Textarea + className={cx('textarea')} + placeholder="내용을 입력하세요." + value={formData.content} + onChange={(e) => onInputChange('content', e.target.value)} + /> + } + /> + <InputField + label="파일첨부" + Input={ + <FileInput + onChange={(e) => onInputChange('files', Array.from(e.target.files || []))} + /> + } + /> </div> - <Button.ButtonGroup> - {/* TODO: onclick 로직 정의 */} - <Button size="small" onClick={() => {}}> - 수정 - </Button> - <Button size="small" onClick={() => {}} variant="filled"> - 삭제 - </Button> - </Button.ButtonGroup> - </div> + <Button size="small" type="submit" variant="filled"> + 공지 등록하기 + </Button> + </form> </div> </> ) diff --git a/app/admin/notices/post/types.ts b/app/admin/notices/post/types.ts new file mode 100644 index 00000000..205d4c6e --- /dev/null +++ b/app/admin/notices/post/types.ts @@ -0,0 +1,24 @@ +import { APIResponseBaseModel } from '@/shared/types/response' + +export interface NoticeFormModel { + title: string + content: string + files: File[] +} + +export interface PostNoticeResopnseModel extends APIResponseBaseModel<boolean> { + data: { + noticeId: number + user: { + id: number + nickname: string + } + title: string + content: string + createdAt: string + publishedAt: string + updatedAt: string + createdBy: string + updatedBy: string + } +} diff --git a/shared/types/response.ts b/shared/types/response.ts new file mode 100644 index 00000000..1585d465 --- /dev/null +++ b/shared/types/response.ts @@ -0,0 +1,5 @@ +export interface APIResponseBaseModel<T extends boolean> { + isSuccess: T + message: string + code: T extends false ? number : never +} diff --git a/shared/ui/modal/session-extension-modal/index.tsx b/shared/ui/modal/session-extension-modal/index.tsx new file mode 100644 index 00000000..58dd8f8c --- /dev/null +++ b/shared/ui/modal/session-extension-modal/index.tsx @@ -0,0 +1,54 @@ +'use client' + +import React from 'react' + +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import Modal from '..' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface SessionExtensionModalProps { + isModalOpen: boolean + onCloseModal: () => void + onExtend: () => void + minutesLeft: number +} + +const SessionExtensionModal = ({ + isModalOpen, + onCloseModal, + onExtend, + minutesLeft, +}: SessionExtensionModalProps) => { + const getMessage = () => { + if (minutesLeft > 0) { + return `${minutesLeft}분 후에 자동 로그아웃됩니다.` + } + return '잠시 후 자동 로그아웃됩니다.' + } + + return ( + <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> + <span className={cx('message')}> + 세션이 곧 만료됩니다. + <br /> + {getMessage()} + <br /> + 로그인을 연장하시겠습니까? + </span> + <div className={cx('two-button')}> + <Button onClick={onCloseModal}>아니오</Button> + <Button onClick={onExtend} variant="filled" className={cx('button')}> + 예 + </Button> + </div> + </Modal> + ) +} + +export default SessionExtensionModal From 52d64fe602913a59f8db4eb0477ae82e8ead4fe2 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 04:25:47 +0900 Subject: [PATCH 567/648] =?UTF-8?q?design:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20(#1?= =?UTF-8?q?37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/page.module.scss | 24 +++++++++++++++++++++ app/(dashboard)/my/profile/page.tsx | 23 +++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/(dashboard)/my/profile/page.module.scss diff --git a/app/(dashboard)/my/profile/page.module.scss b/app/(dashboard)/my/profile/page.module.scss new file mode 100644 index 00000000..9fe14a05 --- /dev/null +++ b/app/(dashboard)/my/profile/page.module.scss @@ -0,0 +1,24 @@ +.container { + padding: 40px 28px; +} + +.title { + margin-top: 40px; + @include typo-h4; + margin-bottom: 22px; +} + +.wrapper { + display: flex; + justify-content: space-between; +} + +.user-profile { + display: flex; + flex-direction: column; +} + +.link-button { + align-self: flex-end; + margin-top: 25px; +} diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx index 1d87202a..511e812b 100644 --- a/app/(dashboard)/my/profile/page.tsx +++ b/app/(dashboard)/my/profile/page.tsx @@ -1,7 +1,28 @@ +import classNames from 'classnames/bind' + +import { LinkButton } from '@/shared/ui/link-button' + import UserInfo from './_ui/user-info' +import UserProfile from './_ui/user-profile' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) const MyProfilePage = () => { - return <UserInfo /> + return ( + <div className={cx('container')}> + <p className={cx('title')}>나의 정보</p> + <div className={cx('wrapper')}> + <UserInfo /> + <div className={cx('user-profile')}> + <UserProfile userType={'트레이더'} name={'고양이'} email={'meow@example.com'} /> + <div className={cx('link-button')}> + <LinkButton href={''}>탈퇴하기</LinkButton> + </div> + </div> + </div> + </div> + ) } export default MyProfilePage From b4980d1018771adc8800a237228aa2d7be0e09f0 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 04:30:30 +0900 Subject: [PATCH 568/648] =?UTF-8?q?feat:=20LinkButton=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#137)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/page.tsx | 3 ++- shared/constants/path.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx index 511e812b..001b6d4f 100644 --- a/app/(dashboard)/my/profile/page.tsx +++ b/app/(dashboard)/my/profile/page.tsx @@ -1,5 +1,6 @@ import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' import { LinkButton } from '@/shared/ui/link-button' import UserInfo from './_ui/user-info' @@ -17,7 +18,7 @@ const MyProfilePage = () => { <div className={cx('user-profile')}> <UserProfile userType={'트레이더'} name={'고양이'} email={'meow@example.com'} /> <div className={cx('link-button')}> - <LinkButton href={''}>탈퇴하기</LinkButton> + <LinkButton href={PATH.PROFILE_WITHDRAW}>탈퇴하기</LinkButton> </div> </div> </div> diff --git a/shared/constants/path.ts b/shared/constants/path.ts index b0a6a0d5..024bbc85 100644 --- a/shared/constants/path.ts +++ b/shared/constants/path.ts @@ -17,6 +17,7 @@ export const PATH = { // My PROFILE: '/my/profile', + PROFILE_WITHDRAW: '/my/profile/withdraw', EDIT_PROFILE: '/my/profile/edit', FAVORITES: '/my/favorites', MY_STRATEGIES: '/my/strategies', From 31de1b1deae7e5c9c9f312b7545ad5e835bfb8e4 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Thu, 5 Dec 2024 05:09:43 +0900 Subject: [PATCH 569/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20api=20?= =?UTF-8?q?=EB=B0=8F=20=ED=9B=85=20=EA=B0=9C=EB=B0=9C=20(#247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/strategies/_api/get-strategies.ts | 18 ++++++++++ .../_api/patch-strategy-approval.ts | 26 ++++++++++++++ .../query/use-patch-strategy-approval.ts | 16 +++++++++ .../_hooks/query/use-strategies-data.ts | 12 +++++++ app/admin/strategies/page.tsx | 6 ++++ app/admin/strategies/types.ts | 36 +++++++++++++++++++ 6 files changed, 114 insertions(+) create mode 100644 app/admin/strategies/_api/get-strategies.ts create mode 100644 app/admin/strategies/_api/patch-strategy-approval.ts create mode 100644 app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts create mode 100644 app/admin/strategies/_hooks/query/use-strategies-data.ts create mode 100644 app/admin/strategies/types.ts diff --git a/app/admin/strategies/_api/get-strategies.ts b/app/admin/strategies/_api/get-strategies.ts new file mode 100644 index 00000000..1229c886 --- /dev/null +++ b/app/admin/strategies/_api/get-strategies.ts @@ -0,0 +1,18 @@ +import axios from 'axios' + +import { StrategiesResponseModel } from '../types' + +const getStrategies = async () => { + try { + const res = await axios<StrategiesResponseModel>('/api/admin/strategies') + + if (!res.data.isSuccess) throw new Error(res.data.message) + + return res.data.data + } catch (err) { + console.log('Error : ' + err) + throw err + } +} + +export default getStrategies diff --git a/app/admin/strategies/_api/patch-strategy-approval.ts b/app/admin/strategies/_api/patch-strategy-approval.ts new file mode 100644 index 00000000..363c9a1c --- /dev/null +++ b/app/admin/strategies/_api/patch-strategy-approval.ts @@ -0,0 +1,26 @@ +import axios from 'axios' + +import { StrategiesResponseModel } from '../types' + +const patchStrategyApproval = async (strategyId: number, isApproved: boolean) => { + try { + const res = await axios.patch<StrategiesResponseModel>( + `/api/admin/strategies/${strategyId}`, + null, + { + params: { + isApproved: isApproved ? 'APPROVED' : 'DENY', + }, + } + ) + + if (!res.data.isSuccess) throw new Error(res.data.message) + + return res.data.data + } catch (err) { + console.log('Error : ' + err) + throw err + } +} + +export default patchStrategyApproval diff --git a/app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts b/app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts new file mode 100644 index 00000000..e0300d31 --- /dev/null +++ b/app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts @@ -0,0 +1,16 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import patchStrategyApproval from '../../_api/patch-strategy-approval' + +const usePatchStrategyApproval = (strategyId: number, isApproved: boolean) => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => patchStrategyApproval(strategyId, isApproved), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['adminStrategies'] }) + }, + }) +} + +export default usePatchStrategyApproval diff --git a/app/admin/strategies/_hooks/query/use-strategies-data.ts b/app/admin/strategies/_hooks/query/use-strategies-data.ts new file mode 100644 index 00000000..d0f74156 --- /dev/null +++ b/app/admin/strategies/_hooks/query/use-strategies-data.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import getStrategies from '../../_api/get-strategies' + +const useStrategiesData = () => { + return useQuery({ + queryKey: ['adminStrategies'], + queryFn: () => getStrategies(), + }) +} + +export default useStrategiesData diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index 278e9531..41ef1feb 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -1,4 +1,10 @@ +import usePatchStrategyApproval from './_hooks/query/use-patch-strategy-approval' +import useStrategiesData from './_hooks/query/use-strategies-data' + const AdminStrategyPage = () => { + const { data } = useStrategiesData() + const { mutate } = usePatchStrategyApproval(1, false) + return <></> } diff --git a/app/admin/strategies/types.ts b/app/admin/strategies/types.ts new file mode 100644 index 00000000..5e9b3a66 --- /dev/null +++ b/app/admin/strategies/types.ts @@ -0,0 +1,36 @@ +import { APIResponseBaseModel } from '@/shared/types/response' + +export interface StrategiesResponseModel extends APIResponseBaseModel<boolean> { + data: { + subscriptionCount: number // 구독 수 + minimumInvestmentAmount: number // 최소 투자 금액 + initialInvestment: number // 투자 원금 + kpRatio: number + smScore: number + lastProfitDate: string // 최종 손익 입력 일자 + strategyRegisteredDate: string // 전략 등록일 + // [ + // { + // "stockGroupId": 1, + // "stockTypeId" : 12312, + // "stockIconUrl": "https://~~", // 종목 아이콘 이미지 경로 + // }, + // { + // "stockGroupId": 1, + // "stockTypeId" : 12312, + // "stockIconUrl": "https://~~", // 종목 아이콘 이미지 경로 + // } + // ] + tradeIconUrl: string // 매매 유형 이미지 경로 + strategyName: string // 전략명 + averageRating: number // 평균별점 + cumulativeProfitRate: number // 누적 수익률 + maxDrawdownRate: number // 최대 자본 인하율 + averageProfitRate: number // 평균 손익률 + profitFactor: number + winRate: number // 승률 (%) + } +} + +// eslint-disable-next-line +export interface StrategiesPatchResponseModel extends APIResponseBaseModel<boolean> {} From 4e7b96ca53929c029d45c29a38147fa0a39692a7 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 09:45:16 +0900 Subject: [PATCH 570/648] =?UTF-8?q?feat:=20=EB=9E=9C=EB=94=A9=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A7=80?= =?UTF-8?q?=ED=91=9C=20api=20=EC=97=B0=EB=8F=99=20(#243)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_api/user-metrics.ts | 11 ++++++++ .../_hooks/query/use-get-user-metrics.ts | 13 ++++++++++ .../(home)/_ui/user-metrics-section/index.tsx | 26 ++++++++++++------- app/(landing)/(home)/types.ts | 6 +++++ 4 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 app/(landing)/(home)/_api/user-metrics.ts create mode 100644 app/(landing)/(home)/_hooks/query/use-get-user-metrics.ts create mode 100644 app/(landing)/(home)/types.ts diff --git a/app/(landing)/(home)/_api/user-metrics.ts b/app/(landing)/(home)/_api/user-metrics.ts new file mode 100644 index 00000000..714c6654 --- /dev/null +++ b/app/(landing)/(home)/_api/user-metrics.ts @@ -0,0 +1,11 @@ +import axios from 'axios' + +export const getUserMetrics = async () => { + try { + const response = await axios.get('/api/main/total-rate') + return response.data.result + } catch (err) { + console.error(err) + throw new Error('사용자 이용 지표 조회에 실패했습니다.') + } +} diff --git a/app/(landing)/(home)/_hooks/query/use-get-user-metrics.ts b/app/(landing)/(home)/_hooks/query/use-get-user-metrics.ts new file mode 100644 index 00000000..2368a017 --- /dev/null +++ b/app/(landing)/(home)/_hooks/query/use-get-user-metrics.ts @@ -0,0 +1,13 @@ +import { useQuery } from '@tanstack/react-query' + +import { getUserMetrics } from '../../_api/user-metrics' +import { UserMetricsModel } from '../../types' + +const useGetUserMetrics = () => { + return useQuery<UserMetricsModel>({ + queryKey: ['userMetrics'], + queryFn: getUserMetrics, + }) +} + +export default useGetUserMetrics diff --git a/app/(landing)/(home)/_ui/user-metrics-section/index.tsx b/app/(landing)/(home)/_ui/user-metrics-section/index.tsx index 52f4bf92..31e59fe0 100644 --- a/app/(landing)/(home)/_ui/user-metrics-section/index.tsx +++ b/app/(landing)/(home)/_ui/user-metrics-section/index.tsx @@ -1,5 +1,10 @@ +'use client' + import classNames from 'classnames/bind' +import Spinner from '@/shared/ui/spinner' + +import useGetUserMetrics from '../../_hooks/query/use-get-user-metrics' import HomeSubtitle from '../home-subtitle' import MetricCard from '../metric-card' import styles from './styles.module.scss' @@ -7,11 +12,14 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) const UserMetricsSection = () => { - const { totalTraderCount, totalStrategiesCount, totalUserCount, totalSubscribeCount } = { - totalTraderCount: 512, - totalStrategiesCount: 5210, - totalUserCount: 43431, - totalSubscribeCount: 8210, + const { data: metrics, isLoading } = useGetUserMetrics() + + if (isLoading) { + return <Spinner /> + } + + if (!metrics) { + return null } return ( @@ -19,10 +27,10 @@ const UserMetricsSection = () => { <HomeSubtitle>이미 이렇게 많은 사람들이 주식 전략을 공유하고, 구독하고 있어요!</HomeSubtitle> <div className={cx('card-wrapper')}> - <MetricCard count={totalTraderCount} label="명의 트레이더가" /> - <MetricCard count={totalStrategiesCount} label="개의 투자 전략을 올렸어요!" /> - <MetricCard count={totalUserCount} label="명의 투자자가" /> - <MetricCard count={totalSubscribeCount} label="개의 전략을 구독 중이에요!" /> + <MetricCard count={metrics.totalTrader} label="명의 트레이더가" /> + <MetricCard count={metrics.totalStrategies} label="개의 투자 전략을 올렸어요!" /> + <MetricCard count={metrics.totalInvestor} label="명의 투자자가" /> + <MetricCard count={metrics.totalSubscribe} label="개의 전략을 구독 중이에요!" /> </div> </section> ) diff --git a/app/(landing)/(home)/types.ts b/app/(landing)/(home)/types.ts new file mode 100644 index 00000000..13e87c2f --- /dev/null +++ b/app/(landing)/(home)/types.ts @@ -0,0 +1,6 @@ +export interface UserMetricsModel { + totalInvestor: number + totalTrader: number + totalStrategies: number + totalSubscribe: number +} From b38fa7920b91f5664da16d8747758e00437b2bc3 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 11:05:03 +0900 Subject: [PATCH 571/648] =?UTF-8?q?fix:=20=EB=82=98=EC=9D=98=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EC=9D=BC=EA=B0=84=EB=B6=84=EC=84=9D=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20=EC=A0=84=EB=9E=B5=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EB=94=94=EB=A5=BC=20=EC=A0=9C=EC=99=B8=ED=95=98?= =?UTF-8?q?=EA=B3=A0=20=EB=84=A3=EC=96=B4=EC=A3=BC=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#251)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/analysis-container/analysis-content.tsx | 4 +--- shared/ui/table/vertical/index.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx index 5ebee7dc..1faf4b82 100644 --- a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx @@ -52,10 +52,8 @@ const AnalysisContent = ({ onPageChange, isEditable = false, }: Props) => { - const { data: analysisData } = useGetAnalysis(strategyId, type, currentPage, ANALYSIS_PAGE_COUNT) const { mutate } = useGetAnalysisDownload() - const [uploadType, setUploadType] = useState<'excel' | 'direct' | null>(null) const { isModalOpen, openModal, closeModal } = useModal() @@ -83,7 +81,7 @@ const AnalysisContent = ({ const handleDownload = () => { mutate({ strategyId, type }) } - + const tableHeader = type === 'daily' ? DAILY_TABLE_HEADER : MONTHLY_TABLE_HEADER const handleExcelUpload = () => { diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 124a12a3..ebd14ad9 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -54,9 +54,11 @@ const VerticalTable = ({ <tbody> {slicedTableBody.map((row) => ( <tr key={Object.values(row)[0]}> - {Object.values(row).map((data, idx) => ( - <td key={data + idx}>{formatNumber(data)}</td> - ))} + {Object.values(row) + .slice(isEditable ? 1 : 0) + .map((data, idx) => ( + <td key={data + idx}>{formatNumber(data)}</td> + ))} {isEditable && ( <td className={cx('button-container')}> <Button size="small" variant="outline" className={cx('edit-button')}> From aa6f54c3e22b18b84c1dd3a28aacd9717eda0dd2 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 11:11:37 +0900 Subject: [PATCH 572/648] =?UTF-8?q?settings:=20xlsx=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=EC=9D=98=20=EB=B3=B4=EC=95=88=20?= =?UTF-8?q?=EC=B7=A8=EC=95=BD=EC=A0=90=20=EB=AC=B8=EC=A0=9C=EB=A1=9C=20aud?= =?UTF-8?q?it=20=EC=B2=B4=ED=81=AC=20=EC=9E=84=EC=8B=9C=EB=A1=9C=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20(#251)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lefthook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lefthook.yml b/lefthook.yml index 8bfe2085..56dda49f 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -13,4 +13,4 @@ pre-commit: pre-push: commands: audit-check: - run: npm audit + skip: true From e4fd7b37f92bb6b096f0e61eed5a5d638e503b93 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 11:21:55 +0900 Subject: [PATCH 573/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20api=20=EC=B6=94=EA=B0=80=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-side-item/index.tsx | 3 ++ .../_ui/details-side-item/side-item.tsx | 33 ++++++++++++++++--- .../strategies/manage/[strategyId]/page.tsx | 2 +- .../[strategyId]/_api/post-question.ts | 24 ++++++++++++++ .../_hooks/query/use-post-question.ts | 18 ++++++++++ .../strategies/[strategyId]/page.tsx | 1 + 6 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 app/(dashboard)/strategies/[strategyId]/_api/post-question.ts create mode 100644 app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-question.ts diff --git a/app/(dashboard)/_ui/details-side-item/index.tsx b/app/(dashboard)/_ui/details-side-item/index.tsx index 7ec97e00..c550f81a 100644 --- a/app/(dashboard)/_ui/details-side-item/index.tsx +++ b/app/(dashboard)/_ui/details-side-item/index.tsx @@ -20,6 +20,7 @@ export interface InformationModel { } interface Props { + strategyId: number information: InformationModel | InformationModel[] profileImage?: string isMyStrategy?: boolean @@ -27,6 +28,7 @@ interface Props { } const DetailsSideItem = ({ + strategyId, information, profileImage, isMyStrategy = true, @@ -48,6 +50,7 @@ const DetailsSideItem = ({ </div> ) : ( <SideItem + strategyId={strategyId} title={information.title} data={information.data} profileImage={profileImage} diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index f9afb4b5..f63d7dd0 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -3,6 +3,7 @@ import { useRef, useState } from 'react' import classNames from 'classnames/bind' import useModal from '@/shared/hooks/custom/use-modal' +import { useAuthStore } from '@/shared/stores/use-auth-store' import Avatar from '@/shared/ui/avatar' import { Button } from '@/shared/ui/button' import AddQuestionModal from '@/shared/ui/modal/add-question-modal' @@ -10,11 +11,13 @@ import QuestionGuideModal from '@/shared/ui/modal/question-guide-modal' import { formatNumber } from '@/shared/utils/format' import { TitleType } from '.' +import usePostQuestion from '../../strategies/[strategyId]/_hooks/query/use-post-question' import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { + strategyId: number title: Omit<TitleType, '트레이더' | '최소 투자 금액' | '투자 원금'> data: number | string profileImage?: string @@ -22,7 +25,14 @@ interface Props { strategyName?: string } -const SideItem = ({ title, data, profileImage, isMyStrategy = false, strategyName }: Props) => { +const SideItem = ({ + strategyId, + title, + data, + profileImage, + isMyStrategy = false, + strategyName, +}: Props) => { const [isEmpty, setIsEmpty] = useState(false) const { isModalOpen: isAddQuestionModalOpen, @@ -34,22 +44,35 @@ const SideItem = ({ title, data, profileImage, isMyStrategy = false, strategyNam openModal: guideOpenModal, closeModal: guideCloseModal, } = useModal() + const user = useAuthStore((state) => state.user) const titleRef = useRef<HTMLInputElement | null>(null) const contentRef = useRef<HTMLTextAreaElement | null>(null) + const { mutate } = usePostQuestion() const handleAddQuestion = () => { if (titleRef.current && contentRef.current) { const title = titleRef.current.value.trim() const content = contentRef.current.value.trim() if (title !== '' && content !== '') { - // TODO: 문의 추가 api 연결 - questionCloseModal() - guideOpenModal() + const question = { + strategyId, + title: titleRef.current.value, + content: contentRef.current.value, + } + mutate(question, { + onSuccess: (result) => { + if (result?.isSuccess) { + questionCloseModal() + guideOpenModal() + } + }, + }) } else { setIsEmpty(true) } } } + const isTrader = user?.role.includes('TRADER') return ( <div className={cx('side-item')}> @@ -61,7 +84,7 @@ const SideItem = ({ title, data, profileImage, isMyStrategy = false, strategyNam <Avatar src={profileImage} /> <p>{data}</p> </div> - {!isMyStrategy && ( + {!isMyStrategy && !isTrader && ( <Button onClick={questionOpenModal} size="small" style={{ height: '30px' }}> 문의하기 </Button> diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index c64af123..c1242056 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -59,7 +59,7 @@ const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { {hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( <div key={`${data}_${idx}`}> - <DetailsSideItem information={data} /> + <DetailsSideItem information={data} strategyId={params.strategyId} /> </div> ))} </SideContainer> diff --git a/app/(dashboard)/strategies/[strategyId]/_api/post-question.ts b/app/(dashboard)/strategies/[strategyId]/_api/post-question.ts new file mode 100644 index 00000000..9e1ec0fc --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_api/post-question.ts @@ -0,0 +1,24 @@ +import axiosInstance from '@/shared/api/axios' +import { APIResponseBaseModel } from '@/shared/types/response' + +interface PostQuestionsReturnModel extends APIResponseBaseModel<boolean> { + result: object +} + +const postQuestion = async ( + strategyId: number, + title: string, + content: string +): Promise<PostQuestionsReturnModel | null | undefined> => { + try { + const response = await axiosInstance.post(`/api/strategies/${strategyId}/questions`, { + title, + content, + }) + return response.data + } catch (err) { + throw new Error(err instanceof Error ? err.message : '문의 등록 실패') + } +} + +export default postQuestion diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-question.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-question.ts new file mode 100644 index 00000000..a340a14f --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-question.ts @@ -0,0 +1,18 @@ +import { useMutation } from '@tanstack/react-query' + +import postQuestions from '../../_api/post-question' + +interface Props { + strategyId: number + title: string + content: string +} + +const usePostQuestion = () => { + return useMutation({ + mutationFn: ({ strategyId, title, content }: Props) => + postQuestions(strategyId, title, content), + }) +} + +export default usePostQuestion diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 4816e62c..63f82902 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -73,6 +73,7 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { detailsSideData?.map((data, idx) => ( <div key={`${data}_${idx}`}> <DetailsSideItem + strategyId={params.strategyId} information={data} isMyStrategy={user?.nickname === information.nickname} strategyName={information.strategyName} From e2e9e311bdf231edab312aced144c571e9d557af Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 11:22:19 +0900 Subject: [PATCH 574/648] =?UTF-8?q?fix:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20props=20=EC=88=98=EC=A0=95=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-side-item/details-side-item.stories.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/_ui/details-side-item/details-side-item.stories.tsx b/app/(dashboard)/_ui/details-side-item/details-side-item.stories.tsx index 5a5f0f1a..a29fe657 100644 --- a/app/(dashboard)/_ui/details-side-item/details-side-item.stories.tsx +++ b/app/(dashboard)/_ui/details-side-item/details-side-item.stories.tsx @@ -8,7 +8,10 @@ const meta: Meta<typeof DetailsSideItem> = { tags: ['autodocs'], } -const sideItems: StoryFn<{ information: InformationModel | InformationModel[] }> = (args) => ( +const sideItems: StoryFn<{ + information: InformationModel | InformationModel[] + strategyId: number +}> = (args) => ( <div style={{ width: '100%', height: '100%', background: '#fafafa', padding: '20px' }}> <DetailsSideItem {...args} /> </div> @@ -17,11 +20,13 @@ const sideItems: StoryFn<{ information: InformationModel | InformationModel[] }> export const Default = sideItems.bind({}) Default.args = { information: { title: '투자 원금', data: '10,000,000' }, + strategyId: 1, } export const Trader = sideItems.bind({}) Trader.args = { information: { title: '트레이더', data: '수밍' }, + strategyId: 1, } export const Multiple = sideItems.bind({}) @@ -30,6 +35,7 @@ Multiple.args = { { title: 'KP Ratio', data: 0.3993 }, { title: 'SM SCORE', data: 67.38 }, ], + strategyId: 1, } export default meta From 7cfccac093c2feae40dfec2c52a9bf783272b34e Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Thu, 5 Dec 2024 12:34:04 +0900 Subject: [PATCH 575/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B5=AC=EC=A1=B0=20=EC=99=84=EC=84=B1=20(#247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/_ui/admin-header/index.tsx | 27 +++++---- app/admin/_ui/admin-header/styles.module.scss | 9 +++ app/admin/_ui/admin-post-button/index.tsx | 42 ++++++++++++++ .../_ui/admin-post-button/styles.module.scss | 5 ++ app/admin/strategies/_api/get-strategies.ts | 2 + .../_hooks/use-admin-strategies-page.ts | 26 +++++++++ app/admin/strategies/constants.ts | 6 ++ app/admin/strategies/page.module.scss | 20 +++++++ app/admin/strategies/page.tsx | 55 ++++++++++++++++++- 9 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 app/admin/_ui/admin-header/styles.module.scss create mode 100644 app/admin/_ui/admin-post-button/index.tsx create mode 100644 app/admin/_ui/admin-post-button/styles.module.scss create mode 100644 app/admin/strategies/_hooks/use-admin-strategies-page.ts create mode 100644 app/admin/strategies/constants.ts create mode 100644 app/admin/strategies/page.module.scss diff --git a/app/admin/_ui/admin-header/index.tsx b/app/admin/_ui/admin-header/index.tsx index dfa1c8d5..cac6b4e7 100644 --- a/app/admin/_ui/admin-header/index.tsx +++ b/app/admin/_ui/admin-header/index.tsx @@ -1,26 +1,25 @@ import { CSSProperties, ReactNode } from 'react' -import Header from '@/shared/ui/header' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) interface Props { Left?: ReactNode Right?: ReactNode styles?: CSSProperties + className?: string } -const headerStyles: CSSProperties = { - position: 'static', - padding: '0', - height: 'calc(40px * 2 + 18px)', - // font B3 - fontSize: '14px', - fontWeight: '600', - lineHeight: '132%', - letterSpacing: '-0.28px', -} - -const AdminContentsHeader = ({ Left, Right }: Props) => { - return <Header as="div" Left={Left} Right={Right} styles={headerStyles} /> +const AdminContentsHeader = ({ Left, Right, className }: Props) => { + return ( + <div className={cx('container', className)}> + {Left} + {Right} + </div> + ) } export default AdminContentsHeader diff --git a/app/admin/_ui/admin-header/styles.module.scss b/app/admin/_ui/admin-header/styles.module.scss new file mode 100644 index 00000000..5feb4570 --- /dev/null +++ b/app/admin/_ui/admin-header/styles.module.scss @@ -0,0 +1,9 @@ +.container { + z-index: zIndex(header); + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 40px 0; + @include typo-b3; +} diff --git a/app/admin/_ui/admin-post-button/index.tsx b/app/admin/_ui/admin-post-button/index.tsx new file mode 100644 index 00000000..bdcc1703 --- /dev/null +++ b/app/admin/_ui/admin-post-button/index.tsx @@ -0,0 +1,42 @@ +'use client' + +import { CSSProperties } from 'react' + +import { useRouter } from 'next/navigation' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + label: string + pathname: string + top?: string + styles?: CSSProperties +} + +const AdminPostButton = ({ label, pathname, top = '-55px', styles }: Props) => { + const router = useRouter() + + const onClick = () => { + router.push(`/admin/${pathname}/post`) + } + + return ( + <Button + onClick={onClick} + variant="filled" + size="small" + className={cx('post-button')} + style={{ top, ...styles }} + > + {label} + </Button> + ) +} + +export default AdminPostButton diff --git a/app/admin/_ui/admin-post-button/styles.module.scss b/app/admin/_ui/admin-post-button/styles.module.scss new file mode 100644 index 00000000..d328031b --- /dev/null +++ b/app/admin/_ui/admin-post-button/styles.module.scss @@ -0,0 +1,5 @@ +.post-button { + position: absolute; + right: 0; + padding: 12px 24px; +} diff --git a/app/admin/strategies/_api/get-strategies.ts b/app/admin/strategies/_api/get-strategies.ts index 1229c886..a893d2c2 100644 --- a/app/admin/strategies/_api/get-strategies.ts +++ b/app/admin/strategies/_api/get-strategies.ts @@ -8,6 +8,8 @@ const getStrategies = async () => { if (!res.data.isSuccess) throw new Error(res.data.message) + console.log('data', res.data.data) + return res.data.data } catch (err) { console.log('Error : ' + err) diff --git a/app/admin/strategies/_hooks/use-admin-strategies-page.ts b/app/admin/strategies/_hooks/use-admin-strategies-page.ts new file mode 100644 index 00000000..d39b7b7a --- /dev/null +++ b/app/admin/strategies/_hooks/use-admin-strategies-page.ts @@ -0,0 +1,26 @@ +import { useState } from 'react' + +import { tabs } from '../constants' + +const useAdminStrategiesPage = () => { + const [activeTab, setActiveTab] = useState(tabs[0].id) + const [inputValue, setInputValue] = useState('') + const [keyword, setKeyword] = useState('') + + const setConditionAndKeyword = () => { + setKeyword(inputValue) + } + + return { + tabs, + activeTab, + setActiveTab, + inputValue, + setInputValue, + keyword, + setKeyword, + setConditionAndKeyword, + } +} + +export default useAdminStrategiesPage diff --git a/app/admin/strategies/constants.ts b/app/admin/strategies/constants.ts new file mode 100644 index 00000000..2c97cccb --- /dev/null +++ b/app/admin/strategies/constants.ts @@ -0,0 +1,6 @@ +import { TabItemModel } from '@/shared/ui/tabs' + +export const tabs: Array<TabItemModel> = [ + { label: '모든 전략', id: 'ALL' }, + { label: '승인 대기', id: 'PENDING' }, +] diff --git a/app/admin/strategies/page.module.scss b/app/admin/strategies/page.module.scss new file mode 100644 index 00000000..366b3e0c --- /dev/null +++ b/app/admin/strategies/page.module.scss @@ -0,0 +1,20 @@ +.title { + margin: $header-height 0 26px 12.6px; +} + +.container { + position: relative; + padding: 35px 30px; + border-radius: 8px; + margin-bottom: 42px; + background-color: $color-white; +} + +.header { + padding-right: 20px; + padding-left: 20px; +} + +.color-primary-500 { + color: $color-orange-500; +} diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index 41ef1feb..e8ee829e 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -1,11 +1,64 @@ +'use client' + +import classNames from 'classnames/bind' + +import Pagination from '@/shared/ui/pagination' +import { SearchInput } from '@/shared/ui/search-input' +import VerticalTable from '@/shared/ui/table/vertical' +import Tabs from '@/shared/ui/tabs' +import Title from '@/shared/ui/title' + +import AdminContentsHeader from '../_ui/admin-header' +import AdminPostButton from '../_ui/admin-post-button' import usePatchStrategyApproval from './_hooks/query/use-patch-strategy-approval' import useStrategiesData from './_hooks/query/use-strategies-data' +import useAdminStrategiesPage from './_hooks/use-admin-strategies-page' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) const AdminStrategyPage = () => { + const { tabs, activeTab, setActiveTab, inputValue, setInputValue } = useAdminStrategiesPage() + const { data } = useStrategiesData() const { mutate } = usePatchStrategyApproval(1, false) - return <></> + return ( + <> + <Title label="전략 관리" className={cx('title')} /> + <section className={cx('container')}> + <AdminPostButton label="전략 등록하기" pathname="strategies" /> + <Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} /> + <AdminContentsHeader + Left={ + <span> + 총 <span className={cx('color-primary-500')}>{3}</span>명 + </span> + } + Right={ + <div> + <SearchInput + value={inputValue} + onChange={(e) => setInputValue(e.target.value)} + // onSearchIconClick={setInputValue} + /> + </div> + } + className={cx('header')} + /> + <VerticalTable + tableHead={['No.', '날짜', '전략명', '닉네임', '공개여부', '승인여부', '']} + tableBody={ + // data?.content ? setTableBody(data.content) : + [[1, '2024.09.15', 'wjsfiraud', 'nkick', true, true, 'buttons']] + } + countPerPage={10} + currentPage={1} + /> + <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + </section> + </> + ) } export default AdminStrategyPage From 453838173c39e05403231f5ac558d00c02d4cf70 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 14:40:23 +0900 Subject: [PATCH 576/648] =?UTF-8?q?fix:=20=ED=83=AD=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=20=EC=8B=9C=20page=EB=A5=BC=201=EC=9C=BC=EB=A1=9C=20=EB=A6=AC?= =?UTF-8?q?=EC=85=8B=20(#248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/questions/_ui/questions-tab/index.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx index ef6b7ea2..505f0ba8 100644 --- a/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx +++ b/app/(dashboard)/my/questions/_ui/questions-tab/index.tsx @@ -2,6 +2,8 @@ import { Suspense, useState } from 'react' +import { useRouter, useSearchParams } from 'next/navigation' + import { QuestionSearchConditionType } from '@/shared/types/questions' import Tabs from '@/shared/ui/tabs' @@ -15,8 +17,18 @@ interface Props { } const QuestionsTab = ({ searchOptions }: Props) => { + const router = useRouter() + const searchParams = useSearchParams() const [activeTab, setActiveTab] = useState('all') + const handleTabChange = (tabId: string) => { + setActiveTab(tabId) + + const params = new URLSearchParams(searchParams) + params.set('page', '1') + router.push(`?${params.toString()}`) + } + const TABS = [ { id: 'all', @@ -65,7 +77,7 @@ const QuestionsTab = ({ searchOptions }: Props) => { }, ] - return <Tabs activeTab={activeTab} onTabChange={setActiveTab} tabs={TABS} /> + return <Tabs activeTab={activeTab} onTabChange={handleTabChange} tabs={TABS} /> } export default QuestionsTab From 0a530650be86e538df4d46e3cedb234391282966 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Thu, 5 Dec 2024 14:41:13 +0900 Subject: [PATCH 577/648] =?UTF-8?q?rename:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20API=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=9D=B4=EB=A6=84=EC=97=90?= =?UTF-8?q?=20=EB=A7=9E=EA=B2=8C=20=EB=B3=80=EA=B2=BD(#137)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/_ui/user-info/index.tsx | 2 +- .../my/profile/_ui/user-info/styles.module.scss | 2 +- .../my/profile/_ui/user-profile/index.tsx | 12 ++++++------ .../my/profile/_ui/user-profile/styles.module.scss | 6 +++--- app/(dashboard)/my/profile/page.tsx | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index ae6948c0..f1bb3fb2 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -31,7 +31,7 @@ const UserInfo = ({ isEditable = false }: Props) => { const handleSave = () => {} return ( <div className={cx('container')}> - <p className={cx('text')}>개인 정보</p> + <p className={cx('title')}>개인 정보</p> <div className={cx('line')}></div> <div className={cx('content')}> diff --git a/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss b/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss index bcb7d74b..11c9ae53 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss +++ b/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss @@ -5,7 +5,7 @@ padding: 44px 40px; } -.text { +.title { @include typo-b1; margin-bottom: 22px; color: $color-gray-700; diff --git a/app/(dashboard)/my/profile/_ui/user-profile/index.tsx b/app/(dashboard)/my/profile/_ui/user-profile/index.tsx index 4bfd1e9b..bd4e247e 100644 --- a/app/(dashboard)/my/profile/_ui/user-profile/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-profile/index.tsx @@ -7,20 +7,20 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) interface Props { - userType: string - name: string + role: string + nickname: string email: string } -const UserProfile = ({ userType, name, email }: Props) => { +const UserProfile = ({ role, nickname, email }: Props) => { return ( <div className={cx('container')}> - <p className={cx('profile-info')}>프로필 정보</p> + <p className={cx('title')}>프로필 정보</p> <div className={cx('line')}></div> <div className={cx('content')}> <div className={cx('left-wrapper')}> - <p className={cx('type')}>{userType}</p> - <p className={cx('name')}>{name}</p> + <p className={cx('role')}>{role}</p> + <p className={cx('nickname')}>{nickname}</p> <p className={cx('email')}>{email}</p> </div> <div className={cx('right-wrapper')}> diff --git a/app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss b/app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss index a3aa6eb6..f95f67fe 100644 --- a/app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss +++ b/app/(dashboard)/my/profile/_ui/user-profile/styles.module.scss @@ -5,7 +5,7 @@ padding: 44px 40px; } -.profile-info { +.title { @include typo-b1; color: $color-gray-700; margin-bottom: 22px; @@ -31,12 +31,12 @@ flex: 1; gap: 12px; - .type { + .role { @include typo-b2; color: $color-orange-500; } - .name { + .nickname { @include typo-h4; font-weight: bold; color: $color-gray-800; diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx index 001b6d4f..05801e96 100644 --- a/app/(dashboard)/my/profile/page.tsx +++ b/app/(dashboard)/my/profile/page.tsx @@ -16,7 +16,7 @@ const MyProfilePage = () => { <div className={cx('wrapper')}> <UserInfo /> <div className={cx('user-profile')}> - <UserProfile userType={'트레이더'} name={'고양이'} email={'meow@example.com'} /> + <UserProfile role={'트레이더'} nickname={'고양이'} email={'meow@example.com'} /> <div className={cx('link-button')}> <LinkButton href={PATH.PROFILE_WITHDRAW}>탈퇴하기</LinkButton> </div> From 059d657f7d37b598e4654a7b20e7ce00e3d098bb Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 14:48:32 +0900 Subject: [PATCH 578/648] =?UTF-8?q?feat:=20=EC=8B=A4=EA=B3=84=EC=A2=8C=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=93=B1=EB=A1=9D=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20API=20=EC=B6=94=EA=B0=80=20(#253)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/_api/get-my-account-iamges.ts | 26 ++++++++++ app/(dashboard)/my/_api/post-account-image.ts | 26 ++++++++++ .../_hooks/query/use-get-my-account-image.ts | 12 +++++ .../_hooks/query/use-upload-account-images.ts | 51 +++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 app/(dashboard)/my/_api/get-my-account-iamges.ts create mode 100644 app/(dashboard)/my/_api/post-account-image.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-upload-account-images.ts diff --git a/app/(dashboard)/my/_api/get-my-account-iamges.ts b/app/(dashboard)/my/_api/get-my-account-iamges.ts new file mode 100644 index 00000000..297958f9 --- /dev/null +++ b/app/(dashboard)/my/_api/get-my-account-iamges.ts @@ -0,0 +1,26 @@ +import { ImageDataModel } from '@/app/(dashboard)/_ui/analysis-container/account-content' + +import axiosInstance from '@/shared/api/axios' + +interface ResponseModel { + content: ImageDataModel + first: boolean + last: boolean + page: number + size: number + totalElements: number + totalPages: number +} + +const getMyAccountImages = async ( + strategyId: number +): Promise<ResponseModel | null | undefined> => { + try { + const response = await axiosInstance.get(`/api/my-strategies/${strategyId}/account-images`) + return response.data.result + } catch (err) { + console.error(err) + } +} + +export default getMyAccountImages diff --git a/app/(dashboard)/my/_api/post-account-image.ts b/app/(dashboard)/my/_api/post-account-image.ts new file mode 100644 index 00000000..ba818053 --- /dev/null +++ b/app/(dashboard)/my/_api/post-account-image.ts @@ -0,0 +1,26 @@ +import axiosInstance from 'shared/api/axios' + +interface UploadAccountImagesRequestModel { + fileName: string + fileSize: number + title: string +} + +interface UploadAccountImagesResponseModel { + isSuccess: boolean + message: string + result: { + presignedUrls: { + presignedUrl: string + }[] + } + code: number +} + +export const uploadAccountImages = async ( + strategyId: number, + data: UploadAccountImagesRequestModel[] +): Promise<UploadAccountImagesResponseModel> => { + const response = await axiosInstance.post(`/api/my-strategies/${strategyId}/account-images`, data) + return response.data +} diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts b/app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts new file mode 100644 index 00000000..e46f22ce --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import getMyAccountImages from '../../_api/get-my-account-iamges' + +const useGetAccountImages = (strategyId: number) => { + return useQuery({ + queryKey: ['myAccountImages', strategyId], + queryFn: () => getMyAccountImages(strategyId), + }) +} + +export default useGetAccountImages diff --git a/app/(dashboard)/my/_hooks/query/use-upload-account-images.ts b/app/(dashboard)/my/_hooks/query/use-upload-account-images.ts new file mode 100644 index 00000000..aa7d3a76 --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-upload-account-images.ts @@ -0,0 +1,51 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { uploadAccountImages } from '../../_api/post-account-image' + +interface UseUploadAccountImagesProps { + strategyId: number + onSuccess?: () => void + onError?: (error: unknown) => void +} + +export const useUploadAccountImages = ({ + strategyId, + onSuccess, + onError, +}: UseUploadAccountImagesProps) => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: async (files: { title: string; imageFile: File }[]) => { + const uploadData = files.map(({ title, imageFile }) => ({ + fileName: imageFile.name, + fileSize: imageFile.size, + title, + })) + const response = await uploadAccountImages(strategyId, uploadData) + + const { presignedUrls } = response.result + const uploadPromises = files.map(({ imageFile }, index) => { + return fetch(presignedUrls[index].presignedUrl, { + method: 'PUT', + body: imageFile, + headers: { + 'Content-Type': imageFile.type, + }, + }) + }) + + await Promise.all(uploadPromises) + return response + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ['myAccountImages', strategyId], + exact: true, + }) + + onSuccess?.() + }, + onError, + }) +} From aed2077d58da2937cc54d322dfe95d84c918a0b9 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 14:51:18 +0900 Subject: [PATCH 579/648] =?UTF-8?q?feat:=20=EC=8B=A4=EA=B3=84=EC=A2=8C=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#253)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/account-content.tsx | 98 +++++-- .../_ui/analysis-container/styles.module.scss | 8 +- .../ui/modal/account-register-modal/index.tsx | 262 ++++++++++++++++++ .../account-register-modal/styles.module.scss | 98 +++++++ 4 files changed, 449 insertions(+), 17 deletions(-) create mode 100644 shared/ui/modal/account-register-modal/index.tsx create mode 100644 shared/ui/modal/account-register-modal/styles.module.scss diff --git a/app/(dashboard)/_ui/analysis-container/account-content.tsx b/app/(dashboard)/_ui/analysis-container/account-content.tsx index 68443546..e7e72dd0 100644 --- a/app/(dashboard)/_ui/analysis-container/account-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/account-content.tsx @@ -9,11 +9,13 @@ import classNames from 'classnames/bind' import { ACCOUNT_PAGE_COUNT } from '@/shared/constants/count-per-page' import useModal from '@/shared/hooks/custom/use-modal' import { Button } from '@/shared/ui/button' +import Checkbox from '@/shared/ui/check-box' import AccountImageModal from '@/shared/ui/modal/account-image-modal' +import AccountRegisterModal from '@/shared/ui/modal/account-register-modal' import Pagination from '@/shared/ui/pagination' import sliceArray from '@/shared/utils/slice-array' -import useGetAccountImages from '../../strategies/[strategyId]/_hooks/query/use-get-account-images' +import useGetAccountImages from '../../my/_hooks/query/use-get-my-account-image' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -33,12 +35,44 @@ interface Props { const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = false }: Props) => { const [selectedImage, setSelectedImage] = useState<ImageDataModel | null>(null) - const { isModalOpen, openModal, closeModal } = useModal() - const { data, isLoading } = useGetAccountImages(strategyId) + const [selectedImages, setSelectedImages] = useState<number[]>([]) - const handleOpenModal = (image: ImageDataModel) => { + const { + isModalOpen: isViewModalOpen, + openModal: openViewModal, + closeModal: closeViewModal, + } = useModal() + + const { + isModalOpen: isUploadModalOpen, + openModal: openUploadModal, + closeModal: closeUploadModal, + } = useModal() + + const viewImagesQuery = useGetAccountImages(strategyId) + + const editImagesQuery = useGetAccountImages(strategyId) + + const { data, isLoading } = isEditable ? editImagesQuery : viewImagesQuery + + const handleOpenViewModal = (image: ImageDataModel) => { setSelectedImage(image) - openModal() + openViewModal() + } + + const handleImageSelect = (id: number, checked: boolean) => { + setSelectedImages((prev) => { + if (!checked) { + return prev.filter((imageId) => imageId !== id) + } + return [...prev, id] + }) + } + + const handleDeleteSelected = async () => { + if (selectedImages.length === 0) return + // 삭제 로직 구현해야 됨 + console.log('Deleting images:', selectedImages) } if (!data || !Array.isArray(data.content) || isLoading) return null @@ -56,10 +90,21 @@ const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = fa <div className={cx('table-wrapper')}> {isEditable && ( <div className={cx('edit-button-container')}> - <Button size="small" className={cx('edit-button')} variant="filled"> + <Button + size="small" + className={cx('edit-button')} + variant="filled" + onClick={openUploadModal} + > 실계좌 이미지 업로드 </Button> - <Button size="small" className={cx('delete-button')} variant="outline"> + <Button + size="small" + className={cx('delete-button')} + variant="outline" + onClick={handleDeleteSelected} + disabled={selectedImages.length === 0} + > 선택 삭제 </Button> </div> @@ -68,15 +113,27 @@ const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = fa <> <div className={cx('account-images-container', isTwoLines && 'line')}> {croppedImagesData?.map((imageData: ImageDataModel) => ( - <div - key={imageData.imageUrl} - className={cx('image-data')} - onClick={() => handleOpenModal(imageData)} - > - <div className={cx('image')}> + <div key={imageData.imageUrl} className={cx('image-data')}> + <div + className={cx('image', { + 'image-selected': selectedImages.includes(imageData.id), + })} + onClick={() => handleOpenViewModal(imageData)} + > <Image src={imageData.imageUrl} alt={imageData.title} fill sizes="100%" /> </div> - <span>{imageData.title}</span> + <div className={cx('title-wrapper')}> + {isEditable && ( + <Checkbox + isChecked={selectedImages.includes(imageData.id)} + onChange={(checked) => handleImageSelect(imageData.id, checked)} + label={imageData.title} + textSize="c1" + textColor="gray600" + /> + )} + {!isEditable && <span className={cx('image-title')}>{imageData.title}</span>} + </div> </div> ))} </div> @@ -93,14 +150,23 @@ const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = fa <p>업데이트 된 실거래계좌 이미지가 없습니다.</p> </div> )} + {selectedImage && ( <AccountImageModal - isOpen={isModalOpen} + isOpen={isViewModalOpen} url={selectedImage.imageUrl} title={selectedImage.title} - onClose={closeModal} + onClose={closeViewModal} /> )} + + <AccountRegisterModal + isOpen={isUploadModalOpen} + onClose={closeUploadModal} + strategyId={strategyId} + uploadType="direct" + message="실계좌 등록" + /> </div> ) } diff --git a/app/(dashboard)/_ui/analysis-container/styles.module.scss b/app/(dashboard)/_ui/analysis-container/styles.module.scss index 284f56a7..75529604 100644 --- a/app/(dashboard)/_ui/analysis-container/styles.module.scss +++ b/app/(dashboard)/_ui/analysis-container/styles.module.scss @@ -32,7 +32,7 @@ .edit-button-container { display: flex; justify-content: space-between; - + margin-bottom: 30px; .edit-button, .delete-button { padding: 7px 18px; @@ -75,6 +75,12 @@ @include typo-c1; } } + .title-wrapper { + display: flex; + justify-content: center; + align-items: center; + gap: 4px; + } } .no-data { diff --git a/shared/ui/modal/account-register-modal/index.tsx b/shared/ui/modal/account-register-modal/index.tsx new file mode 100644 index 00000000..32c09078 --- /dev/null +++ b/shared/ui/modal/account-register-modal/index.tsx @@ -0,0 +1,262 @@ +import { ChangeEvent, useEffect, useRef, useState } from 'react' + +import { useUploadAccountImages } from '@/app/(dashboard)/my/_hooks/query/use-upload-account-images' +import { RegisterIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Modal from '@/shared/ui/modal' + +import { Button } from '../../button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface AnalysisUploadModalProps { + isOpen: boolean + onClose: () => void + strategyId: number + uploadType: 'excel' | 'direct' + message: string +} + +interface UploadEntryModel { + title: string + imageFile: File | null + imageFileName: string + isNewInput?: boolean +} + +const AccountRegisterModal = ({ + isOpen, + onClose, + strategyId, + message, +}: AnalysisUploadModalProps) => { + const [entries, setEntries] = useState<UploadEntryModel[]>([ + { + title: '', + imageFile: null, + imageFileName: '', + isNewInput: true, + }, + ]) + const [error, setError] = useState<string>('') + const fileInputRefs = useRef<Array<HTMLInputElement | null>>([]) + + const resetModal = () => { + setEntries([ + { + title: '', + imageFile: null, + imageFileName: '', + isNewInput: true, + }, + ]) + setError('') + } + + const handleClose = () => { + resetModal() + onClose() + } + + const { mutate: uploadImages, isPending } = useUploadAccountImages({ + strategyId, + onSuccess: () => { + handleClose() + }, + onError: () => { + setError('이미지 업로드 중 오류가 발생했습니다.') + }, + }) + + useEffect(() => { + fileInputRefs.current = fileInputRefs.current.slice(0, entries.length) + }, [entries.length]) + + const handleTitleChange = (index: number, value: string) => { + const entry = entries[index] + if (entry.isNewInput) { + const newEntries = [...entries] + newEntries[index].title = value + setEntries(newEntries) + } + } + + const handleFileChange = (index: number, event: ChangeEvent<HTMLInputElement>) => { + const entry = entries[index] + if (!entry.isNewInput) return + + const file = event.target.files?.[0] + if (file) { + if (!file.type.startsWith('image/')) { + setError('이미지 파일만 업로드 가능합니다.') + return + } + const newEntries = [...entries] + const currentEntry = newEntries[index] + + currentEntry.imageFile = file + currentEntry.imageFileName = file.name + setEntries(newEntries) + setError('') + } + } + + const handleFileClick = (index: number) => { + const entry = entries[index] + if (entry.isNewInput) { + fileInputRefs.current[index]?.click() + } + } + + const handleAddEntry = () => { + const currentNewEntry = entries.find((entry) => entry.isNewInput) + if (!currentNewEntry?.imageFile) { + setError('새 이미지를 먼저 업로드해 주세요.') + return + } + if (!currentNewEntry.title.trim()) { + setError('이미지 제목을 입력해 주세요.') + return + } + + if (entries.length < 5) { + setEntries([ + { + title: '', + imageFile: null, + imageFileName: '', + isNewInput: true, + }, + ...entries.map((entry) => ({ + ...entry, + isNewInput: false, + })), + ]) + setError('') + } + } + + const removeEntry = (index: number) => { + const newEntries = entries.filter((_, i) => i !== index) + setEntries(newEntries) + } + + const handleSubmit = async () => { + const nonEmptyEntries = entries.filter((entry) => !entry.isNewInput) + if (nonEmptyEntries.length === 0) { + setError('최소 1개 이상의 이미지를 업로드해야 합니다.') + return + } + + const uploadData = nonEmptyEntries.map(({ title, imageFile }) => { + if (!imageFile) throw new Error('이미지 파일이 없습니다.') + return { title, imageFile } + }) + + uploadImages(uploadData) + } + + const sortedEntries = [ + ...entries.filter((entry) => entry.isNewInput), + ...entries.filter((entry) => !entry.isNewInput), + ] + + return ( + <Modal isOpen={isOpen} size="big" icon={RegisterIcon} message={message}> + <div className={cx('container')}> + <div className={cx('upload-container')}> + {sortedEntries.map((entry, index) => ( + <div key={index} className={cx('form-grid')}> + {entry.isNewInput ? ( + <> + <div className={cx('input-group')}> + <label className={cx('label')}>제목</label> + <input + className={cx('data-input')} + value={entry.title} + onChange={(e) => handleTitleChange(index, e.target.value)} + placeholder="제목을 입력해주세요" + /> + </div> + + <div className={cx('input-group')}> + <label className={cx('label')}>이미지</label> + <div className={cx('file-input-wrapper')}> + <input + type="file" + ref={(el: HTMLInputElement | null) => { + if (fileInputRefs.current) { + fileInputRefs.current[index] = el + } + }} + className={cx('hidden-input')} + accept="image/*" + onChange={(e) => handleFileChange(index, e)} + /> + <div className={cx('custom-input')} onClick={() => handleFileClick(index)}> + <span className={cx('placeholder')}> + {entry.imageFileName || '이미지 찾아보기'} + </span> + </div> + </div> + </div> + + <div className={cx('input-actions')}> + {entries.length < 5 && ( + <Button + className={cx('add-button')} + variant="filled" + onClick={handleAddEntry} + > + 추가 + </Button> + )} + </div> + </> + ) : ( + <> + <div className={cx('input-group-added')}> + <input className={cx('data-input')} value={entry.title} disabled /> + </div> + + <div className={cx('input-group-added')}> + <div className={cx('file-input-wrapper')}> + <div className={cx('custom-input')}> + <span className={cx('placeholder')}>{entry.imageFileName}</span> + </div> + </div> + </div> + + <div className={cx('input-actions')}> + <Button + variant="outline" + className={cx('delete-button')} + onClick={() => removeEntry(index)} + > + 삭제 + </Button> + </div> + </> + )} + </div> + ))} + + {error && <p className={cx('error-message')}>{error}</p>} + + <div className={cx('button-group')}> + <Button variant="outline" onClick={handleClose} disabled={isPending}> + 취소 + </Button> + <Button variant="filled" onClick={handleSubmit} disabled={isPending}> + 등록 + </Button> + </div> + </div> + </div> + </Modal> + ) +} + +export default AccountRegisterModal diff --git a/shared/ui/modal/account-register-modal/styles.module.scss b/shared/ui/modal/account-register-modal/styles.module.scss new file mode 100644 index 00000000..c47acdea --- /dev/null +++ b/shared/ui/modal/account-register-modal/styles.module.scss @@ -0,0 +1,98 @@ +.container { + width: 100%; + padding: 40px 40px 0 40px; +} + +.upload-container { + display: flex; + flex-direction: column; + gap: 16px; +} + +.form-grid { + display: grid; + grid-template-columns: 200px 300px auto; + gap: 24px; + align-items: flex-start; +} + +.input-group { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; +} + +.label { + @include typo-b3; + color: $color-gray-800; +} + +.data-input { + width: 100%; + padding: 7px 12px; + border-radius: 4px; + border: 1px solid $color-gray-200; + color: $color-gray-400; + @include typo-c1; + height: 40px; + &::placeholder { + color: $color-gray-400; + @include typo-c1; + } +} + +.file-input-wrapper { + position: relative; + width: 100%; +} + +.hidden-input { + display: none; +} + +.custom-input { + display: flex; + align-items: center; + padding: 7px 12px; + border: 1px solid $color-gray-200; + border-radius: 4px; + cursor: pointer; + height: 40px; +} + +.placeholder { + color: $color-gray-400; + @include typo-b3; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.input-actions { + display: flex; + align-items: flex-end; + gap: 8px; + height: 100%; + margin-left: 16px; +} + +.add-button, +.delete-button { + height: 32px; + padding: 0 12px; + font-size: 12px !important; +} + +.button-group { + display: flex; + justify-content: center; + gap: 36px; + margin-top: 40px; +} + +.error-message { + color: $color-orange-500; + font-size: 14px; + margin-top: 8px; +} From 63cbac9f6e4cfe2e028eb4911de04ccc9236ceec Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 15:04:51 +0900 Subject: [PATCH 580/648] =?UTF-8?q?design:=20cursor=20pointer=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#253)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/analysis-container/styles.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/(dashboard)/_ui/analysis-container/styles.module.scss b/app/(dashboard)/_ui/analysis-container/styles.module.scss index 75529604..fb0f12f4 100644 --- a/app/(dashboard)/_ui/analysis-container/styles.module.scss +++ b/app/(dashboard)/_ui/analysis-container/styles.module.scss @@ -68,6 +68,7 @@ width: 100%; height: 100px; border-radius: 8px; + cursor: pointer; overflow: hidden; } span { From a2fe05420d184654ca5ee1d1494c74640ac50e05 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 15:05:35 +0900 Subject: [PATCH 581/648] =?UTF-8?q?fix:=20=EC=BF=BC=EB=A6=AC=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?(#253)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/analysis-container/account-content.tsx | 5 +++-- app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/account-content.tsx b/app/(dashboard)/_ui/analysis-container/account-content.tsx index e7e72dd0..b146c947 100644 --- a/app/(dashboard)/_ui/analysis-container/account-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/account-content.tsx @@ -15,7 +15,8 @@ import AccountRegisterModal from '@/shared/ui/modal/account-register-modal' import Pagination from '@/shared/ui/pagination' import sliceArray from '@/shared/utils/slice-array' -import useGetAccountImages from '../../my/_hooks/query/use-get-my-account-image' +import useGetMyAccountImages from '../../my/_hooks/query/use-get-my-account-image' +import useGetAccountImages from '../../strategies/[strategyId]/_hooks/query/use-get-account-images' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -51,7 +52,7 @@ const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = fa const viewImagesQuery = useGetAccountImages(strategyId) - const editImagesQuery = useGetAccountImages(strategyId) + const editImagesQuery = useGetMyAccountImages(strategyId) const { data, isLoading } = isEditable ? editImagesQuery : viewImagesQuery diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts b/app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts index e46f22ce..7b9d32bc 100644 --- a/app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts +++ b/app/(dashboard)/my/_hooks/query/use-get-my-account-image.ts @@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query' import getMyAccountImages from '../../_api/get-my-account-iamges' -const useGetAccountImages = (strategyId: number) => { +const useGetMyAccountImages = (strategyId: number) => { return useQuery({ queryKey: ['myAccountImages', strategyId], queryFn: () => getMyAccountImages(strategyId), }) } -export default useGetAccountImages +export default useGetMyAccountImages From 465e3e3bd884515f065e07a5242b367a22e9ff86 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 15:06:37 +0900 Subject: [PATCH 582/648] =?UTF-8?q?fix:=20=EC=A0=84=EB=9E=B5=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=95=84=EC=9D=B4=EC=BD=98=20hidden,=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=EC=83=81=EC=84=B8=20=EC=95=84=EC=9D=B4=EC=BD=98=202li?= =?UTF-8?q?ne=20=EC=B2=98=EB=A6=AC=20(#255)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-information/strategy-name-box.tsx | 2 +- app/(dashboard)/_ui/strategies-item/strategies-icon.tsx | 5 +++-- app/(dashboard)/_ui/strategies-item/styles.module.scss | 7 ++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/(dashboard)/_ui/details-information/strategy-name-box.tsx b/app/(dashboard)/_ui/details-information/strategy-name-box.tsx index 18cbd70b..ff29ebe5 100644 --- a/app/(dashboard)/_ui/details-information/strategy-name-box.tsx +++ b/app/(dashboard)/_ui/details-information/strategy-name-box.tsx @@ -24,7 +24,7 @@ const StrategyNameBox = ({ strategyId, iconUrls, iconNames, name }: Props) => { return ( <div className={cx('name-container')}> - <StrategiesIcon iconUrls={iconUrls} iconNames={iconNames} /> + <StrategiesIcon iconUrls={iconUrls} iconNames={iconNames} isDetailsPage={true} /> <p className={cx('name')}>{name}</p> <button onClick={handleDownload}>제안서 다운로드</button> </div> diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 295eb72f..ce39df2a 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -21,9 +21,10 @@ interface ImageSizeModel { interface Props { iconUrls?: string[] iconNames?: string[] + isDetailsPage?: boolean } -const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { +const StrategiesIcon = ({ iconUrls, iconNames, isDetailsPage = false }: Props) => { const [imageSizes, setImageSizes] = useState<{ [key: string]: ImageSizeModel }>({}) const [validImages, setValidImages] = useState<{ [key: string]: boolean }>({}) @@ -66,7 +67,7 @@ const StrategiesIcon = ({ iconUrls, iconNames }: Props) => { if (iconUrls?.length !== iconNames?.length) return null return ( - <div className={cx('icon-container')}> + <div className={cx('icon-container', { details: isDetailsPage })}> {iconUrls?.map((url, idx) => { const name = iconNames?.[idx] if (!url || !name || validImages[url] === false) return null diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index d0131844..0eba9dd1 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -90,7 +90,7 @@ .icon-container { display: flex; align-items: center; - gap: 4px; + column-gap: 4px; .icon-wrapper { .icon { position: relative; @@ -107,4 +107,9 @@ } } } + &.details { + flex-wrap: wrap; + max-height: 50px; + row-gap: 1px; + } } From 77f87f2119118c11fd12347b0fc5caa73bbe744e Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 15:07:26 +0900 Subject: [PATCH 583/648] =?UTF-8?q?design:=20=EB=9D=BC=EC=9D=B8=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20=EC=83=89=EC=83=81=20=EC=88=98=EC=A0=95=20(#255)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/strategies-item/area-chart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/area-chart.tsx b/app/(dashboard)/_ui/strategies-item/area-chart.tsx index 6de0b814..e03e012e 100644 --- a/app/(dashboard)/_ui/strategies-item/area-chart.tsx +++ b/app/(dashboard)/_ui/strategies-item/area-chart.tsx @@ -41,7 +41,7 @@ const AreaChart = ({ profitRateChartData: data }: Props) => { plotOptions: { areaspline: { lineWidth: 1, - lineColor: '#4d4d4d', + lineColor: '#ff8f70', marker: { enabled: false, symbol: 'circle', @@ -64,7 +64,7 @@ const AreaChart = ({ profitRateChartData: data }: Props) => { color: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ - [0, '#4d4d4d'], + [0, '#ff8f70'], [1, 'rgba(255, 255, 255, 0)'], ], }, From e611a5f930e34893447640a0360e85dd3426e755 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 15:34:02 +0900 Subject: [PATCH 584/648] =?UTF-8?q?hotfix:=20=EC=A0=84=EB=9E=B5=20?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20api=20=EC=BF=BC=EB=A6=AC=ED=82=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#262)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/[strategyId]/_hooks/query/use-get-analysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts index 24e53447..6327415c 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts @@ -5,7 +5,7 @@ import getAnalysis from '../../_api/get-analysis' const useGetAnalysis = (strategyId: number, type: AnalysisTabType, page: number, size: number) => { return useQuery({ - queryKey: ['analysis', strategyId], + queryKey: ['analysis', strategyId, type], queryFn: () => getAnalysis(strategyId, type, page, size), }) } From 08f4b8f440fc5fc74b1ac17a4fb25bc49e3f53b4 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Thu, 5 Dec 2024 15:58:44 +0900 Subject: [PATCH 585/648] =?UTF-8?q?feat:=20input=20field=EB=A5=BC=20admin/?= =?UTF-8?q?shared=EB=A1=9C=20=EC=98=AE=EA=B9=80=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/{notices/post => }/_ui/input-field/index.tsx | 0 .../{notices/post => }/_ui/input-field/styles.module.scss | 3 --- app/admin/notices/post/page.tsx | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) rename app/admin/{notices/post => }/_ui/input-field/index.tsx (100%) rename app/admin/{notices/post => }/_ui/input-field/styles.module.scss (62%) diff --git a/app/admin/notices/post/_ui/input-field/index.tsx b/app/admin/_ui/input-field/index.tsx similarity index 100% rename from app/admin/notices/post/_ui/input-field/index.tsx rename to app/admin/_ui/input-field/index.tsx diff --git a/app/admin/notices/post/_ui/input-field/styles.module.scss b/app/admin/_ui/input-field/styles.module.scss similarity index 62% rename from app/admin/notices/post/_ui/input-field/styles.module.scss rename to app/admin/_ui/input-field/styles.module.scss index 38b9c9fd..8b1a5cf1 100644 --- a/app/admin/notices/post/_ui/input-field/styles.module.scss +++ b/app/admin/_ui/input-field/styles.module.scss @@ -11,6 +11,3 @@ color: $color-gray-800; width: 640px; } - -// TODO : textarea palceholder 중앙에 오도록하기 -// TODO : file input UI 고치기 diff --git a/app/admin/notices/post/page.tsx b/app/admin/notices/post/page.tsx index a37a1b7b..e2fd3991 100644 --- a/app/admin/notices/post/page.tsx +++ b/app/admin/notices/post/page.tsx @@ -8,10 +8,10 @@ import { Input } from '@/shared/ui/input' import { Textarea } from '@/shared/ui/textarea' import Title from '@/shared/ui/title' +import InputField from '../../_ui/input-field' import useNoticeForm from './_hooks/use-notice-form' import usePostNotice from './_hooks/use-notice-post' import FileInput from './_ui/file-input' -import InputField from './_ui/input-field' import styles from './page.module.scss' const cx = classNames.bind(styles) From bb84efce1fdb5845da1fb9c84d688524a9ba35cc Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 16:25:45 +0900 Subject: [PATCH 586/648] =?UTF-8?q?feat:=20=EB=8B=B5=EB=B3=80=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20api=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20(#248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-container/index.tsx | 66 +++++++++++++++---- .../my/questions/_api/post-answer.ts | 15 +++++ .../questions/_hooks/query/use-post-answer.ts | 17 +++++ shared/types/questions.ts | 1 + 4 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 app/(dashboard)/my/questions/_api/post-answer.ts create mode 100644 app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index e853c78b..4092c048 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { useRef, useState } from 'react' import { useParams } from 'next/navigation' @@ -8,9 +8,11 @@ import classNames from 'classnames/bind' import { useAuthStore } from '@/shared/stores/use-auth-store' import { Button } from '@/shared/ui/button' +import { ErrorMessage } from '@/shared/ui/error-message' import { Textarea } from '@/shared/ui/textarea' import useGetQuestionDetails from '../../../_hooks/query/use-get-question-details' +import usePostAnswer from '../../../_hooks/query/use-post-answer' import QuestionDetailCard from '../question-detail-card' import styles from './styles.module.scss' @@ -18,21 +20,54 @@ const cx = classNames.bind(styles) const QuestionContainer = () => { const [isActiveAnswer, setIsActiveAnswer] = useState(false) + const [answerErrorMessage, setAnswerErrorMessage] = useState<string | null>(null) const { questionId } = useParams() - const user = useAuthStore((state) => state.user) - const isTrader = user?.role.includes('TRADER') || false + const textareaRef = useRef<HTMLTextAreaElement | null>(null) + const { mutate: submitAnswer } = usePostAnswer(parseInt(questionId as string)) const { data: questionDetails } = useGetQuestionDetails({ questionId: parseInt(questionId as string), }) + const user = useAuthStore((state) => state.user) + + if (!user) { + return null + } + + const isTrader = user.role.includes('TRADER') + const isInvestor = user.role.includes('INVESTOR') + const handleQuestionAdd = () => {} const handleAnswerAdd = () => { setIsActiveAnswer((prevState) => !prevState) } + const handleAnswerSubmit = () => { + const content = textareaRef.current?.value + + if (!content) { + setAnswerErrorMessage('답변을 입력해주세요.') + return + } + + setAnswerErrorMessage(null) + + submitAnswer(content, { + onSuccess: () => { + if (textareaRef.current) { + textareaRef.current.value = '' + } + setIsActiveAnswer(false) + }, + onError: () => { + setAnswerErrorMessage('답변 등록에 실패했습니다.') + }, + }) + } + if (!questionDetails) { return } @@ -41,7 +76,7 @@ const QuestionContainer = () => { <> <div className={cx('container')}> <QuestionDetailCard - isAuthor={!isTrader} + isAuthor={isInvestor} strategyName={questionDetails.strategyName} title={questionDetails.title} contents={questionDetails.questionContent} @@ -64,18 +99,23 @@ const QuestionContainer = () => { <div className={cx('answer-input-wrapper')}> <div className={cx('title-wrapper')}> <h2 className={cx('title')}>답변</h2> - <Button size="small">등록하기</Button> + <Button size="small" onClick={handleAnswerSubmit}> + 등록하기 + </Button> </div> - <Textarea placeholder="내용을 입력하세요." /> + <Textarea placeholder="내용을 입력하세요." ref={textareaRef} /> + <ErrorMessage errorMessage={answerErrorMessage} /> </div> ) : ( - <Button - variant="filled" - className={cx('button')} - onClick={isTrader ? handleAnswerAdd : handleQuestionAdd} - > - {isTrader ? '답변하기' : '추가 질문하기'} - </Button> + ((isTrader && !questionDetails.answer) || isInvestor) && ( + <Button + variant="filled" + className={cx('button')} + onClick={isTrader ? handleAnswerAdd : handleQuestionAdd} + > + {isTrader ? '답변하기' : '추가 질문하기'} + </Button> + ) )} </div> </> diff --git a/app/(dashboard)/my/questions/_api/post-answer.ts b/app/(dashboard)/my/questions/_api/post-answer.ts new file mode 100644 index 00000000..ab6af76f --- /dev/null +++ b/app/(dashboard)/my/questions/_api/post-answer.ts @@ -0,0 +1,15 @@ +import axiosInstance from '@/shared/api/axios' + +const postAnswer = async (questionId: number, content: string) => { + try { + const response = await axiosInstance.post(`/api/trader/questions/${questionId}/answers`, { + content, + }) + return response.data.isSuccess + } catch (err) { + console.error(err) + throw new Error('답변 등록에 실패했습니다.') + } +} + +export default postAnswer diff --git a/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts b/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts new file mode 100644 index 00000000..249076a5 --- /dev/null +++ b/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts @@ -0,0 +1,17 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import postAnswer from '../../_api/post-answer' + +const usePostAnswer = (questionId: number) => { + const QueryClient = useQueryClient() + return useMutation({ + mutationFn: (content: string) => postAnswer(questionId, content), + onSuccess: () => { + QueryClient.invalidateQueries({ + queryKey: ['questionDetails', questionId], + }) + }, + }) +} + +export default usePostAnswer diff --git a/shared/types/questions.ts b/shared/types/questions.ts index 7518ca09..53cc7535 100644 --- a/shared/types/questions.ts +++ b/shared/types/questions.ts @@ -37,6 +37,7 @@ export interface QuestionDetailsModel { questionId: number title: string questionContent: string + answerContent: string strategyName: string nickname: string role: UserType From 6c0a4e0ba7b9d92521152084cbcac234b0bbfdf7 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 16:32:50 +0900 Subject: [PATCH 587/648] =?UTF-8?q?feat:=20=EB=82=98=EC=9D=98=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=EC=9D=BC=EB=95=8C=20=EA=B5=AC=EB=8F=85=20=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EA=B3=A0=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=A0=81=EC=9A=A9=20(#264)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/strategies-item/index.tsx | 6 ++- .../_ui/strategies-item/subscribe.tsx | 43 +++++++++++++------ shared/hooks/custom/use-modal.ts | 7 ++- shared/ui/modal/subscribe-warning-modal.tsx | 26 +++++++++++ 4 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 shared/ui/modal/subscribe-warning-modal.tsx diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 675200b8..99cc5750 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -49,7 +49,11 @@ const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { </div> {type === 'default' && ( <div className={cx('subscribe')}> - <Subscribe subscriptionStatus={data.isSubscribed} strategyId={data.strategyId} /> + <Subscribe + subscriptionStatus={data.isSubscribed} + strategyId={data.strategyId} + traderName={data.nickname} + /> </div> )} {type === 'my' && ( diff --git a/app/(dashboard)/_ui/strategies-item/subscribe.tsx b/app/(dashboard)/_ui/strategies-item/subscribe.tsx index e580b704..d4ca9162 100644 --- a/app/(dashboard)/_ui/strategies-item/subscribe.tsx +++ b/app/(dashboard)/_ui/strategies-item/subscribe.tsx @@ -11,6 +11,7 @@ import { PATH } from '@/shared/constants/path' import useModal from '@/shared/hooks/custom/use-modal' import { useAuthStore } from '@/shared/stores/use-auth-store' import SigninCheckModal from '@/shared/ui/modal/signin-check-modal' +import SubscribeWarningModal from '@/shared/ui/modal/subscribe-warning-modal' import useGetSubscribe from '../../strategies/_hooks/query/use-get-subscribe' import styles from './styles.module.scss' @@ -20,13 +21,23 @@ const cx = classNames.bind(styles) interface Props { strategyId: number subscriptionStatus: boolean + traderName: string } -const Subscribe = ({ strategyId, subscriptionStatus }: Props) => { +const Subscribe = ({ strategyId, subscriptionStatus, traderName }: Props) => { const [isSubscribed, setIsSubscribed] = useState(false) const router = useRouter() const user = useAuthStore((state) => state.user) - const { isModalOpen, openModal, closeModal } = useModal() + const { + isModalOpen: isSigninCheckModalOpen, + openModal: openSigninCheckModal, + closeModal: closeSigninCheckModal, + } = useModal() + const { + isModalOpen: isSubscribeWarningModal, + openModal: openSubscribeWarningModal, + closeModal: closeSubscribeWarningModal, + } = useModal() const { mutate } = useGetSubscribe() useEffect(() => { @@ -38,11 +49,14 @@ const Subscribe = ({ strategyId, subscriptionStatus }: Props) => { const handleSubscribe = (e: React.MouseEvent) => { e.preventDefault() if (!user) { - openModal() + openSigninCheckModal() + } else if (user?.nickname === traderName) { + openSubscribeWarningModal() + } else { + mutate(strategyId, { + onSuccess: () => setIsSubscribed(!isSubscribed), + }) } - mutate(strategyId, { - onSuccess: () => setIsSubscribed(!isSubscribed), - }) } const handleRoute = () => router.push(PATH.SIGN_IN) @@ -54,13 +68,16 @@ const Subscribe = ({ strategyId, subscriptionStatus }: Props) => { {isSubscribed ? <BookmarkIcon /> : <BookmarkOutlineIcon />} </button> </div> - {isModalOpen && ( - <SigninCheckModal - isModalOpen={isModalOpen} - onCloseModal={closeModal} - onChange={handleRoute} - /> - )} + + <SigninCheckModal + isModalOpen={isSigninCheckModalOpen} + onCloseModal={closeSigninCheckModal} + onChange={handleRoute} + /> + <SubscribeWarningModal + isModalOpen={isSubscribeWarningModal} + onCloseModal={closeSubscribeWarningModal} + /> </> ) } diff --git a/shared/hooks/custom/use-modal.ts b/shared/hooks/custom/use-modal.ts index 643f7e63..20c0047d 100644 --- a/shared/hooks/custom/use-modal.ts +++ b/shared/hooks/custom/use-modal.ts @@ -6,7 +6,12 @@ const useModal = () => { const [isModalOpen, setIsModalOpen] = useState(false) const openModal = () => setIsModalOpen(true) - const closeModal = () => setIsModalOpen(false) + const closeModal = (e?: React.MouseEvent) => { + if (e) { + e.preventDefault() + } + setIsModalOpen(false) + } return { isModalOpen, diff --git a/shared/ui/modal/subscribe-warning-modal.tsx b/shared/ui/modal/subscribe-warning-modal.tsx new file mode 100644 index 00000000..82a98c06 --- /dev/null +++ b/shared/ui/modal/subscribe-warning-modal.tsx @@ -0,0 +1,26 @@ +import React from 'react' + +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import Modal from '.' +import { Button } from '../button' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isModalOpen: boolean + onCloseModal: () => void +} + +const SubscribeWarningModal = ({ isModalOpen, onCloseModal }: Props) => { + return ( + <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> + <span className={cx('message')}>나의 전략은 구독 할 수 없습니다.</span> + <Button onClick={onCloseModal}>확인</Button> + </Modal> + ) +} + +export default SubscribeWarningModal From 74da45cdded5fcf899efe99c3eb2e6b19f389088 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 16:50:52 +0900 Subject: [PATCH 588/648] =?UTF-8?q?feat:=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20(#248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[questionId]/_ui/question-container/index.tsx | 4 ++-- shared/types/questions.ts | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 4092c048..86f469d3 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -79,9 +79,9 @@ const QuestionContainer = () => { isAuthor={isInvestor} strategyName={questionDetails.strategyName} title={questionDetails.title} - contents={questionDetails.questionContent} + contents={questionDetails.content} nickname={questionDetails.nickname} - createdAt={questionDetails.questionCreatedAt} + createdAt={questionDetails.createdAt} status={questionDetails.state === 'WAITING' ? '답변 대기' : '답변 완료'} /> {questionDetails.answer ? ( diff --git a/shared/types/questions.ts b/shared/types/questions.ts index 53cc7535..8ff96183 100644 --- a/shared/types/questions.ts +++ b/shared/types/questions.ts @@ -36,14 +36,12 @@ export interface AnswerModel { export interface QuestionDetailsModel { questionId: number title: string - questionContent: string - answerContent: string + content: string strategyName: string - nickname: string - role: UserType profileImageUrl: string + nickname: string state: QuestionStateConditionType - questionCreatedAt: string + createdAt: string answer: AnswerModel | null } From 96173e33e4ed546c467c667b0c6c726784e6ab97 Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Thu, 5 Dec 2024 16:58:51 +0900 Subject: [PATCH 589/648] =?UTF-8?q?feat:=20=EC=8B=A4=EA=B3=84=EC=A2=8C=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AD=EC=A0=9C=20api=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20(#259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/account-content.tsx | 16 +++++++++---- .../_ui/analysis-container/styles.module.scss | 4 ++++ app/(dashboard)/my/_api/post-account-image.ts | 23 +++++++++++++++++++ .../_hooks/query/use-delete-account-images.ts | 21 +++++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 app/(dashboard)/my/_hooks/query/use-delete-account-images.ts diff --git a/app/(dashboard)/_ui/analysis-container/account-content.tsx b/app/(dashboard)/_ui/analysis-container/account-content.tsx index b146c947..5b157a51 100644 --- a/app/(dashboard)/_ui/analysis-container/account-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/account-content.tsx @@ -15,6 +15,7 @@ import AccountRegisterModal from '@/shared/ui/modal/account-register-modal' import Pagination from '@/shared/ui/pagination' import sliceArray from '@/shared/utils/slice-array' +import { useDeleteAccountImages } from '../../my/_hooks/query/use-delete-account-images' import useGetMyAccountImages from '../../my/_hooks/query/use-get-my-account-image' import useGetAccountImages from '../../strategies/[strategyId]/_hooks/query/use-get-account-images' import styles from './styles.module.scss' @@ -51,8 +52,8 @@ const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = fa } = useModal() const viewImagesQuery = useGetAccountImages(strategyId) - const editImagesQuery = useGetMyAccountImages(strategyId) + const deleteImagesMutation = useDeleteAccountImages() const { data, isLoading } = isEditable ? editImagesQuery : viewImagesQuery @@ -72,8 +73,16 @@ const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = fa const handleDeleteSelected = async () => { if (selectedImages.length === 0) return - // 삭제 로직 구현해야 됨 - console.log('Deleting images:', selectedImages) + + try { + await deleteImagesMutation.mutateAsync({ + strategyId, + imageIds: selectedImages, + }) + setSelectedImages([]) + } catch (error) { + console.error('Failed to delete images:', error) + } } if (!data || !Array.isArray(data.content) || isLoading) return null @@ -86,7 +95,6 @@ const AccountContent = ({ strategyId, currentPage, onPageChange, isEditable = fa ) const isTwoLines = (croppedImagesData?.length || 0) > 4 - return ( <div className={cx('table-wrapper')}> {isEditable && ( diff --git a/app/(dashboard)/_ui/analysis-container/styles.module.scss b/app/(dashboard)/_ui/analysis-container/styles.module.scss index fb0f12f4..f9622858 100644 --- a/app/(dashboard)/_ui/analysis-container/styles.module.scss +++ b/app/(dashboard)/_ui/analysis-container/styles.module.scss @@ -37,6 +37,9 @@ .delete-button { padding: 7px 18px; } + .delete-button:disabled { + background-color: transparent; + } } &.analysis { margin-top: 40px; @@ -81,6 +84,7 @@ justify-content: center; align-items: center; gap: 4px; + margin-top: 8px; } } diff --git a/app/(dashboard)/my/_api/post-account-image.ts b/app/(dashboard)/my/_api/post-account-image.ts index ba818053..7e433c37 100644 --- a/app/(dashboard)/my/_api/post-account-image.ts +++ b/app/(dashboard)/my/_api/post-account-image.ts @@ -17,6 +17,17 @@ interface UploadAccountImagesResponseModel { code: number } +interface DeleteAccountImagesRequestModel { + strategyId: number + imageIds: number[] +} + +interface DeleteAccountImagesResponseModel { + isSuccess: boolean + message: string + code: number +} + export const uploadAccountImages = async ( strategyId: number, data: UploadAccountImagesRequestModel[] @@ -24,3 +35,15 @@ export const uploadAccountImages = async ( const response = await axiosInstance.post(`/api/my-strategies/${strategyId}/account-images`, data) return response.data } + +export const deleteAccountImages = async ({ + strategyId, + imageIds, +}: DeleteAccountImagesRequestModel): Promise<DeleteAccountImagesResponseModel> => { + const response = await axiosInstance.post( + `/api/my-strategies/${strategyId}/delete-account-images`, + imageIds + ) + console.log(response.data) + return response.data +} diff --git a/app/(dashboard)/my/_hooks/query/use-delete-account-images.ts b/app/(dashboard)/my/_hooks/query/use-delete-account-images.ts new file mode 100644 index 00000000..bb376905 --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-delete-account-images.ts @@ -0,0 +1,21 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { deleteAccountImages } from '../../_api/post-account-image' + +interface DeleteAccountImagesRequestModel { + strategyId: number + imageIds: number[] +} + +export const useDeleteAccountImages = () => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (request: DeleteAccountImagesRequestModel) => deleteAccountImages(request), + onSuccess: (_, request) => { + queryClient.invalidateQueries({ + queryKey: ['myAccountImages', request.strategyId], + }) + }, + }) +} From f83f6fdd8b7ae1f62238faeb1e9c526587ba3c94 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 17:08:51 +0900 Subject: [PATCH 590/648] =?UTF-8?q?feat:=20=EB=8B=B5=EB=B3=80=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=ED=9B=84=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=96=88=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EC=B5=9C=EC=8B=A0=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=98=EC=98=81=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=ED=82=A4=20=EC=B6=94=EA=B0=80=20(#248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts b/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts index 249076a5..965872bc 100644 --- a/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts +++ b/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts @@ -10,6 +10,10 @@ const usePostAnswer = (questionId: number) => { QueryClient.invalidateQueries({ queryKey: ['questionDetails', questionId], }) + + QueryClient.invalidateQueries({ + queryKey: ['questionList'], + }) }, }) } From 4c3e49293b76a5e92f94a98e68d6ef001a8181a9 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 18:52:35 +0900 Subject: [PATCH 591/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20ui=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9=20(#249)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/skeleton/index.tsx | 29 +++++++++++ .../skeleton/styles.module.scss | 48 +++++++++++++++++++ .../search-bar/search-bar-skeleton/index.tsx | 20 ++++++++ .../search-bar-skeleton/styles.module.scss | 20 ++++++++ .../strategies/_ui/strategy-list/index.tsx | 2 - app/(dashboard)/strategies/loading.tsx | 15 ++++++ app/(dashboard)/strategies/page.module.scss | 19 ++++++-- app/(dashboard)/strategies/page.tsx | 10 +++- 8 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 app/(dashboard)/_ui/strategies-item/skeleton/index.tsx create mode 100644 app/(dashboard)/_ui/strategies-item/skeleton/styles.module.scss create mode 100644 app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/index.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/styles.module.scss create mode 100644 app/(dashboard)/strategies/loading.tsx diff --git a/app/(dashboard)/_ui/strategies-item/skeleton/index.tsx b/app/(dashboard)/_ui/strategies-item/skeleton/index.tsx new file mode 100644 index 00000000..22d8cf89 --- /dev/null +++ b/app/(dashboard)/_ui/strategies-item/skeleton/index.tsx @@ -0,0 +1,29 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const StrategiesItemSkeleton = () => { + return ( + <div className={cx('container')}> + <div className={cx('first')}> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + <div className={cx('second')}> + <div></div> + </div> + <div className={cx('last')}> + <div></div> + </div> + <div className={cx('last')}> + <div></div> + </div> + </div> + ) +} + +export default StrategiesItemSkeleton diff --git a/app/(dashboard)/_ui/strategies-item/skeleton/styles.module.scss b/app/(dashboard)/_ui/strategies-item/skeleton/styles.module.scss new file mode 100644 index 00000000..2b661ffb --- /dev/null +++ b/app/(dashboard)/_ui/strategies-item/skeleton/styles.module.scss @@ -0,0 +1,48 @@ +.container { + @include skeleton; + margin-bottom: 24px; + display: grid; + grid-template-columns: 1.5fr 1.2fr 0.8fr repeat(2, 0.6fr) 0.4fr; + height: 158px; + width: 100%; + align-items: center; + grid-gap: 4px; + div { + width: 100%; + } + .first { + background-color: $color-gray-200; + justify-content: flex-start; + padding-left: 20px; + & * { + height: 18px; + margin-bottom: 10px; + } + :first-child { + width: 50px; + } + :nth-child(2) { + width: 250px; + } + :nth-child(3), + :nth-child(4) { + width: 120px; + } + } + .second { + background-color: $color-gray-200; + place-items: center center; + :first-child { + width: 140px; + height: 115px; + } + } + .last { + background-color: $color-gray-200; + place-items: center center; + :first-child { + width: 100px; + height: 18px; + } + } +} diff --git a/app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/index.tsx new file mode 100644 index 00000000..3acad3f6 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/index.tsx @@ -0,0 +1,20 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const SearchBarSkeleton = () => { + return ( + <> + <div className={cx('top')}></div> + <div className={cx('container')}> + {Array.from({ length: 7 }, (_, idx) => ( + <div key={idx}></div> + ))} + </div> + </> + ) +} + +export default SearchBarSkeleton diff --git a/app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/styles.module.scss b/app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/styles.module.scss new file mode 100644 index 00000000..0754a67c --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/search-bar-skeleton/styles.module.scss @@ -0,0 +1,20 @@ +.top { + @include skeleton; + width: 276px; + height: 66px; + margin-bottom: 10px; +} +.container { + @include skeleton; + width: 276px; + height: 520px; + padding: 15px; + * { + height: 40px; + width: 240px; + margin-bottom: 20px; + } + :first-child { + margin-top: 30px; + } +} diff --git a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx index 70c7a9ea..46d8113d 100644 --- a/app/(dashboard)/strategies/_ui/strategy-list/index.tsx +++ b/app/(dashboard)/strategies/_ui/strategy-list/index.tsx @@ -2,7 +2,6 @@ import { useEffect } from 'react' -import ListHeader from '@/app/(dashboard)/_ui/list-header' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' import classNames from 'classnames/bind' @@ -38,7 +37,6 @@ const StrategyList = () => { return ( <> - <ListHeader /> {strategiesData?.map((strategy) => ( <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> ))} diff --git a/app/(dashboard)/strategies/loading.tsx b/app/(dashboard)/strategies/loading.tsx new file mode 100644 index 00000000..682877d6 --- /dev/null +++ b/app/(dashboard)/strategies/loading.tsx @@ -0,0 +1,15 @@ +import React from 'react' + +import StrategiesItemSkeleton from '../_ui/strategies-item/skeleton' + +const StrategiesLoading = () => { + return ( + <div> + {Array.from({ length: 8 }, (_, idx) => ( + <StrategiesItemSkeleton key={idx} /> + ))} + </div> + ) +} + +export default StrategiesLoading diff --git a/app/(dashboard)/strategies/page.module.scss b/app/(dashboard)/strategies/page.module.scss index 4a762d84..f53b42ce 100644 --- a/app/(dashboard)/strategies/page.module.scss +++ b/app/(dashboard)/strategies/page.module.scss @@ -2,7 +2,20 @@ margin-top: 80px; } -.search-bar { - width: 100%; - background-color: $color-orange-200; +.strategy-layout { + display: flex; + position: relative; + + .strategy { + width: calc(100% - $strategy-sidebar-width); + max-width: $max-width; + padding-right: 10px; + } +} + +.skeleton-side-bar { + width: $strategy-sidebar-width; + position: absolute; + right: 0px; + top: 130px; } diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index eebaff44..b0e36674 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -4,9 +4,12 @@ import classNames from 'classnames/bind' import Title from '@/shared/ui/title' +import ListHeader from '../_ui/list-header' import SearchBarContainer from './_ui/search-bar' +import SearchBarSkeleton from './_ui/search-bar/search-bar-skeleton' import SideContainer from './_ui/side-container' import StrategyList from './_ui/strategy-list' +import StrategiesLoading from './loading' import styles from './page.module.scss' const cx = classNames.bind(styles) @@ -15,11 +18,14 @@ const StrategiesPage = () => { return ( <div className={cx('container')}> <Title label={'전략 랭킹 모음'} /> - <Suspense fallback={<div>Loading...</div>}> + <ListHeader /> + <Suspense fallback={<StrategiesLoading />}> <StrategyList /> </Suspense> <SideContainer> - <SearchBarContainer /> + <Suspense fallback={<SearchBarSkeleton />}> + <SearchBarContainer /> + </Suspense> </SideContainer> </div> ) From ea3875aebc07f5ec6adabfe89d75ef8c5610fc35 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 21:08:27 +0900 Subject: [PATCH 592/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=8B=B5=EB=B3=80=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#271)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-container/index.tsx | 31 +++++++++++++++++- .../my/questions/_api/delete-answer.ts | 20 ++++++++++++ .../_hooks/query/use-delete-answer.ts | 22 +++++++++++++ .../questions/_hooks/query/use-post-answer.ts | 6 ++-- .../_ui/answer-delete-modal/index.tsx | 32 +++++++++++++++++++ .../answer-delete-modal/styles.module.scss | 4 +++ 6 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 app/(dashboard)/my/questions/_api/delete-answer.ts create mode 100644 app/(dashboard)/my/questions/_hooks/query/use-delete-answer.ts create mode 100644 app/(dashboard)/my/questions/_ui/answer-delete-modal/index.tsx create mode 100644 app/(dashboard)/my/questions/_ui/answer-delete-modal/styles.module.scss diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 86f469d3..63405192 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -11,8 +11,10 @@ import { Button } from '@/shared/ui/button' import { ErrorMessage } from '@/shared/ui/error-message' import { Textarea } from '@/shared/ui/textarea' +import useDeleteAnswer from '../../../_hooks/query/use-delete-answer' import useGetQuestionDetails from '../../../_hooks/query/use-get-question-details' import usePostAnswer from '../../../_hooks/query/use-post-answer' +import AnswerDeleteModal from '../../../_ui/answer-delete-modal' import QuestionDetailCard from '../question-detail-card' import styles from './styles.module.scss' @@ -20,19 +22,21 @@ const cx = classNames.bind(styles) const QuestionContainer = () => { const [isActiveAnswer, setIsActiveAnswer] = useState(false) + const [isAnswerDeleteModalOpen, setIsAnswerDeleteModalOpen] = useState(false) const [answerErrorMessage, setAnswerErrorMessage] = useState<string | null>(null) const { questionId } = useParams() const textareaRef = useRef<HTMLTextAreaElement | null>(null) const { mutate: submitAnswer } = usePostAnswer(parseInt(questionId as string)) + const { mutate: deleteAnswer } = useDeleteAnswer() const { data: questionDetails } = useGetQuestionDetails({ questionId: parseInt(questionId as string), }) const user = useAuthStore((state) => state.user) - if (!user) { + if (!user || !questionDetails) { return null } @@ -68,6 +72,25 @@ const QuestionContainer = () => { }) } + const handleDeleteClick = () => { + setIsAnswerDeleteModalOpen(true) + } + + const handleDeleteAnswer = () => { + if (!questionDetails?.answer) return + deleteAnswer( + { + questionId: parseInt(questionId as string), + answerId: questionDetails.answer.answerId, + }, + { + onSuccess: () => { + setIsAnswerDeleteModalOpen(false) + }, + } + ) + } + if (!questionDetails) { return } @@ -91,6 +114,7 @@ const QuestionContainer = () => { contents={questionDetails.answer.content} nickname={questionDetails.answer.nickname} createdAt={questionDetails.answer.createdAt} + onDelete={handleDeleteClick} /> ) : ( <>{!isActiveAnswer && <p className={cx('empty-message')}>아직 답변이 없습니다</p>}</> @@ -118,6 +142,11 @@ const QuestionContainer = () => { ) )} </div> + <AnswerDeleteModal + isModalOpen={isAnswerDeleteModalOpen} + onCloseModal={() => setIsAnswerDeleteModalOpen(false)} + onDelete={handleDeleteAnswer} + /> </> ) } diff --git a/app/(dashboard)/my/questions/_api/delete-answer.ts b/app/(dashboard)/my/questions/_api/delete-answer.ts new file mode 100644 index 00000000..65d82170 --- /dev/null +++ b/app/(dashboard)/my/questions/_api/delete-answer.ts @@ -0,0 +1,20 @@ +import axiosInstance from '@/shared/api/axios' + +export interface DeleteAnswerProps { + questionId: number + answerId: number +} + +const deleteAnswer = async ({ questionId, answerId }: DeleteAnswerProps) => { + try { + const response = await axiosInstance.delete( + `/api/trader/questions/${questionId}/answers/${answerId}` + ) + return response.data.isSuccess + } catch (err) { + console.error(err) + throw new Error('답변 삭제에 실패했습니다.') + } +} + +export default deleteAnswer diff --git a/app/(dashboard)/my/questions/_hooks/query/use-delete-answer.ts b/app/(dashboard)/my/questions/_hooks/query/use-delete-answer.ts new file mode 100644 index 00000000..808cf3ec --- /dev/null +++ b/app/(dashboard)/my/questions/_hooks/query/use-delete-answer.ts @@ -0,0 +1,22 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import deleteAnswer, { DeleteAnswerProps } from '../../_api/delete-answer' + +const useDeleteAnswer = () => { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: ({ questionId, answerId }: DeleteAnswerProps) => + deleteAnswer({ questionId, answerId }), + onSuccess: (_, { questionId }) => { + queryClient.invalidateQueries({ + queryKey: ['questionDetails', questionId], + }) + + queryClient.invalidateQueries({ + queryKey: ['questionList'], + }) + }, + }) +} + +export default useDeleteAnswer diff --git a/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts b/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts index 965872bc..0d3a64af 100644 --- a/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts +++ b/app/(dashboard)/my/questions/_hooks/query/use-post-answer.ts @@ -3,15 +3,15 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import postAnswer from '../../_api/post-answer' const usePostAnswer = (questionId: number) => { - const QueryClient = useQueryClient() + const queryClient = useQueryClient() return useMutation({ mutationFn: (content: string) => postAnswer(questionId, content), onSuccess: () => { - QueryClient.invalidateQueries({ + queryClient.invalidateQueries({ queryKey: ['questionDetails', questionId], }) - QueryClient.invalidateQueries({ + queryClient.invalidateQueries({ queryKey: ['questionList'], }) }, diff --git a/app/(dashboard)/my/questions/_ui/answer-delete-modal/index.tsx b/app/(dashboard)/my/questions/_ui/answer-delete-modal/index.tsx new file mode 100644 index 00000000..b83d01ae --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/answer-delete-modal/index.tsx @@ -0,0 +1,32 @@ +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import Modal from '@/shared/ui/modal' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isModalOpen: boolean + onCloseModal: () => void + onDelete: () => void +} + +const AnswerDeleteModal = ({ isModalOpen, onCloseModal, onDelete }: Props) => { + return ( + <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> + <p className={cx('message')}>답변을 삭제하시겠습니까?</p> + + <Button.ButtonGroup> + <Button onClick={onCloseModal}>취소</Button> + <Button onClick={onDelete} variant="filled"> + 삭제 + </Button> + </Button.ButtonGroup> + </Modal> + ) +} + +export default AnswerDeleteModal diff --git a/app/(dashboard)/my/questions/_ui/answer-delete-modal/styles.module.scss b/app/(dashboard)/my/questions/_ui/answer-delete-modal/styles.module.scss new file mode 100644 index 00000000..91aa7f52 --- /dev/null +++ b/app/(dashboard)/my/questions/_ui/answer-delete-modal/styles.module.scss @@ -0,0 +1,4 @@ +.message { + margin-bottom: 30px; + @include typo-h4; +} From 6c225fe509ade9f16783ce18b591772e1cd40f95 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 21:54:35 +0900 Subject: [PATCH 593/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#271)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/question-container/index.tsx | 42 +++++++++++++++---- .../my/questions/_api/delete-question.ts | 20 +++++++++ .../_hooks/query/use-delete-question.ts | 22 ++++++++++ .../question-delete-modal.tsx} | 7 ++-- .../styles.module.scss | 1 + shared/types/questions.ts | 1 + 6 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 app/(dashboard)/my/questions/_api/delete-question.ts create mode 100644 app/(dashboard)/my/questions/_hooks/query/use-delete-question.ts rename app/(dashboard)/my/questions/_ui/{answer-delete-modal/index.tsx => modal/question-delete-modal.tsx} (76%) rename app/(dashboard)/my/questions/_ui/{answer-delete-modal => modal}/styles.module.scss (71%) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 63405192..0fd5c7ce 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -2,19 +2,21 @@ import { useRef, useState } from 'react' -import { useParams } from 'next/navigation' +import { useParams, useRouter } from 'next/navigation' import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' import { useAuthStore } from '@/shared/stores/use-auth-store' import { Button } from '@/shared/ui/button' import { ErrorMessage } from '@/shared/ui/error-message' import { Textarea } from '@/shared/ui/textarea' import useDeleteAnswer from '../../../_hooks/query/use-delete-answer' +import useDeleteQuestion from '../../../_hooks/query/use-delete-question' import useGetQuestionDetails from '../../../_hooks/query/use-get-question-details' import usePostAnswer from '../../../_hooks/query/use-post-answer' -import AnswerDeleteModal from '../../../_ui/answer-delete-modal' +import QuestionDeleteModal from '../../../_ui/modal/question-delete-modal' import QuestionDetailCard from '../question-detail-card' import styles from './styles.module.scss' @@ -23,13 +25,16 @@ const cx = classNames.bind(styles) const QuestionContainer = () => { const [isActiveAnswer, setIsActiveAnswer] = useState(false) const [isAnswerDeleteModalOpen, setIsAnswerDeleteModalOpen] = useState(false) + const [isQuestionDeleteModalOpen, setIsQuestionDeleteModalOpen] = useState(false) const [answerErrorMessage, setAnswerErrorMessage] = useState<string | null>(null) const { questionId } = useParams() + const router = useRouter() const textareaRef = useRef<HTMLTextAreaElement | null>(null) const { mutate: submitAnswer } = usePostAnswer(parseInt(questionId as string)) const { mutate: deleteAnswer } = useDeleteAnswer() + const { mutate: deleteQuestion } = useDeleteQuestion() const { data: questionDetails } = useGetQuestionDetails({ questionId: parseInt(questionId as string), }) @@ -72,10 +77,14 @@ const QuestionContainer = () => { }) } - const handleDeleteClick = () => { + const handleDeleteAnswerClick = () => { setIsAnswerDeleteModalOpen(true) } + const handleDeleteQuestionClick = () => { + setIsQuestionDeleteModalOpen(true) + } + const handleDeleteAnswer = () => { if (!questionDetails?.answer) return deleteAnswer( @@ -91,8 +100,19 @@ const QuestionContainer = () => { ) } - if (!questionDetails) { - return + const handleDeleteQuestion = () => { + deleteQuestion( + { + questionId: parseInt(questionId as string), + strategyId: questionDetails.strategyId, + }, + { + onSuccess: () => { + setIsQuestionDeleteModalOpen(false) + router.push(PATH.MY_QUESTIONS) + }, + } + ) } return ( @@ -106,6 +126,7 @@ const QuestionContainer = () => { nickname={questionDetails.nickname} createdAt={questionDetails.createdAt} status={questionDetails.state === 'WAITING' ? '답변 대기' : '답변 완료'} + onDelete={handleDeleteQuestionClick} /> {questionDetails.answer ? ( <QuestionDetailCard @@ -114,7 +135,7 @@ const QuestionContainer = () => { contents={questionDetails.answer.content} nickname={questionDetails.answer.nickname} createdAt={questionDetails.answer.createdAt} - onDelete={handleDeleteClick} + onDelete={handleDeleteAnswerClick} /> ) : ( <>{!isActiveAnswer && <p className={cx('empty-message')}>아직 답변이 없습니다</p>}</> @@ -142,10 +163,17 @@ const QuestionContainer = () => { ) )} </div> - <AnswerDeleteModal + <QuestionDeleteModal isModalOpen={isAnswerDeleteModalOpen} onCloseModal={() => setIsAnswerDeleteModalOpen(false)} onDelete={handleDeleteAnswer} + message="답변을 삭제하시겠습니까?" + /> + <QuestionDeleteModal + isModalOpen={isQuestionDeleteModalOpen} + onCloseModal={() => setIsQuestionDeleteModalOpen(false)} + onDelete={handleDeleteQuestion} + message="문의 내역을 삭제하시겠습니까?" /> </> ) diff --git a/app/(dashboard)/my/questions/_api/delete-question.ts b/app/(dashboard)/my/questions/_api/delete-question.ts new file mode 100644 index 00000000..b1c413dc --- /dev/null +++ b/app/(dashboard)/my/questions/_api/delete-question.ts @@ -0,0 +1,20 @@ +import axiosInstance from '@/shared/api/axios' + +export interface DeleteQuestionProps { + strategyId: number + questionId: number +} + +const deleteQuestion = async ({ strategyId, questionId }: DeleteQuestionProps) => { + try { + const response = await axiosInstance.delete( + `/api/strategies/${strategyId}/questions/${questionId}` + ) + return response.data.isSuccess + } catch (err) { + console.error(err) + throw new Error('문의 내역 삭제에 실패했습니다.') + } +} + +export default deleteQuestion diff --git a/app/(dashboard)/my/questions/_hooks/query/use-delete-question.ts b/app/(dashboard)/my/questions/_hooks/query/use-delete-question.ts new file mode 100644 index 00000000..85498daf --- /dev/null +++ b/app/(dashboard)/my/questions/_hooks/query/use-delete-question.ts @@ -0,0 +1,22 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import deleteQuestion, { DeleteQuestionProps } from '../../_api/delete-question' + +const useDeleteQuestion = () => { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: ({ questionId, strategyId }: DeleteQuestionProps) => + deleteQuestion({ questionId, strategyId }), + onSuccess: (_, { questionId }) => { + queryClient.invalidateQueries({ + queryKey: ['questionDetails', questionId], + }) + + queryClient.invalidateQueries({ + queryKey: ['questionList'], + }) + }, + }) +} + +export default useDeleteQuestion diff --git a/app/(dashboard)/my/questions/_ui/answer-delete-modal/index.tsx b/app/(dashboard)/my/questions/_ui/modal/question-delete-modal.tsx similarity index 76% rename from app/(dashboard)/my/questions/_ui/answer-delete-modal/index.tsx rename to app/(dashboard)/my/questions/_ui/modal/question-delete-modal.tsx index b83d01ae..94a5caaf 100644 --- a/app/(dashboard)/my/questions/_ui/answer-delete-modal/index.tsx +++ b/app/(dashboard)/my/questions/_ui/modal/question-delete-modal.tsx @@ -12,12 +12,13 @@ interface Props { isModalOpen: boolean onCloseModal: () => void onDelete: () => void + message: string } -const AnswerDeleteModal = ({ isModalOpen, onCloseModal, onDelete }: Props) => { +const QuestionDeleteModal = ({ isModalOpen, onCloseModal, onDelete, message }: Props) => { return ( <Modal isOpen={isModalOpen} icon={ModalAlertIcon}> - <p className={cx('message')}>답변을 삭제하시겠습니까?</p> + <p className={cx('message')}>{message}</p> <Button.ButtonGroup> <Button onClick={onCloseModal}>취소</Button> @@ -29,4 +30,4 @@ const AnswerDeleteModal = ({ isModalOpen, onCloseModal, onDelete }: Props) => { ) } -export default AnswerDeleteModal +export default QuestionDeleteModal diff --git a/app/(dashboard)/my/questions/_ui/answer-delete-modal/styles.module.scss b/app/(dashboard)/my/questions/_ui/modal/styles.module.scss similarity index 71% rename from app/(dashboard)/my/questions/_ui/answer-delete-modal/styles.module.scss rename to app/(dashboard)/my/questions/_ui/modal/styles.module.scss index 91aa7f52..9f169860 100644 --- a/app/(dashboard)/my/questions/_ui/answer-delete-modal/styles.module.scss +++ b/app/(dashboard)/my/questions/_ui/modal/styles.module.scss @@ -1,4 +1,5 @@ .message { margin-bottom: 30px; @include typo-h4; + text-align: center; } diff --git a/shared/types/questions.ts b/shared/types/questions.ts index 8ff96183..36b49b6c 100644 --- a/shared/types/questions.ts +++ b/shared/types/questions.ts @@ -37,6 +37,7 @@ export interface QuestionDetailsModel { questionId: number title: string content: string + strategyId: number strategyName: string profileImageUrl: string nickname: string From 85b89dce3d82350bd0886052f8217ebf6f39cedf Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 23:34:40 +0900 Subject: [PATCH 594/648] =?UTF-8?q?feat:=20=EC=B6=94=EA=B0=80=20=EB=AC=B8?= =?UTF-8?q?=EC=9D=98=ED=95=98=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-side-item/side-item.tsx | 36 +--------- .../_ui/question-container/index.tsx | 16 ++++- shared/ui/modal/add-question-modal.tsx | 71 +++++++++++++------ 3 files changed, 66 insertions(+), 57 deletions(-) diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index f63d7dd0..f31738cd 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -1,5 +1,3 @@ -import { useRef, useState } from 'react' - import classNames from 'classnames/bind' import useModal from '@/shared/hooks/custom/use-modal' @@ -11,7 +9,6 @@ import QuestionGuideModal from '@/shared/ui/modal/question-guide-modal' import { formatNumber } from '@/shared/utils/format' import { TitleType } from '.' -import usePostQuestion from '../../strategies/[strategyId]/_hooks/query/use-post-question' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -33,7 +30,6 @@ const SideItem = ({ isMyStrategy = false, strategyName, }: Props) => { - const [isEmpty, setIsEmpty] = useState(false) const { isModalOpen: isAddQuestionModalOpen, openModal: questionOpenModal, @@ -45,33 +41,7 @@ const SideItem = ({ closeModal: guideCloseModal, } = useModal() const user = useAuthStore((state) => state.user) - const titleRef = useRef<HTMLInputElement | null>(null) - const contentRef = useRef<HTMLTextAreaElement | null>(null) - const { mutate } = usePostQuestion() - const handleAddQuestion = () => { - if (titleRef.current && contentRef.current) { - const title = titleRef.current.value.trim() - const content = contentRef.current.value.trim() - if (title !== '' && content !== '') { - const question = { - strategyId, - title: titleRef.current.value, - content: contentRef.current.value, - } - mutate(question, { - onSuccess: (result) => { - if (result?.isSuccess) { - questionCloseModal() - guideOpenModal() - } - }, - }) - } else { - setIsEmpty(true) - } - } - } const isTrader = user?.role.includes('TRADER') return ( @@ -96,13 +66,11 @@ const SideItem = ({ </div> {strategyName && ( <AddQuestionModal + strategyId={strategyId} strategyName={strategyName} isModalOpen={isAddQuestionModalOpen} - titleRef={titleRef} - contentRef={contentRef} onCloseModal={questionCloseModal} - onChange={handleAddQuestion} - isEmpty={isEmpty} + guideOpenModal={guideOpenModal} /> )} <QuestionGuideModal isModalOpen={isQuestionGuideModalOpen} onCloseModal={guideCloseModal} /> diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 0fd5c7ce..23a64709 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -4,12 +4,14 @@ import { useRef, useState } from 'react' import { useParams, useRouter } from 'next/navigation' +import usePostQuestion from '@/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-question' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { useAuthStore } from '@/shared/stores/use-auth-store' import { Button } from '@/shared/ui/button' import { ErrorMessage } from '@/shared/ui/error-message' +import AddQuestionModal from '@/shared/ui/modal/add-question-modal' import { Textarea } from '@/shared/ui/textarea' import useDeleteAnswer from '../../../_hooks/query/use-delete-answer' @@ -26,6 +28,7 @@ const QuestionContainer = () => { const [isActiveAnswer, setIsActiveAnswer] = useState(false) const [isAnswerDeleteModalOpen, setIsAnswerDeleteModalOpen] = useState(false) const [isQuestionDeleteModalOpen, setIsQuestionDeleteModalOpen] = useState(false) + const [isAddQuestionModalOpen, setIsAddQuestionModalOpen] = useState(false) const [answerErrorMessage, setAnswerErrorMessage] = useState<string | null>(null) const { questionId } = useParams() const router = useRouter() @@ -35,6 +38,7 @@ const QuestionContainer = () => { const { mutate: submitAnswer } = usePostAnswer(parseInt(questionId as string)) const { mutate: deleteAnswer } = useDeleteAnswer() const { mutate: deleteQuestion } = useDeleteQuestion() + const { mutate: postQuestion } = usePostQuestion() const { data: questionDetails } = useGetQuestionDetails({ questionId: parseInt(questionId as string), }) @@ -48,7 +52,9 @@ const QuestionContainer = () => { const isTrader = user.role.includes('TRADER') const isInvestor = user.role.includes('INVESTOR') - const handleQuestionAdd = () => {} + const handleQuestionAdd = () => { + setIsAddQuestionModalOpen(true) + } const handleAnswerAdd = () => { setIsActiveAnswer((prevState) => !prevState) @@ -175,6 +181,14 @@ const QuestionContainer = () => { onDelete={handleDeleteQuestion} message="문의 내역을 삭제하시겠습니까?" /> + <AddQuestionModal + isModalOpen={isAddQuestionModalOpen} + isEmpty={false} + strategyName={questionDetails.strategyName} + onCloseModal={() => setIsAddQuestionModalOpen(false)} + title={`RE: ${questionDetails.title}`} + content={questionDetails.content} + /> </> ) } diff --git a/shared/ui/modal/add-question-modal.tsx b/shared/ui/modal/add-question-modal.tsx index 9b51b850..4d39c29f 100644 --- a/shared/ui/modal/add-question-modal.tsx +++ b/shared/ui/modal/add-question-modal.tsx @@ -1,7 +1,8 @@ 'use client' -import React from 'react' +import { useEffect, useRef, useState } from 'react' +import usePostQuestion from '@/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-post-question' import { RegisterIcon } from '@/public/icons' import classNames from 'classnames/bind' @@ -16,27 +17,63 @@ const cx = classNames.bind(styles) interface Props { isModalOpen: boolean - isEmpty: boolean strategyName: string title?: string content?: string - titleRef?: React.RefObject<HTMLInputElement> - contentRef?: React.RefObject<HTMLTextAreaElement> + strategyId: number onCloseModal: () => void - onChange?: () => void + guideOpenModal?: () => void } const AddQuestionModal = ({ isModalOpen, - isEmpty = false, strategyName, title, content, - titleRef, - contentRef, + strategyId, onCloseModal, - onChange, + guideOpenModal, }: Props) => { + const [isEmpty, setIsEmpty] = useState(false) + const titleRef = useRef<HTMLInputElement>(null) + const contentRef = useRef<HTMLTextAreaElement>(null) + + const { mutate } = usePostQuestion() + + const handleAddQuestion = () => { + if (titleRef.current && contentRef.current) { + const title = titleRef.current.value.trim() + const content = contentRef.current.value.trim() + if (title !== '' && content !== '') { + const question = { + strategyId, + title: titleRef.current.value, + content: contentRef.current.value, + } + mutate(question, { + onSuccess: (result) => { + if (result?.isSuccess) { + onCloseModal() + if (guideOpenModal) { + guideOpenModal() + } + } + }, + }) + } else { + setIsEmpty(true) + } + } + } + + /* eslint-disable react-hooks/exhaustive-deps */ + useEffect(() => { + if (isModalOpen && titleRef?.current && contentRef?.current) { + titleRef.current.value = title || '' + contentRef.current.value = content ? `\n\n----- 이전 문의내역 -----\n${content}` : '' + } + }, [isModalOpen]) + return ( <Modal isOpen={isModalOpen} @@ -48,27 +85,17 @@ const AddQuestionModal = ({ <div className={cx('question-title')}> <p>제목</p> <div> - <Input - ref={titleRef} - value={title && title} - inputSize="full" - placeholder="제목을 입력하세요" - /> + <Input ref={titleRef} inputSize="full" placeholder="제목을 입력하세요" /> </div> </div> <div className={cx('question-content')}> <p>내용</p> - <Textarea - ref={contentRef} - value={content && content} - placeholder="내용을 입력하세요." - className={cx('content')} - /> + <Textarea ref={contentRef} placeholder="내용을 입력하세요." className={cx('content')} /> </div> {isEmpty && <ErrorMessage errorMessage="제목 또는 내용을 모두 입력해주세요." />} <div className={cx('two-button', 'question')}> <Button onClick={onCloseModal}>취소</Button> - <Button onClick={onChange} variant="filled" className={cx('button')}> + <Button onClick={handleAddQuestion} variant="filled" className={cx('button')}> 문의 </Button> </div> From ee30e8853d5ee226dee7ed83fc03424361b4685f Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Thu, 5 Dec 2024 23:37:52 +0900 Subject: [PATCH 595/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=A1=9C=EB=94=A9=20=EC=8A=A4=EC=BC=88=EB=A0=88?= =?UTF-8?q?=ED=86=A4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=20(#274)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details-side-item/side-skeleton/index.tsx | 16 ++ .../side-skeleton/styles.module.scss | 26 +++ .../[strategyId]/loading.module.scss | 167 ++++++++++++++++++ .../strategies/[strategyId]/loading.tsx | 58 ++++++ .../strategies/[strategyId]/page.tsx | 49 +++-- 5 files changed, 302 insertions(+), 14 deletions(-) create mode 100644 app/(dashboard)/_ui/details-side-item/side-skeleton/index.tsx create mode 100644 app/(dashboard)/_ui/details-side-item/side-skeleton/styles.module.scss create mode 100644 app/(dashboard)/strategies/[strategyId]/loading.module.scss create mode 100644 app/(dashboard)/strategies/[strategyId]/loading.tsx diff --git a/app/(dashboard)/_ui/details-side-item/side-skeleton/index.tsx b/app/(dashboard)/_ui/details-side-item/side-skeleton/index.tsx new file mode 100644 index 00000000..5c67fff6 --- /dev/null +++ b/app/(dashboard)/_ui/details-side-item/side-skeleton/index.tsx @@ -0,0 +1,16 @@ +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) +const SideSkeleton = () => { + return ( + <div className={cx('container')}> + {Array.from({ length: 6 }, (_, idx) => ( + <div key={idx}></div> + ))} + </div> + ) +} + +export default SideSkeleton diff --git a/app/(dashboard)/_ui/details-side-item/side-skeleton/styles.module.scss b/app/(dashboard)/_ui/details-side-item/side-skeleton/styles.module.scss new file mode 100644 index 00000000..ce911561 --- /dev/null +++ b/app/(dashboard)/_ui/details-side-item/side-skeleton/styles.module.scss @@ -0,0 +1,26 @@ +.container { + width: 100%; + div { + @include skeleton; + width: 100%; + margin-bottom: 20px; + } + :first-child { + height: 83px; + } + :nth-child(2) { + height: 122px; + } + :nth-child(3) { + height: 108px; + } + :nth-child(4) { + height: 108px; + } + :nth-child(5) { + height: 200px; + } + :nth-child(6) { + height: 200px; + } +} diff --git a/app/(dashboard)/strategies/[strategyId]/loading.module.scss b/app/(dashboard)/strategies/[strategyId]/loading.module.scss new file mode 100644 index 00000000..91c5aecb --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/loading.module.scss @@ -0,0 +1,167 @@ +@mixin line-small { + width: 70px; + height: 17px; +} + +@mixin line-medium { + width: 170px; + height: 25px; +} + +@mixin line-large { + width: 236px; + height: 33px; +} + +@mixin box-small { + width: 100px; + height: 33px; +} + +@mixin box-medium { + width: 168px; + height: 33px; +} + +@mixin box-large { + width: 417px; + height: 98px; +} + +@mixin flex-column { + display: flex; + flex-direction: column; +} + +.container { + margin-top: 20px; + .first-wrapper { + display: grid; + grid-template-columns: 0.5fr 1.5fr; + height: 132px; + gap: 10px; + margin-bottom: 20px; + .left { + @include skeleton; + @include flex-column; + padding: 10px; + * { + margin-bottom: 15px; + } + :first-child { + @include line-small; + } + :nth-child(2) { + @include line-medium; + } + :nth-child(3) { + @include line-large; + } + } + .right { + @include skeleton; + display: grid; + padding: 15px; + grid-template-columns: repeat(3, 1fr); + * { + margin-top: 10px; + } + :first-child { + @include skeleton; + @include flex-column; + :first-child { + @include box-small; + } + :nth-child(2) { + @include box-medium; + } + } + :nth-child(2) { + @include skeleton; + @include flex-column; + :first-child { + @include box-small; + } + :nth-child(2) { + @include box-medium; + } + } + :nth-child(3) { + @include skeleton; + @include flex-column; + :first-child { + @include box-small; + } + :nth-child(2) { + @include box-medium; + } + } + } + } + .second-wrapper { + height: 160px; + @include skeleton; + padding: 20px 20px 40px; + margin-bottom: 20px; + p { + margin-bottom: 10px; + } + div { + width: 100%; + height: 72px; + } + } + .third-wrapper { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 10px; + height: 108px; + margin-bottom: 20px; + div { + @include skeleton; + @include flex-column; + align-items: center; + padding: 10px; + div { + @include box-small; + margin: 5px 0; + } + } + } + .fourth-wrapper { + @include skeleton; + padding: 20px; + p { + margin-bottom: 10px; + @include typo-h4; + color: $color-gray-600; + } + .chart { + height: 367px; + margin-bottom: 30px; + } + .tab { + display: flex; + background-color: $color-gray-200; + margin-bottom: 30px; + div { + @include box-small; + margin-right: 10px; + } + } + .analysis { + background-color: $color-gray-200; + padding: 10px; + div { + display: flex; + justify-content: space-between; + @include skeleton; + div { + height: 131px; + width: 90%; + margin: 10px; + } + } + } + } +} diff --git a/app/(dashboard)/strategies/[strategyId]/loading.tsx b/app/(dashboard)/strategies/[strategyId]/loading.tsx new file mode 100644 index 00000000..2b6644e1 --- /dev/null +++ b/app/(dashboard)/strategies/[strategyId]/loading.tsx @@ -0,0 +1,58 @@ +import classNames from 'classnames/bind' + +import styles from './loading.module.scss' + +const cx = classNames.bind(styles) + +const DetailsLoading = () => { + return ( + <div className={cx('container')}> + <div className={cx('first-wrapper')}> + <div className={cx('left')}> + {Array.from({ length: 3 }, (_, idx) => ( + <div key={idx}></div> + ))} + </div> + <div className={cx('right')}> + {Array.from({ length: 3 }, (_, idx) => ( + <div key={idx}> + <div></div> + <div></div> + </div> + ))} + </div> + </div> + <div className={cx('second-wrapper')}> + <p>전략 상세 소개</p> + <div></div> + </div> + <div className={cx('third-wrapper')}> + {Array.from({ length: 5 }, (_, idx) => ( + <div key={idx}> + <div></div> + <div></div> + </div> + ))} + </div> + <div className={cx('fourth-wrapper')}> + <p>분석</p> + <div className={cx('chart')}></div> + <div className={cx('tab')}> + {Array.from({ length: 4 }, (_, idx) => ( + <div key={idx}></div> + ))} + </div> + <div className={cx('analysis')}> + {Array.from({ length: 4 }, (_, idx) => ( + <div key={idx}> + <div></div> + <div></div> + </div> + ))} + </div> + </div> + </div> + ) +} + +export default DetailsLoading diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 63f82902..6fc32788 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -1,7 +1,8 @@ 'use client' -import AnalysisContainer from '@/app/(dashboard)/_ui/analysis-container' -import SubscriberItem from '@/app/(dashboard)/_ui/subscriber-item' +import React, { Suspense } from 'react' + +import dynamic from 'next/dynamic' import useModal from '@/shared/hooks/custom/use-modal' import { useAuthStore } from '@/shared/stores/use-auth-store' @@ -9,12 +10,28 @@ import BackHeader from '@/shared/ui/header/back-header' import SubscribeCheckModal from '@/shared/ui/modal/subscribe-check-modal' import Title from '@/shared/ui/title' -import DetailsInformation from '../../_ui/details-information' -import DetailsSideItem, { InformationModel, TitleType } from '../../_ui/details-side-item' +import { InformationModel, TitleType } from '../../_ui/details-side-item' +import SideSkeleton from '../../_ui/details-side-item/side-skeleton' import useGetSubscribe from '../_hooks/query/use-get-subscribe' import SideContainer from '../_ui/side-container' import useGetDetailsInformationData from './_hooks/query/use-get-details-information-data' -import ReviewContainer from './_ui/review-container' +import DetailsLoading from './loading' + +const DetailsInformation = React.lazy(() => import('../../_ui/details-information')) +const AnalysisContainer = React.lazy(() => import('@/app/(dashboard)/_ui/analysis-container')) +const ReviewContainer = React.lazy(() => import('./_ui/review-container')) +const SubscriberItem = React.lazy(() => import('@/app/(dashboard)/_ui/subscriber-item')) +const DetailsSideItem = React.lazy(() => import('../../_ui/details-side-item')) + +const DynamicSkeleton = dynamic(() => import('./loading'), { + loading: () => <DetailsLoading />, + ssr: false, +}) + +const DynamicSideSkeleton = dynamic(() => import('../../_ui/details-side-item/side-skeleton'), { + loading: () => <SideSkeleton />, + ssr: false, +}) export type InformationType = { title: TitleType; data: string | number } | InformationModel[] @@ -51,15 +68,19 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { return ( <> - <div> - <BackHeader label={'목록으로 돌아가기'} /> - <Title label={'전략 상세보기'} /> + <BackHeader label={'목록으로 돌아가기'} /> + <Title label={'전략 상세보기'} /> + <Suspense fallback={<DynamicSkeleton />}> {information && ( - <DetailsInformation information={information} strategyId={params.strategyId} /> + <> + <DetailsInformation information={information} strategyId={params.strategyId} /> + <AnalysisContainer strategyId={params.strategyId} /> + <ReviewContainer strategyId={params.strategyId} /> + </> )} - <AnalysisContainer strategyId={params.strategyId} /> - <ReviewContainer strategyId={params.strategyId} /> - <SideContainer> + </Suspense> + <SideContainer> + <Suspense fallback={<DynamicSideSkeleton />}> {information && ( <SubscriberItem isMyStrategy={user?.nickname === information.nickname} @@ -80,8 +101,8 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { /> </div> ))} - </SideContainer> - </div> + </Suspense> + </SideContainer> {isModalOpen && information && ( <SubscribeCheckModal isSubscribing={information?.isSubscribed} From b11c95f5e17c693d88103780d0e443a0fdc2a0f3 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Thu, 5 Dec 2024 23:47:49 +0900 Subject: [PATCH 596/648] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=97=90=20=EC=A4=84=EB=B0=94=EA=BF=88=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[questionId]/_ui/question-container/index.tsx | 2 +- .../[questionId]/_ui/question-detail-card/index.tsx | 11 ++++++++++- shared/ui/modal/add-question-modal.tsx | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx index 23a64709..9150e3f7 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-container/index.tsx @@ -182,8 +182,8 @@ const QuestionContainer = () => { message="문의 내역을 삭제하시겠습니까?" /> <AddQuestionModal + strategyId={questionDetails.strategyId} isModalOpen={isAddQuestionModalOpen} - isEmpty={false} strategyName={questionDetails.strategyName} onCloseModal={() => setIsAddQuestionModalOpen(false)} title={`RE: ${questionDetails.title}`} diff --git a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx index fc93d249..e1f21245 100644 --- a/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx +++ b/app/(dashboard)/my/questions/[questionId]/_ui/question-detail-card/index.tsx @@ -1,5 +1,7 @@ 'use client' +import React from 'react' + import classNames from 'classnames/bind' import { QuestionStatusType } from '@/shared/types/questions' @@ -58,7 +60,14 @@ const QuestionDetailCard = ({ )} </div> </div> - <div className={cx('card-contents')}>{contents}</div> + <div className={cx('card-contents')}> + {contents.split('\n').map((line, idx) => ( + <React.Fragment key={line + idx}> + {line} + <br /> + </React.Fragment> + ))} + </div> </div> ) } diff --git a/shared/ui/modal/add-question-modal.tsx b/shared/ui/modal/add-question-modal.tsx index 4d39c29f..2b0ad897 100644 --- a/shared/ui/modal/add-question-modal.tsx +++ b/shared/ui/modal/add-question-modal.tsx @@ -43,7 +43,7 @@ const AddQuestionModal = ({ const handleAddQuestion = () => { if (titleRef.current && contentRef.current) { const title = titleRef.current.value.trim() - const content = contentRef.current.value.trim() + const content = contentRef.current.value.trimEnd() if (title !== '' && content !== '') { const question = { strategyId, From e751659fbdf52c554d46b8e676b16674d375407d Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 6 Dec 2024 00:00:17 +0900 Subject: [PATCH 597/648] =?UTF-8?q?feat:=20=ED=8C=8C=EB=B9=84=EC=BD=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#277)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/favicon.ico | Bin 25931 -> 0 bytes app/icon.svg | 5 +++++ 2 files changed, 5 insertions(+) delete mode 100644 app/favicon.ico create mode 100644 app/icon.svg diff --git a/app/favicon.ico b/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 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 LrJugUA?W`A8`#=m diff --git a/app/icon.svg b/app/icon.svg new file mode 100644 index 00000000..be64bbf6 --- /dev/null +++ b/app/icon.svg @@ -0,0 +1,5 @@ +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M29.8302 23.6719H50.525L66.5061 57.3608V75.9972H45.2364L29.8302 42.9227V23.6719Z" fill="#FF7752"/> +<path d="M58.9102 23.6719H79.7394L95.8242 57.3608V75.9972H74.4164L58.9102 42.9227V23.6719Z" fill="#FF7752"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6811 23.6719H4.17566V42.7325L4.30304 43.1472H14.9445V75.9897H14.3901L14.3924 75.9972H35.6621V57.3608L19.6811 23.6719Z" fill="#FF7752"/> +</svg> From 73293750d97194c666c43cb348eef2caf68dce2b Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 00:48:07 +0900 Subject: [PATCH 598/648] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20searchInpu?= =?UTF-8?q?t=20=EC=97=94=ED=84=B0=ED=82=A4=20=EC=B6=94=EA=B0=80=20(#279)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/_ui/search-bar/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/(dashboard)/strategies/_ui/search-bar/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/index.tsx index 0b6055bf..bdb0a0ce 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/index.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/index.tsx @@ -41,6 +41,12 @@ const SearchBarContainer = () => { } } + const handleEnterSearch = (e: React.KeyboardEvent<HTMLInputElement>) => { + if (e.key === 'Enter') { + onSearch() + } + } + const onReset = async () => { await resetState() if (searchRef.current) { @@ -84,6 +90,7 @@ const SearchBarContainer = () => { ref={searchRef} placeholder="전략명을 검색하세요." onChange={handleSearchWord} + onKeyDown={(e) => handleEnterSearch(e)} onSearchIconClick={onSearch} /> </div> From cece1689287d6bb4fd4df28e4685883dda06bc7b Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 6 Dec 2024 00:49:56 +0900 Subject: [PATCH 599/648] =?UTF-8?q?feat:=20spinner=20=EA=B0=80=EC=9A=B4?= =?UTF-8?q?=EB=8D=B0=20=EC=A0=95=EB=A0=AC=20(#281)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/(home)/_ui/user-metrics-section/index.tsx | 2 +- .../(home)/_ui/user-metrics-section/styles.module.scss | 4 ++++ shared/ui/spinner/index.tsx | 8 +++++++- shared/ui/spinner/styles.module.scss | 6 ++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/(landing)/(home)/_ui/user-metrics-section/index.tsx b/app/(landing)/(home)/_ui/user-metrics-section/index.tsx index 31e59fe0..aed9187d 100644 --- a/app/(landing)/(home)/_ui/user-metrics-section/index.tsx +++ b/app/(landing)/(home)/_ui/user-metrics-section/index.tsx @@ -15,7 +15,7 @@ const UserMetricsSection = () => { const { data: metrics, isLoading } = useGetUserMetrics() if (isLoading) { - return <Spinner /> + return <Spinner className={cx('spinner')} /> } if (!metrics) { diff --git a/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss b/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss index 493905b3..e7316e0d 100644 --- a/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss +++ b/app/(landing)/(home)/_ui/user-metrics-section/styles.module.scss @@ -17,3 +17,7 @@ } } } + +.spinner { + margin: 200px 0; +} diff --git a/shared/ui/spinner/index.tsx b/shared/ui/spinner/index.tsx index 1d366cc6..54c7a6e1 100644 --- a/shared/ui/spinner/index.tsx +++ b/shared/ui/spinner/index.tsx @@ -1,3 +1,5 @@ +'use client' + import classNames from 'classnames/bind' import styles from './styles.module.scss' @@ -9,7 +11,11 @@ interface Props { } const Spinner = ({ className }: Props) => { - return <div className={cx('spinner', className)} /> + return ( + <div className={cx('container')}> + <div className={cx('spinner', className)} /> + </div> + ) } export default Spinner diff --git a/shared/ui/spinner/styles.module.scss b/shared/ui/spinner/styles.module.scss index 792878bd..64472ca2 100644 --- a/shared/ui/spinner/styles.module.scss +++ b/shared/ui/spinner/styles.module.scss @@ -1,3 +1,9 @@ +.container { + display: flex; + align-items: center; + justify-content: center; +} + .spinner { width: 32px; height: 32px; From a4936606b1125f8c0d75e8ebf40980dc20807b88 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 6 Dec 2024 00:58:52 +0900 Subject: [PATCH 600/648] =?UTF-8?q?feat:=20=EA=B5=AC=EB=8F=85=ED=95=9C=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=EC=97=86=EC=9D=84=20=EB=95=8C=20=EB=AC=B8?= =?UTF-8?q?=EA=B5=AC=20=EC=B6=94=EA=B0=80=20(#284)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/favorites/_ui/favorite-strategy-list/index.tsx | 3 +++ .../_ui/favorite-strategy-list/styles.module.scss | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx index 54f251e9..98db7233 100644 --- a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx +++ b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx @@ -30,6 +30,9 @@ const FavoriteStrategyList = () => { <> <ListHeader /> <div className={cx('pagination')}> + {!strategiesData.length && ( + <p className={cx('no-strategy')}>구독한 관심 전략이 없습니다.</p> + )} {strategiesData?.map((strategy) => ( <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> ))} diff --git a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss index f5081c9f..57247f5a 100644 --- a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss +++ b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/styles.module.scss @@ -1,3 +1,10 @@ .pagination { margin-bottom: 24px; } + +.no-strategy { + margin-top: 180px; + text-align: center; + @include typo-b1; + color: $color-gray-600; +} From bd309704ab6847947ad70ff8413075f56005d9c9 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 6 Dec 2024 01:09:59 +0900 Subject: [PATCH 601/648] =?UTF-8?q?feat:=20=EC=8A=A4=EC=BC=88=EB=A0=88?= =?UTF-8?q?=ED=86=A4=20UI=20=EC=B6=94=EA=B0=80=20(#284)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/favorite-strategy-list/index.tsx | 37 +++++++++++++------ app/(dashboard)/my/favorites/page.tsx | 6 +-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx index 98db7233..31a50927 100644 --- a/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx +++ b/app/(dashboard)/my/favorites/_ui/favorite-strategy-list/index.tsx @@ -1,7 +1,10 @@ 'use client' +import { Suspense } from 'react' + import ListHeader from '@/app/(dashboard)/_ui/list-header' import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item' +import StrategiesItemSkeleton from '@/app/(dashboard)/_ui/strategies-item/skeleton' import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' @@ -29,17 +32,29 @@ const FavoriteStrategyList = () => { return ( <> <ListHeader /> - <div className={cx('pagination')}> - {!strategiesData.length && ( - <p className={cx('no-strategy')}>구독한 관심 전략이 없습니다.</p> - )} - {strategiesData?.map((strategy) => ( - <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> - ))} - {totalPages && ( - <Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} /> - )} - </div> + <Suspense fallback={<Skeleton />}> + <div className={cx('pagination')}> + {!strategiesData.length && ( + <p className={cx('no-strategy')}>구독한 관심 전략이 없습니다.</p> + )} + {strategiesData?.map((strategy) => ( + <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> + ))} + {totalPages && ( + <Pagination currentPage={page} maxPage={totalPages} onPageChange={handlePageChange} /> + )} + </div> + </Suspense> + </> + ) +} + +const Skeleton = () => { + return ( + <> + {Array.from({ length: 6 }, (_, idx) => ( + <StrategiesItemSkeleton key={idx} /> + ))} </> ) } diff --git a/app/(dashboard)/my/favorites/page.tsx b/app/(dashboard)/my/favorites/page.tsx index e64c2537..aa5d51c0 100644 --- a/app/(dashboard)/my/favorites/page.tsx +++ b/app/(dashboard)/my/favorites/page.tsx @@ -1,5 +1,3 @@ -import { Suspense } from 'react' - import classNames from 'classnames/bind' import Title from '@/shared/ui/title' @@ -13,9 +11,7 @@ const MyFavoritesPage = () => { return ( <div className={cx('container')}> <Title label="구독한 전략" /> - <Suspense fallback={<div>Loading...</div>}> - <FavoriteStrategyList /> - </Suspense> + <FavoriteStrategyList /> </div> ) } From 17558974cc8de6cff338d399000bfe025a7c2f1e Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 6 Dec 2024 01:32:54 +0900 Subject: [PATCH 602/648] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B9=84=EB=B0=80=20=EB=B2=88=ED=98=B8=20=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84=20(#272)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 52 +++++- app/(landing)/signin/styles.module.scss | 4 + shared/ui/modal/find-email-modal/index.tsx | 86 ++++++++++ .../modal/find-email-modal/styles.module.scss | 45 +++++ shared/ui/modal/find-password-modal/index.tsx | 160 ++++++++++++++++++ .../find-password-modal/styles.module.scss | 56 ++++++ 6 files changed, 394 insertions(+), 9 deletions(-) create mode 100644 shared/ui/modal/find-email-modal/index.tsx create mode 100644 shared/ui/modal/find-email-modal/styles.module.scss create mode 100644 shared/ui/modal/find-password-modal/index.tsx create mode 100644 shared/ui/modal/find-password-modal/styles.module.scss diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index 169914ca..a791cb39 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -3,7 +3,6 @@ import { useCallback, useState } from 'react' import dynamic from 'next/dynamic' -import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' import { AxiosError } from 'axios' @@ -11,12 +10,15 @@ import classNames from 'classnames/bind' import { ERROR_MESSAGES } from '@/shared/constants/error-messages' import { PATH } from '@/shared/constants/path' +import useModal from '@/shared/hooks/custom/use-modal' import { useLoginMutation } from '@/shared/hooks/query/auth-queries' import { useAuthStore } from '@/shared/stores/use-auth-store' import type { LoginFormDataModel } from '@/shared/types/auth' import { Button } from '@/shared/ui/button' import Checkbox from '@/shared/ui/check-box' import { Input } from '@/shared/ui/input' +import FindEmailModal from '@/shared/ui/modal/find-email-modal' +import FindPasswordModal from '@/shared/ui/modal/find-password-modal' import { validate } from '@/shared/utils/validation' import styles from './styles.module.scss' @@ -30,6 +32,17 @@ const SignInPage = () => { const setKeepLoggedIn = useAuthStore((state) => state.setKeepLoggedIn) const isKeepLoggedIn = useAuthStore((state) => state.isKeepLoggedIn) + const { + isModalOpen: isFindEmailOpen, + openModal: handleFindEmailOpen, + closeModal: handleFindEmailClose, + } = useModal() + const { + isModalOpen: isFindPasswordOpen, + openModal: handleFindPasswordOpen, + closeModal: handleFindPasswordClose, + } = useModal() + const [formData, setFormData] = useState<LoginFormDataModel>({ email: '', password: '', @@ -37,7 +50,12 @@ const SignInPage = () => { const [errors, setErrors] = useState<string | null>(null) const [isSubmitting, setIsSubmitting] = useState(false) - const validateForm = () => { + const validateForm = (e?: React.MouseEvent) => { + if (e) { + e.preventDefault() + e.stopPropagation() + } + const emailError = validate('EMAIL', formData.email) if (emailError) { setErrors(emailError) @@ -55,14 +73,11 @@ const SignInPage = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() + if (!validateForm()) return + setIsSubmitting(true) setErrors(null) - if (!validateForm()) { - setIsSubmitting(false) - return - } - try { const response = await loginMutation.mutateAsync(formData) @@ -112,6 +127,12 @@ const SignInPage = () => { [setKeepLoggedIn] ) + const handleFindButtonClick = (e: React.MouseEvent, handler: () => void) => { + e.preventDefault() + e.stopPropagation() + handler() + } + const isFormDisabled = isSubmitting || loginMutation.isPending return ( @@ -171,9 +192,19 @@ const SignInPage = () => { /> </label> <div className={cx('find-buttons')}> - <Link href={'/'}>아이디 찾기</Link> + <button + onClick={(e) => handleFindButtonClick(e, handleFindEmailOpen)} + className={cx('find-button')} + > + 아이디 찾기 + </button> <span className={cx('divider')}>|</span> - <Link href={'/'}>비밀번호 찾기</Link> + <button + onClick={(e) => handleFindButtonClick(e, handleFindPasswordOpen)} + className={cx('find-button')} + > + 비밀번호 찾기 + </button> </div> </div> <Button type="submit" size="large" variant="filled" disabled={isFormDisabled}> @@ -189,6 +220,9 @@ const SignInPage = () => { </Button> </form> </div> + + <FindEmailModal isOpen={isFindEmailOpen} onClose={handleFindEmailClose} /> + <FindPasswordModal isOpen={isFindPasswordOpen} onClose={handleFindPasswordClose} /> </div> ) } diff --git a/app/(landing)/signin/styles.module.scss b/app/(landing)/signin/styles.module.scss index 6d404244..7d1b2e4f 100644 --- a/app/(landing)/signin/styles.module.scss +++ b/app/(landing)/signin/styles.module.scss @@ -73,6 +73,10 @@ gap: 8px; color: $color-gray-600; @include typo-c1; + + button { + background-color: transparent; + } } } diff --git a/shared/ui/modal/find-email-modal/index.tsx b/shared/ui/modal/find-email-modal/index.tsx new file mode 100644 index 00000000..9a3ab0d1 --- /dev/null +++ b/shared/ui/modal/find-email-modal/index.tsx @@ -0,0 +1,86 @@ +'use client' + +import { useEffect } from 'react' +import { useState } from 'react' + +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { useFindCredentials } from '@/shared/hooks/query/use-find-credentials' +import { Button } from '@/shared/ui/button' +import { Input } from '@/shared/ui/input' +import Modal from '@/shared/ui/modal' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isOpen: boolean + onClose: () => void +} + +const FindEmailModal = ({ isOpen, onClose }: Props) => { + const [phone, setPhone] = useState('') + const [foundEmail, setFoundEmail] = useState('') + const [error, setError] = useState('') + + const { findEmailMutation } = useFindCredentials() + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + try { + const response = await findEmailMutation.mutateAsync(phone) + if (response.isSuccess && response.result.isFound) { + setFoundEmail(response.result.email) + setError('') + } else { + setError('해당 전화번호로 등록된 이메일을 찾을 수 없습니다.') + } + } catch { + setError('이메일 찾기에 실패했습니다.') + } + } + + useEffect(() => { + if (!isOpen) { + setPhone('') + setFoundEmail('') + setError('') + } + }, [isOpen]) + + return ( + <Modal isOpen={isOpen} icon={ModalAlertIcon} message="이메일 찾기"> + <form onSubmit={handleSubmit} className={cx('form')}> + <p className={cx('explanation')}> + 가입 이메일 주소를 찾기 위해 + <br /> 회원가입 시 입력한 휴대전화 번호를 입력해주세요. + </p> + <div className={cx('input-group')}> + <label>휴대전화</label> + <Input + type="tel" + value={phone} + onChange={(e) => setPhone(e.target.value)} + placeholder="휴대전화 번호를 입력하세요" + disabled={findEmailMutation.isPending} + required + /> + </div> + {error && <p className={cx('error')}>{error}</p>} + {foundEmail && <p className={cx('success')}>찾은 이메일: {foundEmail}</p>} + <div className={cx('buttons')}> + <Button type="button" onClick={onClose} variant="outline"> + 닫기 + </Button> + <Button type="submit" disabled={findEmailMutation.isPending} variant="filled"> + 확인 + </Button> + </div> + </form> + </Modal> + ) +} + +export default FindEmailModal diff --git a/shared/ui/modal/find-email-modal/styles.module.scss b/shared/ui/modal/find-email-modal/styles.module.scss new file mode 100644 index 00000000..cfb8630a --- /dev/null +++ b/shared/ui/modal/find-email-modal/styles.module.scss @@ -0,0 +1,45 @@ +.form { + display: flex; + flex-direction: column; + gap: 1rem; + width: 100%; +} + +.explanation { + color: $color-gray-800; + @include typo-c1; + text-align: center; +} + +.input-group { + display: flex; + flex-direction: column; + gap: 0.5rem; + + label { + font-size: 0.875rem; + font-weight: 500; + color: var(--gray-700); + } +} + +.error { + color: $color-orange-700; + font-size: 0.8rem; +} + +.success { + color: $color-indigo; + font-size: 0.8rem; +} + +.buttons { + display: flex; + gap: 36px; + margin-top: 1rem; + justify-content: center; + + button { + flex: 0.2; + } +} diff --git a/shared/ui/modal/find-password-modal/index.tsx b/shared/ui/modal/find-password-modal/index.tsx new file mode 100644 index 00000000..54011583 --- /dev/null +++ b/shared/ui/modal/find-password-modal/index.tsx @@ -0,0 +1,160 @@ +'use client' + +import { useEffect } from 'react' +import { useState } from 'react' + +import { ModalAlertIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { useResetPassword } from '@/shared/hooks/custom/use-reset-password' +import { Button } from '@/shared/ui/button' +import { Input } from '@/shared/ui/input' +import Modal from '@/shared/ui/modal' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isOpen: boolean + onClose: () => void +} + +const FindPasswordModal = ({ isOpen, onClose }: Props) => { + const [error, setError] = useState('') + const { + formData, + step, + isPending, + handleInputChange, + handleRequestCode, + handleVerifyEmail, + handlePasswordReset, + setStep, + resetForm, + } = useResetPassword({ + onSuccess: onClose, + onError: (message) => setError(message), + }) + + useEffect(() => { + if (!isOpen) { + resetForm() + setError('') + setStep(1) + } + }, [isOpen, resetForm, setStep]) + + return ( + <Modal + isOpen={isOpen} + icon={ModalAlertIcon} + message="비밀번호 재설정" + className={cx('container')} + > + {step === 1 && ( + <form onSubmit={handleVerifyEmail} className={cx('form')}> + <p className={cx('explanation')}>본인 인증을 위해 가입한 이메일 주소를 입력해주세요.</p> + <div className={cx('input-group')}> + <label>이메일 주소</label> + <div className={cx('input-button-group')}> + <Input + type="email" + name="email" + className={cx('input')} + value={formData.email} + onChange={handleInputChange} + placeholder="이메일을 입력하세요" + disabled={isPending} + required + /> + <Button + type="button" + onClick={handleRequestCode} + disabled={isPending || !formData.email} + variant="filled" + > + 인증 + </Button> + </div> + </div> + <div className={cx('input-group')}> + <label>인증번호</label> + <div className={cx('input-button-group')}> + <Input + type="text" + name="code" + className={cx('input')} + value={formData.code} + onChange={handleInputChange} + placeholder="인증번호를 입력하세요" + disabled={isPending} + required + /> + <Button + type="button" + onClick={handleVerifyEmail} + disabled={isPending || !formData.code} + variant="outline" + > + 확인 + </Button> + </div> + </div> + {error && <p className={cx('error')}>{error}</p>} + <div className={cx('buttons')}> + <Button type="button" onClick={onClose} variant="outline"> + 닫기 + </Button> + <Button type="submit" disabled={isPending} variant="filled"> + 다음 + </Button> + </div> + </form> + )} + + {step === 2 && ( + <form onSubmit={handlePasswordReset} className={cx('form')}> + <p className={cx('explanation')}>본인 인증을 위해 가입한 이메일 주소를 입력해주세요.</p> + <div className={cx('input-group')}> + <label>새 비밀번호</label> + <Input + type="password" + name="password" + className={cx('input')} + value={formData.password} + onChange={handleInputChange} + placeholder="새 비밀번호를 입력하세요" + disabled={isPending} + required + /> + </div> + <div className={cx('input-group')}> + <label>비밀번호 확인</label> + <Input + type="password" + name="passwordConfirm" + className={cx('input')} + value={formData.passwordConfirm} + onChange={handleInputChange} + placeholder="비밀번호를 다시 입력하세요" + disabled={isPending} + required + /> + </div> + {error && <p className={cx('error')}>{error}</p>} + <div className={cx('buttons')}> + <Button type="button" onClick={() => setStep(1)} variant="outline"> + 이전 + </Button> + <Button type="submit" disabled={isPending} variant="filled"> + 재설정 + </Button> + </div> + </form> + )} + </Modal> + ) +} + +export default FindPasswordModal diff --git a/shared/ui/modal/find-password-modal/styles.module.scss b/shared/ui/modal/find-password-modal/styles.module.scss new file mode 100644 index 00000000..155d1825 --- /dev/null +++ b/shared/ui/modal/find-password-modal/styles.module.scss @@ -0,0 +1,56 @@ +.container { + width: 440px; + height: 480px; + display: flex; + justify-content: center; +} +.explanation { + color: $color-gray-800; + @include typo-c1; + text-align: center; +} +.form { + display: flex; + flex-direction: column; + gap: 1rem; + width: 100%; +} + +.input-group { + display: flex; + flex-direction: column; + gap: 0.5rem; + + label { + font-size: 0.875rem; + font-weight: 500; + color: var(--gray-700); + } +} + +.input-button-group { + display: flex; + justify-content: space-between; + gap: 0.5rem; + + .input { + width: 120%; + } +} + +.error { + color: $color-orange-700; + font-size: 0.8rem; +} + +.buttons { + display: flex; + gap: 36px; + margin-top: 1rem; + justify-content: center; + margin-top: 40px; + + button { + flex: 0.2; + } +} From bbd3590325e7e68329d221034b0d0f0f6f172c2f Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 6 Dec 2024 01:33:34 +0900 Subject: [PATCH 603/648] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=20=EA=B4=80=EB=A0=A8=20api=20=EC=97=B0=EA=B2=B0=20(#2?= =?UTF-8?q?72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/api/find-credentials.ts | 48 ++++++++++ shared/hooks/custom/use-auth.ts | 1 - shared/hooks/custom/use-reset-password.ts | 105 +++++++++++++++++++++ shared/hooks/query/use-find-credentials.ts | 25 +++++ 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 shared/api/find-credentials.ts create mode 100644 shared/hooks/custom/use-reset-password.ts create mode 100644 shared/hooks/query/use-find-credentials.ts diff --git a/shared/api/find-credentials.ts b/shared/api/find-credentials.ts new file mode 100644 index 00000000..58ee82cb --- /dev/null +++ b/shared/api/find-credentials.ts @@ -0,0 +1,48 @@ +import axios from 'axios' + +interface FindEmailResponseModel { + isSuccess: boolean + message: string + result: { + isFound: boolean + email: string + } + code: number +} + +interface AuthenticateResponseModel { + isSuccess: boolean + message: string + result: Record<string, unknown> + code: number +} + +interface ResetPasswordResponseModel { + isSuccess: boolean + message: string + result: Record<string, unknown> + code: number +} + +export const findCredentialsAPI = { + findEmail: async (phone: string) => { + const response = await axios.get<FindEmailResponseModel>(`/api/users/email?phone=${phone}`) + return response.data + }, + + authenticate: async (email: string, code: string) => { + const response = await axios.post<AuthenticateResponseModel>('/api/users/authenticate', { + email, + code, + }) + return response.data + }, + + resetPassword: async (email: string, password: string) => { + const response = await axios.patch<ResetPasswordResponseModel>('/api/users/reissue/password', { + email, + password, + }) + return response.data + }, +} diff --git a/shared/hooks/custom/use-auth.ts b/shared/hooks/custom/use-auth.ts index a5f9d87a..7260b911 100644 --- a/shared/hooks/custom/use-auth.ts +++ b/shared/hooks/custom/use-auth.ts @@ -11,7 +11,6 @@ import { isAuthRequiredPath } from '@/shared/utils/auth-path' import { getTimeUntilExpiry } from '@/shared/utils/token-utils' export const useAuth = () => { - const { user } = useAuthStore() const { mutate: logout } = useLogoutMutation() const pathname = usePathname() diff --git a/shared/hooks/custom/use-reset-password.ts b/shared/hooks/custom/use-reset-password.ts new file mode 100644 index 00000000..d595401d --- /dev/null +++ b/shared/hooks/custom/use-reset-password.ts @@ -0,0 +1,105 @@ +import { useState } from 'react' + +import axios from 'axios' + +import { useFindCredentials } from '@/shared/hooks/query/use-find-credentials' + +interface UseResetPasswordProps { + onSuccess: () => void + onError: (message: string) => void +} + +export const useResetPassword = ({ onSuccess, onError }: UseResetPasswordProps) => { + const [step, setStep] = useState(1) + const [formData, setFormData] = useState({ + email: '', + code: '', + password: '', + passwordConfirm: '', + }) + + const { authenticateMutation, resetPasswordMutation } = useFindCredentials() + + const handleVerifyEmail = async (e?: React.FormEvent) => { + e?.preventDefault() + try { + const response = await authenticateMutation.mutateAsync({ + email: formData.email, + code: formData.code, + }) + if (response.isSuccess) { + setStep(2) + onError('') + } else { + onError(response.message || '인증에 실패했습니다.') + } + } catch { + onError('인증에 실패했습니다.') + } + } + + const handlePasswordReset = async (e: React.FormEvent) => { + e.preventDefault() + if (formData.password !== formData.passwordConfirm) { + onError('비밀번호가 일치하지 않습니다.') + return + } + try { + const response = await resetPasswordMutation.mutateAsync({ + email: formData.email, + password: formData.password, + }) + if (response.isSuccess) { + onSuccess() + } else { + onError(response.message || '비밀번호 재설정에 실패했습니다.') + } + } catch { + onError('비밀번호 재설정에 실패했습니다.') + } + } + + const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const { name, value } = e.target + setFormData((prev) => ({ + ...prev, + [name]: value, + })) + } + + const handleRequestCode = async () => { + try { + const response = await axios.get(`/api/users/authenticate?email=${formData.email}`) + if (response.data.isSuccess) { + onError('') + } else { + onError(response.data.message || '인증코드 발송에 실패했습니다.') + } + } catch { + onError('인증코드 발송에 실패했습니다.') + } + } + + const isPending = authenticateMutation.isPending || resetPasswordMutation.isPending + + const resetForm = () => { + setFormData({ + email: '', + code: '', + password: '', + passwordConfirm: '', + }) + } + + return { + formData, + step, + isPending, + handleInputChange, + handleRequestCode, + handleVerifyEmail, + handlePasswordReset, + setStep, + resetForm, + } +} diff --git a/shared/hooks/query/use-find-credentials.ts b/shared/hooks/query/use-find-credentials.ts new file mode 100644 index 00000000..f68f26c3 --- /dev/null +++ b/shared/hooks/query/use-find-credentials.ts @@ -0,0 +1,25 @@ +import { useMutation } from '@tanstack/react-query' + +import { findCredentialsAPI } from '@/shared/api/find-credentials' + +export const useFindCredentials = () => { + const findEmailMutation = useMutation({ + mutationFn: (phone: string) => findCredentialsAPI.findEmail(phone), + }) + + const authenticateMutation = useMutation({ + mutationFn: ({ email, code }: { email: string; code: string }) => + findCredentialsAPI.authenticate(email, code), + }) + + const resetPasswordMutation = useMutation({ + mutationFn: ({ email, password }: { email: string; password: string }) => + findCredentialsAPI.resetPassword(email, password), + }) + + return { + findEmailMutation, + authenticateMutation, + resetPasswordMutation, + } +} From 2976c0595f34e6bd12b53b72ab418f5f49195eac Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 02:12:47 +0900 Subject: [PATCH 604/648] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20g?= =?UTF-8?q?et=EC=9A=94=EC=B2=AD,=20=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20get=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_api/get-profile.ts | 31 ++++ .../my/_hooks/query/use-get-profile.ts | 12 ++ .../my/profile/_ui/user-info/index.tsx | 150 +++++++++++++++--- .../my/profile/_ui/user-info/types.ts | 26 +++ .../my/profile/_ui/user-info/utils.ts | 61 +++++++ .../notices/[noticeId]/detail/page.tsx | 6 +- .../notices/_api/get-notice-detail.ts | 31 ++++ .../notices/_hooks/use-notice-detail.ts | 12 ++ .../notices/_ui/notice-detail/index.tsx | 31 ++-- 9 files changed, 316 insertions(+), 44 deletions(-) create mode 100644 app/(dashboard)/my/_api/get-profile.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-get-profile.ts create mode 100644 app/(dashboard)/my/profile/_ui/user-info/types.ts create mode 100644 app/(dashboard)/my/profile/_ui/user-info/utils.ts create mode 100644 app/(landing)/notices/_api/get-notice-detail.ts create mode 100644 app/(landing)/notices/_hooks/use-notice-detail.ts diff --git a/app/(dashboard)/my/_api/get-profile.ts b/app/(dashboard)/my/_api/get-profile.ts new file mode 100644 index 00000000..ac16d903 --- /dev/null +++ b/app/(dashboard)/my/_api/get-profile.ts @@ -0,0 +1,31 @@ +import axiosInstance from '@/shared/api/axios' + +interface ProfileResponseModel { + isSuccess: boolean + message: string + result: { + userId: number + userName: string + email: string + imageUrl: string | null + nickname: string + phone: string + infoAgreement: boolean + role: string + } +} + +export const getProfile = async (): Promise<ProfileResponseModel['result']> => { + try { + const response = await axiosInstance.get<ProfileResponseModel>('/api/users/mypage/profile') + + if (response.data.isSuccess) { + return response.data.result + } else { + throw new Error(response.data.message || '요청 실패') + } + } catch (err) { + console.error(err) + throw new Error('해당 회원의 정보를 찾을 수 없습니다.') + } +} diff --git a/app/(dashboard)/my/_hooks/query/use-get-profile.ts b/app/(dashboard)/my/_hooks/query/use-get-profile.ts new file mode 100644 index 00000000..67ee9ebc --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-get-profile.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import { getProfile } from './../../_api/get-profile' + +const useGetProfile = () => { + return useQuery({ + queryKey: ['userProfile'], + queryFn: getProfile, + }) +} + +export default useGetProfile diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index f1bb3fb2..cdc0edbc 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -1,34 +1,141 @@ 'use client' +import { ChangeEvent, useState } from 'react' + import { useRouter } from 'next/navigation' +import { SIGNUP_ERROR_MESSAGES } from '@/app/(landing)/signup/_constants/signup' import { CameraIcon } from '@/public/icons' import classNames from 'classnames/bind' +import { checkNicknameDuplicate, checkPhoneDuplicate } from '@/shared/api/check-duplicate' import { PATH } from '@/shared/constants/path' import Avatar from '@/shared/ui/avatar' import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import { LinkButton } from '@/shared/ui/link-button' +import Spinner from '@/shared/ui/spinner' +import useGetProfile from '../../../_hooks/query/use-get-profile' import styles from './styles.module.scss' +import { ProfileFormErrorsModel, ProfileFormModel, ProfileFormStateModel } from './types' +import { validateProfileForm } from './utils' const cx = classNames.bind(styles) +const initialForm: ProfileFormModel = { + name: '', + nickname: '', + email: '', + password: '', + passwordConfirm: '', + phone: '', + birthday: '', +} + +const initialFormState = { + isNicknameVerified: false, + isPhoneVerified: false, +} + interface Props { isEditable?: boolean } const UserInfo = ({ isEditable = false }: Props) => { const router = useRouter() - const handleChange = () => {} - const handleDelete = () => {} - const handleNicknameConfirm = () => {} - const handlePhoneConfirm = () => {} + const [form, setForm] = useState<ProfileFormModel>(initialForm) + const [formState, setFormState] = useState<ProfileFormStateModel>(initialFormState) + const [errors, setErrors] = useState<ProfileFormErrorsModel>({}) + const [isValidated, setIsValidated] = useState(false) + + const { data: profile, isLoading } = useGetProfile() + + const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => { + const { name, value } = e.target + setForm((prev) => ({ ...prev, [name]: value })) + + if (isValidated) { + setErrors((prev) => ({ + ...prev, + [name]: null, + })) + } + + if (name === 'nickname') { + setFormState((prev) => ({ ...prev, isNicknameVerified: false })) + setErrors((prev) => ({ ...prev, nickname: null })) + } + if (name === 'phone') { + setFormState((prev) => ({ ...prev, isPhoneVerified: false })) + setErrors((prev) => ({ ...prev, phone: null })) + } + } + + const handleNicknameCheck = async () => { + setFormState((prev) => ({ ...prev, isNicknameVerified: false })) + + try { + const response = await checkNicknameDuplicate(form.nickname) + if (response.result.isAvailable) { + setFormState((prev) => ({ ...prev, isNicknameVerified: true })) + if (errors.nickname) { + setErrors((prev) => ({ ...prev, nickname: null })) + } + } else { + setErrors((prev) => ({ ...prev, nickname: SIGNUP_ERROR_MESSAGES.NICKNAME_DUPLICATED })) + } + } catch (err) { + console.error('닉네임 중복 확인 실패:', err) + setErrors((prev) => ({ ...prev, nickname: SIGNUP_ERROR_MESSAGES.NICKNAME_CHECK_FAILED })) + } + } + const handlePhoneCheck = async () => { + try { + const response = await checkPhoneDuplicate(form.phone) + + if (response.result.isAvailable) { + setFormState((prev) => ({ ...prev, isPhoneVerified: true })) + if (errors.phone) { + setErrors((prev) => ({ ...prev, phone: null })) + } + } else { + setErrors((prev) => ({ ...prev, phone: SIGNUP_ERROR_MESSAGES.PHONE_DUPLICATED })) + } + } catch (err) { + console.error('휴대폰 번호 중복 확인 실패:', err) + setErrors((prev) => ({ ...prev, phone: SIGNUP_ERROR_MESSAGES.PHONE_CHECK_FAILED })) + } + } + + const handleImageChange = () => {} + const handleImageDelete = () => {} const handleBack = () => { router.back() } - const handleSave = () => {} + + const handleFormSubmit = async () => { + const formErrors = validateProfileForm( + form, + formState.isNicknameVerified, + formState.isPhoneVerified + ) + setIsValidated(true) + + if (Object.keys(formErrors).length > 0) { + setErrors(formErrors) + return + } + } + + if (isLoading) { + return <Spinner /> + } + + if (!profile) { + return null + } + return ( <div className={cx('container')}> <p className={cx('title')}>개인 정보</p> @@ -40,10 +147,10 @@ const UserInfo = ({ isEditable = false }: Props) => { <div className={cx('avatar-wrapper')}> <Avatar size="xxlarge" /> <div className={cx('camera-wrapper')}> - <CameraIcon className={cx('camera-icon')} onClick={handleChange} /> + <CameraIcon className={cx('camera-icon')} onClick={handleImageChange} /> </div> </div> - {isEditable && <Button onClick={handleDelete}>프로필 사진 삭제</Button>} + {isEditable && <Button onClick={handleImageDelete}>프로필 사진 삭제</Button>} </div> <div className={cx('right-wrapper')}> @@ -57,7 +164,7 @@ const UserInfo = ({ isEditable = false }: Props) => { <Input id="name" name="name" - value={'고양이'} + value={form.name} inputSize="compact" className={cx('input')} isWhiteDisabled={!isEditable} @@ -70,7 +177,7 @@ const UserInfo = ({ isEditable = false }: Props) => { <p className={cx('title')}>이메일</p> <Input inputSize="compact" - value={'hello@example.com'} + value={form.email} className={cx('input')} isWhiteDisabled={!isEditable} disabled={isEditable} @@ -82,14 +189,14 @@ const UserInfo = ({ isEditable = false }: Props) => { <Input id="phone" name="phone" - value={'01012345678'} - onChange={() => {}} + value={form.phone} + onChange={handleInputChange} className={cx('input')} inputSize="compact" isWhiteDisabled={!isEditable} /> - {isEditable && <Button onClick={handlePhoneConfirm}>확인</Button>} + {isEditable && <Button onClick={handlePhoneCheck}>확인</Button>} </div> </div> </div> @@ -99,7 +206,7 @@ const UserInfo = ({ isEditable = false }: Props) => { <p className={cx('title')}>생년월일</p> <Input inputSize="compact" - value={'2004.05.04'} + value={form.birthday} className={cx('input')} isWhiteDisabled={!isEditable} disabled={isEditable} @@ -112,13 +219,13 @@ const UserInfo = ({ isEditable = false }: Props) => { id="nickname" name="nickname" inputSize="compact" - value={'고양이귀여워'} - onChange={() => {}} + value={form.nickname} + onChange={handleInputChange} className={cx('input')} isWhiteDisabled={!isEditable} /> - {isEditable && <Button onClick={handleNicknameConfirm}>확인</Button>} + {isEditable && <Button onClick={handleNicknameCheck}>확인</Button>} </div> </div> </div> @@ -132,10 +239,11 @@ const UserInfo = ({ isEditable = false }: Props) => { name="password" type="password" inputSize="compact" - onChange={() => {}} + value={form.password} + onChange={handleInputChange} placeholder="비밀번호를 입력하세요" className={cx('input')} - errorMessage={''} + errorMessage={errors.password} /> </div> <div> @@ -145,7 +253,8 @@ const UserInfo = ({ isEditable = false }: Props) => { id="passwordConfirm" name="passwordConfirm" type="password" - onChange={() => {}} + value={form.passwordConfirm} + onChange={handleInputChange} placeholder="한 번 더 입력하세요" className={cx('input')} inputSize="compact" @@ -168,7 +277,7 @@ const UserInfo = ({ isEditable = false }: Props) => { <Button className={cx('left-button')} onClick={handleBack}> 뒤로가기 </Button> - <Button className={cx('right-button')} variant="filled" onClick={handleSave}> + <Button className={cx('right-button')} variant="filled" onClick={handleFormSubmit}> 저장하기 </Button> </div> @@ -177,4 +286,5 @@ const UserInfo = ({ isEditable = false }: Props) => { </div> ) } + export default UserInfo diff --git a/app/(dashboard)/my/profile/_ui/user-info/types.ts b/app/(dashboard)/my/profile/_ui/user-info/types.ts new file mode 100644 index 00000000..dd382e11 --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-info/types.ts @@ -0,0 +1,26 @@ +import { SIGNUP_ERROR_MESSAGES } from '@/app/(landing)/signup/_constants/signup' + +export type ProfileErrorMessageType = + (typeof SIGNUP_ERROR_MESSAGES)[keyof typeof SIGNUP_ERROR_MESSAGES] + +export interface ProfileFormModel { + name: string + nickname: string + email: string + password: string + passwordConfirm: string + phone: string + birthday: string +} + +export interface ProfileFormStateModel { + isNicknameVerified: boolean + isPhoneVerified: boolean +} + +export interface ProfileFormErrorsModel { + nickname?: ProfileErrorMessageType | null + password?: ProfileErrorMessageType | null + passwordConfirm?: ProfileErrorMessageType | null + phone?: ProfileErrorMessageType | null +} diff --git a/app/(dashboard)/my/profile/_ui/user-info/utils.ts b/app/(dashboard)/my/profile/_ui/user-info/utils.ts new file mode 100644 index 00000000..3d5e4dc0 --- /dev/null +++ b/app/(dashboard)/my/profile/_ui/user-info/utils.ts @@ -0,0 +1,61 @@ +import { SIGNUP_ERROR_MESSAGES } from '@/app/(landing)/signup/_constants/signup' + +import { isValidNickname, isValidPassword, isValidPhone } from '@/shared/utils/validation' + +import { ProfileErrorMessageType, ProfileFormModel } from './types' + +type ValidationFieldType = keyof typeof SIGNUP_ERROR_MESSAGES + +const validateField = (field: string, value: string): ProfileErrorMessageType | null => { + if (!value.trim()) { + return SIGNUP_ERROR_MESSAGES[`${field}_REQUIRED` as ValidationFieldType] || null + } + + switch (field) { + case 'NICKNAME': + return isValidNickname(value) ? null : SIGNUP_ERROR_MESSAGES.NICKNAME_LENGTH + case 'PASSWORD': + return isValidPassword(value) ? null : SIGNUP_ERROR_MESSAGES.PASSWORD_INVALID + case 'PHONE': + return isValidPhone(value) ? null : SIGNUP_ERROR_MESSAGES.PHONE_INVALID + default: + return null + } +} + +export const validateProfileForm = ( + form: ProfileFormModel, + isNicknameVerified: boolean, + isPhoneVerified: boolean +): Record<string, ProfileErrorMessageType> => { + const errors: Record<string, ProfileErrorMessageType> = {} + + const nicknameError = validateField('NICKNAME', form.nickname) + if (nicknameError) errors.nickname = nicknameError + else if (!isNicknameVerified) errors.nickname = SIGNUP_ERROR_MESSAGES.NICKNAME_CHECK_REQUIRED + + const passwordError = validateField('PASSWORD', form.password) + if (passwordError) errors.password = passwordError + // 비밀번호 입력할때 빈값이면 아무것도 안되도록 하는 로직 넣어야하는데 어케함... + // const handlePasswordChange = () => { + // const payload = password.trim() === '' ? null : password; + + const passwordMatchError = validatePasswordMatch(form.password, form.passwordConfirm) + if (passwordMatchError) errors.passwordConfirm = passwordMatchError + + const phoneError = validateField('PHONE', form.phone) + if (phoneError) errors.phone = phoneError + if (!isPhoneVerified) errors.phone = SIGNUP_ERROR_MESSAGES.PHONE_CHECK_REQUIRED + + return errors +} + +export const validatePasswordMatch = ( + password: string, + confirmPassword: string +): ProfileErrorMessageType | null => { + if (!confirmPassword.trim()) { + return SIGNUP_ERROR_MESSAGES.PASSWORD_REQUIRED + } + return password === confirmPassword ? null : SIGNUP_ERROR_MESSAGES.PASSWORD_MISMATCH +} diff --git a/app/(landing)/notices/[noticeId]/detail/page.tsx b/app/(landing)/notices/[noticeId]/detail/page.tsx index f06aa3a2..567977fa 100644 --- a/app/(landing)/notices/[noticeId]/detail/page.tsx +++ b/app/(landing)/notices/[noticeId]/detail/page.tsx @@ -1,7 +1,9 @@ +'use client' + import NoticeDetail from '../../_ui/notice-detail' -const NoticeDetailPage = () => { - return <NoticeDetail /> +const NoticeDetailPage = ({ params }: { params: { noticeId: string } }) => { + return <NoticeDetail noticeId={params.noticeId} /> } export default NoticeDetailPage diff --git a/app/(landing)/notices/_api/get-notice-detail.ts b/app/(landing)/notices/_api/get-notice-detail.ts new file mode 100644 index 00000000..4a0af231 --- /dev/null +++ b/app/(landing)/notices/_api/get-notice-detail.ts @@ -0,0 +1,31 @@ +import axiosInstance from '@/shared/api/axios' + +interface NoticeResponseModel { + isSuccess: true + message: string + data: { + noticeId: string + user: { + id: number + nickname: string + } + title: string + content: string + createdAt: string + } +} + +export const getNoticeDetail = async (noticeId: string): Promise<NoticeResponseModel['data']> => { + try { + const response = await axiosInstance.get<NoticeResponseModel>(`/api/notices/${noticeId}`) + + if (response.data.isSuccess) { + return response.data.data + } else { + throw new Error(response.data.message || '요청 실패') + } + } catch (err) { + console.error(err) + throw new Error('해당 회원의 정보를 찾을 수 없습니다.') + } +} diff --git a/app/(landing)/notices/_hooks/use-notice-detail.ts b/app/(landing)/notices/_hooks/use-notice-detail.ts new file mode 100644 index 00000000..ea799cff --- /dev/null +++ b/app/(landing)/notices/_hooks/use-notice-detail.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import { getNoticeDetail } from '../_api/get-notice-detail' + +const useNoticeDetail = (noticeId: string) => { + return useQuery({ + queryKey: ['noticeDetail', noticeId], + queryFn: () => getNoticeDetail(noticeId), + }) +} + +export default useNoticeDetail diff --git a/app/(landing)/notices/_ui/notice-detail/index.tsx b/app/(landing)/notices/_ui/notice-detail/index.tsx index c7f34acb..54cc2fed 100644 --- a/app/(landing)/notices/_ui/notice-detail/index.tsx +++ b/app/(landing)/notices/_ui/notice-detail/index.tsx @@ -3,50 +3,37 @@ import { DownloadIcon } from '@/public/icons' import classNames from 'classnames/bind' +import useNoticeDetail from '../../_hooks/use-notice-detail' import styles from './styles.module.scss' const cx = classNames.bind(styles) -const NoticeDetail = () => { - // TODO: 임시데이터 제거 - const data = { - title: '공지사항 제목입니다.', - createdAd: '2024-12-05', - content: '이것은 공지사항의 내용입니다. 자세한 사항은 첨부 파일을 확인하세요.', - attachments: [ - { title: '파일제목1.pdf', url: '/path/to/file1.pdf' }, - { title: '파일제목2.pdf', url: '/path/to/file2.pdf' }, - { title: '파일제목3.pdf', url: '/path/to/file3.pdf' }, - { title: '파일제목4.pdf', url: '/path/to/file4.pdf' }, - { title: '파일제목5.pdf', url: '/path/to/file5.pdf' }, - ], - } - +const NoticeDetail = ({ noticeId }: { noticeId: string }) => { const handleSave = () => {} - + const { data: notice, isLoading } = useNoticeDetail(noticeId) return ( <div className={cx('container')}> <div className={cx('title-wrapper')}> - <div className={cx('title')}>{data.title}</div> - <div className={cx('date')}>{data.createdAd}</div> + {/* <div className={cx('title')}>{data.title}</div> + <div className={cx('date')}>{data.createdAd}</div> */} </div> <div className={cx('top-line')}></div> - <div className={cx('content')}>{data.content}</div> + {/* <div className={cx('content')}>{data.content}</div> */} <div className={cx('bottom-line')}></div> <div className={cx('attach-file')}> <div className={cx('file-left-wrapper')}> - <p className={cx('file-count')}>첨부 {data.attachments.length}개</p> + {/* <p className={cx('file-count')}>첨부 {data.attachments.length}개</p> */} </div> <div className={cx('file-right-wrapper')}> - {data.attachments.map((file, index) => ( + {/* {data.attachments.map((file, index) => ( <div className={cx('file-wrapper')} key={index}> <div className={cx('file-title')} onClick={() => handleSave}> {file.title} </div> <DownloadIcon className={cx('file-download')} onClick={() => handleSave} /> </div> - ))} + ))} */} </div> </div> </div> From a3830b3da3dbe3a8b99e37a60d67d5968bde56fe Mon Sep 17 00:00:00 2001 From: James <dawn147@snu.ac.kr> Date: Fri, 6 Dec 2024 02:34:59 +0900 Subject: [PATCH 605/648] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=ED=95=A0=20=EB=95=8C=20=EC=9C=A0=EC=A0=80=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=20=EA=B6=8C=ED=95=9C=20=EC=97=86=EC=9D=8C=20=EB=AC=B8=EC=A0=9C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0=20(#272)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signin/page.tsx | 2 + shared/hooks/custom/use-reset-password.ts | 45 ++++++++++++++--------- shared/hooks/query/auth-queries.ts | 9 +++-- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/app/(landing)/signin/page.tsx b/app/(landing)/signin/page.tsx index a791cb39..8ebabca1 100644 --- a/app/(landing)/signin/page.tsx +++ b/app/(landing)/signin/page.tsx @@ -195,6 +195,7 @@ const SignInPage = () => { <button onClick={(e) => handleFindButtonClick(e, handleFindEmailOpen)} className={cx('find-button')} + type="button" > 아이디 찾기 </button> @@ -202,6 +203,7 @@ const SignInPage = () => { <button onClick={(e) => handleFindButtonClick(e, handleFindPasswordOpen)} className={cx('find-button')} + type="button" > 비밀번호 찾기 </button> diff --git a/shared/hooks/custom/use-reset-password.ts b/shared/hooks/custom/use-reset-password.ts index d595401d..4ab84642 100644 --- a/shared/hooks/custom/use-reset-password.ts +++ b/shared/hooks/custom/use-reset-password.ts @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useCallback, useState } from 'react' import axios from 'axios' @@ -20,6 +20,17 @@ export const useResetPassword = ({ onSuccess, onError }: UseResetPasswordProps) const { authenticateMutation, resetPasswordMutation } = useFindCredentials() + const memoizedOnError = useCallback( + (message: string) => { + onError(message) + }, + [onError] + ) + + const memoizedOnSuccess = useCallback(() => { + onSuccess() + }, [onSuccess]) + const handleVerifyEmail = async (e?: React.FormEvent) => { e?.preventDefault() try { @@ -29,19 +40,19 @@ export const useResetPassword = ({ onSuccess, onError }: UseResetPasswordProps) }) if (response.isSuccess) { setStep(2) - onError('') + memoizedOnError('') } else { - onError(response.message || '인증에 실패했습니다.') + memoizedOnError(response.message || '인증에 실패했습니다.') } } catch { - onError('인증에 실패했습니다.') + memoizedOnError('인증에 실패했습니다.') } } const handlePasswordReset = async (e: React.FormEvent) => { e.preventDefault() if (formData.password !== formData.passwordConfirm) { - onError('비밀번호가 일치하지 않습니다.') + memoizedOnError('비밀번호가 일치하지 않습니다.') return } try { @@ -50,46 +61,46 @@ export const useResetPassword = ({ onSuccess, onError }: UseResetPasswordProps) password: formData.password, }) if (response.isSuccess) { - onSuccess() + memoizedOnSuccess() } else { - onError(response.message || '비밀번호 재설정에 실패했습니다.') + memoizedOnError(response.message || '비밀번호 재설정에 실패했습니다.') } } catch { - onError('비밀번호 재설정에 실패했습니다.') + memoizedOnError('비밀번호 재설정에 실패했습니다.') } } - const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target setFormData((prev) => ({ ...prev, [name]: value, })) - } + }, []) - const handleRequestCode = async () => { + const handleRequestCode = useCallback(async () => { try { const response = await axios.get(`/api/users/authenticate?email=${formData.email}`) if (response.data.isSuccess) { - onError('') + memoizedOnError('') } else { - onError(response.data.message || '인증코드 발송에 실패했습니다.') + memoizedOnError(response.data.message || '인증코드 발송에 실패했습니다.') } } catch { - onError('인증코드 발송에 실패했습니다.') + memoizedOnError('인증코드 발송에 실패했습니다.') } - } + }, [formData.email, memoizedOnError]) const isPending = authenticateMutation.isPending || resetPasswordMutation.isPending - const resetForm = () => { + const resetForm = useCallback(() => { setFormData({ email: '', code: '', password: '', passwordConfirm: '', }) - } + }, []) return { formData, diff --git a/shared/hooks/query/auth-queries.ts b/shared/hooks/query/auth-queries.ts index 5a361f6f..7964de6c 100644 --- a/shared/hooks/query/auth-queries.ts +++ b/shared/hooks/query/auth-queries.ts @@ -1,6 +1,7 @@ import { useRouter } from 'next/navigation' import { useMutation } from '@tanstack/react-query' +import axios from 'axios' import { login, logout, refreshAccessToken } from '@/shared/api/auth' import axiosInstance from '@/shared/api/axios' @@ -17,14 +18,16 @@ export const useLoginMutation = () => { if (!accessToken) { throw new Error('No access token received') } - try { const userEmail = getEmailFromToken(accessToken) if (!userEmail) { throw new Error('Invalid token') } - - const userResponse = await axiosInstance.get(`/api/users/mypage/profile?email=${userEmail}`) + const userResponse = await axios.get(`/api/users/mypage/profile?email=${userEmail}`, { + headers: { + 'access-token': `Bearer ${accessToken}`, + }, + }) if (!userResponse.data.isSuccess) { throw new Error('Failed to fetch user profile') } From 85e057579bee4b7af7f420ab0b69b4bfaaf1e04b Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 02:37:57 +0900 Subject: [PATCH 606/648] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=86=A0=ED=81=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_api/get-profile.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/my/_api/get-profile.ts b/app/(dashboard)/my/_api/get-profile.ts index ac16d903..aa449cdb 100644 --- a/app/(dashboard)/my/_api/get-profile.ts +++ b/app/(dashboard)/my/_api/get-profile.ts @@ -1,4 +1,5 @@ import axiosInstance from '@/shared/api/axios' +import { getAccessToken } from '@/shared/lib/auth-tokens' interface ProfileResponseModel { isSuccess: boolean @@ -17,7 +18,13 @@ interface ProfileResponseModel { export const getProfile = async (): Promise<ProfileResponseModel['result']> => { try { - const response = await axiosInstance.get<ProfileResponseModel>('/api/users/mypage/profile') + const token = getAccessToken() + + const response = await axiosInstance.get<ProfileResponseModel>('/api/users/mypage/profile', { + headers: { + Authorization: token ? `Bearer ${token}` : '', + }, + }) if (response.data.isSuccess) { return response.data.result From 4eb6e6fe819b171e92980d0aa515488dc2aa1b48 Mon Sep 17 00:00:00 2001 From: deun <devdeun@gmail.com> Date: Fri, 6 Dec 2024 02:41:02 +0900 Subject: [PATCH 607/648] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=99=84=EB=A3=8C=20=EB=A9=94=EC=8B=9C=EC=A7=80?= =?UTF-8?q?=EA=B0=80=20=EB=B0=94=EB=A1=9C=20=EB=9C=A8=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0=20(#287)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(landing)/signup/complete/page.tsx | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/(landing)/signup/complete/page.tsx b/app/(landing)/signup/complete/page.tsx index e25278ca..a9136c98 100644 --- a/app/(landing)/signup/complete/page.tsx +++ b/app/(landing)/signup/complete/page.tsx @@ -1,8 +1,11 @@ 'use client' +import { useEffect, useState } from 'react' + import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' +import { UserType } from '@/shared/types/auth' import { LinkButton } from '@/shared/ui/link-button' import { getNicknameCookie, getUserTypeCookie } from '../_lib/cookies' @@ -13,17 +16,25 @@ import styles from './page.module.scss' const cx = classNames.bind(styles) const CompletePage = () => { - const userType = getUserTypeCookie() - const nickname = getNicknameCookie() - - if (!userType || !nickname) { + const [userData, setUserData] = useState<{ + userType: UserType | undefined + nickname: string | undefined + }>({ userType: undefined, nickname: undefined }) + + useEffect(() => { + const userType = getUserTypeCookie() + const nickname = getNicknameCookie() + setUserData({ userType, nickname }) + }, []) + + if (!userData.userType || !userData.nickname) { return null } return ( <> <Step /> - <SignupCompleteMessage nickname={nickname} userType={userType} /> + <SignupCompleteMessage nickname={userData.nickname} userType={userData.userType} /> <div className={cx('button-wrapper')}> <LinkButton href={PATH.SIGN_IN} variant="filled"> 로그인하기 From 23d119650317a9229863f6acdb9beaa3a00d9803 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 03:40:20 +0900 Subject: [PATCH 608/648] =?UTF-8?q?feat:=20=EB=82=B4=EC=A0=84=EB=9E=B5?= =?UTF-8?q?=EC=9D=BC=EB=95=8C=20=EC=A0=84=EB=9E=B5=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80=20(#280)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-side-item/side-item.tsx | 16 ++++++++++++++++ .../_ui/details-side-item/styles.module.scss | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/_ui/details-side-item/side-item.tsx b/app/(dashboard)/_ui/details-side-item/side-item.tsx index f31738cd..265bac8e 100644 --- a/app/(dashboard)/_ui/details-side-item/side-item.tsx +++ b/app/(dashboard)/_ui/details-side-item/side-item.tsx @@ -1,5 +1,10 @@ +'use client' + +import { usePathname, useRouter } from 'next/navigation' + import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' import useModal from '@/shared/hooks/custom/use-modal' import { useAuthStore } from '@/shared/stores/use-auth-store' import Avatar from '@/shared/ui/avatar' @@ -41,6 +46,12 @@ const SideItem = ({ closeModal: guideCloseModal, } = useModal() const user = useAuthStore((state) => state.user) + const router = useRouter() + const path = usePathname() + + const handleRouter = () => { + router.push(`${PATH.MY_STRATEGIES}/manage/${strategyId}`) + } const isTrader = user?.role.includes('TRADER') @@ -59,6 +70,11 @@ const SideItem = ({ 문의하기 </Button> )} + {isMyStrategy && !path.includes('my') && ( + <Button onClick={handleRouter} size="small" style={{ height: '30px' }}> + 내 전략 관리하기 + </Button> + )} </> ) : ( <p>{formatNumber(data)}</p> diff --git a/app/(dashboard)/_ui/details-side-item/styles.module.scss b/app/(dashboard)/_ui/details-side-item/styles.module.scss index 9c126b9b..b4393c12 100644 --- a/app/(dashboard)/_ui/details-side-item/styles.module.scss +++ b/app/(dashboard)/_ui/details-side-item/styles.module.scss @@ -41,6 +41,6 @@ display: flex; align-items: center; p { - margin-left: 11px; + margin: 0 4px 0 11px; } } From 4e7a0702e9ae68da36db2782863c41fc7035b6b4 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 04:05:13 +0900 Subject: [PATCH 609/648] =?UTF-8?q?fix:=20params=20string=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#292)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/manage/[strategyId]/page.tsx | 15 +++-- .../strategies/[strategyId]/page.tsx | 63 +++++++++---------- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx index c1242056..0e14f21c 100644 --- a/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx +++ b/app/(dashboard)/my/strategies/manage/[strategyId]/page.tsx @@ -21,13 +21,15 @@ const cx = classNames.bind(styles) export type InformationType = { title: TitleType; data: string | number } | InformationModel[] -const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { +const StrategyManagePage = ({ params }: { params: { strategyId: string } }) => { + const strategyNumber = parseInt(params.strategyId) const { data: detailsInfoData } = useGetDetailsInformationData({ - strategyId: params.strategyId, + strategyId: strategyNumber, }) const { data: subscribeData } = useGetDetailsInformationData({ - strategyId: params.strategyId, + strategyId: strategyNumber, }) + const { detailsSideData, detailsInformationData } = detailsInfoData || {} const { detailsInformationData: subscribeInfo } = subscribeData || {} const hasDetailsSideData = detailsSideData?.map((data) => { @@ -47,11 +49,11 @@ const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { {detailsInformationData && ( <DetailsInformation information={detailsInformationData} - strategyId={params.strategyId} + strategyId={strategyNumber} type="my" /> )} - <AnalysisContainer type="my" strategyId={params.strategyId} /> + <AnalysisContainer type="my" strategyId={strategyNumber} /> <SideContainer hasButton={true}> {subscribeInfo && ( <SubscriberItem subscribers={subscribeInfo?.subscriptionCount} isMyStrategy={true} /> @@ -59,7 +61,7 @@ const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { {hasDetailsSideData?.[0] && detailsSideData?.map((data, idx) => ( <div key={`${data}_${idx}`}> - <DetailsSideItem information={data} strategyId={params.strategyId} /> + <DetailsSideItem information={data} strategyId={strategyNumber} /> </div> ))} </SideContainer> @@ -67,4 +69,5 @@ const StrategyManagePage = ({ params }: { params: { strategyId: number } }) => { </div> ) } + export default StrategyManagePage diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 6fc32788..91468c62 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -35,26 +35,19 @@ const DynamicSideSkeleton = dynamic(() => import('../../_ui/details-side-item/si export type InformationType = { title: TitleType; data: string | number } | InformationModel[] -const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { +const StrategyDetailPage = ({ params }: { params: { strategyId: string } }) => { + const strategyNumber = parseInt(params.strategyId) const user = useAuthStore((state) => state.user) const { isModalOpen, openModal, closeModal } = useModal() const { mutate } = useGetSubscribe() const { refetch, data } = useGetDetailsInformationData({ - strategyId: params.strategyId, + strategyId: strategyNumber, }) const { detailsSideData, detailsInformationData: information } = data || {} const handleSubscribe = () => { - mutate(params.strategyId, { - onSuccess: () => { - closeModal() - refetch() - }, - }) - } - const handleUnsubscribe = () => { - mutate(params.strategyId, { + mutate(strategyNumber, { onSuccess: () => { closeModal() refetch() @@ -73,45 +66,47 @@ const StrategyDetailPage = ({ params }: { params: { strategyId: number } }) => { <Suspense fallback={<DynamicSkeleton />}> {information && ( <> - <DetailsInformation information={information} strategyId={params.strategyId} /> - <AnalysisContainer strategyId={params.strategyId} /> - <ReviewContainer strategyId={params.strategyId} /> + <DetailsInformation information={information} strategyId={strategyNumber} /> + <AnalysisContainer strategyId={strategyNumber} /> + <ReviewContainer strategyId={strategyNumber} /> </> )} </Suspense> <SideContainer> <Suspense fallback={<DynamicSideSkeleton />}> {information && ( - <SubscriberItem - isMyStrategy={user?.nickname === information.nickname} - isSubscribed={information?.isSubscribed} - subscribers={information?.subscriptionCount} - onClick={openModal} - /> + <> + <SubscriberItem + isMyStrategy={user?.nickname === information.nickname} + isSubscribed={information?.isSubscribed} + subscribers={information?.subscriptionCount} + onClick={openModal} + /> + {hasDetailsSideData?.[0] && + detailsSideData?.map((data, idx) => ( + <div key={`${data}_${idx}`}> + <DetailsSideItem + strategyId={strategyNumber} + information={data} + isMyStrategy={user?.nickname === information.nickname} + strategyName={information.strategyName} + /> + </div> + ))} + </> )} - {information && - hasDetailsSideData?.[0] && - detailsSideData?.map((data, idx) => ( - <div key={`${data}_${idx}`}> - <DetailsSideItem - strategyId={params.strategyId} - information={data} - isMyStrategy={user?.nickname === information.nickname} - strategyName={information.strategyName} - /> - </div> - ))} </Suspense> </SideContainer> - {isModalOpen && information && ( + {information && ( <SubscribeCheckModal isSubscribing={information?.isSubscribed} isModalOpen={isModalOpen} - onCloseModal={handleUnsubscribe} + onCloseModal={closeModal} onChange={handleSubscribe} /> )} </> ) } + export default StrategyDetailPage From c8e6cc3b34bb98c075774896326902fa5976fed5 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 04:06:07 +0900 Subject: [PATCH 610/648] =?UTF-8?q?fix:=20=EA=B5=AC=EB=8F=85=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=ED=95=B8=EB=93=A4=EB=9F=AC=EC=88=98=EC=A0=95=20(#2?= =?UTF-8?q?92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/subscribe-check-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/ui/modal/subscribe-check-modal.tsx b/shared/ui/modal/subscribe-check-modal.tsx index ef80305b..c6cd0f18 100644 --- a/shared/ui/modal/subscribe-check-modal.tsx +++ b/shared/ui/modal/subscribe-check-modal.tsx @@ -38,7 +38,7 @@ const SubscribeCheckModal = ({ isModalOpen, isSubscribing, onCloseModal, onChang 구독한 전략은 나의 관심전략 <br /> 페이지에서 확인 가능합니다. </span> - <Button onClick={onCloseModal}>닫기</Button> + <Button onClick={onChange}>닫기</Button> </> )} </Modal> From 5ddd2233836b61b061ca5aa141085f25e3ed6405 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 6 Dec 2024 04:26:20 +0900 Subject: [PATCH 611/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20get=20API=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20(#247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/_ui/file-input/index.tsx | 31 ++++++++++++ app/admin/_ui/file-input/styles.module.scss | 44 +++++++++++++++++ app/admin/strategies/_api/get-strategies.ts | 26 +++++++--- .../_api/set-admin-strategies-table-body.tsx | 16 +++++++ .../_hooks/query/use-strategies-data.ts | 14 ++++-- .../_hooks/use-admin-strategies-page.ts | 35 ++++++++++---- app/admin/strategies/page.tsx | 32 +++++++------ app/admin/strategies/types.ts | 47 ++++++++----------- 8 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 app/admin/_ui/file-input/index.tsx create mode 100644 app/admin/_ui/file-input/styles.module.scss create mode 100644 app/admin/strategies/_api/set-admin-strategies-table-body.tsx diff --git a/app/admin/_ui/file-input/index.tsx b/app/admin/_ui/file-input/index.tsx new file mode 100644 index 00000000..78d8f630 --- /dev/null +++ b/app/admin/_ui/file-input/index.tsx @@ -0,0 +1,31 @@ +'use client' + +import { ComponentPropsWithoutRef } from 'react' + +import Image from 'next/image' + +import { FileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props extends ComponentPropsWithoutRef<'input'> { + accept?: string + preview?: string +} + +const FileInput = ({ preview, accept = '*', value, onChange, ...props }: Props) => { + return ( + <div className={cx('container')}> + {preview && ( + <Image width={24} height={24} src={preview} alt="Preview" className={cx('preview')} /> + )} + <input type="file" accept={accept} onChange={onChange} className={cx('input')} {...props} /> + <FileIcon className={cx('icon')} /> + </div> + ) +} + +export default FileInput diff --git a/app/admin/_ui/file-input/styles.module.scss b/app/admin/_ui/file-input/styles.module.scss new file mode 100644 index 00000000..2a888cb1 --- /dev/null +++ b/app/admin/_ui/file-input/styles.module.scss @@ -0,0 +1,44 @@ +.container { + position: relative; + display: inline-block; + width: 160px; + height: 40px; + border-radius: 4px; + border: 1px solid $color-gray-400; + color: $color-gray-400; + @include typo-c1; + + svg { + width: 24px; + } +} + +.input { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + z-index: 10; + + &:hover { + cursor: pointer; + } +} + +.preview { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); +} + +.icon { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + width: 24px; + color: $color-gray-500; +} diff --git a/app/admin/strategies/_api/get-strategies.ts b/app/admin/strategies/_api/get-strategies.ts index a893d2c2..4fb6eaa0 100644 --- a/app/admin/strategies/_api/get-strategies.ts +++ b/app/admin/strategies/_api/get-strategies.ts @@ -1,16 +1,28 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' -import { StrategiesResponseModel } from '../types' +import { StrategiesApprovalStateType, StrategiesResponseModel } from '../types' -const getStrategies = async () => { +interface ArgModel { + searchWord?: string + isApproved?: StrategiesApprovalStateType + page?: number + size?: number +} + +const getStrategies = async ({ searchWord, isApproved, page = 0, size = 10 }: ArgModel) => { try { - const res = await axios<StrategiesResponseModel>('/api/admin/strategies') + const res = await axiosInstance<StrategiesResponseModel>('/api/admin/strategies', { + params: { + searchWord, + isApproved, + page, + size, + }, + }) if (!res.data.isSuccess) throw new Error(res.data.message) - console.log('data', res.data.data) - - return res.data.data + return res.data.result } catch (err) { console.log('Error : ' + err) throw err diff --git a/app/admin/strategies/_api/set-admin-strategies-table-body.tsx b/app/admin/strategies/_api/set-admin-strategies-table-body.tsx new file mode 100644 index 00000000..4b8203c6 --- /dev/null +++ b/app/admin/strategies/_api/set-admin-strategies-table-body.tsx @@ -0,0 +1,16 @@ +import { StrategiesResponseModel } from '../types' + +const setAdminStrategiesTableBody = (data: StrategiesResponseModel['result']['content']) => + data.map((data, idx) => { + return [ + idx + 1, + data.createAt, + data.strategyName, + data.nickname, + data.isPublic, + data.isApproved, + 'button', + ] + }) + +export default setAdminStrategiesTableBody diff --git a/app/admin/strategies/_hooks/query/use-strategies-data.ts b/app/admin/strategies/_hooks/query/use-strategies-data.ts index d0f74156..6d05e33f 100644 --- a/app/admin/strategies/_hooks/query/use-strategies-data.ts +++ b/app/admin/strategies/_hooks/query/use-strategies-data.ts @@ -1,11 +1,19 @@ import { useQuery } from '@tanstack/react-query' import getStrategies from '../../_api/get-strategies' +import { StrategiesApprovalStateType } from '../../types' -const useStrategiesData = () => { +interface ArgModel { + searchWord?: string + isApproved?: StrategiesApprovalStateType + page?: number + size?: number +} + +const useStrategiesData = ({ searchWord, isApproved, page, size }: ArgModel) => { return useQuery({ - queryKey: ['adminStrategies'], - queryFn: () => getStrategies(), + queryKey: ['adminStrategies', { searchWord, isApproved, page, size }], + queryFn: () => getStrategies({ searchWord, isApproved, page, size }), }) } diff --git a/app/admin/strategies/_hooks/use-admin-strategies-page.ts b/app/admin/strategies/_hooks/use-admin-strategies-page.ts index d39b7b7a..14aa42e5 100644 --- a/app/admin/strategies/_hooks/use-admin-strategies-page.ts +++ b/app/admin/strategies/_hooks/use-admin-strategies-page.ts @@ -1,26 +1,43 @@ import { useState } from 'react' import { tabs } from '../constants' +import { AdminStrategiesTapType } from '../types' -const useAdminStrategiesPage = () => { - const [activeTab, setActiveTab] = useState(tabs[0].id) - const [inputValue, setInputValue] = useState('') +const useAdminQuestionsPage = () => { + const [activeTab, setActiveTab] = useState<AdminStrategiesTapType>('ALL') const [keyword, setKeyword] = useState('') + const initialSearchParams = { + searchWord: '', + } + const [searchParams, setSearchParams] = useState(initialSearchParams) + + const initializeSearchParams = () => { + setKeyword('') + setSearchParams(initialSearchParams) + } + + const searchWithKeyword = () => { + setSearchParams({ + searchWord: keyword, + }) + } - const setConditionAndKeyword = () => { - setKeyword(inputValue) + const onTabChange = (id: string) => { + initializeSearchParams() + setActiveTab(id as AdminStrategiesTapType) } return { tabs, activeTab, setActiveTab, - inputValue, - setInputValue, keyword, setKeyword, - setConditionAndKeyword, + searchParams, + searchWithKeyword, + onTabChange, + initializeSearchParams, } } -export default useAdminStrategiesPage +export default useAdminQuestionsPage diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index e8ee829e..7175fef4 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -10,7 +10,7 @@ import Title from '@/shared/ui/title' import AdminContentsHeader from '../_ui/admin-header' import AdminPostButton from '../_ui/admin-post-button' -import usePatchStrategyApproval from './_hooks/query/use-patch-strategy-approval' +import setAdminStrategiesTableBody from './_api/set-admin-strategies-table-body' import useStrategiesData from './_hooks/query/use-strategies-data' import useAdminStrategiesPage from './_hooks/use-admin-strategies-page' import styles from './page.module.scss' @@ -18,29 +18,36 @@ import styles from './page.module.scss' const cx = classNames.bind(styles) const AdminStrategyPage = () => { - const { tabs, activeTab, setActiveTab, inputValue, setInputValue } = useAdminStrategiesPage() + const { tabs, activeTab, keyword, setKeyword, searchParams, searchWithKeyword, onTabChange } = + useAdminStrategiesPage() - const { data } = useStrategiesData() - const { mutate } = usePatchStrategyApproval(1, false) + const { data, isLoading } = useStrategiesData({ + ...searchParams, + isApproved: activeTab === 'ALL' ? undefined : 'PENDING', + }) + // const { mutate } = usePatchStrategyApproval(1, false) + + if (!data || isLoading) return null return ( <> <Title label="전략 관리" className={cx('title')} /> <section className={cx('container')}> <AdminPostButton label="전략 등록하기" pathname="strategies" /> - <Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} /> + <Tabs tabs={tabs} activeTab={activeTab} onTabChange={onTabChange} /> <AdminContentsHeader Left={ <span> - 총 <span className={cx('color-primary-500')}>{3}</span>명 + 총 <span className={cx('color-primary-500')}>{data.totalElements}</span>명 </span> } Right={ <div> <SearchInput - value={inputValue} - onChange={(e) => setInputValue(e.target.value)} - // onSearchIconClick={setInputValue} + value={keyword} + placeholder="전략명을 입력하세요." + onChange={(e) => setKeyword(e.target.value)} + onSearchIconClick={searchWithKeyword} /> </div> } @@ -48,14 +55,11 @@ const AdminStrategyPage = () => { /> <VerticalTable tableHead={['No.', '날짜', '전략명', '닉네임', '공개여부', '승인여부', '']} - tableBody={ - // data?.content ? setTableBody(data.content) : - [[1, '2024.09.15', 'wjsfiraud', 'nkick', true, true, 'buttons']] - } + tableBody={setAdminStrategiesTableBody(data.content)} countPerPage={10} currentPage={1} /> - <Pagination currentPage={1} maxPage={3} onPageChange={() => {}} /> + <Pagination currentPage={data.page} maxPage={data.totalPages} onPageChange={() => {}} /> </section> </> ) diff --git a/app/admin/strategies/types.ts b/app/admin/strategies/types.ts index 5e9b3a66..f49eb1c2 100644 --- a/app/admin/strategies/types.ts +++ b/app/admin/strategies/types.ts @@ -1,34 +1,25 @@ import { APIResponseBaseModel } from '@/shared/types/response' +type StrategiesPublicStateType = 'PUBLIC' | 'PRIVATE' +export type AdminStrategiesTapType = 'ALL' | 'PENDING' +export type StrategiesApprovalStateType = 'APPROVED' | 'DENY' | 'PENDING' + export interface StrategiesResponseModel extends APIResponseBaseModel<boolean> { - data: { - subscriptionCount: number // 구독 수 - minimumInvestmentAmount: number // 최소 투자 금액 - initialInvestment: number // 투자 원금 - kpRatio: number - smScore: number - lastProfitDate: string // 최종 손익 입력 일자 - strategyRegisteredDate: string // 전략 등록일 - // [ - // { - // "stockGroupId": 1, - // "stockTypeId" : 12312, - // "stockIconUrl": "https://~~", // 종목 아이콘 이미지 경로 - // }, - // { - // "stockGroupId": 1, - // "stockTypeId" : 12312, - // "stockIconUrl": "https://~~", // 종목 아이콘 이미지 경로 - // } - // ] - tradeIconUrl: string // 매매 유형 이미지 경로 - strategyName: string // 전략명 - averageRating: number // 평균별점 - cumulativeProfitRate: number // 누적 수익률 - maxDrawdownRate: number // 최대 자본 인하율 - averageProfitRate: number // 평균 손익률 - profitFactor: number - winRate: number // 승률 (%) + result: { + content: Array<{ + strategyId: number + strategyName: string + nickname: string + isPublic: StrategiesPublicStateType + isApproved: StrategiesApprovalStateType + createAt: string + }> + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean } } From 6aa2b3ae6fb66185329e66b722afc4c9f194d34e Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 04:49:14 +0900 Subject: [PATCH 612/648] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_api/get-profile.ts | 10 ++------ .../my/profile/_ui/user-info/index.tsx | 24 +++++++++---------- .../notices/[noticeId]/detail/page.tsx | 3 ++- .../notices/_api/get-notice-detail.ts | 9 +++---- .../notices/_hooks/use-notice-detail.ts | 2 +- .../notices/_ui/notice-detail/index.tsx | 17 ++++++------- shared/utils/validation.ts | 2 +- 7 files changed, 32 insertions(+), 35 deletions(-) diff --git a/app/(dashboard)/my/_api/get-profile.ts b/app/(dashboard)/my/_api/get-profile.ts index aa449cdb..00b5913e 100644 --- a/app/(dashboard)/my/_api/get-profile.ts +++ b/app/(dashboard)/my/_api/get-profile.ts @@ -1,5 +1,4 @@ import axiosInstance from '@/shared/api/axios' -import { getAccessToken } from '@/shared/lib/auth-tokens' interface ProfileResponseModel { isSuccess: boolean @@ -13,18 +12,13 @@ interface ProfileResponseModel { phone: string infoAgreement: boolean role: string + birthday: string } } export const getProfile = async (): Promise<ProfileResponseModel['result']> => { try { - const token = getAccessToken() - - const response = await axiosInstance.get<ProfileResponseModel>('/api/users/mypage/profile', { - headers: { - Authorization: token ? `Bearer ${token}` : '', - }, - }) + const response = await axiosInstance.get<ProfileResponseModel>('/api/users/mypage/profile') if (response.data.isSuccess) { return response.data.result diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index cdc0edbc..94fb3d90 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -23,16 +23,6 @@ import { validateProfileForm } from './utils' const cx = classNames.bind(styles) -const initialForm: ProfileFormModel = { - name: '', - nickname: '', - email: '', - password: '', - passwordConfirm: '', - phone: '', - birthday: '', -} - const initialFormState = { isNicknameVerified: false, isPhoneVerified: false, @@ -44,13 +34,23 @@ interface Props { const UserInfo = ({ isEditable = false }: Props) => { const router = useRouter() + const { data: profile, isLoading } = useGetProfile() + + const initialForm: ProfileFormModel = { + name: profile?.userName || '', + nickname: profile?.nickname || '', + email: profile?.email || '', + password: '', + passwordConfirm: '', + phone: profile?.phone || '', + birthday: profile?.birthday || '', + } + const [form, setForm] = useState<ProfileFormModel>(initialForm) const [formState, setFormState] = useState<ProfileFormStateModel>(initialFormState) const [errors, setErrors] = useState<ProfileFormErrorsModel>({}) const [isValidated, setIsValidated] = useState(false) - const { data: profile, isLoading } = useGetProfile() - const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target setForm((prev) => ({ ...prev, [name]: value })) diff --git a/app/(landing)/notices/[noticeId]/detail/page.tsx b/app/(landing)/notices/[noticeId]/detail/page.tsx index 567977fa..da11728c 100644 --- a/app/(landing)/notices/[noticeId]/detail/page.tsx +++ b/app/(landing)/notices/[noticeId]/detail/page.tsx @@ -3,7 +3,8 @@ import NoticeDetail from '../../_ui/notice-detail' const NoticeDetailPage = ({ params }: { params: { noticeId: string } }) => { - return <NoticeDetail noticeId={params.noticeId} /> + const noticeId = parseInt(params.noticeId) + return <NoticeDetail noticeId={noticeId} /> } export default NoticeDetailPage diff --git a/app/(landing)/notices/_api/get-notice-detail.ts b/app/(landing)/notices/_api/get-notice-detail.ts index 4a0af231..95cc98ef 100644 --- a/app/(landing)/notices/_api/get-notice-detail.ts +++ b/app/(landing)/notices/_api/get-notice-detail.ts @@ -3,8 +3,8 @@ import axiosInstance from '@/shared/api/axios' interface NoticeResponseModel { isSuccess: true message: string - data: { - noticeId: string + result: { + noticeId: number user: { id: number nickname: string @@ -12,15 +12,16 @@ interface NoticeResponseModel { title: string content: string createdAt: string + attachments: { title: string }[] } } -export const getNoticeDetail = async (noticeId: string): Promise<NoticeResponseModel['data']> => { +export const getNoticeDetail = async (noticeId: number): Promise<NoticeResponseModel['result']> => { try { const response = await axiosInstance.get<NoticeResponseModel>(`/api/notices/${noticeId}`) if (response.data.isSuccess) { - return response.data.data + return response.data.result } else { throw new Error(response.data.message || '요청 실패') } diff --git a/app/(landing)/notices/_hooks/use-notice-detail.ts b/app/(landing)/notices/_hooks/use-notice-detail.ts index ea799cff..d64535eb 100644 --- a/app/(landing)/notices/_hooks/use-notice-detail.ts +++ b/app/(landing)/notices/_hooks/use-notice-detail.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query' import { getNoticeDetail } from '../_api/get-notice-detail' -const useNoticeDetail = (noticeId: string) => { +const useNoticeDetail = (noticeId: number) => { return useQuery({ queryKey: ['noticeDetail', noticeId], queryFn: () => getNoticeDetail(noticeId), diff --git a/app/(landing)/notices/_ui/notice-detail/index.tsx b/app/(landing)/notices/_ui/notice-detail/index.tsx index 54cc2fed..3f633207 100644 --- a/app/(landing)/notices/_ui/notice-detail/index.tsx +++ b/app/(landing)/notices/_ui/notice-detail/index.tsx @@ -8,32 +8,33 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -const NoticeDetail = ({ noticeId }: { noticeId: string }) => { +const NoticeDetail = ({ noticeId }: { noticeId: number }) => { const handleSave = () => {} const { data: notice, isLoading } = useNoticeDetail(noticeId) + return ( <div className={cx('container')}> <div className={cx('title-wrapper')}> - {/* <div className={cx('title')}>{data.title}</div> - <div className={cx('date')}>{data.createdAd}</div> */} + <div className={cx('title')}>{notice?.title}</div> + <div className={cx('date')}>{notice?.createdAt}</div> </div> <div className={cx('top-line')}></div> - {/* <div className={cx('content')}>{data.content}</div> */} + <div className={cx('content')}>{notice?.content}</div> <div className={cx('bottom-line')}></div> <div className={cx('attach-file')}> <div className={cx('file-left-wrapper')}> - {/* <p className={cx('file-count')}>첨부 {data.attachments.length}개</p> */} + <p className={cx('file-count')}>첨부 {notice?.attachments.length}개</p> </div> <div className={cx('file-right-wrapper')}> - {/* {data.attachments.map((file, index) => ( + {notice?.attachments.map((file, index) => ( <div className={cx('file-wrapper')} key={index}> - <div className={cx('file-title')} onClick={() => handleSave}> + <div className={cx('file-title')} onClick={handleSave}> {file.title} </div> <DownloadIcon className={cx('file-download')} onClick={() => handleSave} /> </div> - ))} */} + ))} </div> </div> </div> diff --git a/shared/utils/validation.ts b/shared/utils/validation.ts index 90d8ecb2..b537a9e0 100644 --- a/shared/utils/validation.ts +++ b/shared/utils/validation.ts @@ -2,7 +2,7 @@ import { ERROR_MESSAGES } from '@/shared/constants/error-messages' const PATTERNS = { EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, - PASSWORD: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/, + PASSWORD: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{6,}$/, PHONE: /^01([0|1|6|7|8|9])([0-9]{3,4})([0-9]{4})$/, NAME: /^.{2,}$/, NICKNAME: /^.{2,10}$/, From cac1f362f7e633c4577a247e28e449ea75e14056 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 05:25:12 +0900 Subject: [PATCH 613/648] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20API=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_api/get-profile.ts | 24 ++++++++++--------- .../my/profile/_ui/user-info/index.tsx | 15 ++++-------- .../my/profile/_ui/user-info/types.ts | 2 +- app/(dashboard)/my/profile/edit/page.tsx | 11 ++++++++- app/(dashboard)/my/profile/page.tsx | 15 ++++++++++-- 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/app/(dashboard)/my/_api/get-profile.ts b/app/(dashboard)/my/_api/get-profile.ts index 00b5913e..2112cd32 100644 --- a/app/(dashboard)/my/_api/get-profile.ts +++ b/app/(dashboard)/my/_api/get-profile.ts @@ -1,19 +1,21 @@ import axiosInstance from '@/shared/api/axios' +export interface ProfileModel { + userId: number + userName: string + email: string + imageUrl: string | null + nickname: string + phone: string + infoAgreement: boolean + role: string + birthDate: string +} + interface ProfileResponseModel { isSuccess: boolean message: string - result: { - userId: number - userName: string - email: string - imageUrl: string | null - nickname: string - phone: string - infoAgreement: boolean - role: string - birthday: string - } + result: ProfileModel } export const getProfile = async (): Promise<ProfileResponseModel['result']> => { diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index 94fb3d90..972e83f5 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -14,9 +14,8 @@ import Avatar from '@/shared/ui/avatar' import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import { LinkButton } from '@/shared/ui/link-button' -import Spinner from '@/shared/ui/spinner' -import useGetProfile from '../../../_hooks/query/use-get-profile' +import { ProfileModel } from '../../../_api/get-profile' import styles from './styles.module.scss' import { ProfileFormErrorsModel, ProfileFormModel, ProfileFormStateModel } from './types' import { validateProfileForm } from './utils' @@ -30,11 +29,11 @@ const initialFormState = { interface Props { isEditable?: boolean + profile: ProfileModel } -const UserInfo = ({ isEditable = false }: Props) => { +const UserInfo = ({ profile, isEditable = false }: Props) => { const router = useRouter() - const { data: profile, isLoading } = useGetProfile() const initialForm: ProfileFormModel = { name: profile?.userName || '', @@ -43,7 +42,7 @@ const UserInfo = ({ isEditable = false }: Props) => { password: '', passwordConfirm: '', phone: profile?.phone || '', - birthday: profile?.birthday || '', + birthDate: profile?.birthDate || '', } const [form, setForm] = useState<ProfileFormModel>(initialForm) @@ -128,10 +127,6 @@ const UserInfo = ({ isEditable = false }: Props) => { } } - if (isLoading) { - return <Spinner /> - } - if (!profile) { return null } @@ -206,7 +201,7 @@ const UserInfo = ({ isEditable = false }: Props) => { <p className={cx('title')}>생년월일</p> <Input inputSize="compact" - value={form.birthday} + value={form.birthDate} className={cx('input')} isWhiteDisabled={!isEditable} disabled={isEditable} diff --git a/app/(dashboard)/my/profile/_ui/user-info/types.ts b/app/(dashboard)/my/profile/_ui/user-info/types.ts index dd382e11..05409966 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/types.ts +++ b/app/(dashboard)/my/profile/_ui/user-info/types.ts @@ -10,7 +10,7 @@ export interface ProfileFormModel { password: string passwordConfirm: string phone: string - birthday: string + birthDate: string } export interface ProfileFormStateModel { diff --git a/app/(dashboard)/my/profile/edit/page.tsx b/app/(dashboard)/my/profile/edit/page.tsx index 8fae3268..5c2b9406 100644 --- a/app/(dashboard)/my/profile/edit/page.tsx +++ b/app/(dashboard)/my/profile/edit/page.tsx @@ -1,9 +1,18 @@ +'use client' + +import useGetProfile from '../../_hooks/query/use-get-profile' import UserInfo from '../_ui/user-info' const MyProfileEditPage = () => { + const { data: profile, isLoading } = useGetProfile() + + if (!profile) { + return null + } + return ( <> - <UserInfo isEditable={true} /> + <UserInfo profile={profile} isEditable={true} /> </> ) } diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx index 05801e96..6a2ef23c 100644 --- a/app/(dashboard)/my/profile/page.tsx +++ b/app/(dashboard)/my/profile/page.tsx @@ -1,8 +1,11 @@ +'use client' + import classNames from 'classnames/bind' import { PATH } from '@/shared/constants/path' import { LinkButton } from '@/shared/ui/link-button' +import useGetProfile from '../_hooks/query/use-get-profile' import UserInfo from './_ui/user-info' import UserProfile from './_ui/user-profile' import styles from './page.module.scss' @@ -10,13 +13,21 @@ import styles from './page.module.scss' const cx = classNames.bind(styles) const MyProfilePage = () => { + const { data: profile, isLoading } = useGetProfile() + + console.log(profile) + + if (!profile) { + return null + } + return ( <div className={cx('container')}> <p className={cx('title')}>나의 정보</p> <div className={cx('wrapper')}> - <UserInfo /> + <UserInfo profile={profile} /> <div className={cx('user-profile')}> - <UserProfile role={'트레이더'} nickname={'고양이'} email={'meow@example.com'} /> + <UserProfile role={profile.role} nickname={profile.nickname} email={profile.email} /> <div className={cx('link-button')}> <LinkButton href={PATH.PROFILE_WITHDRAW}>탈퇴하기</LinkButton> </div> From c9eb81940cfb97aea0c847f40ea4190dda4fbf5e Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 6 Dec 2024 05:37:56 +0900 Subject: [PATCH 614/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20patch=20API?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99=20(#247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_api/patch-strategy-approval.ts | 10 ++--- .../_api/set-admin-strategies-table-body.tsx | 10 +++-- .../query/use-patch-strategy-approval.ts | 2 +- .../_ui/admin-strategies-approve-td/index.tsx | 44 +++++++++++++++++++ .../styles.module.scss | 24 ++++++++++ .../_ui/admin-strategy-public-select.tsx | 34 ++++++++++++++ app/admin/strategies/constants.ts | 6 +++ app/admin/strategies/page.tsx | 2 +- app/admin/strategies/types.ts | 2 +- 9 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 app/admin/strategies/_ui/admin-strategies-approve-td/index.tsx create mode 100644 app/admin/strategies/_ui/admin-strategies-approve-td/styles.module.scss create mode 100644 app/admin/strategies/_ui/admin-strategy-public-select.tsx diff --git a/app/admin/strategies/_api/patch-strategy-approval.ts b/app/admin/strategies/_api/patch-strategy-approval.ts index 363c9a1c..df60bb65 100644 --- a/app/admin/strategies/_api/patch-strategy-approval.ts +++ b/app/admin/strategies/_api/patch-strategy-approval.ts @@ -1,22 +1,22 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { StrategiesResponseModel } from '../types' -const patchStrategyApproval = async (strategyId: number, isApproved: boolean) => { +const patchStrategyApproval = async (strategyId: number, isApproved: 'APPROVED' | 'DENY') => { try { - const res = await axios.patch<StrategiesResponseModel>( + const res = await axiosInstance.patch<StrategiesResponseModel>( `/api/admin/strategies/${strategyId}`, null, { params: { - isApproved: isApproved ? 'APPROVED' : 'DENY', + isApproved, }, } ) if (!res.data.isSuccess) throw new Error(res.data.message) - return res.data.data + return res.data.result } catch (err) { console.log('Error : ' + err) throw err diff --git a/app/admin/strategies/_api/set-admin-strategies-table-body.tsx b/app/admin/strategies/_api/set-admin-strategies-table-body.tsx index 4b8203c6..f28c4ba8 100644 --- a/app/admin/strategies/_api/set-admin-strategies-table-body.tsx +++ b/app/admin/strategies/_api/set-admin-strategies-table-body.tsx @@ -1,3 +1,4 @@ +import AdminStrategiesApproveTd from '../_ui/admin-strategies-approve-td' import { StrategiesResponseModel } from '../types' const setAdminStrategiesTableBody = (data: StrategiesResponseModel['result']['content']) => @@ -7,9 +8,12 @@ const setAdminStrategiesTableBody = (data: StrategiesResponseModel['result']['co data.createAt, data.strategyName, data.nickname, - data.isPublic, - data.isApproved, - 'button', + data.isPublic === 'PUBLIC' ? '공개' : '비공개', + <AdminStrategiesApproveTd + isApproved={data.isApproved} + strategyId={data.strategyId} + key={data.strategyId} + />, ] }) diff --git a/app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts b/app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts index e0300d31..802389d3 100644 --- a/app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts +++ b/app/admin/strategies/_hooks/query/use-patch-strategy-approval.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import patchStrategyApproval from '../../_api/patch-strategy-approval' -const usePatchStrategyApproval = (strategyId: number, isApproved: boolean) => { +const usePatchStrategyApproval = (strategyId: number, isApproved: 'APPROVED' | 'DENY') => { const queryClient = useQueryClient() return useMutation({ diff --git a/app/admin/strategies/_ui/admin-strategies-approve-td/index.tsx b/app/admin/strategies/_ui/admin-strategies-approve-td/index.tsx new file mode 100644 index 00000000..cee5b54a --- /dev/null +++ b/app/admin/strategies/_ui/admin-strategies-approve-td/index.tsx @@ -0,0 +1,44 @@ +'use client' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import usePatchStrategyApproval from '../../_hooks/query/use-patch-strategy-approval' +import { StrategiesResponseModel } from '../../types' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + strategyId: StrategiesResponseModel['result']['content'][number]['strategyId'] + isApproved: StrategiesResponseModel['result']['content'][number]['isApproved'] +} + +const AdminStrategiesApproveTd = ({ strategyId, isApproved }: Props) => { + const approveMutation = usePatchStrategyApproval(strategyId, 'APPROVED') + const denyMutation = usePatchStrategyApproval(strategyId, 'DENY') + + if (isApproved !== 'PENDING') return isApproved === 'APPROVED' ? '승인완료' : '승인거부' + + const handleApprove = () => { + approveMutation.mutate() + } + + const handleDeny = () => { + denyMutation.mutate() + } + + return ( + <Button.ButtonGroup gap="12px"> + <Button size="small" className={cx('container', 'approve')} onClick={handleApprove}> + 승인 + </Button> + <Button size="small" className={cx('container', 'deny')} onClick={handleDeny}> + 거부 + </Button> + </Button.ButtonGroup> + ) +} + +export default AdminStrategiesApproveTd diff --git a/app/admin/strategies/_ui/admin-strategies-approve-td/styles.module.scss b/app/admin/strategies/_ui/admin-strategies-approve-td/styles.module.scss new file mode 100644 index 00000000..8014fc17 --- /dev/null +++ b/app/admin/strategies/_ui/admin-strategies-approve-td/styles.module.scss @@ -0,0 +1,24 @@ +$color-approve: $color-indigo; +$color-deny: $color-orange-600; +@mixin base { + width: fit-content; + padding: 7px 16px; + border-radius: 4px; + border: 1px solid; + border-radius: 16px; + @include typo-c1; +} + +.container { + &.approve { + color: $color-approve; + border-color: $color-approve; + @include base; + } + + &.deny { + color: $color-deny; + border-color: $color-deny; + @include base; + } +} diff --git a/app/admin/strategies/_ui/admin-strategy-public-select.tsx b/app/admin/strategies/_ui/admin-strategy-public-select.tsx new file mode 100644 index 00000000..e4e1278f --- /dev/null +++ b/app/admin/strategies/_ui/admin-strategy-public-select.tsx @@ -0,0 +1,34 @@ +'use client' + +import { useState } from 'react' + +import Select from '@/shared/ui/select' + +// import usePatchUserRole from '../_hooks/query/use-patch-user-role' +import { strategyPublicOptions } from '../constants' +import { StrategiesPublicStateType, StrategiesResponseModel } from '../types' + +interface Props { + data: StrategiesResponseModel['result']['content'][number] +} + +const AdminStrategyPublicSelect = ({ data }: Props) => { + const { strategyId, isPublic } = data + const [value, setValue] = useState<StrategiesPublicStateType>(isPublic) + + // const { mutate } = usePatchUserRole(strategyId, value) + + return ( + <Select + value={value} + onChange={(v) => { + setValue(v as StrategiesPublicStateType) + // mutate() + }} + options={strategyPublicOptions} + key={strategyId} + /> + ) +} + +export default AdminStrategyPublicSelect diff --git a/app/admin/strategies/constants.ts b/app/admin/strategies/constants.ts index 2c97cccb..d2c9192a 100644 --- a/app/admin/strategies/constants.ts +++ b/app/admin/strategies/constants.ts @@ -1,6 +1,12 @@ +import { DropdownOptionModel } from '@/shared/ui/dropdown/types' import { TabItemModel } from '@/shared/ui/tabs' export const tabs: Array<TabItemModel> = [ { label: '모든 전략', id: 'ALL' }, { label: '승인 대기', id: 'PENDING' }, ] + +export const strategyPublicOptions: DropdownOptionModel[] = [ + { label: '공개', value: 'PUBLIC' }, + { label: '비공개', value: 'PRIVATE' }, +] diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index 7175fef4..5d2ccfb1 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -54,7 +54,7 @@ const AdminStrategyPage = () => { className={cx('header')} /> <VerticalTable - tableHead={['No.', '날짜', '전략명', '닉네임', '공개여부', '승인여부', '']} + tableHead={['No.', '날짜', '전략명', '닉네임', '공개여부', '승인여부']} tableBody={setAdminStrategiesTableBody(data.content)} countPerPage={10} currentPage={1} diff --git a/app/admin/strategies/types.ts b/app/admin/strategies/types.ts index f49eb1c2..2ae5810e 100644 --- a/app/admin/strategies/types.ts +++ b/app/admin/strategies/types.ts @@ -1,6 +1,6 @@ import { APIResponseBaseModel } from '@/shared/types/response' -type StrategiesPublicStateType = 'PUBLIC' | 'PRIVATE' +export type StrategiesPublicStateType = 'PUBLIC' | 'PRIVATE' export type AdminStrategiesTapType = 'ALL' | 'PENDING' export type StrategiesApprovalStateType = 'APPROVED' | 'DENY' | 'PENDING' From 4cc2e4d23cc191dd62b924072af5ac7f80d877f4 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 05:46:12 +0900 Subject: [PATCH 615/648] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/_ui/user-info/utils.ts | 3 --- app/(dashboard)/my/profile/page.tsx | 2 -- 2 files changed, 5 deletions(-) diff --git a/app/(dashboard)/my/profile/_ui/user-info/utils.ts b/app/(dashboard)/my/profile/_ui/user-info/utils.ts index 3d5e4dc0..1165c166 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/utils.ts +++ b/app/(dashboard)/my/profile/_ui/user-info/utils.ts @@ -36,9 +36,6 @@ export const validateProfileForm = ( const passwordError = validateField('PASSWORD', form.password) if (passwordError) errors.password = passwordError - // 비밀번호 입력할때 빈값이면 아무것도 안되도록 하는 로직 넣어야하는데 어케함... - // const handlePasswordChange = () => { - // const payload = password.trim() === '' ? null : password; const passwordMatchError = validatePasswordMatch(form.password, form.passwordConfirm) if (passwordMatchError) errors.passwordConfirm = passwordMatchError diff --git a/app/(dashboard)/my/profile/page.tsx b/app/(dashboard)/my/profile/page.tsx index 6a2ef23c..da50ca52 100644 --- a/app/(dashboard)/my/profile/page.tsx +++ b/app/(dashboard)/my/profile/page.tsx @@ -15,8 +15,6 @@ const cx = classNames.bind(styles) const MyProfilePage = () => { const { data: profile, isLoading } = useGetProfile() - console.log(profile) - if (!profile) { return null } From e9feaca07ff7232ff9f642f7261f28b9763d59ba Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 6 Dec 2024 06:53:38 +0900 Subject: [PATCH 616/648] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=ED=8E=98=EC=9D=B4=EC=A7=80=20del?= =?UTF-8?q?ete=20API=20=EC=97=B0=EB=8F=99=20(#109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/users/_ui/user-delete-button.tsx | 9 ++++----- app/admin/users/page.tsx | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/admin/users/_ui/user-delete-button.tsx b/app/admin/users/_ui/user-delete-button.tsx index 18cf57b5..d8912595 100644 --- a/app/admin/users/_ui/user-delete-button.tsx +++ b/app/admin/users/_ui/user-delete-button.tsx @@ -9,18 +9,17 @@ interface Props { } const UserDeleteButton = ({ userId }: Props) => { - // TODO : api 완성되면 연결하기 - // const { mutate, isPending } = useDeleteUser(userId) + const { mutate, isPending } = useDeleteUser(userId) return ( <Button variant="filled" - // onClick={() => mutate()} - // disabled={isPending} + onClick={() => mutate()} + disabled={isPending} size="small" style={{ padding: '7px 16px' }} > - 강제삭제 + 강제탈퇴 </Button> ) } diff --git a/app/admin/users/page.tsx b/app/admin/users/page.tsx index 2d1e2cbe..bd0a2b67 100644 --- a/app/admin/users/page.tsx +++ b/app/admin/users/page.tsx @@ -36,8 +36,6 @@ const AdminUsersPage = () => { if (isLoading || !data) return null - console.log('data', data) - return ( <> <Title label="회원 관리" className={cx('title')} /> @@ -72,11 +70,11 @@ const AdminUsersPage = () => { /> <VerticalTable tableHead={['No.', '프로필', '이름', '닉네임', '이메일', '전화번호', '회원분류', '탈퇴']} - tableBody={data?.content ? setTableBody(data.content) : []} + tableBody={setTableBody(data?.content)} countPerPage={10} currentPage={1} /> - <Pagination currentPage={1} maxPage={data?.totalPages} onPageChange={() => {}} /> + <Pagination currentPage={data?.page} maxPage={data?.totalPages} onPageChange={() => {}} /> </section> </> ) From d5377362b64d76f849f91b70c20e63150ba51888 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 07:01:54 +0900 Subject: [PATCH 617/648] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20=EB=B0=8F=20?= =?UTF-8?q?=EC=B2=A8=EB=B6=80=ED=8C=8C=EC=9D=BC=20=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/_api/get-notice-detail.ts | 21 +++++------ .../notices/_ui/notice-detail/index.tsx | 36 ++++++++++++++++--- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/app/(landing)/notices/_api/get-notice-detail.ts b/app/(landing)/notices/_api/get-notice-detail.ts index 95cc98ef..44a36bca 100644 --- a/app/(landing)/notices/_api/get-notice-detail.ts +++ b/app/(landing)/notices/_api/get-notice-detail.ts @@ -2,18 +2,19 @@ import axiosInstance from '@/shared/api/axios' interface NoticeResponseModel { isSuccess: true - message: string + message: 'string' result: { - noticeId: number - user: { - id: number - nickname: string - } - title: string - content: string - createdAt: string - attachments: { title: string }[] + title: 'string' + content: 'string' + createdAt: '2024-12-05T21:49:06.561Z' + files: [ + { + fileName: 'string' + noticeFileId: 0 + }, + ] } + code: 0 } export const getNoticeDetail = async (noticeId: number): Promise<NoticeResponseModel['result']> => { diff --git a/app/(landing)/notices/_ui/notice-detail/index.tsx b/app/(landing)/notices/_ui/notice-detail/index.tsx index 3f633207..7c2949a1 100644 --- a/app/(landing)/notices/_ui/notice-detail/index.tsx +++ b/app/(landing)/notices/_ui/notice-detail/index.tsx @@ -3,15 +3,38 @@ import { DownloadIcon } from '@/public/icons' import classNames from 'classnames/bind' +import axios from '@/shared/api/axios' + import useNoticeDetail from '../../_hooks/use-notice-detail' import styles from './styles.module.scss' const cx = classNames.bind(styles) const NoticeDetail = ({ noticeId }: { noticeId: number }) => { - const handleSave = () => {} const { data: notice, isLoading } = useNoticeDetail(noticeId) + const handleSave = async (fileName: string, noticeFileId: number) => { + try { + const response = await axios.get(`/api/files/download/${noticeFileId}`, { + responseType: 'blob', + }) + + const blob = new Blob([response.data]) + const url = window.URL.createObjectURL(blob) + + const link = document.createElement('a') + link.href = url + link.download = fileName + document.body.appendChild(link) + link.click() + + link.remove() + window.URL.revokeObjectURL(url) + } catch (error) { + console.error('파일 다운로드 중 오류 발생:', error) + } + } + return ( <div className={cx('container')}> <div className={cx('title-wrapper')}> @@ -24,13 +47,16 @@ const NoticeDetail = ({ noticeId }: { noticeId: number }) => { <div className={cx('attach-file')}> <div className={cx('file-left-wrapper')}> - <p className={cx('file-count')}>첨부 {notice?.attachments.length}개</p> + <p className={cx('file-count')}>첨부 {notice?.files.length}개</p> </div> <div className={cx('file-right-wrapper')}> - {notice?.attachments.map((file, index) => ( + {notice?.files.map((file, index) => ( <div className={cx('file-wrapper')} key={index}> - <div className={cx('file-title')} onClick={handleSave}> - {file.title} + <div + className={cx('file-title')} + onClick={() => handleSave(file.fileName, file.noticeFileId)} + > + {file.fileName} </div> <DownloadIcon className={cx('file-download')} onClick={() => handleSave} /> </div> From 92d2958eab71cd6f838e62fe2e8f107847712e12 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 07:36:23 +0900 Subject: [PATCH 618/648] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20patch-profil?= =?UTF-8?q?e=20=EC=83=9D=EC=84=B1=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_api/patch-profile.ts | 38 +++++++++++++++++++ .../my/_hooks/query/use-patch-profile.ts | 32 ++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 app/(dashboard)/my/_api/patch-profile.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-patch-profile.ts diff --git a/app/(dashboard)/my/_api/patch-profile.ts b/app/(dashboard)/my/_api/patch-profile.ts new file mode 100644 index 00000000..a16f4e95 --- /dev/null +++ b/app/(dashboard)/my/_api/patch-profile.ts @@ -0,0 +1,38 @@ +// import axiosInstance from '@/shared/api/axios' + +// interface ImageDto { +// imageName: string +// size: number +// } + +// interface PatchUserProfileRequest { +// nickname: string +// password: string | null +// email?: string +// phone: string +// imageChange: boolean +// imageDto?: ImageDto +// } + +// interface PatchUserProfileResponse { +// isSuccess: boolean +// message: string +// data?: { +// presignedUrl?: string +// } +// } + +// const patchUserProfile = async (userId: number, profileData: PatchUserProfileRequest) => { +// try { +// const response = await axiosInstance.patch<PatchUserProfileResponse>( +// `/api/users/mypage/profile`, +// profileData +// ) +// return response.data.result +// } catch (err) { +// console.error('Error updating user profile:', err) +// throw err +// } +// } + +// export default patchUserProfile diff --git a/app/(dashboard)/my/_hooks/query/use-patch-profile.ts b/app/(dashboard)/my/_hooks/query/use-patch-profile.ts new file mode 100644 index 00000000..ec985d89 --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-patch-profile.ts @@ -0,0 +1,32 @@ +// import { useMutation, useQueryClient } from '@tanstack/react-query' + +// import patchProfile from '../../_api/patch-profile' + +// interface PatchUserProfileData { +// nickname: 'string' +// password: 'string' +// imageDto: { +// imageName: 'string' +// size: 2097152 +// } +// phone: 'string' +// email: 'string' +// imageChange: true +// } + +// const usePatchUserProfile = () => { +// const queryClient = useQueryClient() + +// return useMutation({ +// mutationFn: ({ userId, profileData }: PatchUserProfileData) => +// patchProfile(userId, profileData), +// onSuccess: () => { +// queryClient.invalidateQueries({ queryKey: ['userProfile'] }) +// }, +// onError: (error) => { +// console.error('Error updating user profile:', error) +// }, +// }) +// } + +// export default usePatchUserProfile From ce7b625361d590d738173f60799892b55d239ebe Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 09:41:26 +0900 Subject: [PATCH 619/648] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=ED=8E=98=EC=9D=B4=EC=A7=80=20API=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/[traderId]/page.tsx | 132 +++++++---------- app/(dashboard)/traders/_api/get-traders.ts | 51 +++++++ .../traders/_hooks/use-get-traders.ts | 12 ++ app/(dashboard)/traders/page.tsx | 134 +++++------------- shared/ui/traders-list-card/index.tsx | 12 +- .../traders-list-card.stories.tsx | 8 +- 6 files changed, 158 insertions(+), 191 deletions(-) create mode 100644 app/(dashboard)/traders/_api/get-traders.ts create mode 100644 app/(dashboard)/traders/_hooks/use-get-traders.ts diff --git a/app/(dashboard)/traders/[traderId]/page.tsx b/app/(dashboard)/traders/[traderId]/page.tsx index 1aa8e50b..3b975191 100644 --- a/app/(dashboard)/traders/[traderId]/page.tsx +++ b/app/(dashboard)/traders/[traderId]/page.tsx @@ -1,84 +1,48 @@ -'use client' - -import classNames from 'classnames/bind' - -import BackHeader from '@/shared/ui/header/back-header' -import Title from '@/shared/ui/title' -import TradersListCard from '@/shared/ui/traders-list-card' - -import ListHeader from '../../_ui/list-header' -import StrategiesItem from '../../_ui/strategies-item' -import styles from './page.module.scss' - -const cx = classNames.bind(styles) - -const TraderDetailPage = () => { - // TODO: 임시데이터 제거 - const strategiesModel = { - strategyId: '123', - strategyName: '리치테크 FuturesDay', - nickname: 'MACS', - stockTypeIconUrl: [], - stockTypeNames: [], - profitRateChartData: { - xAxis: [ - '2023-01-01', - '2023-01-02', - '2023-01-03', - '2023-01-04', - '2023-01-05', - '2023-01-06', - '2023-01-07', - '2023-01-08', - '2023-01-09', - ], - yAxis: [7.2, 5.2, 25, 12.8, 17.2, 11.4, 20, 16, 18], - }, - tradeTypeIconUrl: '', - tradeTypeName: '', - mdd: '-20,580,856', - smScore: 60.6, - cumulativeProfitLossRate: 120.1, - recentYearProfitLossRate: 30.1, - subscriptionCnt: 23, - isSubscribed: true, - averageRating: 4.8, - totalReview: 12, - } - - const traderData = { - nickname: '냥냥펀치', - strategyCount: 10, - subscriberCount: 20, - traderId: '940504', - } - - return ( - <> - <div className={cx('page-container')}> - <BackHeader label={'목록으로 돌아가기'} /> - <div className={cx('title')}> - <Title label={'트레이더 상세보기'}> -
    -
    - -
    - - {Array(3) - .fill(strategiesModel) - .map((item, index) => ( - - ))} -
    - - ) -} - -export default TraderDetailPage +// 'use client' + +// import classNames from 'classnames/bind' + +// import BackHeader from '@/shared/ui/header/back-header' +// import Title from '@/shared/ui/title' +// import TradersListCard from '@/shared/ui/traders-list-card' + +// import ListHeader from '../../_ui/list-header' +// import StrategiesItem from '../../_ui/strategies-item' +// import useGetTraders from '../_hooks/use-get-traders' +// import styles from './page.module.scss' + +// const cx = classNames.bind(styles) + +// const TraderDetailPage = () => { +// const { data: trader, isLoading } = useGetTraders() + +// if (!trader) { +// return null +// } + +// return ( +// <> +//
    +// +//
    +// +//
    +//
    +// +//
    +// + +// +//
    +// +// ) +// } + +// export default TraderDetailPage diff --git a/app/(dashboard)/traders/_api/get-traders.ts b/app/(dashboard)/traders/_api/get-traders.ts new file mode 100644 index 00000000..cfce388a --- /dev/null +++ b/app/(dashboard)/traders/_api/get-traders.ts @@ -0,0 +1,51 @@ +import axiosInstance from '@/shared/api/axios' + +export interface TraderModel { + userId: number + nickname: string + userName: string + imageUrl: string + strategyCount: number + totalSubCount: number +} + +interface TradersResponseModel { + isSuccess: boolean + message: string + result: { + result: string + content: TraderModel[] + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean + } +} + +export interface TradersParamsModel { + page: number + size: number + keyword: string + orderBy: 'STRATEGY_TOTAL' | 'SUBSCRIBE_TOTAL' +} + +export const getTraders = async ( + params: TradersParamsModel +): Promise => { + try { + const response = await axiosInstance.get('/api/users/traders', { + params, + }) + + if (response.data.isSuccess) { + return response.data.result + } else { + throw new Error(response.data.message || '요청 실패') + } + } catch (err) { + console.error(err) + throw new Error('트레이더 목록 조회에 실패하였습니다.') + } +} diff --git a/app/(dashboard)/traders/_hooks/use-get-traders.ts b/app/(dashboard)/traders/_hooks/use-get-traders.ts new file mode 100644 index 00000000..fb8e14cb --- /dev/null +++ b/app/(dashboard)/traders/_hooks/use-get-traders.ts @@ -0,0 +1,12 @@ +import { useQuery } from '@tanstack/react-query' + +import { TradersParamsModel, getTraders } from '../_api/get-traders' + +const useGetTraders = (params: TradersParamsModel) => { + return useQuery({ + queryKey: ['traders', params], + queryFn: () => getTraders(params), + }) +} + +export default useGetTraders diff --git a/app/(dashboard)/traders/page.tsx b/app/(dashboard)/traders/page.tsx index 7f0839f1..61ca840e 100644 --- a/app/(dashboard)/traders/page.tsx +++ b/app/(dashboard)/traders/page.tsx @@ -3,8 +3,11 @@ import { useState } from 'react' import styles from '@/app/(dashboard)/traders/page.module.scss' +import { COUNT_PER_PAGE } from '@/app/admin/category/_ui/shared/manage-table/constant' import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' +import { usePagination } from '@/shared/hooks/custom/use-pagination' import { DropdownValueType } from '@/shared/ui/dropdown/types' import Pagination from '@/shared/ui/pagination' import { SearchInput } from '@/shared/ui/search-input' @@ -12,87 +15,29 @@ import Select from '@/shared/ui/select' import Title from '@/shared/ui/title' import TradersListCard from '@/shared/ui/traders-list-card' -const cx = classNames.bind(styles) +import useGetTraders from './_hooks/use-get-traders' -// 임시데이터 -const tradersData = [ - { - nickname: '김트레이더', - strategyCount: 12, - subscriberCount: 345, - traderId: '123456', - }, - { - nickname: '박트레이더', - strategyCount: 8, - subscriberCount: 120, - traderId: '234567', - }, - { - nickname: '이트레이더', - strategyCount: 15, - subscriberCount: 289, - traderId: '345678', - }, - { - nickname: '최트레이더', - strategyCount: 5, - subscriberCount: 87, - traderId: '456789', - }, - { - nickname: '정트레이더', - strategyCount: 20, - subscriberCount: 512, - traderId: '567890', - }, - { - nickname: '조트레이더', - strategyCount: 10, - subscriberCount: 200, - traderId: '678901', - }, - { - nickname: '한트레이더', - strategyCount: 18, - subscriberCount: 467, - traderId: '789012', - }, - { - nickname: '장트레이더', - strategyCount: 7, - subscriberCount: 134, - traderId: '890123', - }, - { - nickname: '유트레이더', - strategyCount: 3, - subscriberCount: 56, - traderId: '901234', - }, - { - nickname: '홍트레이더', - strategyCount: 22, - subscriberCount: 600, - traderId: '012345', - }, - { - nickname: '서트레이더', - strategyCount: 4, - subscriberCount: 75, - traderId: '123457', - }, - { - nickname: '신트레이더', - strategyCount: 9, - subscriberCount: 300, - traderId: '234568', - }, -] +const cx = classNames.bind(styles) const TradersPage = () => { const initialValue = null const [value, setValue] = useState(initialValue) + const { page, handlePageChange } = usePagination({ + basePath: PATH.TRADERS, + pageSize: COUNT_PER_PAGE, + }) + + const { data: traders } = useGetTraders({ + page: 1, + size: 12, + keyword: '영웅', + orderBy: 'STRATEGY_TOTAL', + }) + + if (!traders) { + return null + } + return ( <>
    @@ -106,36 +51,31 @@ const TradersPage = () => { placeholder="구독순" onChange={(newValue) => setValue(newValue)} options={[ - { - value: '1+', - label: '인기순', - }, - { - value: '2+', - label: '추천순', - }, - { - value: '3+', - label: '구독순', - }, + { value: 'STRATEGY_TOTAL', label: '전략순' }, + { value: 'SUBSCRIBE_TOTAL', label: '구독순' }, ]} >
    - {tradersData.map((trader) => ( + {/* {traders?.result?.content?.map((trader) => ( - ))} + ))} */}
    - {/* TODO: 실제 데이터로 변경 */} +
    - {}}> +
    diff --git a/shared/ui/traders-list-card/index.tsx b/shared/ui/traders-list-card/index.tsx index 7c53e6c4..5ce6811a 100644 --- a/shared/ui/traders-list-card/index.tsx +++ b/shared/ui/traders-list-card/index.tsx @@ -10,19 +10,19 @@ const cx = classNames.bind(styles) interface Props { nickname: string - profileImage?: string + imageUrl: string strategyCount: number subscriberCount: number - traderId: string + userId: string | null hasButton?: boolean } const TradersListCard = ({ nickname, - profileImage, strategyCount, subscriberCount, - traderId, + userId, + imageUrl, hasButton = true, }: Props) => { return ( @@ -36,13 +36,13 @@ const TradersListCard = ({
    - +
    {hasButton && (
    export const Default: StoryType = { args: { nickname: '고양이는야옹하고울지', - profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', + imageUrl: 'https://lh3.googleusercontent.com/a/your-image-id', strategyCount: 10, subscriberCount: 10, - traderId: '1234', + userId: '1234', }, } export const WithoutButton: StoryType = { args: { nickname: '고양이는야옹하고울지', - profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', + imageUrl: 'https://lh3.googleusercontent.com/a/your-image-id', strategyCount: 10, subscriberCount: 10, - traderId: '1234', + userId: '1234', hasButton: false, }, } From 64741e11c2f5f1afebe3e7214db51e4992f6a703 Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 6 Dec 2024 09:52:13 +0900 Subject: [PATCH 620/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EB=93=B1=EB=A1=9D=20=EB=A7=81=ED=81=AC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/strategies/page.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/admin/strategies/page.tsx b/app/admin/strategies/page.tsx index 5d2ccfb1..7bbfa426 100644 --- a/app/admin/strategies/page.tsx +++ b/app/admin/strategies/page.tsx @@ -9,7 +9,6 @@ import Tabs from '@/shared/ui/tabs' import Title from '@/shared/ui/title' import AdminContentsHeader from '../_ui/admin-header' -import AdminPostButton from '../_ui/admin-post-button' import setAdminStrategiesTableBody from './_api/set-admin-strategies-table-body' import useStrategiesData from './_hooks/query/use-strategies-data' import useAdminStrategiesPage from './_hooks/use-admin-strategies-page' @@ -25,7 +24,6 @@ const AdminStrategyPage = () => { ...searchParams, isApproved: activeTab === 'ALL' ? undefined : 'PENDING', }) - // const { mutate } = usePatchStrategyApproval(1, false) if (!data || isLoading) return null @@ -33,7 +31,7 @@ const AdminStrategyPage = () => { <> <section className={cx('container')}> - <AdminPostButton label="전략 등록하기" pathname="strategies" /> + {/* <AdminPostButton label="전략 등록하기" pathname="strategies" /> */} <Tabs tabs={tabs} activeTab={activeTab} onTabChange={onTabChange} /> <AdminContentsHeader Left={ From fb18d7e88e99ca24ce22ec8058b69d5cb49bd2bb Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 10:20:08 +0900 Subject: [PATCH 621/648] =?UTF-8?q?refactor:=20=EC=8A=A4=EC=BC=88=EB=A0=88?= =?UTF-8?q?=ED=86=A4=20loading=ED=8C=8C=EC=9D=BC=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EC=9D=BC=EB=B0=98=ED=8C=8C=EC=9D=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#298)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details-skeleton/index.tsx} | 2 +- .../details-skeleton/styles.module.scss} | 0 app/(dashboard)/strategies/[strategyId]/page.tsx | 4 ++-- app/(dashboard)/strategies/loading.tsx | 15 --------------- app/(dashboard)/strategies/page.tsx | 14 ++++++++++++-- 5 files changed, 15 insertions(+), 20 deletions(-) rename app/(dashboard)/strategies/[strategyId]/{loading.tsx => _ui/details-skeleton/index.tsx} (97%) rename app/(dashboard)/strategies/[strategyId]/{loading.module.scss => _ui/details-skeleton/styles.module.scss} (100%) delete mode 100644 app/(dashboard)/strategies/loading.tsx diff --git a/app/(dashboard)/strategies/[strategyId]/loading.tsx b/app/(dashboard)/strategies/[strategyId]/_ui/details-skeleton/index.tsx similarity index 97% rename from app/(dashboard)/strategies/[strategyId]/loading.tsx rename to app/(dashboard)/strategies/[strategyId]/_ui/details-skeleton/index.tsx index 2b6644e1..4f30f76a 100644 --- a/app/(dashboard)/strategies/[strategyId]/loading.tsx +++ b/app/(dashboard)/strategies/[strategyId]/_ui/details-skeleton/index.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames/bind' -import styles from './loading.module.scss' +import styles from './styles.module.scss' const cx = classNames.bind(styles) diff --git a/app/(dashboard)/strategies/[strategyId]/loading.module.scss b/app/(dashboard)/strategies/[strategyId]/_ui/details-skeleton/styles.module.scss similarity index 100% rename from app/(dashboard)/strategies/[strategyId]/loading.module.scss rename to app/(dashboard)/strategies/[strategyId]/_ui/details-skeleton/styles.module.scss diff --git a/app/(dashboard)/strategies/[strategyId]/page.tsx b/app/(dashboard)/strategies/[strategyId]/page.tsx index 91468c62..d928a094 100644 --- a/app/(dashboard)/strategies/[strategyId]/page.tsx +++ b/app/(dashboard)/strategies/[strategyId]/page.tsx @@ -15,7 +15,7 @@ import SideSkeleton from '../../_ui/details-side-item/side-skeleton' import useGetSubscribe from '../_hooks/query/use-get-subscribe' import SideContainer from '../_ui/side-container' import useGetDetailsInformationData from './_hooks/query/use-get-details-information-data' -import DetailsLoading from './loading' +import DetailsLoading from './_ui/details-skeleton' const DetailsInformation = React.lazy(() => import('../../_ui/details-information')) const AnalysisContainer = React.lazy(() => import('@/app/(dashboard)/_ui/analysis-container')) @@ -23,7 +23,7 @@ const ReviewContainer = React.lazy(() => import('./_ui/review-container')) const SubscriberItem = React.lazy(() => import('@/app/(dashboard)/_ui/subscriber-item')) const DetailsSideItem = React.lazy(() => import('../../_ui/details-side-item')) -const DynamicSkeleton = dynamic(() => import('./loading'), { +const DynamicSkeleton = dynamic(() => import('./_ui/details-skeleton'), { loading: () => <DetailsLoading />, ssr: false, }) diff --git a/app/(dashboard)/strategies/loading.tsx b/app/(dashboard)/strategies/loading.tsx deleted file mode 100644 index 682877d6..00000000 --- a/app/(dashboard)/strategies/loading.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' - -import StrategiesItemSkeleton from '../_ui/strategies-item/skeleton' - -const StrategiesLoading = () => { - return ( - <div> - {Array.from({ length: 8 }, (_, idx) => ( - <StrategiesItemSkeleton key={idx} /> - ))} - </div> - ) -} - -export default StrategiesLoading diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index b0e36674..ae0ac8e2 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -5,11 +5,11 @@ import classNames from 'classnames/bind' import Title from '@/shared/ui/title' import ListHeader from '../_ui/list-header' +import StrategiesItemSkeleton from '../_ui/strategies-item/skeleton' import SearchBarContainer from './_ui/search-bar' import SearchBarSkeleton from './_ui/search-bar/search-bar-skeleton' import SideContainer from './_ui/side-container' import StrategyList from './_ui/strategy-list' -import StrategiesLoading from './loading' import styles from './page.module.scss' const cx = classNames.bind(styles) @@ -19,7 +19,7 @@ const StrategiesPage = () => { <div className={cx('container')}> <Title label={'전략 랭킹 모음'} /> <ListHeader /> - <Suspense fallback={<StrategiesLoading />}> + <Suspense fallback={<Skeleton />}> <StrategyList /> </Suspense> <SideContainer> @@ -31,4 +31,14 @@ const StrategiesPage = () => { ) } +const Skeleton = () => { + return ( + <> + {Array.from({ length: 6 }, (_, idx) => ( + <StrategiesItemSkeleton key={idx} /> + ))} + </> + ) +} + export default StrategiesPage From e82de5b8dfde90e7705c4325cc949c27b5d2a539 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 10:26:49 +0900 Subject: [PATCH 622/648] =?UTF-8?q?fix:=20=EC=82=AC=EC=9D=B4=EC=A6=88=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#298)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/strategies/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index ae0ac8e2..a12301c3 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -34,7 +34,7 @@ const StrategiesPage = () => { const Skeleton = () => { return ( <> - {Array.from({ length: 6 }, (_, idx) => ( + {Array.from({ length: 8 }, (_, idx) => ( <StrategiesItemSkeleton key={idx} /> ))} </> From e76b7ced2b8e5f142c27463f7dced26daaf99455 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 10:39:10 +0900 Subject: [PATCH 623/648] =?UTF-8?q?fix:=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EC=A6=88=20=EB=B0=B0=EC=9C=A8=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20=EC=88=98=EC=A0=95=20(#296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/strategies-item/strategies-icon.tsx | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index ce39df2a..8e926158 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -13,11 +13,6 @@ import styles from './styles.module.scss' const cx = classNames.bind(styles) -interface ImageSizeModel { - width: number - height: number -} - interface Props { iconUrls?: string[] iconNames?: string[] @@ -25,7 +20,7 @@ interface Props { } const StrategiesIcon = ({ iconUrls, iconNames, isDetailsPage = false }: Props) => { - const [imageSizes, setImageSizes] = useState<{ [key: string]: ImageSizeModel }>({}) + const [imageSizes, setImageSizes] = useState<{ [key: string]: number }>({}) const [validImages, setValidImages] = useState<{ [key: string]: boolean }>({}) useEffect(() => { @@ -33,8 +28,8 @@ const StrategiesIcon = ({ iconUrls, iconNames, isDetailsPage = false }: Props) = iconUrls?.forEach((url) => { const image = new window.Image() image.src = url - image.onload = () => updateImageSize(url, image.width, image.height) - image.onerror = () => updateImageSize(url, 20, 20) + image.onload = () => updateImageSize(url, image.width) + image.onerror = () => updateImageSize(url, 22) images.push(image) }) @@ -46,14 +41,14 @@ const StrategiesIcon = ({ iconUrls, iconNames, isDetailsPage = false }: Props) = } }, [iconUrls]) - const updateImageSize = (url: string, width: number, height: number) => { + const updateImageSize = (url: string, width: number) => { setImageSizes((prev) => ({ ...prev, - [url]: { width, height }, + [url]: width, })) } - const getImageSize = (url: string) => imageSizes[url] || { width: 20, height: 20 } + const getImageSize = (url: string) => imageSizes[url] || 22 const handleImageErr = (url: string) => { setValidImages((prev) => ({ ...prev, [url]: false })) @@ -71,15 +66,15 @@ const StrategiesIcon = ({ iconUrls, iconNames, isDetailsPage = false }: Props) = {iconUrls?.map((url, idx) => { const name = iconNames?.[idx] if (!url || !name || validImages[url] === false) return null - const { width, height } = getImageSize(url) + const width = getImageSize(url) return ( <div key={url} className={cx('icon-wrapper')}> <div className={cx('icon')} style={{ - width: `${width}px`, - height: `${height}px`, + width: `${width / 2}px`, + height: `21px`, }} data-tooltip-id={name} data-tooltip-content={name} From b25692d0ee9af2db194b9aaa6fae40a92dc2b06d Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 10:41:36 +0900 Subject: [PATCH 624/648] =?UTF-8?q?design:=20=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20height=20=EA=B3=A0=EC=A0=95=20(#296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/strategies-item/strategies-icon.tsx | 5 +---- app/(dashboard)/_ui/strategies-item/styles.module.scss | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx index 8e926158..8447e5d2 100644 --- a/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx +++ b/app/(dashboard)/_ui/strategies-item/strategies-icon.tsx @@ -72,10 +72,7 @@ const StrategiesIcon = ({ iconUrls, iconNames, isDetailsPage = false }: Props) = <div key={url} className={cx('icon-wrapper')}> <div className={cx('icon')} - style={{ - width: `${width / 2}px`, - height: `21px`, - }} + style={{ width: `${width / 2}px` }} data-tooltip-id={name} data-tooltip-content={name} data-tooltip-class-name="tooltip" diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index 0eba9dd1..072d1e6a 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -94,6 +94,7 @@ .icon-wrapper { .icon { position: relative; + height: 21px; } .tooltip { background-color: $color-gray-700; From 98fe152b340151783d150afcd7e66a21b0cbbb7d Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 6 Dec 2024 11:11:18 +0900 Subject: [PATCH 625/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20ca?= =?UTF-8?q?tegoty=20=ED=8E=98=EC=9D=B4=EC=A7=80=20axiosInstance=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts | 4 ++-- .../_ui/stock/stock-manage/_api/toggle-stock-active-state.ts | 4 ++-- app/admin/category/_ui/trade/trade-manage/_api/get-trades.ts | 4 ++-- .../_ui/trade/trade-manage/_api/toggle-trade-active-state.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts b/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts index 5da57c6c..8de844e5 100644 --- a/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts +++ b/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts @@ -1,10 +1,10 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { StockResponseModel } from '../types' const getStocks = async (activateState: boolean, page: number, size: number) => { try { - const res = await axios<StockResponseModel>('/api/admin/strategies/stock-type', { + const res = await axiosInstance<StockResponseModel>('/api/admin/strategies/stock-type', { params: { activateState, page, diff --git a/app/admin/category/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts b/app/admin/category/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts index 16d1a16b..43629679 100644 --- a/app/admin/category/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts +++ b/app/admin/category/_ui/stock/stock-manage/_api/toggle-stock-active-state.ts @@ -1,10 +1,10 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { ToggleStockActiveStateResponseModel } from '../types' const ToggleStockActiveState = async (stockTypeId: number) => { try { - const res = await axios.patch<ToggleStockActiveStateResponseModel>( + const res = await axiosInstance.patch<ToggleStockActiveStateResponseModel>( `/api/admin/strategies/stock-type/${stockTypeId}` ) diff --git a/app/admin/category/_ui/trade/trade-manage/_api/get-trades.ts b/app/admin/category/_ui/trade/trade-manage/_api/get-trades.ts index f661fbca..e676cc75 100644 --- a/app/admin/category/_ui/trade/trade-manage/_api/get-trades.ts +++ b/app/admin/category/_ui/trade/trade-manage/_api/get-trades.ts @@ -1,10 +1,10 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { TradeResponseModel } from '../types' const getTrades = async (activateState: boolean) => { try { - const res = await axios<TradeResponseModel>('/api/admin/strategies/trade-type', { + const res = await axiosInstance<TradeResponseModel>('/api/admin/strategies/trade-type', { params: { activateState, }, diff --git a/app/admin/category/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts b/app/admin/category/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts index 75733a4c..cee615a9 100644 --- a/app/admin/category/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts +++ b/app/admin/category/_ui/trade/trade-manage/_api/toggle-trade-active-state.ts @@ -1,10 +1,10 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { ToggleTradeActiveStateResponseModel } from '../types' const ToggleTradeActiveState = async (tradeTypeId: number) => { try { - const res = await axios.patch<ToggleTradeActiveStateResponseModel>( + const res = await axiosInstance.patch<ToggleTradeActiveStateResponseModel>( `/api/admin/strategies/trade-type/${tradeTypeId}` ) From 5769356e643e14c023e853b4dfa09f19d94614ef Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 11:18:08 +0900 Subject: [PATCH 626/648] =?UTF-8?q?feat:=20API=20=EC=97=B0=EB=8F=99=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/[traderId]/page.tsx | 98 ++++++++++--------- app/(dashboard)/traders/_api/get-traders.ts | 11 ++- .../traders/_hooks/use-get-traders.ts | 6 +- app/(dashboard)/traders/page.tsx | 59 ++++++----- shared/ui/traders-list-card/index.tsx | 2 +- .../traders-list-card.stories.tsx | 4 +- 6 files changed, 98 insertions(+), 82 deletions(-) diff --git a/app/(dashboard)/traders/[traderId]/page.tsx b/app/(dashboard)/traders/[traderId]/page.tsx index 3b975191..db486e49 100644 --- a/app/(dashboard)/traders/[traderId]/page.tsx +++ b/app/(dashboard)/traders/[traderId]/page.tsx @@ -1,48 +1,50 @@ -// 'use client' - -// import classNames from 'classnames/bind' - -// import BackHeader from '@/shared/ui/header/back-header' -// import Title from '@/shared/ui/title' -// import TradersListCard from '@/shared/ui/traders-list-card' - -// import ListHeader from '../../_ui/list-header' -// import StrategiesItem from '../../_ui/strategies-item' -// import useGetTraders from '../_hooks/use-get-traders' -// import styles from './page.module.scss' - -// const cx = classNames.bind(styles) - -// const TraderDetailPage = () => { -// const { data: trader, isLoading } = useGetTraders() - -// if (!trader) { -// return null -// } - -// return ( -// <> -// <div className={cx('page-container')}> -// <BackHeader label={'목록으로 돌아가기'} /> -// <div className={cx('title')}> -// <Title label={'트레이더 상세보기'}> -//
    -//
    -// -//
    -// - -// -//
    -// -// ) -// } - -// export default TraderDetailPage +'use client' + +import classNames from 'classnames/bind' + +import BackHeader from '@/shared/ui/header/back-header' +import Title from '@/shared/ui/title' +import TradersListCard from '@/shared/ui/traders-list-card' + +import ListHeader from '../../_ui/list-header' +import StrategiesItem from '../../_ui/strategies-item' +import useGetTraders from '../_hooks/use-get-traders' +import styles from './page.module.scss' + +const cx = classNames.bind(styles) + +const TraderDetailPage = () => { + // const { data, isLoading } = useGetTraders() + + // const traders = data?.content + + // if (!traders) { + // return null + // } + + return ( + <> +
    + +
    + +
    +
    + {/* */} +
    + + + {/* */} +
    + + ) +} + +export default TraderDetailPage diff --git a/app/(dashboard)/traders/_api/get-traders.ts b/app/(dashboard)/traders/_api/get-traders.ts index cfce388a..65fcc5c9 100644 --- a/app/(dashboard)/traders/_api/get-traders.ts +++ b/app/(dashboard)/traders/_api/get-traders.ts @@ -13,7 +13,6 @@ interface TradersResponseModel { isSuccess: boolean message: string result: { - result: string content: TraderModel[] page: number size: number @@ -22,12 +21,13 @@ interface TradersResponseModel { first: boolean last: boolean } + code: number } export interface TradersParamsModel { page: number size: number - keyword: string + keyword?: string orderBy: 'STRATEGY_TOTAL' | 'SUBSCRIBE_TOTAL' } @@ -35,9 +35,10 @@ export const getTraders = async ( params: TradersParamsModel ): Promise => { try { - const response = await axiosInstance.get('/api/users/traders', { - params, - }) + const { page, size, keyword = '', orderBy } = params + const response = await axiosInstance.get( + `/api/users/traders?sort=${orderBy}&page=${page}&size=${size}&keyword=${keyword}` + ) if (response.data.isSuccess) { return response.data.result diff --git a/app/(dashboard)/traders/_hooks/use-get-traders.ts b/app/(dashboard)/traders/_hooks/use-get-traders.ts index fb8e14cb..5524ebf3 100644 --- a/app/(dashboard)/traders/_hooks/use-get-traders.ts +++ b/app/(dashboard)/traders/_hooks/use-get-traders.ts @@ -2,10 +2,10 @@ import { useQuery } from '@tanstack/react-query' import { TradersParamsModel, getTraders } from '../_api/get-traders' -const useGetTraders = (params: TradersParamsModel) => { +const useGetTraders = ({ page, size, keyword, orderBy }: TradersParamsModel) => { return useQuery({ - queryKey: ['traders', params], - queryFn: () => getTraders(params), + queryKey: ['traders', page, size, keyword, orderBy], + queryFn: () => getTraders({ page, size, keyword, orderBy }), }) } diff --git a/app/(dashboard)/traders/page.tsx b/app/(dashboard)/traders/page.tsx index 61ca840e..1d5cbef4 100644 --- a/app/(dashboard)/traders/page.tsx +++ b/app/(dashboard)/traders/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { useRef, useState } from 'react' import styles from '@/app/(dashboard)/traders/page.module.scss' import { COUNT_PER_PAGE } from '@/app/admin/category/_ui/shared/manage-table/constant' @@ -20,20 +20,31 @@ import useGetTraders from './_hooks/use-get-traders' const cx = classNames.bind(styles) const TradersPage = () => { - const initialValue = null - const [value, setValue] = useState(initialValue) + const [selectedOption, setSelectedOption] = useState('STRATEGY_TOTAL') + const [searchKeyword, setSearchKeyword] = useState('') + const searchInputRef = useRef(null) + const { page, handlePageChange } = usePagination({ basePath: PATH.TRADERS, pageSize: COUNT_PER_PAGE, }) - const { data: traders } = useGetTraders({ - page: 1, + const { data } = useGetTraders({ + page, size: 12, - keyword: '영웅', - orderBy: 'STRATEGY_TOTAL', + keyword: searchKeyword, + orderBy: selectedOption as 'STRATEGY_TOTAL' | 'SUBSCRIBE_TOTAL', }) + const handleSearch = () => { + if (searchInputRef.current) { + setSearchKeyword(searchInputRef.current.value || '') + handlePageChange(1) + } + } + + const traders = data?.content + if (!traders) { return null } @@ -47,35 +58,37 @@ const TradersPage = () => {
    - +
    - {/* {traders?.result?.content?.map((trader) => ( + {traders.map((trader) => ( - ))} */} + ))}
    - + {data.totalElements > 0 && ( + + )}
    diff --git a/shared/ui/traders-list-card/index.tsx b/shared/ui/traders-list-card/index.tsx index 5ce6811a..4e07ce4c 100644 --- a/shared/ui/traders-list-card/index.tsx +++ b/shared/ui/traders-list-card/index.tsx @@ -13,7 +13,7 @@ interface Props { imageUrl: string strategyCount: number subscriberCount: number - userId: string | null + userId: number hasButton?: boolean } diff --git a/shared/ui/traders-list-card/traders-list-card.stories.tsx b/shared/ui/traders-list-card/traders-list-card.stories.tsx index 0efb520b..3a506edc 100644 --- a/shared/ui/traders-list-card/traders-list-card.stories.tsx +++ b/shared/ui/traders-list-card/traders-list-card.stories.tsx @@ -29,7 +29,7 @@ export const Default: StoryType = { imageUrl: 'https://lh3.googleusercontent.com/a/your-image-id', strategyCount: 10, subscriberCount: 10, - userId: '1234', + userId: 1234, }, } @@ -39,7 +39,7 @@ export const WithoutButton: StoryType = { imageUrl: 'https://lh3.googleusercontent.com/a/your-image-id', strategyCount: 10, subscriberCount: 10, - userId: '1234', + userId: 123, hasButton: false, }, } From 3a52a9cad11efa51c30cb8d9257ee272300992fe Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 6 Dec 2024 11:19:58 +0900 Subject: [PATCH 627/648] =?UTF-8?q?fix:=20=EC=BF=BC=EB=A6=AC=ED=82=A4?= =?UTF-8?q?=EB=A1=9C=20page=20=EC=B6=94=EA=B0=80=20(#301)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts | 2 +- .../strategies/[strategyId]/_hooks/query/use-get-analysis.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts b/app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts index 9d17cffd..3df8e5c8 100644 --- a/app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts +++ b/app/(dashboard)/my/_hooks/query/use-get-my-daily-analysis.ts @@ -4,7 +4,7 @@ import getMyDailyAnalysis from '../../_api/get-my-daily-analysis' const useGetAnalysis = (strategyId: number, page: number, size: number) => { return useQuery({ - queryKey: ['myDailyAnalysis', strategyId], + queryKey: ['myDailyAnalysis', strategyId, page], queryFn: () => getMyDailyAnalysis(strategyId, page, size), }) } diff --git a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts index 6327415c..44208c6d 100644 --- a/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts +++ b/app/(dashboard)/strategies/[strategyId]/_hooks/query/use-get-analysis.ts @@ -5,7 +5,7 @@ import getAnalysis from '../../_api/get-analysis' const useGetAnalysis = (strategyId: number, type: AnalysisTabType, page: number, size: number) => { return useQuery({ - queryKey: ['analysis', strategyId, type], + queryKey: ['analysis', strategyId, type, page], queryFn: () => getAnalysis(strategyId, type, page, size), }) } From 790cdf5494f3531f3dccda799a81ac10bd16ceee Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 6 Dec 2024 11:25:55 +0900 Subject: [PATCH 628/648] =?UTF-8?q?design:=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#301)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/details-information/styles.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/(dashboard)/_ui/details-information/styles.module.scss b/app/(dashboard)/_ui/details-information/styles.module.scss index ce316d35..46224894 100644 --- a/app/(dashboard)/_ui/details-information/styles.module.scss +++ b/app/(dashboard)/_ui/details-information/styles.module.scss @@ -36,6 +36,7 @@ width: 100%; height: 100%; border-right: 1px solid $color-gray-200; + padding-right: 4px; &:last-child { border-right: 0; } From 08d7995d1cff6e4fedf821f8d6d6eb2df6e8a650 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 6 Dec 2024 11:26:23 +0900 Subject: [PATCH 629/648] =?UTF-8?q?fix:=20=EB=B6=84=EC=84=9D=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=97=86=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?UI=ED=86=B5=EC=9D=BC=20(#301)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/analysis-content.tsx | 31 ++++++++++--------- .../analysis-container/statistics-content.tsx | 16 +++++----- .../_ui/analysis-container/styles.module.scss | 5 +-- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx index 1faf4b82..6b2f7f7e 100644 --- a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx @@ -110,8 +110,6 @@ const AnalysisContent = ({ } } - if (analysisData?.content === null || analysisData?.content === undefined) return null - return (
    {!isEditable && ( @@ -149,19 +147,22 @@ const AnalysisContent = ({
    )} - - - + {analysisData && ( + <> + + + + )} {uploadType && ( { - if (statisticsData === null || statisticsData === undefined) return null - - const statisticsDataToArray = Object.entries(statisticsData) - return (
    - {statisticsDataToArray.map(([title, data]) => ( - - ))} + {statisticsData ? ( + Object.entries(statisticsData).map(([title, data]) => ( + + )) + ) : ( +
    +

    업데이트 된 통계 데이터가 없습니다.

    +
    + )}
    ) } diff --git a/app/(dashboard)/_ui/analysis-container/styles.module.scss b/app/(dashboard)/_ui/analysis-container/styles.module.scss index f9622858..e139a8e9 100644 --- a/app/(dashboard)/_ui/analysis-container/styles.module.scss +++ b/app/(dashboard)/_ui/analysis-container/styles.module.scss @@ -91,9 +91,10 @@ .no-data { display: flex; justify-content: center; - margin: 60px 0 40px; + margin-top: 80px; color: $color-gray-600; - @include typo-b2; + height: 200px; + @include typo-b1; } .button-container { From 2406b730e76c453bdc844088d00fe904f79a90c3 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 6 Dec 2024 11:37:27 +0900 Subject: [PATCH 630/648] =?UTF-8?q?fix:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=EA=B2=BD=EC=9A=B0=20ui=EC=88=98=EC=A0=95=20(?= =?UTF-8?q?#301)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/analysis-content.tsx | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx index 6b2f7f7e..a87ea1e2 100644 --- a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx @@ -112,43 +112,43 @@ const AnalysisContent = ({ return (
    - {!isEditable && ( - - )} - {isEditable && ( -
    -
    - + {analysisData ? ( + <> + {!isEditable && ( -
    - -
    - )} - {analysisData && ( - <> + )} + {isEditable && ( +
    +
    + + +
    + +
    + )} + ) : ( +
    +

    업데이트 된 분석 데이터가 없습니다.

    +
    )} {uploadType && ( From 155fae6c7f41ed1e68d76b9fc5c91a0ccff2b9ad Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 6 Dec 2024 11:38:52 +0900 Subject: [PATCH 631/648] =?UTF-8?q?fix:=20=EC=A1=B0=EA=B1=B4=EB=AC=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#301)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis-container/analysis-content.tsx | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx index a87ea1e2..43b53475 100644 --- a/app/(dashboard)/_ui/analysis-container/analysis-content.tsx +++ b/app/(dashboard)/_ui/analysis-container/analysis-content.tsx @@ -112,43 +112,43 @@ const AnalysisContent = ({ return (
    - {analysisData ? ( - <> - {!isEditable && ( + {!isEditable && analysisData && ( + + )} + {isEditable && ( +
    +
    + - )} - {isEditable && ( -
    -
    - - -
    - -
    - )} +
    + +
    + )} + {analysisData ? ( + <> Date: Fri, 6 Dec 2024 12:23:59 +0900 Subject: [PATCH 632/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20ca?= =?UTF-8?q?tegoty=20=ED=8E=98=EC=9D=B4=EC=A7=80=20useSuspenseQuery?= =?UTF-8?q?=EC=99=80=20Suspense=EB=A1=9C=20=EC=B2=98=EB=A6=AC=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/manage-table/index.tsx | 2 +- .../_ui/stock/stock-manage/_api/get-stocks.ts | 2 +- .../_hooks/query/use-stocks-data.ts | 7 ++--- .../_ui/active-stock-manage-table.tsx | 29 +++++-------------- .../_ui/inactive-stock-manage-table.tsx | 12 ++++++-- .../_hooks/query/use-trades-data.ts | 4 +-- .../_ui/active-trade-manage-table.tsx | 5 +++- .../_ui/inactive-trade-manage-table.tsx | 5 +++- shared/ui/table/vertical/index.tsx | 7 ++--- 9 files changed, 34 insertions(+), 39 deletions(-) diff --git a/app/admin/category/_ui/shared/manage-table/index.tsx b/app/admin/category/_ui/shared/manage-table/index.tsx index f7c3dd62..884ac878 100644 --- a/app/admin/category/_ui/shared/manage-table/index.tsx +++ b/app/admin/category/_ui/shared/manage-table/index.tsx @@ -59,7 +59,7 @@ const Skeleton = ({ active, domain, size }: Pick{active ? '활성화' : '비활성화'}
    ) diff --git a/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts b/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts index 8de844e5..aed70b79 100644 --- a/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts +++ b/app/admin/category/_ui/stock/stock-manage/_api/get-stocks.ts @@ -2,7 +2,7 @@ import axiosInstance from '@/shared/api/axios' import { StockResponseModel } from '../types' -const getStocks = async (activateState: boolean, page: number, size: number) => { +const getStocks = async (activateState: boolean, page: number = 1, size: number = 10) => { try { const res = await axiosInstance('/api/admin/strategies/stock-type', { params: { diff --git a/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts index 1af8f245..77f43aca 100644 --- a/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts +++ b/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts @@ -1,16 +1,15 @@ -import { useQuery } from '@tanstack/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' import getStocks from '../../_api/get-stocks' type ArgType = 'active' | 'inactive' -const useStocksData = (activateState: ArgType, page: number, size: number, enabled?: boolean) => { +const useStocksData = (activateState: ArgType, page: number = 1, size: number = 10) => { const isActive = activateState === 'active' ? true : false - return useQuery({ + return useSuspenseQuery({ queryKey: ['adminStocks', activateState, page, size], queryFn: () => getStocks(isActive, page, size), - enabled, }) } diff --git a/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx index 5d9ec006..1adf7cb4 100644 --- a/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx +++ b/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -4,6 +4,8 @@ import { useState } from 'react' import Image from 'next/image' +import withSuspense from '@/shared/utils/with-suspense' + import ManageTable from '../../../shared/manage-table' import useStocksData from '../_hooks/query/use-stocks-data' import StockActiveStateToggleButton from './stock-active-state-toggle-button' @@ -13,39 +15,21 @@ const TABLE_BODY_SIZE = 10 const ActiveStockManageTable = () => { const [currentPage, setCurrentPage] = useState(1) - const { data: initialData, isLoading: isLoadingInitial } = useStocksData( - 'active', - currentPage, - TABLE_BODY_SIZE - ) - - const isUnderflow = initialData?.content.length === 0 && !initialData?.first - - if (isUnderflow) setCurrentPage((prev) => prev - 1) + const { data } = useStocksData('active', currentPage, TABLE_BODY_SIZE) - const { data: fallbackData, isLoading: isLoadingFallback } = useStocksData( - 'active', - currentPage - 1, - TABLE_BODY_SIZE, - isUnderflow - ) - - const data = initialData ?? fallbackData const tableData = data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, {stockTypeName}, , ]) ?? [] - if (isLoadingInitial || isLoadingFallback) return - return ( { ) } -export default ActiveStockManageTable +export default withSuspense( + ActiveStockManageTable, + +) diff --git a/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx index a96eb468..d0c80db6 100644 --- a/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx +++ b/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx @@ -1,3 +1,5 @@ +'use client' + import { useState } from 'react' import Image from 'next/image' @@ -14,14 +16,15 @@ const InactiveTradeManageTable = () => { const [currentPage, setCurrentPage] = useState(1) const { data } = useStocksData('inactive', currentPage, TABLE_BODY_SIZE) + const tableData = data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ stockTypeName, {stockTypeName}, , @@ -39,4 +42,7 @@ const InactiveTradeManageTable = () => { ) } -export default withSuspense(InactiveTradeManageTable, ) +export default withSuspense( + InactiveTradeManageTable, + +) diff --git a/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts b/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts index 7d32530d..50e151ad 100644 --- a/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts +++ b/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' import getTrades from '../../_api/get-trades' @@ -7,7 +7,7 @@ type ArgType = 'active' | 'inactive' const useTradeData = (activateState: ArgType) => { const isActive = activateState === 'active' ? true : false - return useQuery({ + return useSuspenseQuery({ queryKey: ['adminTrades', activateState], queryFn: () => getTrades(isActive), }) diff --git a/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx index d51da41f..cbe66174 100644 --- a/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx @@ -16,4 +16,7 @@ const ActiveTradeManageTable = () => { return } -export default withSuspense(ActiveTradeManageTable, ) +export default withSuspense( + ActiveTradeManageTable, + +) diff --git a/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx index aa54973c..f3c0f178 100644 --- a/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx @@ -16,4 +16,7 @@ const InactiveTradeManageTable = () => { return } -export default withSuspense(InactiveTradeManageTable, ) +export default withSuspense( + InactiveTradeManageTable, + +) diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index ebd14ad9..515dbd51 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -83,15 +83,12 @@ const VerticalTable = ({ ) } -const Skeleton = ({ tableHead, countPerPage, isEditable = false }: Partial) => { +const Skeleton = ({ tableHead, countPerPage }: Partial) => { return (
    - - {tableHead?.map((head) => )} - {isEditable && } - + {tableHead?.map((head) => )}
    {head}
    {head}
    From 2b934c34bcb170b0fcdaf3e804c996166a18830e Mon Sep 17 00:00:00 2001 From: nanafromjeju Date: Fri, 6 Dec 2024 12:32:58 +0900 Subject: [PATCH 633/648] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20API=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_api/patch-profile.ts | 55 +++++++------------ .../my/_hooks/query/use-patch-profile.ts | 53 +++++++++--------- .../notices/_api/get-notice-detail.ts | 17 +++--- shared/api/check-duplicate.ts | 8 +-- 4 files changed, 59 insertions(+), 74 deletions(-) diff --git a/app/(dashboard)/my/_api/patch-profile.ts b/app/(dashboard)/my/_api/patch-profile.ts index a16f4e95..e613effb 100644 --- a/app/(dashboard)/my/_api/patch-profile.ts +++ b/app/(dashboard)/my/_api/patch-profile.ts @@ -1,38 +1,25 @@ -// import axiosInstance from '@/shared/api/axios' +import axiosInstance from '@/shared/api/axios' -// interface ImageDto { -// imageName: string -// size: number -// } +import { UserProfileModel } from '../_hooks/query/use-patch-profile' -// interface PatchUserProfileRequest { -// nickname: string -// password: string | null -// email?: string -// phone: string -// imageChange: boolean -// imageDto?: ImageDto -// } +interface PatchUserProfileModel { + isSuccess: boolean + message: string + result: string + code: number +} -// interface PatchUserProfileResponse { -// isSuccess: boolean -// message: string -// data?: { -// presignedUrl?: string -// } -// } +const patchUserProfile = async (data: UserProfileModel) => { + try { + const response = await axiosInstance.patch( + `/api/users/mypage/profile`, + data + ) + return response.data + } catch (err) { + console.error('Error updating user profile:', err) + throw err + } +} -// const patchUserProfile = async (userId: number, profileData: PatchUserProfileRequest) => { -// try { -// const response = await axiosInstance.patch( -// `/api/users/mypage/profile`, -// profileData -// ) -// return response.data.result -// } catch (err) { -// console.error('Error updating user profile:', err) -// throw err -// } -// } - -// export default patchUserProfile +export default patchUserProfile diff --git a/app/(dashboard)/my/_hooks/query/use-patch-profile.ts b/app/(dashboard)/my/_hooks/query/use-patch-profile.ts index ec985d89..6d122247 100644 --- a/app/(dashboard)/my/_hooks/query/use-patch-profile.ts +++ b/app/(dashboard)/my/_hooks/query/use-patch-profile.ts @@ -1,32 +1,31 @@ -// import { useMutation, useQueryClient } from '@tanstack/react-query' +import { useMutation, useQueryClient } from '@tanstack/react-query' -// import patchProfile from '../../_api/patch-profile' +import patchProfile from '../../_api/patch-profile' -// interface PatchUserProfileData { -// nickname: 'string' -// password: 'string' -// imageDto: { -// imageName: 'string' -// size: 2097152 -// } -// phone: 'string' -// email: 'string' -// imageChange: true -// } +export interface UserProfileModel { + nickname?: string + password?: string + imageDto?: { + imageName: string + size: number + } + phone?: string + email?: string + imageChange: boolean +} -// const usePatchUserProfile = () => { -// const queryClient = useQueryClient() +const usePatchUserProfile = () => { + const queryClient = useQueryClient() -// return useMutation({ -// mutationFn: ({ userId, profileData }: PatchUserProfileData) => -// patchProfile(userId, profileData), -// onSuccess: () => { -// queryClient.invalidateQueries({ queryKey: ['userProfile'] }) -// }, -// onError: (error) => { -// console.error('Error updating user profile:', error) -// }, -// }) -// } + return useMutation({ + mutationFn: (data: UserProfileModel) => patchProfile(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['userProfile'] }) + }, + onError: (error) => { + console.error('Error updating user profile:', error) + }, + }) +} -// export default usePatchUserProfile +export default usePatchUserProfile diff --git a/app/(landing)/notices/_api/get-notice-detail.ts b/app/(landing)/notices/_api/get-notice-detail.ts index 44a36bca..e4b677b4 100644 --- a/app/(landing)/notices/_api/get-notice-detail.ts +++ b/app/(landing)/notices/_api/get-notice-detail.ts @@ -2,24 +2,23 @@ import axiosInstance from '@/shared/api/axios' interface NoticeResponseModel { isSuccess: true - message: 'string' + message: '공지사항 상세 조회' result: { - title: 'string' - content: 'string' - createdAt: '2024-12-05T21:49:06.561Z' + title: '제목' + content: '내용' + createdAt: '2024-12-06 11:24:59' files: [ { - fileName: 'string' - noticeFileId: 0 + fileName: '2.jpg' + noticeFileId: 3 }, ] } - code: 0 } export const getNoticeDetail = async (noticeId: number): Promise => { try { - const response = await axiosInstance.get(`/api/notices/${noticeId}`) + const response = await axiosInstance.get(`/api/notice/${noticeId}`) if (response.data.isSuccess) { return response.data.result @@ -28,6 +27,6 @@ export const getNoticeDetail = async (noticeId: number): Promise { try { - const response = await axios.get(`/api/users/check/nickname?nickname=${nickname}`) + const response = await axiosInstance.get(`/api/users/check/nickname?nickname=${nickname}`) return response.data } catch (err) { console.error(err) @@ -12,7 +12,7 @@ export const checkNicknameDuplicate = async (nickname: string) => { export const checkPhoneDuplicate = async (phone: string) => { try { - const response = await axios.get(`/api/users/check/phone?phone=${phone}`) + const response = await axiosInstance.get(`/api/users/check/phone?phone=${phone}`) return response.data } catch (err) { console.error(err) @@ -24,7 +24,7 @@ export const checkEmailDuplicate = async (email: string, domain: string) => { const emailAddress = `${email}@${domain}` try { - const response = await axios.get(`/api/users/check/email?email=${emailAddress}`) + const response = await axiosInstance.get(`/api/users/check/email?email=${emailAddress}`) return response.data } catch (err) { console.error(err) From aaa9cb29558e53d3b3bdcc102e0106c7bcba04c5 Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 6 Dec 2024 12:44:26 +0900 Subject: [PATCH 634/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20ca?= =?UTF-8?q?tegoty=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A5=BC=20=EA=B0=80?= =?UTF-8?q?=EA=B3=B5=ED=95=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20api=EB=A1=9C?= =?UTF-8?q?=20=EB=94=B0=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared/manage-table/index.tsx | 2 +- .../set-admin-stock-manage-table-data.tsx | 19 +++++++++++++++++++ .../_ui/active-stock-manage-table.tsx | 18 ++---------------- .../_ui/inactive-stock-manage-table.tsx | 18 ++---------------- .../_ui/trade/trade-manage/_api/get-trades.ts | 1 + .../set-admin-trade-manage-table-data.tsx | 19 +++++++++++++++++++ .../_ui/active-trade-manage-table.tsx | 9 ++------- .../_ui/inactive-trade-manage-table.tsx | 9 ++------- 8 files changed, 48 insertions(+), 47 deletions(-) create mode 100644 app/admin/category/_ui/stock/stock-manage/_api/set-admin-stock-manage-table-data.tsx create mode 100644 app/admin/category/_ui/trade/trade-manage/_api/set-admin-trade-manage-table-data.tsx diff --git a/app/admin/category/_ui/shared/manage-table/index.tsx b/app/admin/category/_ui/shared/manage-table/index.tsx index 884ac878..ca6bb0ca 100644 --- a/app/admin/category/_ui/shared/manage-table/index.tsx +++ b/app/admin/category/_ui/shared/manage-table/index.tsx @@ -59,7 +59,7 @@ const Skeleton = ({ active, domain, size }: Pick{active ? '활성화' : '비활성화'}
    ) diff --git a/app/admin/category/_ui/stock/stock-manage/_api/set-admin-stock-manage-table-data.tsx b/app/admin/category/_ui/stock/stock-manage/_api/set-admin-stock-manage-table-data.tsx new file mode 100644 index 00000000..c4ceb326 --- /dev/null +++ b/app/admin/category/_ui/stock/stock-manage/_api/set-admin-stock-manage-table-data.tsx @@ -0,0 +1,19 @@ +import Image from 'next/image' + +import StockActiveStateToggleButton from '../_ui/stock-active-state-toggle-button' +import { StockResponseModel } from '../types' + +const setAdminStockManageTableData = (data: StockResponseModel['result']) => + data?.content.map((data) => [ + data.stockTypeName, + {data.stockTypeName}, + , + ]) ?? [] + +export default setAdminStockManageTableData diff --git a/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx index 1adf7cb4..f2a548d7 100644 --- a/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx +++ b/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -2,13 +2,11 @@ import { useState } from 'react' -import Image from 'next/image' - import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' +import setAdminStockManageTableData from '../_api/set-admin-stock-manage-table-data' import useStocksData from '../_hooks/query/use-stocks-data' -import StockActiveStateToggleButton from './stock-active-state-toggle-button' const TABLE_BODY_SIZE = 10 @@ -16,19 +14,7 @@ const ActiveStockManageTable = () => { const [currentPage, setCurrentPage] = useState(1) const { data } = useStocksData('active', currentPage, TABLE_BODY_SIZE) - - const tableData = - data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ - stockTypeName, - {stockTypeName}, - , - ]) ?? [] + const tableData = setAdminStockManageTableData(data) return ( { const [currentPage, setCurrentPage] = useState(1) const { data } = useStocksData('inactive', currentPage, TABLE_BODY_SIZE) - - const tableData = - data?.content.map(({ stockTypeName, stockTypeIconUrl, stockTypeId }) => [ - stockTypeName, - {stockTypeName}, - , - ]) ?? [] + const tableData = setAdminStockManageTableData(data) return ( { return res.data } catch (err) { console.log('Error : ' + err) + throw err } } diff --git a/app/admin/category/_ui/trade/trade-manage/_api/set-admin-trade-manage-table-data.tsx b/app/admin/category/_ui/trade/trade-manage/_api/set-admin-trade-manage-table-data.tsx new file mode 100644 index 00000000..fa4a110e --- /dev/null +++ b/app/admin/category/_ui/trade/trade-manage/_api/set-admin-trade-manage-table-data.tsx @@ -0,0 +1,19 @@ +import Image from 'next/image' + +import StockActiveStateToggleButton from '../../../stock/stock-manage/_ui/stock-active-state-toggle-button' +import { TradeResponseModel } from '../types' + +const setAdminTradeManageTableData = (data: TradeResponseModel['result']) => + data?.map((data) => [ + data.tradeName, + {data.tradeName}, + , + ]) ?? [] + +export default setAdminTradeManageTableData diff --git a/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx index cbe66174..6e3606a2 100644 --- a/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx @@ -1,17 +1,12 @@ import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' +import setAdminTradeManageTableData from '../_api/set-admin-trade-manage-table-data' import useTradeData from '../_hooks/query/use-trades-data' -import TradeActiveStateToggleButton from './trade-active-state-toggle-button' const ActiveTradeManageTable = () => { const { data } = useTradeData('active') - const tableData = - data?.result?.map(({ tradeName, tradeTypeIconUrl, tradeTypeId }) => [ - tradeName, - {tradeName}, - , - ]) ?? [] + const tableData = setAdminTradeManageTableData(data.result) return } diff --git a/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx index f3c0f178..d9d0c487 100644 --- a/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx @@ -1,17 +1,12 @@ import withSuspense from '@/shared/utils/with-suspense' import ManageTable from '../../../shared/manage-table' +import setAdminTradeManageTableData from '../_api/set-admin-trade-manage-table-data' import useTradeData from '../_hooks/query/use-trades-data' -import TradeActiveStateToggleButton from './trade-active-state-toggle-button' const InactiveTradeManageTable = () => { const { data } = useTradeData('inactive') - const tableData = - data?.result?.map(({ tradeName, tradeTypeIconUrl, tradeTypeId }) => [ - tradeName, - {tradeName}, - , - ]) ?? [] + const tableData = setAdminTradeManageTableData(data.result) return } From efab2e5af196b777f9410be79b55e2dec23722dc Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 6 Dec 2024 13:04:37 +0900 Subject: [PATCH 635/648] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=20=ED=81=B4?= =?UTF-8?q?=EB=A0=88=EC=8A=A4=EB=AA=85=20=EC=88=98=EC=A0=95=20(#304)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/modal/account-image-modal.tsx | 2 +- shared/ui/modal/styles.module.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/ui/modal/account-image-modal.tsx b/shared/ui/modal/account-image-modal.tsx index a38aaa60..723a1a23 100644 --- a/shared/ui/modal/account-image-modal.tsx +++ b/shared/ui/modal/account-image-modal.tsx @@ -17,7 +17,7 @@ interface Props { const AccountImageModal = ({ isOpen, title, url, onClose }: Props) => { return ( - +
    {'imageData.title'}
    diff --git a/shared/ui/modal/styles.module.scss b/shared/ui/modal/styles.module.scss index 0311d986..dcda2eb6 100644 --- a/shared/ui/modal/styles.module.scss +++ b/shared/ui/modal/styles.module.scss @@ -34,7 +34,7 @@ } } } - &.account { + &.medium { width: 600px; } &.big { From c5e3e2f5c20edc7921200e9e8905e3f4a67a286b Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 6 Dec 2024 13:05:43 +0900 Subject: [PATCH 636/648] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EA=B0=80=20=EC=95=84=EB=8B=8C=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A1=9C=20=EC=A0=84=EB=9E=B5=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=EB=AA=A8=EB=8B=AC=EC=98=A4=ED=94=88=20(#3?= =?UTF-8?q?04)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/_ui/strategies-item/index.tsx | 131 ++++++++++-------- .../_ui/strategies-item/styles.module.scss | 2 + .../_ui/strategies-item/subscribe.tsx | 50 ++----- shared/hooks/custom/use-modal.ts | 3 +- shared/ui/modal/signin-check-modal.tsx | 15 +- 5 files changed, 105 insertions(+), 96 deletions(-) diff --git a/app/(dashboard)/_ui/strategies-item/index.tsx b/app/(dashboard)/_ui/strategies-item/index.tsx index 99cc5750..63a99ae4 100644 --- a/app/(dashboard)/_ui/strategies-item/index.tsx +++ b/app/(dashboard)/_ui/strategies-item/index.tsx @@ -1,10 +1,14 @@ -import Link from 'next/link' +import { useRouter } from 'next/navigation' import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' +import useModal from '@/shared/hooks/custom/use-modal' +import { useAuthStore } from '@/shared/stores/use-auth-store' import { StrategiesModel } from '@/shared/types/strategy-data' import { Button } from '@/shared/ui/button' import { LinkButton } from '@/shared/ui/link-button' +import SigninCheckModal from '@/shared/ui/modal/signin-check-modal' import { formatNumber } from '@/shared/utils/format' import AreaChart from './area-chart' @@ -20,63 +24,80 @@ interface Props { } const StrategiesItem = ({ strategiesData: data, type = 'default' }: Props) => { + const router = useRouter() + const user = useAuthStore((state) => state.user) + const { isModalOpen, openModal, closeModal } = useModal() + + const handleRouter = () => { + if (!user) { + openModal() + } else { + router.push(`${PATH.STRATEGIES}/${data.strategyId}`) + } + } + return ( - - - -
    -

    {formatNumber(data.mdd)}

    -
    -
    -

    {data.smScore}

    -
    -
    - 누적 수익률 -

    {data.cumulativeProfitRate}%

    - 최근 1년 수익률 -

    {data.recentYearProfitLossRate ? data.recentYearProfitLossRate + '%' : '-'}

    -
    - {type === 'default' && ( -
    - + <> + +
    +

    {data.smScore}

    +
    +
    + 누적 수익률 +

    {data.cumulativeProfitRate.toFixed(2)}%

    + 최근 1년 수익률 +

    + {data.recentYearProfitLossRate ? data.recentYearProfitLossRate.toFixed(2) + '%' : '-'} +

    +
    + {type === 'default' && ( +
    +
    - - )} - + )} + {type === 'my' && ( + <> +
    +

    {data.isPublic === 'PUBLIC' ? '공개' : '비공개'}

    +
    +
    + + 관리 + + +
    + + )} + + + ) } diff --git a/app/(dashboard)/_ui/strategies-item/styles.module.scss b/app/(dashboard)/_ui/strategies-item/styles.module.scss index 072d1e6a..6f871b2d 100644 --- a/app/(dashboard)/_ui/strategies-item/styles.module.scss +++ b/app/(dashboard)/_ui/strategies-item/styles.module.scss @@ -52,6 +52,8 @@ .title { @include typo-h4; @include ellipsis(1); + display: flex; + justify-content: flex-start; } .trader-profile { display: flex; diff --git a/app/(dashboard)/_ui/strategies-item/subscribe.tsx b/app/(dashboard)/_ui/strategies-item/subscribe.tsx index d4ca9162..8e9d7a78 100644 --- a/app/(dashboard)/_ui/strategies-item/subscribe.tsx +++ b/app/(dashboard)/_ui/strategies-item/subscribe.tsx @@ -2,15 +2,11 @@ import { useEffect, useState } from 'react' -import { useRouter } from 'next/navigation' - import { BookmarkIcon, BookmarkOutlineIcon } from '@/public/icons' import classNames from 'classnames/bind' -import { PATH } from '@/shared/constants/path' import useModal from '@/shared/hooks/custom/use-modal' import { useAuthStore } from '@/shared/stores/use-auth-store' -import SigninCheckModal from '@/shared/ui/modal/signin-check-modal' import SubscribeWarningModal from '@/shared/ui/modal/subscribe-warning-modal' import useGetSubscribe from '../../strategies/_hooks/query/use-get-subscribe' @@ -26,18 +22,8 @@ interface Props { const Subscribe = ({ strategyId, subscriptionStatus, traderName }: Props) => { const [isSubscribed, setIsSubscribed] = useState(false) - const router = useRouter() const user = useAuthStore((state) => state.user) - const { - isModalOpen: isSigninCheckModalOpen, - openModal: openSigninCheckModal, - closeModal: closeSigninCheckModal, - } = useModal() - const { - isModalOpen: isSubscribeWarningModal, - openModal: openSubscribeWarningModal, - closeModal: closeSubscribeWarningModal, - } = useModal() + const { isModalOpen, openModal, closeModal } = useModal() const { mutate } = useGetSubscribe() useEffect(() => { @@ -47,37 +33,27 @@ const Subscribe = ({ strategyId, subscriptionStatus, traderName }: Props) => { }, [subscriptionStatus]) const handleSubscribe = (e: React.MouseEvent) => { - e.preventDefault() - if (!user) { - openSigninCheckModal() - } else if (user?.nickname === traderName) { - openSubscribeWarningModal() - } else { - mutate(strategyId, { - onSuccess: () => setIsSubscribed(!isSubscribed), - }) + if (user) { + e.stopPropagation() + if (user?.nickname === traderName) { + openModal() + } else { + e.preventDefault() + mutate(strategyId, { + onSuccess: () => setIsSubscribed(!isSubscribed), + }) + } } } - const handleRoute = () => router.push(PATH.SIGN_IN) - return ( <>
    -
    - - - + ) } diff --git a/shared/hooks/custom/use-modal.ts b/shared/hooks/custom/use-modal.ts index 20c0047d..bbfa2112 100644 --- a/shared/hooks/custom/use-modal.ts +++ b/shared/hooks/custom/use-modal.ts @@ -6,9 +6,10 @@ const useModal = () => { const [isModalOpen, setIsModalOpen] = useState(false) const openModal = () => setIsModalOpen(true) + const closeModal = (e?: React.MouseEvent) => { if (e) { - e.preventDefault() + e.stopPropagation() } setIsModalOpen(false) } diff --git a/shared/ui/modal/signin-check-modal.tsx b/shared/ui/modal/signin-check-modal.tsx index d9942a84..dc1c0294 100644 --- a/shared/ui/modal/signin-check-modal.tsx +++ b/shared/ui/modal/signin-check-modal.tsx @@ -2,9 +2,13 @@ import React from 'react' +import { useRouter } from 'next/navigation' + import { ModalAlertIcon } from '@/public/icons' import classNames from 'classnames/bind' +import { PATH } from '@/shared/constants/path' + import Modal from '.' import { Button } from '../button' import styles from './styles.module.scss' @@ -14,10 +18,15 @@ const cx = classNames.bind(styles) interface Props { isModalOpen: boolean onCloseModal: () => void - onChange?: () => void } -const SigninCheckModal = ({ isModalOpen, onCloseModal, onChange }: Props) => { +const SigninCheckModal = ({ isModalOpen, onCloseModal }: Props) => { + const router = useRouter() + + const handleRouter = () => { + router.push(PATH.SIGN_IN) + } + return ( @@ -25,7 +34,7 @@ const SigninCheckModal = ({ isModalOpen, onCloseModal, onChange }: Props) => {
    -
    From e4ba373c7c1b1c275f63a224332e3366c4b465f5 Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 6 Dec 2024 13:07:57 +0900 Subject: [PATCH 637/648] =?UTF-8?q?fix:=20file=20input=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20admin/=5Fui=EB=A1=9C=20=EC=98=AE?= =?UTF-8?q?=EA=B9=80=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/shared => _ui}/file-input/index.tsx | 0 .../file-input/styles.module.scss | 0 .../shared/manage-table/file-input/index.tsx | 32 -------------- .../_ui/stock-post-button/index.tsx | 2 +- .../_ui/trade-post-button/index.tsx | 2 +- .../notices/post/_ui/file-input/index.tsx | 38 ---------------- .../post/_ui/file-input/styles.module.scss | 44 ------------------- app/admin/notices/post/page.tsx | 2 +- 8 files changed, 3 insertions(+), 117 deletions(-) rename app/admin/{category/_ui/shared => _ui}/file-input/index.tsx (100%) rename app/admin/{category/_ui/shared => _ui}/file-input/styles.module.scss (100%) delete mode 100644 app/admin/category/_ui/shared/manage-table/file-input/index.tsx delete mode 100644 app/admin/notices/post/_ui/file-input/index.tsx delete mode 100644 app/admin/notices/post/_ui/file-input/styles.module.scss diff --git a/app/admin/category/_ui/shared/file-input/index.tsx b/app/admin/_ui/file-input/index.tsx similarity index 100% rename from app/admin/category/_ui/shared/file-input/index.tsx rename to app/admin/_ui/file-input/index.tsx diff --git a/app/admin/category/_ui/shared/file-input/styles.module.scss b/app/admin/_ui/file-input/styles.module.scss similarity index 100% rename from app/admin/category/_ui/shared/file-input/styles.module.scss rename to app/admin/_ui/file-input/styles.module.scss diff --git a/app/admin/category/_ui/shared/manage-table/file-input/index.tsx b/app/admin/category/_ui/shared/manage-table/file-input/index.tsx deleted file mode 100644 index 335a833a..00000000 --- a/app/admin/category/_ui/shared/manage-table/file-input/index.tsx +++ /dev/null @@ -1,32 +0,0 @@ -'use client' - -import { ComponentPropsWithoutRef } from 'react' - -import { FileIcon } from '@/public/icons' -import classNames from 'classnames/bind' - -import styles from './styles.module.scss' - -const cx = classNames.bind(styles) - -interface Props extends ComponentPropsWithoutRef<'input'> { - placeholder?: string - onSearchIconClick?: () => void -} - -const FileInput = ({ placeholder = '', value, onChange, ...props }: Props) => { - return ( -
    - - -
    - ) -} - -export default FileInput diff --git a/app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx index 44419b91..1bb449aa 100644 --- a/app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx +++ b/app/admin/category/_ui/stock/stock-manage/_ui/stock-post-button/index.tsx @@ -2,7 +2,7 @@ import { FormEvent } from 'react' -import FileInput from '@/app/admin/category/_ui/shared/file-input' +import FileInput from '@/app/admin/_ui/file-input' import { RegisterIcon } from '@/public/icons' import { useQueryClient } from '@tanstack/react-query' import classNames from 'classnames/bind' diff --git a/app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx index 4ac7abc0..69808927 100644 --- a/app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/trade-post-button/index.tsx @@ -2,7 +2,7 @@ import { FormEvent } from 'react' -import FileInput from '@/app/admin/category/_ui/shared/file-input' +import FileInput from '@/app/admin/_ui/file-input' import { RegisterIcon } from '@/public/icons' import { useQueryClient } from '@tanstack/react-query' import classNames from 'classnames/bind' diff --git a/app/admin/notices/post/_ui/file-input/index.tsx b/app/admin/notices/post/_ui/file-input/index.tsx deleted file mode 100644 index 5b2ec3f9..00000000 --- a/app/admin/notices/post/_ui/file-input/index.tsx +++ /dev/null @@ -1,38 +0,0 @@ -'use client' - -import { ComponentPropsWithoutRef } from 'react' - -import Image from 'next/image' - -import { FileIcon } from '@/public/icons' -import classNames from 'classnames/bind' - -import styles from './styles.module.scss' - -const cx = classNames.bind(styles) - -interface Props extends ComponentPropsWithoutRef<'input'> { - accept?: string - preview?: string -} - -const FileInput = ({ preview, accept = '*', onChange, ...props }: Props) => { - return ( -
    - {preview && ( - Preview - )} - - -
    - ) -} - -export default FileInput diff --git a/app/admin/notices/post/_ui/file-input/styles.module.scss b/app/admin/notices/post/_ui/file-input/styles.module.scss deleted file mode 100644 index 1437a7c1..00000000 --- a/app/admin/notices/post/_ui/file-input/styles.module.scss +++ /dev/null @@ -1,44 +0,0 @@ -.container { - position: relative; - display: inline-block; - width: 100%; - height: 40px; - border-radius: 4px; - border: 1px solid $color-gray-400; - color: $color-gray-400; - @include typo-c1; - - svg { - width: 24px; - } -} - -.input { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0; - z-index: 10; - - &:hover { - cursor: pointer; - } -} - -.preview { - position: absolute; - left: 12px; - top: 50%; - transform: translateY(-50%); -} - -.icon { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - width: 24px; - color: $color-gray-500; -} diff --git a/app/admin/notices/post/page.tsx b/app/admin/notices/post/page.tsx index e2fd3991..25180303 100644 --- a/app/admin/notices/post/page.tsx +++ b/app/admin/notices/post/page.tsx @@ -8,10 +8,10 @@ import { Input } from '@/shared/ui/input' import { Textarea } from '@/shared/ui/textarea' import Title from '@/shared/ui/title' +import FileInput from '../../_ui/file-input' import InputField from '../../_ui/input-field' import useNoticeForm from './_hooks/use-notice-form' import usePostNotice from './_hooks/use-notice-post' -import FileInput from './_ui/file-input' import styles from './page.module.scss' const cx = classNames.bind(styles) From f002c602fcbe4bc0f09a25fa61511bc1f7e79947 Mon Sep 17 00:00:00 2001 From: kimpra Date: Fri, 6 Dec 2024 13:15:35 +0900 Subject: [PATCH 638/648] =?UTF-8?q?fix:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20ca?= =?UTF-8?q?tegoty=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9Capi=20axiosInstance?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/category/_ui/shared/_api/get-presigned-url.ts | 4 ++-- .../_ui/shared/_api/upload-file-with-presigned-url.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/admin/category/_ui/shared/_api/get-presigned-url.ts b/app/admin/category/_ui/shared/_api/get-presigned-url.ts index ab9b70a5..fbfd8346 100644 --- a/app/admin/category/_ui/shared/_api/get-presigned-url.ts +++ b/app/admin/category/_ui/shared/_api/get-presigned-url.ts @@ -1,4 +1,4 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' import { PresignedUrlResponseModel } from '../../trade/trade-manage/types' import { DomainType } from '../_hooks/use-category-icon-post' @@ -22,7 +22,7 @@ const getPresignedUrl = async ( size: imageSize, } - const res = await axios.post( + const res = await axiosInstance.post( `/api/admin/strategies/${domain}-type`, body ) diff --git a/app/admin/category/_ui/shared/_api/upload-file-with-presigned-url.ts b/app/admin/category/_ui/shared/_api/upload-file-with-presigned-url.ts index 1dca39bf..eb561b65 100644 --- a/app/admin/category/_ui/shared/_api/upload-file-with-presigned-url.ts +++ b/app/admin/category/_ui/shared/_api/upload-file-with-presigned-url.ts @@ -1,7 +1,7 @@ -import axios from 'axios' +import axiosInstance from '@/shared/api/axios' const uploadFileWithPresignedUrl = async (presignedUrl: string, file: File) => { - await axios.put(presignedUrl, file, { + await axiosInstance.put(presignedUrl, file, { headers: { 'Content-Type': file.type, }, From 0866cbf9ac84fd9d93b91963aa6d89b6a6252bca Mon Sep 17 00:00:00 2001 From: James Date: Fri, 6 Dec 2024 13:46:36 +0900 Subject: [PATCH 639/648] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20=EB=B0=8F=20?= =?UTF-8?q?API=20=EC=97=B0=EA=B2=B0=20(#170)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/_api/add-strategy.ts | 70 ++++ .../my/_constants/investment-amount.ts | 33 ++ .../my/_hooks/query/use-add-strategy.ts | 59 ++++ app/(dashboard)/my/strategies/add/page.tsx | 308 ++++++++++++++++++ .../my/strategies/add/styles.module.scss | 144 ++++++++ 5 files changed, 614 insertions(+) create mode 100644 app/(dashboard)/my/_api/add-strategy.ts create mode 100644 app/(dashboard)/my/_constants/investment-amount.ts create mode 100644 app/(dashboard)/my/_hooks/query/use-add-strategy.ts create mode 100644 app/(dashboard)/my/strategies/add/page.tsx create mode 100644 app/(dashboard)/my/strategies/add/styles.module.scss diff --git a/app/(dashboard)/my/_api/add-strategy.ts b/app/(dashboard)/my/_api/add-strategy.ts new file mode 100644 index 00000000..d78a692d --- /dev/null +++ b/app/(dashboard)/my/_api/add-strategy.ts @@ -0,0 +1,70 @@ +import axiosInstance from '@/shared/api/axios' + +export interface StockTypeModel { + stockTypeId: number + stockTypeName: string + stockIconUrl: string +} + +export interface TradeTypeModel { + tradeTypeId: number + tradeTypeName: string + tradeTypeIconUrl: string +} + +export interface StrategyTypeResponseModel { + isSuccess: boolean + message: string + result: { + stockTypes: StockTypeModel[] + tradeTypes: TradeTypeModel[] + } +} + +export type OperationCycleType = 'DAY' | 'POSITION' + +export type MinimumInvestmentAmountType = + | 'UNDER_10K' + | 'UP_TO_500K' + | 'UP_TO_1M' + | 'UP_TO_2M' + | 'UP_TO_5M' + | 'FROM_5M_TO_10M' + | 'FROM_10M_TO_20M' + | 'FROM_20M_TO_30M' + | 'FROM_30M_TO_40M' + | 'FROM_40M_TO_50M' + | 'FROM_50M_TO_100M' + | 'ABOVE_100M' + +export interface ProposalFileInfoModel { + proposalFileName: string + proposalFileSize: number +} + +export interface StrategyModel { + strategyName: string + tradeTypeId: number + operationCycle: OperationCycleType + stockTypeIds: number[] + minimumInvestmentAmount: MinimumInvestmentAmountType + description: string + proposalFile?: ProposalFileInfoModel +} + +export interface StrategyResponseModel { + isSuccess: boolean + message: string + result: { + presignedUrl: string + } + code: number +} + +export const strategyApi = { + getStrategyTypes: () => + axiosInstance.get('/api/my-strategies/register'), + + registerStrategy: (data: StrategyModel) => + axiosInstance.post('/api/my-strategies/register', data), +} diff --git a/app/(dashboard)/my/_constants/investment-amount.ts b/app/(dashboard)/my/_constants/investment-amount.ts new file mode 100644 index 00000000..be410c08 --- /dev/null +++ b/app/(dashboard)/my/_constants/investment-amount.ts @@ -0,0 +1,33 @@ +import { MinimumInvestmentAmountType, OperationCycleType } from '../_api/add-strategy' + +const INVESTMENT_AMOUNT_MAP: Record = { + UNDER_10K: '1만원 ~ 500만원', + UP_TO_500K: '500만원', + UP_TO_1M: '1000만원', + UP_TO_2M: '2000만원', + UP_TO_5M: '5000만원', + FROM_5M_TO_10M: '5000만원 ~ 1억', + FROM_10M_TO_20M: '1억 ~ 2억', + FROM_20M_TO_30M: '2억 ~ 3억', + FROM_30M_TO_40M: '3억 ~ 4억', + FROM_40M_TO_50M: '4억 ~ 5억', + FROM_50M_TO_100M: '5억 ~ 10억', + ABOVE_100M: '10억 이상', +} + +const OPERATION_CYCLE_MAP: Record = { + DAY: '데이', + POSITION: '포지션', +} + +export const minimumInvestmentAmountOptions = Object.entries(INVESTMENT_AMOUNT_MAP).map( + ([value, label]) => ({ + value, + label, + }) +) + +export const operationCycleOptions = Object.entries(OPERATION_CYCLE_MAP).map(([value, label]) => ({ + value, + label, +})) diff --git a/app/(dashboard)/my/_hooks/query/use-add-strategy.ts b/app/(dashboard)/my/_hooks/query/use-add-strategy.ts new file mode 100644 index 00000000..73f69c94 --- /dev/null +++ b/app/(dashboard)/my/_hooks/query/use-add-strategy.ts @@ -0,0 +1,59 @@ +import { useState } from 'react' + +import { useRouter } from 'next/navigation' + +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { AxiosError } from 'axios' + +import { + StrategyModel, + StrategyResponseModel, + StrategyTypeResponseModel, + strategyApi, +} from '../../_api/add-strategy' + +interface ErrorResponseModel { + message: string +} + +export const useAddStrategy = () => { + const router = useRouter() + const queryClient = useQueryClient() + const [error, setError] = useState(null) + + const { data: strategyTypes, isLoading: isTypesLoading } = useQuery< + StrategyTypeResponseModel, + AxiosError + >({ + queryKey: ['strategyTypes'], + queryFn: () => strategyApi.getStrategyTypes().then((response) => response.data), + retry: false, + refetchOnWindowFocus: false, + }) + + const mutation = useMutation< + StrategyResponseModel, + AxiosError, + StrategyModel + >({ + mutationFn: (data) => strategyApi.registerStrategy(data).then((response) => response.data), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ['addStrategies'], + }) + router.back() + }, + onError: (err) => { + const errorMessage = err.response?.data?.message || '전략 등록에 실패했습니다.' + setError(errorMessage) + }, + }) + + return { + strategyTypes: strategyTypes?.result, + isTypesLoading, + registerStrategy: mutation.mutate, + isRegistering: mutation.isPending, + error, + } +} diff --git a/app/(dashboard)/my/strategies/add/page.tsx b/app/(dashboard)/my/strategies/add/page.tsx new file mode 100644 index 00000000..c7999780 --- /dev/null +++ b/app/(dashboard)/my/strategies/add/page.tsx @@ -0,0 +1,308 @@ +'use client' + +import { useState } from 'react' + +import Image from 'next/image' +import { useRouter } from 'next/navigation' + +import { FileIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import BackHeader from '@/shared/ui/header/back-header' +import { Input } from '@/shared/ui/input' +import Select from '@/shared/ui/select' +import Title from '@/shared/ui/title' + +import { + MinimumInvestmentAmountType, + OperationCycleType, + ProposalFileInfoModel, + StrategyModel, +} from '../../_api/add-strategy' +import { + minimumInvestmentAmountOptions, + operationCycleOptions, +} from '../../_constants/investment-amount' +import { useAddStrategy } from '../../_hooks/query/use-add-strategy' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface StrategyFormDataModel { + strategyName: string + tradeType: string + operationCycle: OperationCycleType + stockTypes: string[] + minimumInvestmentAmount: MinimumInvestmentAmountType + description: string + proposalFile?: File +} + +interface FormErrorsModel { + strategyName: string + tradeType: string + operationCycle: string + stockTypes: string + minimumInvestmentAmount: string + description: string + proposalFile?: string +} + +const StrategyAddPage = () => { + const router = useRouter() + const { strategyTypes, registerStrategy, isTypesLoading, isRegistering, error } = useAddStrategy() + + const [formData, setFormData] = useState({ + strategyName: '', + tradeType: '', + operationCycle: 'DAY', + stockTypes: [], + minimumInvestmentAmount: 'UNDER_10K', + description: '', + }) + + const [formErrors, setFormErrors] = useState({ + strategyName: '', + tradeType: '', + operationCycle: '', + stockTypes: '', + minimumInvestmentAmount: '', + description: '', + }) + + const validateForm = (): boolean => { + const newErrors = { + strategyName: !formData.strategyName ? '전략 명칭을 입력해주세요.' : '', + tradeType: !formData.tradeType ? '매매 유형을 선택해주세요.' : '', + operationCycle: !formData.operationCycle ? '주기를 선택해주세요.' : '', + stockTypes: formData.stockTypes.length === 0 ? '종목을 선택해주세요.' : '', + minimumInvestmentAmount: !formData.minimumInvestmentAmount + ? '최소 운용가능 금액을 선택해주세요.' + : '', + description: !formData.description ? '전략 소개를 입력해주세요.' : '', + proposalFile: '', + } + + setFormErrors(newErrors) + return !Object.values(newErrors).some((error) => error) + } + + const handleFileChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0] + if ( + file && + (file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || + file.type === 'application/vnd.ms-excel') + ) { + setFormData((prev) => ({ ...prev, proposalFile: file })) + setFormErrors((prev) => ({ ...prev, proposalFile: '' })) + } else { + setFormErrors((prev) => ({ ...prev, proposalFile: '엑셀 파일만 업로드 가능합니다.' })) + } + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + + if (!validateForm()) return + + const fileInfo: ProposalFileInfoModel | undefined = formData.proposalFile + ? { + proposalFileName: formData.proposalFile.name, + proposalFileSize: formData.proposalFile.size, + } + : undefined + + const data: StrategyModel = { + strategyName: formData.strategyName, + tradeTypeId: Number(formData.tradeType), + operationCycle: formData.operationCycle, + stockTypeIds: formData.stockTypes.map(Number), + minimumInvestmentAmount: formData.minimumInvestmentAmount, + description: formData.description, + proposalFile: fileInfo, + } + + registerStrategy(data) + } + + const toggleStockType = (value: string) => { + setFormData((prev) => { + const newStockTypes = prev.stockTypes.includes(value) + ? prev.stockTypes.filter((type) => type !== value) + : [...prev.stockTypes, value] + return { ...prev, stockTypes: newStockTypes } + }) + setFormErrors((prev) => ({ ...prev, stockTypes: '' })) + } + + const tradeTypeOptions = + strategyTypes?.tradeTypes.map((type) => ({ + value: String(type.tradeTypeId), + label: type.tradeTypeName, + })) || [] + + if (isTypesLoading) { + return
    로딩 중...
    + } + return ( + <> + + + <form onSubmit={handleSubmit} className={cx('form')}> + {error && <div className={cx('error')}>{error}</div>} + <div className={cx('form-row')}> + <label>전략 명칭</label> + <div className={cx('form-field')}> + <Input + value={formData.strategyName} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + setFormData((prev) => ({ ...prev, strategyName: e.target.value })) + setFormErrors((prev) => ({ ...prev, strategyName: '' })) + }} + placeholder="전략명을 입력하세요" + errorMessage={formErrors.strategyName} + /> + </div> + </div> + <div className={cx('horizontal-wrapper')}> + <div className={cx('form-row', 'half')}> + <label>매매 유형</label> + <div className={cx('form-field')}> + <Select + value={formData.tradeType} + onChange={(value) => { + setFormData((prev) => ({ ...prev, tradeType: value as string })) + setFormErrors((prev) => ({ ...prev, tradeType: '' })) + }} + options={tradeTypeOptions} + placeholder="매매 유형 선택" + titleStyle={{ width: '200px', height: '50px' }} + /> + {formErrors.tradeType && ( + <div className={cx('field-error')}>{formErrors.tradeType}</div> + )} + </div> + </div> + + <div className={cx('form-row', 'half')}> + <label>주기</label> + <div className={cx('form-field')}> + <Select + value={formData.operationCycle} + onChange={(value) => { + setFormData((prev) => ({ ...prev, operationCycle: value as OperationCycleType })) + setFormErrors((prev) => ({ ...prev, operationCycle: '' })) + }} + options={operationCycleOptions} + placeholder="주기 선택" + titleStyle={{ width: '200px', height: '50px' }} + containerStyle={{ width: '100%' }} + /> + {formErrors.operationCycle && ( + <div className={cx('field-error')}>{formErrors.operationCycle}</div> + )} + </div> + </div> + </div> + <div className={cx('form-row')}> + <label>종목</label> + <div className={cx('form-field')}> + <div className={cx('stock-grid')}> + {strategyTypes?.stockTypes.map((type) => ( + <button + key={type.stockTypeId} + type="button" + onClick={() => toggleStockType(String(type.stockTypeId))} + className={cx('stock-item', { + selected: formData.stockTypes.includes(String(type.stockTypeId)), + })} + > + {type.stockTypeName} + <span className={cx('marker')}> + <Image + src={type.stockIconUrl} + alt={type.stockTypeName} + width={20} + height={20} + /> + </span> + </button> + ))} + </div> + {formErrors.stockTypes && ( + <div className={cx('field-error')}>{formErrors.stockTypes}</div> + )} + </div> + </div> + <div className={cx('form-row')}> + <label>최소 운용가능 금액</label> + <div className={cx('form-field')}> + <Select + value={formData.minimumInvestmentAmount} + onChange={(value) => { + setFormData((prev) => ({ + ...prev, + minimumInvestmentAmount: value as MinimumInvestmentAmountType, + })) + setFormErrors((prev) => ({ ...prev, minimumInvestmentAmount: '' })) + }} + options={minimumInvestmentAmountOptions} + placeholder="최소 운용가능 금액 선택" + titleStyle={{ width: '200px', height: '50px' }} + containerStyle={{ width: '100%' }} + /> + {formErrors.minimumInvestmentAmount && ( + <div className={cx('field-error')}>{formErrors.minimumInvestmentAmount}</div> + )} + </div> + </div> + <div className={cx('form-row')}> + <label>전략 소개</label> + <div className={cx('form-field')}> + <Input + value={formData.description} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + setFormData((prev) => ({ ...prev, description: e.target.value })) + setFormErrors((prev) => ({ ...prev, description: '' })) + }} + placeholder="내용을 입력하세요" + errorMessage={formErrors.description} + /> + </div> + </div> + <div className={cx('form-row')}> + <label>제안서</label> + <div className={cx('form-field')}> + <div className={cx('file-upload')}> + <input + type="file" + accept=".xlsx,.xls" + onChange={handleFileChange} + id="proposalFile" + className={cx('file-input')} + /> + <label htmlFor="proposalFile" className={cx('file-label')}> + <span>{formData.proposalFile?.name || '엑셀 파일을 선택해주세요'}</span> + <FileIcon className={cx('file-icon')} /> + </label> + </div> + {formErrors.proposalFile && ( + <div className={cx('field-error')}>{formErrors.proposalFile}</div> + )} + </div> + </div> + <div className={cx('button-wrapper')}> + <Button variant="outline" onClick={() => router.back()} type="button"> + 취소 + </Button> + <Button variant="filled" type="submit" disabled={isRegistering}> + {isRegistering ? '등록 중...' : '전략 등록하기'} + </Button> + </div> + </form> + </> + ) +} +export default StrategyAddPage diff --git a/app/(dashboard)/my/strategies/add/styles.module.scss b/app/(dashboard)/my/strategies/add/styles.module.scss new file mode 100644 index 00000000..54fc4866 --- /dev/null +++ b/app/(dashboard)/my/strategies/add/styles.module.scss @@ -0,0 +1,144 @@ +.form { + max-width: 800px; + margin: 0 auto; + padding: 24px; + position: relative; +} + +.form-row { + display: flex; + align-items: flex-start; + margin-bottom: 24px; + gap: 24px; + + label { + flex: 0 0 120px; + margin-top: 8px; + font-weight: 500; + } + + &.half { + margin-bottom: 0; + } +} + +.horizontal-wrapper { + display: flex; + gap: 24px; + margin-bottom: 24px; + + .form-row { + flex: 1; + } +} + +.form-field { + flex: 1; +} + +.error { + margin-bottom: 16px; + padding: 12px; + border-radius: 4px; + background-color: $color-white; + color: $color-orange-600; + font-size: 14px; +} + +.loading { + display: flex; + justify-content: center; + align-items: center; + min-height: 200px; +} + +.input { + width: 100%; + padding: 8px 12px; + border: 1px solid $color-gray-300; + border-radius: 4px; + font-size: 14px; + line-height: 1.5; + transition: border-color 0.2s; + + &.error { + border-color: $color-orange-600; + } +} + +.stock-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 12px; + margin-bottom: 8px; +} + +.stock-item { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 12px; + border: 1px solid $color-gray-300; + border-radius: 4px; + background-color: $color-white; + font-size: 14px; + transition: all 0.2s; + cursor: pointer; + width: 100%; + + &:hover { + border-color: $color-indigo; + } + + &.selected { + border-color: $color-indigo; + } + + .marker { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 4px; + overflow: hidden; + } +} + +.file-upload { + position: relative; + + .file-input { + position: absolute; + width: 0; + height: 0; + opacity: 0; + } + + .file-label { + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + padding: 8px 16px; + border: 1px solid $color-gray-300; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s; + color: $color-gray-500; + font-size: 14px; + + .file-icon { + width: 24px; + height: 24px; + } + } +} + +.button-wrapper { + display: flex; + justify-content: center; + margin-top: 32px; + gap: 24px; +} From 8e4981c73cfbf0da71d2c3f40d9438cf7b03670a Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 14:02:57 +0900 Subject: [PATCH 640/648] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/profile/_ui/user-info/index.tsx | 414 ++++++++++++++++-- .../profile/_ui/user-info/styles.module.scss | 11 + .../traders/_api/get-trader-details.ts | 21 + .../traders/_hooks/use-get-trader-details.ts | 0 .../traders-list-card.stories.tsx | 2 +- 5 files changed, 413 insertions(+), 35 deletions(-) create mode 100644 app/(dashboard)/traders/_api/get-trader-details.ts create mode 100644 app/(dashboard)/traders/_hooks/use-get-trader-details.ts diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index 972e83f5..d2855c48 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -6,8 +6,10 @@ import { useRouter } from 'next/navigation' import { SIGNUP_ERROR_MESSAGES } from '@/app/(landing)/signup/_constants/signup' import { CameraIcon } from '@/public/icons' +import axios from 'axios' import classNames from 'classnames/bind' +import axiosInstance from '@/shared/api/axios' import { checkNicknameDuplicate, checkPhoneDuplicate } from '@/shared/api/check-duplicate' import { PATH } from '@/shared/constants/path' import Avatar from '@/shared/ui/avatar' @@ -32,6 +34,30 @@ interface Props { profile: ProfileModel } +// const uploadImageToS3 = async (presignedUrl: string, file: File): Promise<void> => { +// try { +// await axiosInstance.put(presignedUrl, file, { +// headers: { +// 'Content-Type': file.type, +// }, +// }) +// } catch (error) { +// console.error('이미지 업로드 실패:', error) +// throw new Error('이미지 업로드에 실패했습니다') +// } +// } +const uploadImageToS3 = async (presignedUrl: string, file: File): Promise<void> => { + try { + await axiosInstance.put(presignedUrl, file, { + headers: { + 'Content-Type': file.type, + }, + }) + } catch (error) { + console.error('이미지 업로드 실패:', error) + throw new Error('이미지 업로드에 실패했습니다') + } +} const UserInfo = ({ profile, isEditable = false }: Props) => { const router = useRouter() @@ -49,6 +75,11 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { const [formState, setFormState] = useState<ProfileFormStateModel>(initialFormState) const [errors, setErrors] = useState<ProfileFormErrorsModel>({}) const [isValidated, setIsValidated] = useState(false) + const [isNicknameModified, setIsNicknameModified] = useState(false) + const [isPhoneModified, setIsPhoneModified] = useState(false) + const [selectedImage, setSelectedImage] = useState<File | null>(null) + const [previewUrl, setPreviewUrl] = useState<string | null>(null) + const [isUploading, setIsUploading] = useState(false) const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target @@ -62,57 +93,271 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { } if (name === 'nickname') { - setFormState((prev) => ({ ...prev, isNicknameVerified: false })) + setIsNicknameModified(true) setErrors((prev) => ({ ...prev, nickname: null })) } if (name === 'phone') { - setFormState((prev) => ({ ...prev, isPhoneVerified: false })) + setIsPhoneModified(true) setErrors((prev) => ({ ...prev, phone: null })) } } - const handleNicknameCheck = async () => { - setFormState((prev) => ({ ...prev, isNicknameVerified: false })) + const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => { + const file = e.target.files?.[0] + if (!file) return + + // 파일 유효성 검사 + if (!file.type.startsWith('image/')) { + alert('이미지 파일만 업로드가 가능합니다.') + return + } + const maxSize = 5 * 1024 * 1024 // 5MB + if (file.size > maxSize) { + alert('파일 크기는 5MB 이하여야 합니다.') + return + } + + // 이미지 미리보기 설정 + const reader = new FileReader() + reader.onload = (e) => { + setPreviewUrl(e.target?.result as string) + } + reader.readAsDataURL(file) + + setSelectedImage(file) + } + + const handleNicknameCheck = async () => { try { const response = await checkNicknameDuplicate(form.nickname) if (response.result.isAvailable) { setFormState((prev) => ({ ...prev, isNicknameVerified: true })) + setIsNicknameModified(false) if (errors.nickname) { setErrors((prev) => ({ ...prev, nickname: null })) } } else { setErrors((prev) => ({ ...prev, nickname: SIGNUP_ERROR_MESSAGES.NICKNAME_DUPLICATED })) + setFormState((prev) => ({ ...prev, isNicknameVerified: false })) } } catch (err) { console.error('닉네임 중복 확인 실패:', err) setErrors((prev) => ({ ...prev, nickname: SIGNUP_ERROR_MESSAGES.NICKNAME_CHECK_FAILED })) + setFormState((prev) => ({ ...prev, isNicknameVerified: false })) } } + const handlePhoneCheck = async () => { try { const response = await checkPhoneDuplicate(form.phone) - if (response.result.isAvailable) { setFormState((prev) => ({ ...prev, isPhoneVerified: true })) + setIsPhoneModified(false) if (errors.phone) { setErrors((prev) => ({ ...prev, phone: null })) } } else { setErrors((prev) => ({ ...prev, phone: SIGNUP_ERROR_MESSAGES.PHONE_DUPLICATED })) + setFormState((prev) => ({ ...prev, isPhoneVerified: false })) } } catch (err) { console.error('휴대폰 번호 중복 확인 실패:', err) setErrors((prev) => ({ ...prev, phone: SIGNUP_ERROR_MESSAGES.PHONE_CHECK_FAILED })) + setFormState((prev) => ({ ...prev, isPhoneVerified: false })) } } - const handleImageChange = () => {} - const handleImageDelete = () => {} + const handleImageDelete = () => { + setSelectedImage(null) + setPreviewUrl(null) + } + const handleBack = () => { router.back() } + // const handleFormSubmit = async () => { + // const formErrors = validateProfileForm( + // form, + // formState.isNicknameVerified, + // formState.isPhoneVerified + // ) + // setIsValidated(true) + + // if (Object.keys(formErrors).length > 0) { + // setErrors(formErrors) + // return + // } + + // try { + // setIsUploading(true) + + // // 1. 프로필 정보 업데이트 + // const updatedProfileResponse = await axiosInstance.put('/api/user/profile', { + // nickname: form.nickname, + // phone: form.phone, + // password: form.password, + // }) + + // if (!updatedProfileResponse.data.isSuccess) { + // throw new Error(updatedProfileResponse.data.message) + // } + + // // 2. 이미지가 선택되어 있다면 이미지 업로드 + // if (selectedImage) { + // // presigned URL 요청 + // const presignedUrlResponse = await axiosInstance.put('/api/user/profile/image') + + // if (!presignedUrlResponse.data.isSuccess) { + // throw new Error(presignedUrlResponse.data.message) + // } + + // // S3에 이미지 업로드 + // await uploadImageToS3(presignedUrlResponse.data.presignedUrl, selectedImage) + // } + + // alert('프로필이 성공적으로 업데이트되었습니다.') + // router.push(PATH.PROFILE) + // } catch (error) { + // console.error('프로필 업데이트 실패:', error) + // alert( + // error instanceof Error + // ? error.message + // : '프로필 업데이트에 실패했습니다. 다시 시도해주세요.' + // ) + // } finally { + // setIsUploading(false) + // } + // } + // const handleFormSubmit = async () => { + // const formErrors = validateProfileForm( + // form, + // formState.isNicknameVerified, + // formState.isPhoneVerified + // ) + // setIsValidated(true) + + // if (Object.keys(formErrors).length > 0) { + // setErrors(formErrors) + // return + // } + + // try { + // setIsUploading(true) + + // // 업데이트할 데이터 준비 + // const updateData = { + // nickname: form.nickname, + // phone: form.phone, + // ...(form.password ? { password: form.password } : {}), // 비밀번호는 입력된 경우만 포함 + // } + + // console.log('프로필 업데이트 요청 데이터:', updateData) // 요청 데이터 확인 + + // // 1. 프로필 정보 업데이트 + // const updatedProfileResponse = await axiosInstance.patch('/api/user/profile', updateData) + + // console.log('프로필 업데이트 응답:', updatedProfileResponse) // 응답 확인 + + // if (!updatedProfileResponse.data.isSuccess) { + // throw new Error(updatedProfileResponse.data.message || '프로필 업데이트에 실패했습니다.') + // } + + // // 2. 이미지가 선택되어 있다면 이미지 업로드 + // if (selectedImage) { + // console.log('이미지 업로드 시작') // 이미지 업로드 시작 로그 + + // // presigned URL 요청 + // const presignedUrlResponse = await axiosInstance.put('/api/user/profile/image') + + // console.log('Presigned URL 응답:', presignedUrlResponse) // presigned URL 응답 확인 + + // if (!presignedUrlResponse.data.isSuccess) { + // throw new Error(presignedUrlResponse.data.message || 'Presigned URL 생성에 실패했습니다.') + // } + + // // S3에 이미지 업로드 + // await uploadImageToS3(presignedUrlResponse.data.presignedUrl, selectedImage) + // } + + // alert('프로필이 성공적으로 업데이트되었습니다.') + // router.push(PATH.PROFILE) + // } catch (error) { + // console.error('프로필 업데이트 실패:', error) + + // // 에러 응답 구조 자세히 확인 + // if (axios.isAxiosError(error)) { + // console.error('서버 응답:', error.response?.data) + // alert(error.response?.data?.message || '프로필 업데이트에 실패했습니다. 다시 시도해주세요.') + // } else { + // alert( + // error instanceof Error + // ? error.message + // : '프로필 업데이트에 실패했습니다. 다시 시도해주세요.' + // ) + // } + // } finally { + // setIsUploading(false) + // } + // } + // const handleFormSubmit = async () => { + // const formErrors = validateProfileForm( + // form, + // formState.isNicknameVerified, + // formState.isPhoneVerified + // ) + // setIsValidated(true) + + // if (Object.keys(formErrors).length > 0) { + // setErrors(formErrors) + // return + // } + + // try { + // setIsUploading(true) + + // // 요청 데이터 구조화 + // const updateData = { + // nickname: form.nickname, + // phone: form.phone, + // password: form.password || null, + // email: form.email, // optional + // imageChange: selectedImage !== null, // 이미지 변경 여부 + // ...(selectedImage && { + // imageDto: { + // imageName: selectedImage.name, + // size: selectedImage.size, + // }, + // }), + // } + + // // 프로필 정보 업데이트 + // const response = await axiosInstance.patch('/api/users/mypage/profile', updateData) + + // if (!response.data.isSuccess) { + // throw new Error(response.data.message) + // } + + // // 이미지가 있고, presignedUrl을 받았다면 이미지 업로드 + // if (selectedImage && response.data.data?.presignedUrl) { + // await uploadImageToS3(response.data.data.presignedUrl, selectedImage) + // } + + // alert('프로필이 성공적으로 업데이트되었습니다.') + // router.push(PATH.PROFILE) + // } catch (error) { + // console.error('프로필 업데이트 실패:', error) + // if (axios.isAxiosError(error)) { + // const errorMessage = error.response?.data?.message || '프로필 업데이트에 실패했습니다.' + // alert(errorMessage) + // } else { + // alert('프로필 업데이트에 실패했습니다. 다시 시도해주세요.') + // } + // } finally { + // setIsUploading(false) + // } + // } const handleFormSubmit = async () => { const formErrors = validateProfileForm( form, @@ -125,6 +370,51 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { setErrors(formErrors) return } + + try { + setIsUploading(true) + + // 요청 데이터 구조화 - 필드명 수정 + const updateData = { + nickName: form.nickname, + phoneNum: form.phone, + password: form.password || null, + email: form.email, + imageChange: selectedImage !== null, + profileImage: selectedImage + ? { + imageName: selectedImage.name, + size: selectedImage.size, + } + : null, + } + + console.log('요청 데이터:', updateData) // 요청 데이터 확인 + + const response = await axiosInstance.patch('/api/users/mypage/profile', updateData) + + if (!response.data.isSuccess) { + throw new Error(response.data.message) + } + + // 이미지가 있고, presignedUrl을 받았다면 이미지 업로드 + if (selectedImage && response.data.data?.presignedUrl) { + await uploadImageToS3(response.data.data.presignedUrl, selectedImage) + } + + alert('프로필이 성공적으로 업데이트되었습니다.') + router.push(PATH.PROFILE) + } catch (error) { + console.error('프로필 업데이트 실패:', error) + if (axios.isAxiosError(error)) { + const errorMessage = error.response?.data?.message || '프로필 업데이트에 실패했습니다.' + alert(errorMessage) + } else { + alert('프로필 업데이트에 실패했습니다. 다시 시도해주세요.') + } + } finally { + setIsUploading(false) + } } if (!profile) { @@ -140,12 +430,30 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { <div className={cx('content-wrapper')}> <div className={cx('left-wrapper')}> <div className={cx('avatar-wrapper')}> - <Avatar size="xxlarge" /> + {previewUrl ? ( + <img src={previewUrl} alt="Preview" className={cx('avatar-preview')} /> + ) : ( + <Avatar size="xxlarge" /> + )} <div className={cx('camera-wrapper')}> - <CameraIcon className={cx('camera-icon')} onClick={handleImageChange} /> + <input + type="file" + id="profile-image" + accept="image/*" + onChange={handleImageChange} + style={{ display: 'none' }} + /> + <label htmlFor="profile-image"> + <CameraIcon + className={cx('camera-icon')} + style={{ cursor: isUploading ? 'wait' : 'pointer' }} + /> + </label> </div> </div> - {isEditable && <Button onClick={handleImageDelete}>프로필 사진 삭제</Button>} + {isEditable && selectedImage && ( + <Button onClick={handleImageDelete}>프로필 사진 삭제</Button> + )} </div> <div className={cx('right-wrapper')}> @@ -181,17 +489,27 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { <div> <p className={cx('title')}>휴대전화</p> <div className={cx('position')}> - <Input - id="phone" - name="phone" - value={form.phone} - onChange={handleInputChange} - className={cx('input')} - inputSize="compact" - isWhiteDisabled={!isEditable} - /> - - {isEditable && <Button onClick={handlePhoneCheck}>확인</Button>} + <div className={cx('position-wrapper')}> + <Input + id="phone" + name="phone" + value={form.phone} + onChange={handleInputChange} + className={cx('input')} + inputSize="compact" + isWhiteDisabled={!isEditable} + errorMessage={errors.phone} + /> + {isEditable && <Button onClick={handlePhoneCheck}>확인</Button>} + </div> + {formState.isPhoneVerified && !isPhoneModified && ( + <p className={cx('verified-message')}>사용할 수 있는 휴대폰 번호입니다.</p> + )} + {isPhoneModified && ( + <p className={cx('modified-message')}> + 휴대폰 번호가 수정되었습니다. 다시 확인해 주세요. + </p> + )} </div> </div> </div> @@ -210,17 +528,27 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { <div> <p className={cx('title')}>닉네임</p> <div className={cx('position')}> - <Input - id="nickname" - name="nickname" - inputSize="compact" - value={form.nickname} - onChange={handleInputChange} - className={cx('input')} - isWhiteDisabled={!isEditable} - /> - - {isEditable && <Button onClick={handleNicknameCheck}>확인</Button>} + <div className={cx('position-wrapper')}> + <Input + id="nickname" + name="nickname" + inputSize="compact" + value={form.nickname} + onChange={handleInputChange} + className={cx('input')} + isWhiteDisabled={!isEditable} + errorMessage={errors.nickname} + /> + {isEditable && <Button onClick={handleNicknameCheck}>확인</Button>} + </div> + {formState.isNicknameVerified && !isNicknameModified && ( + <p className={cx('verified-message')}>사용할 수 있는 닉네임입니다.</p> + )} + {isNicknameModified && ( + <p className={cx('modified-message')}> + 닉네임이 수정되었습니다. 다시 중복확인해 주세요. + </p> + )} </div> </div> </div> @@ -243,7 +571,6 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { </div> <div> <p className={cx('title')}>비밀번호 확인</p> - <Input id="passwordConfirm" name="passwordConfirm" @@ -267,7 +594,7 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { </div> </div> - {isEditable && ( + {/* {isEditable && ( <div className={cx('button-wrapper')}> <Button className={cx('left-button')} onClick={handleBack}> 뒤로가기 @@ -280,6 +607,25 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { </div> </div> ) +} */} + {isEditable && ( + <div className={cx('button-wrapper')}> + <Button className={cx('left-button')} onClick={handleBack} disabled={isUploading}> + 뒤로가기 + </Button> + <Button + className={cx('right-button')} + variant="filled" + onClick={handleFormSubmit} + disabled={isUploading} + > + {isUploading ? '저장 중...' : '저장하기'} + </Button> + </div> + )} + </div> + </div> + ) } export default UserInfo diff --git a/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss b/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss index 11c9ae53..da2506e2 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss +++ b/app/(dashboard)/my/profile/_ui/user-info/styles.module.scss @@ -130,6 +130,17 @@ .position { display: flex; + flex-direction: column; align-items: center; gap: 27px; } +.position-wrapper { + display: flex; + gap: 12px; +} + +.nickname-verified { + margin-top: 0; + @include typo-b3; + color: $color-indigo; +} diff --git a/app/(dashboard)/traders/_api/get-trader-details.ts b/app/(dashboard)/traders/_api/get-trader-details.ts new file mode 100644 index 00000000..7d284d07 --- /dev/null +++ b/app/(dashboard)/traders/_api/get-trader-details.ts @@ -0,0 +1,21 @@ +import axiosInstance from '@/shared/api/axios' + +// interface TraderReturnModel { +// // 수민님이 만들어두신 모델 찾아와서 넣으면될듯.. reuslt content에.. +// } + +interface Props { + traderId: number +} + +const getTraderDetails = async ({ traderId }: Props) => { + try { + const response = await axiosInstance.get(`/api/strategies/search/trader/${traderId}`) + return response.data.result + } catch (err) { + console.error(err) + throw new Error('트레이더 상세 조회에 실패했습니다.') + } +} + +export default getTraderDetails diff --git a/app/(dashboard)/traders/_hooks/use-get-trader-details.ts b/app/(dashboard)/traders/_hooks/use-get-trader-details.ts new file mode 100644 index 00000000..e69de29b diff --git a/shared/ui/traders-list-card/traders-list-card.stories.tsx b/shared/ui/traders-list-card/traders-list-card.stories.tsx index 3a506edc..014041c0 100644 --- a/shared/ui/traders-list-card/traders-list-card.stories.tsx +++ b/shared/ui/traders-list-card/traders-list-card.stories.tsx @@ -39,7 +39,7 @@ export const WithoutButton: StoryType = { imageUrl: 'https://lh3.googleusercontent.com/a/your-image-id', strategyCount: 10, subscriberCount: 10, - userId: 123, + userId: 1234, hasButton: false, }, } From ac779362b065835c696805cd4837a9a54c320803 Mon Sep 17 00:00:00 2001 From: ssumanlife <ksm9805@naver.com> Date: Fri, 6 Dec 2024 14:37:07 +0900 Subject: [PATCH 641/648] =?UTF-8?q?feat:=20%=20=EC=88=98=EC=B9=98=20?= =?UTF-8?q?=EC=86=8C=EC=88=98=EC=A0=90=20=EB=91=90=EC=9E=90=EB=A6=AC?= =?UTF-8?q?=EA=B9=8C=EC=A7=80=20=EB=B3=B4=EC=97=AC=EC=A7=80=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=A0=81=EC=9A=A9=20(#306)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/details-information/percentage.tsx | 2 +- app/(landing)/(home)/_ui/line-chart/index.tsx | 2 +- .../(home)/_ui/sm-score-card/index.tsx | 69 --------- .../sm-score-card/sm-score-card.stories.tsx | 131 ------------------ .../_ui/sm-score-card/styles.module.scss | 108 --------------- .../(home)/_ui/top-strategy-card/index.tsx | 2 +- .../top-strategy-card/top-sm-score-card.tsx | 3 +- shared/ui/table/statistics/index.tsx | 10 +- 8 files changed, 11 insertions(+), 316 deletions(-) delete mode 100644 app/(landing)/(home)/_ui/sm-score-card/index.tsx delete mode 100644 app/(landing)/(home)/_ui/sm-score-card/sm-score-card.stories.tsx delete mode 100644 app/(landing)/(home)/_ui/sm-score-card/styles.module.scss diff --git a/app/(dashboard)/_ui/details-information/percentage.tsx b/app/(dashboard)/_ui/details-information/percentage.tsx index 9bb24fb1..85856597 100644 --- a/app/(dashboard)/_ui/details-information/percentage.tsx +++ b/app/(dashboard)/_ui/details-information/percentage.tsx @@ -15,7 +15,7 @@ const Percentage = ({ percent, label }: Props) => { return ( <div className={cx('percentage-wrapper')}> <p className={cx('percent', isMinus && 'minus')}> - {percent} + {percent.toFixed(2)} {label !== 'Profit Factor' && '%'} </p> <p className={cx('label')}>{label}</p> diff --git a/app/(landing)/(home)/_ui/line-chart/index.tsx b/app/(landing)/(home)/_ui/line-chart/index.tsx index ec7b2f3f..2c6bb469 100644 --- a/app/(landing)/(home)/_ui/line-chart/index.tsx +++ b/app/(landing)/(home)/_ui/line-chart/index.tsx @@ -4,7 +4,7 @@ import dynamic from 'next/dynamic' import Highcharts from 'highcharts' -import { CardSizeType } from '../sm-score-card/index' +import { CardSizeType } from '../top-strategy-card/types' const HighchartsReact = dynamic(() => import('highcharts-react-official'), { ssr: false, diff --git a/app/(landing)/(home)/_ui/sm-score-card/index.tsx b/app/(landing)/(home)/_ui/sm-score-card/index.tsx deleted file mode 100644 index 15f35322..00000000 --- a/app/(landing)/(home)/_ui/sm-score-card/index.tsx +++ /dev/null @@ -1,69 +0,0 @@ -'use client' - -import classNames from 'classnames/bind' - -import Avatar from '@/shared/ui/avatar' - -import LineChart from '../line-chart' -import styles from './styles.module.scss' - -const cx = classNames.bind(styles) - -export type CardSizeType = 'small' | 'large' - -interface Props { - name: string - title: string - score: number - percentageChange: number - chartData: number[] - ranking: number - profileImage?: string - size?: CardSizeType -} - -const ScoreCard = ({ - name, - title, - score, - percentageChange, - chartData, - ranking, - profileImage, - size = 'small', -}: Props) => { - const isNegative = percentageChange < 0 - - return ( - <div className={cx('card-wrapper', size)}> - <div className={cx('top-frame', size)}> - <div className={cx('content-area')}> - <div className={cx('rank')}>Top {ranking}</div> - <div className={cx('profile')}> - <Avatar src={profileImage} size="small" /> - <span className={cx('profile-name')}>{name}</span> - </div> - <h3 className={cx('title')}>{title}</h3> - </div> - <div className={cx('chart-area', size)}> - <LineChart data={chartData} isNegative={isNegative} size={size} /> - </div> - </div> - <div className={cx('bottom-frame')}> - <div className={cx('sm-score')}> - <span className={cx('label')}>SM SCORE</span> - <span className={cx('value', 'score')}>{score.toFixed(2)}</span> - </div> - <div className={cx('profit')}> - <span className={cx('label')}>누적수익률</span> - <span className={cx('value', { negative: isNegative })}> - {isNegative ? '' : '+'} - {percentageChange}% - </span> - </div> - </div> - </div> - ) -} - -export default ScoreCard diff --git a/app/(landing)/(home)/_ui/sm-score-card/sm-score-card.stories.tsx b/app/(landing)/(home)/_ui/sm-score-card/sm-score-card.stories.tsx deleted file mode 100644 index f8d51e08..00000000 --- a/app/(landing)/(home)/_ui/sm-score-card/sm-score-card.stories.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react' - -import ScoreCard from './index' - -const meta = { - title: 'Components/ScoreCard', - component: ScoreCard, - parameters: { - layout: 'centered', - backgrounds: { - default: 'dark', - values: [ - { - name: 'dark', - value: '#000000', - }, - ], - }, - }, - tags: ['autodocs'], - argTypes: { - size: { - control: 'radio', - options: ['small', 'large'], - }, - }, - decorators: [ - (Story) => ( - <div style={{ padding: '20px' }}> - <Story /> - </div> - ), - ], -} satisfies Meta<typeof ScoreCard> - -export default meta -type StoryType = StoryObj<typeof meta> - -const mockChartData = [8, 15, 10, 17, 15, 19, 25, 20] - -export const Default: StoryType = { - args: { - name: '내 이름은 김다은', - title: '내 전략은 엄청나', - score: 60.63, - percentageChange: 37, - chartData: mockChartData, - ranking: 1, - size: 'small', - profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', - }, -} - -export const LargeCard: StoryType = { - args: { - ...Default.args, - size: 'large', - }, -} - -export const NegativeChange: StoryType = { - args: { - name: '나 김다은 아니다', - title: '내 전략은 엄청나ㅋ', - score: 50.63, - percentageChange: -12, - chartData: [20, 19, 17, 18, 15, 13, 11, 8], - ranking: 4, - profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', - }, -} - -export const LayoutExample: StoryType = { - args: { - name: '내 이름은 김다은', - title: '내 전략은 엄청나', - score: 60.63, - percentageChange: 37, - chartData: [8, 15, 10, 17, 15, 19, 25, 20], - ranking: 1, - size: 'large', - profileImage: 'https://lh3.googleusercontent.com/a/your-image-id', - }, - decorators: [ - () => ( - <div style={{ width: '900px' }}> - <div - style={{ - display: 'flex', - gap: '20px', - width: '100%', - }} - > - <div style={{ flex: '0 0 300px' }}> - <ScoreCard - name="내 이름은 김다은" - title="내 전략은 엄청나" - score={60.63} - percentageChange={37} - chartData={[8, 15, 10, 17, 15, 19, 25, 20]} - ranking={1} - size="large" - profileImage="https://lh3.googleusercontent.com/a/your-image-id" - /> - </div> - <div - style={{ - flex: 1, - display: 'grid', - gridTemplateColumns: 'repeat(2, 1fr)', - gap: '20px', - }} - > - {[2, 3, 4, 5].map((ranking) => ( - <ScoreCard - key={ranking} - name="엄청난 트레이더" - title="내 전략은 엄청나" - score={60.63} - percentageChange={37} - chartData={[10, 12, 15, 14, 16, 18, 20, 18]} - ranking={ranking} - profileImage="https://lh3.googleusercontent.com/a/your-image-id" - /> - ))} - </div> - </div> - </div> - ), - ], -} diff --git a/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss b/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss deleted file mode 100644 index 8d571acd..00000000 --- a/app/(landing)/(home)/_ui/sm-score-card/styles.module.scss +++ /dev/null @@ -1,108 +0,0 @@ -.card-wrapper { - background-color: $color-white; - border-radius: 5px; - padding: 14px 18px; - width: 300px; - min-width: 300px; - display: flex; - flex-direction: column; - - &.small { - height: 160px; - } - - &.large { - height: 340px; - } -} - -.top-frame { - display: flex; - justify-content: space-between; - align-items: center; - gap: 1rem; - flex: 1; - - &.large { - flex-direction: column; - align-items: flex-start; - } -} - -.content-area { - display: flex; - flex-direction: column; - gap: 14px; -} - -.rank { - font-size: $text-b1; - font-weight: $text-bold; - color: $color-black; -} - -.profile { - display: flex; - align-items: center; - - .profile-name { - font-size: $text-c1; - color: $color-gray-500; - font-weight: $text-medium; - margin-left: 0.5rem; - } -} - -.title { - font-size: $text-b2; - font-weight: $text-semibold; - color: $color-black; - margin-top: -0.5rem; - @include ellipsis(2); -} - -.chart-area { - display: flex; - align-items: center; - flex: 1; - justify-content: center; - - &.large { - width: 100%; - } - - &.small { - margin-left: 2rem; - } -} - -.bottom-frame { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 14px; -} - -.sm-score, -.profit { - display: flex; - gap: 0.5rem; - align-items: center; - font-size: $text-b2; - font-weight: $text-semibold; - margin-bottom: 7px; - - .label { - color: $color-gray-700; - } - - .value { - &.negative { - color: $color-indigo; - } - - &:not(.negative) { - color: $color-orange-800; - } - } -} diff --git a/app/(landing)/(home)/_ui/top-strategy-card/index.tsx b/app/(landing)/(home)/_ui/top-strategy-card/index.tsx index 61c86616..73907c20 100644 --- a/app/(landing)/(home)/_ui/top-strategy-card/index.tsx +++ b/app/(landing)/(home)/_ui/top-strategy-card/index.tsx @@ -84,7 +84,7 @@ const ProfitChart = ({ <span className={cx('label')}>누적수익률</span> <span className={cx('value', { negative: isNegative })}> {isNegative ? '' : '+'} - {percentageChange}% + {percentageChange.toFixed(2)}% </span> </div> </div> diff --git a/app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx b/app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx index 233ef2e9..2285522a 100644 --- a/app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx +++ b/app/(landing)/(home)/_ui/top-strategy-card/top-sm-score-card.tsx @@ -1,9 +1,10 @@ 'use client' import TopStrategyCard from '.' -import { CardSizeType } from '../sm-score-card' import { TopStrategyCardCommonProps } from './types' +export type CardSizeType = 'small' | 'large' + interface Props extends TopStrategyCardCommonProps { size?: CardSizeType score: number diff --git a/shared/ui/table/statistics/index.tsx b/shared/ui/table/statistics/index.tsx index be46135e..af8eb75c 100644 --- a/shared/ui/table/statistics/index.tsx +++ b/shared/ui/table/statistics/index.tsx @@ -51,16 +51,18 @@ const StatisticsTable = ({ title, statisticsData }: Props) => { <tr key={idx}> <td>{row[0]}</td> <td> - {formatNumber(row[1])} - {STATISTICS_PERCENT.includes(row[0] as string) && '%'} + {STATISTICS_PERCENT.includes(row[0] as string) + ? Number(row[1]).toFixed(2) + '%' + : formatNumber(row[1])} {STATISTICS_DATE.includes(row[0] as string) && '일'} </td> {row[2] && ( <> <td>{row[2]}</td> <td> - {formatNumber(row[3])} - {STATISTICS_PERCENT.includes(row[2] as string) && '%'} + {STATISTICS_PERCENT.includes(row[2] as string) + ? Number(row[3]).toFixed(2) + '%' + : formatNumber(row[3])} {STATISTICS_DATE.includes(row[2] as string) && '일'} </td> </> From 3b226754186c8fd11a07ba4ad951f49f29c0f7ca Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 14:43:26 +0900 Subject: [PATCH 642/648] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8D=94=20=EB=AA=A9=EB=A1=9D=20=EC=83=81=EC=84=B8=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20API=20=EC=97=B0=EB=8F=99=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my/profile/_ui/user-info/index.tsx | 208 ------------------ app/(dashboard)/traders/[traderId]/page.tsx | 38 ++-- .../traders/_api/get-trader-details.ts | 131 ++++++++++- .../traders/_hooks/use-get-trader-details.ts | 30 +++ .../traders/_hooks/use-get-traders.ts | 1 - app/(landing)/notices/_api/get-notice.ts | 0 6 files changed, 175 insertions(+), 233 deletions(-) create mode 100644 app/(landing)/notices/_api/get-notice.ts diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index d2855c48..5f12c5d8 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -34,18 +34,6 @@ interface Props { profile: ProfileModel } -// const uploadImageToS3 = async (presignedUrl: string, file: File): Promise<void> => { -// try { -// await axiosInstance.put(presignedUrl, file, { -// headers: { -// 'Content-Type': file.type, -// }, -// }) -// } catch (error) { -// console.error('이미지 업로드 실패:', error) -// throw new Error('이미지 업로드에 실패했습니다') -// } -// } const uploadImageToS3 = async (presignedUrl: string, file: File): Promise<void> => { try { await axiosInstance.put(presignedUrl, file, { @@ -177,187 +165,6 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { router.back() } - // const handleFormSubmit = async () => { - // const formErrors = validateProfileForm( - // form, - // formState.isNicknameVerified, - // formState.isPhoneVerified - // ) - // setIsValidated(true) - - // if (Object.keys(formErrors).length > 0) { - // setErrors(formErrors) - // return - // } - - // try { - // setIsUploading(true) - - // // 1. 프로필 정보 업데이트 - // const updatedProfileResponse = await axiosInstance.put('/api/user/profile', { - // nickname: form.nickname, - // phone: form.phone, - // password: form.password, - // }) - - // if (!updatedProfileResponse.data.isSuccess) { - // throw new Error(updatedProfileResponse.data.message) - // } - - // // 2. 이미지가 선택되어 있다면 이미지 업로드 - // if (selectedImage) { - // // presigned URL 요청 - // const presignedUrlResponse = await axiosInstance.put('/api/user/profile/image') - - // if (!presignedUrlResponse.data.isSuccess) { - // throw new Error(presignedUrlResponse.data.message) - // } - - // // S3에 이미지 업로드 - // await uploadImageToS3(presignedUrlResponse.data.presignedUrl, selectedImage) - // } - - // alert('프로필이 성공적으로 업데이트되었습니다.') - // router.push(PATH.PROFILE) - // } catch (error) { - // console.error('프로필 업데이트 실패:', error) - // alert( - // error instanceof Error - // ? error.message - // : '프로필 업데이트에 실패했습니다. 다시 시도해주세요.' - // ) - // } finally { - // setIsUploading(false) - // } - // } - // const handleFormSubmit = async () => { - // const formErrors = validateProfileForm( - // form, - // formState.isNicknameVerified, - // formState.isPhoneVerified - // ) - // setIsValidated(true) - - // if (Object.keys(formErrors).length > 0) { - // setErrors(formErrors) - // return - // } - - // try { - // setIsUploading(true) - - // // 업데이트할 데이터 준비 - // const updateData = { - // nickname: form.nickname, - // phone: form.phone, - // ...(form.password ? { password: form.password } : {}), // 비밀번호는 입력된 경우만 포함 - // } - - // console.log('프로필 업데이트 요청 데이터:', updateData) // 요청 데이터 확인 - - // // 1. 프로필 정보 업데이트 - // const updatedProfileResponse = await axiosInstance.patch('/api/user/profile', updateData) - - // console.log('프로필 업데이트 응답:', updatedProfileResponse) // 응답 확인 - - // if (!updatedProfileResponse.data.isSuccess) { - // throw new Error(updatedProfileResponse.data.message || '프로필 업데이트에 실패했습니다.') - // } - - // // 2. 이미지가 선택되어 있다면 이미지 업로드 - // if (selectedImage) { - // console.log('이미지 업로드 시작') // 이미지 업로드 시작 로그 - - // // presigned URL 요청 - // const presignedUrlResponse = await axiosInstance.put('/api/user/profile/image') - - // console.log('Presigned URL 응답:', presignedUrlResponse) // presigned URL 응답 확인 - - // if (!presignedUrlResponse.data.isSuccess) { - // throw new Error(presignedUrlResponse.data.message || 'Presigned URL 생성에 실패했습니다.') - // } - - // // S3에 이미지 업로드 - // await uploadImageToS3(presignedUrlResponse.data.presignedUrl, selectedImage) - // } - - // alert('프로필이 성공적으로 업데이트되었습니다.') - // router.push(PATH.PROFILE) - // } catch (error) { - // console.error('프로필 업데이트 실패:', error) - - // // 에러 응답 구조 자세히 확인 - // if (axios.isAxiosError(error)) { - // console.error('서버 응답:', error.response?.data) - // alert(error.response?.data?.message || '프로필 업데이트에 실패했습니다. 다시 시도해주세요.') - // } else { - // alert( - // error instanceof Error - // ? error.message - // : '프로필 업데이트에 실패했습니다. 다시 시도해주세요.' - // ) - // } - // } finally { - // setIsUploading(false) - // } - // } - // const handleFormSubmit = async () => { - // const formErrors = validateProfileForm( - // form, - // formState.isNicknameVerified, - // formState.isPhoneVerified - // ) - // setIsValidated(true) - - // if (Object.keys(formErrors).length > 0) { - // setErrors(formErrors) - // return - // } - - // try { - // setIsUploading(true) - - // // 요청 데이터 구조화 - // const updateData = { - // nickname: form.nickname, - // phone: form.phone, - // password: form.password || null, - // email: form.email, // optional - // imageChange: selectedImage !== null, // 이미지 변경 여부 - // ...(selectedImage && { - // imageDto: { - // imageName: selectedImage.name, - // size: selectedImage.size, - // }, - // }), - // } - - // // 프로필 정보 업데이트 - // const response = await axiosInstance.patch('/api/users/mypage/profile', updateData) - - // if (!response.data.isSuccess) { - // throw new Error(response.data.message) - // } - - // // 이미지가 있고, presignedUrl을 받았다면 이미지 업로드 - // if (selectedImage && response.data.data?.presignedUrl) { - // await uploadImageToS3(response.data.data.presignedUrl, selectedImage) - // } - - // alert('프로필이 성공적으로 업데이트되었습니다.') - // router.push(PATH.PROFILE) - // } catch (error) { - // console.error('프로필 업데이트 실패:', error) - // if (axios.isAxiosError(error)) { - // const errorMessage = error.response?.data?.message || '프로필 업데이트에 실패했습니다.' - // alert(errorMessage) - // } else { - // alert('프로필 업데이트에 실패했습니다. 다시 시도해주세요.') - // } - // } finally { - // setIsUploading(false) - // } - // } const handleFormSubmit = async () => { const formErrors = validateProfileForm( form, @@ -593,21 +400,6 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { )} </div> </div> - - {/* {isEditable && ( - <div className={cx('button-wrapper')}> - <Button className={cx('left-button')} onClick={handleBack}> - 뒤로가기 - </Button> - <Button className={cx('right-button')} variant="filled" onClick={handleFormSubmit}> - 저장하기 - </Button> - </div> - )} - </div> - </div> - ) -} */} {isEditable && ( <div className={cx('button-wrapper')}> <Button className={cx('left-button')} onClick={handleBack} disabled={isUploading}> diff --git a/app/(dashboard)/traders/[traderId]/page.tsx b/app/(dashboard)/traders/[traderId]/page.tsx index db486e49..212d3add 100644 --- a/app/(dashboard)/traders/[traderId]/page.tsx +++ b/app/(dashboard)/traders/[traderId]/page.tsx @@ -8,40 +8,44 @@ import TradersListCard from '@/shared/ui/traders-list-card' import ListHeader from '../../_ui/list-header' import StrategiesItem from '../../_ui/strategies-item' -import useGetTraders from '../_hooks/use-get-traders' +import useGetTraderStrategies from '../_hooks/use-get-trader-details' import styles from './page.module.scss' const cx = classNames.bind(styles) const TraderDetailPage = () => { - // const { data, isLoading } = useGetTraders() + const traderId = 1 // URL 파라미터나 상태에서 가져와야 합니다 + const { data: strategiesData, isLoading } = useGetTraderStrategies({ + traderId, + }) - // const traders = data?.content + const strategies = strategiesData?.content + const firstStrategy = strategies?.[0] - // if (!traders) { - // return null - // } + if (!firstStrategy || isLoading) { + return null + } return ( <> <div className={cx('page-container')}> <BackHeader label={'목록으로 돌아가기'} /> <div className={cx('title')}> - <Title label={'트레이더 상세보기'}> + </div> <div className={cx('card-wrapper')}> - {/* <TradersListCard - key={trader.userId} - imageUrl={trader.imageUrl} - nickname={trader.nickname} - strategyCount={trader.strategyCount} - subscriberCount={trader.totalSubCount} - userId={trader.userId} - /> */} + <TradersListCard + imageUrl={firstStrategy.traderImgUrl} + nickname={firstStrategy.nickname} + strategyCount={strategies.length} + subscriberCount={firstStrategy.subscriptionCount} + userId={traderId} + /> </div> <ListHeader /> - - {/* <StrategiesItem key={''} strategiesData={''} /> */} + {strategies?.map((strategy) => ( + <StrategiesItem key={strategy.strategyId} strategiesData={strategy} /> + ))} </div> </> ) diff --git a/app/(dashboard)/traders/_api/get-trader-details.ts b/app/(dashboard)/traders/_api/get-trader-details.ts index 7d284d07..42303634 100644 --- a/app/(dashboard)/traders/_api/get-trader-details.ts +++ b/app/(dashboard)/traders/_api/get-trader-details.ts @@ -1,21 +1,138 @@ +// // import axiosInstance from '@/shared/api/axios' +// // interface TraderReturnModel { +// // // 수민님이 만들어두신 모델 찾아와서 넣으면될듯.. reuslt content에.. +// // } +// // interface Props { +// // traderId: number +// // } +// // const getTraderDetails = async ({ traderId }: Props) => { +// // try { +// // const response = await axiosInstance.get(`/api/strategies/search/trader/${traderId}`) +// // return response.data.result +// // } catch (err) { +// // console.error(err) +// // throw new Error('트레이더 상세 조회에 실패했습니다.') +// // } +// // } +// // export default getTraderDetails +// import axiosInstance from '@/shared/api/axios' +// interface Props { +// traderId: number +// } +// export interface StockTypeInfoModel { +// stockTypeIconUrls: string[] +// stockTypeNames: string[] +// } +// export interface ProfitRateChartDataModel { +// dates: string[] +// profitRates: number[] +// } +// export interface StrategyModel { +// strategyId: number +// strategyName: string +// traderImgUrl: string +// nickname: string +// stockTypeInfo: StockTypeInfoModel +// tradeTypeIconUrl: string +// tradeTypeName: string +// profitRateChartData: ProfitRateChartDataModel +// mdd: number +// smScore: number +// cumulativeProfitRate: number +// recentYearProfitLossRate: number +// subscriptionCount: number +// averageRating: number +// totalReviews: number +// isSubscribed: boolean +// } +// interface TraderStrategiesResponseModel { +// isSuccess: boolean +// message: string +// result: { +// content: StrategyModel[] +// page: number +// size: number +// totalElements: number +// totalPages: number +// first: boolean +// last: boolean +// } +// } +// const getTraderStrategies = async ({ +// traderId, +// }: Props): Promise<TraderStrategiesResponseModel['result']> => { +// try { +// const response = await axiosInstance.get<TraderStrategiesResponseModel>( +// `/api/strategies/search/trader/${traderId}` +// ) +// return response.data.result +// } catch (err) { +// console.error(err) +// throw new Error('트레이더의 전략 목록 조회에 실패했습니다.') +// } +// } +// export default getTraderStrategies import axiosInstance from '@/shared/api/axios' -// interface TraderReturnModel { -// // 수민님이 만들어두신 모델 찾아와서 넣으면될듯.. reuslt content에.. -// } +export interface StockTypeInfoModel { + stockTypeIconUrls: string[] + stockTypeNames: string[] +} + +export interface ProfitRateChartDataModel { + dates: string[] + profitRates: number[] +} + +export interface StrategyModel { + strategyId: number + strategyName: string + traderImgUrl: string + nickname: string + stockTypeInfo: StockTypeInfoModel + tradeTypeIconUrl: string + tradeTypeName: string + profitRateChartData: ProfitRateChartDataModel + mdd: number + smScore: number + cumulativeProfitRate: number + recentYearProfitLossRate: number + subscriptionCount: number + averageRating: number + totalReviews: number + isSubscribed: boolean +} + +interface TraderStrategiesResponseModel { + isSuccess: boolean + message: string + result: { + content: StrategyModel[] + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean + } +} interface Props { traderId: number } -const getTraderDetails = async ({ traderId }: Props) => { +const getTraderStrategies = async ({ + traderId, +}: Props): Promise<TraderStrategiesResponseModel['result']> => { try { - const response = await axiosInstance.get(`/api/strategies/search/trader/${traderId}`) + const response = await axiosInstance.get<TraderStrategiesResponseModel>( + `/api/strategies/search/trader/${traderId}` + ) return response.data.result } catch (err) { console.error(err) - throw new Error('트레이더 상세 조회에 실패했습니다.') + throw new Error('트레이더의 전략 목록 조회에 실패했습니다.') } } -export default getTraderDetails +export default getTraderStrategies diff --git a/app/(dashboard)/traders/_hooks/use-get-trader-details.ts b/app/(dashboard)/traders/_hooks/use-get-trader-details.ts index e69de29b..1c6c6b3e 100644 --- a/app/(dashboard)/traders/_hooks/use-get-trader-details.ts +++ b/app/(dashboard)/traders/_hooks/use-get-trader-details.ts @@ -0,0 +1,30 @@ +// import { useQuery } from '@tanstack/react-query' +// import getTraderStrategies from '../_api/get-trader-details' +// interface Props { +// traderId: number +// } +// const useGetTraderStrategies = ({ traderId }: Props) => { +// return useQuery({ +// queryKey: ['trader-strategies', traderId], +// queryFn: () => getTraderStrategies({ traderId }), +// enabled: !!traderId, +// }) +// } +// export default useGetTraderStrategies +import { useQuery } from '@tanstack/react-query' + +import getTraderStrategies from '../_api/get-trader-details' + +interface Props { + traderId: number +} + +const useGetTraderStrategies = ({ traderId }: Props) => { + return useQuery({ + queryKey: ['trader-strategies', traderId], + queryFn: () => getTraderStrategies({ traderId }), + enabled: !!traderId, + }) +} + +export default useGetTraderStrategies diff --git a/app/(dashboard)/traders/_hooks/use-get-traders.ts b/app/(dashboard)/traders/_hooks/use-get-traders.ts index 5524ebf3..d03ce64c 100644 --- a/app/(dashboard)/traders/_hooks/use-get-traders.ts +++ b/app/(dashboard)/traders/_hooks/use-get-traders.ts @@ -8,5 +8,4 @@ const useGetTraders = ({ page, size, keyword, orderBy }: TradersParamsModel) => queryFn: () => getTraders({ page, size, keyword, orderBy }), }) } - export default useGetTraders diff --git a/app/(landing)/notices/_api/get-notice.ts b/app/(landing)/notices/_api/get-notice.ts new file mode 100644 index 00000000..e69de29b From 6ec8c8fe81796a47ae37d11e24a695cecae46806 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 14:53:08 +0900 Subject: [PATCH 643/648] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20API=20=EA=B5=AC=ED=98=84=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../traders/_hooks/use-get-trader-details.ts | 13 ----- app/(landing)/notices/_api/get-notice.ts | 49 +++++++++++++++++++ app/(landing)/notices/_hooks/use-notice.ts | 17 +++++++ .../notices/_ui/notice-table/index.tsx | 49 ++++--------------- app/(landing)/notices/page.tsx | 2 + 5 files changed, 77 insertions(+), 53 deletions(-) create mode 100644 app/(landing)/notices/_hooks/use-notice.ts diff --git a/app/(dashboard)/traders/_hooks/use-get-trader-details.ts b/app/(dashboard)/traders/_hooks/use-get-trader-details.ts index 1c6c6b3e..3193d3b6 100644 --- a/app/(dashboard)/traders/_hooks/use-get-trader-details.ts +++ b/app/(dashboard)/traders/_hooks/use-get-trader-details.ts @@ -1,16 +1,3 @@ -// import { useQuery } from '@tanstack/react-query' -// import getTraderStrategies from '../_api/get-trader-details' -// interface Props { -// traderId: number -// } -// const useGetTraderStrategies = ({ traderId }: Props) => { -// return useQuery({ -// queryKey: ['trader-strategies', traderId], -// queryFn: () => getTraderStrategies({ traderId }), -// enabled: !!traderId, -// }) -// } -// export default useGetTraderStrategies import { useQuery } from '@tanstack/react-query' import getTraderStrategies from '../_api/get-trader-details' diff --git a/app/(landing)/notices/_api/get-notice.ts b/app/(landing)/notices/_api/get-notice.ts index e69de29b..f8b3ce99 100644 --- a/app/(landing)/notices/_api/get-notice.ts +++ b/app/(landing)/notices/_api/get-notice.ts @@ -0,0 +1,49 @@ +import axiosInstance from '@/shared/api/axios' + +export interface NoticeUserModel { + userId: number + nickname: string +} + +export interface NoticeModel { + noticeId: number + user: NoticeUserModel + title: string + content: string + createdAt: string +} + +interface NoticesResponseModel { + isSuccess: boolean + message: string + result: { + content: NoticeModel[] + page: number + size: number + totalElements: number + totalPages: number + first: boolean + last: boolean + } +} + +interface Props { + page?: number + size?: number +} + +const getNotices = async ({ page = 1, size = 9 }: Props = {}): Promise< + NoticesResponseModel['result'] +> => { + try { + const response = await axiosInstance.get<NoticesResponseModel>( + `/api/notices?page=${page}&size=${size}` + ) + return response.data.result + } catch (err) { + console.error(err) + throw new Error('공지사항 목록 조회에 실패했습니다.') + } +} + +export default getNotices diff --git a/app/(landing)/notices/_hooks/use-notice.ts b/app/(landing)/notices/_hooks/use-notice.ts new file mode 100644 index 00000000..5c01c37e --- /dev/null +++ b/app/(landing)/notices/_hooks/use-notice.ts @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query' + +import getNotices from '../_api/get-notice' + +interface Props { + page?: number + size?: number +} + +const useGetNotices = ({ page, size }: Props = {}) => { + return useQuery({ + queryKey: ['notices', page, size], + queryFn: () => getNotices({ page, size }), + }) +} + +export default useGetNotices diff --git a/app/(landing)/notices/_ui/notice-table/index.tsx b/app/(landing)/notices/_ui/notice-table/index.tsx index f9b10716..2d0d2c2e 100644 --- a/app/(landing)/notices/_ui/notice-table/index.tsx +++ b/app/(landing)/notices/_ui/notice-table/index.tsx @@ -9,6 +9,7 @@ import { usePagination } from '@/shared/hooks/custom/use-pagination' import Pagination from '@/shared/ui/pagination' import VerticalTable from '@/shared/ui/table/vertical' +import useGetNotices from '../../_hooks/use-notice' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -27,50 +28,18 @@ const NoticeTable = () => { pageSize: COUNT_PER_PAGE, }) - const data = { - content: [ - { - noticeId: 1, - user: { - userId: 1, - nickname: '투자왕', - }, - title: '오늘의 공지사항은 무엇일까요 궁금하면 클릭', - content: 'content3', - createdAt: '2024.12.04', - }, - { - noticeId: 2, - user: { - userId: 1, - nickname: '투자왕', - }, - title: '오늘의 공지사항은 무엇일까요 궁금하면 클릭', - content: 'content3', - createdAt: '2024.12.04', - }, - { - noticeId: 3, - user: { - userId: 1, - nickname: '투자왕', - }, - title: '오늘의 공지사항은 무엇일까요 궁금하면 클릭', - content: 'content3', - createdAt: '2024.12.04', - }, - ], - page: 1, - size: 3, - totalElements: 3, - totalPages: 1, - first: true, - last: true, + const { data, isLoading } = useGetNotices({ + page, + size: COUNT_PER_PAGE, + }) + + if (!data || isLoading) { + return null } const notices = data.content.map((values, idx) => ({ no: (page - 1) * COUNT_PER_PAGE + (idx + 1), - title: <Link href={`${PATH.NOTICES}/${values.noticeId}`}>{values.title}</Link>, + title: <Link href={`${PATH.NOTICES}/${values.noticeId}/detail`}>{values.title}</Link>, createdAt: values.createdAt, })) diff --git a/app/(landing)/notices/page.tsx b/app/(landing)/notices/page.tsx index c24e3feb..99818fff 100644 --- a/app/(landing)/notices/page.tsx +++ b/app/(landing)/notices/page.tsx @@ -1,3 +1,5 @@ +'use client' + import classNames from 'classnames/bind' import NoticeTable from './_ui/notice-table' From 58d8040fbf330aa8143bc11507aaefc6cf9e9b50 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 6 Dec 2024 14:54:48 +0900 Subject: [PATCH 644/648] =?UTF-8?q?fix:=20vertical=20table=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=88=EB=A0=88=ED=86=A4=EC=9D=98=20isEditable=20=EB=B6=80?= =?UTF-8?q?=ED=99=9C=20(#162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/ui/table/vertical/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/shared/ui/table/vertical/index.tsx b/shared/ui/table/vertical/index.tsx index 515dbd51..ebd14ad9 100644 --- a/shared/ui/table/vertical/index.tsx +++ b/shared/ui/table/vertical/index.tsx @@ -83,12 +83,15 @@ const VerticalTable = ({ ) } -const Skeleton = ({ tableHead, countPerPage }: Partial<VerticalTableProps>) => { +const Skeleton = ({ tableHead, countPerPage, isEditable = false }: Partial<VerticalTableProps>) => { return ( <div className={cx('container')}> <table> <thead> - <tr>{tableHead?.map((head) => <td key={head}>{head}</td>)}</tr> + <tr> + {tableHead?.map((head) => <td key={head}>{head}</td>)} + {isEditable && <td></td>} + </tr> </thead> </table> <div className={cx('no-data')} style={{ height: `calc(40px * ${countPerPage}` }} /> From 2496dd825328e5914e44e484c54c848923630225 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 14:57:24 +0900 Subject: [PATCH 645/648] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=20=EC=86=8D=EC=84=B1=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/traders/[traderId]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/traders/[traderId]/page.tsx b/app/(dashboard)/traders/[traderId]/page.tsx index 212d3add..390556e1 100644 --- a/app/(dashboard)/traders/[traderId]/page.tsx +++ b/app/(dashboard)/traders/[traderId]/page.tsx @@ -14,7 +14,7 @@ import styles from './page.module.scss' const cx = classNames.bind(styles) const TraderDetailPage = () => { - const traderId = 1 // URL 파라미터나 상태에서 가져와야 합니다 + const traderId = 1 const { data: strategiesData, isLoading } = useGetTraderStrategies({ traderId, }) From a0cb43d08b72bded36d9620e4c1d46f174474715 Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 14:59:46 +0900 Subject: [PATCH 646/648] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../traders/_api/get-trader-details.ts | 74 ------------------- 1 file changed, 74 deletions(-) diff --git a/app/(dashboard)/traders/_api/get-trader-details.ts b/app/(dashboard)/traders/_api/get-trader-details.ts index 42303634..348c2724 100644 --- a/app/(dashboard)/traders/_api/get-trader-details.ts +++ b/app/(dashboard)/traders/_api/get-trader-details.ts @@ -1,77 +1,3 @@ -// // import axiosInstance from '@/shared/api/axios' -// // interface TraderReturnModel { -// // // 수민님이 만들어두신 모델 찾아와서 넣으면될듯.. reuslt content에.. -// // } -// // interface Props { -// // traderId: number -// // } -// // const getTraderDetails = async ({ traderId }: Props) => { -// // try { -// // const response = await axiosInstance.get(`/api/strategies/search/trader/${traderId}`) -// // return response.data.result -// // } catch (err) { -// // console.error(err) -// // throw new Error('트레이더 상세 조회에 실패했습니다.') -// // } -// // } -// // export default getTraderDetails -// import axiosInstance from '@/shared/api/axios' -// interface Props { -// traderId: number -// } -// export interface StockTypeInfoModel { -// stockTypeIconUrls: string[] -// stockTypeNames: string[] -// } -// export interface ProfitRateChartDataModel { -// dates: string[] -// profitRates: number[] -// } -// export interface StrategyModel { -// strategyId: number -// strategyName: string -// traderImgUrl: string -// nickname: string -// stockTypeInfo: StockTypeInfoModel -// tradeTypeIconUrl: string -// tradeTypeName: string -// profitRateChartData: ProfitRateChartDataModel -// mdd: number -// smScore: number -// cumulativeProfitRate: number -// recentYearProfitLossRate: number -// subscriptionCount: number -// averageRating: number -// totalReviews: number -// isSubscribed: boolean -// } -// interface TraderStrategiesResponseModel { -// isSuccess: boolean -// message: string -// result: { -// content: StrategyModel[] -// page: number -// size: number -// totalElements: number -// totalPages: number -// first: boolean -// last: boolean -// } -// } -// const getTraderStrategies = async ({ -// traderId, -// }: Props): Promise<TraderStrategiesResponseModel['result']> => { -// try { -// const response = await axiosInstance.get<TraderStrategiesResponseModel>( -// `/api/strategies/search/trader/${traderId}` -// ) -// return response.data.result -// } catch (err) { -// console.error(err) -// throw new Error('트레이더의 전략 목록 조회에 실패했습니다.') -// } -// } -// export default getTraderStrategies import axiosInstance from '@/shared/api/axios' export interface StockTypeInfoModel { From f4f20b64a1429b22a2270e1edba7bb99d160e2cc Mon Sep 17 00:00:00 2001 From: nanafromjeju <nanafromjeju@gmail.com> Date: Fri, 6 Dec 2024 15:02:27 +0900 Subject: [PATCH 647/648] =?UTF-8?q?refactor:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=B6=88=ED=95=84=EC=9A=94=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/my/profile/_ui/user-info/index.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/(dashboard)/my/profile/_ui/user-info/index.tsx b/app/(dashboard)/my/profile/_ui/user-info/index.tsx index 5f12c5d8..0d3aa53d 100644 --- a/app/(dashboard)/my/profile/_ui/user-info/index.tsx +++ b/app/(dashboard)/my/profile/_ui/user-info/index.tsx @@ -94,19 +94,17 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { const file = e.target.files?.[0] if (!file) return - // 파일 유효성 검사 if (!file.type.startsWith('image/')) { alert('이미지 파일만 업로드가 가능합니다.') return } - const maxSize = 5 * 1024 * 1024 // 5MB + const maxSize = 5 * 1024 * 1024 if (file.size > maxSize) { alert('파일 크기는 5MB 이하여야 합니다.') return } - // 이미지 미리보기 설정 const reader = new FileReader() reader.onload = (e) => { setPreviewUrl(e.target?.result as string) @@ -181,7 +179,6 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { try { setIsUploading(true) - // 요청 데이터 구조화 - 필드명 수정 const updateData = { nickName: form.nickname, phoneNum: form.phone, @@ -196,7 +193,7 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { : null, } - console.log('요청 데이터:', updateData) // 요청 데이터 확인 + console.log('요청 데이터:', updateData) const response = await axiosInstance.patch('/api/users/mypage/profile', updateData) @@ -204,7 +201,6 @@ const UserInfo = ({ profile, isEditable = false }: Props) => { throw new Error(response.data.message) } - // 이미지가 있고, presignedUrl을 받았다면 이미지 업로드 if (selectedImage && response.data.data?.presignedUrl) { await uploadImageToS3(response.data.data.presignedUrl, selectedImage) } From 1b97fc0eba22bf362f1ce5b513a3b10c26e67dd6 Mon Sep 17 00:00:00 2001 From: kimpra <dustmqaksdltkfrlfdldi@gmail.com> Date: Fri, 6 Dec 2024 15:11:08 +0900 Subject: [PATCH 648/648] fix: build error --- .../_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts | 4 ++-- .../_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx | 2 ++ .../stock/stock-manage/_ui/inactive-stock-manage-table.tsx | 2 ++ .../_ui/trade/trade-manage/_hooks/query/use-trades-data.ts | 4 ++-- .../_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx | 2 ++ .../trade/trade-manage/_ui/inactive-trade-manage-table.tsx | 2 ++ 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts b/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts index 77f43aca..840f2bae 100644 --- a/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts +++ b/app/admin/category/_ui/stock/stock-manage/_hooks/query/use-stocks-data.ts @@ -1,4 +1,4 @@ -import { useSuspenseQuery } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' import getStocks from '../../_api/get-stocks' @@ -7,7 +7,7 @@ type ArgType = 'active' | 'inactive' const useStocksData = (activateState: ArgType, page: number = 1, size: number = 10) => { const isActive = activateState === 'active' ? true : false - return useSuspenseQuery({ + return useQuery({ queryKey: ['adminStocks', activateState, page, size], queryFn: () => getStocks(isActive, page, size), }) diff --git a/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx index f2a548d7..1e7c5fdf 100644 --- a/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx +++ b/app/admin/category/_ui/stock/stock-manage/_ui/active-stock-manage-table.tsx @@ -14,6 +14,8 @@ const ActiveStockManageTable = () => { const [currentPage, setCurrentPage] = useState(1) const { data } = useStocksData('active', currentPage, TABLE_BODY_SIZE) + if (!data) return null + const tableData = setAdminStockManageTableData(data) return ( diff --git a/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx b/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx index 1180acca..602dad61 100644 --- a/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx +++ b/app/admin/category/_ui/stock/stock-manage/_ui/inactive-stock-manage-table.tsx @@ -14,6 +14,8 @@ const InactiveTradeManageTable = () => { const [currentPage, setCurrentPage] = useState(1) const { data } = useStocksData('inactive', currentPage, TABLE_BODY_SIZE) + if (!data) return null + const tableData = setAdminStockManageTableData(data) return ( diff --git a/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts b/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts index 50e151ad..7d32530d 100644 --- a/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts +++ b/app/admin/category/_ui/trade/trade-manage/_hooks/query/use-trades-data.ts @@ -1,4 +1,4 @@ -import { useSuspenseQuery } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' import getTrades from '../../_api/get-trades' @@ -7,7 +7,7 @@ type ArgType = 'active' | 'inactive' const useTradeData = (activateState: ArgType) => { const isActive = activateState === 'active' ? true : false - return useSuspenseQuery({ + return useQuery({ queryKey: ['adminTrades', activateState], queryFn: () => getTrades(isActive), }) diff --git a/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx index 6e3606a2..593164ab 100644 --- a/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/active-trade-manage-table.tsx @@ -6,6 +6,8 @@ import useTradeData from '../_hooks/query/use-trades-data' const ActiveTradeManageTable = () => { const { data } = useTradeData('active') + if (!data) return null + const tableData = setAdminTradeManageTableData(data.result) return <ManageTable data={tableData} active domain="매매 유형" /> diff --git a/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx b/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx index d9d0c487..922659ba 100644 --- a/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx +++ b/app/admin/category/_ui/trade/trade-manage/_ui/inactive-trade-manage-table.tsx @@ -6,6 +6,8 @@ import useTradeData from '../_hooks/query/use-trades-data' const InactiveTradeManageTable = () => { const { data } = useTradeData('inactive') + if (!data) return null + const tableData = setAdminTradeManageTableData(data.result) return <ManageTable data={tableData} domain="매매 유형" />

    y|ZFV&d)(Z>7y=HGD+ie$p^jGzgu28oxCc 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

    >Sg?OGtk=w%Q9w@4Tc*m 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⪙4|lDi z00+C5WW)6ggBHfN%w*PF0!lIs(9D95gIlVLVZs7z*yXP+b}CzL^DNkr&w{noI=;e_ zuU=^BiP?2*xouWlVx7NCP+2rA7>tZ!i%H;`5?5KNAd;>1k7Tv6Xe!J1fh*pB0BLF+ z&NzTsAjf?rZRSRKpc7k_rZ+!v@JIIsrr3`xwg#Guozs@bJPS6)x93_2cI6A$Jrrcg zi0a3@peiGtuV5WiBYYspqE3`Gbt08&cRMVj@dE{-j%_>0IW*@2hPYDTkUx|&4we>H z+_eY_96W>lhEoV)g~Y}1Imqm5d~p{;><(M_%gUB5GXLT8m#Q*coKec ztgr&cq@WMp08hWhb6QnFxPR}G$l#Fx=dcQXpD`C`Ux5bvftVT5Eau1oUTAVL>>Vw@b*Zz+|`+%2)PyHu3V3@pIlK9{pc zV47ItW{AHXsMU4T_u!M3Yok*cUYL_r?rq`r{rpM6pW##p4T@vTMyJ; ztEjTmh%g`uTY{u28;iPLxgOMV$dY@C{VMVc0tY84ms4Hims#1p`$DizX_-bz2(I@k zcY7pUB^L%H_)m6n;8x72}L4o~TE8)X6 zIYB8BvXZ;2jA^tNIdMc;r z44bB_P)-MB4jeh0%K9b*6=zyH4nSlw#SY1Az(@V6@7X(|?tscg#=8k<>)M|TBx~*F z8)g;%w({S4C|getgHI^d;$%eF$a|_FjW=|YtavqM#czL3BGNrE;`5$JwV2IWhBaND zOeavAbE^ z!~t{0#(c0`fmkse$IgeAZ*N5wJj};rgH(9{bu%uZTH+w03aIbH)a@M-a(2Rhk+@S( zx%RLynqh}B`U=Boy@5JItmC^Cwlkm$ZVe%s6utn6w}JbcD122h7VUL}l|Tq$vu=_l z^f`rnk(a#lP(i+XY{sV2QMo^S1%bCwcyEER5CTk4KfU2P;qCSqf4=VA83iY%!+6FX z1DuBRyt3_q$d~3XJv3 zJ;5khN9hWYk2LzLDU?izU%%7EG+4Q+u+0He*ssBVG%=D{3BWqGQ(;?IR9;tu3AzEB z7xwP59@-H_y|j2N@ab?VZUM?9qN}R`?CiilkVv znf(HunxKxZ7wJWaPK?@#vQx4aXoa8$Q+c^?nmQ2eMX(z>6WoH|1NqU z=`;#l&Dw;Z?Cj|19F~!lIS6>Q9&@rD_EliKNt6HA z7!bvDVuU@KuMvSIxfzmn!~RQa#i_#2T7niv5)uA%Q}T_sxV`WfTu?~ij7TR8921FJ z>$en?*)JJh5kPK4e;{G%iwm=)p-GCC7kC@qPZifJ{?FSnAB5>oy`)FNd1-a=BN zjmHnR*)KRy2_5C3537J-?w9%s7DxQmqt)1#XZpmc_ZmYYO0Xdq<~|fgmg2p$*4KrP zHN;B7OEfb*3zbrs2zAlTQTHFJ{*GV=>n(+eN5b9Xx^5cV8c|;!Q>0vc_T5ZnTwooP zPTC26yy0}UO^8ZuT^5D2S?pfmaBjb3|D#@nkeN!(u#Rq|;T2ZM7Hfsy{_wf@=*79& z1$Z1NY(JWz1^;TeZ*tr)9+t$*{*jPjj<^AeaF;?KaUyDSp3(&3G`Z9dZR`+>r8wy( z2<1-IcPCGtL<~cMiKrE)fV4MEma5_q2C{H^g~YSj)nmFIyy9d1FUg-!$SO65Ao-8P zuDKo|%-8YsG5_g=s;i8R0}Bc=E!{Xjt^%+S5he7h0fmyxhF zZ-pI`cmvGicwqFdQGE;j8Z-3+i5QSv)B}Q#Nz?&W=`t->2E57~csI;2X@g$zZS)g3 z6ZqtpZ8j3UC;CJ&!!HKr!DuX~KO$XoGM8V_+3Kks{fr7q$C3veTCaiSxF2tmtXL4? z8`G-1gaKK$b=`R*FhX)Kz#gi(MhGHwvLV&= z8Fd_yKna1g+icGZd>4#DAJ~)$Hcx>FBX#nS8Zn~@;|$7<9(e#WRswl^qH;(7`xj0> zd~N?SgJI2`rCMHbB@wGC`f2Gpa=P%6U0WZ8@(iJ&QwH^HpJD6L^!k_s^%Vcd&U$9NM2T@y2GJ;|pH zOhWNG!Yt;E^syGQd`c1##Y`7!EyVu-F5E5Hg8W43qz6KNUN&o=uhY_;}govN&!Z2(9JVc+$}Chnk%QrK}7k}M1}k^#R|xV z)YPpvW2a+T>*+Wht|Vf4 z9e-!m5QMLlt|k)cGHqjqM5!t}`ZIF5?EOXSKlL73V6a#!h8ZeeW9xTBuLK%kB6S7` zs&8M<^$=sfCfgn%skfB&5TO-+Vg^tv5xaBRug$kmU*jqb*R_$8#Z{38Sc0WwzWQwj&Mad5Cgb`o?ep3srbykfO|&!Lh}jW-y`sH6fh^-@f%gDrcwi^mp6)pl(Ja<39^ni z?L+@kJ~J|Bh8u!^@%>>h8ut}H&H+Q2h3L)zfBq1p-i6+QkMap<6!PC<3Jj^F#4x-B zTiqim0<;J9+McJImXRToy3iZg?lmw>+qPFU3wrgLNWIt=Ed4ETriv$B$*J%Pi+uH_Qa73fhd$!XVrfFpOR??%pyylVKv7coBKlYp_+U zxao}79<@#0#3SEVWHnSvW-K1dKC6SOs#oJ&^-!Ye6=#XXRqFzJjUcN0D18xiaC0`BCx%iF6EnuVtHuu|TztD+IwZ0yy}{6z&-s>HwPS1cgqVidar0Y7HzO zsz|2#-2c37ns%366<9aS5>gZOxq+se!q31UxApnZ<6mD^p(QU+L(vS@H^Su?%8#%L z)MUsujs34Che`no^&^Z%idRzz`*qbcDK1VLbob1+?uTknU25|h%B>U1A;fyD5X#L5 zY}gZJZ$uAeRT_xW=7Y!V_(s-UUURY9KoUFf%cA+dLjmKXz!Aa2y;0IX6>rmVrd(93 zQ#Bi2zo`5+sWk(DcqkbJ{V$`FLrv|#BsJEqbm>e-|DBy*7S}BKv19r@ilQEFwxEN&!qe_hhSggaI;+i}_y=Le(={B-v2L7Ty*U&#T}PX9Y8Y1% z^9S&bYw>R`tEYTr18t-i6OaX#5tZ(Pi#_il=7c@Pu!@A<$wNNs%JQyoVAM6&(#tXO znOkzL-05ec0Dn0@AHj0Wuz0 z4_`>UNa~p=pf`RrQHFDA%z{Cm6AOh6br(ZPS?o}1?hBUlx+s&W@QwS?4h0~5;sdrdTaD_? zN6#N_5F{~n;XDSZda<#K=Vy60HOl)%0dJ*dgO>r;J27co1FgA;E*LE}30w_EG5oE_ z!&~GCSD(wdtlF<>LbXwfvX$cReN=DvESRqNNZnrfp;sx73J zd(J_{a3sBHP_>6JRrzc6awoeB?>>R`Shh>DHqj zqE2PG5A#VuPij^`n#3pzVc>O_E1|B$h>I&{ zVeU8k9|mU(Y5Yx63415{Ci7c8F5QuJ`)^=;NU$d=q;;H7Rx6Mv1i2a;U$1hQ)qagJ z@+@fat7m!?(mhT%s};zS|F^b;n$%nlca}Q z4Y;&(fT6HekF{Et%Zcy`qwlS;I7)-wmSov43@ehOt#%JkIMYsUuWjY6a=6v{0HI%E z7#1eSTWuZ?C5i_-43xOa0arT#XrL7Vh3`ZdmL)F?WUov1iij(o)3LBBQsN>(KktBd ztHr(nzlSg^OIq+(AjF2LI(sti3RoV63Q(k6Ww(MNrO%+tCgt_#3 zc>0gjt5|Je(KUmx8Z$ZfYB3}!);78paZ&kd{|1S~MqD+%{pIYdU4umQk-AI2+8-d1 zFo9Fqt!l{mSNk{Uq}bc&Ug)6$_TZOVl`tQP&Ke|VVC_F36@7^IDcFO*vMOQT63P>6 zFgXWn{|3Eu-{21BGD&Nb3o2plH3XqlPsM?^LOBa-?*Iy+F%cz^)39~}L9j!idX!B-ms#L56=!o?f>@S+L#?K>rKebR7OVMxmOB!bWN^X5%<>h3o<;W2ZrjvFz ztXNK!8ju{DVS>9KsA4%CdxRTyCVjUjv{bD@#d`TK+|j|ZpxN6) z*g@SfxxZ=!!e+5X2b+yudd(3Mp;>D~BBrfAQm`R#_USNkpO?eiJu`QVe)SLS476G{ zYjn`rc;IvzArYM2v}qezITJsbUvhNTUc)$vNovKp9G*1}<0QsV=8YVmUEf_BX0X+@ zT@}1zr9s=6LR0?m0iK&`2+9#!>tUpDAM(&d8Lb?mwZF$m`Jk6$+hHijXxDSY&ZSc$ za%qp3CT(L1>!t_On{9b*;$y<@CL4OgIOH&`<=`SA&H6wE$#L3^Ua}?)l98>Y2QvKl zYt;5z^wItG0CJ=@va9j*pVg^2Rf_=`ZZMKSwT1v4T3QY0jnyt}Fff6G(%J~tlCi@Q zu^6EKt0+kGx9Bt*D6O52#fw|M&@6={cwmhub`&{WYc)hk^Q-7QJ62vhz3hK_%fB5T zR&Ex3dzb1)iLD&4H3b7w%*>bOFEB`s*xCr=gdK#?Wq_%h9I~}O#tBa@Fb`?XkLB5F z46({$7q>4$08?-QG%-maQFD31+OVZ*^W%K`;v5pqhgkz zs>5TUQnj0E1lQh2mar|%0a~09~DN^Y=GRHb!_^W@`}0h znw;EVLZ2az%R08t+T1(0<2{6+EBaXQ%HGe2yxd?yuTP81I<{BF?4r!vuEAJ2k~tkyPhSLy)^I9#TdAU!eo5E zmGMV)FWALHBuwBuTRRT0uAzIl1;U3x)dgFUDY%waUzZi{TTY;osNyb}Tj@qw-r1;2 z(N04{He#|Ko1)^^;Tkhf-SR$xCxbqgUw9OInqpF5{P$vFZr?is&g*=;P*gZn z8NCxDpsFo>2F@UcZqc?opy~kn9l+XdDaZz!3@M8g24{4_XI^?9s$`RsK?b8ou3?H7 z=?l@u6=V2YMtZW8jwSwicQdTP7lonaI_0_Ht1 zzDk@J?BSgrKUrSPTRNgFQrM#Ldz!RWQjHAJLN z$_s2LvSGn8uUWqv7@2n!K6=`OR%^Kt&6z4cko6xuFygDX)-BHd<;RpthSXP`#{tE= z3RDzuG=-tvIVz|zUKLa+0yZof`qiK0<8U0{t)Tb}<94&x`NKK+`<)gCBzOU!3=X{k z;Znk^#EMH(@5V>CX`1MF#O)kX!;pAZFRnsA)E;+5$yr-!-?##*b(Q#GRJqGn-4y1r zb!_Vx6`b~P7v7Zui(>*lo?>+?2ZD)P1U->Ub+Y24>x`lQDk=Opr&R>&Py2+$@3 ziK7TJahBVZuhN>|+%Mr;t8e!41B2a(1A)OF7vK?XHtexqK~UH_cV)b}TEFP}88D*; zhy34inrpn9uGf`WhR!Z9=6oNi-| zs-u{s@v3=XYW7F+Ps$%LAKG^`fMz6mE&Fx+X(RDjL`5%asBQQSg$#}uyqMCC zV0pR!OxA?zd<8@?s`YfvVp5;R4Aq0wWg=A!!itC5HJBu+0@(j-cU>J-=9yoRG6Znq zy(5I6kZjAZzi4gRZOIz#d$G+U6@!rI4~kAliV(o*jmD^E-$s>S^FbJ4G${#QF3%~; zFJe+fwQ!H4=>Ir?rN9kG-SQ%B7JENRN1#feu{zVmygoegAjZ(QJTPZh8nLOWysM?!Yqnm!HxDn0m zA#uMJP0LD`F7GbQmOW_N?rOL(svowJ^L$y@6`oXdSJL(WoK`dkR)NGw{@ueI#2ORz z{2bhnZCVx8_2)P@SA2qLa{xYAl0>P8P-0*)W<_)+6$N`r6;+jAm`(d&>S#f~Wu_J} zJ{>k~!6qQ~&#+OAoCTR^VHNMz-{eUF=kZw3HIF(eB$rgjDFIkB(Xc0Lbhd_q-8T%> z-2o$o0_&r@yKWe8X-_NB7nuz${jz4N<~~vr+Yfj43PqZ!ccFMj(s59lqRx1mcgHQ~ zgUCONwjF%5LlYDf^4z$;zhvr0?@o;lotogTHGe@U8xJA@5C(#*;$3v645x6eZ39N3 zx#>GtFf%vQ-o>^{F_M1ua{&!619;iKaLP$Lk|p^Ia|!m{I$R2dp=HYxs()LrqXIslp`oWBr)A z7`5RKO;SZM6ouqnij-9#Yb=oA=CzMnWWaj#> z|IC|-?h_TcYU;8;QVaU>YEUs42F$xtMkhRE3U?yBg+3o0a=dMGi|ZpdbVOvit`zOL zB&-0CXBU1^gnbTvtSW0{O5noJ(ynU-=Xh*UR}zsgHHQiSga8)k(KTvWEa%j0@jujT`p<>c z&5D~pzy4+XUMY&^PfoGS)KM_O1}oeK^SpWb$eZNfWXwNqSIC>6ng|#ao^OTUy9@dV z6YP?u`|=^Ww!o3DMjIUO2F@@OeoE8SOGfk?OvO!N!VmdAD#KgCC+cMW@5t|)@y7w( zPF;tERje3;j}AHITxq<--5Ast>dI0aoss^&)PIudB2(grW$bbJ7>b2XslKSw&0p`t z_t=c1(}=+gu@U#EnoiW8Gxk=|%KgB_%7M5Rkut+Q8yY>rQWWC^(+`nu5MjMF;IREk zf}qr(>+IduCfrDnozQN#vYh1Wik`XgZ?pCzqu?Fe8}*HUS;}U20-?h_^^FU;MK|IB z)6_=BfvS|DTP&)WDi^BUF1(!~l20)E05CzLI@%Z(v`ow$fPrV1d$o*2m@9xm?Ue zyT)WUctdgPlcedYyhuo0)=o-2`5nVqZl7C>v96bfL%YR-qSbf z(TmnUC_gBs@g{u4pyf8Gks{wqSg`JJdWBp@%4*$+W;B`Y@#?(?A7?)(7&jQR_L!op zi38Ps6etQ`*IRs;+XHE3Pvtc}l~qw4UH}+8D=aZ;e^2v3h|t*X-~uW-m}90%$MYsj}Ji;;75DuqpO~A(`-Vh3=VlB}K;>6v)oo z@O)S`qOM3=`h){$78i-9|3Z9h=Up|z2wptfLOKRlSJbV4$P+85BPH)j6!Mxr*B%(} zeJbyEWP9PoVu`I7G^Z6N`tL99^`58@EcbM&GdPu42vz00Vj z+5%T^Z@qUL@7?q*Z>*o427V+l-{lR2K=R__K%yYOlf4Wmq4B)2eBS2;?v2K%MzBw1 zE(wPIm+iyAS#ozjqrsB91)-W%VXnYq6Spd=L?#a_UMlx}HZTLg4EO-Bg)mylxK&XV zLNsbe#j~bx3Pa-1lb*3t44%&Uy2#$AH0BZ?a^*V+fMFT8Dyl?54=ZdcNcsfeENDSu zG9|@t<#|zJkEx=%Zoc3p)_hb*kV4m@OkYj;%{X?-MlEF*c#5Z@Qf?2MsJW6pCakjU z(K&9fIQHQrt+&eR1nc;IRg^|&EnOX@*aq@1>1yYmVeDhp`?*i;Dli7_f)Wx1a^P5B zlQM}qymM3vyt*8D=l1aO2*Y7|^;BV(j%=Q&psU)5AL)ZorELW#ym1Q*9(IlSuo(2l z%5jc7k*pFZh2h0y#K2Q>Sc*y&0<-}%)rRq-+F7R28Bb+xSJNw%zwTvO2sm2dp9{geb>wlW7NVG6;#3RHFV&!s8!6{+&YLp;K_iLVSaLhT&F+;_mB#z zs(76yKdC;O5108gHn^$%u9%8z9?U`jnqXi<^-i|*0S5^_qTmF5YApc;!HXg{@*YRB z9EfC$py6rhUsVqk_?IdPpULREmj%GiNY`JQXayMzu86S~Gx|RJK!_V*SyJG2I8>sGHSxcFO1GbEkhqyJ( zax^~~k~xpIH4Q3U$^yj!Q0NMVR#nLDyz-J`Sv2#=cXy0LK}x6#>v=4z8O<_yG2ok1 zdwl+ie0rRD%`m z*Zq*e##4K5KSKF|wb-{ly1fmXF&d(!wHcqy3%^Zv0tH^Un+@S|OdlvS|{WTwllG1V0N5-srX})+WWHf$mY|&U? z;$o4bOVUl|bdB_H`Ro?`zbdKnFROnZ)FkaIfPLhLS9mHx)XjKTT+Ls37yh{x8(Kog zzM`55mbtrCs=BGrV#SxCf*TGopDj&%NRni~x);q}h_)B4@<=jUblkK-HX({_di z63!6wons&QtUz1#UyUOZW7WPkZ@e{{W>^FCj47(gga4&wV3*$Cdg@)VRFKc#DZPx2}=}Rls;>AfTw+=zNs)X^) zEjWL!jg&se*htmLR!lOHDOk~C|D`7H3eXkFaD4DF&QOb?ezsKQLfqs5&j700SzAUJ zfGtw{fEFCm^DY&?+lxq~tF28|pe6DUbY;2^-#Hrs%T&fYnh$$a6JuGr8WRFH44!lNZt@QeW=1nHYJx2C5F>|O&{6H!LP+4Hgy z2ziBoWT^yg2^V;};wV3kJ>TkG22n69OllxYU9yE4&?7GhAg`aKjj|+ z-M6gPqph*iX69l^AMpf$Dy$_|@v77{EW_+X5ghpKQxS}>$~BZ4&i)0M{ly^r7l4=o z!(oR-2sSJ)@_MdcCZYxrMz` zjn;jmO^d(fLFH6cL*WZC%%~J@GMjjw!K!8QAucLJ+d`D1SQ%64d5}3dwE*Q8hf>k@ zQ-?D3UO!+;l~`HWzk5mqiMOfx2uXz)L0YJ~|5h{P4Rc}t+%!T3$#qR4 zE@LifObl*$2ttaI)|_85HdpAH=_2<0RQpHAo;2}J-z+|HHbT^%Aw0=SiEEX7 z;CnH&GlCLbaK4(S+f;Z<8o!I?rYmUxrt7#kTui5uiJ*RLp55WWnveVu{46 zQiY))aup+$;wPr6XC%(TlPVI<2Nrn}##wkQ)>ulCbs&MIJsTlpCQK1&_ z+AEQd>U+ZrxBF)0;O^9gB4eEqkRkJMDRn$u~B1woJf>BkJ2G&-n z>0r7>k#NZjcZxDr$9pG{v&O@+fgoz4#k5MOWmR$dqC{#PQtc8(y~{vRraH&>o1JoW z(RClpKD4fp7RrCL>Z}9%ur{>vd`*|LaYbv}Gtr(7>?Qq;*HFp>8cIzzTU(QARJ@ z#*F19e)KQ~=)Oe1R0tY-l`g39TY=}0(@Af;^+(#2_09C_VHA3CCZI^m#m|!LvtY24 z;I(g!Opq}WwPg$SsX=|$of1H|mYiUhl#l$GG^$iZ~Fc7id>p!C&Ewe2+oqKTt zZsE!!FEdBNZMj!uHC@nK%B!yXfamhcX-AUYqYQdAgO(nnxh@l?vMAjFYdC^tU2dALxYes>gO6qg%1@xji?2rWgMTViGt1$C2cWoeQGfWUv1s-Bl?f;S$gWva*DiJ4$)q^B6QAnS8r2YybPge1H|>u^`>RFm)kf3C;ep4JK{;Gr2b+* zl|lZMviBK&lloUVO*K@|YD~3`a>G4Gl&(fxzBaqRJ$1Z%g3A;ouW9S`{Gok`??7?D zm8W}K%lyzgyCGm%HQ_KY{p5Z!JD_69=V1g+Do6<(b<%p)l`8=VeRtWW0&5apW$>T9Dsx|EJy}1MnwBhNF`} z#ahvMlQSjo4>BH0b`KXMg?>TaabV(q$6K;oD9;oBe1G0hWTONHiRao}PAqY@Ack)g zG2vh_7bB{Ha_|S{aC_CK74OsIi{78&AmW8q%nnhO)?sJJId)B+s&)C@k#ah**DajK zVHk7&*jZ^m#g(d&>`4%Gd``}Gw_11rFJh{zhEfk^9B9gYOW4AA z)3B+@#7j_+);_Rh!}LANsC0Re*>aprp~ zF9nW4KzhqE9t|wy1F!OmBHWki8sUx{nL%uc^E@yyG(?}w-*1F!Gc2+O(5)nSNM+T? z!YOa8KU@@cEQ$AvWm-i>fyLu!vC`IGXQ+^c^Xo8e)BM!}Tb&++G|EzpLsy z2YP6bE@CKb3q|9Bx5Q%5@IkE2)Z77?;GF?V_C`vm)?p3TsMOl8i+I)b4L5~r5`mJt zp@}G*vMQd+!zE5`R&HTO5ME(N^+%=^Q%p2A zu@04`e?-ezC@L9Y2>Xk^3LaPmZ}flhB){} zr45xR##wtODyaR}BbDzu=INg_3xciZ1@8rlIBdxaaBP>TLb{0{3BRsl+OTuZ3f+sQ zOMTJgGOk=QkXJl)d=jc^jq((DK(@1GmxmuYah|l(jLYXOdtwag5WE^L#(QN3g1=qJ z^gWc_T&A}*sGYPLSb(OG-MQg$8>NnCiz)kvdt@#oigBgbh2o){b9`p{fwPuOf#{QV zZf@i1;JV?m6D4g9EvL^vvRNZivQm3(pH78FYW@%q)oelgOpTi`?4HFGlV%W)tHeYa zI{jv8uqb-hfO*#R&Z&9v?(M$3fP6)+6s^T{XGSDA@Hz;(;xE*6cD3OFvLAj+7Bpb{ zLDnO9D>hpY&lQw>we@tZXFT*!UT9`wLp5)@bYXkg&fI@9h3Fihzt=ceS-RafuS9>h zDfk4dQTHO-L%~))svo)K-O8~UH>vXe?FXi*#8U&#ev_XDWPVi^|HNOM<;?BJ7ZYOL zGtdUkpl!P=(mW!zIVgx>V$JSG1R+TVUJcfL_TO@kC$!3z77DC5Bx2PSAYlWwEqJ+z zcah)2Gw@%kR0~o(i1(n_a3C9L+q|~0H3uX0y_%AC4IGfx+^rSTF#Z9`+GpCy z`eLBjEXpY$S?q*wQgmTuAS`hj7A>r~$g)3`{+g@6sf@dsXPRvafI-e7)fY_`t!_&( z*)EYkj`Fa&sG@YubSGY0MR1LB{?mbum`a=S7q~_QRFgaNJbvbMB&Xu+#x@txZ!H=+ z$vOG#BWs~(`F7uE&c!1I_hI*Jd!u1M%$4<729n(&bw8SdpPZRnE)Lu3JVyjMg%n~L zt6V0aSDV+MBJZoNQS+o4S(N90=lj=J4x9%e%W`XYQBJ`_CfB?!Y)WRg`Ga%F7cnrx zqlRgzJ`J6tDw50!SYg0k z&dFsST6c<;ZugBIdGJWanOp*AprAT;X(*$OQxZK!=JxQA8fxQ+XFi7a;;FBHw=;q@ z>zk!)84LD6J`a%ZGb)aW|HPY`V5(9eMi*a7;Q~b!9*C}q0JB@9P_`TAX1nSjK);Xu z@LkP&30Pob#kC#($YNj4Yu7IJQ!t-aqdifrQb!7~edl4sg`eF8c^A-4d*1C?w?$y2 zSr>9bq<=RL9rc-54ciAa6G==2pxL0+4Ipkh%Z}5N$dFj!W{lV-CPX1C~|&f)R z{tHA6NcB|CtiRk=YQh#2o9l>y{&*vKA(r$r3NDDIf#oJ|z{>{vFAzZC`wJC#m4=?& z=*)4)yK&T0Z_$dX!Qe*v25CiPZb|v>7e9YE&{was#=kx+u$?h?*nclYesx(jUZxIp z#5){4^2pxJa)tt;)jqXVjAzEZ;fEG$ooL0+N#eRwMteT8HHK9lcX~ehPB;HaA6E@6 zNa;U$ABZW!@-9Gxz$q!{9m>72Jy*`K9{Ds*jOZ3HKF3 zEz4K`bn*!hUb=-tV`tvNP)L5Gk#eyq{qT4rGek@hziKEejp1xdT^{3BwYpLbt2$6k zm+>E9iZxSQsKm4x$`8rBQ`d6`w$f+(9;R5M)(!3LIaRir;9RtzG#K%7rTwt)*A+wU zQL?572gYmnF6OD74!x~K^4Z221CQqNw3fTp2!@s4zUCO~3B%Fcn)ET(w3&B4K8QT?il5%2MtUsj^<1LtRW4nGUZiXLF>7!)WY)RbGtt-Z!%; zc~x-MLFjT9`f_%g2d5g7@nseu%{?J458o6@9<_vHmAP3dH=kGrH#0ZLl`8JWl@Gu8 zIMGz4u2*U&k1025^)%jo&YBrm8q$;DIYna}MzIVK=P+)IGp7h7KnXDfUth`Z$(i6W~byYi+ zcvb-3WrRNKt<-vQBN`DTe!!PoUwj;&N3d=&viX=JRgp%zw#A#AM91I+4@zke*Iw#@ zJ%z|S8o_&9m)k^a)QmCkiqO?98}qS4kF`<>l(ER0n{Z9 z=gugLY3p>`aG1r&ke)WeGFb$LUdJx;GZaB-KyUopuO06OPw!R1fol7iSbfI|^$lY;nnp~-INokX8>bMj4ngTpGNEAwNYWZNijwlGp!4y`dK z-f^|ka(MmA%LJlQEM0Cd!y~$-YxPZAFz0h!83T`WTD*FndFvaN<1C4B(biQ{%RVA`1k=%>|y>Kb^HP_6u{vSo23Tbc%A(-kk4qW~6~sSg|4{ z!#$f4AB!a9MECQ)Vvw;!Wmk>v#xaJ>$0~%3Df~c8=h2n#oNo(sbtJK@SK*Q3UubcL zrK92*;Ys3F`-JN>B-i)-d)v1jbqpEo66NDsH*!%K2`g(FZ+-b{`}Y$52P4>Kheq$H}J89{o4+6>hw6 zS%RfhO|Vdz2EXJilY5hq5!>=CzYD076t})tK6?Nmbp*g1+tMr-;kU5|Y+qRiZ=Tlw z%T$Q%Gruq?wiOQ)NhzMg(zhRh&;H(6l#QxRkgk8m>k3Y%^0K$mBxm7Rc)KU@1bVWG zxP5KwSOR4_x&1Z%Hm8C?nERQ?w=Apcpu+Su)1|LKT{y)Zp_G+%rDYULf_7f7nM`en`Lzl_p`GX6F1lY@@ZYt=~Pzzbrv*Qbk|DT{oMl{X2sRu?5r*z8AW*#LT# z>eXYNHJel?PW366VYNIM_%S3Eo4>cVr#sM}Qnf7Oj2niocFrJbIs!bFq@#t)tHYM$ z9e78b4tivJ{$wu>izO-9{G+e$_$?Zksc&0N{rcCFPiq_>Z*(Ezy_`i&Z!Kc%0BU5& zwDddMfY3L=SFBS-*zEv*^NI_qc=!{RO*-2$^cIs^M21*PAKL~bRtPmRBwPAtTVIxw z${PD)KxS9>;}6fxV%)K;)Ol60Lgaw|=Uh=zwczhTJsrZ?QmE3IMp`+JeiQ3?QHk8F zGT8Q#w-L{`w2p6E`6NU!rrI@|c4i>q`{N?|D&}dr=CzaPn~HZEE`{LZ9>dHTlwLkH z06|;|OMN=*u96h%{B-$(&~7RIwSFtaJTA?~8ru6~6Z-#2%FMRI;CwIR*`{F{Y#O-P zIU7Q6*2wMF6d(hs>;$plr=deeQrU^3Q-UC5D3zTsHv2eK8r!i{JL~Cq!}eO$jyzLf z3ce*ot{Qf>y`WeUKM(u@DV=v)6*uF$LCUDYFV1unMeEIyI;T@79JMQpAa}K;PptWF z)9T3jV>QLLC7`|P0wfU7+lJ6fK^&wu(g<4y@rGV==ZlXM3MavlVpWdkO693>zApt-UjA!$|H+D;rb`zSZ<4Mf8jOmvq^w>Bz3 zp4fxKx~B!|!%B@-@~cZWt4iw+Zc)5Xn@)nOu}0JUWlIvh*>1<8k+l(cwFB3POD1{) z-gk9NLanOR?^~u>g-w__G@@9WGi?oion>fu6~aACkF^;iE2D7EodQ{#H@&eQd0`ue z$sbAliH%|{RF*WMVnwmvMFoH7o0|z^w1w~M;*Fax@F+)gvZ{RuSw_tQi#`1dogoJp zBV2FN2la_yEqU>#vuE%zq60^8J?AbPh=YIur=`3vfLe3I-C3UaLo6l-UYd2*{ zDzfWp0B8t1P7i@8*NL*Eb=C89u49-TR=~EDfSOZ=eO0#{Q9wU_dhXG`@eID(Gfyjs zF46@C>FTBdqpzAPLVhYA9l|$JO)x<}l&=^gKCvPc7AW~|9l)*@f+@gSeQb{KJ&vVr zk}L@l2}wfmF4q+b2<~t!r(-x-9_!`6PFpZD|<&xpM56F*Pz3R!Ksdt+%mKZSX)6 zIhSEvn%oN&@sR5Z0(I}pqNo#O)0l^qGCW>5?`0U`yOT3oPgaf1S<%gFxE0Q0fyN@= z7JjGw+T}DjSTZc~mNgs0*#$844kt@qHC+l^68&K*`L+!m^btyf*tP=N zZ0-cmuHGhZ*Plt_TjlOUVllF9VUTNn9C+LftwNk4Sw?rb?=Pi^^suQW?s%Tmd!RDrb zcvhjF`i zk*N)}u`r}vlTm%HznNV`j2Q5mjQEW|vxuiqn3yaKU|y3`!eV|mtAQy8y(XuL#(!f+ z5$x?Uo)9VHoBZFG;olWwr4y(lfPrI$(b(g6ovnG+wLnfiwt&}^s2LsE%kV$>*jNjd z&m&+(Ltlsb#Ora>gJ4BNR8M*1$LyF=5mw2%=j7g;nFta{2ne=s>`FxZl9 zZj965`;$HS24TM-!S|;XHdI)dr=R7OU$&lli@Y36OqX@@Pun*DnnM2iR9ph6Mn_HY zj}lX>HL)F&f;q-HnJm}2E6FeD?yv#(WXq(xLu8)AGl${Qc<9&%Mc6{sWyn;Lr6yiK z^&rlKwrzt7TIVxoS1(glHXw#Y_(2C`$A0F)+^m=>qj<1OO6800B?F_ zL$u`EyPKWQZN_MotZy3K`i189YSP}Uw|@9ZkFrLGcCh&mY1>^zXv}$GXv{;PiJJ%Q z@d~%(IJvIKh8^$BstzPb2_#O6a3%fO@o@Cdk z-X3d0}c%LvoeDnr-TV&l-iE~RD7*`y`9bUFUHBR@6AU$)%y1AP&8gJYEHoK(% zI@4mbyRgq-O7aSj!mqS&C1qtWLb31;g&PZ=?xcbx93U$34uXe zvrW}Z@yeofgIkB2KE^3slkk$n@oFWjohwVaxFJnkxsOG*Kl6KU*LEhaMze@&npvok zjW!X71e-gi4twQS;bO5(9EWIPO}t18Nkv3#Y!qDP!I%1tTE$ z&63_l*R*dO#=LQlCqhTo1zwqDw%Y@~iS)+06CTdFee7~AQ_5{jB{$gXO%$)o-D}Ok zjL9o2rj>FrFGeFXQQN6|PTrqS(lEZz#e!Fc7Q!r5ESSDxBOMndQlZQ!Q8O~PF)lYa zbWRj}cE7fZlL)2B0b|nA1J&HyCRbfl;F|1tI)pkRzUl;=a>yMa;c>_ z-l%A6bL%Re+Yp=^a}uHqxL{0L`j*e_vJL1NM0XgLG$_hI!);+ec#rGSIfS=ahSAZi zERJN+!zKkCSOinjZRz7rl*FiC5;_T4p8>P}KWpj!h~qga#)$$-hjOB@w*$#18KhI> z<2NBa-6?0~BS3lMVbuH@iVgpr)V;?)LkhU4Jk^Z)ARr`-&l8$oOR?@>RdwHMSY`^5 z@Iz5D+peOT9iNFy)-5QvVbhh@OpKxV41p} zp=x!U)b{TESVcAZ)XPp8wktX1)bf8<-R-gsWU&j5Bw4IbRE4E=W2VwE*TGWjKTs4! zCo87D9MUO^El^CVNx0=tba&aarbu2ARgQnQ?&w{5cTB6C1re+w8kmvG7^wuQapVK@s&MVyTOP}9iGX_LE@9B!ScU~R z`>oF)wSzpZx-HDV06kNi_2$0}QcA=7I51(16Hm8ySq4gklJA=KF_J-ABxP{#hIeh; zQMLa{Lkhl3~w!f0n8=L8=q&!hD-QW0_-Yh?K>UkDx<&@8s-_dWYhCT&FW}8;qORO02{&suh zSy&I)pZEC!$Y-v)3Fjvv z!q8K@Sru!yhN>~UYz>enr9`nh?Vk+O>VNryb^6rn0?hjmXp1)zhTWp<(3;p;|B z?~IW`)#O+cO`h?n;@?%fM3@#A;Sy;T6q@3xMpiFYJyAs?Rs>Hx%=mh4tNV|iJAAV} zh4)Nwm&BSivFk%m6m3jo2kYVqAoqEW)Db75)W=~T)$DDIxhghjs3?!G3DJ6l(pX7s z+TIolzmC9c3P|PB=kVGYv9+Bo5}17YU}Z!H4)!$KU3M{CY zn5dAw2vW+W&t7zEg1Zzhu#d)1wS<$FfDC8zPp-4ItWjO7QY0tiuR>J8_v3hl^2=sz zT}vRyw~Nn)N@qW_ zbUUJq`M<>~P*4*O+98Awi!A2H^H=)8CqW6VQ;_dDr`MZnv8*zLWctl5EAE5nVHbrl zwVwHY@M<0}+Kg5;+zY0H0GTQln!?41O>D2gW`F89Q0ai$$YwunA&l-o#1~7=VclBrhiE7P&fl>sDCbHv#+{FaQN|TL&l%kH5?H2doouW0|`Bl+RI%Cp*%u>({Xk^i=yU zO*C|2l4q`L+CV*Lp&8S^wM~itNFc1=>JrXg zhkGgXytgi`MQ+gu4$i#iN>OHSD?>shr^>(VfoTUTfx88D&TyA)n~1KB7wS}0Qu1M%opV{Sv%!2C z!2R$D;Yrupq*?gp2d`*~7Z=WzL9ax86PKdXNIFAaHgUZ%CnKhb4`>EuW{4~Oj8o#@ zj0Wk1GKN54_Pi27QAW2wFM2@=kb)|hNXrSBSxe=f$QW+W)42isL7gW1d}J9t4Ch#v z2ujN`z&eaPcR=6_FB{mpNaLkSeoU|F#hx|co(~wpl&W;u2FOHpZeXyVOBgx0y{q&g zZDL(yYk&imLZPFBZU=}q;N%7#eB_+?^}jnBgul?Fr!`1CAB@q&_TCG9qWE|A`I2j8 zv}cUqHK88;nt6-+L1*IibAn_hvy46-2XkY|JggfAiFsN_2pxw(#dA#^<3MhFG~=g2 zq5K!_&72DjeGzMs<({zf^&S$69xgNt@%d#Q`H8~G0ohM9iZrFE!du+(>-aCHO=hlW z*T?)eBs1^i?R+YO{LFVsjmpMJ0s-G0nO! zE`*9C96#4^AX34P<88%z8~boC`#(3LGP=i0yqQhgQl7+bBQ&or3MEP7poo7?~0OTUx4B{JxIv{?b6WQ`9mJWv=R%^CNF_|sU;;ds}{SNy+^!k^5OxN$Y6endwc5+*cX z?RuTey?Sl-6w2&Wc5D>^*5hKczvX#)4J=sF7`ddCQ=OAsYdy6(ZL*y2o#7BGQa#S1fDmw z^btxgv8E&j+9(Ip;N`?lEn1)Sbff0eatsQJc2@Hoz4Wia;c2XKZh0?JCi@&-`xyow zK_pGVOqt-05wFVRBQXwwBAnkB=?*eSYf4XQbLCHT#hUoLebAf8>aKJ&#+(0(?yXD( zKQ-&TbJe>y;YZ&e%onIe0fZXHms{biEbvozJ7z`$iaz|@cn;y_Sk9W|CuvGL5Cp7} zf9m@8HR)lUbq5TbNaP4ER6Q~?DsP%#&G&6tap+6U+%N(_sRmGp^w+E@{oqbgW(RGK zg#k5|&6`4|UU4N2SjX}5lH!tZASFxI0* zz3Ebcr+=2>oonuG90~Gqnn${d zHpHTdt>}zEUoaTxur4Qbf#?wN_v#)?I)<>#xBvBBr~fo86iNjR0y7mlZa`fofk{fh zqU3mHql%R)4ITnijKcP zZX2*udaDFOS!NP_tWs^>E7!uhf~fVqnl8Ya80pS+F#`BPViYiKJZNjdFWA@Af*E+n zr+0zUe!{UtjK{k$f56`McYPe&zEn%qM&^FD*4=lx4bH(URx`w4}@U{JNxFm4d{v)b9?Lq8gj^KV@1mYyW4HR1S-c=VMd`;aK!|Lb*ZpT74{ z#^q%wS!Z9C$Am{Vm#I->#-9`K-ZE-|NlK0=qK}7~o&AF4EbE0X?V;XhNXmt~>Hq>u z3NASSaA~3FEyOu~UM6#|jVg{i2ex0d4CAWih<=zV>G2959TTpXk3S473zCk7&uBXL zo$SRoV2Ivl(YoI8ec8soHpRfj5cV1>ODC!mGy^@*CPCVBugDlUPfgdSEmqkc9E)bO zs_>Ndub=y_O@bunC<0S=nI0%@>;IJ7!J?3xQ^;{^r0po0jiivR*E`~rrk}^qXBJX0Ys_a+=p*cvfb^u71 zkuXQ35cON`kwg%n?ZUxa`k@ck5s6=gB%mAoVVXzD%zOK-^16rGd zaIUywFNmMUZEuRJ<w2t8H!IWLYxj1xYVWu&~fotPK5-!jP$ahG(F6bqX$4(@6pR zxJ^zgiaJsa{7rARlbIvI2?SJM7PNH3*ixjYW!V$esB4sqY79tUMt0Vucjo(YNE~el z9(EB)DdubFg@J9+JM4X~bZ#1J)uWTBO2RpA?reD2%K-->C9#uRpv(TF) z+?;@xLBJ?FJ>n^rJv}`g?O=Q%Vyvb?DJC%DHb7GUciL4Ac;V8EW>X+fXV&gM$7Jffvf_%m=;%3DM9|}H`#Xkx2p0riQ6D(pWkfhuGK7fv!L&_eXiP0y*&$~^ zNPMMbwT_bt>u^rY7H=3j)711fvj#BBJTyuxKzN#G$+$eUUFdX-)2b99dZ(2>!KVb3 zz|n8b$f1}bh`AztQEjp}pE#s)&YPNZu^{o(|N8}PjRT1^cU$1Cmr{nCHtAQV2`@D7X&AyRn zSO&i}k~^)oa~clui&~yq9a%yxS`(}dGqfhP^I91+`pmVlk5JKox{D`3U6e)A1PPw@ zS*mRb(|HtBox5dOOH^=3EOZ^5Lmsht16Jr>^so)mZKTh^`f56<5vaO(n*N4y)z#(y zS<2#F%c2bHIhVkEpV@zt-W>+R?_UrX(e2CnfMc%}V|NW&5RqPhMNxREITv0uN1o|wdWpzAUK9;9 zI+hjFnCWsIKMIzl|3H@I@1p)n5EOaHi&e2VZ>#!F>g9d)2FiXvd03fcQ86q0E_!D8b`zdtTQZv15l>QykF}#uMy=`!*!ItBSPL;Hk_m zT(FUZRr@Gvwe7Jy*4K1p;b>uOatgE`7^}^wJFkd|`px-e{Ev(w0I=X+e46^AA>P*A z9OYw2rmIKJF!a1c2i9F#g2N9u)z(QHva7~C$MpY0Nj7y;rv1|;us6Og=OEzmC-RW* zUuZdQ54~S_O~vv77LUE2)Le%}50Jb){N0jZWU+ca@R8^i!5U7AY|Rb6it8XUz*I>Y z{$B~vpd$qInU`b0dVx@h02fsNn6;lv z#VPMS1m2o!4p@}xY~qYm{?@D%sEk9&(5{LjEyLsecZ0w17}RIni8{ zbz_C0TvQh-YgY71;niX9hxX)i6)NL|FsZyIN*6rf)XST?6k5-2XT~hYlo`al0B@qf zDuPN4U;!D(jJ+ci2e9{^Vi%Myv~wJuY93KY;6D^DJAKl-DSA)6Th|*PNwW zV}}z|O=0R$vw7pRS#}>Q*7N>)h?5;G^+aS@^w!Emn{nlZ<>|}HiE*y;LAKG=TF-Wu zzpnudaWpZecE#(90Qf>c?Os$}(0dw8m+)szM>C851EWj{eHINOGU<;EjGij7v?J2S zh0vS`dBp%BR(u6TpDR4E^|gnEuSk%Vw+_Jyx5!~UJ;cc3Rgz%=ASIB7flj8m%>#qN znh8^%Y{5AUE6mO0Io1_kcoJsdCD+8Stgb_qCPE4t^W$XhH-I609)I%HDwc!?JNJ(| zV>6DxD7r?ed!>Z9_*V&p8BDhmP#zpH_qK8v#t#}0@`Z!Jvy%=(+DlWfYYPIgP+cqB z%KzJ!h~_Xra0zFYFJ$PkappzdiOt%<7rTeNW-xRuoK}$fK1+^&Dju@p%+6atKDl5y z{o--mpmb|Cl6Tdh!cbd zh};ZfOQv{oq3u7Y=-CGO6PB`_peV^4{uCD^AN&46`s3ZoLQXGz^y?OqxsrhMzw-sA zsx}00d%6QsSwC7D*rQt(XRLf-A^kAd4CZ74SPG}YVmVes(&bQpGZCbkCIFvu6#=sL zCoqFPJkbFV{&f1rR?gRdtpK~SXR#()cC(3`z=ew$zfVq9nJWsozmDJsbm|ZGtaTO? zf4|pkHp-=YDXqQt!z{F70mzkqe>V$Rv$MVGNFXW7b}Kx1qH5N0Z2>s_f1S#!{P!<3 zmkS7X0o1xYu1BK^H*4_%VMM1_?Y~w~uJ7UW3r#t{z<}H5=G?a{?#t+65r`prC(-f$FU56TIl&x#W{1XX52~q{C;1)RA%?76XY81lw zpX}nU+B?XWnj@V5HD_>NoE>RvhU$M4#{a@GNLspG$8-u;$%O%&5E?8XC(acwSt z+TooneAk$~@WhaX4~fh@$!*mT(5!#yDbv&ZUqkQMaEL% z_n{`6N-2^XjPcY5L+5o&W29{=QICPBg-q%cE5!ccwqj{?$yn72Gyj61I8_K$*Jhj= z__y<-j3rnD{Q8`LW`@V_ybn2M0qaBYIq#>Le**1r7@3rhmYgema-xA#+4xRy0LO3L zT>9@*8!W9h;J*#2v$;T^sgCKYn=PWUFPg4qX6*;6UMlb0)a|~x(%vlTs$VM|pZKv8 zfABtJ#|6Qvj+=bganYo5PSfmwHWfAT`??lhP={3|;fXUKe+dX2QUUbPvlhpf&KfPciUZQSZ0!>so$6Gz({5Pgm`>+c)d7Nr|c7_M3MDgWLr6VmxiJ z8Akved)HVTCEgC{Y11iqj}52ZIHfsn_bone&DU+CWLDn(y4(^-&wgCjO$?%$$y8}4 zCkigroq0^xxoU=!Vk6`4x%OU<%W}E0CH19w{N1JmR%Tjq)HE z3t-M97HO)rdDvd$s*cXuKMv-f?K4r)v}$($+K<`i{&4Ms+1Er*6!uY?}zsugJ1KnT%{aiBm{^us)RZ_E0V(VcLsnN0CdQ;%nnpqSa8KKJbC9^Dw++WwBxF% zpuoCO0%cvJ7ox3)8W2$lkRA+*zN?vix5yGDI6WQo+%3-_V+tr6>dYKV%A&>wzYGAffU9Y4a9CPF zcetkbk2unBO|3*uQ`^^FdTcWgB^(b=h+>O?L)I_#_CMR=Tn?& zSi^;)=B(_s!wmf}QcMX5P1Amx@($NpH85NrQOts&)5WC7Q#+DoxZ=MDpcuy~5Tlh& z@;4cCvach{#BX3Z-m)0OX|WK?rl7eI>~_So3kL@e%RB`%rMOUW z7CejP*vfJ&(NbY`p|95hCvUIm5FQ>oQxt3`8i@0=E&5CAJ$M3R25AwdosGC&9!&)v z9nnv)cB}@LepD^-78A?hQemgQeX!_6o(E z1KL%eY-#x(+v#3MSjC1AFv(#!N%nq=wwerQu79tt0Wu!Z}*T0UM;0Up0Uh5wtyJ(2UN@P z_k)G1F3MDh&8b)l)yv5v?nJlkvoD#(IP!Suy|iHs<9>>Uxg%A#?R*q?)n^r{r;gdH zfv5=?N|u!Dg5a$-0n)33;oe@dn(DSUoaq=5TIH0D^29l>4#HewriZ=^2gQM|Fax3~ zf(u3_+tSoiF9j?~9xM`cBAbrjx2vrEdrxEJgmDpoU!7F-+M*3K67TRSq&)BYCP` z!ctHLP%6Sg7z1G|%FF9!?yl%V z1^nNAadwaTe67r*H$6^&tUd?QdK`wu(VPz>^s1P^;PzDoVhisJqFlIQH*E9XMQP+3 zeFyxJB<54vWj4-Jfmcba_r0QI9~WXFYNS5vRIRU zRv!HFw$KltfB&h?Hig^&u9z#)CDUC-#OnMZUrzp0U^!q<S$yZZ?J-yUPfpGX%&{3&H-6$yF8x zRz{A-(SbQ4I7&CP98?DvX8c*ffz+tFHD*^27y+RwY)fO@3SV*t@UJo+ShQbJl-y0z z6%GUCYDj#BsvbiLM8V#qsa@@rj%jsRfnYydCb%j)?mGygS9g1y)L=0MN+snBCYtMU zV35Idha+dPz+jy-eN~KRYfHhzC|uzTh^_-Z8~QO=P#qY8u`pM4&iOP!$|wE*4! z-+c=q;*VWI)%Atd(bg{;Th54|iJPT|x) z|7VEw@EP5gm|UyJBo51jnbIwEp}5oet?*_r1Yw9HD<7oN3-8UPW_3N4wVt!A$JS4v#h$*zn&he7{$etN2Xh_B_`6tuv7*h4R z>|Ct>9n$JW(;gW%fNG^ji@9PF#6x*NWn06z&YJy8hQb?xX@p|II94|&U6}T5KQIW0 zmZ(&8rxvxX8Xi!1<#=?jANyhATj8&+HRzuQ7L-jd(c0WOQZH%!yiufO24;P+$OEfV zw5<7KIIkshw+h1w`zOgOBH0>g8uIlmU9DqXgJ;iN_{;BT{B70~H~5o>K?L+><=waa z?Y+u1{L-sq8>KzOR$qrG1{MAk_oK}qlL?HWPkp=1spk2c#>tc7NX!bPJRNe(nJiE( zn+YYt&jIhYg*Z3^(SO1SVF*bo0N;MPili=jAWE{EGN0&%V{J^7!F~+9GFMj}T|+)q z*7fC(%puSv5QPPCrzTk%tw8}tu)-gJGlhi0NKB63s_4dQ#<(a6m^u>frCA`Mk_&tu zKrQBn93)2Td3SEI>RQLYM=SZSoEAbA+~X@X5W;50N}}F#{Lic>93RWH|KnR0Ck?aY zzu7iUGt~`3U@sOXZG_o!c&{L5c@3U+ylE7uRy&hQi0+s)Ww&4skj3T^=XeCK3N+iT zN138D49ENv>ckWgN+YegLF)izD}Npo;~X<1sTJulAT0U6gF!3V--J>zqD;2|)o55s zv#Am7o>$weko+Ie{DwMn11n}X&5=L|Ye8`gST)I_hT&aMj{Af%hoL+6<8D!dNe(6) zDAfYrR|do{I)-->Ir+b{R^bqyWmwSytWbna9A3>r>G$2U@f|8RmV+5KwD%gF^B|5r zQeNU!y)fcdGtu8*Km8u%nO$=UW$kG%N`wm?av(OiB0$zxkb8~f^qU;;NLEm^Z;ShG zFPo_jSMGb3r8Z%PymG|2ac+3A*!TY@dNA)T3-e$$x3UJsq?+ zJ*2)A1RSHQs^c!A$^U;LJ75?5x7s2)%fg6|*1^Rg7maaHF6GV}xL4u@Pf!%nM#sE! zoBZE#kEXnd%7|yn1A0XQxH8inVgA6SuLcEmxG|R@iD%ZzbZp^&-=`1j_Eu*9{n;Xy zQ>Q2x5Fs5yK7N$qH2HrU3l8v`GixM3Xf)83z9*l2N!j98a`Ga(+@SxwZJ>2HMe<&J z@{h`+ul3`-v%zoW1%5v)tHFp^XLw%IrEEK$e-A1L?xY4!jT z!7>e*M3B4*@r;TmdBD>M*!?6U#JcTLPfT^G=o&a1zBVLXnX?+)#EG5~f(L>i3sa?( zu`VwV?>5VGewyOguVN?ZqdIQ;ux=w)`)O1tM@XBUwx+o_)0A1*bRG;qipw11DVE|a z{wexjVUU?k>0tI%X5^#&*fc#y*L5_1O@pZfV8HL5&u=+b`9UABL&?KR1E?Iq=WRd_ z1=UObZe7{VABsTDBLj`HvY*D(aw&^eNp+|`9g;;q+C*g%Pak+>ie<&bn;QFf2L3vS za_?%uQf$C@P2cBL`ORD=vU(dKuz1NY(aMNgI~14!zyxqy%qd#dqDbF@vunC=0V0c^ zH|I6Dk!VNe`6RFYb7HV=Zxs+PJ7+TWrBSl8rQ{ENBbzo>5y`}jsWPKDu2pCKuHkHI zXsnK!l>L`V&mqxo#&PSU;d30@9#YL>$=O!&t5xwndN{tvKUjqH`TFze(T6gHK{y*5 zVwf>wgZ^Lnd}GbN%kW_TK`*pz&5jm94QD_F3P1?|3R`R4(AZo$LIl(L(jyH$4C`7D zg!lTgXK*Se;%v;lQ!P!)4bx?Dgb@k3dGxhir$WV9))%1LW9Qo56+)>&9$q72Va1wl z->KS{Q@ z?vPjM+A)K^o`=X#TwO{l)>ukIXirWMcnt%T+Z=?Fn+0BhUweyXhZq9)rU@6~r?<4> z-8PDMV{nWi$~6dvzk>2h!IS@-=WW?2E!l7#lvigIX^TP>13iv4K8I80P!t@s)b?LN zi{neCQ$$eQTgkkLE5q^POo!wYnmo*T0|UPRr(&y|dr1t+id;ewlCD$j&qjwRr4F(D zs6}x9Z=Om~Ez~X3!0r1n=h%d-Ibf=@M#;u3`6D#UsZ|h%!vx5u9rXpute5Uok>|jfP^Jh$#2jz;ChKRufBbXmFlh756kZ)2aVum zr(e5EdisBj0Bm313&PLBT$n>KHDu2!uN1~3T}7}4f9d;i$v=8+&2MTF)rN`X{A&5^rAJl) zGluUK;17g{_^-*sF8iqtWQnsf%qCMI%f_~1siL11Z`NPx(HJU6OGXM0}PelxX8dDs4z8IqaeZ$ZJ2vvkr zTh~%!@YIhQ1O3PW3&W9YRkuv*YD1V1tAx+`lx>-Idv47oDxR>%4ieXQ>yDY|+=o-w zF0wxYv(K5geW8T2pgt!fJ+vC8>fe|9c4(|M!^F?L4a$QxvwI;WD&`?muM8by9DAc z>NBNm)e5T5S3t;QS%$`C?`c?o6~q5JV$)O4!PeodGD~2=_+0zyZA#01&|l18CS6m- zmEMDTuE(qV?M zYRv7|H^;F~=WqH_Mdq}*cf)rZ?{IoSaKqeo=Us}@ZM?$?nUtht zA$}gCU;obLD2PcDD;YUtOP@F*1)N?G^eY86H>Hpzek8Uv*y@LlOOZXB2)r1=e8ULL z+~;6)LQFOH>Q##fvzaU__|ZP(hmoyWHi)a1EuJH{ul+&Nieh^*cNN)>E)*awWm1g( zKY9i2n{X_cMm@_cY*MZTJRLGw?jl3t68Br#`hGNurutxBMYJ>m7Sf+pzBBBn%E4FT z#i>kTGiIR)GO8E&-nWJCyEe|xSfLoc^K3waa6k6Iv2+TA4)UF+iN0*=H&O-PHnyt! z`!GrFhg8XA8$E)=nq*n7ajk#zy%vMt86E}+0yFs%E(A0!E#` z3Y*L@{5&j0yqI)7A;o#zx4q0(G!7TEF^#GgDG27l*uI36UnS#JWKec4c6x6`DZHN+ zO^{}FYlCkoGQ)_{r(JDQ+(`!RtAl6ro<&qUKd3LqDBsVnl%v_|yonI+#Z%CKSl>%c z3-H599-;^|V^3n=fxr(Cg7@h%fAXRJZ)SO6hoG@IV892uzsfo1GFe(K^PE)xPqa4_ zBYOlYxdU{pHHu##AjJi$s_aGTjEVKJsU_wi%khSUavluE2RT3GAKqBXWe&&~CV z;WjTZo5^3K>h)MftwnYJ9>;cE7kURTCpWl8ua1-_mBi@8p)w2Xyn{_uD+P!Gq|cFtT;q0|Y=LIE#EBS5yerJJ(IS%>e*Q4q?nsBiQ>u zn>y#Zsh-h_v7TUolizxpG4zH$#8|u`W>!q$`zG&IB~jRbNsZ#+b}e3+&uw2Gik?({ zl#%48^8N?0aw*Lfc*nC-jVJe=kIpFk3(@uwniy5lfqZvCmBooKq3m$O4ki3q%U0UW z7nf=^nm~eL%)aVR0Zg;SgJ^x{_{4`)=siQdEE78usvJjLGaf=hd{O0fp+hw!l2ufb zuyPz5E)}hgO&4WJ1kcW>O9@_Q)aY%EkE6&U?vy#rTFnKnWAoTID8p*TIS&_DZH^4v z5W+C^8LOgVpbMds49RtkGcg#Lu~Z;|pGHb?m6~a{0ffp*n!l9nXBez3gxQ3+E7_K) zsoRm_N;D_;S$evgexRkFo-GDi_3n^sXzy)ATbhqWWG5Kw2kh{&V{{0?dW zCj6A_(0+g3mNG!Nn3{uUPT@xkv@mo`$4O#ib~>Q*p=j+$>e$8w90@3BbvQ&CC1|Z; z`f%XF+YiF=ItJL|MCG_qkA29a%^P@S-=qk9z^y?gmQ_jUB zzY|3j+RB%-Z7t2XqwAPV-R$%>N+Mii?!exceGH%VzWHVlgg?kd?1{c|+G}4*EUHKz zbu2oFg3xsLVg+XotK2a~9#=1t+h#YVnST*?%+ESG< zPmVxDs=DI~`B6)sS^!3XNz9k93S0iQ4rvgRoIkY*vKS1Bc@Y3|k1Z)^@Dip7tX)_`}wI0jq9P`17XHxIVvHe!5t zEKlP=eD4{pUB7cp460eW<7ZLZd@cAqi-*Zg%$BZNGe+}FGRF0yt7PwS9icB&;C_(Y zeAp_mIkm4Q8T*#gPLI(*j4^iLwMcl0^0MR7TslG!*VW~1g4*d>I+;xp7DI?R_)$_`(& z{^obN1}?T@zvo0I=Uq`0!U=t^;;(y2<&_Y+yA=_V){OkdHq9miAFUg%yD?Gb)2UB! z&8koA7htzxSF4W{U&wq$%`_aB5E}pBaU)%r*2|ok_T#)qkj_(ItUX5J15F{YPhjMX zs@$V#UAAoEMoN$h6UPv~JvuQ(7}JC3jymVnAS!3ZZ2T2b2BWtAwp_xTA(d2Ee1_Ot z@IVMZ0r#W|>{Ot5>*qpsI_S`>KnD~&j4liek|MMd=B&=$x;Ge^X9pMoaM$l}o(*3V zv@_T>IQ-JG%B6c7Wy>A?85t_{jk0CxZ_8!7)yg4uxzCu}rf$syZg)}{H$FcvQrDtmtY4!wDTfIgKc#?8u5kp{_;`wRej>a%QAx0NBuF<#ZZR1)y&gd*0 z_bjIaxMYz^CC7M0plfU>W}4SPprmNxslY3YsKFVZdF!~vZr>iZO5UNmc1CpQ%{d(l z)_k53GvO4M52#|ZDg9dCq!ySoBUE8g@y1_Pa8Ez(fPW~sK2Vp+`{FmE7|hUp$LlhB(xo+=Se)Q>=3Bv1M5dxp>} zmw+v)!<@^9+EB&`6CDJ@kp+*UbUIOJVJDKlZB*;UxobqnduAa4TXx*T!PtdJ^Z`#H zdo18BnYniNhd~sll`)cRnvEE4@ec7*zaUZuIX_3rYplWN(O(J6nl2H)8(cqo^I>DZmgJm zs)!VucQ;u{mHIRvV3DhhgTGLC&{3l0F8!h(Y%xrS-R5*0!?!=ZrVfiLaYsyj= zz6DOGo%7-PT^v_aRpK^kq_U2Ge?~=9cy(C^?*2N|g8uAI`|b`f6W!s8jMPrt1qP`g zFbA1Mp*<0%Mn&%7>86_dukucmV(TG^XOK6#K%}RT>5Wl^rvs-{i z9>(E8ZgMORc}rUvB!eU7{SDK}T`DzYAvu5Kg^RzQRMuHUp$J(%x8}CcabSd@az4ur$G*FY_C#Fm7Sh5v`&+ zQ0`)EFIh-CBZtc9Fh>?Y2iSVkN;zVc$RmWIaml|=fd8`NUVA|w zKc_Fp#($%#x~iIvFBT~kJ`4L+$VfnPQjR-Sp^-6fx>qT}p(FnBA?}GPJF;~_BP3en z9AH`)NiHDbbE*Y0>t$zBR(;8m*6#cuW@*2Q7aj!Szh#VQc6t;m>^gSA4^x2c#wY_N zQ>HLv0Ol-vCtB^?14u=3jfGl9ScZjE&mC<$mOUhD&HGhab@ZBITQ~$_nk6O;V+aE6 zK#NvC_W=5$9bzLlGwismQ!40|c5zP^l_@ zP0>x?8%rd5+GSq^Xi4!!TCbn2G9&NqSi5k*=IifF(2wei@&ciy-42;JlHiak{tTL#HnhI3V5Vd)7#XYL5L&*@3pK?gx?0k-BW{-pkvuz0s&D3ajvglKvL>pvN zq=5<0<{V|2y;K$rPC;h-6xRJ`g z#ICHa>FW=bhBP)An+eHGVr{Z6T?}bjKMFb2^6xqBQ#1ia)7K_10Lth#&0JYz%+nv` zhM|jrp$h%;JGgcWf(x+@LEGwMP0{TYLs06@)w&eYZH%Rrv8sv3KSyZkONb_zU`pvW z%`Biwo4E9p2-FE)06u$e|NXa5F1)XW-|9=(6O1Vt38` z_wh37Psx|8;UrS`e5xXfX6^paiJ5(SBXmjiC&_D8%Oayi>TYOiz_`+iBEVqKu>sg7 z*rE*_5y|;~zqR-Yd^!7Nna%j7FrGlJ_tTkkvw^1Vj?7b$i4~m?TV?UW#a{=R4evz+ z7F8jJ*I8_iJo((D zk#VJ=OUevU;L(!ROu*jAQP##nwYuTSTmS>W6eEJz$4r&!5aD|q4$tY=LdBfYfe9FI zasdp@8LfCT$6e6&>-QRCUG*0rKBrlI_887ObFw&dD#p5T9PINJ&gj~63b}A{NTan2 z*qIUo654>D?U4ip4P18`Vokc68d0}!6CEtDe1}JD@1Cx~jcTrMi04TY^rOjJZBh?OO~`uHi46$4C@q-LBFf~Sca9jHRUzj6@8Ng zEOB&Rvqaf~?|mQ=cp@J)j2{m1N?r856;WUeRzp;Ibe0#-_}%03-|C1;34hq+9*mj9 z4%y;Dm!)~riC>Kuyw-P7#dcX3xK9ubW?5xlE4RBD&v|gX*~LMG>GdB~Z&xBITm&T` z{Wt(~yLnyvoaaut{^am?T3ertf|u-u?k2IB_&ujXBS6_pkt}O5zfGv2QSnP+gLgZ9 zjhWb-!4EfGno{x(EwSOdPd1eCr@4NG8d3RO!$iH=D2Gh=fi>p$vW7vHp{Z3PEXGm& zU^tgvO4q*jj6xDu=fwq0D2HqpCW(33~oyy{2vR1n{n}<_9X!6XY;ps^RmiXYJ@lD6mlfo=GZUv8EFvQY&YF44kigQPqf~i zab>WZeXhaz_N;^CKb9S+B-V*y#;6TPG5fE!t}59p0Vk80mXypH56{d$=pESOGseg4 zPrZw34w!lEh=}=8)D4G>TnODr(EabiF)}BzB!@J9aXjnZLWd#BU%euD&Dp-82%}n)UZ@Va-1BAeRqX>oehSMgGGz|LaV$3$O7dbz)rzXLO)e z1yV8_A~vptwJxoMa!QhP)LpNzfxTprUX~y3a)HTSkJXqX48g7zxuLye3>y8ATbSFW zW^etuK}0E5qjGPQi4+zN-s92o2cvdxl!+7v4&UQ=KGYi(R)JByH_Ai`{03j~XnBKC zzcwvX7T;48wS!f*6DP5`5ZuLz5x(&%^G0MZilz^LLY!lGz3 z`WsFFqmHi#i=x=*pSb}T0AK^rH|6Saeksd$9XGDrFy!Qp43Jb=xoV?NxQ5fSsn7Ve zg!LTi|FmdG8@W+}fR)=0Kji83H|F%S)hlLsX#pg~Fd-7uXyLB;oFR=Kvbut%Mw zD*uahzVIavXsKDOWq4vho)rowct8o@rscT~E6^Zd;%2eF&L(c%7!d3Gq&I6KRXjPH z!+Z20yv~-cLs<@&{=k62!Y`Bj0QFcU_p4xVc11Fyqryga>=oA=^~PV_GYm>rXS`gf z3KE)PSA6=0tuaE%jwJZn?IsMva5p$nD~T!k%UG=PmtW=#C`w8=*D@RX8>VKsPon%& z^?}bPv(o(6qhMY3=cGI#z6BM*ab&j>NMydmz-n9XiJ{36ewPOEXs4g}v@m50FWBX~ zuSmHj3al<%WhI@|-hS&281$1a%6PqKxs+fkhBto#x3G>X6gEeOrL&AVr^OnAG_`3f zj4w*E1{fc+h+r48p3&~v#Zc1}pK7}NJ}6nS5+z-Nq5HZ?J0vVc^r0hi(MJWRj4n$> zHpC)d{VhTjK__Z%c_Z-@`hkL1`E^?9_5PGDJo+6EGsY5hhQn~r(g%oI=!LJ{h|#mO zL>5=0f@iczV*(KS9Q+OeJSeMEwcwZ109m8w-OlE!5*sL+ouw22Z zgUD_bZCnuJ^Z_(wz%)}K7x|NcsaaM7_X`wMQs5DKKMn)S>?1hcyJ1;H@nDuETKtF1 zt3tu?UyKL8i>^LRt(UROq^3$y?}dtdm()dBwFFgiE5OMA5)c-piy7*;yZyVO)CH_n zxi{RqG=<|Tl9iMZ`5>NDLV^g(YT?t74RA@#F4;DEap1m2wn1HWl9X-oE^@-qf%an2 zWaSR)WQBt(-tu&=r7r3e>2WzCL>a8*A?bBuXYV&zu;mMxw(L9irF^$Y-igm$BuKWC%1F zxl1^}kW1349o+`2QiBFaA!;cS%Az=u{2innSL{$7VG{_o(d&;$;j$O&CJT1ni*=3e zeAfX7bW!}#BslI(cjT@MIfnF-dhcsIb^giHglJUeZU!s$OJXPX90y>|X@4tF-TgaV z4Zn*97{_7-R939{e9x*J5p800BI*KM*x}Kut&g9ipa5L2sgY{PVN~oQSX#oFKnv;Z zcEi}MUc?tDf`jptLvVAsjaz^Lbu_U*t(i~mmubkcqRB;{$Owj%2<5$F8uMmgRq&iQ zz4$)0AD=4Fxf3sT8^*M8`?mU?4#B02*GyF>=}ampffphnDr1U;S}#*3M8MrySjA50 zW||_XRmH&Qsy3tB>J@*EKrr~c8GGhGIDoVS`dJAQqZ6&-PeTMapAU#VQy=umi0+8_ z!$g*xBAIPztJhe_GH!rE#tk{y^cw^vytmWOS_FkUC&Jm_>qqcD78hapzXe7DzWs^d zo~zsjSXiWK-s2B)nD)9F`H{qC`lsW?Ui)NLe<5W**MZ)v7ocGnj)UYO0Z^JoEN*4!S?)<~-3&Djkok zdgCT)Ua>PYSU@HZ+%T_CAP(xPfQDFyGbC4KTG#1w3P~)^D={^_I&H9GV?BXRxK#P^ zm>tzvIO5~^Eq1~gDMbIR)s%;5pGvFI+8b}`xTxmbtoayNXSl5sAVG=y5IUwrybT`F z7&h@tS;A#iNH+Mu1{%JS>zVR=?C^o$b|MipmZ2QQT>Vo z&=1mynrnjiGE(!scS%YN9Kk?|<@inO!DN~16YWhyQ;!f`r(3l}z3#I1P)NmqhgHn- zc6(CzCvZ4*uvY>*>>#~mnOf^)f1QU2+Q@^y%&|KO^M^pbKy!FqNZgXh>h6x5@HkL& zWb;DRj#UE@uF}Yt^LOWbB%-3J@qF&e+7E8jH58^F9Lg`0TY-E6ueFfO$X$hw;|BYp zuI*+Va8eGyT%x6=|Bp8eap6+{QVLBD)jnxKT%iw>}2y$|fgqQOW9>qLo(&5`Bm z9COa8k;YhL96(xh%y3X!dfkd#;AEcTj1>;Tf*BKP8Fe%a= z&S!zMz=iWGj)E1qFIp8Zq7Bn5)RsPRIv10LRpE-izyuml04-i$8AxgRgP5q@FZBp4 zvU=LK?E-XKijQ>bka5a08=3{*Nm1xqmg5eI++hiYlHkctrQs1D_*g6rTUl*LKZsOU z2Olbn_05iuIhio4d1>b#c(&DD*<9`m7759| z?F55$KH`Z#k%6e@*Sul;%6kc+B5M0fDEbqKE?nZp4;sVMG3=VP1$>4RFzyk9Ss*8nG^_)+_h@IF9$dk?{X z=4#{wtX!My?x(4oFbc#FzY27p>~)?S zt&1Ay9d`{f0Gx|Hz8@DiWb%~=Z*1@2Q3E&T=h-F&Owg4f;ZsFSmo2b-)!4P%JC%ex z2v9(IoN&JdN-7Q+j#bjj-I0VWUsN$~1yLfZuhH3u7v!SkvdVe6Ur0hW?M~zioT|bV zBl(|}f1K4jJI?9L+tY_m5fz7Sd{Y%Sj%^be?7xLhl$GYQlB5-Ii2b-jxBlLHfwu-h zZDhW|+qGR0-NYaj$>5k3TK);>iO2j~o20JT&{WTqCQ;0hVhT68glZ7T*$%ZB0RB79 z{DKIRlp|Zr@nQO;}d zpx-zWT)bPdOC#_QUIp)&|>DHn2DtboK}yXIZGk!bEiO*(U>zr zW)nw@HvfKB8ES#WnXnjrUZ3D|YC2Vwe_RsalCOKk-yXBXf*p1Ef+NqRl>kDPMI!V5 z|K>b2?3vcgMYP3o2wcKhGhc&!9R7VYtI{-}q8~J?R*WLsf#@qoPXlXeq7@j)!K)cF zeE=Bm(dvz$KLQmypi*1O`3&za;y;SOMHS;r#*`JQeNRPqh-8U35>=x&G!K9sm#Ho* zV2R>h7b0{3VTO^mj@?acE`766-Mi>`R!e97I>tUMi3y6DQHl^CocvLX$_yj9OMz}n zi^4O5#6FrbMRg;HK=6uOn4zUvfSUpNx=hmL;OnzI)5FPteRXN+T_>YCK@!HY81Iko z4{$v%I(6yCKc}bGj^DXAFp~sMAI1opP}W%+wX*_Kx}WAuik$fMv}exr5D;Ewb{COW z7>7zf5?txf^I3hYr*^+{u4x;ZVHESUNNS?a@El#eg^XI=n5qL7hR^CSj&}G7gF?_$ z;bt;M8$-vN80#+H>b54@;X`&$xjBYoTnLxJcaq) zJTdbc8UZLdIl~1wK_mXq>ddolB>NV6w_xfP^-`H_ z=G||qjcE8u(IC>?`FKLiK+ql;ljN(Q{6Xu2O9Tox8{Y1ws4ZJix| zFI?rD7jSMRfF2Nxz!2~(<4e$aLzYfw!@i4j~+*ZZ3Rchx`BS) zl&FGpol8P31skYc{XV5HX5-p2NTBj`xzg_vlsnT@SskB-il-b0mnWTc2 zRr!)|8-GKEK=s7jcpvN}|8@T_`9->@YitxB{yRb%zMYGYJ`0oraZJidaWGI|&C!*V zRUVSq(gOc?)bNxq?z~h$`MVTDRAdOGjN%~Z8e`0dKa89PZG(nYC>(8|%WUFC_PVgV z7ge^cxcjQPUY2|Q%G?j#(x+tKu=UU^eJ;_czX84NkiS=eD@Foq4KHXoQ}98v6S1h_ zsf8*VnsQ9vQ9xzk(26C8dkT~I7d8|vcflV6+7WLR!##`F7EbBFZ3WZ$pcT7BaE@quPbUS z%z$p?fFi{)S}d9c(<@;YfQhhvni`i0{(TjuoP+ZrtXLciVBb*0<~fGxe^qzRL78-i zQ9Ns1^=#n$FZ}70Sm?w#YZW!CoF-Mz#WUQ$Sqq(~U}wgBLdZ4L3$=dV%IMEYfbMg+ zXZ7^RV58OcFME;QUeM}aB5b6l%;xQ&gUyv`u~ZrayVRIKBWw$X;E**dIZA=3_&EZPu&s;G z9JS_HYA{QJM;!<(j|g^L15)c=D-nk4>7X`j6tA4S<`nj%dOs<1(&=j~!!}YMKI(#R za}q>Lu(@fHX}RJ$iH*`+_PxKT-=^)yCSWxum$Ikr$|hv>VZ;gT|kZBDGssgN=T6w@~##UVvwriUa#(zK?`A{n?_i{JL@`?=? z`dESM#y0fLV9wuh{p=&@-ZotZdIJDj>z2?|0#fMD`5O*=7<&r4w^6db#NU4F`N}=P z*8O^8(F$&m)6(cw2lT>DCLdA{@A`LPl=iakq*e8MuDjj9sWszOuB08_#97U$Q+=Ft z&Km+#06mlGCYNzJ?u|C$J3e{`N%jB88kMt+2W2GZU5Qr${%157j+QL}D?cX`KxGJ+}pPF1k>6-CvpH9_9p zT>qeR(vEUUoUF-)D;I?`*h`D3iJGt5jl!&NJz~upW<20dq@mB40RF)dcz&)N?NlO? zk4@pnO{qc&Ws_UCScobhO!OvB@Dsv!X-+HQ0?Y|$@2M&L#yOT73X0xAVK>I%K(ze~ zs!))d8hk9>_gpwp5sR$noHq+{^}syp==>cS9(cZc(fdR6Z9mb#Y_y(*xNtbQEFH*^ zwg%^5)9__hxp<~!FXq#nR?`cu2=CmgabZ_{7YZWzg2vc&-k{tKH{#KR))m3O$GDj(@jSycKRM4w9j0{*7_1=}yh>Ql)qM!MH?}CmtLOn-o+t~d z-Bgzb0LZt(ZmsgeEsa?mlY8Y9WQ8oys~j^Ma+hA}I^zGLVeAwZ=01^9_sj=TPslY> z%6oQ`Tvj4amsCkL00bTSW-NMvHLpvFXKbH%{f_UU>KS$20o@?QD9d%76{_kv=$ttH z6W-ZL`G&EQO17ZJFeRQp6BE$AMLW9FBP=`g6r&4pV}C@+2%(|okkwT9KC3l73n4!# z_n@3@RnqZA(+l$kArPG&^N$NRIrhd2nkG{X1zRqGt*a)x%2CET`-o1}XRd8EgLH=a znGyygL6G7?Fh4q9AvMFouSBWHG*?Y$RgNs|5Z+VeMC4%)1tLHiUb?}2R2tx^E^x>f zmN3`l4${q5Li(nWBI&O35gU~L~^2a(+k zmJ_G=aQs>XfUZ^q<3k2B;K~^?p)KVW`W~L$M2_r}^c*LGqQ6%bMiqtz7^(|;;VD`SqT3+GOU@~NJ z@jHgfGVHUG3kjqtF}Uv3V`ZFpR%Ld#BQ-7IlE{3;ul%`(^Z*o_S?Nx! z>7@A;B-8dAve5r&T>S;$q0$OCRKWE-+b5OuaX@w7JldE~T z|L~zC2u9Bo9wbAus*9n99yDKQ=@P7b7->H|lI2)=7kbKt6m*zV@ko|t<=seYnhm%1Ro}>P2|~#7`EL;_(u)QI-&y76q<&Hi+f9uaNw1l}=M>e4 zh$fD&;@G4;_3kA~Z&6k#o%#K3p}K8fH=#hGP=t^q?vN4QM->9q(mxX8BVe%Y^1bM)3c)!J=*Db%m7z z%StW9=gqQP#Win^-1A&077eOqqqMFSG^k5L7(9dhp-+rOe1!F$PCGDiEkolJJ&zNC zH?OS9rToSfn`@2crYCkkZL^b!2}=2nX6N)8z?0#*{n8 zrB~iy&KZ>U(_ywlVZCh_<{2|%R}!v5U06Z0mz}bOFaztVNU1kgXb9NWdftUif1#=k zVYB}{yWE;R62#v$iWn-RlkVbqbr7o<^U*GdBR}|MFA@k2DK)!>hSA~v5U`-g|02N6;AeCp!DR@6)mdS(dAY8xvE?{#_Y?q_O1@Jfr5ey-He z2(OK(pl3JrN2ZCx*kf+1R+^|o)Z)1{5_4m{r3TUY`a*tI$jm@+`r|yld{1&jlKsK1 zw4~QH8UD^?u+A=Z>;kE+tZpUvuDW7!{a9S%(^UhQBlMEk&`g|_*tZ?CDL{b)NDI3w z|KX;d&~AI%2Zj?y51J5$)$ys|V<^P%wqg=Se{Z0Uk3$QELBMQGehxXIu)Bh+CJ!Ii z&3Dps7F!op_OLo}UvTKawsnshi;|_MwfwRQPP#-(3b-xDmw}{6yR@W6f6f{9^l(LY zE$$;0uL;dZ%C$Py`pURdAu&BhnfGOsQYR=8BzJ|^yFLPN%NqX92Nb=|o+^wEpJ|AA zu~B7{nCg!<*vi21&z){t{wTv<8wkcj*a<5TDRDACeGZ{+a+PM zIf-lR9rJPe;wC7vv^j#qAJ*WrGGr7nk@3LfH9Ll3iP&+DB=-dcBI$XWqt2-hFia>` zBhNzS0icshciwrg%;#6_V}So!V~pn0;&Qz?qlC1Fq4h$BDqi)sW!dIp;YW{OvrWO9 zG5b@g!;Ne_JkIaU384W4{*9aHQ`g^C8SrJl_K} zwo7r#TdaOykq!ApLp|gTEY~rZ#SRE*X}o-S8XOvMsQk<&Hc7uuzUx%i%qe}Q9~QDz zaq&pGh&@#uP)X@VvF+4IWp?AiOWZiOKlTK^p30bcX%IJbnf<`8qd2oc?bT?@h*KFE z==)!=N6qp6jR7hf0*q=ehe-tZuQ(b;849h87{Ndajq#V0p8~;~3Jtc9<3rY90P$Xz z+&I|}_cDsNYwt7cBZ}ZEYW4m}{M*i?q(H2Nh{J}^Dkofp(7nMtLrs(khxB<$I`>tk z$VU=KbiE9rffv)!#OX=o?G@@ATb&Y0#(1}BUCq!dpAMJuy}kxQe8V(o?94DszVSMg zXp`@iZ?gmM6JtyB6o)9^rRUAl!ewg=&C7(Vm_a}DDd2h{;d^89GD$kUPr~Eab_aVN z=EuY+1QRsW!JmQzBFN2xHPF5S{QlY&cUer!%8Rt&%HZX6+Wlr__e_{KGW{^R#ueir zcvgen`FOkt4JmU0>~S-<<;0wB#L;bsosazX{0bdWw{Vsh-h2n=p8^Ee!dx?WIGf8o zvf^U-Kw}5D3>SG?k4w!57WLNu@dg&+vgZkpwxWOege*OL+52Gp_n*S=4STSqH#ngd zkW7{1s=E8i#WJ%nlKI%dEc2+&kx1%?l3P1}@A8V_&gLZ=7Yg{0j8a@+<|*Pc6>;H5 z&7)#15iwVY5Lp@D3+^PpN#2WGy9Ll#gjaC9P*yJAb^@$%P@Dw_luv|cyosVT#{oM2 zB-y_#e?sSkE)=H%`j+oX>h(9t7FMn^r~LY}y78NHA393RDdmy(RB{po>XL?NeTlVZ zgXn-jS5Kt-H&IyoCsgNxxz>}4OIL#i%eQpZOWynL>Yfv6ixkpENoFY|v{chJp=XWH_Ej=gNJ+$6@!JRhBVlt+0)^90BV)$Sm4 zIXt{Rq%)bZA)&OL2%)M;*idW6oohF_C~g9m5voN(HZ-d2PUwjPdE`z)h(IU=s_Sy` z_w+kmXq{g^$MTv|`Pi9C#Eh;oK&|pNU~v-Pk-5TX4P5#V>YFiery<`CuMil9w}-udN&x#k})75Pr_5odf^P@H6`ciHK#7;7)r7l z@Li`oHj%#1V_8_}FnFi}=(M@)>BK+~fX`^;_=I8Hw8V5R28E z%Peh0-F=9~>dB2dH;MK?L}K!<7qD_IS=%7;>j3E2LZ?39KHkeG)nWY>E^GO&!FzluWf?lq=yy>bcaEchDPz+`wH{mZAnG${<%p77Q1%F+iqbI zo&@k#AVJsEB7v^=%2N5|459jKbxBH;!r?WNIkNm!Kf*VFDslaP76|&Bes_i8u&JV| zJMRNkGry~* zvY8)9?vn%bIj-cX_u_>SwL8E48dj4D z{>#7{eqlTLd<(DJ!q#FrFc-&F20w4cMBNEq30F2!qC+?0enmwYCJ-srhj#iY#;Dde>_+|xk`I#Wd2j2ctg2{#BDPuH;j7 z*V!0PiqCwDkvgdRO0ecf#>o&b%IM482veaf=ga=)t?`| z?VG{FkCNBly3dad4|eTTLFwez@I;L$9A%Yl;a7EkhsF9Awa;5l2aTT7pv6yX{sv>l z06y^OgKTXT_!tyFXL;-2);#dP8$encGRDwrRf0?qvTBzYpSc0o?)4;+|cHhC(n)R z-ka@D)T~87@rtFfHq`Nj9W@w#vhx2{D@6!sJoIi3fFi}{lUh}@(P+{ExoqAI(F~u4 zCB>JJB+yA(tp$wpsqM$_!3(u)1^&D#Hw7Ob2pI-im)r5}4559$tH=9Q6ak!J&?&sz z@Ea~ULuvQd^>-^kVz$%1e?C>W1#xcTeSQc_jV&H!%_{%rY=t&Ls(L9(P-sT{+f`rj zXT0%rIVLgC*?L0D*h@@or*B0>B1BYHH9Ene*Oi!d8`m`-Sw0dtUx%!1hm~cl3PoU` zw*5&x3J5_O>-ZoVPjJotGs3B^Kgd>PhDo8#O=VC8siM2P@)EiQV_;B9B}|iZ)xSh@tT=Rabw9D!{2y0I5_6R@rooB`ESe9$alcYz zG(h5c4yh-k{F^X!v4zp=)8tZ%m;6y~Y7qk=61lmr6I|3c9ezId!4^=`x2r~&pg za$O)y+Bf$`sKSXNV)5fghs2ho)Q<>;lm?$*6T}DRw@USsdq?o`wU~m5atB@t{%%X< zM9E}Z-*#FA&IU9Lt644-y2P{%RyCZ%ym@qt!m}W~8Vji%N)^OYMY9Hlc#8L#(MILmkPS|YxB#dM!WF`(Kaj0Fk2WxoeFIU;HS^QSGM&F6a37KlAgK_^F|izq z3)zPeCT1(H0!S25Sw~qGi=QNtY?RU(7QFvM6z3{;CYBeqZMKsax@x0tz6`|^d5f$)Y0J?A?nezBskq%r`7SWbwnYP(5MT^%4Ms$BBr(~FKu8qJ zCs+%wTnua2-#yg$jhb{G85;N0&|dMFijUDataOW-*i9!;X@Nq4Jee3K_r61A=(`P1 zZ!9Om$xOVJgs5R~Zi{G+hu}$g2~R$g2q*-{RjPwbw>mnNHYFp<4reX;sQVg=?KHT+ z5Ta=Hf`HO>8I3g+r5OyMSA>C%vdr2AxQ`@dmMGbs`^Ypmi<;wcu9DNedeHYLl2O4F zXn?XNngppymZXhUHJx5dgV%t6T;ui5`6}(bmW9=JEjP_wqx?^6x_;FR zfIQlg%X*J05VzKx!V&J!DQ4~T0AhmvuU7^rh_Tf(#;gjQf|L^)Vv^-~?Fq0npY936 zaF?D6Yl?zFq%+DUDi29#^6xh5fl&b-0*cpvs!E07{vtB+rzl&14XWfCCGC1u6UIN| zYZW`A&$K?K-EB6)&|!fq?OW+Qt682Z^P$DKiW$QF5yBd)A=8}yuh`v?2_zsX(b8dE zEvqjV5un(;^AnlN^h(h!Vfbri92v{hV|fF`70R=gUoOfVV%YwxhL+mQ*a@vtS2caq zBJ<4(fz#!kHZ$GV?lqRRZGO~rQaeu>(xzU2#Is}q`ZsN!`;oD8Ph` z4X(IXp7-Bp7ZZ7IGK)%4<0qF+a}FoY_u1tDo_soaXNhaO?x_U3QALAC0$B(M@!fb^ z7!X;GlIEmRMh7tL$t4ea=E=deXjW;DE5Z;T?U?=`JINZj}Gc^<@dP15aBt7>&7Z zjzMhl9@F_ipCdHWv{x!&UTL^+lreqHzY>$<(vu<*plOhhI5F!xA>5y(*xo@6JO8TM zd`+F_3MTw@XHz%t{D^Q=WP9hK#Q)F_)$P1)B{1eH+fH9hq{XVO4NzzVQ@%qYn!2_& z0HHC@q{XolV@*k_iW`r^08acbIvOZVBKa3uI_>Q`jH)+0mPxXg&JExM^`Pnk3<$wePjI9;ceK>%?R)uV1Ox znP$1GGndyYD-sT|g%Qm%m#ME*13a_T57!@!OonL96%Yo4HIt3*OWvgEW8s}&$c#=| zip-?_s#lG(cxG@X-D$)ASbe=UU%1Own}XdroIl)cb|=%`O22<8GNZVQ zydu1fRoRj!I^{SbdesY>#2Rt<;_Id7eNrjeci?jGvt}V9u`sy*GJ`Phcb1sFe|~pA z?X8Uyl&|Zet8=b9#+=t-XkxIGt8M52Zn5Tti>U&QmAL-31HxjTwF<6+`g4x(9SAiBeU7Ln!xE6y>i z2xN<34s6)@BgNlA=uY~NU z9WDjspXNL50g81~Y7Ib^dZB>>xePcltcTg8UfSz-Wp!Y>F5WawK+!`%PYju_YmRXE z`3iLRJ*Lq>teD9BmYizfuVVXN)eWpAgZVnYz;v6KEGt&EO^ArDsWCwZw_STL;FTiZdb*ube_XOX2dK zFbt`oDxZ`RZb5XsNExz;!{ibRUM=61upJPN6%JKE6Hp{gJ29!lRj~DT{9koLgL|Os z(dC7WSi3+#O9Et}7bs}(Efg9}56m#%V3W}HY|^Y*0NsTS`UFHJsMSIV*j?oCKh$yBwmuf+U5#iU;ZQ>;Cx|3EP*u)Od zyX!22dJHL59$9)0nd?~SyO^G>isoP~B`uvA`S=I}cz<}1RQpchyV7HVG2Wjj*y;myyii#X@eDur5P3$zv?rG=&!x6CzyXulIoJfT4{h&Ec)ax7alT zQ=xMTfzliO1jGyaM}<+mTdA2cg1b+}!pet{5{O8%2F8n#Q(Gw^KD-2zw^M^6Z}FSzDiKk6Px!aZ z6!t|;E&g@Ib>}Uk;csDi;maV+w0#;}H~{maNjeFaAuL3l2%&iufsdg$*Y+wR;Jh3B z0iWlG>TH)yiJUWd-VOh>hOKfaQ&)a+|Np4wa!*cd0>2{pyO@b#LN9rx*6lJZCF(y9 z`oF-VKmhJ(C?U`WjqyVJKzxFYS4MJ(-CnxiEKVRBEXRgve--~nfc!iubZ>Qv!g#7g z*e`5F7w(WbLp20ZwyLlQ8C;;Qtc5u5p_}T8H3A4Jkh*iy5#+QB*sZ*BJw9XU>A7$tw7z#@`hC`Kyvod&b zICTq@AoVoHMLCv1tOq_g2+VLk295!fr$4m~KxJp3;osUe_5dp$|Ae@zHpy_UEenQb zWt3)qC>pK`o;uZy^7=e1+lh)Xg4#1&1}~0|vN5Zx>cXK9cp}WjX%6nG`jD_Xb|QOF zm=TVG4nNb3DUS;HL!;c1@a1{oef_WR7A8>`N~B+dJG|IJn#kNRP1~W}Bc3nG_M)jg zlpL3{ERWr7I9m$>&C*UO70?LlNRH)-L>oF|aY`kASO6M3VzBzZkhGJIH8(}9z%&65~i>Ev{4t$41bVl1OWn$u?~{0x!152 zo#xb=r%G%CrRl)_IBwkA>Cwwb;&6isprh@xqV_7nxXPq|Z5lQ9Z4Hati=Njk1Puqq z(cXW4#X1=?Sg2b^w$&wc?;p`e$L@xMEFO8O{Emo!f(-};X+MIPq+-t0K|BhS9h`~Y zoAy1ZW56{(;j(z>0+e%=BZ6;akoCKq#k}7K&87~zfXGDgP5UE^O?g8l>584(2p@nm zGp2d!cQNs!9~<0qIm^lzmp&8smZhjm;h2FX-#;aSH7R7WAHLW4(+Wfg{zieyILpYa zy(n&-D*@|DOz2S zrUR}q#r0Liheo{bPOTg?nf`>YBKvO%BF>_3r(z=ePWUSRYRt)nZS<7MjW4$EgzsQ+ zF;a>+v~wSL(S0ZU0Dm=<3}WAXcwltZ+Q4FCMJ4I7)Q%lQu)C{0gh%|f6q^XY{T6as zys2l|@l~0W4oY4qO7OSrsxw-N8O2pI7<~M^uDP6sb_DWKWAKdYUfyv^C)F`7D~l?! zKKq(=9dJ()A7&>fC-gpx50$Hri2JI3r*lRcIG_{g-}@<-5&B_Qh?fr+mW%t<`d)Eq zI>_=w-GhB7XB?n8Bn0?{Gr@k9dkASFMW}0>5D|HrxnJc6xGN!!qsm;FfWOLH2yr4I zM#K1y!Qgj+$zSEa;x0Uk9iEYv)m;u>V1oYkzmCA|n4h3=k^y0VhlUh&tF|NERCacr zrxrtk0{@yB*asD0hP)UM`uE=%=lFcwz9$cER;Tgey@4UTjx_R|&kLEHd9?J$3 z{+9yG{ud=(GdQ~f;N%;qdH@M|NYI^xM_mQ5I2Qd%pt0ZD{w@|iGrVf8Q&N!FWU z5k^}E9t)*YJw@HzA7Yusnf>4Y*e1kJ{sIBoH8H3cz?5h48aMqEwLd;y?{F>Eh9^Z{ zFG7X%OMEgbR5$zQvR@}kp|DWcftcepYCRH>UZ!EAp=`DE_Y7vZ3`CDndXRs5!SJ)V^n3w(OITx z_n`nxr3>bdQkEe-F1|jaUd6kCgqxm$K(9xGs{nT#>xz00(DSkWy6pfzlXk!o&o*k; zevBk{fDoBv7HcI09(1Cj%0sqXgLE-Q6QL$CmbM_6ThR#bBqdQnbX850@SI|J3%SK7 zc|{^@4-j_4Z82gEsIBA-;j-R(`X++LY8w`L6K~U=NKX-cB(k)c;fDqOaYrm1VuX`$ zk3@k39WUzEOcrB=NhC^6E>bR+8h_)H(0Nj~L4-7GYD$0HE<|A4<7q zpMrmxXt1w;YJn#loY?x?~t?ODa`Mih+snUAI8`Nb$T&o!r2AV3&H_TQv z_ax4ZGv8BDK+*tVAu92IAKLgMrdjS7dd<5a0ef#rJiLjpmG)or_rIre>^&bpL+ExMB z;8Dg}&u}5MOvh3wOp%}Z6yWke(Oy;spgd?Fa;(vzAaJ2! z!-#YMTE{~ZPdanTTABe*{E{^f)GA8`)t${X6DGsKjv;I*YM2vGfGk=Jg(!CnF7|a> zpuGOqw4eI}g4hh)rvEkXrV>P;bpEPxP1{q8Ji@&OO69-q#28GB`+!BHjCf7IqMHJ1 z?9^rpr%&&t{C!j<5A9HlvG6ZU^@NafM1(0 zJ{B&lh>@wNYlPI>as!=N)IP2?*JjQis~YPjqOL!pO~IB*fp8dh5&8yiD9aD2!2ai( zmTWaF+e*82d~n_r>tN0MZ(FYB*WE7)Ef25~gu-((abcKvO${rBsis49<81sCeP4DI zLI?pnqwW|;4q&o<#ni2F8thAw)@KjFtn;b6SX5XyPPSKWC6X2Gc_m>9J^;M33H2XS zlkhQdcJ7B~%z%5C*R?6VUkw!S ziq1_a+12Z7a}lv~5OfuP+RQKUS082a385r5JbtzKav6o58gkQO)37@rXaf*aBZ5$j z-@JMIS?x~6ReQow>8CKdH^%Xg&{h=f!enHE;E$h=16tP!*aT}Akzk<>0=Mw5!`sfX z&A3-l^VyZfy6K~v;TQ9~|7W_V*+-1Oy)~Zy?x{J{;9KIk(XZLAJl0LWzUlplQXADr zj4MBV#p}1ZUpN_Dy?=ZAf5kZdE3VYVuga#~Dw`axe)D$r|B4~Kep=FmPF$YnRi-hw z3cpr;aKrlIS6L%&6@IFEr+oUt(UYPZ8j-6dq?vQ-gGezDgxrYH4ga&>xv|OMX6-Nx zp4Cwj>59oy_;O|>se|2fk?0JV07V-5>VbYhO>(PipqG!ZzBD{}1peI*-3UrIS86(E z^x3#gZB_g}Uo_M{u!Y<9yA>_0+o#uf)}nt`&P%{OQM_ zR9Jxn8AC^TyINW-H?#IPrIw0CS~fVOZ+yHYWC`iNn;d{M*ZH~>z*eYzT6cFqbb)&8 zBGEY}dMN?Ax)JKMak^Uf0ZwoWJ3|V|l z^&_F^0aDJst7d4I#e}sz2@ka*-3vzxDBEF1kyKCTKk}Jw&G8bLz^cp zeQ?A0k~d3Q`?^uK;$IzyHbq+c_y+N#ntCDr40kUuP^qjYDw3$?@^Khn6#XC{LG1hA zujo1@!C!ZqNyx;jYH8HrI5WGOYCeyp;Fhm?oIfD;z49jlTFLJdFxl)qHochht7y3B zN_^k^F%xsQ4?1-vy>I@E72vun;eGROnLG>iJKiOKdEvzZohh(MKv+5=e8@c{o5A6sdp*q>ysZ@I+5iL6(a2L?V}bu(oX7)A9Y~D{}`|mVNa!O^$N2vtB$Lws>Yl zz)5*ciE2tI&q__dB$O(_PlF8wYr@1G{O6Ibl(rPxsmI?)S?gJ({%NbK)5s1eVm&TX zj+v2?)w4+b@ToJ3Hk&AA9bx017izYBOrpq2uZV@!l~VtFKIZI}p9yEJ0&;-WFigQp zVI&~C;$SRu7jVYNUlGivB2*JU$Qf;TEZe@V43f?GV(`9K9HVKgw#|HLS4)*^BYYgt zafLvDY0iaOx$$3mPf7rNi`21~UqR$WA(H56Ym+D4*yD6Sw7m54Iyw&r?PKMnmnKR( zrF+rR8I+G+PNDPgxqPf#^zs7I(bL4~r)UQ!od+V1VU7)@y!P3jY3IT(oGO=%&4=i3 z%T&`fl>I~%KSWL1UuKdGkk zioZB@8T)fqt#zGE&s)@ndeS-!h)BItn+B8l6@<1A7gas?ZT@kHEXbce>hsJJL^A)%>8ELVuTjZBg1c2v%(7oNsuV zXKqn6{-XE&0?Z5hI?0J4LlN0qQd<9Xr}k9mLQ~#bu&Oh~sE()#%k~Dh=j6idVYpcH`XgTOk0J#ROhXNw@E+6!#JU4*~?_<`OqHX1o5FXTBqXdVa8g{+*5T$##RQz4ze!~n(&PMsIj)on}Z~*lYm9&$5KQBAG8}Poc!-zwfw$f}mhsOz|i+beZlzW+~cA*jfG92W6 z6KnxX!E6aP&#}WV4iCaUuhXt_MvX$V6ghcbPLqPX|Ey18dDvCE&8iCfS}n!En8ccmzsh=9DbyqNRLW2^`{~=jZ0I6J#ktS1j*ehv zl835A-!tq>p~^u$4^rnf#nOfg?)DY5-*EaJBPffUp=6dl?;mL7oaI&ekmPa4V$4O z*C5)M!EB%!+J>m6z6h$T55o^pwS`Brs^Dd+<~XYn5%tyvm2e8L$!&iv==Pyq4?szu-GaK62b1Ew>WRMv_0ssntCQkm$ z16%)m{uE`ey)sVZmtudNr4Vha=tx`RqSQ3N4jfB#Y+Rc2@4#EmOC&YnS0X6r)z`uS zGe&@h=i6-P&tF8Iy~>EG_3pY?mJWKop{y7xj_L3(1ELv2(Iwe976ZlS*LIro^uU70 zZ#ZgVqwYY(fRWzQ&=tR9r7FO)QKUZ*tKW$aIC<$B00DNPJx!cWT`JN^58AEgZZupyaNxN`PFr%o2z?cS@}T1-xMeKj{_V$AGVBM*q}q z6*<{a&8pA|9Alz3Ku;bUUujiiu!@C6q9j~&=8!i;WKqhv;Dl0)h<^HXQGng|tc8%a zgR2;u$zP=!Uo?G}YcsX(i?-oAEER^8xiB^99`P+qCEwJveQRtj48)yF#P0Hy0yHOW zkuKOh>rAS2kFBCJ1!=fC+oh9jmDJar6*x%An)SC4oPDq*B z`rJG&*=gLk?%3WFRnunyv2eZNi&Yt9C8TD%fZE+52xUb{rd~b^Bnwv#e(9WqfWyxz zFB+WwG1f@bHvb(9fUKyxec_OM zcP_B?>3L!gCJ=Ucm1mx*tx@H*KkF>!f&={YC_(CttKha5=Nxx9`2|GTP`?X!Zk&VY zi`HKi&qg>7SdDMy7eGT9^_ry>kIOIZ%s`t7t9F1@*PJ@^^qE%KU7JD<3p(7)l1i5K zpEAaes%%}9zMIXmi$7tgDUB)otnrW;w`uWqEzgt%U0V@eKqy~R9J||9*7At87sOkQ zSZIEQ|G|Q%dGf=cd~54pD;VwCenIRx=L$syyVcsdGE*npKgp>VWD&z)lA)xnZOm8> z$86MngLpZpbzu(a{6sRXw(PBK(q}=4o3k$vtt?DCYbuRv8~2%0`>)pueQFv53UPAW z?m^FW$C;6?UzN|bO$=5CDCb7}*);bTfm|w5&8-zS-wCPSb;i6-5|dgvqw)S@&9uSp zp`_`ZIiPIt<< z%CyFsp`(RSH}?%)7DgD8BePt9_Z})Tl7<%#jTzeYIOfv7rml5hjmroQ)6k4$f~|#r zVRa$?&#sJ1YAleXu(j}StS-bJAvPZWAo*cy;on$Yi2su>hb9K!kvy@r>7Q6Q)rvpz zo=td&8gI(d?W=0vJp7kZ?A2CKB%=7_>=cszo^7>rOuLskEAsBwl>XMMQ|qeL*%^Bj zoh^obZ}g(!>Md69n29H*kcSJZN5am8+itg*g6lIfNwckDzGxYag9Sm8wGsebW!pl@ z)+sQaQr;m1WTyKmr{mbw%InC5TsM?l)_gBL@#U+K7rGzc5RTCTwZy384LMpC3F@lOi~5{b^o>}hs9p~bIQpm$ls}W z&VrXo+8;n&#qG1}$#XSeq|!UUjALi>93r|jIQe909S!)LHbJCDX0DpW4!mjj6j4|W zzr`Noa?hS_jT&11bq_utFUEXOo6s&dnYn8gPs?W16XzKvac-)rBeUtJnwY0R(hp{X zu4qozg=kRT$A29gW#xwcQ+q-GMT_Oy0^cY(jlcc=u|*Oi#qpfm_rqZNZ`BI5*6J=V zx`;i0tQe}zWtSXn1q-*N{{^@c81_OJ*@TmU=v@`(vS@~XN3ZXjq^-0yDpfY(Y5vKs z9}<7S;8=Q^m~yK!%J@ZHN35hpyELO5$hkMrmOqCwbdM^+R81X-|MJbTiI!d z)1C>@e@s#fTzTJvk69Ru6xW}o8$%VF8033&NLiQ>Cq4PECf)8-%^BWDh?uL1Gkl*W z#u%MSJDlEodOYei_$1q`Ce*gcWjTZggkN|hOlA*=n4pK%J5Ix zr_~G`g|0k$XbF2Au2me6hq^8PgA!qW2kyz`blTZ|pu_DN-r{iG_N)RWiLypquJc<5 z7{jV!eBkZ8r51!O9pG@^5Ae+FVt5^uJ6R@@E^W=0H5VzZ%i8I~G}y&h$ z(C22SK<)k%*(=a*HO;6(l)9`?EWrq6u_!YE9whkD6he}ELgWB`SdVX&Mt!6S`~q*J zl0uZDMrqP@-$tEw%dT%=-sSby`MuL-d&Z+PnD-;+ApEjhyxsZ_PL^zm?J)5W_@Hx_ z{HW{nJK9VI83n$2i6})SX%=B&8oD;y08|yjjsZU}@F7!E%{>xt2~-XfcS<2r{{{uA z=2d7C#%fP_A4OV$U(+Lvi;wkIwGMs9!w{mk0e}!<9`Y;w%x??0!yiNGOw-Tg%v()* z_WvvJqI_yLL_?&2xl9K<9kV4nTq7L$N7`y9Ki`L-ShG`6C)k363V+@L zYAvQ-3$?wa_*Ng_CuI0@J!=EHgq+7KE#7Zi=al);AH@>yzT!?@p7a`e{WI#=Z$11E zB$4k@>kyY+p6w0FyV`Wr7yk_ewzBd$WjUDroTKjqI!?J=Fw#@h9L#4=ZuQ#2Tvt*OvW=FjQ9>%27n|JTrh%>HMltoh&CVLU@Hg+NkOc}R8#ku)W&s! zSP#m}_*BZ#@hX=p*4X__OZs#E_X)=s9rA3{~47m#TC@3QG$m;`1Uk&K)^odSCi zMw^H?E6Wtq=^WFY%O-|lqFcV-t&-%T$O=`@E9Xim7w<>JIc~eSY{Lhd5n zEvR#ws+ZNoQ64EhJ7-f@Jv5W~@irFNdCP#8<+w)ev= zNUlhdBrj4RFA^!#E+bWQJV!sG_W6i&`@74Wv_?v!sUNf#MX*-i=n2L_*U#&k3lH|}QK(qr2jweC#9njP|{ylCRjV7Upa zkAzKL{7+Shvv>YEJ)b3lmZ?}7+*#6| z5w_HULT{SQF{-|Oa7;Rn)|j(s8Q>AV0sWHi( zl0$Sy?TM-?DeECu=T7Rll$eW*Ah-QBG7Up*L zqDrn}uyez&EIG#QOX~YA9m|U4IEDx&1NQ&p_!i5!h#8tDD8`{?NonaVw)ERW@MA=* z@m7*azdcDsqhf7?Nkv%{dtbme{pG%Bt;d*Sk(87S@xWyq%V}$0)1Eg^DA1%Y>95RE zXg^ZlZxLeY{<4htCX}>gX95%-zx}Zz{(TluSx~JDApkN{`nBzy-xhXRmi1TQxsH0i zk%bfv8APAyRer~uE-YtFrEddj=lpq&Lk_1z7B@T=EuP^zI4A0fu7y3i<6Dxfv;v79 zhIAscSYWehe4drQS=KC(YV-4&cL#Lge`ny0wv2=Z(126+tRSKTdIv-ROx+LCK6l>Y zoYKv1VbAn-&RC#%G+zqrabN7h3MFd^y)xqLu}69V?=M4U1*;|$vZ7b)cty8pC+}bv z<6FhWYwGcx0jPx0uqrX0BkrL$@B@N@5XN3WyUgyD-TP^P`3GVP`T*lAZhz z$Z3z~tsa7`3vHD1T=szl!nki6%sK%hmo(TN*&g6#e5dOWZ+0hUBTVS32nKkc+q&=c z0JQfx2Z&6lJok;v@aKW6M5MVYZasME(dcoI0zfUw5~dC}@3W8ay;f~IZFj7Nn{B=9 z;{)8J8d0vCcR!<%c6252G2SP?mloU2EVqwHk=Tuz ztg0?xU7jnLMW^6QPJK+O=X6&i4}Mg%P`-*dj-Dl-^+aPq=zTW09p7{d+l;e~FYxgK zOT(%M%f1Cm;MtRyQacWv?eK4imhGvgrx!|yK+SNd%C7f*vsJ=1#on{LO*eNS=|ip| z=5BN*3L%VRa_OA%^v4HaVwOEv@-5y0ztq=94{gDBpy-%tUaOxR#?@g!{YIlNPS(1_ zVNWK}HVDhNT?(36UgICLVIcQ*DQNB+8~>P11s&n8nSnO`TQ)V&E$-H{r!8{MHb{Pr ztDI*o9AWukk?0w?(nk8CKqEcyN6eQZWnaKe#->WG4#s=VECuMUGLdCV(zu(+l%}e{ zFKZHY4m)?ZaTmB!2ZN?te+GY~QY%D{jrD?5=v_qxWzX zx`u6!K(SF?A36X6Vp!h_D%*f20|iLbK9TN#5@!5lf&lk5_yi0J0Hty8Xp{V_1{2>y zwgsN@IHVW9kqdXsY}jIsul$u=0No`C_HaYRyo+gycg!{8f6fDA4D?+7XtO&m<|qd=fTVvA$e-9y`n#i&+G(%xXsi^LU(G8l2< zkOC2WMpDzbecfV*6V!>Z$o2}=3kzTH0@>>Ow$s!RwomH}>HVIi#XGsRd_Q1kC1M0) z0hHtooy58b8kYzSkg#Tu4NI1Wt-HXO_iKRs;yLT-_zKt$++_T-3_P*V>gL5~<5=W7 zImuek5lY)c6{I&-ox?3}Hh>)o!x_)(x`BaN2`aRTp1=;KS-h<4YDCoqRuehUsj^-gv+TUikmjlV4ieUnYy5!Z%mgxa~7B*^J1r> z3bB&NLO%%Bb4*E?7|OaxG+qt+kabJ`q7RYX{7!*=b>R5hvr(Lfc~u00l-F@dD%9$p zW$92c^Mx@lp-E=%Tm(~aC1Jwvz|>D%z07tdr)*b}y~(L5=A9SAt`3YBQH|#M)F1b3 zV^q-8KF3NjNj7ba4IgLU@0=@DNpL2i%%&PZ3IhNcC^SF#v8XC8Y2R)#sqzUPA3du! z7NZ(b&4-Js4=o!zUfXy#b;%u%ZK$|MS7IEAa3rB3xH{;b2}7Zr)e8?0tG1kH8TxG_ z_rMcMCVhmr@Jire@u^b}r^2YO;*TF4*I12P5_T|dXtwW~Ejv$(kH_*^59cCV4zT3iq5fZt0hxqS&+=vi zfpn%L<^UjPca~=|WYQ`P1c6Tp4LRcF<LN5L%TQ zizGnT(dc?mYeLAbqy!$zRZwgR>}i2l34u`(M@#gq5^M_=$uaAlqDay^BggqOE5n9D zXellUw-V0vAqheo%8{_bkhs^fZ2GYnlXTDaCGo~miaLH)Ns|BAWC7UJFz6|SSp#d{ z56U)~^Km|qb%BXqmkYehzQsPU9WEnlomD*N1ffl|WCOd{W3tSPAvzFOqDf+u72jvn z0b|i0x^mxySfD!YYb(ht?*}z~>UG>dk-zQ-B0k6WFKfYwu*A3}z+e0E2C;BD@=e4Gso&{K*D&{BY<;wH@l3LFTojf|E_O=pA;RgVnMkSkUpL zvJ0FRwi4zbEdkiK071?-0Vt?$$#P&7y|!+QRWwZd#<^cWprLR3l%|zk_ERXu_KLta zK&e_~N)@+UXw-wOVZdieAKAPhvl@<(giXLGVi@wnd_QuDIBEDkwizGp=OXMg@nquP zOYXavuYMySBZ~bLQKL^8R4VBr2vHL~8{)14*MqJHs0U+pdY!e|pH@+nLNnxUbO8+v0`-uRB2shFXT9DnY5&1k6xJI%1WD@rl$7)Eien`6 zCq&PLEv+*Sbh*526(9m3{WZluIYHh39yIh8y=-+ySY*_HtJG5iyM-+(8q!<-J(njp zL^h0097At@6MdR6g@7=6%)Ze9P==5L4YDdDha?ghkIYW}(-DtC*)rEtLW_7br@yS_ ziGA4Mms4}-ZWzDBle`jfWDT1bn{X?#xMZ{JhTsQ^&r5v52@DA1KH`B;hgTIH&$l92 zDbmmJ42xq8(rev>{FB}9B|3#gssi`{`Q^sMw}^a_uNW<5l3YE*3pwe#uqP@Z9A?6N z3$J&I%pc->zgAJd8!uiHLL1pMWt^}SaSth7@w<-^&Bv zO5m+zm??rZjTf2P$1_{#zOh5joFR2;)JF#z4oCQdv+U%c>p?AVT(;I5z*`0g+*ta#72cc`DcDG%8fTdr#kMc@c& zg-DtIpMGA=Q(ygsHC>KCygZM@{+Giv4fsX7bHg|sZ@5Lcx+PNn>SbR+tiO3wzbTe` zwxUI&BCek!ac7LvFwYG5pq_T^8~bz9PprpOwYF&p=cllUufet)OS(J3PUefx8e>*> zz021VS&wo){zZQ44MKLLM0f}&*{yoKaw2zNf$Is8E;t<#e|h~}e)e|YgovIn{vsE5wQ+m$)`Tv#ad7)eo~vE_$;i!h88=i zgqV`v^y$~TidHwAZj}c`NtBxotuur2XOzFmgQ+?vFsNoVM$YY`>0>;I0SiAAszJ$W zH&tRw3A5^!F7&(1aePlO%06PDB^T9s2GTvuVx(HcxhfWl%`sGw1|8lfC}m1ap7CmxGiy|4VA7)YUaPOSSOi^q9t>y3;4w#3}lB&X{?tpDX@c^Ulu| z7R}FMS7a)U%npCEWMMEI@mJP`Lg9OGAQM@FT1I^Tsw4-o1i*eYioEoUhNz5ux%nAU zLDO}phA#fC9&Ez`Qrx$#6x4ENFFY$pI34wqc|$2#o&HqIU6o39aw1n?a7`aDHa0u3 z&B)aWrH`?})I4P{L0E-9indK$w_xxVDS;~=yx>{@@QbE9Kp5>uOyFr$&-2!%Gwzn_ zaFwL^En{$;F#Nfa^8BFDY$zFE&wg&MqrE~jVhOU4OzjDE<1m8-QzDB3piYH$`kg_$qUTk)f6!InrOuyYiY-qZRs!8r zU{0%~WTtAiDKHb7#Th~5{NfWx8;MTXd_o85ukK?!d7mycy`lzzlB9cUybZKNK$0-mmz0(#zf2G;{$|&on8LAX( zQ;#?&>1}8llTuBP|8FK6j)mo0U{X0(1 zheF8~t0gNT<|?2@5^wN+WCbMx5l%>y<%(sU;PjtQQ$S`%ers3(=JTIwG8&7o^4{ZB zEkR$%i2>h_Fo}rnL7bpfxfleC)ZTSInV7>Etl)+HZqNLx2bBs9l=#B_Du59=c(N#k zFqWg}@m+AlUnn*0$?j?^(rWnFGy7YQ`O0RYpCXTjgB>3M=85ccy{VZM&00eeU`wjK@dZfN*>s>RVR_cT)TaR7;^Di2K@7c88B z)}Qd1iSynhS0}=%!DXXup3DS=aQKE zaS&`yt}-^P?1!MP4S)Ds)QL)|R$N$5b4Ta@A@^&+@zku?iLBaQwC5N5nax_2zj>J$ zd=*MS@#@reJc?vwsGYEaHuxpKBKb6)Obau`xxd#eAS5Ms{7ezwZ(T$R0nG{E( z%+@zK;Z;Jyyc93&$|^7w-a3~6R^`(X)ASmM%&_ABpHRBp5lg#^&$YW!T(S}A*Vd1! zrsny@e>0x@{*T2#5qqYuMTG@EdRA3}y!k}nFWs$)uKxc^>-nl7o^TWEv}QS%!-WW* z4p%rb;krUA$_<`^IWtIWG0nr_>6y8H~7H|MjdaoF$Lsg zdTG9pMe{RcHr$Cxk#-nI-QHAx_RN5EiTQOg%x+Ahi_KxQz*5@Iq8yrLC}Ezt zRs25$pAe>SMgd!+S3;wj1O0M4hLsmbf>O#T^%F6L-Xwqn%)WojKTklt(#OPfEigAt69f&{C^dMKW#T+UN!`f z`@3o3y2(paf2*ryUgseg4%Po%VQcIhyLja>x!?t5w1n)Qm;udIFY5N=iNPqPj9PJ# zt2P#OvlP*eU~F2mwHNKQ`{Q!*1+&MA_#ZHJlUoGX)j<~++9l&1q0WEcmwVf2s5@v`3G4A#W3)-Z^h+WOCwc_eCui&T;;S!LdACj$dH$ zoPY7ix0dU@DK^A{mHeGI?>}W5sj%@DT;^Ju*3GA)zLog_4UVi1i;je*io!{!X&J}h3Pp`jx#&Db zrpY@SRl1GrMt(87UwGqpvw4rynpQaWjM>(QanV*8t9V^`=tVcqw5^Zo`8e81xgg+* z`D4U$7Ez2_MaxQ-8QMZm&*_CGqErwFTRx3Rr7!JCoK7Eu>5nuyYDeYT2;>DePsT4( zqb$vxK37bw2ku>3;ku*g>*FE?Bm{&ucgg8^k*3|VhgZpYYZZ2Xv>@u0UL5Y*n4A@^ zg#rLI_9aC=Yy9Ss(4L_$5Rea|$WRWqfo4Tu<<=h$^9X6ZXpcK0-!0LdNL^lTIhQe{ z73w{^*pQ~pmikb;{qP{uEw}%LM&tKr1(*jJPN`_2F>X(tO!}+VeW#n~X%8biT1+bo4us|5Y(;OEsS$(x& zfjULe=&l7-81GvdW-VC6UY4IPt<#N|@|e4fE7h;+^&6lS;|tV)|plErxEWz$RKFj9)%}vNA5Fqh11cbsx(2mxpwZG*a}^4s)~)rC9y1!2N7M(CFnVO_nrs2i7o5OmXsOJ( zwy#DRYkY=1arl=}1yy5V-gF}I9pOn?w|onN=(u&TuRlZi3!aQ3+d`7PYq}4;<)}xJ z8lvU+{#ZBtGmtjBE>w~ z1S~=Zdx0Q4e-YqNmh{Q0TZ>}q!+`Q4XgBFcRBV?_8!iL^jw%)cwG3WPgJFKJ4-_?0 z0Ov)!W(w{TydGi)%6?nSuW{3u*~|+`*8RhU(y2M{o$D6ZcWE@BN93u;YhBf|4!I_$ z>}Nv2g*&h9Hg_a?+811SE~Tk8mvO4J5A&v+hV-7)GtZz{ge8V*!UkY(LHTl8B|-~q zKqG}r-s|PPq$(>y+lKaHX4UYpYa8fJ++;PNvi*=+A{RFL*Yn`mZY$1bP|v*-tOy!x zhxKZ%qmcFL5QMhMoOZq+R5u=Yo={rj83bjm)B<|;*nY6JX6B))_)Iia1HUujIt8t7 z>}m!i&WMwrAZq92G!__;&rb{b4-Z6Wr`-jkc!7Q$jfd=9MgbA*>%~W z4ax1FQANRIh9$IH17IEmyf`(NnUN4^G5@0W%k~h!GYRQN=ZdhUuw@x}oC_o9aE#pD zfVF+XI2;{?acVSJ+teJP2;{luc`(n znn0ARSm*)+QtqvCFNPPl$xkoJk~Vk1ArbmUYurJx`086M&gu5=680&ksTVGyd@_ge z&KQOLd*p=Fo++e%HOo|BuFt6Jq<}@s`fs(bj6jgR9v2n(<=Kgq9&p^El5@%z>KO?` zXYpB{-2%27I1gRe4)8G1FpT6 zdG=^{33y#*n)Yo>3hML))(1wGZeJK)W);l8`k_z`=NIPh-1ZdH@C$xM@5#h!Pvs`& zA%%i7>#0Y??(E8~u zK2#SMlnxD?ik9DrhUsm6u(l(h3b|D&Qaf<%0L_&b%7cbRk6J!FbTye(8TA{bqk4fs zFb2f>=5@hXpllQ?7^LGu?AfEAJ-Mlhu*P6u#SE6_U>JVD42YyI;wZwvS7A@0RV zMedSuns0EI67DS3NRMVqXhbA|dy~pV7lA7yVi!MUGTVdm135Cgg>1O!yb#q+42|w; zbhPmJ%7=H@2;y`c4r_BX`Dfi`qx|RGN$&^TJZ4thwC5M{3-OuXz$)W zF8M3fj_f0$=z1Ekr{#}bZu$1$GEM>~w%}np8fAZ;Rv7shISiRjVHh$Hg%?{kh+?Eu zAjYZyAq<($Oh!jceCRn+KpZ2@f-&4Sq@GrC(V6U!U1-(EERFyxVu^aVK((-S!LOGV zvTZ_2bqE!VaXe(p8^v?kTBo}dNYMe9Cb$tk$#93XWzH;Yh2zFbhQ_9kTBS7QSnc(+ z6XX6~&|eib?okCzCOfp1KGLU*MRq1?f-QZ%<=J~;fy-8Rt#RIu4p>*&ev?RppfSC$ z)?@jms-vDO5E%L<5|Kva@@0G`rHdnET}#Zibcjh@#JZ0$x$lK>wY81$6D!HjHBKwm zzUV}TLF5|$nSiLUfm6kcwbkfbN@9-m$h?Ssv);;XG+8RLEWbtGv;O$9Kdu07l7iyu z^MSz07bGO8w^T6$Nf?UK{ce~8;+n2K6pQTYhyZZjy9xe{u#MhT@m(>s7@g*paTDi0)ZyAW<98&A@>>k^>aP#7xS_X9L&AmUeffsFbl0ALNY%j{ z5YR|f>2z6>B^YU~z8T#M!)KKu51xGRu2?>W$m*=5q)yAjSp?Hrw?VayCJ4n>*{=5; zS;sM-%Ur%ij;5Pq{MDU|3?sjF+FB5dQfLnU>*F#D?CcM4UyD+Q!b0etL8Ii2+v~mx zK&Z64!A+{Zn|-CCbGW!(hukkT?yG<@B)>~>!VLrjjtbaLz2 zEJt$WUWAn)Jk1hlj^dpJ%YlGEk&{(yo+C+fxc3kdJMiA;c647H50Um*+KdHEl1}0| z`d?eV9N!gFI$F9I<|I==Y7TsJmHEm9h^ycy%%Y!L&4opA5kZewBkeHF_MT! z6c>T@&D+6ACo6lpDr$LbRi`;Nk_lJnxADJ5u3%SWtl3dIH>oHpE41T|NtVI+0N=hr zwjzkW{=OAqe&mMaSp&k4o0p41u6C3N!$n!k5L>G~1O@ozVT?jV8m ztT=*qm^z#xkkwathHKY5nQGv;6Rp)5fRYo1_a>SDRx!-|IS~oc^8@8#56)PokIky& z{l6b1TM?4m?0Sp$Eu$@}@1~S4d5w%oU?wVe=m*IIp7ZdxRX4ZIjTCx;F!9s&u?C7{ z4&++lJJ~AfRY=;0-JJ zkr+*~K&S^|fYkX+#+V#CXJqH|Y4T%BQ&e6DmB_q|X?wcd?=-CY2ibytNUJ3K;eLRK zEG46{ZB6pK-@ZOf1f}Q?1=`>Nv6Zqu^|cZT1*emSB>&;AAtiQjq9VvN6DL62C(u2- zzSX(o!2d#02|^&jYBg~Tq`zR#Ya3#B03UKe>d^%8%XuPlOYR;}&V0j~S(X3`LLxz6 z&iq77(k4KI(7O+mvkcbE8U%O{5)A@#=7(Yu%V;hU6JYGnNHQUA8J?h)GoYG>vAK{W z72;lmm7S^Nl1qk2a3L76aHAIdve}R%8RBjrCW<&WEj!tCxC%)1Rm1QdYgU+@$^)|S)q(;KDtT;C^NFTwlb>+(xCr848k}FYe#6Emp%+6vV zCa|X#5v=*4o<$FoT#2$H#%ZNN{RmW#ARv7a!8#pI=sthnl3Z9OB4o8NB<@2*We*1* zOUl$jk+^pelY58o7x{M+awLce8te{hAxYdjNJ+ZWR!U?^&U&lWO7lzm@^WgY9QvDO z_{|eF01qf>vkE71z9$-_)`*RiXrLraK>N2Lh^kwNa;E=cD5=;ehLh(==d?0tjqVH7 zX63i44rXo6>u&`YxvI$WJx6;ho^L;|l_C!|7AAxOkUn?N=+<{VMbBk*l$q@!stOct z{CJP?sh-G?r)=^WP`jVK-|SI!WPaW~!KTFu1HHByXGEszB*ei^ zR+VMkw8%)qcP+nk7}*(&9wrM1$!-+7UDR%uo8kQaL*p~*#aAB73&U2YJ%E51=E59< zjXbPrQX{Px_W83i^z#SDNUqBCAfu}Nf(*}<_Q}-;EMZ<{8I4 z&Q6ZbSLQ1&+ZB7G_#0thvJhQui1NR4TlLtJ(oR?~Pd3|dqp0fD!Yk;1W^o!=G?kNf z8&o(^o+?F%F8pYLX{8fWIq_8^Esj|q0yrgslO zg?GizplEft>9`&yne>Q^;&-=j9UVF)nnn;9y`Cp?HPV@Ny5IZ&-zLnNeOt^%(19u4 zflzMC@Vn;wf&)nmmGlEKCll17b*?Pu{5B{0hA%$%|MEt(qNhhQ9!|*+6d`@zzi853 zF9za-&qkua7i3A?VI_DA$+9vI-~- z2^9Xym!u>oF{Dv(V|?iI;{oUeKC5|RI>GoxBkwNrMUfeCDGnew=%;cH-ANGC=7mDUP+-?MF9vsH=anC!KR4%Q98xF2P2ZMY)3MDid3+!oiYg(#hNvKdm@0x zs0asclu~g3p-4Kood|L&Qo+2ol^|8r_@dRW=CLX+1tYRGDE}hKR%l-qJ{0;zko+Z~w0r_4{(j zT5mMr+aE!Be$+B1B}PEw2L^|;z9?^;rEnH zw-DGC#hW>q)q@p#gPb?&>+GIjRSvw-fY4FA9>HbDP*lOiy@$Aj%d^p#x>CJ)2iD*2 z1yx+~>IlIT-E-^W(9OE0m%EPJ2#I@9KfqUV@sO{}^>}8^$duqwP~c54>>S0H%Y{{p zdVmhs0n54YIJuJ5j1FcJz79@yO|vLAHZC)=>dC5wI1eLH>t?GFF`{sbTdr?ThRxt_ zvk(Nhmp=q9HN}=41^K@~`>!GOMtridc4)Z(_aDG#c)qzuo9$3lOPq$d0|PC3WW-6#@;*YhU;caKU^Wd7qzQB z=bBF|eRni&$$0AajGUI`aCY-Oy~=h!GH4uxW%=D+7=ol?$+VQ@&x*G*`?{gin4V+D zki|8eT-F@wN`282RjPD2us+S5YOinjA2XcS$M+#w;|q zq#8wI57{5DBfVO?Erigd(Q76d1@vpMwZ%k8gl7A;EXLAr5mgKCEsHO0 z1c1L;QJ26@k=UjfR@1U_m_9KwoBa>S+O(_U|Cq_|l&Ui2M*HpzTL`1Y~&GBMQ97@Z9h9 zQrUpjLv?|j!3wWGWJb?ItVb2qJ6TVhB}Hj^nFb_&ki!_LunUwrOLr>TcV$?w^GdU9 zl#g%IwNUP(uzERHV#_%preYu_r|QGV4WRgnwLF5+*98`x?!Q9d@c7WRLT>=$k43M4 z>Y?F})3!eF5|mzBocDE#q~I*JRNqlQ@A-oI%G)iv z5da=^-#m%{EhDJht0Rq6!hjwl72e_2EeX|Qq==VaQR5yKOyu&wPGgQ{eC>d>VO)mIwl~@M9=8Ax6=$L#@13noCjgdUE4;l}yBZ3FfA>$%Ba@ z?(qcW`Dzo!eb3NCMVBRK7z#?D+Q+?&9HttQ&Pue+{Z#;rv$-%I#(>v3s?8R%OmDNt z=x5li{vtJmwt~k-B)J%+L-gJ7%eSUuk#z-_Cj_qkl%{|tuKO3QujreO>##9_4~i%4 zod#bKw(Cz`5$$QomV+8PhAB<59|N+bJf4faioJwd6JrsCEXI=6s>gJZC=T$#Gu;~r zM}>j#3rgdElm%JQIiFzZyJP>VsOV!tNn_7R;4Roe{B{@Fta36{?` zKJ@*cicvZf{pQ_-oLuncM#oq2RN;jvI`troqT@CR7P+~g84`-X}*h%S(uVH`JHSO?e%4Z?`3m626)JI2t*_i#raP*s`GK&xiB_I`T zPT5sSjq<3l*ia10bmqjrwn(NRZ*@!AJoYvo^vgBdsEBHQ`K>0d`cJA)IM5FxquH@u zFf|*@tA*b@hmdu>AM`=VY?b^*xwzO3D;aeP$J+qIYylOPZ{|)95Z(gBE?LLIg&{~n zE3@}>&24ix!jXU_i-wSqig!88b4k+{SurwS_ujH0izxYH$wg#6cusBjH z-nEMgEZc<~e0L<@R3==pzR}QqR&BFe8!LCK-OF+*0aI8bEw5V<#(RP!d9ybef4aFg z9Xv&uzc{0KgT^A9Swf_kh_udB@aY)7;)RLFgE70AXJ!ZNBWmK!Itk7?4kNcDr~)pN z#EFL^e%-U=mbIY{RLNx*UO;{j+jl=@#P%sSdrsP-GAdG_n&He!_e#cn(G*34fOZL% zUZ;1|#0z`T(By z_krwBud$ZOFl$XX9`jX+ZVde(I5BI-hh0mQWt7OGg%`s1;D4F!otk`)tPx+xl9pyP zZ8_K;glSe|6FZLjob(5wk}yP~&FrQEM1|7l_%q$(y`Yim3xZ~y@W}}egl~AuA6hDh=NM=> zhfpqJtrF>{^wN_j0jYp8Y7GSsb^QHnH`R8PQ$*_z#Zp>>-c2Cjj{Z{i)Q7;lsH*YIH+=n`vu1HJzPwrMkM{dK$iV}#Y#@@+gF27^JYzRG) z0f;bB*O65)W5r+_IJ4%>!=A6%w;qYAi#v?p$C@nK?hloEp*r4r z#UmNGUB_#4QFN#4STZ!Jf$CE1xm?tJ*o*_Y?p6n0z|y`CRX`2RDGjy=qZWQRO!=F5 z%2x#ORDMpOEF|sb7t)56og9r$c=DpcVjNXjMU_UpmjJ_q=Y}uWDnYEMVkp78!W0(C zMBUPVT_0bJ2e8iRYBt|KlKXve5pCluzePpAiRuBowe@y*B(#{I=~b47)=>p^8LM%b z?|~>$Tcar~$cb`$$ls&2^7HVZ21vBjha3)=d)Dq|f-Q~l*o_zAx}aL(RciH!kAGpF zXMPdbS8~G1NKTSOiI-AuiZz-cxV$HlE&0raNi*;jUONE4%gdroqvPl{wBN~;mAJRN|P-f|kU z_`@>8I3n7ts!Ah5Mwvyp2-REp9n;-Z{^sPxSLk;^pZO|1^|w(kS|DbB#=pNbJpZRs zT&fyiojB(@=KopOmKwc$=3eXa>Q_iKP2kdo3z6;pRo{5I>^_(@aN*K7h=hTB+4Ey{ zYPle#v2j>audd{CzS3=lAX#5_&O;r zGnrW$anZ#N7yy=`?Ud|luyr{q`YIaA))ALG03j=OTGkPc)bEbJg|SS}`>~X;V%XX+ zubGo))p>Zp?jy+p_*eoP{fr0)DL!1KK2H*Usq0?t3?h@Tjt0PiEfMLbK@l~?3D z>XOs6wF`uhpL*-`4xifh7$-E!H^D4U#_=YXs>z4R`He?SiGgJa_^}V<0rdzztEL2) zIWpdif3AXwza>tA0dR030Dv$<$6<$nYu@^Mj2{y_41ccX!a8iWnrap<&wp+TZt0qq z2NVwtQ{`f-B8(W3jdaS0spO_FEmv*Z+o=Xc_!HW#7NI>E@plBtb07fx0(qP~?H^Xq z=C4uj=ZSmCT~d>-#x=x=rB#j&Ar=oR7AG4e*5TC_xe24%2E)`P$S(Mjx`qsu%z^X4--9YD6~T76LbEf9f73=UE} zslg}f81XvhRI;)CT9TQ*>&~=b!awOvHP~TdxZGsT-~$=2wIJ%3H@WVvVG<(CybW2+ z;NukRk;r;^M{UJN8IS$uHJMa#$!9*HdNxLROg2XGN;%jY>y>-(I2Ex?%gUzcCr7eq zvH*ugicu&24JR^EMPr)u)R*BM3HcBIgTJ_lzrGMyP))-tAB7wn#3fS5*n5CDxjtmfiVdy4T8qTcr;{!j8Wjs-;8Z(qT z`ve?`&~U{NqT7k14`6EC77nec8qDhkf!fIK*71 z!Oh-M-pBNMrm0?@dZu@k>*E3!b%72UMvW0xZ+%%NENY~l(Kd%Ej!FKgmu4e1ktBa8 zA?xJ~5LTir%Pd$QEo#JuYU-6J4Jtmwu$Yl(u_;-UD;}gYo%jh5nmj$q4vyvn4eS+8 zG(a-+Zx(Z>ji$*VacfV4s-#eEyDORz_fjUDd|3d}zqjZ14)C6!0!RF%CfrD}bMZyQ zk6@^2n7$DrXzlzNFMk0cDDy4|A;#@xIG^gtVh>~#>{C6JAr&S3mEr2`pPcaM;buje zbOI?mP@qhQwebw)bVXM7MNP^s>a^_lDIVESG?A0u=g}&GWk7>#tKp9LkC|GCywg}J z*3PzzidR8`$zp*E-kl({fL*q2ch#*x_RGv)=yDNA;x09_1%qf5uNs<=7LH-0ff1hp zqV>a+;lO4R6{MvtIr!2z<2_`C`_nylm2e9N`W;@D@&-&_N<-c0n5Owm#*-=Q)R;#i zXI70zo#x?C#{c5y=}Y#+2Ae=$fUiC^EkH4(x>nZx^~FQRMxGe2_L9yPGW8VE?4y>J zdwLMUt+p^*r3t&_;nfo&Zy)NmB)bn-YLT7O?HMmHM5jmr(a4SxUg)x98&Dj-U~xqu zk@vzN9Hk-^ha_jF?zc$@91%Ik3v`^qb1<&;elH^5mlXZC)?KipNb)*F>C=Vu8_LR2 zkb_f&>=5V}DZL2b(G@&xbp-&k`xPmqeM4WzoTqNQD6|!U6M9Ya!3wqbV~n|dVyalS z)s1)Jw5->aOlt9{>?JzJWz^j8)Kn1D(WjKEPfrt)_F`q9j%=u0TRthDI)RB$2L=qZ zMKhrnWjLi}-dpjqtno&T7qFf&rua)3rpGl23rN`b#Fo?ILwkyl$CRVoCWzV4)o76O zMT_w)N3#`eOq*X@Ttj^U)iga$CTelUIh*?9>5=aV8fTXHi%0qxW8$`qV7yIc$nNbc zREd6T8$qLt<6+A=Hv;gkIN`j>G4KHZv)|8ZEl9?^uVVZZk(&mh)z6CdXPi|avw;Gh z{NPaT+MateYRU2CWjQ$q(*<`K5JAE>7pX!~I{kdFsqJ#BSZ04>ypih$2pjL4DmebL zdI;-PI-CJZm#_qOj8&1RW{oF9${NRE806k}Nbfigi!VFLzxV!p9sgSn4XE>`@OdQ}4Cdv;G=U`a?A9U{%OY>uoM7?;H*ekooF-rBO$Aj+d zxUUe;V5;6nWroBz!c5F@Nv)6JO`8t9!U*+ck{BZ$d{&Dnk~$ufUs_Po^&7FW0T1aQSBa!Z-LxGcyu&@(sF*sVFf&poE@S;@hcZZGvUjk4 z{9{ALnEv@EN=Mn@{i%uH?FIj4WG-v;CrW!;^D+n#T&OmSIIp2;Am5#w({81>4%M)& z9}xL~HS(MsZ*suc2EWKT3|T8bwR^)+f2WL$_6x7l@{<)-&UkbYWNUfW(hn=~#@@UZcI5~&+k&v}sd-R0vpHK$`ap9iz58~RsU2n`L zTh;V_1xM}0QSC(Rqgs~J9vCt?>yWxTe%zL|>?6xxv|hT(uq=f8@^1Q=S8fuMGTt!^ zw_9dznHNs-O!HTjNE2r(&Wk4rQ^Wcj-|ik!$k#z+6g!eBX18Z;-=#B5LW;xPIp`$7 zaNJo8zPRbnGPXx>FfvZA=CtPwWm$zi!Zp31giNsN^9hlq_M`&G1%Z4}JX2+7yuEpM^RH+UBP zOZxV7d8`|_`h-6p?1CFpuiNMmmY>d_)+|bP6iw0&=Cq_t-Goa~ zf=yo@g1N?7NnUNaHIl`|$U+v;bu&|P-gNM4}Ek0@OSqeS)#NR3=F!*%Zz(o=-( zwPf8NVRL4{E?*{75YjS^chaIvVqZ1UN*S$P;T6P+yIuNf@6P-)X63N2l3}~h$GY{$ z#$JBegtLV52Tp7h^2#_&&2hIEEr1lSbceb3F9WimouDzKmZjSPQaK!DF;v_{og1iY z2|`KV*}=i}t@OMx--s6wG7&-9gc`=4>_&&T1k`}@T)e&(P`!&BNn+AE7VETaL5-C%b{P z{7_10w*PJNepR1zN*b5q<(Z8KG@kTq&^0|SAY(@Z(lH3d>`pZ6eixSUUJ%K^qK@4z z!?|RD0zvH2UXw8EL7=1k6G8X8{P>p%HJ%9)mL@%p;a3G^-18EKqYw3BzgfB6a}hrN z8b;gUP52pJSVj%W=K!1ZER^Mt1jvHcW)iOfzTgIc0wL){ben9sUr7ti&?gVsp4S*z z3~!NwGx~>7-fdyPU^oa{sTtt&gkNn7&&cGX6Qz0+p(%!jNC6thRZ|6D>Eix1e->7+ zajRU~B8Y%3-mq7325ZIUhU!#Ad|cahN`#X^T`3{2oI4^OrWAv)j?~JM#y#ic-yx|8 znJ`4JPMqONAq%&#fTZzhe-ayuirXf>ja>+XdH1UhlIx5ZOJljyp)wB!i`GbMtUZkb zhPs`SWxz%Gpv5~wR|WhT%z+R~B~Zdw0z71qoGR;Hj|A%|d%18kkCN289>h1uNz&;l z_t(@_h42Gamr7=kE~vC9mmj0Iy=STb`&}iVjr4HUsqVE{c zp2@~AI5-T;C8_B-^N#Z?#QHq@sfi&{nHg|O+WO;(Kkq_7e@-8Bbs+jBMAqpGy{vrc zibk~>VczTMqD`K{8n(V`(+-jxFUc2rSfA3{3kh-?GY2Cz6MKIY5gV2%p2ueD9pq`t z(CmsBMHZ?dT|g9dB{nR+R79?|Bx#J#&tdSb1fVOH$g=CXw@B}A5WU`#_6FBbY|wz& z1GGg_oCWf_yk3Z^DU1a%h=9!r!M%$Je-108<%da7J^))lq`!SSVMjAmU*bi^f9G0# z5eg|s6o+e6PzMNlv@NUy9NAQTR0M~H!H`;B3M@1H%SPFTlcr}D`ABjM8zN&)w=1xN z%-Fw`qEIT&ME%;5bQ7ACP}qXGdH29OLSDm2i&G0p-vK2A0PX zPI&g;i(a@dHN*39lgBk2YZaC)!VS&A9x52uc&_u3=d~j99uE7r{@7qOe!itxebKv9 zzsfhk7TtBGVN~`~V9I3Dr$GYF!+z=#i6{?=zy(JuRR_4GZ(rBN+-87LBF;lDfy=z( z@?Jk%v`ko!{#lat!nG0UQP1_Pw3MQ297uBNi`jVt(qcXAjtnOg)3p5jV~uDudG&<2h7Pgx1mOV-E{sGwMqhh3B8tiZPU+ zReLWm$*~%AKD?)Yw>TGem;N1nB_i}Gy`7l%Tw)y=;|1th60!LwlmBEz4!5E#`Swf2 ziC*twdHbCpOuD*YN?9TqpOX|wqmQj%3{T!Q`oQi@)g4pBIp|!$q=k@bT2C3|#K%vb zF6TTQD_%|UJ)PQ`MbqBPw56+L$&w|j4@hLh)It1e0lCnDL^a8SOnzXoc|B?4{@sSU zFc@Cp&uI0`mT_I+Ua&=?PwTw)7hu0BcfV5-X%fzK*N$*K?)+;Fkgew{W`zqW1I4k{ zP9BUWN!Nk5^iPe6^VcUNPP8xpN=ixyE&a~7#fN^hYtco8l1BY|yZ)G9ayNZ^Od1Mo zvVq{qeWe=>o?8AOxiJ_#=`b)@(tZ_Xne}>0B^3>GmT(n>a{xiGJ7-Rh)Uz~-*$4E4 z_5hY0jurqpyqou6WDP4_&d$0rX{0kyNCW}JF$o0d#!!L5F4hVThEk^J!h%~h$2CWH zA31#8o(ZpCg+0lKCMi&lJisHj?HZV95Kr8pM&kC*7aM~OK=LQ0;elnmX{o!|?l_syG6g>#PS1EM~Z_^TAG35LAG|q@v0UEZ!ISM=Y1(y_`{>@O@AMUD4 zX)OsQ;%*;0TvFbE8IvL+9hHysuM~*~*rmZf$e0yD*Wys@0g6U<e#q-`oneu*wXQ~`C%B*2V$k%h!#NTEfBP|Lg|dA&3J!T5;NEiI2* zFJ2ScZO$f~iWy?ai~KYoEu`{Yi}(JBe?RI{$w6*M0GwTEFf-mz2rs$H$a@n~R*Y8D z=n~mpvaa(?S4EeV!NIWMsk{K{eV3-b4(69xmS-Ks;1Sr>NBvk#Sbp$lZ~=KE_OT4E zKFRnRDqIxZy3>}oWE>t!!Cvad1nkHtaHjx*9FecXl@SGccQ-d`DsPW{^xKZLd*3!B z;)i)zqAZs@M^tJ_cv``7IXkT4_;z7O5Dg9LKqM%Se-$R^>WsnK@FSyO-Bi#c^6y9} z*!RXVdMsZflHBgYgw1!=tt$W4?lI2;)FYs1nC3s6EhtLFS$MIX3%g7TO*y!nyuj{1 z9?|laNG0-Vg2XMuh>5lzu4{zHA85RMs>+CeMU*_hq{rlPg$MgsScYWsmR&?7p%hai z(I1)#L0QYaNIgh@?ow;`VDwWZWtvRKHG8%X9Z@84w_qf;-A&%9?4;nb`xJa|ek0#h z#;g!ysgsbDN!$`Dd8v$l9_^!Q0ytMFA*+*A#;hh|5hWy^#61Ei7X2>5_!Y=$U$QQu zoFMd zxM0I3G1O$Znmo~PlcG2^a>{eYH7G}?11D>fG;X4>3Ohcn^ImM-bB_ZjGA}1Y5;+;q zejR+Vo6w2#t(8%CL9u?3$4(S)LYmwp&76#(Q77P1}tvtvvEn2fGUVIH|tOIS{R2@2Pap;g#^a?ykJ#GE*nLs|sU- z{hnRcN_!sMyFE;abyo+r{@PS;r(X7f@_``ODYRZ|-JkZ%W_k=Y1tq zr7bxiP-uPq#5Z-SKn%>)&9w%%R^1368zhb}-)*{);q3tw5K_^}$;;w~xN$t9(?{Fw zcp6Rfs!ESv_=l|AOakG_f4FxMgbk*dSHwSGwYjQ!D^w_xt;+ZffCe}yC_EzB!BBq6 zpBcbh*fFx0U)vPII(g>9-2%w#$59oAxI(&);PO;ly&TK^USrcS#TqlRVn_HD?h92b z)seTSVn~INC@jc1?(9$61!Yv!SUq(rFiI>`(r-#!*J|(n?JIBWriRBupwZW`#LG%D z=xXOdj!b-LoT4v$j#T@U~Hc5Q4{hE|{=oOl(%(h5}+&6?*f z+)846dN$mhFlt9|0mA8)v~QJpx8e)AB%pscQnR}zz1^AYSsKvG-9m|aYySBeyC zZ*Q(AxVJzMMDfm%u*n?cA=V`6r@ld0; zf*T8KW$hc<>;fSWz|GMS5Z@W-3c096y|8J9RsK?O(^ZDZ2PlJn{qh%DbC{BY_a8rV ze0*o7Y6wv>!FCj=P>+~-WzedWS?*;UZE7JdNp5|LQeDY;&Ifk9Zdx3U9n_siKH^Fn zr!2PfvSrz^-h&F^p-qo})$0D3Fh)-4BL7yNuCJh+s+g zrYFId#qU+8utuUhjO3CZM6_61b51n z<9v7P)u{6Sn0VDfrqe@X6<$m?0r%@pb54B=4(ifNZ~d{CMrGcAyH9s|kc;gO(b(L) zP9ASlDaUGy4|7%g$G$-im0sSdZnl%0{NmnqoH^=gJr{cxS8*fJi~pzvv9@B@tp4!0lKhpxh~I%ACmb`vLW>aBT1 z93@@cCm;&iTdUE4=*%SQVK$ej%4}8wJ<|tH3>ZG^;wk{i&p?haQQSppma_IJ47{iu zry2>0GGjqH&&phr{$GS7SJ&T(2mH!?1(-?VuY-0!AOpY+KQ2$_rSuvS@gq`_dXaMf z+CYeVfwiCbG~t#fln!$RODIj4&>G|jlR52ENI86MCw8Ykw0^;Dw=S`SNS?a%Od1@O zelrD65;8?r|0nMIe3)g1vof2Er)9=!Kuy+~_@XGRt-xwLaf5awjuNI1{#cT*86Vpa zgl&ryB!QStc4MN;Ps4@xG(`JQa!h3;Yxn&Xy*2uURvIfV$F;rzO*Md*>M30sWN>f_ z$(n@n6ZaJ`l-)(>+tnj{Rr59Wxf$nJ%QS)boq*n>mOJ8f$ z+;dfxK1diY*E`9%GnVCD-Y|-$+HEL*Gnm)F1uRITK|NB8DXCHjewDYYIbv{%`!AMJ zMni;qw52z}i5z$3yABwGF&(0M)fPWYtysBUI2k3iqsY?C#ayQ!BQ2qnqoq_%tRXWh zp&1r!apSzz+SN~#9y^sesE{ct5ZANlQAum+1kM<|KLG_!3ha$eo}Zb zC-JG++P^8@EFR&EiZVRV-K7Z1;9O4)>I&32HUh&rxCW)$;6WnwN>EuPpTOkS00(-s zsEqsy34}UG^q}CAtbNR&b_2^>B3LF-99{96h2_I};{4(=I0H$+UbScb!P;ZE>HE?p z-E^&)@#(~zL*8)@ky4z^8z`+|piWZ=_o#$)y&3liAb*?4w^pKwn$?F;`)!*I+imQtmqX?(M<< zMb2Oo%u;Jb_uG>@@4%V^H5(ad%hu^}r{{m&ern+bauo#E8|XE(jr;w(jm}MX>O$CW z7YT(M`3c3S+~ljUY+i{^7Cn_T#c%spY)oX5Cq8xKB!EPdD0%RPJF{AQltyptmNIcu z^9s$y)#I$R>diaU`3R8$)?by2uvA*eL?6{@b;dI8Lzz$+o6Uv+%NlW&Xtvz6Oc*2} z<;|iZHFfQZJyDko5lDS?dxlb)4n<-c7cW!}*^)%i#tM1s&u6_QEL`)l;I)0-#Gp8r zO@>NzPII+SmL(>u1=LHd;eQs973;DPvS<4^1XpIKeNoBCdEPG+&}YIVG_uX#W6y>8 zc_WJ=k)|3vbBb(w!l+d=?8-u3Bzm*!DX9+E`8d=o8=Ce})gxCMQLhSq7vX$cw&e$} zstL z7g!!d(+T<**pQmh@*|_pAE`Hzqu;;3z|{$q5n!r_?gqh|Jw@wZ*ZP%^BZvsIGa@PO ziE!=QV2;uG+u%7gm0-S>A_J;edANC;r=x(AgPPO#a%5Z*-O5+*dw$Gse7OgWMEYMc z2RIJs6$g0Uj!#N(Hvj2k+hSIY`zVESH`QMam1LoiZ^SyijpM?IH@I*24xy>GGMti! zP0xgSJULI1(Q-q1q&YVW(Qc*Y$JCR*)>Xw3v05lSIqx=?=|qxjCEp4xzL!q}5VnIp z8Q8lZNwrZ8mNjGX+T1xfTl`>R&3IDeuat%1iQV7R1;Y$6+!@`5&ChsnUKuDu>CwrF zn%u|1{7=3+IOU*~>W*hu6Gcr$jEW$5RZWL`m5P+j&tGNXtXmovdRQVwDYxnx6JQiG zN-tf6L`qYxbXqqDN`PrR$secOkIJPHZ+dnq8OPo?nJ8OcS>`zr(OU)X0W2l%9xPin zzlZ9kXk0IdmgB9((d-k~{~&@BA;?W$_6cyGuiMx#h7 z68tfQeSZ^9fMH{$Z%*?h%7cI$4X-LV5@3l}9o(rg65ZdSoy9_spmn4bt>HWk>456K zMy{)%R+RICnbCLz`q~W&rppemciq9o)DxdFeTUzZ<-GDael|e)^lD|GOXQ z`C>zVaNCu8B8ta1$y-J5dDmp{4ZOq7L*!%hg$Il&MC0f!E`K!c^q6FhsEf&SwHPs0 zimIV3o|L{8{Hkl1Rf62RlQy;B-#fb(QiB4QtH%cpN8)g9;joZm8DsvI*YSdQZz`EI zQsG(Yc^H?(i8KuFn3pr8WHxJhr~wnwAYzBy%O}FJIXI(gupO1`AeZ}-Aw$%^%gOFH z*2yq~k*iy#`Tx>3|u}q%qU$#330fCQh^gubAr{EsWLB z7}=MNFYnM}2guvECc@+{ISBL4uwc`sBbOSswRDhwrsuh?w=dg&I`TNLssVZ0uQJBD zG6=#TkV1q|jlyX(J5?1hb<%|n7BPY^`=)<7-r>dp$8xGT07MU9`*Eo|`>qG&ZPz$> z8lotU`#ErB9j~hbuOEB}=LpYJhJGWB4{)Y}JOa-Lq(Egs=Psndo6JzwLU1eVWsFQz z@(3V-M?u{ns6E$+uJ|2oJ_kh5YGqu?AdGr2Qam#2>tr?c^txs~hu6xZW#xG%=6fr0 z*BnN%ho^ReVU`zn$0TL-W%&L=|I=|o*IyT8?R)c!*4=VI1h4BBJ=^3I6HK`oYESK# z{cLS#;53pYGtwndaeG(s)~&e)ya)0W+gT)9lEzydyS}O@S#=&L=({d@9+OKEKbA~X5Jt`swZv9HN>j#R=pqukog)PF3cY4GWF0)1?A0^#|| zvx?wiA2T<_Qh`%0=jxhsuQfD-F*d_XbTM7n6`=~drdNimmqipWWxOS?KrbHu5`7BF z$}rO(rECN%Je4sv2O;4%lQTU+Qv-y}bXgc1B?W6Ud$TBFh9^&ZB#Fz!^kP?} z4Jux%k3qMv&H$v^??k-K>g?gBNMd=H0G925ZSEmO{PodFG}uglkb@2OqMrPC>``_LrP(z16J@M`n~}lxHoBQXm$kyCs|1M7 zcPZ1piHLkv0@wwWWvi15C?C9RD{D}D92uZEm%^CH7!?J2k{u|>9Nj%FmRRo!o`}Wiw?`OUuYlR_vBl`n2@*8xL}V_KH1@ z2SJ7c4K2LcPKBLKdR7k3CvQllJ10oc+L!_Bx=NVXU};}4)YlrBB1mKGO(OjwhGX-` zVGxi}ccx)|s=ngTp2f|sQ*l}Lg+E2C?0SAZZPlv6d6NkKjeTQ%$x*I8u^_GTpN40& z08hrDfKr9S*JU{ir@OguNVKDk1UJ$qT;h!zad z+2Up>U|Ps_Ta(ebfwAq;qBzv$9q#89eUw0WenS_`+M0r$DIi$V9tjNh;8WpM1<7_5 z@;tF_I^iH&|Ak)uw{$G_Be0ljDJTd|9N+FceM|^rRZgsCAY@v%GAO-QltlE+{noEx zG(LV#VbE%Bap=yw3Do*ngxsEi?UJbiHwCk*4JO+eDEi^?8d^)Isp*_37PsiWiDy<~A0Ahe zQj>_(Ht?lGsErtXUH||QWLQ2`ZSG8E!w;ly?*Ih5 z1}pj@ob)JyWlvcbxd50qgQtC&5~`4+U_K7-2}60YS1OmJEI=@P9RFN}`!flJ|K@IG zw_MboRc^uuCs-xqoO8Zy^5ZS)F^l3p2=9~q%TmO49OnEHpezxE`MP06_F~#Oe?2$N z4umQ`ej0?y$tjd(UKXvmBbJ3y0oKtZ%(m+i?MMvrnenf_p-=rZo{tv$t%~P;5{?9( zG>OG7k*H9BJ!)L)PU{O_3r8Y|CJbwK8!9BbG`K+fO1&_8q{+N>>`+mGr`8<{8ufY} zF((LKZ|8}lr6SJGqq~0c8W*xG8pn*tRo}LmSiUfK?^fJocomn>Ph zhmr&V5dRZMFt$O~0-ZMtOV_F#E-ODcnGky3m}K?Ex{D3* z`T(?{RVy`2+42MG>F7QeDfqUSX7xDqI0)(#>UgF&mE949-?3m#?lD_t9`J=VwS`1hcy{)P5!EOe~Fg-4iiGh+8=P zQ&_qkYEBPnD$xyI2o%R`1YDsTs|Ww`?B)C8Ty0&eWO*{Cxb^%nH$cX=aV!NY>I&bt z;z#WTT{88nGnru5wbC?x-!ujFZMTbk7o+ zUC#+~1l}jp6>et$ES~J-23bt7y!fG-{fu_PMF#?bFAgA{YeqSf84ak~^K1ku%eIEl zfm3PCLDvXEnen{=(wHI*&DpM7g9VM!A`OHN;yVT3_5gVdOtYNw zxLH6!9a+|Zn&ZKEhN+_}3oKNP2<2P!+}|nFe$7#?v|2#&vr(6bi+(riU=^;__)wvs z<~Wue_asp3WC95m!c}460w&O*nA6Wa9L$NLTFBcLX)syOtN(alEP4m(1NgNfmwzadnVvSIqr zc_qgltgPapWXo{hgjq8WHr+;DZ5BK2Tot41T(KPP!;ZkGf5%l{TEA$4Z%gPub_;A(c zjFDZF=Q3`2-m4wOR_}}@cx0?C<^@l1w^0Kglp0s4%fU3uAk5d?577?{pb1$d)KUwn zLl}Es?tY)ryM7p^mIYWlmop&M%xpeI_?SBHEEth4mg7C|Xb;bsvy&U7;LT#ah`qXaw`? zIAv-0`^BP2VemXx+Lo}XuokI?J{TLj-s+nQ9F-su<^>)1pY@st<0Iz6vdt#5HF38P zx)f=y=^eJi_k`iu{<5nkXKK+$LmFkIO@>M{57sFBc1Zc{smxY9FAFixYnlqTWTtZm zgX^k?TuX3c$-NC!PtwZtq!wtM{$lu_52|xg(PTx;O@Xf}ZLVM;1alK{kYL;RoY33^}vxy$_r=DHdM15a0kzpuR=oD z`oq$lVjOd-iSwC~say_%Xx(&Y036$i=y77w5oPhh<8@4RxRYX`xTUK6OBtdJ0jlPC zyjc0UR~)s?>4LYygow(8D1!hZu7pw;N^>`6WY~ANb=g)M($K0dvGnu5{f$m+9?DHRwrJn@;S8YcSiTp8Q~Vtw;r1UGaV{ zaS1d%c|O}|x|Lu!_#^o)b!?*#myI3=QAbk!LD5tPe?un9cX0@>STG( zqg}#Y7_(Doe|{1!`7tERI*EMKM8S2nF~W*wIoP1+5jm3TYoeS-MD3=wTCYn&18+a# zQc=6f%NV~Te839IVwze+$0s@*Z^`fXTg@tv5O4A`V&p1{Dqj+zO%&doRStV*w_N-> zL6lAxytcM=a&pdRkunG3Uc(03uuB^k=E{e@3~9KD%(fd9C*9q4iZHDC7E=gwEZZU} z#;C4WsvK4-YLUdVQ?u3dqY{}FG>rco{@Vz4uN!j$Ba|Qw&nJX&Mi3mclgaawBT$SI z%O*jeDM|byry>I?<#@&iO{Ban5;HvJiigxw@3b&-Di> z;&(X4N(`Gfl~XNen&VGp-U>TtimdkqodWn#`P$Y(u?E2siO*SXnXl=EaXL%W7ou0& zf82ZS%Qa}Zjndvo1%N~?SmPu)cZpn~YE0`OzWnvzsWPlPH3Hkv7{+L zczm{}gJj0S|j+N2gUj4j*p>N zAA46hi?}$~k+PNf=766iR(KuOH+V0X#B72=Sa!P!L|}l>MU1Zprwd9dY}d61zhav) z#B0C?v-5N*UQAx1>}B@>#Qi@3qCdORffR3qF6W*v!DWEh=vcq8;kf2*nC74i@5L11 zmMtuP-vV)sfI?P7rDr$}$5O~Fn{+T&jpxyo2mLIMfOu+7@JbIAc+ zY8hGE2JO>pb0uqD^tY&4lWS0N@Eb$X?Z^mHv5+jZ@IRxiEA-7;$X9CV=P$pL5Nz|$ zZ|!o}BK9YV9b%3`P}`KpKtsQtx@auIsFu%KzB$fl$`aGj=>$U}c^0kBSElNkk1h%Y zMJ$@vx(~pU*qPjH+B8fG}Re}aV+-f9Ix zk7HY)?Ff^s+L;_ocDIBg3mP+e@D@uFBh}&)vJswIF#q)e%e-{D?3AQsM;tB6;?zvi zlR%}Sbq7MgneYsGo-yFT5ufT@^$?kN#*`QVW^K>*Oe>;0qD6E!l$|`IK}79_3-BEj zKLvadL>H!Xgx^16mwWUx7U!Ds#4t}L3OkB>;bAM-_#K~Z+0v35MM#gMmdxw+FYIJv;A3-q0m-dP36D4z3fH0<*mt=tt z)AB%IsVJO1udNejo2SZL@Z^^rYpIM@_?5CAiOLTohNT}Y;i?;-vV|tGI}_94y&Mg1 zVU$kgG5t`TG9EnM!Lwm{RiCmc;Bvxpmsi=Ny3B5k^uPgE1@`C=A`!UwHn3~IEnrwk zu?UdECp`+u(f({L6SkHC3T5GP{0g5X`oQugREqV3!Fz7b&|oGi)qb!MA(t(pf@WuW zL^6F!LfJG%aw(>~e5@BpHH{6_i8{Ru-Ko8Wd9<{wyHcKSSsDL5a1%mAxe4uKV_C&* z+g6If6kBa1dHv|Q_Yv%Rk|8t!E5Xc59#jDp7LVdASYl=iyR&=ut6c8jH@vgq?d?E*RU4mpG#&pdOIopseA2nxwOTxBu=3DMS|F0tSk+N`apzRd@hw03&?}juKM&xSS5C$JF;888iu$QTD8xZvc9g46Ejm|C9&s zB4o*c@A-ovpy0~WXiLK)GBUx}G0B`jIAT}wfF5^dRU@Il$O>Fi(It)*3LRYWy3V#w zdR%&HUi;pQo!$e42jx^^CN2hl!Opgn;&JHVBL!ywAhqJ#I0z*X=I>uyWYtxTBLb>jtYoVtLyn4k_v@)DuCTW`k}59b6ea zME;o9%_B676?&Lbrvo|{#z0WS3QZ)l<}nNc z)F+kX$M~>*(GR~>890}#%=m2Uy*kBMY<59@!(1)JoV_2pw>YZt=m+`sMxNXCzMH4Z8 zxBoIx#zn+Rr;y1$FX3*9zodQ@sIyT054+vpn`o696?64=cBU@eNe#AyGvq8KFSu+U zwziJgntJ^n?ZGX#K}bm(wy5Hy-;qIau<5Xs%-9be`g)lQGK#D#N@`S|JgvZ+M zFuwBZarzvsXK9^hbLJsz2#++7H3N>Em^I?oX)H)JN8ACFxIuA{n!tU5j$ zxv);w)5yZ*b3DtVh-Qi7W|O_9IyZZ@k+ICo$oNA^YqJhKUN$;qCYC*rm#n&;dNjxz zjs$gZm~0_qZYfv}G|VCHh=$;jp0c7H)p^rE43?5$5>DWW*H*n9sd2;hk434fRHw%B zrO<l%p9P8Fv{N9Rlt4Kz_1nrAFE;g$mkq-s8t{!KSV50&?m%KA$BynjE!bRBjHO zgVjUGnwR_a#5HLc_k@ZBg%8dX{FeI|oG~l!vCDDhjXENFA-?h6)062*or!bFgZ#4f z_uW|;H13}L#x3_-3?UCNLMepdcX$pYq!e%&-NY2jb2%5=RFGg^j0s9OB=4s0N!aZ$ z=f%8NG|~Jq1}hIwd^R0szWCs<=TmY=2y&+mD2_pEG4mkWg4xDF02k%yHt3<3*&IF{ zUbv>>tLWR9COv@WsMQT!6G&z2k>4>WR7)u}M&sGe^O|CePsfQMn-IDlKgURPMw_W{ z)g;eS;}aZ-HFcC{%U$^XDqCvyj7A_wZqzbBa4|K`!6DtJQ3v_E1CWI<4tVafXr?=! zgd4XNA74}DV>c=HyEMwO>rqm0F!|o<^cZTPHz3ofUQ$Tss$$}FI^4~lkwfY+bXj}g zh{Y^>qKM4)jE;6|tZa+z8O90QNn?iq7UcZxL@++Xm+~8s#WCayz@2Ojr4TJ~FR)vFuVn&D66M^p=|a=|8GV}|(YsW(Fc_$w{d*rE zwmkphU@5HKa-}S0;kuY{4&L`ONmLP3wIy0~OdBixrsszPq}xW3_)M=`T81hafw#z7z{Un{~OU}QYfXQ1%3p9)93R0eh>SyHJkr(|i-qp|WJj8&el6Vfw%v1&hOKll50qN!PMhe5@4jF>DL%4p=CNYZkLm4qbNi1c zE)bMTSV!m6US~V>EF)e64wD4eMAPs+WJ&0)^}4fdcD17ehTHbf(yHEKPux=$IrEeO z#?lx&xdG?o$_6atzTY$cfv57zoBF*bjGY|VFv+j%V0}GjJ)UBr zi>VaFSDVL^!S};h>F}9Z`;y&?A0l`G6FNpf8&B3#bm4tPi2L67)pT21X3mgA( zyrC>{hyU0qc_TovRTH619oBLh0i|LkjsHBXhpOQtEmk$m=be-R|7+5@0(s{TBB;8Q-F;aVG9* z%#m22mCIUPgDtvYwtXqqj%`N^kBWP1u#*36XXH^(BbHN%@}|$?e@19OjSpdEFKOJ7 ziMbS{>?Q0}r_g3KAxs#S4cWzIueSQxg8Y5kqvbgmXC=03hBG;AsbKdp%(`mo_gKP_ zP$TX+Hr5Of`x2!!sA!s^NqLBdTZ%dyC(ZwPOd%7TK$uxnIH>r3!zfR-{k96n2U8seiRhd{XOzMIvR-XuRY$bw58r723Id zthLX}vdI^l0o(It>=N-n>@_7vUAhE}mYmo!K$UmwLv%2t)Y#y+*dKX5HHYy>3DJEk z)ICj7KIK;oX^{mQQ={H=kEuB|pvEF`ke*BJ&GWF5w(XYrdgp*<+P2Z7HaZhc#t>4S zNf`@jN>rxHnG8ZEsYNyFPaC^VtTCt*9`6r^R7;J<`S*Iz*W4oSag1E8Nc!>vLnBqk zy$2(G((`#-CP%J(nG}<)i!u;qTN%{UxUN}~RrM3iSvp0`h%Sf0LQRmCFjWy;aR3Wr zu##z3)dN@*OO{REMExn=m;&FypQfhkD5`Oh7H3Ac%c_zNim(L&fCO`DMTmBXYH=>& zGS@+@vru%($|<--<@K6^Mpo8uvCYzbXj!*RSnq1&d?;?Z-o+C}X3>^2+DP{BiaZzK zS7;)=HEd?yN(AMo_oH(97;22k>}758b`R<3%|9TdP71kJ08t*!cv6aDFwRj~djKt~ z#U4yGm-^BWRQ*8OFHF4@tehm;u!j>*;e&t6j9=uTvF% z1l+W{!+a8CvY~rO`ucH3NCz?%?dpLKF|acw&j0@qa}2&;&!ktdNI;CnS|Wdfe3GFE z!mKJY=<__StXml)wMOC$>&llbqR%z_6w-%CiFLLH$^uq&p9wkikKvjqJI~qprJ>gX z`6qn6uCeZ%?$!`l+N0!M+d{`{5P=1W;gNP?{jKp#>@lR}(A_(Z3qX-lJ%>NYZ|{{> z=w^1B@^?eo=`oUC^a99iy%zsoGu}27QUV}4rNm1eD_=6%+iIs=i zig=0Ut(aV=$YKZS{4$$(ko+FOGq3rZNOOkwaA8-)&Owx*5jSm4Lp{{K2S$sP%{K~$-;RoWEj<@d@%881C~6_{98I}L(&s+iACCR`L`rz zSN;?FtBobPS``s&L*@)o1tAV$gS#T7k~&6@XRMQu6mBv}v5iRuN#P=s6z3{cSFXpk zjkIH4ewIW6NNjm!!Dw}irg{?R8KCRQuS1m6s2(UPLG z?j4gwR`AP|1ij87EJ_54=-?yk`4yAKmW?@{dRqGa()#w|uvt%R;rktdBsIAnkrQLq z|KA_z+XllBLCg%>_5HKMx|f8zk|$M|kI91a>zGT$_)+H{F_-0$ElW=IF$_X?UbN_I zA8D#^siD#EoqjT*Ve@fyczLVTEY;vxW%UYv)6eq}uTWTJyQ+12a#4j0a8402)CDN0TeUW>E3cs>!$ zF1{LP>U+-VPP~&KpsC-m-Gc1K9Jepb&OR8X*aekpbkU$bjFaQ$2D#yra#_FaGdb=j zJh8?B-J3iP2-xO+MK@<o(F zH4M@pMe5tdGblEqNzVaUi_MCde>_uHLwu+J{yALsAVC*3&7=PiqWM2yndb+t>)z}6 zypN=!jxUb5fJ$0#z9yI{M~=A)_=#FLz+#(4%>rYK9*7@CyYp#rL{Pb5-?V3}q>w-^ zUmRF6H`F#DstHai!Oj#z$woz47uk31wdz?Al~nE+K{0aJW4mh4+>hymUC)F#9$FN( zcb|b7DY0eR+L1G*F!(U9%bASFQtv`GI(Wgmae4#mcOLTOp+o2k(fT?T>Srvz4l31G zH=t`fy~x^;jr&=$mA+|dQfb2~7LgEpaS4T`y*Hu}sPWPXNt$l5=yg^k-9#Lp?$+Fc z`zi&zo}Dr+D%T>m7EIMjJQWhzwAgR*#SK@-DbyG#5mHcWL&yJ9@9mO(X0jIxmRy9& z5;0mN_o=LF1hAq$<~T^q1t% zX!6n0PJZv?M*ejz3tK0(lSoUy!ctuLvfE7v{mE$hoF+}Am z$zE7xhz=(%Inm73wpe491Cfk0VEJkjvmJ?@DNP`2HWIa-kXad4n%1S^tr=juTQ8H&<57 zbeMj`^mS^`b(A75a8#s)C!#aET2oD45oOQGBGtr$(01x-Blps@lZMSBeL-iM3w@7+ zh;`^B>*8dVbf#|7ZX4dMTS1Nx9N6`e&UPu*`;{aYyUrMVI>~D;(LqxxI`w&5^DmN{ zFRUcO4$_lDmOP61{VWND%7FAjkwoH#3yRb?6v7~prIvMKeG)wf5gZ{)6Av*s@@Lo; z3x^7KJoiJgcxmmgmLel^(~N32Uhe+lu2$&$T^MywmD&%C&Zym{Qh&f02-M=>D=vTf zI9J?of;cX@buW2zd~$4KmO2-8yw|{n0Z$3Vb&b%(F6R>2%_LD{-0!m7H)LIMjAdF~u6$Ud^#rL9Fl66w!)2$8o$U$vhrEL-y;3CyX1k zgBiD@1Qa6sqTB>b@Vy6)l?E zbHiVAI7*d&{t`$V8q*E3jyw;Us(d-p^im&BI@-linyW$rn#YG(GbhY zmd`(+VV;+HVn^kE*MLT@FpR|`Et5m%=CD(r8dkImwQg*a5QEqHVaoHQn?{da>w*~z zmL0sd5KSKM_bIuMnMFt_$@UuPf{8f(R%kK8Y#ELW-#4tHfNuEy(?tk%co2p@uiANo z@`iV%_N`p_&1p|=t((zMv&zBk0NyZ>!=4D}%9zhrvOps}1|Z$H&EnD5c<3)0{htTx^Y;wXnO|Btj1-4(=s(`e#9QIOBYQv@l+Vz%f=v;W4@ zfCSk(YF;XW)@qDXy2fJN5oGYU3=!~Iuo6Z?Ta@KI;b{}f-4rZzz3_1APD#}n7A7=;iBFy4N*J`1w^vZx^-ej|FUbC z0u_MmeoEWQNA`xzR6;q+@US&p+v$mkL8m!r!S-y$fO!VyCUC8)4o%URQ>p4?tj+jG zYuAYb`%>#)`$brF_f%R^hFN6f+&-x!bCM|?IZj;QM$oan1FEN|1A`*bxr`2bA1~Nt zbAl&_ik%j<#)GeRP*P%zld)G@L+NAWdW*gBE|#a*d52DxC$%vLwVRwt#Zu?WtQV@G zB*fwl;G<{+)SK@fAA_ZM^D+Kw2W{-s z0knAJ?x6Ml^9DX^v+U*(mB4n1mnSKvN~L4Ltr0nUJ_w~P{}tsHeH#4IXn-SOn92o) zz?Q##1~s+AVz6diI5cSQE7l}C#*I=VyOJH!uv)hK%=zns+-g*NVG;3&fnu!Whu5gz z;<{?{7ikFbC>Di^mn`x=(bKejjl@2vRcmq%Ee_&XuLilcO+ABOew>W_W&he94TkSE zQ&2t|+MRV0KT(jNy^H)(+Pa}%*8odEw7&zVgk;RL7hfOoZpotAIKDhQ zwoCd@^bmustZ48tA+?MdPVquaFl2kr1^Zv~yk9ZWMbsOK1RrnJ7{C~Vo(Fi>(2Mxs>4`+(BIK9qG~A4YLX z_X?Jx_H9HdP8T79HNjjWlHM4N6oMFAfdXw8^U3}mlL^?29B^wW>mi&SG4>%g+!AgsJ14v|8a-K+X@(AZ)iv-%C>&;1zYJ0Ko|IUJ|B2@Hz+~!1I>MqXoUF16zca$9ek8 z53l9~dhaxVn6e;r7k4luu;GudVtfvh10|ckhY^Ztead7^aLY(mUuHo9=-cHpD4x60 zxAJ6~+^wW(U_1u{ca3D_B_?~ z;?Mg0bv;+7p2s{}1k-L`yPHH0EgBDOgCJB9GhfF~=jjXHl=AhzewF)|bPnE(7HZXV z-!Cg=_g)Hdi?pT;b!@!f$QW1Bj8ZU1O_f>4?2`9>t~JP^Rol4^i06<3v|2=Wa6#1S zZIWrCR7W|4w`BDZQ97t)cU9?1Z7!GAirrH>OW=s_oiHtrR19wcL}N%4#X5xFxua;- zAst(kjmmz9WSnV76ol(ggFA|}N~xDc!TWgLHe?m>IdPrYJj20ZPQH}(p2Lpgm}@w3 zN=qsze!@Z*{;HaVfimM*R#H+2pN$>Sq@){EfB9*s%rj4%Ucjrx>yAWOR^8X6sU$v~ zE>e^}eGLTmE2H6CJah{U7e`q=7(1#v?ddt0)n=sBUwnFBsfW<5_@nwt20Kw8HV2lW z+#6FAq6yKhD=@%T6hL~r;Uc(5wF%v{j2DP@`>zuu0hTpnmU2>_syXtJZk&v$V>VKH z1M5&+yr>C~h~{(Y4>&0=s%Q^6^5h5?sVXf9WR}RS#NMkqOSpILR*vwG* z1BMGpkOauNzDsV^EBDmP(kBKZxb?syM_(DPh?UuQq!sJ21z_@Rs11Xrm~Pu5L(D#bn_4?z@t`;f}_B(7(a}j1wd)x zqg-3sM7W#>WhqJ>ASEv$A>gSI$`%KKKBtb5l9y0X)?+G^^dVBKHAQv)F;a1Nbciy7 zZ9oH(+L5keXbOOgN}~u|oP!hI&_Oj|iuJc@#=w*mNB8fZapZ|GIti4NSYORczd8TM zLDA)_lL2iFo(Bql-LZ1h1K>)VHL2NJ!iwKS`-ZZb8NVX@S7}^dQb$V3K@^odNSB^j zmCKDMb*Pkl6}1&3T{{0*DKp(V|6nP5rlW2fXwXu3DwZ$aMavYnjq+fuZ#Jb9Byx;BGjm!LeW_DRw3WG>)dJMY5lza}UCm4m z8{X@Fj-KYt01S47re1$MhGe*3UWZEc3?6reB^6q*r7YI{1qH#z3_2R_0Lp}xKmx~d|th}WEf~+KmeXPS=GWZK~A(r zZR8idibwB5pkNm{Gg4@vBp%#zHx^WsF!yBowfN2~iB67F`U~YD$`3c>Nu-I#+>2!| zgdJ~KfFs!}tnF{261o^enqip31`D?b?af_orJ=_BDW0ww{9@YsA`%;|HoqSwTf~hN zRb-My%^Q63e)VAr-1L@Fg-s9d>`bXC={CNubWj!&>!A!@4M zixxDBegGIMO?OU9d9f+$zdJtq36len(khS5vlHGcNz8nnP;gZ7V%bs>N?&_?qq5Wi zj&my8hB^-#1~tcLZ&S+Y`LW;y_%P8ekR(&N4^SY2iYKzE+$E%itMH zCn1R_XPofme6ml-9>n5!pHdX~IcZn>9;YMXZXHO+g5(f`x5T>;a3T?$$D-dSp9fl@*}%roj3G zP$8h;`0MSI{|-bfg^@?0RX4?`%A=FdBQ`TPTIADw!-BNl)}!A4|5w@N>2h6h{~Av5 z7W^0r#?nDjbk^V6^(xPilS=ZM_KE%B?$)H#T%c%lRi%A)k4syy)uRhU7V*CEZ zei7IAeTCJ0#u;7}>rP**I8~sQTs6`dTV`8!m3#?l#9!sUbx12+yCvIPR3`K3w)86b zGHN5(*0hoh_S(IM(BvtX6!=wm<0lWblc-(fepJYfEK-pSLBCAZmS81cLhZ!0wN2*|cTQT^^*;o>N~J#kv* zQuX`|X)-Kf-M_=5u;6*QelTw+0%Az9#y1xBo-`~7;(7tseHftd5SBG-=}y>*bOuJQ zGr&<0Q7PFpVcA7SssD0abNw#V>WmzNkTRGLpWNp-b?@Q1L;5TZ*Rq1CCDD9!61Ct0qzg3mpP^oMIOO!TAo+C?I zYN1=f8D73Qv_{v)x4QQoz1D_Y)nddIoZ;7Qh7kapXDxAc3yFZCZIz z%$09W<}vPbt2jRD>XO?)29ddWfK$mqS1rC7qu?@t>@M8vPUEnvyUNk@s>GEKuxOO&-xCFr^m+ zBM#PREi=ZS^4BRvCF8z&vf=`aU|G^tM454w>7m>!9&+}!Xwr&AnYhOg?R+lRRm2tiL?wX{wnZiRl>C04;xxys)jrG>UD0-|&&VsI+U>*c z;kg&aEqo<}pk{mFbuELpx){GEldk@fEuM(VY~!0Bk!vGA7JK6Ye<#64IC*siPX5HI zV?AFx87dBYHNea$_^>QZ!3pJDISm2=Zd4`0q#*MrP4c_2x0YL&xg(errP?HvPE*f= zyLD@d!@_S$8`{l9da~AARATG%T2RWto84mNs4OI}D^$}rMB!~6DB>Ot1O_OAYUBS8 zZb07dW^%wi!zH-G-(xnxu7f7`jxSQW6Ho606yx6JTP0?D6SDqkMIus^Q4o5eg+;e` zkEe9c6=Fo<%e29|bMTEDcx{L<(K0T#RZc@aG%)nA0 zlHtU?jp;x7U)!yzFsg33ALLNukPy6%QwAGa;Brm^qbK~twc%{Zhsw~tW{eUsL{wv1 zB^S2+9Q8OOEq)^g(;FA3%TE=66QtBS$O>E1GRk2hzhp=qOW+&EdR z7=^`%rA|x{wy(I#0>wpaCE^Iu#M1&ngtVx9+E6kU+gG%NgFG$NM|ad1(`OttN5~Zp zSLnb*40^wAUBe?mrhPPu>hv>7XT}|ENw>cRqR#s$zIHsglBc*RSv*4Pmwe1l_x$N0 z#a2NgiHpe}e64&G7vdLhVWNN(-jI#wBUUc_OR|=&b7P=?|7IfJGsK~=t$N;)vV-}R zYset4U%OM?I|7-1@0}hz&!YA79j6e+Y7AOKhtwZJRtz0jm22Hxi5Z-8?TgNt6v>kl ztdqr}bk;A8M@g1wC8tNd`h6NuK#@I%FRptSy}qDHmI842#1Q(GtSA>D;=JjaLBS25 z8ND-b*Use=qQ!bPeC8zc*(3dD{m!$38`b~P5dNOf$H3qM!cspWTH5Gtzn-pk^c$TB zvP}j*?BzNLRFjzzfFeLJEe+GKAfBwq6jpC$29P zo?@)HobAlRJJ1%f=QikkC>o_TloNEuV(en7>A{=_&1};=l?SfvA1d>nYwCoSwK++2 zQ#k7*Zt~>D^N*7xW+ODA8YXT_OjsU0_TGQSqiMJ-LY0R~%n7QT6h8 zRkF2h9X$MM^`<|~;7<=XC9$^e5Gk@}Su$T1 zv{eD%9!`@ljU1=-10;f7vHBGCDVM*-_@?OQHu#>`rPLX$f{^GT4bfUQyWT|SMx*vt z*B>f#(J>^}_6WTl#y#mYO6@%on|J+R1p?4B>k_cD2+-Jdm~-_8*zhS{rovU)IZT51_8 zy?!u3(z6>V*-ZweUv{Z-z?$RGh}jPrkgy&)j$HuIj&M~1x-blX*V_-|?&|N#^#cS) zmcN5T0URj@iwek6WD`^#%dFtPbH(X~%>#CUQw-n)ly(b)`^(6{6XJ8oavg-b9+N>a zKq;rCKbb*=RE_gX-3>#n1d>J!?wP47;Pn7x%cAH^Zx(oz)(N)vyH10>^0v=Piujs@w2B@H5e@l)DJ|}xid~4VH3q9nh>s# zJB-*SH!8%;D20$Re=i7Bs6L^opp)^Gi$;%Dx#Y8PkwFV1*x<}Pkz=FGvaAoaz%2#{ ztuWAiFd9KyJqCi(mX$u-c;C5mNKT`w026cI9v-cf4&^2CFo(f$oZ% zCVT_Qhh0Y;b_jkbV@q$6Wz}+;`xUvU9{zDi{v0_!bEJ7+?~w^zSd|`iM;IHchLfo( zC2(n_1SUwSi~ie8w!Rilr0rn$CkhirC^qs5hvVxwrOOokbPS{C9c{sVM|;w>@6i65 zwjYMNJPsqgt#GWJ0#kuaO!@fP-ZCqc<7s_8JENa)7?#W|<6es)+ID5cwN|+#kqh;o zOx*}~{#Et5B@dMFE1$Lc#lOD@4^v3>&&7Yck&xTy?bSe=1XsBTUoF?+06)r*G3qIL zuxfRG_CoQy^0g3@Nt_e`dIfXuJ!_gBA}rV(=+wE-T4<=ys+R8~Rk)Hafq-kw?h|EJ zt}Yuh;7-oBMSjkh0y9Mvd9$OGC+c~VE2VY|K*`2tZlB5wN#`SL;<9|OU^fE=(sQCt zh2LZ+IIm&8hnOLAoyN~h=$x4v3+Es~-|{2H0U;`M(kuafxIgDrUI~gi5OMFAj|CSj zJTHf3bINfQm(Cq4Mb|Q()k0^iQ8Y4>ACGaKS3Na&LyHN%iKZZF#4}sG zVPlz`X8?$U6=d*L=Goo{EGt>GN2inR8H`g3>0J~;L&GM49}KYC;qZeAW_Vq3A>!V_ z>Kah~qo!F}`4>Jstb9L#l(I$3mIvQW;U`19PkCzL-cw-@&wp)9=Jt$$ga1eOI#bj! zi*s-k4*{_$vhznUh1$Ld5UOA#AC_R-q}k3AbKM)K(6w!=6>nD6$ttpuV?B|craWTH zO{_H>JeHhiqN}zBSnECT*npg0;@nnj)!~NqZU_%0=am?I(y<^|>qhV`0J48c$|upg zRM+f8(hz1GMo`v<9La@SaZ3n)x7mWeTPY)1cPs8ygxW09i5P4;(>{`|U68H575o$Q z$E~0J2o8ewlgPT*G~;ablT>e#c5x$8LLDG zRE*LBu(I7X9*-=+V6U!+{F%%BnJTAMaDGX-7A}+X6ai(j52emJfE5Y2Sgh27sYdCN zM$yO6iy-{WBw5VJAI1=A&I#m1!HVy$X8S}rrFmJtnIpe%gBRt(@)(+=6Fo~PiZ}>- zd;(R9S# zaj1m-R*9-~q9$RoXs%z3ym7)Z;O!9_iH_Utzz1K#`|=TW6dk_{&IEF;ib~$2pE4L1 zxb8{7vc-z0{SZ6!z$YuB_Kt-!NVj6T4 zDe97;kyVT2pfXQ`h@_h)lr%^wJCo$gL^D-Fv1h$EIRBumwyK`j$1LFc zK*kj>!o@cJQJKM?FBl!+3Ti0CjT-zZ!FV^7smW?>wAVoq>3mclsDFJRnfat$QklLBNdX$ z3^9FriYNQ5DS6!N8FGhO_eM;i1XX;JSbto-la%7{D^=*$D~@?9A4D>v0fCh9R)TamWJ@4gxKMnNI|m-adeW?C&Np5 zS^jFFN`M*C&UK(K9C>3*)3V_~GKKgIHmDnoS6q7D%L3P;h;02iO_-D@d-d)bpfwCU zf&K2r+^_5PBaJQGL8IuG6@Pvy=$d<@1tfpH^(I34qK>Q~!^{rN1PC;>!o6yVOlJ}P z#38SY#4sl!ka82K^(r>s9!gRVM$Y-&Xj;OJhDT)(*%92MOVcb`aa3BXC*+_gkj#+L zKsInSNyX_*{~-#I+-@IbYQ5^Yl1M&?3#(F1sigUrx|MiRy6LYugtm2S-FFO5uo~QO zmW0QvUgq|!KH|I}EEyEH9SbMl7s~{DTwK#o)^+JwB2*5$wXd3~R^0%HOKu^sFH%Sk zs?@IlQ?Sp3VXTS$&mwyIR?-C!U_Jos!JchQ zp@oLv#&RYJ5J3$-A^Nm~#!#`1C`a}-wz0iGqTEQb+&>rVx)m*`N(kh6Kt0SpDw3ue z`c<~axjnz0HdV_|PT`{G`1ROVwMEl0RHK1FGcr9=&h zsg@N%h3MRr3h}6t$c>UAAd@e&b-J>@pI{5yxyhk<*?Ysoj3VW44krOtJdg_qn?InC zBEyi8y)L;u$=d)ROIB;uhoJQ>GzwhHxEZiqKc2pwecckIWc}D@LBM<-i(r`vM%9_m`Zk~DU)`A*YdOw38sV{SWWLcx zyNC1_QX?ZYejOt!iYnK)b)j4>SK?DwdL4SkO@hhTfc)49eD7jIn>IAiQ`3mVXOloL zj5dXJDa^+dHGB{OQq31~x5D_Z0mxV5PoX*OrL(KKP{D+5g!a~kY=yOa2HHma5HGd! zN22!~od-*H*I=^h2Ro4rG8@9wiy7PJq$)AXa?aFa@wvp1rJ|69N> z6&JN-IP+3*SzDAZZ?_`3Wl%rewJPak_@`|5EK94Nxe38bx2(=Z zP~%TqO`ikbF98OKa1CG889|~3a#s<6Y*!91dT_l(pO9?JRryM~cQr?GJ|6b9TqgC& zk*x{Z?O$Ikb}pCf>&Ny@>EB<>1|#UkcUekyA@^CL=2HWB(?pnu7Pl1SO9Q*nZwAk& zo@e;t7*U@cH{(YDEWHtCU2=wXsP{bN`Z$1ll+019`ik*DtzFhyY@`5>H0nf|00sm< zuz!@(@VIv8t5#WY%4v=JEF<2G*4t-%dRM@4U1X4>Y$$5Q^op?K`3VK!b_pT3=h-?V z8e?2IOt(Gn@~~_}fv`Md!iIR=!vAbh$FVeHauH^tWN1&)1~~m!3JL^E9#4O8z3uZe z8O1FQm-0<3!-YsBb>Tq@lipc#yalm^s}H4ggESJ~cXSDtmvI)#WTi%4i1~^lp9>D9 z&>ve((y!Gg`$!&57H=vaIqRdctoQRLtD7w2H7v!VPFCNoS$4f^;?`3r&=%PwF6d@H zRjkL6zR*w`ZvYGh3ZG<1n}A=i1Q)3Q}Kl9GXZ^-M93$mIcgiHaxo< z)m@hxwiiaxdA%OyZv4}dOT&-t=d{TcJI)+%$g+w3+~ti;S?vSDu_#+%jIhbaR_ybR zsQZ}7ZF|9wnI5EnuHy%2>1qSKR|0MoE($CeITSUSoJ~hbk*5iQu^Z_Q!EO6Z2t{B_ z>7h@qLctyotgA`y3`{6l2&RXV<4iny;>rn#*)h$;=!j7+4<)6OJT zpMB@`Ha~nC-ig7+Qjn^MpRI?AYd2O-iP9dgl0B28+@N-bKao%w1E^e9R!1)O^R1Fy zV3 zJ&RN%f{ehzSCQd6%tABu9&lB)Wlh>v*BMBdJy~^Zf)S+FRi6}+yfa% zA6{!a+Do1B%)gpfUn`Pg*E$y25ZP%5uy@j5;!%}XPd$y&PWd+`ZPHmO-|mF3P0)jj zZ0*|_eY(h^-))Dr1JtrT@>Paa!s=R8W_ovv$D5!_Cb;X!EIwe+2n}9g)HH5w7wCeK-Fi;0YKdnKw4d>#bxB@Ex>BhDGBC`&B(cizD&{DLs&pf$A2&!sL#<^cX(grYc-`V�Z%eqY(cS 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