From 7ff3e92cd238127d58b867ca1100c9671891745c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= Date: Tue, 30 Jul 2024 17:35:05 +0900 Subject: [PATCH 01/43] =?UTF-8?q?chore:=20hdesign=20v0.1.37=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Soyeon Choe --- HDesign/package-lock.json | 4 ++-- HDesign/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index 916bd1ae5..e4937badb 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -1,12 +1,12 @@ { "name": "haengdong-design", - "version": "0.1.36", + "version": "0.1.37", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "haengdong-design", - "version": "0.1.36", + "version": "0.1.37", "license": "ISC", "dependencies": { "@emotion/react": "^11.11.4", diff --git a/HDesign/package.json b/HDesign/package.json index a10d83b7b..35d4abcf5 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -1,6 +1,6 @@ { "name": "haengdong-design", - "version": "0.1.36", + "version": "0.1.37", "description": "", "main": "./dist/index.js", "module": "./dist/index.js", From add91cacf8198e1acb2d8bf0e1de3bb53a8af0f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= Date: Tue, 30 Jul 2024 17:35:37 +0900 Subject: [PATCH 02/43] =?UTF-8?q?remove:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Modal/Modal.style.ts | 23 ---------------------- 1 file changed, 23 deletions(-) delete mode 100644 client/src/components/Modal/Modal.style.ts diff --git a/client/src/components/Modal/Modal.style.ts b/client/src/components/Modal/Modal.style.ts deleted file mode 100644 index 727d0482f..000000000 --- a/client/src/components/Modal/Modal.style.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {css} from '@emotion/react'; - -export const modalBodyStyle = css` - z-index: 100; - background-color: white; - position: fixed; - bottom: 0; - left: 0; - right: 0; - padding: 20px; - border-radius: 20px 20px 0 0; - box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); -`; - -export const modalBackdropStyle = css` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - z-index: 99; -`; From d1570e9139834513c278ba0cce15d12dfceba6fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= Date: Wed, 31 Jul 2024 16:51:51 +0900 Subject: [PATCH 03/43] =?UTF-8?q?feat:=20v0.1.44=20=EB=B0=B0=ED=8F=AC=20in?= =?UTF-8?q?put=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Soyeon Choe --- HDesign/package-lock.json | 4 +-- HDesign/package.json | 2 +- HDesign/src/components/Input/Input.style.ts | 2 +- HDesign/src/components/Input/Input.tsx | 19 ++++++++---- HDesign/src/components/Input/useInput.ts | 34 ++++++++++++++++++--- HDesign/src/index.tsx | 4 +++ 6 files changed, 50 insertions(+), 15 deletions(-) diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index e4937badb..ae8935320 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -1,12 +1,12 @@ { "name": "haengdong-design", - "version": "0.1.37", + "version": "0.1.44", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "haengdong-design", - "version": "0.1.37", + "version": "0.1.44", "license": "ISC", "dependencies": { "@emotion/react": "^11.11.4", diff --git a/HDesign/package.json b/HDesign/package.json index 35d4abcf5..018c411b1 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -1,6 +1,6 @@ { "name": "haengdong-design", - "version": "0.1.37", + "version": "0.1.44", "description": "", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/HDesign/src/components/Input/Input.style.ts b/HDesign/src/components/Input/Input.style.ts index 285908bab..8b8854865 100644 --- a/HDesign/src/components/Input/Input.style.ts +++ b/HDesign/src/components/Input/Input.style.ts @@ -26,7 +26,7 @@ export const inputBoxStyle = ( css({ display: 'flex', justifyContent: 'space-between', - + gap: '1rem', padding: '0.75rem 1rem', borderRadius: '1rem', backgroundColor: inputBoxBackgroundColorByInputType(theme, inputType), diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index 81f0f1c93..64ff98cdf 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @emotion/react */ -import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; +import React, {forwardRef, useImperativeHandle, useRef} from 'react'; import IconButton from '@components/IconButton/IconButton'; import {InputProps} from '@components/Input/Input.type'; @@ -9,7 +9,7 @@ import {useInput} from '@components/Input/useInput'; import {useTheme} from '@theme/HDesignProvider'; export const Input: React.FC = forwardRef(function Input( - {value: propsValue, onChange, inputType, isError, ...htmlProps}: InputProps, + {value: propsValue, onChange, onFocus, onBlur, inputType, isError, ...htmlProps}: InputProps, ref, ) { const {theme} = useTheme(); @@ -17,7 +17,13 @@ export const Input: React.FC = forwardRef inputRef.current!); - const {value, hasFocus, handleChange, handleClickDelete, toggleFocus} = useInput({propsValue, onChange, inputRef}); + const {value, hasFocus, handleChange, handleClickDelete, handleBlur, handleFocus, handleKeyDown} = useInput({ + propsValue, + onChange, + onFocus, + onBlur, + inputRef, + }); return (
@@ -26,11 +32,12 @@ export const Input: React.FC = forwardRef - {value && hasFocus && } + {value && hasFocus && }
); }); diff --git a/HDesign/src/components/Input/useInput.ts b/HDesign/src/components/Input/useInput.ts index 5b37d275a..fd5e0bf04 100644 --- a/HDesign/src/components/Input/useInput.ts +++ b/HDesign/src/components/Input/useInput.ts @@ -3,10 +3,12 @@ import {RefObject, useEffect, useState} from 'react'; interface UseInputProps { propsValue: T; onChange?: (e: React.ChangeEvent) => void; + onFocus?: (event: React.FocusEvent) => void; + onBlur?: (event: React.FocusEvent) => void; inputRef: RefObject; } -export const useInput = ({propsValue, onChange, inputRef}: UseInputProps) => { +export const useInput = ({propsValue, onChange, onBlur, onFocus, inputRef}: UseInputProps) => { const [value, setValue] = useState(propsValue || ''); const [hasFocus, setHasFocus] = useState(false); @@ -27,15 +29,37 @@ export const useInput = ({propsValue, onChange, inputRef}: UseInputProps) }; const handleChange = (e: React.ChangeEvent) => { - setValue(e.target.value); if (onChange) { onChange(e); } + setValue(e.target.value); + }; + + const handleFocus = (event?: React.FocusEvent) => { + if (onFocus) { + onFocus(event!); + } + setHasFocus(true); }; - const toggleFocus = () => { - setHasFocus(!hasFocus); + const handleBlur = (event?: React.FocusEvent) => { + setHasFocus(false); + if (onBlur) { + onBlur(event!); + } + }; + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.nativeEvent.isComposing) { + event.stopPropagation(); + } + if (event.key === 'Enter' || event.key === 'Escape') { + handleBlur(); + if (inputRef.current) { + inputRef.current.blur(); + } + } }; - return {value, hasFocus, handleChange, handleClickDelete, toggleFocus}; + return {value, hasFocus, handleChange, handleClickDelete, handleFocus, handleBlur, handleKeyDown}; }; diff --git a/HDesign/src/index.tsx b/HDesign/src/index.tsx index 9cac56e5d..909bef23f 100644 --- a/HDesign/src/index.tsx +++ b/HDesign/src/index.tsx @@ -8,6 +8,7 @@ import Flex from '@components/Flex/Flex'; import IconButton from '@components/IconButton/IconButton'; import InOutItem from '@components/InOutItem/InOutItem'; import Input from '@components/Input/Input'; +import LabelInput from '@components/LabelInput/LabelInput'; import Search from '@components/Search/Search'; import StepItem from '@components/StepItem/StepItem'; import Switch from '@components/Switch/Switch'; @@ -15,6 +16,7 @@ import Tabs from '@/components/Tabs/Tabs'; import Text from '@components/Text/Text'; import TextButton from '@components/TextButton/TextButton'; import Title from '@components/Title/Title'; +import Toast from '@components/Toast/Toast'; import TopNav from '@components/TopNav/TopNav'; import {MainLayout} from '@layouts/MainLayout'; @@ -37,6 +39,7 @@ export { IconButton, InOutItem, Input, + LabelInput, Search, StepItem, Switch, @@ -45,6 +48,7 @@ export { Text, TextButton, Title, + Toast, TopNav, MainLayout, ContentLayout, From 7c3f5f70aa82ce8a82fc4d89e9fa7aeb489ae6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= Date: Wed, 31 Jul 2024 17:13:54 +0900 Subject: [PATCH 04/43] =?UTF-8?q?refactor:=20event/create=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20input=20=EC=9E=91=EB=8F=99=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20input=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 233 +++++++++++++++++- client/package.json | 4 +- .../UpdateParticipants.tsx | 2 +- .../SetInitialParticipants.tsx | 2 +- client/src/constants/errorMessage.ts | 5 + client/src/constants/regExp.ts | 1 + ...dditionalInput.tsx => useDynamicInput.tsx} | 0 client/src/hooks/useInput/useInput.ts | 32 +++ client/src/pages/Create/Name.tsx | 42 +++- client/src/pages/Main/Main.tsx | 2 - .../src/utils/validate/validateEventName.ts | 11 + .../src/utils/validate/validateResultType.ts | 4 + 12 files changed, 320 insertions(+), 18 deletions(-) create mode 100644 client/src/constants/errorMessage.ts create mode 100644 client/src/constants/regExp.ts rename client/src/hooks/{useDynamicAdditionalInput.tsx => useDynamicInput.tsx} (100%) create mode 100644 client/src/hooks/useInput/useInput.ts create mode 100644 client/src/utils/validate/validateEventName.ts create mode 100644 client/src/utils/validate/validateResultType.ts diff --git a/client/package-lock.json b/client/package-lock.json index 91269318a..96a137d38 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,7 +12,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.35", + "haengdong-design": "^0.1.44", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" @@ -2317,6 +2317,18 @@ "node": ">=14.0.0" } }, + "node_modules/@storybook/addon-webpack5-compiler-swc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@storybook/addon-webpack5-compiler-swc/-/addon-webpack5-compiler-swc-1.0.5.tgz", + "integrity": "sha512-1NlM3noit2vA22OyWb8Ma2lhcEKCS1Snv2kr+EkaVABUqNDfVc9AD/GgYQhF7F/2CoF5N2JU7uzXDzFHd5TzZg==", + "dependencies": { + "@swc/core": "^1.7.3", + "swc-loader": "^0.2.3" + }, + "engines": { + "node": ">=18" + } + }, "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", @@ -2560,6 +2572,206 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@swc/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.3.tgz", + "integrity": "sha512-HHAlbXjWI6Kl9JmmUW1LSygT1YbblXgj2UvvDzMkTBPRzYMhW6xchxdO8HbtMPtFYRt/EQq9u1z7j4ttRSrFsA==", + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.7.3", + "@swc/core-darwin-x64": "1.7.3", + "@swc/core-linux-arm-gnueabihf": "1.7.3", + "@swc/core-linux-arm64-gnu": "1.7.3", + "@swc/core-linux-arm64-musl": "1.7.3", + "@swc/core-linux-x64-gnu": "1.7.3", + "@swc/core-linux-x64-musl": "1.7.3", + "@swc/core-win32-arm64-msvc": "1.7.3", + "@swc/core-win32-ia32-msvc": "1.7.3", + "@swc/core-win32-x64-msvc": "1.7.3" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.3.tgz", + "integrity": "sha512-CTkHa6MJdov9t41vuV2kmQIMu+Q19LrEHGIR/UiJYH06SC/sOu35ZZH8DyfLp9ZoaCn21gwgWd61ixOGQlwzTw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.3.tgz", + "integrity": "sha512-mun623y6rCoZ2EFIYfIRqXYRFufJOopoYSJcxYhZUrfTpAvQ1zLngjQpWCUU1krggXR2U0PQj+ls0DfXUTraNg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.3.tgz", + "integrity": "sha512-4Jz4UcIcvZNMp9qoHbBx35bo3rjt8hpYLPqnR4FFq6gkAsJIMFC56UhRZwdEQoDuYiOFMBnnrsg31Fyo6YQypA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.3.tgz", + "integrity": "sha512-p+U/M/oqV7HC4erQ5TVWHhJU1984QD+wQBPxslAYq751bOQGm0R/mXK42GjugqjnR6yYrAiwKKbpq4iWVXNePA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.3.tgz", + "integrity": "sha512-s6VzyaJwaRGTi2mz2h6Ywxfmgpkc69IxhuMzl+sl34plH0V0RgnZDm14HoCGIKIzRk4+a2EcBV1ZLAfWmPACQg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.3.tgz", + "integrity": "sha512-IrFY48C356Z2dU2pjYg080yvMXzmSV3Lmm/Wna4cfcB1nkVLjWsuYwwRAk9CY7E19c+q8N1sMNggubAUDYoX2g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.3.tgz", + "integrity": "sha512-qoLgxBlBnnyUEDu5vmRQqX90h9jldU1JXI96e6eh2d1gJyKRA0oSK7xXmTzorv1fGHiHulv9qiJOUG+g6uzJWg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.3.tgz", + "integrity": "sha512-OAd7jVVJ7nb0Ev80VAa1aeK+FldPeC4eZ35H4Qn6EICzIz0iqJo2T33qLKkSZiZEBKSoF4KcwrqYfkjLOp5qWg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.3.tgz", + "integrity": "sha512-31+Le1NyfSnILFV9+AhxfFOG0DK0272MNhbIlbcv4w/iqpjkhaOnNQnLsYJD1Ow7lTX1MtIZzTjOhRlzSviRWg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.3.tgz", + "integrity": "sha512-jVQPbYrwcuueI4QB0fHC29SVrkFOBcfIspYDlgSoHnEz6tmLMqUy+txZUypY/ZH/KaK0HEY74JkzgbRC1S6LFQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/types": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -6274,11 +6486,12 @@ "dev": true }, "node_modules/haengdong-design": { - "version": "0.1.35", - "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.35.tgz", - "integrity": "sha512-JhlZgZXzYfCMV57glEOiI+YKGstGAAOhBOKGad6Mugio/vNVUmInf3/eJPEy/10SM9reizYsSCt7eoyj71ZaSA==", + "version": "0.1.44", + "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.44.tgz", + "integrity": "sha512-gu8hxQ1b1Y5/U/wlxegCSB+adIhjeeKfa9Tx/ExEzx4nY1JIHFLSVXrjNnsCp7tPyyzd7xpgqqfwmLr2AFS5Yw==", "dependencies": { "@emotion/react": "^11.11.4", + "@storybook/addon-webpack5-compiler-swc": "^1.0.5", "@svgr/webpack": "^8.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -9872,6 +10085,18 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/swc-loader": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.6.tgz", + "integrity": "sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==", + "dependencies": { + "@swc/counter": "^0.1.3" + }, + "peerDependencies": { + "@swc/core": "^1.2.147", + "webpack": ">=2" + } + }, "node_modules/synckit": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", diff --git a/client/package.json b/client/package.json index cb8e9a714..614a48fe6 100644 --- a/client/package.json +++ b/client/package.json @@ -47,7 +47,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.35", + "haengdong-design": "^0.1.44", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" @@ -56,4 +56,4 @@ "npm": ">=10.7.0", "node": ">=20.15.1" } -} \ No newline at end of file +} diff --git a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx index 5adc3cd43..527618f8c 100644 --- a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx +++ b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx @@ -2,7 +2,7 @@ import {Input, FixedButton} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; -import useDynamicInput from '@hooks/useDynamicAdditionalInput'; +import useDynamicInput from '@hooks/useDynamicInput'; import {updateParticipantsInputStyle, updateParticipantsStyle} from './UpdateParticipants.style'; diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx index e5dda7c96..5a4eb68f3 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx @@ -2,7 +2,7 @@ import {Text, Input, BottomSheet, FixedButton} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; -import useDynamicInput from '@hooks/useDynamicAdditionalInput'; +import useDynamicInput from '@hooks/useDynamicInput'; import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from './SetInitialParticipants.style'; diff --git a/client/src/constants/errorMessage.ts b/client/src/constants/errorMessage.ts new file mode 100644 index 000000000..e6e6564af --- /dev/null +++ b/client/src/constants/errorMessage.ts @@ -0,0 +1,5 @@ +const ERROR_MESSAGE = { + eventName: '행사 이름은 30자 이하로만 가능해요', +}; + +export default ERROR_MESSAGE; diff --git a/client/src/constants/regExp.ts b/client/src/constants/regExp.ts new file mode 100644 index 000000000..c8fc6cb1e --- /dev/null +++ b/client/src/constants/regExp.ts @@ -0,0 +1 @@ +// const eventName = /^[가-힣]{2,30}]&/; diff --git a/client/src/hooks/useDynamicAdditionalInput.tsx b/client/src/hooks/useDynamicInput.tsx similarity index 100% rename from client/src/hooks/useDynamicAdditionalInput.tsx rename to client/src/hooks/useDynamicInput.tsx diff --git a/client/src/hooks/useInput/useInput.ts b/client/src/hooks/useInput/useInput.ts new file mode 100644 index 000000000..ae248217f --- /dev/null +++ b/client/src/hooks/useInput/useInput.ts @@ -0,0 +1,32 @@ +// interface UseInputProps { + +// } + +// const useInput = ({}) => { +// const [value, setValue] = useState(); +// const handleChange = (index: number, value: string) => { +// const newInputs = [...inputs]; +// newInputs[index] = value; +// setInputs(newInputs); +// }; + +// const handleBlur = (index: number) => { +// if (inputs[index].trim() === '') { +// setInputs(prev => { +// const newInputs = [...prev]; +// newInputs[index] = ''; +// return newInputs; +// }); +// } else if (inputs[index].trim() !== '' && index === inputs.length - 1) { +// setInputs(prev => { +// const newInputs = [...prev, '']; +// newInputs[index] = inputs[index].trim(); +// return newInputs; +// }); +// } +// }; + +// const getNonEmptyInputs = () => { +// return inputs.filter(input => input.trim() !== ''); +// }; +// } diff --git a/client/src/pages/Create/Name.tsx b/client/src/pages/Create/Name.tsx index c16870430..edeab4a9e 100644 --- a/client/src/pages/Create/Name.tsx +++ b/client/src/pages/Create/Name.tsx @@ -1,13 +1,17 @@ import {useState} from 'react'; import {useNavigate} from 'react-router-dom'; -import {FixedButton, Input, MainLayout, Title, TopNav, Back} from 'haengdong-design'; +import {FixedButton, MainLayout, LabelInput, Input, Title, TopNav, Back} from 'haengdong-design'; import {requestCreateNewEvent} from '@apis/request/event'; import {ROUTER_URLS} from '@constants/routerUrls'; +import validateEventName from '@utils/validate/validateEventName'; + const CreateEvent = () => { const [eventName, setEventName] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + const [canSubmit, setCanSubmit] = useState(false); const navigate = useNavigate(); const submitEventName = async (event: React.FormEvent) => { @@ -24,6 +28,24 @@ const CreateEvent = () => { } }; + const handleChange = (event: React.ChangeEvent) => { + const newValue = event.target.value; + const validation = validateEventName(newValue); + + if (newValue.length === 0) { + setCanSubmit(false); + } else { + setCanSubmit(true); + } + + if (validation.isValid) { + setEventName(newValue); + setErrorMessage(''); + } else { + event.target.value = eventName; + setErrorMessage(validation.errorMessage ?? ''); + } + }; return ( @@ -31,13 +53,17 @@ const CreateEvent = () => { <form onSubmit={submitEventName} style={{padding: '0 1rem'}}> - <Input - value={eventName} - onChange={event => setEventName(event.target.value)} - onBlur={() => setEventName(eventName.trim())} - placeholder="ex) 행동대장 야유회" - /> - <FixedButton disabled={!eventName.length}>행동 개시!</FixedButton> + <LabelInput labelText="행사 이름" errorText={errorMessage}> + <Input + value={eventName} + type="text" + placeholder="행사 이름" + onChange={e => handleChange(e)} + isError={!!errorMessage} + autoFocus + /> + </LabelInput> + <FixedButton disabled={!canSubmit}>행동 개시!</FixedButton> </form> </MainLayout> ); diff --git a/client/src/pages/Main/Main.tsx b/client/src/pages/Main/Main.tsx index 2a032b5cf..dfd7c406e 100644 --- a/client/src/pages/Main/Main.tsx +++ b/client/src/pages/Main/Main.tsx @@ -8,8 +8,6 @@ const Main = () => { return ( <MainLayout> - {/* <TopNav navType="back" /> */} - <Title title="행동대장" description="랜딩페이지입니다. 뿌뿌 잠깐만 테스트해볼게요.." /> <TopNav children={<></>} /> <Title title="행동대장" description="랜딩페이지입니다." /> <FixedButton onClick={() => navigate(ROUTER_URLS.eventCreateName)}>행사 생성하기</FixedButton> diff --git a/client/src/utils/validate/validateEventName.ts b/client/src/utils/validate/validateEventName.ts new file mode 100644 index 000000000..38a23d9ec --- /dev/null +++ b/client/src/utils/validate/validateEventName.ts @@ -0,0 +1,11 @@ +import {ValidateResult} from './validateResultType'; +import ERROR_MESSAGE from '@constants/errorMessage'; + +const validateEventName = (name: string): ValidateResult => { + if (name.length > 30) { + return {isValid: false, errorMessage: ERROR_MESSAGE.eventName}; + } + return {isValid: true}; +}; + +export default validateEventName; diff --git a/client/src/utils/validate/validateResultType.ts b/client/src/utils/validate/validateResultType.ts new file mode 100644 index 000000000..1c994c1f8 --- /dev/null +++ b/client/src/utils/validate/validateResultType.ts @@ -0,0 +1,4 @@ +export interface ValidateResult { + isValid: boolean; + errorMessage?: string; +} From 9dcb6ecb69045e87ada55c9bc8c876a2f8d329d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Wed, 31 Jul 2024 17:17:43 +0900 Subject: [PATCH 05/43] =?UTF-8?q?design:=20Input=20outline=20boxshadow?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=9A=B0=EC=84=A0?= =?UTF-8?q?=EC=88=9C=EC=9C=84=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/src/components/Input/Input.style.ts | 6 +++++- HDesign/src/components/Input/useInput.ts | 7 +++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/HDesign/src/components/Input/Input.style.ts b/HDesign/src/components/Input/Input.style.ts index 8b8854865..94fb70de7 100644 --- a/HDesign/src/components/Input/Input.style.ts +++ b/HDesign/src/components/Input/Input.style.ts @@ -32,7 +32,11 @@ export const inputBoxStyle = ( backgroundColor: inputBoxBackgroundColorByInputType(theme, inputType), boxSizing: 'border-box', - outline: isFocus ? `1px solid ${theme.colors.primary}` : isError ? `1px solid ${theme.colors.error}` : 'none', + boxShadow: isError + ? `0 0 0 1px ${theme.colors.error} inset` + : isFocus + ? `0 0 0 1px ${theme.colors.primary} inset` + : 'none', }); export const inputStyle = (theme: Theme) => diff --git a/HDesign/src/components/Input/useInput.ts b/HDesign/src/components/Input/useInput.ts index fd5e0bf04..208086f36 100644 --- a/HDesign/src/components/Input/useInput.ts +++ b/HDesign/src/components/Input/useInput.ts @@ -43,16 +43,15 @@ export const useInput = <T>({propsValue, onChange, onBlur, onFocus, inputRef}: U }; const handleBlur = (event?: React.FocusEvent<HTMLInputElement>) => { - setHasFocus(false); if (onBlur) { onBlur(event!); } + setHasFocus(false); }; const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => { - if (event.nativeEvent.isComposing) { - event.stopPropagation(); - } + if (event.nativeEvent.isComposing) return; + if (event.key === 'Enter' || event.key === 'Escape') { handleBlur(); if (inputRef.current) { From b44b5e05b9db55ac398076e053e7ccbcc82a5dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Wed, 31 Jul 2024 21:17:44 +0900 Subject: [PATCH 06/43] =?UTF-8?q?feat:=20=EC=B4=88=EA=B8=B0=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=EC=84=A4=EC=A0=95=20modal=20=EB=82=B4=EC=9D=98=20i?= =?UTF-8?q?nput=20=EB=8F=99=EC=9E=91=20=EB=B0=A9=EC=8B=9D=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 Co-authored-by: Soyeon Choe <soi-ha@users.noreply.github.com> --- client/package-lock.json | 8 ++--- client/package.json | 2 +- .../UpdateParticipants.tsx | 16 +++++----- .../SetInitialParticipants.tsx | 31 +++++++++++-------- client/src/constants/errorMessage.ts | 1 + client/src/constants/regExp.ts | 6 +++- client/src/hooks/useDynamicInput.tsx | 26 ++++++++++++---- .../{validateResultType.ts => type.ts} | 0 .../src/utils/validate/validateEventName.ts | 2 +- .../src/utils/validate/validateMemberName.ts | 12 +++++++ 10 files changed, 70 insertions(+), 34 deletions(-) rename client/src/utils/validate/{validateResultType.ts => type.ts} (100%) create mode 100644 client/src/utils/validate/validateMemberName.ts diff --git a/client/package-lock.json b/client/package-lock.json index 96a137d38..de125cf59 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,7 +12,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.44", + "haengdong-design": "^0.1.45", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" @@ -6486,9 +6486,9 @@ "dev": true }, "node_modules/haengdong-design": { - "version": "0.1.44", - "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.44.tgz", - "integrity": "sha512-gu8hxQ1b1Y5/U/wlxegCSB+adIhjeeKfa9Tx/ExEzx4nY1JIHFLSVXrjNnsCp7tPyyzd7xpgqqfwmLr2AFS5Yw==", + "version": "0.1.45", + "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.45.tgz", + "integrity": "sha512-s3Xf7xRPWHfcFF4tiG470eHa1+iaBgLNrRKXaTYpmfJTO5vDV7g+zEboq0jno1zTrD0bS7QTPRE+4A5R9OlbDg==", "dependencies": { "@emotion/react": "^11.11.4", "@storybook/addon-webpack5-compiler-swc": "^1.0.5", diff --git a/client/package.json b/client/package.json index 614a48fe6..6b999f6b3 100644 --- a/client/package.json +++ b/client/package.json @@ -47,7 +47,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.44", + "haengdong-design": "^0.1.45", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" diff --git a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx index 527618f8c..16b687a61 100644 --- a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx +++ b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx @@ -2,7 +2,7 @@ import {Input, FixedButton} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; -import useDynamicInput from '@hooks/useDynamicInput'; +// import useDynamicInput from '@hooks/useDynamicInput'; import {updateParticipantsInputStyle, updateParticipantsStyle} from './UpdateParticipants.style'; @@ -12,19 +12,19 @@ interface UpdateParticipantsProps { } const UpdateParticipants = ({inOutAction, setOpenBottomSheet}: UpdateParticipantsProps) => { - const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs} = useDynamicInput(); + // const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs} = useDynamicInput(); const {updateMemberList} = useStepList(); const handleUpdateParticipantsSubmit = () => { - updateMemberList({memberNameList: getNonEmptyInputs(), type: inOutAction}); - setOpenBottomSheet(false); + // updateMemberList({memberNameList: getNonEmptyInputs(), type: inOutAction}); + // setOpenBottomSheet(false); }; return ( <div css={updateParticipantsStyle}> <div css={updateParticipantsInputStyle}> {/* TODO: (@soha) Search로 변경하기 */} - {inputs.map((name, index) => ( + {/* {inputs.map((name, index) => ( <Input key={index} placeholder="이름" @@ -34,12 +34,12 @@ const UpdateParticipants = ({inOutAction, setOpenBottomSheet}: UpdateParticipant onChange={e => handleInputChange(index, e.target.value)} onBlur={() => handleInputBlur(index)} /> - ))} + ))} */} </div> <FixedButton - disabled={!(inputs.length - 1)} + // disabled={!(inputs.length - 1)} variants={'primary'} - children={`${inputs.length - 1}명 ${inOutAction === 'OUT' ? '탈주' : '늦참'}`} + // children={`${inputs.length - 1}명 ${inOutAction === 'OUT' ? '탈주' : '늦참'}`} onClick={handleUpdateParticipantsSubmit} /> </div> diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx index 5a4eb68f3..d43fd8b1b 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx @@ -1,9 +1,11 @@ -import {Text, Input, BottomSheet, FixedButton} from 'haengdong-design'; +import {Text, Input, BottomSheet, FixedButton, LabelInput} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; import useDynamicInput from '@hooks/useDynamicInput'; +import validateMemberName from '@utils/validate/validateMemberName'; + import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from './SetInitialParticipants.style'; interface SetInitialParticipantsProps { @@ -12,7 +14,8 @@ interface SetInitialParticipantsProps { } const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitialParticipantsProps) => { - const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs} = useDynamicInput(); + const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs, errorMessage} = + useDynamicInput(validateMemberName); const {updateMemberList} = useStepList(); const handleSubmit = () => { @@ -25,17 +28,19 @@ const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitia <div css={setInitialParticipantsStyle}> <Text size="bodyBold">초기 인원 설정하기</Text> <div css={setInitialParticipantsInputGroupStyle}> - {inputs.map((participant, index) => ( - <Input - key={index} - placeholder="이름" - type="text" - value={participant} - ref={el => (inputRefs.current[index] = el)} - onChange={e => handleInputChange(index, e.target.value)} - onBlur={() => handleInputBlur(index)} - /> - ))} + <LabelInput labelText="이름" errorText={errorMessage}> + {inputs.map((input, index) => ( + <Input + key={index} + type="text" + value={input} + ref={el => (inputRefs.current[index] = el)} + onChange={e => handleInputChange(index, e)} + onBlur={() => handleInputBlur(index)} + autoFocus + /> + ))} + </LabelInput> </div> </div> <FixedButton diff --git a/client/src/constants/errorMessage.ts b/client/src/constants/errorMessage.ts index e6e6564af..a72be530c 100644 --- a/client/src/constants/errorMessage.ts +++ b/client/src/constants/errorMessage.ts @@ -1,5 +1,6 @@ const ERROR_MESSAGE = { eventName: '행사 이름은 30자 이하로만 가능해요', + memberName: '참여자 이름은 30자 이하의 한글만 가능해요', }; export default ERROR_MESSAGE; diff --git a/client/src/constants/regExp.ts b/client/src/constants/regExp.ts index c8fc6cb1e..c4846490f 100644 --- a/client/src/constants/regExp.ts +++ b/client/src/constants/regExp.ts @@ -1 +1,5 @@ -// const eventName = /^[가-힣]{2,30}]&/; +const REGEXP = { + memberName: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z\s]{1,30}$/, +}; + +export default REGEXP; diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index 6bd592254..96dfc2b80 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -1,14 +1,27 @@ import {useEffect, useRef, useState} from 'react'; +import {ValidateResult} from '@utils/validate/type'; -const useDynamicInput = () => { +const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { const [inputs, setInputs] = useState<string[]>(['']); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); + const [errorMessage, setErrorMessage] = useState(''); // TODO: (@soha) 입력이 완료되고 중간에 값을 모두 지웠을 경우 Input이 없애지도록 수정하기 - const handleInputChange = (index: number, value: string) => { - const newInputs = [...inputs]; - newInputs[index] = value; - setInputs(newInputs); + const handleInputChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => { + const value = event.target.value; + const validation = validateFunc(value); + + if (validation.isValid || value.length === 0) { + setInputs(prevInputs => { + const newInputs = [...prevInputs]; + newInputs[index] = event.target.value; + return newInputs; + }); + setErrorMessage(''); + } else { + event.target.value = inputs[index]; + setErrorMessage(validation.errorMessage ?? ''); + } }; const handleInputBlur = (index: number) => { @@ -21,7 +34,7 @@ const useDynamicInput = () => { } else if (inputs[index].trim() !== '' && index === inputs.length - 1) { setInputs(prev => { const newInputs = [...prev, '']; - newInputs[index] = inputs[index].trim(); + newInputs[index] = inputs[index]; return newInputs; }); } @@ -45,6 +58,7 @@ const useDynamicInput = () => { inputRefs, handleInputChange, handleInputBlur, + errorMessage, getNonEmptyInputs, }; }; diff --git a/client/src/utils/validate/validateResultType.ts b/client/src/utils/validate/type.ts similarity index 100% rename from client/src/utils/validate/validateResultType.ts rename to client/src/utils/validate/type.ts diff --git a/client/src/utils/validate/validateEventName.ts b/client/src/utils/validate/validateEventName.ts index 38a23d9ec..601ad96e3 100644 --- a/client/src/utils/validate/validateEventName.ts +++ b/client/src/utils/validate/validateEventName.ts @@ -1,4 +1,4 @@ -import {ValidateResult} from './validateResultType'; +import {ValidateResult} from './type'; import ERROR_MESSAGE from '@constants/errorMessage'; const validateEventName = (name: string): ValidateResult => { diff --git a/client/src/utils/validate/validateMemberName.ts b/client/src/utils/validate/validateMemberName.ts new file mode 100644 index 000000000..df143b32e --- /dev/null +++ b/client/src/utils/validate/validateMemberName.ts @@ -0,0 +1,12 @@ +import REGEXP from '@constants/regExp'; +import {ValidateResult} from './type'; +import ERROR_MESSAGE from '@constants/errorMessage'; + +const validateMemberName = (name: string): ValidateResult => { + if (!REGEXP.memberName.test(name)) { + return {isValid: false, errorMessage: ERROR_MESSAGE.memberName}; + } + return {isValid: true}; +}; + +export default validateMemberName; From 9d4d9c9781a38a28059b8965845518cdbe7fede0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Thu, 1 Aug 2024 03:55:16 +0900 Subject: [PATCH 07/43] =?UTF-8?q?feat:=20LabelInput,=20LabelGroupInput=20?= =?UTF-8?q?=EB=93=B1=20=EB=8B=A4=EC=96=91=ED=95=9C=20Input=20Component=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Soyeon Choe <soi-ha@users.noreply.github.com> --- HDesign/package-lock.json | 4 +- HDesign/package.json | 2 +- .../src/components/Input/Input.stories.tsx | 3 +- HDesign/src/components/Input/Input.tsx | 20 ++------ HDesign/src/components/Input/useInput.ts | 47 +++++++++--------- .../components/LabelGroupInput/Element.tsx | 33 +++++++++++++ .../LabelGroupInput/Element.type.ts | 10 ++++ .../LabelGroupInput/GroupInputContext.tsx | 32 +++++++++++++ .../LabelGroupInput.stories.tsx | 40 ++++++++++++++++ .../LabelGroupInput/LabelGroupInput.style.ts | 37 ++++++++++++++ .../LabelGroupInput/LabelGroupInput.tsx | 48 +++++++++++++++++++ .../LabelGroupInput/LabelGroupInput.type.ts | 13 +++++ .../LabelInput/LabelInput.stories.tsx | 9 ++-- .../components/LabelInput/LabelInput.style.ts | 30 ++++++++---- .../src/components/LabelInput/LabelInput.tsx | 42 +++++++++++----- .../components/LabelInput/LabelInput.type.ts | 11 ++--- .../components/LabelInput/useLabelInput.ts | 31 ++++++++++++ HDesign/src/index.tsx | 3 ++ 18 files changed, 341 insertions(+), 74 deletions(-) create mode 100644 HDesign/src/components/LabelGroupInput/Element.tsx create mode 100644 HDesign/src/components/LabelGroupInput/Element.type.ts create mode 100644 HDesign/src/components/LabelGroupInput/GroupInputContext.tsx create mode 100644 HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx create mode 100644 HDesign/src/components/LabelGroupInput/LabelGroupInput.style.ts create mode 100644 HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx create mode 100644 HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts create mode 100644 HDesign/src/components/LabelInput/useLabelInput.ts diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index ae8935320..c7a1aa312 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -1,12 +1,12 @@ { "name": "haengdong-design", - "version": "0.1.44", + "version": "0.1.45", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "haengdong-design", - "version": "0.1.44", + "version": "0.1.45", "license": "ISC", "dependencies": { "@emotion/react": "^11.11.4", diff --git a/HDesign/package.json b/HDesign/package.json index 018c411b1..16e6e4d39 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -1,6 +1,6 @@ { "name": "haengdong-design", - "version": "0.1.44", + "version": "0.1.45", "description": "", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/HDesign/src/components/Input/Input.stories.tsx b/HDesign/src/components/Input/Input.stories.tsx index 0f5187cb3..7a5e4da70 100644 --- a/HDesign/src/components/Input/Input.stories.tsx +++ b/HDesign/src/components/Input/Input.stories.tsx @@ -20,7 +20,8 @@ const meta = { }, }, args: { - disabled: false, + value: '', + isError: false, placeholder: 'placeholder', }, } satisfies Meta<typeof Input>; diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index 64ff98cdf..ac6a0def9 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @emotion/react */ -import React, {forwardRef, useImperativeHandle, useRef} from 'react'; +import React, {forwardRef, useEffect, useImperativeHandle, useRef} from 'react'; import IconButton from '@components/IconButton/IconButton'; import {InputProps} from '@components/Input/Input.type'; @@ -9,21 +9,13 @@ import {useInput} from '@components/Input/useInput'; import {useTheme} from '@theme/HDesignProvider'; export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input( - {value: propsValue, onChange, onFocus, onBlur, inputType, isError, ...htmlProps}: InputProps, + {value: propsValue, onChange, onFocus, onBlur, inputType, isError, placeholder, ...htmlProps}: InputProps, ref, ) { + useImperativeHandle(ref, () => inputRef.current!); const {theme} = useTheme(); const inputRef = useRef<HTMLInputElement>(null); - - useImperativeHandle(ref, () => inputRef.current!); - - const {value, hasFocus, handleChange, handleClickDelete, handleBlur, handleFocus, handleKeyDown} = useInput({ - propsValue, - onChange, - onFocus, - onBlur, - inputRef, - }); + const {value, handleChange, hasFocus, handleClickDelete} = useInput({propsValue, onChange, inputRef}); return ( <div css={inputBoxStyle(theme, inputType, hasFocus, isError)}> @@ -32,9 +24,7 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro ref={inputRef} value={value} onChange={handleChange} - onFocus={handleFocus} - onBlur={handleBlur} - onKeyDown={handleKeyDown} + placeholder={inputRef.current === document.activeElement ? '' : placeholder} {...htmlProps} /> {value && hasFocus && <IconButton iconType="inputDelete" onMouseDown={handleClickDelete} />} diff --git a/HDesign/src/components/Input/useInput.ts b/HDesign/src/components/Input/useInput.ts index 208086f36..12505439c 100644 --- a/HDesign/src/components/Input/useInput.ts +++ b/HDesign/src/components/Input/useInput.ts @@ -8,57 +8,54 @@ interface UseInputProps<T> { inputRef: RefObject<HTMLInputElement>; } -export const useInput = <T>({propsValue, onChange, onBlur, onFocus, inputRef}: UseInputProps<T>) => { +export const useInput = <T>({propsValue, onChange, inputRef}: UseInputProps<T>) => { const [value, setValue] = useState(propsValue || ''); - const [hasFocus, setHasFocus] = useState(false); + const [hasFocus, setHasFocus] = useState(inputRef.current === document.activeElement); useEffect(() => { setValue(propsValue || ''); }, [propsValue]); - const handleClickDelete = () => { + useEffect(() => { + inputRef.current?.addEventListener('focus', () => setHasFocus(true)); + inputRef.current?.addEventListener('blur', () => setHasFocus(false)); + inputRef.current?.addEventListener('keydown', () => handleKeyDown); + + return () => { + inputRef.current?.removeEventListener('focus', () => setHasFocus(true)); + inputRef.current?.removeEventListener('blur', () => setHasFocus(false)); + inputRef.current?.addEventListener('keydown', () => handleKeyDown); + }; + }, []); + + const handleClickDelete = (event: React.MouseEvent) => { + event.preventDefault(); setValue(''); - - if (inputRef.current) { - inputRef.current.focus(); - } - if (onChange) { onChange({target: {value: ''}} as React.ChangeEvent<HTMLInputElement>); } + if (inputRef.current) { + inputRef.current.focus(); + } }; const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { + setValue(e.target.value); if (onChange) { onChange(e); } - setValue(e.target.value); - }; - - const handleFocus = (event?: React.FocusEvent<HTMLInputElement>) => { - if (onFocus) { - onFocus(event!); - } - setHasFocus(true); - }; - - const handleBlur = (event?: React.FocusEvent<HTMLInputElement>) => { - if (onBlur) { - onBlur(event!); - } - setHasFocus(false); }; const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => { if (event.nativeEvent.isComposing) return; if (event.key === 'Enter' || event.key === 'Escape') { - handleBlur(); + setHasFocus(false); if (inputRef.current) { inputRef.current.blur(); } } }; - return {value, hasFocus, handleChange, handleClickDelete, handleFocus, handleBlur, handleKeyDown}; + return {value, handleChange, hasFocus, handleClickDelete}; }; diff --git a/HDesign/src/components/LabelGroupInput/Element.tsx b/HDesign/src/components/LabelGroupInput/Element.tsx new file mode 100644 index 000000000..8224e4c45 --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/Element.tsx @@ -0,0 +1,33 @@ +/** @jsxImportSource @emotion/react */ + +import {forwardRef, useEffect, useImperativeHandle, useRef} from 'react'; +import {ElementProps} from './Element.type'; +import Input from '../Input/Input'; +import {useGroupInputContext} from './GroupInputContext'; + +const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProps>(function Element( + {key, value, onChange, isError, ...htmlProps}: ElementProps, + ref, +) { + useImperativeHandle(ref, () => inputRef.current!); + const inputRef = useRef<HTMLInputElement>(null); + const {setHasAnyFocus, values, setValues, hasErrors, setHasErrors} = useGroupInputContext(); + + useEffect(() => { + setHasErrors({...hasErrors, [key]: isError ?? false}); + + inputRef.current?.addEventListener('change', () => setValues({...values, [key]: `${value}`})); + inputRef.current?.addEventListener('focus', () => setHasAnyFocus(true)); + inputRef.current?.addEventListener('blur', () => setHasAnyFocus(false)); + + return () => { + inputRef.current?.removeEventListener('change', () => setValues({...values, [key]: `${value}`})); + inputRef.current?.removeEventListener('focus', () => setHasAnyFocus(true)); + inputRef.current?.removeEventListener('blur', () => setHasAnyFocus(false)); + }; + }, []); + + return <Input ref={inputRef} isError={isError} {...htmlProps} />; +}); + +export default Element; diff --git a/HDesign/src/components/LabelGroupInput/Element.type.ts b/HDesign/src/components/LabelGroupInput/Element.type.ts new file mode 100644 index 000000000..edf039d17 --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/Element.type.ts @@ -0,0 +1,10 @@ +export interface ElementStyleProps {} + +export interface ElementCustomProps { + key: string; + isError?: boolean; +} + +export type ElementOptionProps = ElementStyleProps & ElementCustomProps; + +export type ElementProps = React.ComponentProps<'input'> & ElementOptionProps; diff --git a/HDesign/src/components/LabelGroupInput/GroupInputContext.tsx b/HDesign/src/components/LabelGroupInput/GroupInputContext.tsx new file mode 100644 index 000000000..50f0780ce --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/GroupInputContext.tsx @@ -0,0 +1,32 @@ +import React, {createContext, PropsWithChildren, useContext, useState} from 'react'; + +interface GroupInputContextProps { + hasAnyFocus: boolean; + setHasAnyFocus: React.Dispatch<React.SetStateAction<boolean>>; + values: {[key: string]: string}; + setValues: React.Dispatch<React.SetStateAction<{[key: string]: string}>>; + hasErrors: {[key: string]: boolean}; + setHasErrors: React.Dispatch<React.SetStateAction<{[key: string]: boolean}>>; +} + +const GroupInputContext = createContext<GroupInputContextProps | undefined>(undefined); + +export const useGroupInputContext = () => { + const context = useContext(GroupInputContext); + if (!context) { + throw new Error('useGroupInputContext must be used within an GroupInputProvider'); + } + return context; +}; + +export const GroupInputProvider: React.FC<PropsWithChildren> = ({children}: React.PropsWithChildren) => { + const [hasAnyFocus, setHasAnyFocus] = useState(false); + const [values, setValues] = useState<{[key: string]: string}>({}); + const [hasErrors, setHasErrors] = useState<{[key: string]: boolean}>({}); + + return ( + <GroupInputContext.Provider value={{hasAnyFocus, setHasAnyFocus, values, setValues, hasErrors, setHasErrors}}> + {children} + </GroupInputContext.Provider> + ); +}; diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx new file mode 100644 index 000000000..049a1ad02 --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx @@ -0,0 +1,40 @@ +/** @jsxImportSource @emotion/react */ +import type {Meta, StoryObj} from '@storybook/react'; + +import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; + +const meta = { + title: 'Components/LabelGroupInput', + component: LabelGroupInput, + tags: ['autodocs'], + parameters: { + // layout: 'centered', + }, + argTypes: { + labelText: { + description: 'label에 들어갈 텍스트를 작성', + control: {type: 'text'}, + }, + errorText: { + description: 'error에 들어갈 텍스트를 작성', + control: {type: 'text'}, + }, + }, + args: { + labelText: '지출내역 / 금액', + errorText: 'error가 발생했을 때 나타납니다!', + }, +} satisfies Meta<typeof LabelGroupInput>; + +export default meta; + +type Story = StoryObj<typeof meta>; + +export const Playground: Story = { + render: ({...args}) => ( + <LabelGroupInput {...args}> + <LabelGroupInput.Element key="name" placeholder="지출내역" isError={false} /> + <LabelGroupInput.Element key="price" placeholder="금액" isError={false} /> + </LabelGroupInput> + ), +}; diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.style.ts b/HDesign/src/components/LabelGroupInput/LabelGroupInput.style.ts new file mode 100644 index 000000000..99075fe1d --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.style.ts @@ -0,0 +1,37 @@ +import {css} from '@emotion/react'; + +import {Theme} from '@/theme/theme.type'; + +export const labelInputStyle = css({ + display: 'flex', + flexDirection: 'column', + gap: '0.375rem', +}); + +export const labelGroupStyle = css({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + + paddingInline: '0.5rem', + marginBottom: '0.375rem', +}); + +export const inputGroupStyle = css({ + display: 'flex', + flexDirection: 'column', + gap: '0.5rem', +}); + +export const labelTextStyle = (theme: Theme) => + css({ + height: '1.125rem', + color: theme.colors.gray, + }); + +export const errorTextStyle = (theme: Theme) => + css({ + height: '1.125rem', + color: theme.colors.onErrorContainer, + }); diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx new file mode 100644 index 000000000..cd1ca2ca9 --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx @@ -0,0 +1,48 @@ +/** @jsxImportSource @emotion/react */ + +import Text from '@components/Text/Text'; +import {useTheme} from '@/theme/HDesignProvider'; + +import {LabelGroupInputProps} from './LabelGroupInput.type'; +import { + errorTextStyle, + inputGroupStyle, + labelGroupStyle, + labelInputStyle, + labelTextStyle, +} from './LabelGroupInput.style'; +import Element from './Element'; +import {GroupInputProvider, useGroupInputContext} from './GroupInputContext'; + +const LabelGroupInput: React.FC<LabelGroupInputProps> = ({labelText, errorText, children}: LabelGroupInputProps) => { + const {theme} = useTheme(); + const {hasAnyFocus, values, hasErrors} = useGroupInputContext(); + + return ( + <div css={labelInputStyle}> + <div css={labelGroupStyle}> + <label> + <Text size="caption" css={labelTextStyle(theme)}> + {(hasAnyFocus || !Object.values(values).every(value => value === '')) && labelText} + </Text> + </label> + {errorText && ( + <Text size="caption" css={errorTextStyle(theme)}> + {!Object.values(hasErrors).every(error => !error) && errorText} + </Text> + )} + </div> + <div css={inputGroupStyle}>{children}</div> + </div> + ); +}; + +const LabelGroupInputContainer = (props: LabelGroupInputProps) => ( + <GroupInputProvider> + <LabelGroupInput {...props} /> + </GroupInputProvider> +); + +LabelGroupInputContainer.Element = Element; + +export default LabelGroupInputContainer; diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts b/HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts new file mode 100644 index 000000000..a25d6caf1 --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts @@ -0,0 +1,13 @@ +export interface LabelGroupInputStyleProps {} + +export interface LabelGroupInputCustomProps { + values: string[]; + labelText: string; + placeHolders?: string[]; + errorText?: string; + errorIndexList?: number[]; +} + +export type LabelGroupInputOptionProps = LabelGroupInputStyleProps & LabelGroupInputCustomProps; + +export type LabelGroupInputProps = React.ComponentProps<'input'> & LabelGroupInputOptionProps; diff --git a/HDesign/src/components/LabelInput/LabelInput.stories.tsx b/HDesign/src/components/LabelInput/LabelInput.stories.tsx index 63ebcea37..e65f859f6 100644 --- a/HDesign/src/components/LabelInput/LabelInput.stories.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.stories.tsx @@ -2,7 +2,6 @@ import type {Meta, StoryObj} from '@storybook/react'; import LabelInput from '@components/LabelInput/LabelInput'; -import Input from '@components/Input/Input'; const meta = { title: 'Components/LabelInput', @@ -16,15 +15,19 @@ const meta = { description: 'label에 들어갈 텍스트를 작성', control: {type: 'text'}, }, + isError: { + description: '', + control: {type: 'boolean'}, + }, errorText: { description: 'error에 들어갈 텍스트를 작성', control: {type: 'text'}, }, }, args: { - labelText: 'label 내용', + value: '', + labelText: '이름', errorText: 'error가 발생했을 때 나타납니다!', - children: <Input placeholder="labelInput 테스트를 위한 Input" />, }, } satisfies Meta<typeof LabelInput>; diff --git a/HDesign/src/components/LabelInput/LabelInput.style.ts b/HDesign/src/components/LabelInput/LabelInput.style.ts index 208d6e56c..7fcc9eb69 100644 --- a/HDesign/src/components/LabelInput/LabelInput.style.ts +++ b/HDesign/src/components/LabelInput/LabelInput.style.ts @@ -2,23 +2,35 @@ import {css} from '@emotion/react'; import {Theme} from '@/theme/theme.type'; -export const labelGroupStyle = () => - css({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - width: '100%', +export const labelInputStyle = css({ + display: 'flex', + flexDirection: 'column', + gap: '0.375rem', +}); - paddingInline: '0.5rem', - marginBottom: '0.375rem', - }); +export const labelGroupStyle = css({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + + paddingInline: '0.5rem', +}); + +export const inputGroupStyle = css({ + display: 'flex', + flexDirection: 'column', + gap: '0.5rem', +}); export const labelTextStyle = (theme: Theme) => css({ + height: '1.125rem', color: theme.colors.gray, }); export const errorTextStyle = (theme: Theme) => css({ + height: '1.125rem', color: theme.colors.onErrorContainer, }); diff --git a/HDesign/src/components/LabelInput/LabelInput.tsx b/HDesign/src/components/LabelInput/LabelInput.tsx index 97cc9bcd1..d3e9f117d 100644 --- a/HDesign/src/components/LabelInput/LabelInput.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.tsx @@ -4,27 +4,45 @@ import Text from '@components/Text/Text'; import {useTheme} from '@/theme/HDesignProvider'; import {LabelInputProps} from './LabelInput.type'; -import {errorTextStyle, labelGroupStyle, labelTextStyle} from './LabelInput.style'; +import {errorTextStyle, inputGroupStyle, labelGroupStyle, labelInputStyle, labelTextStyle} from './LabelInput.style'; +import Input from '../Input/Input'; +import {forwardRef, useImperativeHandle, useRef} from 'react'; +import {useLabelInput} from './useLabelInput'; -const LabelInput = ({labelText, errorText, children}: LabelInputProps) => { +const LabelInput: React.FC<LabelInputProps> = forwardRef<HTMLInputElement, LabelInputProps>(function LabelInput( + {value: propsValue, onChange, labelText, errorText, isError, ...htmlProps}: LabelInputProps, + ref, +) { + useImperativeHandle(ref, () => inputRef.current!); const {theme} = useTheme(); + const inputRef = useRef<HTMLInputElement>(null); + const {value, handleChange, hasFocus} = useLabelInput({inputRef, propsValue, onChange}); + return ( - <> + <div css={labelInputStyle}> <div css={labelGroupStyle}> <label> <Text size="caption" css={labelTextStyle(theme)}> - {labelText} + {(hasFocus || value) && labelText} </Text> </label> - {errorText && ( - <Text size="caption" css={errorTextStyle(theme)}> - {errorText} - </Text> - )} + + <Text size="caption" css={errorTextStyle(theme)}> + {isError && errorText} + </Text> + </div> + <div css={inputGroupStyle}> + <Input + value={value} + onChange={handleChange} + ref={inputRef} + isError={isError} + placeholder={labelText} + {...htmlProps} + /> </div> - {children} - </> + </div> ); -}; +}); export default LabelInput; diff --git a/HDesign/src/components/LabelInput/LabelInput.type.ts b/HDesign/src/components/LabelInput/LabelInput.type.ts index 539a10da1..39deb1fb0 100644 --- a/HDesign/src/components/LabelInput/LabelInput.type.ts +++ b/HDesign/src/components/LabelInput/LabelInput.type.ts @@ -1,13 +1,12 @@ -import {InputProps} from '../Input/Input.type'; - export interface LabelInputStyleProps {} -export interface ButtonCustomProps { +export interface LabelInputCustomProps { + value: string; labelText: string; errorText?: string; - children?: React.ReactElement<InputProps>[] | React.ReactElement<InputProps>; + isError?: boolean; } -export type LabelInputOptionProps = LabelInputStyleProps & ButtonCustomProps; +export type LabelInputOptionProps = LabelInputCustomProps & LabelInputCustomProps; -export type LabelInputProps = React.ComponentProps<'div'> & LabelInputOptionProps; +export type LabelInputProps = React.ComponentProps<'input'> & LabelInputOptionProps; diff --git a/HDesign/src/components/LabelInput/useLabelInput.ts b/HDesign/src/components/LabelInput/useLabelInput.ts new file mode 100644 index 000000000..fedd01423 --- /dev/null +++ b/HDesign/src/components/LabelInput/useLabelInput.ts @@ -0,0 +1,31 @@ +import {RefObject, useEffect, useState} from 'react'; + +interface UseLabelInput<T> { + inputRef: RefObject<HTMLInputElement>; + propsValue: T; + onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; +} + +export const useLabelInput = <T>({inputRef, propsValue, onChange}: UseLabelInput<T>) => { + const [value, setValue] = useState(propsValue || ''); + const [hasFocus, setHasFocus] = useState(inputRef.current === document.activeElement); + + useEffect(() => { + inputRef.current?.addEventListener('focus', () => setHasFocus(true)); + inputRef.current?.addEventListener('blur', () => setHasFocus(false)); + + return () => { + inputRef.current?.removeEventListener('focus', () => setHasFocus(true)); + inputRef.current?.removeEventListener('blur', () => setHasFocus(false)); + }; + }, []); + + const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { + setValue(e.target.value); + if (onChange) { + onChange(e); + } + }; + + return {value, hasFocus, handleChange}; +}; diff --git a/HDesign/src/index.tsx b/HDesign/src/index.tsx index 909bef23f..b9c761239 100644 --- a/HDesign/src/index.tsx +++ b/HDesign/src/index.tsx @@ -9,6 +9,8 @@ import IconButton from '@components/IconButton/IconButton'; import InOutItem from '@components/InOutItem/InOutItem'; import Input from '@components/Input/Input'; import LabelInput from '@components/LabelInput/LabelInput'; +// import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; +import {LabelGroupInputContainer as LabelGroupInput} from '@components/LabelGroupInput/LabelGroupInput'; import Search from '@components/Search/Search'; import StepItem from '@components/StepItem/StepItem'; import Switch from '@components/Switch/Switch'; @@ -40,6 +42,7 @@ export { InOutItem, Input, LabelInput, + LabelGroupInput, Search, StepItem, Switch, From 28c175955e358beb7261d13f5a1182e35e796b96 Mon Sep 17 00:00:00 2001 From: Soyeon Choe <soy2302ten@gmail.com> Date: Thu, 1 Aug 2024 09:30:09 +0900 Subject: [PATCH 08/43] =?UTF-8?q?fix:=20=EA=B3=B5=EB=B0=B1=EC=9D=B4=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=ED=95=98=EB=8A=94=20input=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useDynamicInput.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index 96dfc2b80..c12ed7e20 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -1,4 +1,5 @@ import {useEffect, useRef, useState} from 'react'; + import {ValidateResult} from '@utils/validate/type'; const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { @@ -25,13 +26,15 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { }; const handleInputBlur = (index: number) => { - if (inputs[index].trim() === '') { + const filterEmpty = inputs.filter(input => input.trim() !== ''); + + if (filterEmpty.length !== inputs.length) { setInputs(prev => { - const newInputs = [...prev]; - newInputs[index] = ''; - return newInputs; + const removeEmpty = prev.filter(value => value.trim() !== ''); + return [...removeEmpty, '']; }); - } else if (inputs[index].trim() !== '' && index === inputs.length - 1) { + } + if (inputs[index].trim() !== '' && index === inputs.length - 1) { setInputs(prev => { const newInputs = [...prev, '']; newInputs[index] = inputs[index]; From 537905b9f9b16e7aca4a5b20eebab56bc07c763e Mon Sep 17 00:00:00 2001 From: Soyeon Choe <soy2302ten@gmail.com> Date: Thu, 1 Aug 2024 09:30:38 +0900 Subject: [PATCH 09/43] =?UTF-8?q?style:=20lint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/SetInitialParticipants/SetInitialParticipants.tsx | 3 +-- client/src/pages/Create/Name.tsx | 3 +-- client/src/utils/validate/validateEventName.ts | 3 ++- client/src/utils/validate/validateMemberName.ts | 3 ++- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx index d43fd8b1b..fa668557e 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx @@ -1,11 +1,10 @@ import {Text, Input, BottomSheet, FixedButton, LabelInput} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; +import validateMemberName from '@utils/validate/validateMemberName'; import useDynamicInput from '@hooks/useDynamicInput'; -import validateMemberName from '@utils/validate/validateMemberName'; - import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from './SetInitialParticipants.style'; interface SetInitialParticipantsProps { diff --git a/client/src/pages/Create/Name.tsx b/client/src/pages/Create/Name.tsx index edeab4a9e..e8fd1f649 100644 --- a/client/src/pages/Create/Name.tsx +++ b/client/src/pages/Create/Name.tsx @@ -3,11 +3,10 @@ import {useNavigate} from 'react-router-dom'; import {FixedButton, MainLayout, LabelInput, Input, Title, TopNav, Back} from 'haengdong-design'; import {requestCreateNewEvent} from '@apis/request/event'; +import validateEventName from '@utils/validate/validateEventName'; import {ROUTER_URLS} from '@constants/routerUrls'; -import validateEventName from '@utils/validate/validateEventName'; - const CreateEvent = () => { const [eventName, setEventName] = useState(''); const [errorMessage, setErrorMessage] = useState(''); diff --git a/client/src/utils/validate/validateEventName.ts b/client/src/utils/validate/validateEventName.ts index 601ad96e3..1647a896c 100644 --- a/client/src/utils/validate/validateEventName.ts +++ b/client/src/utils/validate/validateEventName.ts @@ -1,6 +1,7 @@ -import {ValidateResult} from './type'; import ERROR_MESSAGE from '@constants/errorMessage'; +import {ValidateResult} from './type'; + const validateEventName = (name: string): ValidateResult => { if (name.length > 30) { return {isValid: false, errorMessage: ERROR_MESSAGE.eventName}; diff --git a/client/src/utils/validate/validateMemberName.ts b/client/src/utils/validate/validateMemberName.ts index df143b32e..8163af556 100644 --- a/client/src/utils/validate/validateMemberName.ts +++ b/client/src/utils/validate/validateMemberName.ts @@ -1,7 +1,8 @@ import REGEXP from '@constants/regExp'; -import {ValidateResult} from './type'; import ERROR_MESSAGE from '@constants/errorMessage'; +import {ValidateResult} from './type'; + const validateMemberName = (name: string): ValidateResult => { if (!REGEXP.memberName.test(name)) { return {isValid: false, errorMessage: ERROR_MESSAGE.memberName}; From 976d7976ce011a8034f83de2e9f4818bb5e9cd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Thu, 1 Aug 2024 09:33:21 +0900 Subject: [PATCH 10/43] =?UTF-8?q?style:=20lint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/src/components/LabelGroupInput/Element.tsx | 4 +++- HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx | 4 +--- HDesign/src/components/LabelInput/LabelInput.tsx | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/HDesign/src/components/LabelGroupInput/Element.tsx b/HDesign/src/components/LabelGroupInput/Element.tsx index 8224e4c45..151b8d0a0 100644 --- a/HDesign/src/components/LabelGroupInput/Element.tsx +++ b/HDesign/src/components/LabelGroupInput/Element.tsx @@ -1,8 +1,10 @@ /** @jsxImportSource @emotion/react */ import {forwardRef, useEffect, useImperativeHandle, useRef} from 'react'; -import {ElementProps} from './Element.type'; + import Input from '../Input/Input'; + +import {ElementProps} from './Element.type'; import {useGroupInputContext} from './GroupInputContext'; const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProps>(function Element( diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx index cd1ca2ca9..63bba57b8 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx @@ -37,12 +37,10 @@ const LabelGroupInput: React.FC<LabelGroupInputProps> = ({labelText, errorText, ); }; -const LabelGroupInputContainer = (props: LabelGroupInputProps) => ( +export const LabelGroupInputContainer = (props: LabelGroupInputProps) => ( <GroupInputProvider> <LabelGroupInput {...props} /> </GroupInputProvider> ); LabelGroupInputContainer.Element = Element; - -export default LabelGroupInputContainer; diff --git a/HDesign/src/components/LabelInput/LabelInput.tsx b/HDesign/src/components/LabelInput/LabelInput.tsx index d3e9f117d..073daf4a4 100644 --- a/HDesign/src/components/LabelInput/LabelInput.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.tsx @@ -1,12 +1,14 @@ /** @jsxImportSource @emotion/react */ +import {forwardRef, useImperativeHandle, useRef} from 'react'; + import Text from '@components/Text/Text'; import {useTheme} from '@/theme/HDesignProvider'; +import Input from '../Input/Input'; + import {LabelInputProps} from './LabelInput.type'; import {errorTextStyle, inputGroupStyle, labelGroupStyle, labelInputStyle, labelTextStyle} from './LabelInput.style'; -import Input from '../Input/Input'; -import {forwardRef, useImperativeHandle, useRef} from 'react'; import {useLabelInput} from './useLabelInput'; const LabelInput: React.FC<LabelInputProps> = forwardRef<HTMLInputElement, LabelInputProps>(function LabelInput( From 7701d7b6364b0481db2a94467acd883a69057047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Thu, 1 Aug 2024 09:52:28 +0900 Subject: [PATCH 11/43] =?UTF-8?q?chore:=20v0.1.47=20=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/package-lock.json | 4 ++-- HDesign/package.json | 2 +- HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx | 4 +++- .../src/components/LabelGroupInput/LabelGroupInput.type.ts | 3 --- HDesign/src/components/LabelGroupInput/index.ts | 3 +++ HDesign/src/index.tsx | 4 ++-- 6 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 HDesign/src/components/LabelGroupInput/index.ts diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index c7a1aa312..47f116bf0 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -1,12 +1,12 @@ { "name": "haengdong-design", - "version": "0.1.45", + "version": "0.1.47", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "haengdong-design", - "version": "0.1.45", + "version": "0.1.47", "license": "ISC", "dependencies": { "@emotion/react": "^11.11.4", diff --git a/HDesign/package.json b/HDesign/package.json index 16e6e4d39..7f379f996 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -1,6 +1,6 @@ { "name": "haengdong-design", - "version": "0.1.45", + "version": "0.1.47", "description": "", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx index 63bba57b8..cd1ca2ca9 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx @@ -37,10 +37,12 @@ const LabelGroupInput: React.FC<LabelGroupInputProps> = ({labelText, errorText, ); }; -export const LabelGroupInputContainer = (props: LabelGroupInputProps) => ( +const LabelGroupInputContainer = (props: LabelGroupInputProps) => ( <GroupInputProvider> <LabelGroupInput {...props} /> </GroupInputProvider> ); LabelGroupInputContainer.Element = Element; + +export default LabelGroupInputContainer; diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts b/HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts index a25d6caf1..cfa938457 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.type.ts @@ -1,11 +1,8 @@ export interface LabelGroupInputStyleProps {} export interface LabelGroupInputCustomProps { - values: string[]; labelText: string; - placeHolders?: string[]; errorText?: string; - errorIndexList?: number[]; } export type LabelGroupInputOptionProps = LabelGroupInputStyleProps & LabelGroupInputCustomProps; diff --git a/HDesign/src/components/LabelGroupInput/index.ts b/HDesign/src/components/LabelGroupInput/index.ts new file mode 100644 index 000000000..c31f35eaf --- /dev/null +++ b/HDesign/src/components/LabelGroupInput/index.ts @@ -0,0 +1,3 @@ +import LabelGroupInputContainer from './LabelGroupInput'; + +export {LabelGroupInputContainer as LabelGroupInput}; diff --git a/HDesign/src/index.tsx b/HDesign/src/index.tsx index b9c761239..f5bef35df 100644 --- a/HDesign/src/index.tsx +++ b/HDesign/src/index.tsx @@ -10,7 +10,7 @@ import InOutItem from '@components/InOutItem/InOutItem'; import Input from '@components/Input/Input'; import LabelInput from '@components/LabelInput/LabelInput'; // import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; -import {LabelGroupInputContainer as LabelGroupInput} from '@components/LabelGroupInput/LabelGroupInput'; +import LabelGroupInputContainer from '@components/LabelGroupInput/LabelGroupInput'; import Search from '@components/Search/Search'; import StepItem from '@components/StepItem/StepItem'; import Switch from '@components/Switch/Switch'; @@ -42,7 +42,7 @@ export { InOutItem, Input, LabelInput, - LabelGroupInput, + LabelGroupInputContainer as LabelGroupInput, Search, StepItem, Switch, From 261742d5d678151b2fef24147bcc8053aee7ecb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Thu, 1 Aug 2024 11:36:33 +0900 Subject: [PATCH 12/43] =?UTF-8?q?feat:=20v0.1.49=20=EB=B0=B0=ED=8F=AC=20in?= =?UTF-8?q?put=20sync=EA=B0=80=20=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EB=A7=9E?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8D=98=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20errorMessage=20=EC=A0=9C=EB=8C=80=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8D=98=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Soyeon Choe <soi-ha@users.noreply.github.com> --- HDesign/package-lock.json | 4 +- HDesign/package.json | 2 +- .../src/components/Input/Input.stories.tsx | 30 +++++++++----- HDesign/src/components/Input/Input.tsx | 2 +- HDesign/src/components/Input/useInput.ts | 6 +-- .../components/LabelGroupInput/Element.tsx | 10 ++++- .../LabelGroupInput.stories.tsx | 39 ++++++++++++++++--- .../LabelInput/LabelInput.stories.tsx | 20 +++++++++- .../src/components/LabelInput/LabelInput.tsx | 17 +++----- .../components/LabelInput/LabelInput.type.ts | 1 - .../components/LabelInput/useLabelInput.ts | 14 +------ 11 files changed, 93 insertions(+), 52 deletions(-) diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index 47f116bf0..e51db38e2 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -1,12 +1,12 @@ { "name": "haengdong-design", - "version": "0.1.47", + "version": "0.1.49", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "haengdong-design", - "version": "0.1.47", + "version": "0.1.49", "license": "ISC", "dependencies": { "@emotion/react": "^11.11.4", diff --git a/HDesign/package.json b/HDesign/package.json index 7f379f996..e290f07f7 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -1,6 +1,6 @@ { "name": "haengdong-design", - "version": "0.1.47", + "version": "0.1.49", "description": "", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/HDesign/src/components/Input/Input.stories.tsx b/HDesign/src/components/Input/Input.stories.tsx index 7a5e4da70..d7ca5c11e 100644 --- a/HDesign/src/components/Input/Input.stories.tsx +++ b/HDesign/src/components/Input/Input.stories.tsx @@ -1,27 +1,21 @@ +import React, {useEffect} from 'react'; + import type {Meta, StoryObj} from '@storybook/react'; import Input from '@components/Input/Input'; +import {useState} from 'react'; const meta = { title: 'Components/Input', component: Input, tags: ['autodocs'], - parameters: { - // layout: 'centered', - }, argTypes: { - value: { - description: '', - control: {type: 'text'}, - }, inputType: { // TODO: (@cookie) 스토리북 라디오버튼 보이도록 설정해야 함 control: {type: 'radio'}, }, }, args: { - value: '', - isError: false, placeholder: 'placeholder', }, } satisfies Meta<typeof Input>; @@ -30,4 +24,20 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Playground: Story = {}; +export const Playground: Story = { + render: ({...args}) => { + const [value, setValue] = useState(''); + const [isError, setIsError] = useState(false); + const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { + if (event.target.value.length < 4) { + setValue(event.target.value); + setIsError(false); + } else { + event.target.value = value; + setIsError(true); + } + }; + + return <Input value={value} onChange={e => handleChange(e)} isError={isError} {...args} />; + }, +}; diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index ac6a0def9..1fd612454 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @emotion/react */ -import React, {forwardRef, useEffect, useImperativeHandle, useRef} from 'react'; +import React, {forwardRef, useImperativeHandle, useRef} from 'react'; import IconButton from '@components/IconButton/IconButton'; import {InputProps} from '@components/Input/Input.type'; diff --git a/HDesign/src/components/Input/useInput.ts b/HDesign/src/components/Input/useInput.ts index 12505439c..1a7f3ac85 100644 --- a/HDesign/src/components/Input/useInput.ts +++ b/HDesign/src/components/Input/useInput.ts @@ -9,12 +9,12 @@ interface UseInputProps<T> { } export const useInput = <T>({propsValue, onChange, inputRef}: UseInputProps<T>) => { - const [value, setValue] = useState(propsValue || ''); + const [value, setValue] = useState(propsValue as string); const [hasFocus, setHasFocus] = useState(inputRef.current === document.activeElement); useEffect(() => { - setValue(propsValue || ''); - }, [propsValue]); + setValue(propsValue as string); + }, [value]); useEffect(() => { inputRef.current?.addEventListener('focus', () => setHasFocus(true)); diff --git a/HDesign/src/components/LabelGroupInput/Element.tsx b/HDesign/src/components/LabelGroupInput/Element.tsx index 151b8d0a0..d68c7318c 100644 --- a/HDesign/src/components/LabelGroupInput/Element.tsx +++ b/HDesign/src/components/LabelGroupInput/Element.tsx @@ -8,7 +8,7 @@ import {ElementProps} from './Element.type'; import {useGroupInputContext} from './GroupInputContext'; const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProps>(function Element( - {key, value, onChange, isError, ...htmlProps}: ElementProps, + {key, value, isError, ...htmlProps}: ElementProps, ref, ) { useImperativeHandle(ref, () => inputRef.current!); @@ -17,7 +17,13 @@ const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProp useEffect(() => { setHasErrors({...hasErrors, [key]: isError ?? false}); + }, [isError]); + useEffect(() => { + setValues({...values, [key]: `${value}` ?? ''}); + }, [value]); + + useEffect(() => { inputRef.current?.addEventListener('change', () => setValues({...values, [key]: `${value}`})); inputRef.current?.addEventListener('focus', () => setHasAnyFocus(true)); inputRef.current?.addEventListener('blur', () => setHasAnyFocus(false)); @@ -29,7 +35,7 @@ const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProp }; }, []); - return <Input ref={inputRef} isError={isError} {...htmlProps} />; + return <Input ref={inputRef} value={value} isError={isError} {...htmlProps} />; }); export default Element; diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx index 049a1ad02..66e38f568 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx @@ -2,6 +2,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; +import {useState} from 'react'; const meta = { title: 'Components/LabelGroupInput', @@ -31,10 +32,36 @@ export default meta; type Story = StoryObj<typeof meta>; export const Playground: Story = { - render: ({...args}) => ( - <LabelGroupInput {...args}> - <LabelGroupInput.Element key="name" placeholder="지출내역" isError={false} /> - <LabelGroupInput.Element key="price" placeholder="금액" isError={false} /> - </LabelGroupInput> - ), + render: ({...args}) => { + const [name, setName] = useState(''); + const [isError, setIsError] = useState(false); + const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { + if (event.target.value.length < 4) { + setName(event.target.value); + setIsError(false); + } else { + event.target.value = name; + setIsError(true); + } + }; + const [price, setPrice] = useState(''); + return ( + <LabelGroupInput {...args}> + <LabelGroupInput.Element + key="name" + placeholder="지출내역" + value={name} + onChange={handleChange} + isError={isError} + /> + <LabelGroupInput.Element + value={price} + onChange={e => setPrice(e.target.value)} + key="price" + placeholder="금액" + isError={false} + /> + </LabelGroupInput> + ); + }, }; diff --git a/HDesign/src/components/LabelInput/LabelInput.stories.tsx b/HDesign/src/components/LabelInput/LabelInput.stories.tsx index e65f859f6..62742e287 100644 --- a/HDesign/src/components/LabelInput/LabelInput.stories.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.stories.tsx @@ -2,6 +2,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import LabelInput from '@components/LabelInput/LabelInput'; +import {useEffect, useState} from 'react'; const meta = { title: 'Components/LabelInput', @@ -25,7 +26,7 @@ const meta = { }, }, args: { - value: '', + // value: '', labelText: '이름', errorText: 'error가 발생했을 때 나타납니다!', }, @@ -35,4 +36,19 @@ export default meta; type Story = StoryObj<typeof meta>; -export const Playground: Story = {}; +export const Playground: Story = { + render: ({...args}) => { + const [value, setValue] = useState(''); + const [isError, setIsError] = useState(false); + const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { + if (event.target.value.length < 4) { + setValue(event.target.value); + setIsError(false); + } else { + event.target.value = value; + setIsError(true); + } + }; + return <LabelInput value={value} onChange={e => handleChange(e)} isError={isError} {...args} />; + }, +}; diff --git a/HDesign/src/components/LabelInput/LabelInput.tsx b/HDesign/src/components/LabelInput/LabelInput.tsx index 073daf4a4..38221475f 100644 --- a/HDesign/src/components/LabelInput/LabelInput.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @emotion/react */ -import {forwardRef, useImperativeHandle, useRef} from 'react'; +import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react'; import Text from '@components/Text/Text'; import {useTheme} from '@/theme/HDesignProvider'; @@ -12,20 +12,20 @@ import {errorTextStyle, inputGroupStyle, labelGroupStyle, labelInputStyle, label import {useLabelInput} from './useLabelInput'; const LabelInput: React.FC<LabelInputProps> = forwardRef<HTMLInputElement, LabelInputProps>(function LabelInput( - {value: propsValue, onChange, labelText, errorText, isError, ...htmlProps}: LabelInputProps, + {labelText, errorText, isError, ...htmlProps}: LabelInputProps, ref, ) { useImperativeHandle(ref, () => inputRef.current!); const {theme} = useTheme(); const inputRef = useRef<HTMLInputElement>(null); - const {value, handleChange, hasFocus} = useLabelInput({inputRef, propsValue, onChange}); + const {hasFocus} = useLabelInput({inputRef}); return ( <div css={labelInputStyle}> <div css={labelGroupStyle}> <label> <Text size="caption" css={labelTextStyle(theme)}> - {(hasFocus || value) && labelText} + {(hasFocus || htmlProps.value) && labelText} </Text> </label> @@ -34,14 +34,7 @@ const LabelInput: React.FC<LabelInputProps> = forwardRef<HTMLInputElement, Label </Text> </div> <div css={inputGroupStyle}> - <Input - value={value} - onChange={handleChange} - ref={inputRef} - isError={isError} - placeholder={labelText} - {...htmlProps} - /> + <Input ref={inputRef} isError={isError} placeholder={labelText} {...htmlProps} /> </div> </div> ); diff --git a/HDesign/src/components/LabelInput/LabelInput.type.ts b/HDesign/src/components/LabelInput/LabelInput.type.ts index 39deb1fb0..ae0d0c5f6 100644 --- a/HDesign/src/components/LabelInput/LabelInput.type.ts +++ b/HDesign/src/components/LabelInput/LabelInput.type.ts @@ -1,7 +1,6 @@ export interface LabelInputStyleProps {} export interface LabelInputCustomProps { - value: string; labelText: string; errorText?: string; isError?: boolean; diff --git a/HDesign/src/components/LabelInput/useLabelInput.ts b/HDesign/src/components/LabelInput/useLabelInput.ts index fedd01423..ed64fd8bc 100644 --- a/HDesign/src/components/LabelInput/useLabelInput.ts +++ b/HDesign/src/components/LabelInput/useLabelInput.ts @@ -2,12 +2,9 @@ import {RefObject, useEffect, useState} from 'react'; interface UseLabelInput<T> { inputRef: RefObject<HTMLInputElement>; - propsValue: T; - onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; } -export const useLabelInput = <T>({inputRef, propsValue, onChange}: UseLabelInput<T>) => { - const [value, setValue] = useState(propsValue || ''); +export const useLabelInput = <T>({inputRef}: UseLabelInput<T>) => { const [hasFocus, setHasFocus] = useState(inputRef.current === document.activeElement); useEffect(() => { @@ -20,12 +17,5 @@ export const useLabelInput = <T>({inputRef, propsValue, onChange}: UseLabelInput }; }, []); - const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValue(e.target.value); - if (onChange) { - onChange(e); - } - }; - - return {value, hasFocus, handleChange}; + return {hasFocus}; }; From 014363e7e41687d628590b184456140ef3ea95cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Thu, 1 Aug 2024 16:14:53 +0900 Subject: [PATCH 13/43] =?UTF-8?q?feat:=20v0.1.51=20=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/package-lock.json | 4 +- HDesign/package.json | 2 +- .../src/components/Input/Input.stories.tsx | 5 +- HDesign/src/components/Input/Input.tsx | 11 +++- HDesign/src/components/Input/useInput.ts | 30 +++++----- .../components/LabelGroupInput/Element.tsx | 59 +++++++++++++------ .../LabelGroupInput/Element.type.ts | 2 +- .../LabelGroupInput.stories.tsx | 15 +++-- 8 files changed, 83 insertions(+), 45 deletions(-) diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index e51db38e2..94092065b 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -1,12 +1,12 @@ { "name": "haengdong-design", - "version": "0.1.49", + "version": "0.1.51", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "haengdong-design", - "version": "0.1.49", + "version": "0.1.51", "license": "ISC", "dependencies": { "@emotion/react": "^11.11.4", diff --git a/HDesign/package.json b/HDesign/package.json index e290f07f7..f4fe0752e 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -1,6 +1,6 @@ { "name": "haengdong-design", - "version": "0.1.49", + "version": "0.1.51", "description": "", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/HDesign/src/components/Input/Input.stories.tsx b/HDesign/src/components/Input/Input.stories.tsx index d7ca5c11e..f13deb826 100644 --- a/HDesign/src/components/Input/Input.stories.tsx +++ b/HDesign/src/components/Input/Input.stories.tsx @@ -37,7 +37,10 @@ export const Playground: Story = { setIsError(true); } }; + const handleBlur = () => { + console.log('blur'); + }; - return <Input value={value} onChange={e => handleChange(e)} isError={isError} {...args} />; + return <Input value={value} onChange={e => handleChange(e)} isError={isError} onBlur={handleBlur} {...args} />; }, }; diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index 1fd612454..8fdde0729 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -15,7 +15,13 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro useImperativeHandle(ref, () => inputRef.current!); const {theme} = useTheme(); const inputRef = useRef<HTMLInputElement>(null); - const {value, handleChange, hasFocus, handleClickDelete} = useInput({propsValue, onChange, inputRef}); + const {value, handleChange, hasFocus, handleClickDelete, handleBlur, handleFocus, handleKeyDown} = useInput({ + propsValue, + onChange, + onBlur, + onFocus, + inputRef, + }); return ( <div css={inputBoxStyle(theme, inputType, hasFocus, isError)}> @@ -24,7 +30,10 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro ref={inputRef} value={value} onChange={handleChange} + onBlur={handleBlur} + onFocus={handleFocus} placeholder={inputRef.current === document.activeElement ? '' : placeholder} + onKeyDown={handleKeyDown} {...htmlProps} /> {value && hasFocus && <IconButton iconType="inputDelete" onMouseDown={handleClickDelete} />} diff --git a/HDesign/src/components/Input/useInput.ts b/HDesign/src/components/Input/useInput.ts index 1a7f3ac85..18ea6b0ac 100644 --- a/HDesign/src/components/Input/useInput.ts +++ b/HDesign/src/components/Input/useInput.ts @@ -8,7 +8,7 @@ interface UseInputProps<T> { inputRef: RefObject<HTMLInputElement>; } -export const useInput = <T>({propsValue, onChange, inputRef}: UseInputProps<T>) => { +export const useInput = <T>({propsValue, onChange, onBlur, onFocus, inputRef}: UseInputProps<T>) => { const [value, setValue] = useState(propsValue as string); const [hasFocus, setHasFocus] = useState(inputRef.current === document.activeElement); @@ -16,18 +16,6 @@ export const useInput = <T>({propsValue, onChange, inputRef}: UseInputProps<T>) setValue(propsValue as string); }, [value]); - useEffect(() => { - inputRef.current?.addEventListener('focus', () => setHasFocus(true)); - inputRef.current?.addEventListener('blur', () => setHasFocus(false)); - inputRef.current?.addEventListener('keydown', () => handleKeyDown); - - return () => { - inputRef.current?.removeEventListener('focus', () => setHasFocus(true)); - inputRef.current?.removeEventListener('blur', () => setHasFocus(false)); - inputRef.current?.addEventListener('keydown', () => handleKeyDown); - }; - }, []); - const handleClickDelete = (event: React.MouseEvent) => { event.preventDefault(); setValue(''); @@ -46,6 +34,20 @@ export const useInput = <T>({propsValue, onChange, inputRef}: UseInputProps<T>) } }; + const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => { + setHasFocus(false); + if (onBlur) { + onBlur(e); + } + }; + + const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => { + setHasFocus(true); + if (onFocus) { + onFocus(e); + } + }; + const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => { if (event.nativeEvent.isComposing) return; @@ -57,5 +59,5 @@ export const useInput = <T>({propsValue, onChange, inputRef}: UseInputProps<T>) } }; - return {value, handleChange, hasFocus, handleClickDelete}; + return {value, handleChange, hasFocus, handleClickDelete, handleBlur, handleFocus, handleKeyDown}; }; diff --git a/HDesign/src/components/LabelGroupInput/Element.tsx b/HDesign/src/components/LabelGroupInput/Element.tsx index d68c7318c..8db8dbd99 100644 --- a/HDesign/src/components/LabelGroupInput/Element.tsx +++ b/HDesign/src/components/LabelGroupInput/Element.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @emotion/react */ -import {forwardRef, useEffect, useImperativeHandle, useRef} from 'react'; +import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react'; import Input from '../Input/Input'; @@ -8,7 +8,8 @@ import {ElementProps} from './Element.type'; import {useGroupInputContext} from './GroupInputContext'; const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProps>(function Element( - {key, value, isError, ...htmlProps}: ElementProps, + {elementKey, value: propsValue, onChange, onBlur, onFocus, isError, ...htmlProps}: ElementProps, + ref, ) { useImperativeHandle(ref, () => inputRef.current!); @@ -16,26 +17,46 @@ const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProp const {setHasAnyFocus, values, setValues, hasErrors, setHasErrors} = useGroupInputContext(); useEffect(() => { - setHasErrors({...hasErrors, [key]: isError ?? false}); - }, [isError]); + setValues({...values, [elementKey]: `${propsValue}` ?? ''}); + }, [propsValue]); - useEffect(() => { - setValues({...values, [key]: `${value}` ?? ''}); - }, [value]); + const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const newValue = e.target.value; + setValues({...values, [elementKey]: newValue}); + if (onChange) { + onChange(e); + } + }; useEffect(() => { - inputRef.current?.addEventListener('change', () => setValues({...values, [key]: `${value}`})); - inputRef.current?.addEventListener('focus', () => setHasAnyFocus(true)); - inputRef.current?.addEventListener('blur', () => setHasAnyFocus(false)); - - return () => { - inputRef.current?.removeEventListener('change', () => setValues({...values, [key]: `${value}`})); - inputRef.current?.removeEventListener('focus', () => setHasAnyFocus(true)); - inputRef.current?.removeEventListener('blur', () => setHasAnyFocus(false)); - }; - }, []); - - return <Input ref={inputRef} value={value} isError={isError} {...htmlProps} />; + setHasErrors({...hasErrors, [elementKey]: isError ?? false}); + }, [isError]); + + const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => { + setHasAnyFocus(false); + if (onBlur) { + onBlur(e); + } + }; + + const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => { + setHasAnyFocus(true); + if (onFocus) { + onFocus(e); + } + }; + + return ( + <Input + ref={inputRef} + isError={isError} + value={propsValue} + onChange={handleChange} + onBlur={handleBlur} + onFocus={handleFocus} + {...htmlProps} + /> + ); }); export default Element; diff --git a/HDesign/src/components/LabelGroupInput/Element.type.ts b/HDesign/src/components/LabelGroupInput/Element.type.ts index edf039d17..2ec2ef9ed 100644 --- a/HDesign/src/components/LabelGroupInput/Element.type.ts +++ b/HDesign/src/components/LabelGroupInput/Element.type.ts @@ -1,7 +1,7 @@ export interface ElementStyleProps {} export interface ElementCustomProps { - key: string; + elementKey: string; isError?: boolean; } diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx index 66e38f568..2a4f87131 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx @@ -34,8 +34,9 @@ type Story = StoryObj<typeof meta>; export const Playground: Story = { render: ({...args}) => { const [name, setName] = useState(''); + const [price, setPrice] = useState(''); const [isError, setIsError] = useState(false); - const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { + const handleChangeName = (event: React.ChangeEvent<HTMLInputElement>) => { if (event.target.value.length < 4) { setName(event.target.value); setIsError(false); @@ -44,20 +45,22 @@ export const Playground: Story = { setIsError(true); } }; - const [price, setPrice] = useState(''); + const handleChangePrice = (event: React.ChangeEvent<HTMLInputElement>) => { + setPrice(event.target.value); + }; return ( <LabelGroupInput {...args}> <LabelGroupInput.Element - key="name" + elementKey="name" placeholder="지출내역" value={name} - onChange={handleChange} + onChange={e => handleChangeName(e)} isError={isError} /> <LabelGroupInput.Element value={price} - onChange={e => setPrice(e.target.value)} - key="price" + onChange={handleChangePrice} + elementKey="price" placeholder="금액" isError={false} /> From 6547b10465ac07f31cece1cea3892f1e85208825 Mon Sep 17 00:00:00 2001 From: Soyeon Choe <soy2302ten@gmail.com> Date: Fri, 2 Aug 2024 06:58:25 +0900 Subject: [PATCH 14/43] =?UTF-8?q?feat:=20DynamicInput=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: TaehunLee <85233397+Todari@users.noreply.github.com> --- client/package-lock.json | 94 +++++++++---------- client/package.json | 2 +- .../SetActionModalContent/SetPurchase.tsx | 11 ++- .../UpdateParticipants.tsx | 41 ++++---- .../SetInitialParticipants.tsx | 34 ++++--- client/src/constants/errorMessage.ts | 6 +- client/src/constants/regExp.ts | 3 +- client/src/constants/rule.ts | 7 ++ client/src/hooks/useDynamicInput.tsx | 52 +++++++--- client/src/hooks/useDynamicInputPairs.tsx | 47 +++++++--- client/src/pages/Create/Name.tsx | 21 +++-- client/src/utils/generateUniqueId.ts | 7 ++ .../src/utils/validate/validateEventName.ts | 3 +- .../src/utils/validate/validateMemberName.ts | 18 +++- client/src/utils/validate/validatePurchase.ts | 35 +++++++ 15 files changed, 257 insertions(+), 124 deletions(-) create mode 100644 client/src/constants/rule.ts create mode 100644 client/src/utils/generateUniqueId.ts create mode 100644 client/src/utils/validate/validatePurchase.ts diff --git a/client/package-lock.json b/client/package-lock.json index de125cf59..b1714ab4a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,7 +12,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.45", + "haengdong-design": "^0.1.51", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" @@ -2573,9 +2573,9 @@ } }, "node_modules/@swc/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.3.tgz", - "integrity": "sha512-HHAlbXjWI6Kl9JmmUW1LSygT1YbblXgj2UvvDzMkTBPRzYMhW6xchxdO8HbtMPtFYRt/EQq9u1z7j4ttRSrFsA==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.4.tgz", + "integrity": "sha512-+wSycNxOw9QQz81AJAZlNS34EtOIifwUXMPACg05PWjECsjOKDTXLCVPx6J0lRaxhHSGBU2OYs9mRfIvxGt3CA==", "hasInstallScript": true, "dependencies": { "@swc/counter": "^0.1.3", @@ -2589,16 +2589,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.7.3", - "@swc/core-darwin-x64": "1.7.3", - "@swc/core-linux-arm-gnueabihf": "1.7.3", - "@swc/core-linux-arm64-gnu": "1.7.3", - "@swc/core-linux-arm64-musl": "1.7.3", - "@swc/core-linux-x64-gnu": "1.7.3", - "@swc/core-linux-x64-musl": "1.7.3", - "@swc/core-win32-arm64-msvc": "1.7.3", - "@swc/core-win32-ia32-msvc": "1.7.3", - "@swc/core-win32-x64-msvc": "1.7.3" + "@swc/core-darwin-arm64": "1.7.4", + "@swc/core-darwin-x64": "1.7.4", + "@swc/core-linux-arm-gnueabihf": "1.7.4", + "@swc/core-linux-arm64-gnu": "1.7.4", + "@swc/core-linux-arm64-musl": "1.7.4", + "@swc/core-linux-x64-gnu": "1.7.4", + "@swc/core-linux-x64-musl": "1.7.4", + "@swc/core-win32-arm64-msvc": "1.7.4", + "@swc/core-win32-ia32-msvc": "1.7.4", + "@swc/core-win32-x64-msvc": "1.7.4" }, "peerDependencies": { "@swc/helpers": "*" @@ -2610,9 +2610,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.3.tgz", - "integrity": "sha512-CTkHa6MJdov9t41vuV2kmQIMu+Q19LrEHGIR/UiJYH06SC/sOu35ZZH8DyfLp9ZoaCn21gwgWd61ixOGQlwzTw==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.4.tgz", + "integrity": "sha512-RbWrdGh+x9xKFUA9/kPZRR8OPxUsDUuPyLjPIGLYZMO+ftht2vhVH7QsUq6lg+jAP34eIya72UA1isiZe+BRaA==", "cpu": [ "arm64" ], @@ -2625,9 +2625,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.3.tgz", - "integrity": "sha512-mun623y6rCoZ2EFIYfIRqXYRFufJOopoYSJcxYhZUrfTpAvQ1zLngjQpWCUU1krggXR2U0PQj+ls0DfXUTraNg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.4.tgz", + "integrity": "sha512-TxCWMJs4OrqApjFuT8cUiqMz0zg97F0JsXBEeZ7zjkyv9XJ/rN2pdwqMlZv0Wv2C2rivOPo6FsWYlZ3V8ZHhyA==", "cpu": [ "x64" ], @@ -2640,9 +2640,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.3.tgz", - "integrity": "sha512-4Jz4UcIcvZNMp9qoHbBx35bo3rjt8hpYLPqnR4FFq6gkAsJIMFC56UhRZwdEQoDuYiOFMBnnrsg31Fyo6YQypA==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.4.tgz", + "integrity": "sha512-5IhwIJZAgkkfI6PqgQ3xk0/2hTAVsAczIPLiR2Epp30EgsNo1KIFL0ZHzrnvJPy5BZ3jy3T1dEbDE/memBOEmA==", "cpu": [ "arm" ], @@ -2655,9 +2655,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.3.tgz", - "integrity": "sha512-p+U/M/oqV7HC4erQ5TVWHhJU1984QD+wQBPxslAYq751bOQGm0R/mXK42GjugqjnR6yYrAiwKKbpq4iWVXNePA==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.4.tgz", + "integrity": "sha512-0787jri83jigf26mF8FndWehh7jqMaHwAm/OV6VdToyNo/g+d1AxVpkEizrywZK46el+AObnHUIHIHwZgO21LA==", "cpu": [ "arm64" ], @@ -2670,9 +2670,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.3.tgz", - "integrity": "sha512-s6VzyaJwaRGTi2mz2h6Ywxfmgpkc69IxhuMzl+sl34plH0V0RgnZDm14HoCGIKIzRk4+a2EcBV1ZLAfWmPACQg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.4.tgz", + "integrity": "sha512-A45hGKWAGcjU5Ol0uQUoK0tHerwEKxfprYUZbmPLpD2yrpMZr+dTrwY2n075sixs7RuZEccBkgGNpehEe5BPBQ==", "cpu": [ "arm64" ], @@ -2685,9 +2685,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.3.tgz", - "integrity": "sha512-IrFY48C356Z2dU2pjYg080yvMXzmSV3Lmm/Wna4cfcB1nkVLjWsuYwwRAk9CY7E19c+q8N1sMNggubAUDYoX2g==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.4.tgz", + "integrity": "sha512-bcO1MpAm39TXqqHuYW4ox4vDvhB7jkguwMwxvmL+cKBGsUHrIoUTfGt9NM9N4D4CvOwULlxqbyt19veUJ7CVPw==", "cpu": [ "x64" ], @@ -2700,9 +2700,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.3.tgz", - "integrity": "sha512-qoLgxBlBnnyUEDu5vmRQqX90h9jldU1JXI96e6eh2d1gJyKRA0oSK7xXmTzorv1fGHiHulv9qiJOUG+g6uzJWg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.4.tgz", + "integrity": "sha512-N6nXuHyDO/q5kPN2xQxz5BEvhFpgnFSkP+9wxg5xWq+qIQL5bv37jk8dkKvMLx/8fHzTqrIjPDSRzVbcL7sqXg==", "cpu": [ "x64" ], @@ -2715,9 +2715,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.3.tgz", - "integrity": "sha512-OAd7jVVJ7nb0Ev80VAa1aeK+FldPeC4eZ35H4Qn6EICzIz0iqJo2T33qLKkSZiZEBKSoF4KcwrqYfkjLOp5qWg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.4.tgz", + "integrity": "sha512-7W1owqCNR1cG+mpS55juiZlR/lrAdxB1pH32egeOipNKOLGwyqmlzQ0g9tkQTNgzwgfpCUg8z606+GqqXvajZw==", "cpu": [ "arm64" ], @@ -2730,9 +2730,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.3.tgz", - "integrity": "sha512-31+Le1NyfSnILFV9+AhxfFOG0DK0272MNhbIlbcv4w/iqpjkhaOnNQnLsYJD1Ow7lTX1MtIZzTjOhRlzSviRWg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.4.tgz", + "integrity": "sha512-saLkY+q7zNPk4gYiUBCc93FYPo4ECXMjHcSPtLVHoPZBIxRrklgaAf6aDpblBo30nVdoBE2V3YPd0Y/cPiY6RQ==", "cpu": [ "ia32" ], @@ -2745,9 +2745,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.3.tgz", - "integrity": "sha512-jVQPbYrwcuueI4QB0fHC29SVrkFOBcfIspYDlgSoHnEz6tmLMqUy+txZUypY/ZH/KaK0HEY74JkzgbRC1S6LFQ==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.4.tgz", + "integrity": "sha512-zKF6jpRBNuVKgOf2W5dMcPyjwcNCp21syjl9lvLRbCeIg+1U+zjdoQCAmMWWoPNE7fLg+yfvohnnOJG2AdzQ9Q==", "cpu": [ "x64" ], @@ -6486,9 +6486,9 @@ "dev": true }, "node_modules/haengdong-design": { - "version": "0.1.45", - "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.45.tgz", - "integrity": "sha512-s3Xf7xRPWHfcFF4tiG470eHa1+iaBgLNrRKXaTYpmfJTO5vDV7g+zEboq0jno1zTrD0bS7QTPRE+4A5R9OlbDg==", + "version": "0.1.51", + "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.51.tgz", + "integrity": "sha512-7/9FGKS3NNjyXkH8h2U7IlHkBcFROus8iXJejQR8qsvAtu/OZrM/0ebXEZ24LP7Kps3AaaD2Cv9YyfnWI/KrzA==", "dependencies": { "@emotion/react": "^11.11.4", "@storybook/addon-webpack5-compiler-swc": "^1.0.5", diff --git a/client/package.json b/client/package.json index 6b999f6b3..65fc9b99c 100644 --- a/client/package.json +++ b/client/package.json @@ -47,7 +47,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.45", + "haengdong-design": "^0.1.51", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" diff --git a/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx b/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx index a83a697a7..7244c1808 100644 --- a/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx +++ b/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx @@ -1,6 +1,7 @@ import {Input, FixedButton} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; +import validatePurchase from '@utils/validate/validatePurchase'; import useDynamicInputPairs from '@hooks/useDynamicInputPairs'; @@ -12,21 +13,21 @@ interface SetPurchaseProps { } const SetPurchase = ({setOpenBottomSheet, setOrder}: SetPurchaseProps) => { - const {inputPairs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputPairs} = useDynamicInputPairs(); + // const {inputPairs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputPairs} = useDynamicInputPairs(validatePurchase); const {addBill} = useStepList(); const handleSetPurchaseSubmit = () => { setOrder(prev => prev + 1); // TODO: (@weadie) 요청 실패시 오류 핸들 필요 - addBill(getNonEmptyInputPairs()); + // addBill(getNonEmptyInputPairs()); setOpenBottomSheet(false); }; return ( <div css={setPurchaseStyle}> <div css={setPurchaseInputContainerStyle}> - {inputPairs.map((pair, index) => ( + {/* {inputPairs.map((pair, index) => ( <div key={index} css={setPurchaseInputStyle}> <Input type="text" @@ -45,10 +46,10 @@ const SetPurchase = ({setOpenBottomSheet, setOrder}: SetPurchaseProps) => { ref={el => (inputRefs.current[index * 2 + 1] = el)} /> </div> - ))} + ))} */} </div> <FixedButton - disabled={!(inputPairs.length - 1)} + // disabled={!(inputPairs.length - 1)} variants={'primary'} children={'추가하기'} onClick={handleSetPurchaseSubmit} diff --git a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx index 16b687a61..0bffa5a00 100644 --- a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx +++ b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx @@ -1,8 +1,9 @@ -import {Input, FixedButton} from 'haengdong-design'; +import {Input, FixedButton, LabelGroupInput} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; +import validateMemberName from '@utils/validate/validateMemberName'; -// import useDynamicInput from '@hooks/useDynamicInput'; +import useDynamicInput from '@hooks/useDynamicInput'; import {updateParticipantsInputStyle, updateParticipantsStyle} from './UpdateParticipants.style'; @@ -12,34 +13,38 @@ interface UpdateParticipantsProps { } const UpdateParticipants = ({inOutAction, setOpenBottomSheet}: UpdateParticipantsProps) => { - // const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs} = useDynamicInput(); + const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs, errorMessage, canSubmit} = + useDynamicInput(validateMemberName); const {updateMemberList} = useStepList(); const handleUpdateParticipantsSubmit = () => { - // updateMemberList({memberNameList: getNonEmptyInputs(), type: inOutAction}); - // setOpenBottomSheet(false); + updateMemberList({memberNameList: getNonEmptyInputs(), type: inOutAction}); + setOpenBottomSheet(false); }; return ( <div css={updateParticipantsStyle}> <div css={updateParticipantsInputStyle}> {/* TODO: (@soha) Search로 변경하기 */} - {/* {inputs.map((name, index) => ( - <Input - key={index} - placeholder="이름" - value={name} - type="text" - ref={el => (inputRefs.current[index] = el)} - onChange={e => handleInputChange(index, e.target.value)} - onBlur={() => handleInputBlur(index)} - /> - ))} */} + <LabelGroupInput labelText="이름" errorText={errorMessage}> + {inputs.map((name, index) => ( + <LabelGroupInput.Element + elementKey={`${index}`} + value={name} + type="text" + ref={el => (inputRefs.current[index] = el)} + onChange={e => handleInputChange(index, e)} + onBlur={() => handleInputBlur(index)} + placeholder="이름" + autoFocus + /> + ))} + </LabelGroupInput> </div> <FixedButton - // disabled={!(inputs.length - 1)} + disabled={!canSubmit} variants={'primary'} - // children={`${inputs.length - 1}명 ${inOutAction === 'OUT' ? '탈주' : '늦참'}`} + children={`${inputs.length - 1}명 ${inOutAction === 'OUT' ? '탈주' : '늦참'}`} onClick={handleUpdateParticipantsSubmit} /> </div> diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx index fa668557e..6cc4e35ef 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx @@ -1,4 +1,4 @@ -import {Text, Input, BottomSheet, FixedButton, LabelInput} from 'haengdong-design'; +import {Text, Input, BottomSheet, FixedButton, LabelGroupInput} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; import validateMemberName from '@utils/validate/validateMemberName'; @@ -13,7 +13,7 @@ interface SetInitialParticipantsProps { } const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitialParticipantsProps) => { - const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs, errorMessage} = + const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs, errorMessage, canSubmit} = useDynamicInput(validateMemberName); const {updateMemberList} = useStepList(); @@ -27,27 +27,37 @@ const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitia <div css={setInitialParticipantsStyle}> <Text size="bodyBold">초기 인원 설정하기</Text> <div css={setInitialParticipantsInputGroupStyle}> - <LabelInput labelText="이름" errorText={errorMessage}> + <LabelGroupInput labelText="이름" errorText={errorMessage}> {inputs.map((input, index) => ( - <Input - key={index} + // <LabelGroupInput.Element + // elementKey={`${index}`} + // key={`${index}`} + // type="text" + // value={input} + // ref={el => (inputRefs.current[index] = el)} + // onChange={e => handleInputChange(index, e)} + // onBlur={() => handleInputBlur(index)} + // isError={!!errorMessage} + // placeholder="이름" + // autoFocus + // /> + <input + // elementKey={`${index}`} + key={`${index}`} type="text" value={input} ref={el => (inputRefs.current[index] = el)} onChange={e => handleInputChange(index, e)} onBlur={() => handleInputBlur(index)} + // isError={!!errorMessage} + placeholder="이름" autoFocus /> ))} - </LabelInput> + </LabelGroupInput> </div> </div> - <FixedButton - disabled={!(inputs.length - 1)} - variants={'primary'} - onClick={handleSubmit} - children={'인원 설정 완료'} - /> + <FixedButton disabled={!canSubmit} variants={'primary'} onClick={handleSubmit} children={'인원 설정 완료'} /> </BottomSheet> ); }; diff --git a/client/src/constants/errorMessage.ts b/client/src/constants/errorMessage.ts index a72be530c..e37220f7f 100644 --- a/client/src/constants/errorMessage.ts +++ b/client/src/constants/errorMessage.ts @@ -1,6 +1,8 @@ const ERROR_MESSAGE = { - eventName: '행사 이름은 30자 이하로만 가능해요', - memberName: '참여자 이름은 30자 이하의 한글만 가능해요', + eventName: '행사 이름은 30자 이하만 가능해요', + memberName: '참여자 이름은 8자 이하의 한글, 영어만 가능해요', + purchasePrice: '10,000,000원 이하의 숫자만 입력이 가능해요', + purchaseTitle: '지출 이름은 30자 이하의 한글, 영어, 숫자만 가능해요', }; export default ERROR_MESSAGE; diff --git a/client/src/constants/regExp.ts b/client/src/constants/regExp.ts index c4846490f..674be7ba2 100644 --- a/client/src/constants/regExp.ts +++ b/client/src/constants/regExp.ts @@ -1,5 +1,6 @@ const REGEXP = { - memberName: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z\s]{1,30}$/, + memberName: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z\s]*$/, + purchaseTitle: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9\s]*$/, }; export default REGEXP; diff --git a/client/src/constants/rule.ts b/client/src/constants/rule.ts new file mode 100644 index 000000000..0001cb73a --- /dev/null +++ b/client/src/constants/rule.ts @@ -0,0 +1,7 @@ +const RULE = { + maxEventNameLength: 30, + maxMemberNameLength: 8, + maxPrice: 10000000, +}; + +export default RULE; diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index c12ed7e20..7a26a8d4e 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -6,6 +6,8 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { const [inputs, setInputs] = useState<string[]>(['']); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); const [errorMessage, setErrorMessage] = useState(''); + const [canSubmit, setCanSubmit] = useState(false); + // const [errorIds, setErrorIds] = useState<string>(''); // TODO: (@soha) 입력이 완료되고 중간에 값을 모두 지웠을 경우 Input이 없애지도록 수정하기 const handleInputChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => { @@ -19,28 +21,51 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { return newInputs; }); setErrorMessage(''); + // setErrorIds(prevIds => prevIds.filter(errorId => errorId !== id)); } else { event.target.value = inputs[index]; setErrorMessage(validation.errorMessage ?? ''); + // setErrorIds(prevIds => [...new Set([...prevIds, id])]); + } + + if (value.length > 0 && inputs.length > 0) { + setCanSubmit(true); + } else { + setCanSubmit(false); } }; const handleInputBlur = (index: number) => { + // const newInputs = inputs.map((input, idx) => (idx === index ? input : '')); const filterEmpty = inputs.filter(input => input.trim() !== ''); - if (filterEmpty.length !== inputs.length) { - setInputs(prev => { - const removeEmpty = prev.filter(value => value.trim() !== ''); - return [...removeEmpty, '']; - }); - } - if (inputs[index].trim() !== '' && index === inputs.length - 1) { - setInputs(prev => { - const newInputs = [...prev, '']; - newInputs[index] = inputs[index]; - return newInputs; - }); - } + console.log(filterEmpty, '!!!!!!'); + + setInputs([...filterEmpty, '']); + + // if (filterEmpty.length !== inputs.length) { + // setInputs(prev => { + // const notEmpty = prev.filter((input, index) => { + // if (index === prev.length - 1) return; + // return input.trim() !== ''; + // }); + // console.log(notEmpty, '@@@'); + // console.log([...notEmpty, '']); + // return [...notEmpty, '']; + // }); + // } + + // if (!inputs.every(input => input !== '')) { + // setInputs(prev => prev.filter(input => input !== '')); + // } else { + // setInputs(prev => { + // console.log(prev, 'ore~!~~~~~~'); + // const newInputs = [...prev, '']; + // // newInputs[index] = inputs[index]; + // return newInputs; + // }); + // } + // console.log(inputs, '@@@@@@@@@'); }; const getNonEmptyInputs = () => { @@ -63,6 +88,7 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { handleInputBlur, errorMessage, getNonEmptyInputs, + canSubmit, }; }; diff --git a/client/src/hooks/useDynamicInputPairs.tsx b/client/src/hooks/useDynamicInputPairs.tsx index b6f86b000..220598fd6 100644 --- a/client/src/hooks/useDynamicInputPairs.tsx +++ b/client/src/hooks/useDynamicInputPairs.tsx @@ -1,29 +1,52 @@ import {useEffect, useRef, useState} from 'react'; -const useDynamicInputPairs = () => { +import {ValidateResult} from '@utils/validate/type'; + +const useDynamicInputPairs = (validateFunc: (inputPair: Bill) => ValidateResult) => { const [inputPairs, setInputPairs] = useState<Bill[]>([{title: '', price: 0}]); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); + const [errorMessage, setErrorMessage] = useState(''); + const [canSubmit, setCanSubmit] = useState(false); + + const handleInputChange = (index: number, field: 'title' | 'price', event: React.ChangeEvent<HTMLInputElement>) => { + const value = event.target.value; + const currentPair = inputPairs[index]; + const validation = validateFunc(currentPair); + + if (validation.isValid) { + const newInputPairs = [...inputPairs]; + newInputPairs[index] = { + ...newInputPairs[index], + [field]: field === 'price' ? parseFloat(value) : value, + }; + setInputPairs(newInputPairs); + setErrorMessage(''); + } else { + event.target.value = inputPairs[index][field].toString(); + setErrorMessage(validation.errorMessage ?? ''); + } - const handleInputChange = (index: number, field: 'title' | 'price', value: string) => { - const newInputPairs = [...inputPairs]; - newInputPairs[index] = { - ...newInputPairs[index], - [field]: field === 'price' ? parseFloat(value) : value, - }; - setInputPairs(newInputPairs); + if (inputPairs[index].price !== 0 && inputPairs[index].title.trim().length > 0 && inputPairs.length > 0) { + setCanSubmit(true); + } else { + setCanSubmit(false); + } }; const handleInputBlur = (index: number) => { const currentPair = inputPairs[index]; - if (currentPair.title.trim() === '' && currentPair.price === 0) { + const filterEmpty = inputPairs.filter(pair => pair.title.trim() !== '' && pair.price !== 0); + + if (filterEmpty.length !== inputPairs.length) { setInputPairs(prev => prev.filter((_, i) => i !== index)); - } else if (currentPair.title.trim() !== '' && currentPair.price !== 0 && index === inputPairs.length - 1) { + } + if (currentPair.title.trim() !== '' && currentPair.price !== 0 && index === inputPairs.length - 1) { setInputPairs(prev => [...prev, {title: '', price: 0}]); } }; const getNonEmptyInputPairs = () => { - return inputPairs.filter(currentPair => currentPair.title.trim() !== '' && currentPair.price !== 0); + return inputPairs.filter(pair => pair.title.trim() !== '' && pair.price !== 0); }; useEffect(() => { @@ -39,6 +62,8 @@ const useDynamicInputPairs = () => { inputRefs, handleInputChange, handleInputBlur, + errorMessage, + canSubmit, }; }; diff --git a/client/src/pages/Create/Name.tsx b/client/src/pages/Create/Name.tsx index e8fd1f649..65e4916c0 100644 --- a/client/src/pages/Create/Name.tsx +++ b/client/src/pages/Create/Name.tsx @@ -38,6 +38,7 @@ const CreateEvent = () => { } if (validation.isValid) { + console.log('!!'); setEventName(newValue); setErrorMessage(''); } else { @@ -52,16 +53,16 @@ const CreateEvent = () => { </TopNav> <Title title="행사 이름 입력" description="시작할 행사 이름을 입력해 주세요." /> <form onSubmit={submitEventName} style={{padding: '0 1rem'}}> - <LabelInput labelText="행사 이름" errorText={errorMessage}> - <Input - value={eventName} - type="text" - placeholder="행사 이름" - onChange={e => handleChange(e)} - isError={!!errorMessage} - autoFocus - /> - </LabelInput> + <LabelInput + labelText="행사 이름" + errorText={errorMessage} + value={eventName} + type="text" + placeholder="행사 이름" + onChange={e => handleChange(e)} + isError={!!errorMessage} + autoFocus + /> <FixedButton disabled={!canSubmit}>행동 개시!</FixedButton> </form> </MainLayout> diff --git a/client/src/utils/generateUniqueId.ts b/client/src/utils/generateUniqueId.ts new file mode 100644 index 000000000..ec1564ce8 --- /dev/null +++ b/client/src/utils/generateUniqueId.ts @@ -0,0 +1,7 @@ +const generateUniqueId = (): string => { + const timestamp = Date.now().toString(); + const randomNum = Math.floor(Math.random() * 1000000).toString(); + return timestamp + randomNum; +}; + +export default generateUniqueId; diff --git a/client/src/utils/validate/validateEventName.ts b/client/src/utils/validate/validateEventName.ts index 1647a896c..b390f12b9 100644 --- a/client/src/utils/validate/validateEventName.ts +++ b/client/src/utils/validate/validateEventName.ts @@ -1,9 +1,10 @@ import ERROR_MESSAGE from '@constants/errorMessage'; +import RULE from '@constants/rule'; import {ValidateResult} from './type'; const validateEventName = (name: string): ValidateResult => { - if (name.length > 30) { + if (name.length > RULE.maxEventNameLength) { return {isValid: false, errorMessage: ERROR_MESSAGE.eventName}; } return {isValid: true}; diff --git a/client/src/utils/validate/validateMemberName.ts b/client/src/utils/validate/validateMemberName.ts index 8163af556..0e7483358 100644 --- a/client/src/utils/validate/validateMemberName.ts +++ b/client/src/utils/validate/validateMemberName.ts @@ -1,13 +1,25 @@ import REGEXP from '@constants/regExp'; import ERROR_MESSAGE from '@constants/errorMessage'; +import RULE from '@constants/rule'; import {ValidateResult} from './type'; const validateMemberName = (name: string): ValidateResult => { - if (!REGEXP.memberName.test(name)) { - return {isValid: false, errorMessage: ERROR_MESSAGE.memberName}; + const validateOnlyString = () => { + if (!REGEXP.memberName.test(name)) return false; + return true; + }; + + const validateLength = () => { + if (name.length > RULE.maxMemberNameLength || name.length < 1) return false; + return true; + }; + + if (validateOnlyString() && validateLength()) { + return {isValid: true}; } - return {isValid: true}; + + return {isValid: false, errorMessage: ERROR_MESSAGE.memberName}; }; export default validateMemberName; diff --git a/client/src/utils/validate/validatePurchase.ts b/client/src/utils/validate/validatePurchase.ts new file mode 100644 index 000000000..852f9c6d6 --- /dev/null +++ b/client/src/utils/validate/validatePurchase.ts @@ -0,0 +1,35 @@ +import {useState} from 'react'; + +import ERROR_MESSAGE from '@constants/errorMessage'; +import RULE from '@constants/rule'; +import REGEXP from '@constants/regExp'; + +import {ValidateResult} from './type'; + +const validatePurchase = (inputPair: Bill): ValidateResult => { + const [error, setError] = useState(''); + const {title, price} = inputPair; + + const validatePrice = () => { + if (price > RULE.maxPrice) { + setError(ERROR_MESSAGE.purchasePrice); + return false; + } + return true; + }; + + const validateTitle = () => { + if (REGEXP.purchaseTitle.test(title)) { + setError(ERROR_MESSAGE.purchaseTitle); + return false; + } + return true; + }; + + if (validatePrice() && validateTitle()) { + return {isValid: true}; + } + return {isValid: false, errorMessage: error}; +}; + +export default validatePurchase; From 5aca949a4631f23010d9eab20184d52433073cf5 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Fri, 2 Aug 2024 15:35:15 +0900 Subject: [PATCH 15/43] =?UTF-8?q?chore:=20=EC=B6=A9=EB=8F=8C=20=EB=B3=91?= =?UTF-8?q?=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/src/components/Input/Input.tsx | 15 +-- .../SetInitialParticipants.tsx | 63 +++++----- client/src/hooks/useDynamicInput.tsx | 116 +++++++++--------- client/src/pages/Create/Name.tsx | 6 +- 4 files changed, 97 insertions(+), 103 deletions(-) diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index 8fdde0729..e1fddb8fc 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -1,19 +1,13 @@ /** @jsxImportSource @emotion/react */ import React, {forwardRef, useImperativeHandle, useRef} from 'react'; - -import IconButton from '@components/IconButton/IconButton'; -import {InputProps} from '@components/Input/Input.type'; -import {inputBoxStyle, inputStyle} from '@components/Input/Input.style'; -import {useInput} from '@components/Input/useInput'; - -import {useTheme} from '@theme/HDesignProvider'; +import {useInput} from './useInput'; +import {InputProps} from './Input.type'; export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input( {value: propsValue, onChange, onFocus, onBlur, inputType, isError, placeholder, ...htmlProps}: InputProps, ref, ) { useImperativeHandle(ref, () => inputRef.current!); - const {theme} = useTheme(); const inputRef = useRef<HTMLInputElement>(null); const {value, handleChange, hasFocus, handleClickDelete, handleBlur, handleFocus, handleKeyDown} = useInput({ propsValue, @@ -24,9 +18,8 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro }); return ( - <div css={inputBoxStyle(theme, inputType, hasFocus, isError)}> + <div> <input - css={inputStyle(theme)} ref={inputRef} value={value} onChange={handleChange} @@ -36,7 +29,7 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro onKeyDown={handleKeyDown} {...htmlProps} /> - {value && hasFocus && <IconButton iconType="inputDelete" onMouseDown={handleClickDelete} />} + {value && hasFocus && <button onMouseDown={handleClickDelete}>x버튼</button>} </div> ); }); diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx index 6cc4e35ef..63b24e273 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx @@ -1,11 +1,11 @@ -import {Text, Input, BottomSheet, FixedButton, LabelGroupInput} from 'haengdong-design'; - +import {Text, BottomSheet, FixedButton} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; import validateMemberName from '@utils/validate/validateMemberName'; import useDynamicInput from '@hooks/useDynamicInput'; import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from './SetInitialParticipants.style'; +import LabelGroupInputContainer from './GroupInput'; interface SetInitialParticipantsProps { openBottomSheet: boolean; @@ -13,12 +13,12 @@ interface SetInitialParticipantsProps { } const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitialParticipantsProps) => { - const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs, errorMessage, canSubmit} = + const {inputs, inputRefs, handleInputChange, handleInputBlur, getFilledInputList, errorMessage} = useDynamicInput(validateMemberName); const {updateMemberList} = useStepList(); const handleSubmit = () => { - updateMemberList({memberNameList: getNonEmptyInputs(), type: 'IN'}); + updateMemberList({memberNameList: getFilledInputList(), type: 'IN'}); setOpenBottomSheet(false); }; @@ -27,34 +27,35 @@ const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitia <div css={setInitialParticipantsStyle}> <Text size="bodyBold">초기 인원 설정하기</Text> <div css={setInitialParticipantsInputGroupStyle}> - <LabelGroupInput labelText="이름" errorText={errorMessage}> - {inputs.map((input, index) => ( - // <LabelGroupInput.Element - // elementKey={`${index}`} - // key={`${index}`} - // type="text" - // value={input} - // ref={el => (inputRefs.current[index] = el)} - // onChange={e => handleInputChange(index, e)} - // onBlur={() => handleInputBlur(index)} - // isError={!!errorMessage} - // placeholder="이름" - // autoFocus - // /> - <input - // elementKey={`${index}`} - key={`${index}`} - type="text" - value={input} - ref={el => (inputRefs.current[index] = el)} - onChange={e => handleInputChange(index, e)} - onBlur={() => handleInputBlur(index)} - // isError={!!errorMessage} - placeholder="이름" - autoFocus - /> + <LabelGroupInputContainer labelText="이름" errorText={'d'}> + {inputs.map(({value, index}) => ( + <> + <LabelGroupInputContainer.Element + // autoFocus + id={String(index)} // 확인용도 임시임 + key={String(index)} + elementKey={String(index)} + type="text" + value={value} + ref={el => (inputRefs.current[index] = el)} + onChange={e => handleInputChange(index, e)} + onBlur={() => handleInputBlur(index)} + onKeyDown={e => { + if (e.nativeEvent.isComposing) return; + + if (e.key === 'Enter') { + if (index < inputs.length - 1) { + inputRefs.current[index + 1]?.focus(); + } + } + }} + /> + <div> + {value} | {index} + </div> + </> ))} - </LabelGroupInput> + </LabelGroupInputContainer> </div> </div> <FixedButton disabled={!canSubmit} variants={'primary'} onClick={handleSubmit} children={'인원 설정 완료'} /> diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index 7a26a8d4e..9731e9b08 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -2,82 +2,83 @@ import {useEffect, useRef, useState} from 'react'; import {ValidateResult} from '@utils/validate/type'; +type InputValue = { + value: string; + index: number; +}; + const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { - const [inputs, setInputs] = useState<string[]>(['']); + const [inputs, setInputs] = useState<InputValue[]>([{value: '', index: 0}]); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); const [errorMessage, setErrorMessage] = useState(''); const [canSubmit, setCanSubmit] = useState(false); // const [errorIds, setErrorIds] = useState<string>(''); - // TODO: (@soha) 입력이 완료되고 중간에 값을 모두 지웠을 경우 Input이 없애지도록 수정하기 const handleInputChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => { - const value = event.target.value; - const validation = validateFunc(value); + const {value} = event.target; + const {isValid, errorMessage} = validateFunc(value); - if (validation.isValid || value.length === 0) { + if (isLastInputFilled(index, value)) { setInputs(prevInputs => { - const newInputs = [...prevInputs]; - newInputs[index] = event.target.value; - return newInputs; + const updatedInputs = [...prevInputs]; + const targetInput = updatedInputs.filter(input => input.index === index); + + targetInput[0].value = value; + + // 새로운 인덱스를 inputs 배열 길이를 기준으로 설정 + const newIndex = updatedInputs[updatedInputs.length - 1].index + 1; + const finalInputs = [...updatedInputs, {index: newIndex, value: ''}]; + + return finalInputs; }); - setErrorMessage(''); - // setErrorIds(prevIds => prevIds.filter(errorId => errorId !== id)); } else { - event.target.value = inputs[index]; - setErrorMessage(validation.errorMessage ?? ''); - // setErrorIds(prevIds => [...new Set([...prevIds, id])]); - } + setInputs(prevInputs => { + const updatedInputs = [...prevInputs]; + const targetInput = updatedInputs.filter(input => input.index === index); - if (value.length > 0 && inputs.length > 0) { - setCanSubmit(true); - } else { - setCanSubmit(false); + targetInput[0].value = value; + + return updatedInputs; + }); } }; - const handleInputBlur = (index: number) => { - // const newInputs = inputs.map((input, idx) => (idx === index ? input : '')); - const filterEmpty = inputs.filter(input => input.trim() !== ''); - - console.log(filterEmpty, '!!!!!!'); - - setInputs([...filterEmpty, '']); - - // if (filterEmpty.length !== inputs.length) { - // setInputs(prev => { - // const notEmpty = prev.filter((input, index) => { - // if (index === prev.length - 1) return; - // return input.trim() !== ''; - // }); - // console.log(notEmpty, '@@@'); - // console.log([...notEmpty, '']); - // return [...notEmpty, '']; - // }); - // } - - // if (!inputs.every(input => input !== '')) { - // setInputs(prev => prev.filter(input => input !== '')); - // } else { - // setInputs(prev => { - // console.log(prev, 'ore~!~~~~~~'); - // const newInputs = [...prev, '']; - // // newInputs[index] = inputs[index]; - // return newInputs; - // }); - // } - // console.log(inputs, '@@@@@@@@@'); + const getFilledStringList = (stringList: string[]) => { + return stringList.filter(string => string.trim() !== ''); }; - const getNonEmptyInputs = () => { - return inputs.filter(input => input.trim() !== ''); + const getFilledInputList = () => { + return getFilledStringList(inputs.map(({value}) => value)); + }; + + const isLastInputFilled = (index: number, value: string) => { + const lastInputIndex = inputs[inputs.length - 1].index; + + return value !== '' && index === lastInputIndex; + }; + + const handleInputBlur = (index: number) => { + if (getFilledInputList().length !== inputs.length) { + setInputs(prevInputs => { + const filledInputList = prevInputs.filter(({value}) => value !== ''); + + // 새 입력의 인덱스를 inputs 길이를 기준으로 설정 + const newIndex = filledInputList[filledInputList.length - 1].index + 1; + + const finalInputs = [...filledInputList, {index: newIndex, value: ''}]; + + return finalInputs; + }); + } }; useEffect(() => { - if (inputRefs.current.length > 0) { - const lastInput = inputRefs.current[inputRefs.current.length - 1]; - if (lastInput) { - lastInput.scrollIntoView({behavior: 'smooth', block: 'center'}); - } + if (inputRefs.current.length <= 0) return; + + const lastInput = inputRefs.current[inputRefs.current.length - 1]; + + if (lastInput) { + lastInput.scrollIntoView({behavior: 'smooth', block: 'center'}); } }, [inputs]); @@ -87,8 +88,7 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { handleInputChange, handleInputBlur, errorMessage, - getNonEmptyInputs, - canSubmit, + getFilledInputList, }; }; diff --git a/client/src/pages/Create/Name.tsx b/client/src/pages/Create/Name.tsx index 65e4916c0..97f98a0c3 100644 --- a/client/src/pages/Create/Name.tsx +++ b/client/src/pages/Create/Name.tsx @@ -53,9 +53,8 @@ const CreateEvent = () => { </TopNav> <Title title="행사 이름 입력" description="시작할 행사 이름을 입력해 주세요." /> <form onSubmit={submitEventName} style={{padding: '0 1rem'}}> - <LabelInput - labelText="행사 이름" - errorText={errorMessage} + {/* <LabelInput labelText="행사 이름" errorText={errorMessage}> */} + <Input value={eventName} type="text" placeholder="행사 이름" @@ -63,6 +62,7 @@ const CreateEvent = () => { isError={!!errorMessage} autoFocus /> + {/* </LabelInput> */} <FixedButton disabled={!canSubmit}>행동 개시!</FixedButton> </form> </MainLayout> From 35be82d8422091613520d14acc544ae7d94ca66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Fri, 2 Aug 2024 15:46:06 +0900 Subject: [PATCH 16/43] =?UTF-8?q?feat:=20v0.1.52=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/package-lock.json | 4 ++-- HDesign/package.json | 2 +- HDesign/src/components/Input/Input.tsx | 22 +++++++++++-------- .../components/LabelGroupInput/Element.tsx | 4 ++-- .../LabelGroupInput/GroupInputContext.tsx | 8 +++---- .../LabelGroupInput.stories.tsx | 2 ++ .../LabelGroupInput/LabelGroupInput.tsx | 4 ++-- 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index 94092065b..cb10f6ee4 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -1,12 +1,12 @@ { "name": "haengdong-design", - "version": "0.1.51", + "version": "0.1.52", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "haengdong-design", - "version": "0.1.51", + "version": "0.1.52", "license": "ISC", "dependencies": { "@emotion/react": "^11.11.4", diff --git a/HDesign/package.json b/HDesign/package.json index f4fe0752e..f1bc9c8e7 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -1,6 +1,6 @@ { "name": "haengdong-design", - "version": "0.1.51", + "version": "0.1.52", "description": "", "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index 8fdde0729..5c188f3d1 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -1,19 +1,23 @@ /** @jsxImportSource @emotion/react */ import React, {forwardRef, useImperativeHandle, useRef} from 'react'; +import {useInput} from './useInput'; -import IconButton from '@components/IconButton/IconButton'; -import {InputProps} from '@components/Input/Input.type'; -import {inputBoxStyle, inputStyle} from '@components/Input/Input.style'; -import {useInput} from '@components/Input/useInput'; +export type InputType = 'input' | 'search'; -import {useTheme} from '@theme/HDesignProvider'; +export interface InputCustomProps { + inputType?: InputType; + isError?: boolean; +} + +export type InputOptionProps = InputCustomProps; + +export type InputProps = React.ComponentProps<'input'> & InputOptionProps; export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input( {value: propsValue, onChange, onFocus, onBlur, inputType, isError, placeholder, ...htmlProps}: InputProps, ref, ) { useImperativeHandle(ref, () => inputRef.current!); - const {theme} = useTheme(); const inputRef = useRef<HTMLInputElement>(null); const {value, handleChange, hasFocus, handleClickDelete, handleBlur, handleFocus, handleKeyDown} = useInput({ propsValue, @@ -24,9 +28,9 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro }); return ( - <div css={inputBoxStyle(theme, inputType, hasFocus, isError)}> + <div> <input - css={inputStyle(theme)} + style={{backgroundColor: 'lavender'}} ref={inputRef} value={value} onChange={handleChange} @@ -36,7 +40,7 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro onKeyDown={handleKeyDown} {...htmlProps} /> - {value && hasFocus && <IconButton iconType="inputDelete" onMouseDown={handleClickDelete} />} + {value && hasFocus && <button onMouseDown={handleClickDelete}>x버튼</button>} </div> ); }); diff --git a/HDesign/src/components/LabelGroupInput/Element.tsx b/HDesign/src/components/LabelGroupInput/Element.tsx index 8db8dbd99..a7c9bc5f9 100644 --- a/HDesign/src/components/LabelGroupInput/Element.tsx +++ b/HDesign/src/components/LabelGroupInput/Element.tsx @@ -14,7 +14,7 @@ const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProp ) { useImperativeHandle(ref, () => inputRef.current!); const inputRef = useRef<HTMLInputElement>(null); - const {setHasAnyFocus, values, setValues, hasErrors, setHasErrors} = useGroupInputContext(); + const {setHasAnyFocus, values, setValues, hasAnyErrors, setHasAnyErrors} = useGroupInputContext(); useEffect(() => { setValues({...values, [elementKey]: `${propsValue}` ?? ''}); @@ -29,7 +29,7 @@ const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProp }; useEffect(() => { - setHasErrors({...hasErrors, [elementKey]: isError ?? false}); + setHasAnyErrors({...hasAnyErrors, [elementKey]: isError ?? false}); }, [isError]); const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => { diff --git a/HDesign/src/components/LabelGroupInput/GroupInputContext.tsx b/HDesign/src/components/LabelGroupInput/GroupInputContext.tsx index 50f0780ce..a564ef94f 100644 --- a/HDesign/src/components/LabelGroupInput/GroupInputContext.tsx +++ b/HDesign/src/components/LabelGroupInput/GroupInputContext.tsx @@ -5,8 +5,8 @@ interface GroupInputContextProps { setHasAnyFocus: React.Dispatch<React.SetStateAction<boolean>>; values: {[key: string]: string}; setValues: React.Dispatch<React.SetStateAction<{[key: string]: string}>>; - hasErrors: {[key: string]: boolean}; - setHasErrors: React.Dispatch<React.SetStateAction<{[key: string]: boolean}>>; + hasAnyErrors: {[key: string]: boolean}; + setHasAnyErrors: React.Dispatch<React.SetStateAction<{[key: string]: boolean}>>; } const GroupInputContext = createContext<GroupInputContextProps | undefined>(undefined); @@ -22,10 +22,10 @@ export const useGroupInputContext = () => { export const GroupInputProvider: React.FC<PropsWithChildren> = ({children}: React.PropsWithChildren) => { const [hasAnyFocus, setHasAnyFocus] = useState(false); const [values, setValues] = useState<{[key: string]: string}>({}); - const [hasErrors, setHasErrors] = useState<{[key: string]: boolean}>({}); + const [hasAnyErrors, setHasAnyErrors] = useState<{[key: string]: boolean}>({}); return ( - <GroupInputContext.Provider value={{hasAnyFocus, setHasAnyFocus, values, setValues, hasErrors, setHasErrors}}> + <GroupInputContext.Provider value={{hasAnyFocus, setHasAnyFocus, values, setValues, hasAnyErrors, setHasAnyErrors}}> {children} </GroupInputContext.Provider> ); diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx index 2a4f87131..b4bf68182 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx @@ -55,6 +55,7 @@ export const Playground: Story = { placeholder="지출내역" value={name} onChange={e => handleChangeName(e)} + onBlur={() => console.log('!!!')} isError={isError} /> <LabelGroupInput.Element @@ -62,6 +63,7 @@ export const Playground: Story = { onChange={handleChangePrice} elementKey="price" placeholder="금액" + onBlur={() => console.log('!!!')} isError={false} /> </LabelGroupInput> diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx index cd1ca2ca9..46032530a 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx @@ -16,7 +16,7 @@ import {GroupInputProvider, useGroupInputContext} from './GroupInputContext'; const LabelGroupInput: React.FC<LabelGroupInputProps> = ({labelText, errorText, children}: LabelGroupInputProps) => { const {theme} = useTheme(); - const {hasAnyFocus, values, hasErrors} = useGroupInputContext(); + const {hasAnyFocus, values, hasAnyErrors} = useGroupInputContext(); return ( <div css={labelInputStyle}> @@ -28,7 +28,7 @@ const LabelGroupInput: React.FC<LabelGroupInputProps> = ({labelText, errorText, </label> {errorText && ( <Text size="caption" css={errorTextStyle(theme)}> - {!Object.values(hasErrors).every(error => !error) && errorText} + {!Object.values(hasAnyErrors).every(error => !error) && errorText} </Text> )} </div> From 35ff11fb812bbe62c5f529ec0a5c89a817a8ae84 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Fri, 2 Aug 2024 16:01:37 +0900 Subject: [PATCH 17/43] =?UTF-8?q?fix:=20=EB=8B=A4=EC=9D=8C=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EC=9D=84=20=EA=B8=B0=EB=8C=80=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=9D=B8=ED=92=8B=EC=9D=B4=20=EB=9C=A8=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=98=EA=B3=A0=20=EC=A4=91=EA=B0=84=20=EC=9D=B8=ED=92=8B?= =?UTF-8?q?=EC=9D=84=20=EC=82=AD=EC=A0=9C=ED=96=88=EC=9D=84=20=EB=95=8C=20?= =?UTF-8?q?input=20=EC=97=98=EB=A6=AC=EB=A8=BC=ED=8A=B8=20=EC=9E=90?= =?UTF-8?q?=EC=B2=B4=EA=B0=80=20=EC=82=AC=EB=9D=BC=EC=A7=80=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 86 +++++++++---------- .../UpdateParticipants.tsx | 4 +- .../SetInitialParticipants.tsx | 51 +++++------ client/src/hooks/useDynamicInput.tsx | 3 + 4 files changed, 70 insertions(+), 74 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index b1714ab4a..fc13e418d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -2573,9 +2573,9 @@ } }, "node_modules/@swc/core": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.4.tgz", - "integrity": "sha512-+wSycNxOw9QQz81AJAZlNS34EtOIifwUXMPACg05PWjECsjOKDTXLCVPx6J0lRaxhHSGBU2OYs9mRfIvxGt3CA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.5.tgz", + "integrity": "sha512-qKK0/Ta4qvxs/ok3XyYVPT7OBenwRn1sSINf1cKQTBHPqr7U/uB4k2GTl6JgEs8H4PiJrMTNWfMLTucIoVSfAg==", "hasInstallScript": true, "dependencies": { "@swc/counter": "^0.1.3", @@ -2589,16 +2589,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.7.4", - "@swc/core-darwin-x64": "1.7.4", - "@swc/core-linux-arm-gnueabihf": "1.7.4", - "@swc/core-linux-arm64-gnu": "1.7.4", - "@swc/core-linux-arm64-musl": "1.7.4", - "@swc/core-linux-x64-gnu": "1.7.4", - "@swc/core-linux-x64-musl": "1.7.4", - "@swc/core-win32-arm64-msvc": "1.7.4", - "@swc/core-win32-ia32-msvc": "1.7.4", - "@swc/core-win32-x64-msvc": "1.7.4" + "@swc/core-darwin-arm64": "1.7.5", + "@swc/core-darwin-x64": "1.7.5", + "@swc/core-linux-arm-gnueabihf": "1.7.5", + "@swc/core-linux-arm64-gnu": "1.7.5", + "@swc/core-linux-arm64-musl": "1.7.5", + "@swc/core-linux-x64-gnu": "1.7.5", + "@swc/core-linux-x64-musl": "1.7.5", + "@swc/core-win32-arm64-msvc": "1.7.5", + "@swc/core-win32-ia32-msvc": "1.7.5", + "@swc/core-win32-x64-msvc": "1.7.5" }, "peerDependencies": { "@swc/helpers": "*" @@ -2610,9 +2610,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.4.tgz", - "integrity": "sha512-RbWrdGh+x9xKFUA9/kPZRR8OPxUsDUuPyLjPIGLYZMO+ftht2vhVH7QsUq6lg+jAP34eIya72UA1isiZe+BRaA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.5.tgz", + "integrity": "sha512-Y+bvW9C4/u26DskMbtQKT4FU6QQenaDYkKDi028vDIKAa7v1NZqYG9wmhD/Ih7n5EUy2uJ5I5EWD7WaoLzT6PA==", "cpu": [ "arm64" ], @@ -2625,9 +2625,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.4.tgz", - "integrity": "sha512-TxCWMJs4OrqApjFuT8cUiqMz0zg97F0JsXBEeZ7zjkyv9XJ/rN2pdwqMlZv0Wv2C2rivOPo6FsWYlZ3V8ZHhyA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.5.tgz", + "integrity": "sha512-AuIbDlcaAhYS6mtF4UqvXgrLeAfXZbVf4pgtgShPbutF80VbCQiIB55zOFz5aZdCpsBVuCWcBq0zLneK+VQKkQ==", "cpu": [ "x64" ], @@ -2640,9 +2640,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.4.tgz", - "integrity": "sha512-5IhwIJZAgkkfI6PqgQ3xk0/2hTAVsAczIPLiR2Epp30EgsNo1KIFL0ZHzrnvJPy5BZ3jy3T1dEbDE/memBOEmA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.5.tgz", + "integrity": "sha512-99uBPHITRqgGwCXAjHY94VaV3Z40+D2NQNgR1t6xQpO8ZnevI6YSzX6GVZfBnV7+7oisiGkrVEwfIRRa+1s8FA==", "cpu": [ "arm" ], @@ -2655,9 +2655,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.4.tgz", - "integrity": "sha512-0787jri83jigf26mF8FndWehh7jqMaHwAm/OV6VdToyNo/g+d1AxVpkEizrywZK46el+AObnHUIHIHwZgO21LA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.5.tgz", + "integrity": "sha512-xHL3Erlz+OGGCG4h6K2HWiR56H5UYMuBWWPbbUufi2bJpfhuKQy/X3vWffwL8ZVfJmCUwr4/G91GHcm32uYzRg==", "cpu": [ "arm64" ], @@ -2670,9 +2670,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.4.tgz", - "integrity": "sha512-A45hGKWAGcjU5Ol0uQUoK0tHerwEKxfprYUZbmPLpD2yrpMZr+dTrwY2n075sixs7RuZEccBkgGNpehEe5BPBQ==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.5.tgz", + "integrity": "sha512-5ArGdqvFMszNHdi4a67vopeYq8d1K+FuTWDrblHrAvZFhAyv+GQz2PnKqYOgl0sWmQxsNPfNwBFtxACpUO3Jzg==", "cpu": [ "arm64" ], @@ -2685,9 +2685,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.4.tgz", - "integrity": "sha512-bcO1MpAm39TXqqHuYW4ox4vDvhB7jkguwMwxvmL+cKBGsUHrIoUTfGt9NM9N4D4CvOwULlxqbyt19veUJ7CVPw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.5.tgz", + "integrity": "sha512-mSVVV/PFzCGtI1nVQQyx34NwCMgSurF6ZX/me8pUAX054vsE/pSFL66xN+kQOe/1Z/LOd4UmXFkZ/EzOSnYcSg==", "cpu": [ "x64" ], @@ -2700,9 +2700,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.4.tgz", - "integrity": "sha512-N6nXuHyDO/q5kPN2xQxz5BEvhFpgnFSkP+9wxg5xWq+qIQL5bv37jk8dkKvMLx/8fHzTqrIjPDSRzVbcL7sqXg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.5.tgz", + "integrity": "sha512-09hY3ZKMUORXVunESKS9yuP78+gQbr759GKHo8wyCdtAx8lCZdEjfI5NtC7/1VqwfeE32/U6u+5MBTVhZTt0AA==", "cpu": [ "x64" ], @@ -2715,9 +2715,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.4.tgz", - "integrity": "sha512-7W1owqCNR1cG+mpS55juiZlR/lrAdxB1pH32egeOipNKOLGwyqmlzQ0g9tkQTNgzwgfpCUg8z606+GqqXvajZw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.5.tgz", + "integrity": "sha512-B/UDtPI3RlYRFW42xQxOpl6kI/9LtkD7No+XeRIKQTPe15EP2o+rUlv7CmKljVBXgJ8KmaQbZlaEh1YP+QZEEQ==", "cpu": [ "arm64" ], @@ -2730,9 +2730,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.4.tgz", - "integrity": "sha512-saLkY+q7zNPk4gYiUBCc93FYPo4ECXMjHcSPtLVHoPZBIxRrklgaAf6aDpblBo30nVdoBE2V3YPd0Y/cPiY6RQ==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.5.tgz", + "integrity": "sha512-BgLesVGmIY6Nub/sURqtSRvWYcbCE/ACfuZB3bZHVKD6nsZJJuOpdB8oC41fZPyc8yZUzL3XTBIifkT2RP+w9w==", "cpu": [ "ia32" ], @@ -2745,9 +2745,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.4.tgz", - "integrity": "sha512-zKF6jpRBNuVKgOf2W5dMcPyjwcNCp21syjl9lvLRbCeIg+1U+zjdoQCAmMWWoPNE7fLg+yfvohnnOJG2AdzQ9Q==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.5.tgz", + "integrity": "sha512-CnF557tidLfQRPczcqDJ8x+LBQYsFa0Ra6w2+YU1iFUboaI2jJVuqt3vEChu80y6JiRIBAaaV2L/GawDJh1dIQ==", "cpu": [ "x64" ], diff --git a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx index 0bffa5a00..04851db6b 100644 --- a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx +++ b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx @@ -27,10 +27,10 @@ const UpdateParticipants = ({inOutAction, setOpenBottomSheet}: UpdateParticipant <div css={updateParticipantsInputStyle}> {/* TODO: (@soha) Search로 변경하기 */} <LabelGroupInput labelText="이름" errorText={errorMessage}> - {inputs.map((name, index) => ( + {inputs.map(({}, index) => ( <LabelGroupInput.Element elementKey={`${index}`} - value={name} + value={`${name}`} type="text" ref={el => (inputRefs.current[index] = el)} onChange={e => handleInputChange(index, e)} diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx index 63b24e273..4b478d022 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx @@ -1,11 +1,10 @@ -import {Text, BottomSheet, FixedButton} from 'haengdong-design'; +import {Text, BottomSheet, FixedButton, LabelGroupInput} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; import validateMemberName from '@utils/validate/validateMemberName'; import useDynamicInput from '@hooks/useDynamicInput'; import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from './SetInitialParticipants.style'; -import LabelGroupInputContainer from './GroupInput'; interface SetInitialParticipantsProps { openBottomSheet: boolean; @@ -13,7 +12,7 @@ interface SetInitialParticipantsProps { } const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitialParticipantsProps) => { - const {inputs, inputRefs, handleInputChange, handleInputBlur, getFilledInputList, errorMessage} = + const {inputs, inputRefs, handleInputChange, handleInputBlur, getFilledInputList, errorMessage, canSubmit} = useDynamicInput(validateMemberName); const {updateMemberList} = useStepList(); @@ -27,35 +26,29 @@ const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitia <div css={setInitialParticipantsStyle}> <Text size="bodyBold">초기 인원 설정하기</Text> <div css={setInitialParticipantsInputGroupStyle}> - <LabelGroupInputContainer labelText="이름" errorText={'d'}> + <LabelGroupInput labelText="이름" errorText={'d'}> {inputs.map(({value, index}) => ( - <> - <LabelGroupInputContainer.Element - // autoFocus - id={String(index)} // 확인용도 임시임 - key={String(index)} - elementKey={String(index)} - type="text" - value={value} - ref={el => (inputRefs.current[index] = el)} - onChange={e => handleInputChange(index, e)} - onBlur={() => handleInputBlur(index)} - onKeyDown={e => { - if (e.nativeEvent.isComposing) return; - - if (e.key === 'Enter') { - if (index < inputs.length - 1) { - inputRefs.current[index + 1]?.focus(); - } + <LabelGroupInput.Element + id={`${index}`} // 확인용도 임시임 + key={`${index}`} + elementKey={`${index}`} + type="text" + value={value} + ref={el => (inputRefs.current[index] = el)} + onChange={e => handleInputChange(index, e)} + onBlur={() => handleInputBlur(index)} + onKeyDown={e => { + if (e.nativeEvent.isComposing) return; + + if (e.key === 'Enter') { + if (index < inputs.length - 1) { + inputRefs.current[index + 1]?.focus(); } - }} - /> - <div> - {value} | {index} - </div> - </> + } + }} + /> ))} - </LabelGroupInputContainer> + </LabelGroupInput> </div> </div> <FixedButton disabled={!canSubmit} variants={'primary'} onClick={handleSubmit} children={'인원 설정 완료'} /> diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index 9731e9b08..ac6b99694 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -89,6 +89,9 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { handleInputBlur, errorMessage, getFilledInputList, + canSubmit, // TODO: (@weadie) 이거 리모트에서 보고 가져오기 + // TODO: (@weadie) 네이밍 수정 + getNonEmptyInputs: getFilledInputList, }; }; From b86d14343fb382ef618861db3a133da45b7f428e Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:06:46 +0900 Subject: [PATCH 18/43] =?UTF-8?q?rename:=20SerPurchase=20->=20AddBillActio?= =?UTF-8?q?nListModalContent=EB=A1=9C=20=EC=9D=B4=EB=A6=84=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 --- .../AddBillActionListModalContent.style.ts | 31 ++++++++ .../AddBillActionListModalContent.tsx | 71 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.style.ts create mode 100644 client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx diff --git a/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.style.ts b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.style.ts new file mode 100644 index 000000000..80c770362 --- /dev/null +++ b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.style.ts @@ -0,0 +1,31 @@ +import {css} from '@emotion/react'; + +const container = () => + css({ + height: '100%', + }); + +const inputContainer = () => + css({ + display: 'flex', + height: '100%', + flexDirection: 'column', + gap: '1.5rem', + overflow: 'auto', + paddingBottom: '14rem', + }); + +export const input = () => + css({ + display: 'flex', + flexDirection: 'column', + gap: '0.5rem', + }); + +const addBillActionListStyle = { + container, + inputContainer, + input, +}; + +export default addBillActionListStyle; diff --git a/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx new file mode 100644 index 000000000..ab61d9402 --- /dev/null +++ b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx @@ -0,0 +1,71 @@ +import {FixedButton, LabelGroupInput} from 'haengdong-design'; +import style from './AddBillActionListModalContent.style'; +import {useStepList} from '@hooks/useStepList/useStepList'; +import useDynamicBillActionInput from '@hooks/useDynamicInputPairs'; +import validatePurchase from '@utils/validate/validatePurchase'; + +interface SetPurchaseProps { + setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; + setOrder: React.Dispatch<React.SetStateAction<number>>; +} + +const AddBillActionListModalContent = ({setOpenBottomSheet, setOrder}: SetPurchaseProps) => { + const { + inputPairList, + inputRefList, + handleInputChange, + getFilledInputPairList, + deleteEmptyInputPairElementOnBlur, + focusNextInputOnEnter, + } = useDynamicBillActionInput(validatePurchase); + const {addBill} = useStepList(); + + const handleSetPurchaseSubmit = () => { + setOrder(prev => prev + 1); + + // TODO: (@weadie) 요청 실패시 오류 핸들 필요 + addBill(getFilledInputPairList().map(({title, price}) => ({title, price: Number(price)}))); // TODO: (@weadie) DTO같은게 다이내믹에 필요할까? + setOpenBottomSheet(false); + }; + + return ( + <div css={style.container}> + <div css={style.inputContainer}> + <LabelGroupInput labelText="지출내역 / 금액"> + {inputPairList.map(({index, title, price}) => ( + <div key={index} css={style.input}> + <LabelGroupInput.Element + elementKey={`${index}`} + type="text" + value={title} + onChange={e => handleInputChange(index, 'title', e)} + onKeyDown={e => focusNextInputOnEnter(e, index, 'title')} + onBlur={() => deleteEmptyInputPairElementOnBlur()} // TODO: (@weadie) 이 블러프롭이 내부적으로 index를 넘기고 있기 때문에 화살표 함수로 써야만하내요.. + placeholder="지출 내역" + ref={el => (inputRefList.current[index * 2] = el)} + /> + <LabelGroupInput.Element + elementKey={`${index}`} + type="number" + value={price} + onChange={e => handleInputChange(index, 'price', e)} + onKeyDown={e => focusNextInputOnEnter(e, index, 'price')} + onBlur={() => deleteEmptyInputPairElementOnBlur()} + placeholder="금액" + ref={el => (inputRefList.current[index * 2 + 1] = el)} + /> + </div> + ))} + </LabelGroupInput> + </div> + <FixedButton + // disabled={!(inputPairs.length - 1)} + variants={'primary'} + children={'추가하기'} + onClick={handleSetPurchaseSubmit} + /> + </div> + ); +}; + +export default AddBillActionListModalContent; From 0df0ce2557786d449209f63259ffc5adb5973f80 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:08:01 +0900 Subject: [PATCH 19/43] =?UTF-8?q?rename:=20UpdateParticipants=20->=20AddMe?= =?UTF-8?q?mberActionListModalContent=20=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddMemberActionListModalContent.style.ts} | 11 +++- .../AddMemberActionListModalContent.tsx | 62 +++++++++++++++++++ .../UpdateParticipants.tsx | 54 ---------------- 3 files changed, 71 insertions(+), 56 deletions(-) rename client/src/components/Modal/{SetActionModalContent/UpdateParticipants.style.ts => SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.style.ts} (60%) create mode 100644 client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx delete mode 100644 client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx diff --git a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.style.ts b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.style.ts similarity index 60% rename from client/src/components/Modal/SetActionModalContent/UpdateParticipants.style.ts rename to client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.style.ts index 087e25a85..6162d55b5 100644 --- a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.style.ts +++ b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.style.ts @@ -1,6 +1,6 @@ import {css} from '@emotion/react'; -export const updateParticipantsStyle = () => +const container = () => css({ display: 'flex', flexDirection: 'column', @@ -8,7 +8,7 @@ export const updateParticipantsStyle = () => height: '100%', }); -export const updateParticipantsInputStyle = () => +const inputGroup = () => css({ display: 'flex', flexDirection: 'column', @@ -16,3 +16,10 @@ export const updateParticipantsInputStyle = () => overflow: 'auto', paddingBottom: '14rem', }); + +const addMemberActionListModalContentStyle = { + container, + inputGroup, +}; + +export default addMemberActionListModalContentStyle; diff --git a/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx new file mode 100644 index 000000000..a1129e0bf --- /dev/null +++ b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx @@ -0,0 +1,62 @@ +import {FixedButton, LabelGroupInput} from 'haengdong-design'; + +import {useStepList} from '@hooks/useStepList/useStepList'; +import validateMemberName from '@utils/validate/validateMemberName'; + +import useDynamicInput from '@hooks/useDynamicInput'; +import style from './AddMemberActionListModalContent.style'; + +interface UpdateParticipantsProps { + inOutAction: MemberType; + setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; +} + +const AddMemberActionListModalContent = ({inOutAction, setOpenBottomSheet}: UpdateParticipantsProps) => { + const { + inputList, + inputRefList, + handleInputChange, + deleteEmptyInputElementOnBlur, + getFilledInputList, + errorMessage, + canSubmit, + focusNextInputOnEnter, + } = useDynamicInput(validateMemberName); + const {updateMemberList} = useStepList(); + + const handleUpdateParticipantsSubmit = () => { + updateMemberList({memberNameList: getFilledInputList().map(({value}) => value), type: inOutAction}); + setOpenBottomSheet(false); + }; + + return ( + <div css={style.container}> + <div css={style.inputGroup}> + {/* TODO: (@soha) Search로 변경하기 */} + <LabelGroupInput labelText="이름" errorText={errorMessage}> + {inputList.map(({value, index}) => ( + <LabelGroupInput.Element + key={`${index}`} + elementKey={`${index}`} + type="text" + value={`${value}`} + ref={el => (inputRefList.current[index] = el)} + onChange={e => handleInputChange(index, e)} + onBlur={() => deleteEmptyInputElementOnBlur()} + onKeyDown={e => focusNextInputOnEnter(e, index)} + placeholder="이름" + /> + ))} + </LabelGroupInput> + </div> + <FixedButton + disabled={!canSubmit} + variants={'primary'} + children={`${inputList.length - 1}명 ${inOutAction === 'OUT' ? '탈주' : '늦참'}`} + onClick={handleUpdateParticipantsSubmit} + /> + </div> + ); +}; + +export default AddMemberActionListModalContent; diff --git a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx deleted file mode 100644 index 04851db6b..000000000 --- a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import {Input, FixedButton, LabelGroupInput} from 'haengdong-design'; - -import {useStepList} from '@hooks/useStepList/useStepList'; -import validateMemberName from '@utils/validate/validateMemberName'; - -import useDynamicInput from '@hooks/useDynamicInput'; - -import {updateParticipantsInputStyle, updateParticipantsStyle} from './UpdateParticipants.style'; - -interface UpdateParticipantsProps { - inOutAction: MemberType; - setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; -} - -const UpdateParticipants = ({inOutAction, setOpenBottomSheet}: UpdateParticipantsProps) => { - const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs, errorMessage, canSubmit} = - useDynamicInput(validateMemberName); - const {updateMemberList} = useStepList(); - - const handleUpdateParticipantsSubmit = () => { - updateMemberList({memberNameList: getNonEmptyInputs(), type: inOutAction}); - setOpenBottomSheet(false); - }; - - return ( - <div css={updateParticipantsStyle}> - <div css={updateParticipantsInputStyle}> - {/* TODO: (@soha) Search로 변경하기 */} - <LabelGroupInput labelText="이름" errorText={errorMessage}> - {inputs.map(({}, index) => ( - <LabelGroupInput.Element - elementKey={`${index}`} - value={`${name}`} - type="text" - ref={el => (inputRefs.current[index] = el)} - onChange={e => handleInputChange(index, e)} - onBlur={() => handleInputBlur(index)} - placeholder="이름" - autoFocus - /> - ))} - </LabelGroupInput> - </div> - <FixedButton - disabled={!canSubmit} - variants={'primary'} - children={`${inputs.length - 1}명 ${inOutAction === 'OUT' ? '탈주' : '늦참'}`} - onClick={handleUpdateParticipantsSubmit} - /> - </div> - ); -}; - -export default UpdateParticipants; From 9a40e2cd3929510f886b4de14ae5f966eaed6f5c Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:08:26 +0900 Subject: [PATCH 20/43] =?UTF-8?q?rename:=20SetPurchase=20->=20SetActionLis?= =?UTF-8?q?tModal=20=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SetActionListModal.style.ts | 20 ++++++ .../SetActionModal/SetActionListModal.tsx | 50 +++++++++++++++ .../SetPurchase.style.ts | 23 ------- .../SetActionModalContent/SetPurchase.tsx | 61 ------------------- 4 files changed, 70 insertions(+), 84 deletions(-) create mode 100644 client/src/components/Modal/SetActionModal/SetActionListModal.style.ts create mode 100644 client/src/components/Modal/SetActionModal/SetActionListModal.tsx delete mode 100644 client/src/components/Modal/SetActionModalContent/SetPurchase.style.ts delete mode 100644 client/src/components/Modal/SetActionModalContent/SetPurchase.tsx diff --git a/client/src/components/Modal/SetActionModal/SetActionListModal.style.ts b/client/src/components/Modal/SetActionModal/SetActionListModal.style.ts new file mode 100644 index 000000000..ca6703309 --- /dev/null +++ b/client/src/components/Modal/SetActionModal/SetActionListModal.style.ts @@ -0,0 +1,20 @@ +import {css} from '@emotion/react'; + +export const container = css({ + display: 'flex', + flexDirection: 'column', + width: '100%', + height: '100%', + padding: '0 1.5rem', + gap: '1.5rem', +}); + +export const switchContainer = css({ + display: 'flex', + width: '100%', + justifyContent: 'space-between', +}); + +const setActionListModalStyle = {container, switchContainer}; + +export default setActionListModalStyle; diff --git a/client/src/components/Modal/SetActionModal/SetActionListModal.tsx b/client/src/components/Modal/SetActionModal/SetActionListModal.tsx new file mode 100644 index 000000000..f33597334 --- /dev/null +++ b/client/src/components/Modal/SetActionModal/SetActionListModal.tsx @@ -0,0 +1,50 @@ +import {useState} from 'react'; +import {BottomSheet, Switch} from 'haengdong-design'; + +import SetPurchase from './AddBillActionListModalContent/AddBillActionListModalContent'; +import AddMemberActionListModalContent from './AddMemberActionListModalContent/AddMemberActionListModalContent'; +import style from './SetActionListModal.style'; + +export type ActionType = '지출' | '인원'; + +interface SetActionModalContentProps { + openBottomSheet: boolean; + setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; + setOrder: React.Dispatch<React.SetStateAction<number>>; +} + +const SetActionListModal = ({openBottomSheet, setOpenBottomSheet, setOrder}: SetActionModalContentProps) => { + const [action, setAction] = useState<ActionType>('지출'); + const [inOutAction, setInOutAction] = useState<InOutType>('탈주'); + + const handleActionTypeChange = (value: string) => { + setAction(value as ActionType); + }; + + const handleParticipantTypeChange = (value: string) => { + setInOutAction(value as InOutType); + }; + + return ( + <BottomSheet isOpened={openBottomSheet} onChangeClose={() => setOpenBottomSheet(false)}> + <div css={style.container}> + <div css={style.switchContainer}> + <Switch value={action} onChange={handleActionTypeChange} values={['지출', '인원']} /> + {action === '인원' && ( + <Switch values={['늦참', '탈주']} value={inOutAction} onChange={handleParticipantTypeChange} /> + )} + </div> + + {action === '지출' && <SetPurchase setOpenBottomSheet={setOpenBottomSheet} setOrder={setOrder} />} + {action === '인원' && ( + <AddMemberActionListModalContent + inOutAction={inOutAction === '탈주' ? 'OUT' : 'IN'} + setOpenBottomSheet={setOpenBottomSheet} + /> + )} + </div> + </BottomSheet> + ); +}; + +export default SetActionListModal; diff --git a/client/src/components/Modal/SetActionModalContent/SetPurchase.style.ts b/client/src/components/Modal/SetActionModalContent/SetPurchase.style.ts deleted file mode 100644 index cc852da28..000000000 --- a/client/src/components/Modal/SetActionModalContent/SetPurchase.style.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {css} from '@emotion/react'; - -export const setPurchaseStyle = () => - css({ - height: '100%', - }); - -export const setPurchaseInputContainerStyle = () => - css({ - display: 'flex', - height: '100%', - flexDirection: 'column', - gap: '1.5rem', - overflow: 'auto', - paddingBottom: '14rem', - }); - -export const setPurchaseInputStyle = () => - css({ - display: 'flex', - flexDirection: 'column', - gap: '0.5rem', - }); diff --git a/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx b/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx deleted file mode 100644 index 7244c1808..000000000 --- a/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import {Input, FixedButton} from 'haengdong-design'; - -import {useStepList} from '@hooks/useStepList/useStepList'; -import validatePurchase from '@utils/validate/validatePurchase'; - -import useDynamicInputPairs from '@hooks/useDynamicInputPairs'; - -import {setPurchaseInputStyle, setPurchaseStyle, setPurchaseInputContainerStyle} from './SetPurchase.style'; - -interface SetPurchaseProps { - setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; - setOrder: React.Dispatch<React.SetStateAction<number>>; -} - -const SetPurchase = ({setOpenBottomSheet, setOrder}: SetPurchaseProps) => { - // const {inputPairs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputPairs} = useDynamicInputPairs(validatePurchase); - const {addBill} = useStepList(); - - const handleSetPurchaseSubmit = () => { - setOrder(prev => prev + 1); - - // TODO: (@weadie) 요청 실패시 오류 핸들 필요 - // addBill(getNonEmptyInputPairs()); - setOpenBottomSheet(false); - }; - - return ( - <div css={setPurchaseStyle}> - <div css={setPurchaseInputContainerStyle}> - {/* {inputPairs.map((pair, index) => ( - <div key={index} css={setPurchaseInputStyle}> - <Input - type="text" - value={pair.title} - onChange={e => handleInputChange(index, 'title', e.target.value)} - onBlur={() => handleInputBlur(index)} - placeholder="지출 내역" - ref={el => (inputRefs.current[index * 2] = el)} - /> - <Input - type="number" - value={pair.price} - onChange={e => handleInputChange(index, 'price', e.target.value)} - onBlur={() => handleInputBlur(index)} - placeholder="금액" - ref={el => (inputRefs.current[index * 2 + 1] = el)} - /> - </div> - ))} */} - </div> - <FixedButton - // disabled={!(inputPairs.length - 1)} - variants={'primary'} - children={'추가하기'} - onClick={handleSetPurchaseSubmit} - /> - </div> - ); -}; - -export default SetPurchase; From e91c87f3c2dbabc7c0fc241605dc3f63733f4ccc Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:09:22 +0900 Subject: [PATCH 21/43] =?UTF-8?q?rename:=20SetInitialParticipants=20->=20S?= =?UTF-8?q?etInitialMemberListModal=20=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SetInitialMemberListModal.style.ts} | 4 +- .../SetInitialMemberListModal.tsx | 60 +++++++++++++++++++ .../SetInitialParticipants.tsx | 59 ------------------ 3 files changed, 62 insertions(+), 61 deletions(-) rename client/src/components/Modal/{SetInitialParticipants/SetInitialParticipants.style.ts => SetInitialMemberListModal/SetInitialMemberListModal.style.ts} (73%) create mode 100644 client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx delete mode 100644 client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.style.ts b/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.style.ts similarity index 73% rename from client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.style.ts rename to client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.style.ts index f0f5bf996..ed835682c 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.style.ts +++ b/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.style.ts @@ -1,6 +1,6 @@ import {css} from '@emotion/react'; -export const setInitialParticipantsStyle = () => +export const setInitialMemberListModalStyle = () => css({ display: 'flex', flexDirection: 'column', @@ -10,7 +10,7 @@ export const setInitialParticipantsStyle = () => padding: '0 1.5rem', }); -export const setInitialParticipantsInputGroupStyle = () => +export const setInitialMemberListModalInputGroupStyle = () => css({ display: 'flex', flexDirection: 'column', diff --git a/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx b/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx new file mode 100644 index 000000000..a69f09192 --- /dev/null +++ b/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx @@ -0,0 +1,60 @@ +import {Text, BottomSheet, FixedButton, LabelGroupInput} from 'haengdong-design'; +import {useStepList} from '@hooks/useStepList/useStepList'; +import validateMemberName from '@utils/validate/validateMemberName'; + +import useDynamicInput from '@hooks/useDynamicInput'; +import { + setInitialMemberListModalInputGroupStyle, + setInitialMemberListModalStyle, +} from './SetInitialMemberListModal.style'; + +interface SetInitialMemberListProps { + openBottomSheet: boolean; + setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; +} + +const SetInitialMemberListModal = ({openBottomSheet, setOpenBottomSheet}: SetInitialMemberListProps) => { + const { + inputList, + inputRefList, + handleInputChange, + deleteEmptyInputElementOnBlur, + getFilledInputList, + errorMessage, + canSubmit, + focusNextInputOnEnter, + } = useDynamicInput(validateMemberName); + const {updateMemberList} = useStepList(); + + const handleSubmit = () => { + updateMemberList({memberNameList: getFilledInputList().map(({value}) => value), type: 'IN'}); + setOpenBottomSheet(false); + }; + + return ( + <BottomSheet isOpened={openBottomSheet} onChangeClose={() => setOpenBottomSheet(false)}> + <div css={setInitialMemberListModalStyle}> + <Text size="bodyBold">초기 인원 설정하기</Text> + <div css={setInitialMemberListModalInputGroupStyle}> + <LabelGroupInput labelText="이름" errorText={errorMessage}> + {inputList.map(({value, index}) => ( + <LabelGroupInput.Element + key={`${index}`} + elementKey={`${index}`} + type="text" + value={value} + ref={el => (inputRefList.current[index] = el)} + onChange={e => handleInputChange(index, e)} + onBlur={() => deleteEmptyInputElementOnBlur()} + onKeyDown={e => focusNextInputOnEnter(e, index)} + /> + ))} + </LabelGroupInput> + </div> + </div> + <FixedButton disabled={!canSubmit} variants={'primary'} onClick={handleSubmit} children={'인원 설정 완료'} /> + </BottomSheet> + ); +}; + +export default SetInitialMemberListModal; diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx deleted file mode 100644 index 4b478d022..000000000 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import {Text, BottomSheet, FixedButton, LabelGroupInput} from 'haengdong-design'; -import {useStepList} from '@hooks/useStepList/useStepList'; -import validateMemberName from '@utils/validate/validateMemberName'; - -import useDynamicInput from '@hooks/useDynamicInput'; - -import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from './SetInitialParticipants.style'; - -interface SetInitialParticipantsProps { - openBottomSheet: boolean; - setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; -} - -const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitialParticipantsProps) => { - const {inputs, inputRefs, handleInputChange, handleInputBlur, getFilledInputList, errorMessage, canSubmit} = - useDynamicInput(validateMemberName); - const {updateMemberList} = useStepList(); - - const handleSubmit = () => { - updateMemberList({memberNameList: getFilledInputList(), type: 'IN'}); - setOpenBottomSheet(false); - }; - - return ( - <BottomSheet isOpened={openBottomSheet} onChangeClose={() => setOpenBottomSheet(false)}> - <div css={setInitialParticipantsStyle}> - <Text size="bodyBold">초기 인원 설정하기</Text> - <div css={setInitialParticipantsInputGroupStyle}> - <LabelGroupInput labelText="이름" errorText={'d'}> - {inputs.map(({value, index}) => ( - <LabelGroupInput.Element - id={`${index}`} // 확인용도 임시임 - key={`${index}`} - elementKey={`${index}`} - type="text" - value={value} - ref={el => (inputRefs.current[index] = el)} - onChange={e => handleInputChange(index, e)} - onBlur={() => handleInputBlur(index)} - onKeyDown={e => { - if (e.nativeEvent.isComposing) return; - - if (e.key === 'Enter') { - if (index < inputs.length - 1) { - inputRefs.current[index + 1]?.focus(); - } - } - }} - /> - ))} - </LabelGroupInput> - </div> - </div> - <FixedButton disabled={!canSubmit} variants={'primary'} onClick={handleSubmit} children={'인원 설정 완료'} /> - </BottomSheet> - ); -}; - -export default SetInitialParticipants; From f767e003f7483da08bfd04565ba68c58c7b7c01c Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:10:01 +0900 Subject: [PATCH 22/43] =?UTF-8?q?feat:=20enter=EA=B0=80=20=EB=88=8C?= =?UTF-8?q?=EB=A0=B8=EC=9D=84=20=EB=95=8C=20=EC=8B=A4=ED=96=89=ED=95=A0=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=ED=9B=85=20=EC=95=88=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useDynamicInput.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index ac6b99694..9d759db98 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -72,6 +72,20 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { } }; + const focusNextInputOnEnter = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => { + if (e.nativeEvent.isComposing) return; + + if (e.key === 'Enter') { + inputRefList.current[index + 1]?.focus(); + } + }; + // 아래부터는 이 훅에서 재사용되는 함수입니다. + + // list 인자를 넘겨주면 그 인자로 찾고, 없다면 inputList state를 사용합니다. + const findInputByIndex = (index: number, list?: InputValue[]) => { + return (list ?? inputList).filter(input => input.index === index)[0]; + }; + useEffect(() => { if (inputRefs.current.length <= 0) return; From 895b8f2d90a83700dc4701c218a592fa5f332841 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:11:50 +0900 Subject: [PATCH 23/43] =?UTF-8?q?fix:=200,=201=EB=B2=88=EB=A7=8C=20?= =?UTF-8?q?=EC=9E=88=EC=9D=84=20=EB=95=8C=202=EA=B0=9C=EC=9D=98=20?= =?UTF-8?q?=EB=B9=88=20=EC=9E=87=ED=92=8B=20=EC=97=98=EB=A6=AC=EB=A8=BC?= =?UTF-8?q?=ED=8A=B8=EA=B0=80=20=EB=82=A8=EC=95=84=EB=B2=84=EB=A6=AC?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0,=20handleBl?= =?UTF-8?q?ur=20->=20deleteEmptyInputElementOnBlur=20=EB=A1=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=EC=9D=84=20=EB=93=9C=EB=9F=AC=EB=82=B4=EB=8A=94=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=AA=85=EC=9C=BC=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 --- client/src/hooks/useDynamicInput.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index 9d759db98..1e6677f24 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -51,8 +51,12 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { return getFilledStringList(inputs.map(({value}) => value)); }; - const isLastInputFilled = (index: number, value: string) => { - const lastInputIndex = inputs[inputs.length - 1].index; + const deleteEmptyInputElementOnBlur = () => { + // 0, 1번 input이 값이 있는 상태에서 두 input의 값을 모두 x버튼으로 제거해도 input이 2개 남아있는 문제를 위해 조건문을 추가했습니다. + if (getFilledInputList().length === 0 && inputList.length > 1) { + setInputList([{index: 0, value: ''}]); + return; + } return value !== '' && index === lastInputIndex; }; From 2f9d5386264b02c7fa6cc546cdd55fa67fec964c Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:12:17 +0900 Subject: [PATCH 24/43] =?UTF-8?q?feat:=20canSubmit=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A5=BC=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8A=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20=ED=95=A8=EC=88=98=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useDynamicInput.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index 1e6677f24..cac5a9cc5 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -47,8 +47,13 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { return stringList.filter(string => string.trim() !== ''); }; - const getFilledInputList = () => { - return getFilledStringList(inputs.map(({value}) => value)); + // 현재까지 입력된 값들로 submit을 할 수 있는지 여부를 핸들합니다. + const handleCanSubmit = () => { + if (inputList.length > 0 && getFilledInputList().length > 0) { + setCanSubmit(true); + } else { + setCanSubmit(false); + } }; const deleteEmptyInputElementOnBlur = () => { From a1af31c503045cdeca27362176d7b1b6613291b2 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:13:42 +0900 Subject: [PATCH 25/43] =?UTF-8?q?refactor:=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EB=B3=80=ED=99=94=EC=A4=91=EC=9D=B8=20targetInput=EC=9D=84=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EB=B0=98=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC,=20=EC=84=A0=EC=96=B8=EB=90=98=EC=96=B4=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=ED=95=A8=EC=88=98=20=EC=88=9C=EC=84=9C=EB=A5=BC=20?= =?UTF-8?q?useEffect,=20on-*=20props=EC=97=90=20=EC=9E=A5=EC=B0=A9?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=ED=95=A8=EC=88=98,=20=EC=9C=A0=ED=8B=B8?= =?UTF-8?q?=EC=84=B1=20=ED=95=A8=EC=88=98=EB=A1=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useDynamicInput.tsx | 103 ++++++++++++++++----------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index cac5a9cc5..c9310f86c 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -8,43 +8,65 @@ type InputValue = { }; const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { - const [inputs, setInputs] = useState<InputValue[]>([{value: '', index: 0}]); - const inputRefs = useRef<(HTMLInputElement | null)[]>([]); + const [inputList, setInputList] = useState<InputValue[]>([{value: '', index: 0}]); + const inputRefList = useRef<(HTMLInputElement | null)[]>([]); const [errorMessage, setErrorMessage] = useState(''); const [canSubmit, setCanSubmit] = useState(false); - // const [errorIds, setErrorIds] = useState<string>(''); + + useEffect(() => { + if (inputRefList.current.length <= 0) return; + + const lastInput = inputRefList.current[inputRefList.current.length - 1]; + + if (lastInput) { + lastInput.scrollIntoView({behavior: 'smooth', block: 'center'}); + } + }, [inputList]); const handleInputChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => { const {value} = event.target; - const {isValid, errorMessage} = validateFunc(value); + const {isValid: isValidInput, errorMessage: validationResultMessage} = validateFunc(value); if (isLastInputFilled(index, value)) { - setInputs(prevInputs => { - const updatedInputs = [...prevInputs]; - const targetInput = updatedInputs.filter(input => input.index === index); + // 마지막 인풋이 한 자라도 채워진다면 새로운 인풋을 생성해 간편한 다음 입력을 유도합니다. - targetInput[0].value = value; + setErrorMessage(''); + setInputList(prevInputs => { + const updatedInputList = [...prevInputs]; + const targetInput = findInputByIndex(index, updatedInputList); + + targetInput.value = value; // 새로운 인덱스를 inputs 배열 길이를 기준으로 설정 - const newIndex = updatedInputs[updatedInputs.length - 1].index + 1; - const finalInputs = [...updatedInputs, {index: newIndex, value: ''}]; + const newIndex = updatedInputList[updatedInputList.length - 1].index + 1; - return finalInputs; + return [...updatedInputList, {index: newIndex, value: ''}]; }); - } else { - setInputs(prevInputs => { - const updatedInputs = [...prevInputs]; - const targetInput = updatedInputs.filter(input => input.index === index); + } else if (isValidInput || value.length === 0) { + // 인풋이 비어있다면 새로운 인풋을 생성하지 않습니다. + + setErrorMessage(''); + setInputList(prevInputs => { + const updatedInputList = [...prevInputs]; + const targetInput = findInputByIndex(index, updatedInputList); - targetInput[0].value = value; + targetInput.value = value; - return updatedInputs; + return updatedInputList; }); + } else { + // 유효성 검사에 실패한 입력입니다. 이전 입력으로 복구하고 에러 메세지를 세팅합니다. + + // index에 해당하는 아이템을 찾습니다. + const targetInput = findInputByIndex(index); + + // 오류가 난 값말고 기존의 값을 사용합니다. + event.target.value = targetInput.value; + + setErrorMessage(validationResultMessage ?? ''); } - }; - const getFilledStringList = (stringList: string[]) => { - return stringList.filter(string => string.trim() !== ''); + handleCanSubmit(); }; // 현재까지 입력된 값들로 submit을 할 수 있는지 여부를 핸들합니다. @@ -63,20 +85,18 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { return; } - return value !== '' && index === lastInputIndex; - }; + // *표시 조건문은 처음에 input을 클릭했다가 블러시켰을 때 filledInputList가 아예 없어 .index에 접근할 때 오류가 납니다. 이를 위한 얼리리턴을 두었습니다. + if (getFilledInputList().length === 0) return; - const handleInputBlur = (index: number) => { - if (getFilledInputList().length !== inputs.length) { - setInputs(prevInputs => { - const filledInputList = prevInputs.filter(({value}) => value !== ''); + // * + if (getFilledInputList().length !== inputList.length) { + setInputList(inputList => { + const filledInputList = getFilledInputList(inputList); // 새 입력의 인덱스를 inputs 길이를 기준으로 설정 const newIndex = filledInputList[filledInputList.length - 1].index + 1; - const finalInputs = [...filledInputList, {index: newIndex, value: ''}]; - - return finalInputs; + return [...filledInputList, {index: newIndex, value: ''}]; }); } }; @@ -95,26 +115,27 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { return (list ?? inputList).filter(input => input.index === index)[0]; }; - useEffect(() => { - if (inputRefs.current.length <= 0) return; + // list 인자를 넘겨주면 그 인자로 찾고, 없다면 inputList state를 사용합니다. + const getFilledInputList = (list?: InputValue[]) => { + return (list ?? inputList).filter(({value}) => value !== ''); + }; - const lastInput = inputRefs.current[inputRefs.current.length - 1]; + const isLastInputFilled = (index: number, value: string) => { + const lastInputIndex = inputList[inputList.length - 1].index; - if (lastInput) { - lastInput.scrollIntoView({behavior: 'smooth', block: 'center'}); - } - }, [inputs]); + return value !== '' && index === lastInputIndex; + }; return { - inputs, - inputRefs, + inputList, + inputRefList, handleInputChange, - handleInputBlur, + deleteEmptyInputElementOnBlur, errorMessage, getFilledInputList, - canSubmit, // TODO: (@weadie) 이거 리모트에서 보고 가져오기 + focusNextInputOnEnter, + canSubmit, // TODO: (@weadie) 네이밍 수정 - getNonEmptyInputs: getFilledInputList, }; }; From ec0a285d13fc6c9d32e8f67d2a60e74c8c2921ec Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:16:05 +0900 Subject: [PATCH 26/43] =?UTF-8?q?rename:=20pages=EC=95=88=EC=9D=98=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=97=90=20=EC=A0=84=EB=B6=80=20-Page=20?= =?UTF-8?q?=EB=A5=BC=20=EB=B6=99=EC=97=AC=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=84=B1=EA=B2=A9=EC=9D=84=20=EC=9E=98=20=EB=93=9C?= =?UTF-8?q?=EB=9F=AC=EB=82=BC=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Create/index.ts | 2 -- .../CompleteCreateEventPage.tsx} | 4 +-- .../SetEventNamePage.tsx} | 4 +-- client/src/pages/CreateEventPage/index.ts | 2 ++ client/src/pages/Event/Admin/index.ts | 1 - client/src/pages/Event/Home/index.ts | 1 - client/src/pages/Event/index.ts | 1 - .../AdminPage/AdminPage.style.ts} | 0 .../AdminPage/AdminPage.tsx} | 34 ++++++++----------- client/src/pages/EventPage/AdminPage/index.ts | 1 + .../EvenPageLayout.tsx} | 4 +-- .../HomePage/HomePage.tsx} | 4 +-- client/src/pages/EventPage/HomePage/index.ts | 1 + client/src/pages/EventPage/index.ts | 1 + client/src/pages/Main/index.ts | 1 - .../{Main/Main.tsx => MainPage/MainPage.tsx} | 4 +-- client/src/pages/MainPage/index.ts | 1 + client/src/router.tsx | 17 +++++----- 18 files changed, 38 insertions(+), 45 deletions(-) delete mode 100644 client/src/pages/Create/index.ts rename client/src/pages/{Create/Complete.tsx => CreateEventPage/CompleteCreateEventPage.tsx} (93%) rename client/src/pages/{Create/Name.tsx => CreateEventPage/SetEventNamePage.tsx} (96%) create mode 100644 client/src/pages/CreateEventPage/index.ts delete mode 100644 client/src/pages/Event/Admin/index.ts delete mode 100644 client/src/pages/Event/Home/index.ts delete mode 100644 client/src/pages/Event/index.ts rename client/src/pages/{Event/Admin/Admin.style.ts => EventPage/AdminPage/AdminPage.style.ts} (100%) rename client/src/pages/{Event/Admin/Admin.tsx => EventPage/AdminPage/AdminPage.tsx} (78%) create mode 100644 client/src/pages/EventPage/AdminPage/index.ts rename client/src/pages/{Event/EventLayout.tsx => EventPage/EvenPageLayout.tsx} (88%) rename client/src/pages/{Event/Home/Home.tsx => EventPage/HomePage/HomePage.tsx} (95%) create mode 100644 client/src/pages/EventPage/HomePage/index.ts create mode 100644 client/src/pages/EventPage/index.ts delete mode 100644 client/src/pages/Main/index.ts rename client/src/pages/{Main/Main.tsx => MainPage/MainPage.tsx} (90%) create mode 100644 client/src/pages/MainPage/index.ts diff --git a/client/src/pages/Create/index.ts b/client/src/pages/Create/index.ts deleted file mode 100644 index 5ce37a93e..000000000 --- a/client/src/pages/Create/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {default as CreateNamePage} from './Name'; -export {default as CreateCompletePage} from './Complete'; diff --git a/client/src/pages/Create/Complete.tsx b/client/src/pages/CreateEventPage/CompleteCreateEventPage.tsx similarity index 93% rename from client/src/pages/Create/Complete.tsx rename to client/src/pages/CreateEventPage/CompleteCreateEventPage.tsx index 786979607..0f30c1f04 100644 --- a/client/src/pages/Create/Complete.tsx +++ b/client/src/pages/CreateEventPage/CompleteCreateEventPage.tsx @@ -4,7 +4,7 @@ import {FixedButton, MainLayout, Title, TopNav} from 'haengdong-design'; import {ROUTER_URLS} from '@constants/routerUrls'; -const CompleteCreateEvent = () => { +const CompleteCreateEventPage = () => { const [url, setUrl] = useState(''); const navigate = useNavigate(); const location = useLocation(); @@ -35,4 +35,4 @@ const CompleteCreateEvent = () => { ); }; -export default CompleteCreateEvent; +export default CompleteCreateEventPage; diff --git a/client/src/pages/Create/Name.tsx b/client/src/pages/CreateEventPage/SetEventNamePage.tsx similarity index 96% rename from client/src/pages/Create/Name.tsx rename to client/src/pages/CreateEventPage/SetEventNamePage.tsx index 97f98a0c3..932a11945 100644 --- a/client/src/pages/Create/Name.tsx +++ b/client/src/pages/CreateEventPage/SetEventNamePage.tsx @@ -7,7 +7,7 @@ import validateEventName from '@utils/validate/validateEventName'; import {ROUTER_URLS} from '@constants/routerUrls'; -const CreateEvent = () => { +const SetEventNamePage = () => { const [eventName, setEventName] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const [canSubmit, setCanSubmit] = useState(false); @@ -69,4 +69,4 @@ const CreateEvent = () => { ); }; -export default CreateEvent; +export default SetEventNamePage; diff --git a/client/src/pages/CreateEventPage/index.ts b/client/src/pages/CreateEventPage/index.ts new file mode 100644 index 000000000..9b66c3ba5 --- /dev/null +++ b/client/src/pages/CreateEventPage/index.ts @@ -0,0 +1,2 @@ +export {default as SetEventNamePage} from './SetEventNamePage'; +export {default as CompleteCreateEventPage} from './CompleteCreateEventPage'; diff --git a/client/src/pages/Event/Admin/index.ts b/client/src/pages/Event/Admin/index.ts deleted file mode 100644 index 9799a7485..000000000 --- a/client/src/pages/Event/Admin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {default as AdminPage} from './Admin'; diff --git a/client/src/pages/Event/Home/index.ts b/client/src/pages/Event/Home/index.ts deleted file mode 100644 index 75e27b8a3..000000000 --- a/client/src/pages/Event/Home/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {default as HomePage} from './Home'; diff --git a/client/src/pages/Event/index.ts b/client/src/pages/Event/index.ts deleted file mode 100644 index af2aa805d..000000000 --- a/client/src/pages/Event/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {default as EventPage} from './EventLayout'; diff --git a/client/src/pages/Event/Admin/Admin.style.ts b/client/src/pages/EventPage/AdminPage/AdminPage.style.ts similarity index 100% rename from client/src/pages/Event/Admin/Admin.style.ts rename to client/src/pages/EventPage/AdminPage/AdminPage.style.ts diff --git a/client/src/pages/Event/Admin/Admin.tsx b/client/src/pages/EventPage/AdminPage/AdminPage.tsx similarity index 78% rename from client/src/pages/Event/Admin/Admin.tsx rename to client/src/pages/EventPage/AdminPage/AdminPage.tsx index bda03da55..6932fc59a 100644 --- a/client/src/pages/Event/Admin/Admin.tsx +++ b/client/src/pages/EventPage/AdminPage/AdminPage.tsx @@ -6,35 +6,29 @@ import {useStepList} from '@hooks/useStepList/useStepList'; import {requestGetEventName} from '@apis/request/event'; import useEventId from '@hooks/useEventId/useEventId'; -import {SetActionModalContent, SetInitialParticipants} from '@components/Modal'; +import {ReceiptStyle} from './AdminPage.style'; +import {SetActionListModal, SetInitialMemberListModal} from '@components/Modal'; -import {ReceiptStyle} from './Admin.style'; - -export type PurchaseInformation = { - title: string; - price: number; -}; - -export type ParticipantType = { - name: string; - type: InOutType; -}; - -interface ModalRenderingProps { +interface ModalBasedOnMemberCountProps { memberNameList: string[]; openBottomSheet: boolean; setOrder: React.Dispatch<React.SetStateAction<number>>; setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; } -const ModalRendering = ({memberNameList, openBottomSheet, setOrder, setOpenBottomSheet}: ModalRenderingProps) => { +const ModalBasedOnMemberCount = ({ + memberNameList, + openBottomSheet, + setOrder, + setOpenBottomSheet, +}: ModalBasedOnMemberCountProps) => { switch (memberNameList.length) { case 0: - return <SetInitialParticipants setOpenBottomSheet={setOpenBottomSheet} openBottomSheet={openBottomSheet} />; + return <SetInitialMemberListModal setOpenBottomSheet={setOpenBottomSheet} openBottomSheet={openBottomSheet} />; default: return ( - <SetActionModalContent + <SetActionListModal setOrder={setOrder} setOpenBottomSheet={setOpenBottomSheet} openBottomSheet={openBottomSheet} @@ -43,7 +37,7 @@ const ModalRendering = ({memberNameList, openBottomSheet, setOrder, setOpenBotto } }; -const Admin = () => { +const AdminPage = () => { const [openBottomSheet, setOpenBottomSheet] = useState(false); const [order, setOrder] = useState<number>(1); @@ -82,7 +76,7 @@ const Admin = () => { onClick={() => setOpenBottomSheet(prev => !prev)} /> {openBottomSheet && ( - <ModalRendering + <ModalBasedOnMemberCount memberNameList={memberNameList} setOrder={setOrder} setOpenBottomSheet={setOpenBottomSheet} @@ -94,4 +88,4 @@ const Admin = () => { ); }; -export default Admin; +export default AdminPage; diff --git a/client/src/pages/EventPage/AdminPage/index.ts b/client/src/pages/EventPage/AdminPage/index.ts new file mode 100644 index 000000000..b80c0bb2f --- /dev/null +++ b/client/src/pages/EventPage/AdminPage/index.ts @@ -0,0 +1 @@ +export {default as AdminPage} from './AdminPage'; diff --git a/client/src/pages/Event/EventLayout.tsx b/client/src/pages/EventPage/EvenPageLayout.tsx similarity index 88% rename from client/src/pages/Event/EventLayout.tsx rename to client/src/pages/EventPage/EvenPageLayout.tsx index 2b2b439de..3f68b4ba1 100644 --- a/client/src/pages/Event/EventLayout.tsx +++ b/client/src/pages/EventPage/EvenPageLayout.tsx @@ -5,7 +5,7 @@ import StepListProvider from '@hooks/useStepList/useStepList'; import useNavSwitch from '@hooks/useNavSwitch'; -const EventLayout = () => { +const EventPageLayout = () => { const {nav, paths, onChange} = useNavSwitch(); return ( @@ -20,4 +20,4 @@ const EventLayout = () => { ); }; -export default EventLayout; +export default EventPageLayout; diff --git a/client/src/pages/Event/Home/Home.tsx b/client/src/pages/EventPage/HomePage/HomePage.tsx similarity index 95% rename from client/src/pages/Event/Home/Home.tsx rename to client/src/pages/EventPage/HomePage/HomePage.tsx index 8ff195cb8..19227d404 100644 --- a/client/src/pages/Event/Home/Home.tsx +++ b/client/src/pages/EventPage/HomePage/HomePage.tsx @@ -7,7 +7,7 @@ import {useStepList} from '@hooks/useStepList/useStepList'; import useEventId from '@hooks/useEventId/useEventId'; import {requestGetEventName} from '@apis/request/event'; -const HomeContent = () => { +const HomePage = () => { const {getTotalPrice} = useStepList(); const {eventId} = useEventId(); @@ -37,4 +37,4 @@ const HomeContent = () => { ); }; -export default HomeContent; +export default HomePage; diff --git a/client/src/pages/EventPage/HomePage/index.ts b/client/src/pages/EventPage/HomePage/index.ts new file mode 100644 index 000000000..aa0bf2b3f --- /dev/null +++ b/client/src/pages/EventPage/HomePage/index.ts @@ -0,0 +1 @@ +export {default as HomePage} from './HomePage'; diff --git a/client/src/pages/EventPage/index.ts b/client/src/pages/EventPage/index.ts new file mode 100644 index 000000000..c560632da --- /dev/null +++ b/client/src/pages/EventPage/index.ts @@ -0,0 +1 @@ +export {default as EventPage} from './EvenPageLayout'; diff --git a/client/src/pages/Main/index.ts b/client/src/pages/Main/index.ts deleted file mode 100644 index c3cce532e..000000000 --- a/client/src/pages/Main/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {default as MainPage} from './Main'; diff --git a/client/src/pages/Main/Main.tsx b/client/src/pages/MainPage/MainPage.tsx similarity index 90% rename from client/src/pages/Main/Main.tsx rename to client/src/pages/MainPage/MainPage.tsx index dfd7c406e..b216e50c6 100644 --- a/client/src/pages/Main/Main.tsx +++ b/client/src/pages/MainPage/MainPage.tsx @@ -3,7 +3,7 @@ import {FixedButton, MainLayout, Title, TopNav} from 'haengdong-design'; import {ROUTER_URLS} from '@constants/routerUrls'; -const Main = () => { +const MainPage = () => { const navigate = useNavigate(); return ( @@ -15,4 +15,4 @@ const Main = () => { ); }; -export default Main; +export default MainPage; diff --git a/client/src/pages/MainPage/index.ts b/client/src/pages/MainPage/index.ts new file mode 100644 index 000000000..017fff307 --- /dev/null +++ b/client/src/pages/MainPage/index.ts @@ -0,0 +1 @@ +export {default as MainPage} from './MainPage'; diff --git a/client/src/router.tsx b/client/src/router.tsx index 70f7a1e71..15a39cbcd 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -1,14 +1,13 @@ import {createBrowserRouter} from 'react-router-dom'; -import {AdminPage} from '@pages/Event/Admin'; -import {HomePage} from '@pages/Event/Home'; - -import {MainPage} from '@pages/Main'; -import {CreateNamePage, CreateCompletePage} from '@pages/Create'; -import {EventPage} from '@pages/Event'; - import {ROUTER_URLS} from '@constants/routerUrls'; +import {CompleteCreateEventPage, SetEventNamePage} from '@pages/CreateEventPage'; +import {MainPage} from '@pages/MainPage'; +import {EventPage} from '@pages/EventPage'; +import {AdminPage} from '@pages/EventPage/AdminPage'; +import {HomePage} from '@pages/EventPage/HomePage'; + import App from './App'; const router = createBrowserRouter([ @@ -23,11 +22,11 @@ const router = createBrowserRouter([ }, { path: ROUTER_URLS.eventCreateName, - element: <CreateNamePage />, + element: <SetEventNamePage />, }, { path: ROUTER_URLS.eventCreateComplete, - element: <CreateCompletePage />, + element: <CompleteCreateEventPage />, }, { path: ROUTER_URLS.event, From a4942e87dde9903d00c4a9b1c6fc61565d00afd2 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:19:18 +0900 Subject: [PATCH 27/43] =?UTF-8?q?fix:=20=EC=9D=B8=EB=8D=B1=EC=8A=A4?= =?UTF-8?q?=ED=8B=80=20=EC=82=AC=EC=9A=A9=ED=95=B4=20=EC=9D=B8=ED=92=8B=20?= =?UTF-8?q?=EC=8C=8D=EC=9D=84=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95.=20=EC=9D=B8=EB=8D=B1=EC=8A=A4?= =?UTF-8?q?=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=A8=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=EB=AA=A8=EB=93=A0=20=ED=95=A8=EC=88=98=EB=8F=84=20?= =?UTF-8?q?=EC=9D=B8=EB=8D=B1=EC=8A=A4=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useDynamicInputPairs.tsx | 135 +++++++++++++++++----- 1 file changed, 105 insertions(+), 30 deletions(-) diff --git a/client/src/hooks/useDynamicInputPairs.tsx b/client/src/hooks/useDynamicInputPairs.tsx index 220598fd6..459fecade 100644 --- a/client/src/hooks/useDynamicInputPairs.tsx +++ b/client/src/hooks/useDynamicInputPairs.tsx @@ -2,31 +2,98 @@ import {useEffect, useRef, useState} from 'react'; import {ValidateResult} from '@utils/validate/type'; -const useDynamicInputPairs = (validateFunc: (inputPair: Bill) => ValidateResult) => { - const [inputPairs, setInputPairs] = useState<Bill[]>([{title: '', price: 0}]); - const inputRefs = useRef<(HTMLInputElement | null)[]>([]); +type InputPair = Omit<Bill, 'price'> & { + price: string; + index: number; +}; + +type BillInputType = 'title' | 'price'; + +// TODO: (@weadie) 지나치게 도메인에 묶여있는 인풋. 절대 다른 페어인풋으로 재사용할 수 없다. +const useDynamicBillActionInput = (validateFunc: (inputPair: Bill) => ValidateResult) => { + const [inputPairList, setInputPairList] = useState<InputPair[]>([{title: '', price: '', index: 0}]); + const inputRefList = useRef<(HTMLInputElement | null)[]>([]); const [errorMessage, setErrorMessage] = useState(''); const [canSubmit, setCanSubmit] = useState(false); - const handleInputChange = (index: number, field: 'title' | 'price', event: React.ChangeEvent<HTMLInputElement>) => { - const value = event.target.value; - const currentPair = inputPairs[index]; - const validation = validateFunc(currentPair); - - if (validation.isValid) { - const newInputPairs = [...inputPairs]; - newInputPairs[index] = { - ...newInputPairs[index], - [field]: field === 'price' ? parseFloat(value) : value, - }; - setInputPairs(newInputPairs); + useEffect(() => { + if (inputRefList.current.length > 0) { + const lastInputPair = inputRefList.current.slice(-2); + lastInputPair.forEach(ref => ref?.scrollIntoView({behavior: 'smooth', block: 'center'})); + } + }, [inputPairList]); + + const handleInputChange = (index: number, field: BillInputType, event: React.ChangeEvent<HTMLInputElement>) => { + const {value} = event.target; + const targetInputPair = findInputPairByIndex(index); + const {isValid: isValidInput, errorMessage: validationResultMessage} = validateFunc({ + ...targetInputPair, + price: 0, // price가 input에서 0을 초기값으로 갖지않도록 타입을 수정했기 때문에 0을 명시적으로 넘겨줍니다. + [field]: value, + }); + + const {title, price} = targetInputPair; + + if (isLastInputPairFilled({index, field, value})) { + setErrorMessage(''); + setInputPairList(prevInputPairList => { + const updatedInputPairList = [...prevInputPairList]; + const targetInputPair = findInputPairByIndex(index, updatedInputPairList); + + targetInputPair[field] = value; + + // 새로운 인덱스를 inputs 배열 길이를 기준으로 설정 + const newIndex = updatedInputPairList[updatedInputPairList.length - 1].index + 1; + const finalInputs = [...updatedInputPairList, {index: newIndex, title: '', price: ''}]; + + return finalInputs; + }); + } else if (isValidInput || value.length === 0) { setErrorMessage(''); + setInputPairList(prevInputPairList => { + const updatedInputPairList = [...prevInputPairList]; + const targetInputPair = findInputPairByIndex(index, updatedInputPairList); + + targetInputPair[field] = value; + + return updatedInputPairList; + }); } else { - event.target.value = inputPairs[index][field].toString(); - setErrorMessage(validation.errorMessage ?? ''); + const targetInput = findInputPairByIndex(index); + + event.target.value = targetInput[field]; + + setErrorMessage(validationResultMessage ?? ''); } - if (inputPairs[index].price !== 0 && inputPairs[index].title.trim().length > 0 && inputPairs.length > 0) { + handleCanSubmit(); + }; + + const deleteEmptyInputPairElementOnBlur = () => { + // 이름, 금액 2개중 최소 하나 이상 값을 가지고 있는 inputPair 배열 + const filledMinInputPairList = inputPairList.filter(({title, price}) => title !== '' || price !== ''); + + // 0쌍, 1쌍 input이 값이 있는 상태에서 두 쌍의 값을 모두 x버튼으로 제거해도 입력 쌍이 2개 남아있는 문제를 위해 조건문을 추가했습니다. + if (filledMinInputPairList.length === 0 && inputPairList.length > 1) { + setInputPairList([{index: 0, title: '', price: ''}]); + return; + } + + if (filledMinInputPairList.length === 0) return; + + if (filledMinInputPairList.length !== inputPairList.length) { + // 이름, 금액 2개중 하나라도 값이 있다면 지우지 않습니다. + setInputPairList(prevInputPairList => { + const filledInputPairList = prevInputPairList.filter(({title, price}) => title !== '' || price !== ''); + + const newIndex = filledInputPairList[filledInputPairList.length - 1].index + 1; + return [...filledInputPairList, {index: newIndex, title: '', price: ''}]; + }); + } + }; + + const handleCanSubmit = () => { + if (inputPairList.length > 0 && getFilledInputPairList().length > 0) { setCanSubmit(true); } else { setCanSubmit(false); @@ -45,25 +112,33 @@ const useDynamicInputPairs = (validateFunc: (inputPair: Bill) => ValidateResult) } }; - const getNonEmptyInputPairs = () => { - return inputPairs.filter(pair => pair.title.trim() !== '' && pair.price !== 0); + // 아래부터는 이 훅에서 재사용되는 함수입니다. + + // list 인자를 넘겨주면 그 인자로 찾고, 없다면 InputPairList state를 사용합니다. + const findInputPairByIndex = (index: number, list?: InputPair[]) => { + return (list ?? inputPairList).filter(input => input.index === index)[0]; }; - useEffect(() => { - if (inputRefs.current.length > 0) { - const lastInputPair = inputRefs.current.slice(-2); - lastInputPair.forEach(ref => ref?.scrollIntoView({behavior: 'smooth', block: 'center'})); - } - }, [inputPairs]); + // list 인자를 넘겨주면 그 인자로 찾고, 없다면 InputPairList state를 사용합니다. + const getFilledInputPairList = (list?: InputPair[]) => { + return (list ?? inputPairList).filter(({title, price}) => title !== '' && price !== ''); + }; + + const isLastInputPairFilled = ({index, value}: {index: number; field: BillInputType; value: string}) => { + const lastInputIndex = inputPairList[inputPairList.length - 1].index; + + return value !== '' && index === lastInputIndex; + }; return { - inputPairs, - getNonEmptyInputPairs, - inputRefs, + inputPairList, + getFilledInputPairList, + inputRefList, handleInputChange, - handleInputBlur, + deleteEmptyInputPairElementOnBlur, errorMessage, canSubmit, + focusNextInputOnEnter, }; }; From d2419dd32fd6a5991c9bcad5897894c80416bc33 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:20:50 +0900 Subject: [PATCH 28/43] =?UTF-8?q?feat:=20pair=EB=8B=B9=20=ED=95=98?= =?UTF-8?q?=EB=82=98=EC=9D=98=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=96=EC=A7=80=EB=A7=8C,=20input=20element=EB=8A=94=20?= =?UTF-8?q?=EB=91=90=20=EA=B0=9C=EC=9D=B4=EB=AF=80=EB=A1=9C=20=EC=A0=95?= =?UTF-8?q?=ED=99=95=ED=95=9C=20input=20element=ED=8A=B9=EC=A0=95=EC=9D=84?= =?UTF-8?q?=20=EC=9C=84=ED=95=B4=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=EB=A5=BC=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=ED=95=B4=20focus=EB=A5=BC=20=EB=84=98?= =?UTF-8?q?=EA=B2=A8=EC=A3=BC=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useDynamicInputPairs.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/src/hooks/useDynamicInputPairs.tsx b/client/src/hooks/useDynamicInputPairs.tsx index 459fecade..f85966e43 100644 --- a/client/src/hooks/useDynamicInputPairs.tsx +++ b/client/src/hooks/useDynamicInputPairs.tsx @@ -1,6 +1,7 @@ import {useEffect, useRef, useState} from 'react'; import {ValidateResult} from '@utils/validate/type'; +import {Bill} from 'types/serviceType'; type InputPair = Omit<Bill, 'price'> & { price: string; @@ -100,15 +101,14 @@ const useDynamicBillActionInput = (validateFunc: (inputPair: Bill) => ValidateRe } }; - const handleInputBlur = (index: number) => { - const currentPair = inputPairs[index]; - const filterEmpty = inputPairs.filter(pair => pair.title.trim() !== '' && pair.price !== 0); + const focusNextInputOnEnter = (e: React.KeyboardEvent<HTMLInputElement>, index: number, field: BillInputType) => { + if (e.nativeEvent.isComposing) return; - if (filterEmpty.length !== inputPairs.length) { - setInputPairs(prev => prev.filter((_, i) => i !== index)); - } - if (currentPair.title.trim() !== '' && currentPair.price !== 0 && index === inputPairs.length - 1) { - setInputPairs(prev => [...prev, {title: '', price: 0}]); + if (e.key === 'Enter') { + // 2개(제목, 가격)를 쌍으로 index를 관리하고 있으므로 input element 정확히 특정하기 위한 개별 input element key 값을 계산합니다. + const exactInputIndex = index * 2 + (field === 'title' ? 0 : 1); + + inputRefList.current[exactInputIndex + 1]?.focus(); } }; @@ -142,4 +142,4 @@ const useDynamicBillActionInput = (validateFunc: (inputPair: Bill) => ValidateRe }; }; -export default useDynamicInputPairs; +export default useDynamicBillActionInput; From 0a6c763d01c19c0d0c9f5495591b2de456629f37 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:22:31 +0900 Subject: [PATCH 29/43] =?UTF-8?q?rename:=20=ED=95=B4=EB=8B=B9=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=90=20=EC=9D=B4=EB=AF=B8=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EA=B3=BC=20=EA=B9=8A=EA=B2=8C=20=EC=96=BD=ED=9E=8C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EA=B0=80=20=EB=82=B4=EC=9E=A5=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EC=9C=BC=EB=AF=80=EB=A1=9C=20=EC=9D=98?= =?UTF-8?q?=EB=AF=B8=EB=A5=BC=20=EB=8D=94=20=EB=93=9C=EB=9F=AC=EB=82=B4?= =?UTF-8?q?=EB=8A=94=20=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD.=20useDynamicInputPair=20->=20useDynamicBillActionInp?= =?UTF-8?q?ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{useDynamicInputPairs.tsx => useDynamicBillActionInput.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/src/hooks/{useDynamicInputPairs.tsx => useDynamicBillActionInput.tsx} (100%) diff --git a/client/src/hooks/useDynamicInputPairs.tsx b/client/src/hooks/useDynamicBillActionInput.tsx similarity index 100% rename from client/src/hooks/useDynamicInputPairs.tsx rename to client/src/hooks/useDynamicBillActionInput.tsx From da8070fbf62c30d2c815e5de3a8ad8f9e8f008d6 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:23:16 +0900 Subject: [PATCH 30/43] =?UTF-8?q?chore:=20Modal=20=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=EB=82=B4=EC=9D=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20=EC=83=9D=EA=B8=B4=20?= =?UTF-8?q?import=20=EB=B3=80=EB=8F=99=20=EC=82=AC=ED=95=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Modal/SetActionModal/index.ts | 1 + .../SetActionModalContent.style.ts | 16 ------ .../SetActionModalContent.tsx | 50 ------------------- .../Modal/SetActionModalContent/index.ts | 1 - .../Modal/SetInitialMemberListModal/index.ts | 1 + .../Modal/SetInitialParticipants/index.ts | 1 - client/src/components/Modal/index.ts | 4 +- 7 files changed, 4 insertions(+), 70 deletions(-) create mode 100644 client/src/components/Modal/SetActionModal/index.ts delete mode 100644 client/src/components/Modal/SetActionModalContent/SetActionModalContent.style.ts delete mode 100644 client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx delete mode 100644 client/src/components/Modal/SetActionModalContent/index.ts create mode 100644 client/src/components/Modal/SetInitialMemberListModal/index.ts delete mode 100644 client/src/components/Modal/SetInitialParticipants/index.ts diff --git a/client/src/components/Modal/SetActionModal/index.ts b/client/src/components/Modal/SetActionModal/index.ts new file mode 100644 index 000000000..f689cb596 --- /dev/null +++ b/client/src/components/Modal/SetActionModal/index.ts @@ -0,0 +1 @@ +export {default as SetActionListModal} from './SetActionListModal'; diff --git a/client/src/components/Modal/SetActionModalContent/SetActionModalContent.style.ts b/client/src/components/Modal/SetActionModalContent/SetActionModalContent.style.ts deleted file mode 100644 index 560f672d5..000000000 --- a/client/src/components/Modal/SetActionModalContent/SetActionModalContent.style.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {css} from '@emotion/react'; - -export const setActionModalContentStyle = css({ - display: 'flex', - flexDirection: 'column', - width: '100%', - height: '100%', - padding: '0 1.5rem', - gap: '1.5rem', -}); - -export const setActionModalContentSwitchContainerStyle = css({ - display: 'flex', - width: '100%', - justifyContent: 'space-between', -}); diff --git a/client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx b/client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx deleted file mode 100644 index b3ccd64c9..000000000 --- a/client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import {useState} from 'react'; -import {BottomSheet, Switch} from 'haengdong-design'; - -import SetPurchase from './SetPurchase'; -import UpdateParticipants from './UpdateParticipants'; -import {setActionModalContentStyle, setActionModalContentSwitchContainerStyle} from './SetActionModalContent.style'; - -export type ActionType = '지출' | '인원'; - -interface SetActionModalContentProps { - openBottomSheet: boolean; - setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; - setOrder: React.Dispatch<React.SetStateAction<number>>; -} - -const SetActionModalContent = ({openBottomSheet, setOpenBottomSheet, setOrder}: SetActionModalContentProps) => { - const [action, setAction] = useState<ActionType>('지출'); - const [inOutAction, setInOutAction] = useState<InOutType>('탈주'); - - const handleActionTypeChange = (value: string) => { - setAction(value as ActionType); - }; - - const handleParticipantTypeChange = (value: string) => { - setInOutAction(value as InOutType); - }; - - return ( - <BottomSheet isOpened={openBottomSheet} onChangeClose={() => setOpenBottomSheet(false)}> - <div css={setActionModalContentStyle}> - <div css={setActionModalContentSwitchContainerStyle}> - <Switch value={action} onChange={handleActionTypeChange} values={['지출', '인원']} /> - {action === '인원' && ( - <Switch values={['늦참', '탈주']} value={inOutAction} onChange={handleParticipantTypeChange} /> - )} - </div> - - {action === '지출' && <SetPurchase setOpenBottomSheet={setOpenBottomSheet} setOrder={setOrder} />} - {action === '인원' && ( - <UpdateParticipants - inOutAction={inOutAction === '탈주' ? 'OUT' : 'IN'} - setOpenBottomSheet={setOpenBottomSheet} - /> - )} - </div> - </BottomSheet> - ); -}; - -export default SetActionModalContent; diff --git a/client/src/components/Modal/SetActionModalContent/index.ts b/client/src/components/Modal/SetActionModalContent/index.ts deleted file mode 100644 index c7514135c..000000000 --- a/client/src/components/Modal/SetActionModalContent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {default as SetActionModalContent} from './SetActionModalContent'; diff --git a/client/src/components/Modal/SetInitialMemberListModal/index.ts b/client/src/components/Modal/SetInitialMemberListModal/index.ts new file mode 100644 index 000000000..18098e4f6 --- /dev/null +++ b/client/src/components/Modal/SetInitialMemberListModal/index.ts @@ -0,0 +1 @@ +export {default as SetInitialMemberListModal} from './SetInitialMemberListModal'; diff --git a/client/src/components/Modal/SetInitialParticipants/index.ts b/client/src/components/Modal/SetInitialParticipants/index.ts deleted file mode 100644 index c41b6a206..000000000 --- a/client/src/components/Modal/SetInitialParticipants/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {default as SetInitialParticipants} from './SetInitialParticipants'; diff --git a/client/src/components/Modal/index.ts b/client/src/components/Modal/index.ts index e5d5264cd..36ccd8b2e 100644 --- a/client/src/components/Modal/index.ts +++ b/client/src/components/Modal/index.ts @@ -1,2 +1,2 @@ -export {default as SetInitialParticipants} from './SetInitialParticipants/SetInitialParticipants'; -export {default as SetActionModalContent} from './SetActionModalContent/SetActionModalContent'; +export {default as SetInitialMemberListModal} from './SetInitialMemberListModal/SetInitialMemberListModal'; +export {default as SetActionListModal} from './SetActionModal/SetActionListModal'; From 7fb5f31b46d51c7d2dca631a89f3812d90768356 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:28:22 +0900 Subject: [PATCH 31/43] =?UTF-8?q?fix:=20=ED=95=A8=EC=88=98=EA=B0=80=20stat?= =?UTF-8?q?e=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/utils/validate/validatePurchase.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/client/src/utils/validate/validatePurchase.ts b/client/src/utils/validate/validatePurchase.ts index 852f9c6d6..4a1229d19 100644 --- a/client/src/utils/validate/validatePurchase.ts +++ b/client/src/utils/validate/validatePurchase.ts @@ -1,18 +1,24 @@ -import {useState} from 'react'; - import ERROR_MESSAGE from '@constants/errorMessage'; import RULE from '@constants/rule'; import REGEXP from '@constants/regExp'; import {ValidateResult} from './type'; +/** + * TODO: (@soha) 이 함수를 만드신 분께..(웨디가) + * 함수 안에서는 state를 사용할 수 없어용. 그래서 상태를 제거하였습니다. + * 하지만 로직을 깊이 볼 시간이 없어 대충만 만져놓았습니다. + * + * 혹시 문제가 있다면 수정 부탁드립니다. + */ + const validatePurchase = (inputPair: Bill): ValidateResult => { - const [error, setError] = useState(''); const {title, price} = inputPair; + let errorMessage; const validatePrice = () => { if (price > RULE.maxPrice) { - setError(ERROR_MESSAGE.purchasePrice); + errorMessage = ERROR_MESSAGE.purchasePrice; return false; } return true; @@ -20,7 +26,7 @@ const validatePurchase = (inputPair: Bill): ValidateResult => { const validateTitle = () => { if (REGEXP.purchaseTitle.test(title)) { - setError(ERROR_MESSAGE.purchaseTitle); + errorMessage = ERROR_MESSAGE.purchaseTitle; return false; } return true; @@ -29,7 +35,8 @@ const validatePurchase = (inputPair: Bill): ValidateResult => { if (validatePrice() && validateTitle()) { return {isValid: true}; } - return {isValid: false, errorMessage: error}; + + return {isValid: true, errorMessage: ''}; }; export default validatePurchase; From 0c31cabfb58475424b4e623002c26e6b3fff82d6 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:28:48 +0900 Subject: [PATCH 32/43] =?UTF-8?q?feat:=20type.d.ts=EC=97=90=20=EC=9E=88?= =?UTF-8?q?=EB=8D=98=20=EB=82=B4=EC=9A=A9=EC=9D=84=20=EC=98=AE=EA=B2=A8?= =?UTF-8?q?=EC=98=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/types/serviceType.ts | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 client/src/types/serviceType.ts diff --git a/client/src/types/serviceType.ts b/client/src/types/serviceType.ts new file mode 100644 index 000000000..9b6fbe06f --- /dev/null +++ b/client/src/types/serviceType.ts @@ -0,0 +1,54 @@ +export type MemberType = 'IN' | 'OUT'; + +export type InOutType = '늦참' | '탈주'; + +export type MemberReport = { + name: string; + price: number; +}; + +export type Bill = { + title: string; + price: number; +}; + +type StepBase = { + members: string[]; +}; + +export type MemberStep = StepBase & { + type: MemberType; + stepName: null; + actions: MemberAction[]; +}; + +export type BillStep = StepBase & { + type: 'BILL'; + stepName: string; + actions: BillAction[]; +}; + +// TODO: (@weadie) 준 데이터 형식에서 steps를 빼내 flat하게 사용중. 일관성있게 하는게 좋긴 하나 사용시 번거로움이 있을 거라고 판단. +export type StepList = { + steps: (MemberStep | BillStep)[]; +}; + +export type Action = { + actionId: number; + name: string; + price: number | null; + sequence: number; +}; + +export type BillAction = Omit<Action, 'price'> & { + price: number; +}; + +export type MemberAction = Omit<Action, 'price'> & { + price: null; +}; + +export type Member = { + name: string; + status: MemberType; +}; From e00b2b690e5fe03c7ec64a9121485172da79c7c5 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:29:11 +0900 Subject: [PATCH 33/43] =?UTF-8?q?chore:=20import=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddBillActionListModalContent.tsx | 2 +- .../AddMemberActionListModalContent.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx index ab61d9402..5d2e3f1d5 100644 --- a/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx +++ b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx @@ -1,7 +1,7 @@ import {FixedButton, LabelGroupInput} from 'haengdong-design'; import style from './AddBillActionListModalContent.style'; import {useStepList} from '@hooks/useStepList/useStepList'; -import useDynamicBillActionInput from '@hooks/useDynamicInputPairs'; +import useDynamicBillActionInput from '@hooks/useDynamicBillActionInput'; import validatePurchase from '@utils/validate/validatePurchase'; interface SetPurchaseProps { diff --git a/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx index a1129e0bf..0d96fb98f 100644 --- a/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx +++ b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx @@ -5,6 +5,7 @@ import validateMemberName from '@utils/validate/validateMemberName'; import useDynamicInput from '@hooks/useDynamicInput'; import style from './AddMemberActionListModalContent.style'; +import {MemberType} from 'types/serviceType'; interface UpdateParticipantsProps { inOutAction: MemberType; From 9be9ed0ad09606ec3f10edf631b936b53c193a45 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sat, 3 Aug 2024 04:49:16 +0900 Subject: [PATCH 34/43] =?UTF-8?q?chore:=20package-lock=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 652 +++++++++++++++++++-------------------- 1 file changed, 315 insertions(+), 337 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index fc13e418d..db862c7d5 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -78,28 +78,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", - "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -120,11 +120,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@babel/generator": { - "version": "7.24.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", - "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dependencies": { - "@babel/types": "^7.24.9", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -157,11 +157,11 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dependencies": { - "@babel/compat-data": "^7.24.8", + "@babel/compat-data": "^7.25.2", "@babel/helper-validator-option": "^7.24.8", "browserslist": "^4.23.1", "lru-cache": "^5.1.1", @@ -172,18 +172,16 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.8.tgz", - "integrity": "sha512-4f6Oqnmyp2PP3olgUMmOwC3akxSm5aBYraQ6YDdKy7NcAMkDECHWG0DEnV6M2UAkERgIBhYt8S27rURPg7SxWA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", + "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", "@babel/helper-member-expression-to-functions": "^7.24.8", "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/traverse": "^7.25.0", "semver": "^6.3.1" }, "engines": { @@ -194,9 +192,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", @@ -224,40 +222,6 @@ "@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==", - "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==", - "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==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", @@ -283,15 +247,14 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", - "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -320,13 +283,13 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -336,13 +299,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -375,17 +338,6 @@ "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==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", @@ -411,26 +363,25 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", - "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.8" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -451,9 +402,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", - "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -462,12 +416,26 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -477,11 +445,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.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.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "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.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -507,12 +475,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -792,14 +760,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -839,11 +807,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -884,17 +852,15 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.8.tgz", - "integrity": "sha512-VXy91c47uujj758ud9wx+OMgheXm4qJfyhj1P18YvlrQkNOSrwsteHk+EFS3OMGfhMhpZa0A+81eE7G4QC+3CA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", + "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", "globals": "^11.1.0" }, "engines": { @@ -970,6 +936,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -1031,13 +1012,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1062,11 +1043,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1136,14 +1117,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1350,11 +1331,11 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.7.tgz", - "integrity": "sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.1.tgz", + "integrity": "sha512-SLV/giH/V4SmloZ6Dt40HjTGTAIkxn33TVIHxNGNvo8ezMhrxBkzisj4op1KZYPIOHFLqhv60OHvX+YRu4xbmQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1378,15 +1359,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz", + "integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -1525,13 +1506,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.8.tgz", - "integrity": "sha512-CgFgtN61BbdOGCP4fLaAMOPkzWUh6yQZNMr5YSt8uz2cZSSiQONCQFWqsE4NeVfOIhqDOlS9CR3WD91FzMeB2Q==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz", + "integrity": "sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.8", + "@babel/helper-create-class-features-plugin": "^7.25.0", "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-typescript": "^7.24.7" }, "engines": { @@ -1601,18 +1583,19 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.8.tgz", - "integrity": "sha512-vObvMZB6hNWuDxhSaEPTKCwcqkAIuDtE+bQGn4XMXne1DSLzFVY8Vmj1bm+mUQXYNN8NmaQEO+r8MMbzPr1jBQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-compilation-targets": "^7.24.8", + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1633,29 +1616,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.8", + "@babel/plugin-transform-classes": "^7.25.0", "@babel/plugin-transform-computed-properties": "^7.24.7", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -1750,9 +1734,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1761,31 +1745,28 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", - "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.8", - "@babel/types": "^7.24.8", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1802,9 +1783,9 @@ } }, "node_modules/@babel/types": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", - "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dependencies": { "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", @@ -1842,9 +1823,9 @@ } }, "node_modules/@emotion/cache": { - "version": "11.13.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.0.tgz", - "integrity": "sha512-hPV345J/tH0Cwk2wnU/3PBzORQ9HeX+kQSbwI+jslzpRCHE6fSGTohswksA/Ensr8znPzwfzKZCmAM9Lmlhp7g==", + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", + "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -2053,9 +2034,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", - "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", + "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2231,9 +2212,9 @@ } }, "node_modules/@jsonjoy.com/util": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.2.0.tgz", - "integrity": "sha512-4B8B+3vFsY4eo33DMKyJPlQ3sBMpPFUZK2dr3O3rXrOGKKbYG44J0XSFkDo1VOQiri5HFEhIeVvItjR2xcazmg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.3.0.tgz", + "integrity": "sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==", "dev": true, "engines": { "node": ">=10.0" @@ -2310,9 +2291,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz", - "integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.0.tgz", + "integrity": "sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA==", "engines": { "node": ">=14.0.0" } @@ -2914,11 +2895,11 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.13.0" } }, "node_modules/@types/node-forge": { @@ -3018,25 +2999,25 @@ } }, "node_modules/@types/ws": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", - "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz", - "integrity": "sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.17.0", - "@typescript-eslint/type-utils": "7.17.0", - "@typescript-eslint/utils": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3060,15 +3041,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.17.0", - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/typescript-estree": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4" }, "engines": { @@ -3088,13 +3069,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz", - "integrity": "sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.0" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3105,13 +3086,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz", - "integrity": "sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.17.0", - "@typescript-eslint/utils": "7.17.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -3132,9 +3113,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz", - "integrity": "sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3145,13 +3126,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz", - "integrity": "sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/visitor-keys": "7.17.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3185,15 +3166,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz", - "integrity": "sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.17.0", - "@typescript-eslint/types": "7.17.0", - "@typescript-eslint/typescript-estree": "7.17.0" + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3207,12 +3188,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz", - "integrity": "sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -3757,9 +3738,9 @@ } }, "node_modules/axe-core": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", - "integrity": "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "dev": true, "engines": { "node": ">=4" @@ -3957,9 +3938,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "funding": [ { "type": "opencollective", @@ -3975,9 +3956,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -4065,9 +4046,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001643", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", - "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", + "version": "1.0.30001646", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001646.tgz", + "integrity": "sha512-dRg00gudiBDDTmUhClSdv3hqRfpbOnU28IpI1T6PBTLWa+kOj0681C8uML3PifYfREuBrVjDGhL3adYpBT6spw==", "funding": [ { "type": "opencollective", @@ -4566,9 +4547,9 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dependencies": { "ms": "2.1.2" }, @@ -4910,9 +4891,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz", - "integrity": "sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz", + "integrity": "sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -5190,16 +5171,16 @@ } }, "node_modules/eslint": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz", - "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", + "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.17.0", + "@eslint/config-array": "^0.17.1", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.7.0", + "@eslint/js": "9.8.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -6415,9 +6396,9 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, "node_modules/globals": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.8.0.tgz", - "integrity": "sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", + "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", "dev": true, "engines": { "node": ">=18" @@ -6486,9 +6467,9 @@ "dev": true }, "node_modules/haengdong-design": { - "version": "0.1.51", - "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.51.tgz", - "integrity": "sha512-7/9FGKS3NNjyXkH8h2U7IlHkBcFROus8iXJejQR8qsvAtu/OZrM/0ebXEZ24LP7Kps3AaaD2Cv9YyfnWI/KrzA==", + "version": "0.1.52", + "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.52.tgz", + "integrity": "sha512-BLbPvCm/CnuixH9kLbqPC2l7GLJQfxhjf/hRCTpfadUybSiMkYA1HOSXCBT4QY4CP5PsfzZC0L7llRCoDSNAyQ==", "dependencies": { "@emotion/react": "^11.11.4", "@storybook/addon-webpack5-compiler-swc": "^1.0.5", @@ -6669,9 +6650,9 @@ ] }, "node_modules/html-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-5.0.0.tgz", - "integrity": "sha512-puaGKdjdVVIFRtgIC2n5dt5bt0N5j6heXlAQZ4Do1MLjHmOT1gCE1Ogg7XZNeJlnOVHHsrZKGs5dfh+XwZ3XPw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-5.1.0.tgz", + "integrity": "sha512-Jb3xwDbsm0W3qlXrCZwcYqYGnYz55hb6aoKQTlzyZPXsPpi6tHXzAfqalecglMQgNvtEfxrCQPaKT90Irt5XDA==", "dev": true, "dependencies": { "html-minifier-terser": "^7.2.0", @@ -8615,9 +8596,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.40", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", + "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", "dev": true, "funding": [ { @@ -8918,11 +8899,11 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-router": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz", - "integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.0.tgz", + "integrity": "sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg==", "dependencies": { - "@remix-run/router": "1.18.0" + "@remix-run/router": "1.19.0" }, "engines": { "node": ">=14.0.0" @@ -8932,12 +8913,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz", - "integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.0.tgz", + "integrity": "sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ==", "dependencies": { - "@remix-run/router": "1.18.0", - "react-router": "6.25.1" + "@remix-run/router": "1.19.0", + "react-router": "6.26.0" }, "engines": { "node": ">=14.0.0" @@ -9199,9 +9180,9 @@ } }, "node_modules/rimraf": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.9.tgz", - "integrity": "sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, "dependencies": { "glob": "^10.3.7" @@ -9209,9 +9190,6 @@ "bin": { "rimraf": "dist/esm/bin.mjs" }, - "engines": { - "node": "14 >=14.20 || 16 >=16.20 || >=18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -10509,14 +10487,14 @@ } }, "node_modules/typescript-eslint": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.17.0.tgz", - "integrity": "sha512-spQxsQvPguduCUfyUvLItvKqK3l8KJ/kqs5Pb/URtzQ5AC53Z6us32St37rpmlt2uESG23lOFpV4UErrmy4dZQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.18.0.tgz", + "integrity": "sha512-PonBkP603E3tt05lDkbOMyaxJjvKqQrXsnow72sVeOFINDE/qNmnnd+f9b4N+U7W6MXnnYyrhtmF2t08QWwUbA==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "7.17.0", - "@typescript-eslint/parser": "7.17.0", - "@typescript-eslint/utils": "7.17.0" + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", + "@typescript-eslint/utils": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -10550,9 +10528,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -10860,13 +10838,13 @@ "dev": true }, "node_modules/webpack-dev-middleware/node_modules/memfs": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.9.3.tgz", - "integrity": "sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", + "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", "dev": true, "dependencies": { "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.1.2", + "@jsonjoy.com/util": "^1.3.0", "tree-dump": "^1.0.1", "tslib": "^2.0.0" }, @@ -11106,13 +11084,13 @@ } }, "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", "dev": true, "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.0.5", "is-finalizationregistry": "^1.0.2", @@ -11121,8 +11099,8 @@ "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" From 8dca946eddb868e1214c499f827b319590f69899 Mon Sep 17 00:00:00 2001 From: pakxe <pigkill40@naver.com> Date: Sun, 4 Aug 2024 15:17:41 +0900 Subject: [PATCH 35/43] =?UTF-8?q?design:=20Input=EC=97=90=20css=20props=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/src/components/Input/Input.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index e1fddb8fc..488ee7b71 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -2,11 +2,14 @@ import React, {forwardRef, useImperativeHandle, useRef} from 'react'; import {useInput} from './useInput'; import {InputProps} from './Input.type'; +import {inputBoxStyle, inputStyle} from './Input.style'; +import {useTheme} from '@/theme/HDesignProvider'; export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input( {value: propsValue, onChange, onFocus, onBlur, inputType, isError, placeholder, ...htmlProps}: InputProps, ref, ) { + const {theme} = useTheme(); useImperativeHandle(ref, () => inputRef.current!); const inputRef = useRef<HTMLInputElement>(null); const {value, handleChange, hasFocus, handleClickDelete, handleBlur, handleFocus, handleKeyDown} = useInput({ @@ -18,8 +21,9 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro }); return ( - <div> + <div css={inputBoxStyle(theme, inputType, hasFocus, isError)}> <input + css={inputStyle(theme)} ref={inputRef} value={value} onChange={handleChange} From fcc18c33aa4dbba3103105529eebad602872e892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:15:57 +0900 Subject: [PATCH 36/43] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Soyeon Choe <soi-ha@users.noreply.github.com> Co-authored-by: Pakxe <pakxe@users.noreply.github.com> Co-authored-by: jinhokim98 <jinhokim98@users.noreply.github.com> --- HDesign/src/components/Input/Input.style.ts | 13 ++++----- HDesign/src/components/Input/Input.tsx | 3 +- HDesign/src/components/Input/useInput.ts | 15 ++++++---- .../LabelGroupInput/LabelGroupInput.tsx | 29 ++++++++----------- .../components/LabelInput/LabelInput.style.ts | 21 -------------- .../src/components/LabelInput/LabelInput.tsx | 28 +++++++++--------- HDesign/src/index.tsx | 5 ++-- 7 files changed, 45 insertions(+), 69 deletions(-) diff --git a/HDesign/src/components/Input/Input.style.ts b/HDesign/src/components/Input/Input.style.ts index 94fb70de7..cb7471afc 100644 --- a/HDesign/src/components/Input/Input.style.ts +++ b/HDesign/src/components/Input/Input.style.ts @@ -4,7 +4,7 @@ import {Theme} from '@theme/theme.type'; import {InputType} from './Input.type'; -const inputBoxBackgroundColorByInputType = (theme: Theme, inputType: InputType = 'input') => { +const getBackgroundColorStyle = (theme: Theme, inputType: InputType = 'input') => { switch (inputType) { case 'input': return theme.colors.lightGrayContainer; @@ -17,6 +17,9 @@ const inputBoxBackgroundColorByInputType = (theme: Theme, inputType: InputType = } }; +const getBorderStyle = (isFocus: boolean, theme: Theme, isError?: boolean) => + isError ? `0 0 0 1px ${theme.colors.error} inset` : isFocus ? `0 0 0 1px ${theme.colors.primary} inset` : 'none'; + export const inputBoxStyle = ( theme: Theme, inputType: InputType = 'input', @@ -29,14 +32,10 @@ export const inputBoxStyle = ( gap: '1rem', padding: '0.75rem 1rem', borderRadius: '1rem', - backgroundColor: inputBoxBackgroundColorByInputType(theme, inputType), + backgroundColor: getBackgroundColorStyle(theme, inputType), boxSizing: 'border-box', - boxShadow: isError - ? `0 0 0 1px ${theme.colors.error} inset` - : isFocus - ? `0 0 0 1px ${theme.colors.primary} inset` - : 'none', + boxShadow: getBorderStyle(isFocus, theme, isError), }); export const inputStyle = (theme: Theme) => diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index 488ee7b71..870965be0 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -4,6 +4,7 @@ import {useInput} from './useInput'; import {InputProps} from './Input.type'; import {inputBoxStyle, inputStyle} from './Input.style'; import {useTheme} from '@/theme/HDesignProvider'; +import IconButton from '../IconButton/IconButton'; export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input( {value: propsValue, onChange, onFocus, onBlur, inputType, isError, placeholder, ...htmlProps}: InputProps, @@ -33,7 +34,7 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro onKeyDown={handleKeyDown} {...htmlProps} /> - {value && hasFocus && <button onMouseDown={handleClickDelete}>x버튼</button>} + {value && hasFocus && <IconButton iconType="inputDelete" onClick={handleClickDelete} />} </div> ); }); diff --git a/HDesign/src/components/Input/useInput.ts b/HDesign/src/components/Input/useInput.ts index 18ea6b0ac..46a195032 100644 --- a/HDesign/src/components/Input/useInput.ts +++ b/HDesign/src/components/Input/useInput.ts @@ -6,19 +6,24 @@ interface UseInputProps<T> { onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void; onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void; inputRef: RefObject<HTMLInputElement>; + autoFocus?: boolean; } -export const useInput = <T>({propsValue, onChange, onBlur, onFocus, inputRef}: UseInputProps<T>) => { - const [value, setValue] = useState(propsValue as string); +export const useInput = <T>({propsValue, onChange, onBlur, onFocus, inputRef, autoFocus}: UseInputProps<T>) => { + const [value, setValue] = useState<T>(propsValue); const [hasFocus, setHasFocus] = useState(inputRef.current === document.activeElement); useEffect(() => { - setValue(propsValue as string); + setHasFocus(inputRef.current === document.activeElement); + }, []); + + useEffect(() => { + setValue(propsValue); }, [value]); const handleClickDelete = (event: React.MouseEvent) => { event.preventDefault(); - setValue(''); + setValue('' as T); if (onChange) { onChange({target: {value: ''}} as React.ChangeEvent<HTMLInputElement>); } @@ -28,7 +33,7 @@ export const useInput = <T>({propsValue, onChange, onBlur, onFocus, inputRef}: U }; const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValue(e.target.value); + setValue(e.target.value as T); if (onChange) { onChange(e); } diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx index 46032530a..d42817f10 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx @@ -4,36 +4,31 @@ import Text from '@components/Text/Text'; import {useTheme} from '@/theme/HDesignProvider'; import {LabelGroupInputProps} from './LabelGroupInput.type'; -import { - errorTextStyle, - inputGroupStyle, - labelGroupStyle, - labelInputStyle, - labelTextStyle, -} from './LabelGroupInput.style'; +import {errorTextStyle, labelTextStyle} from './LabelGroupInput.style'; import Element from './Element'; import {GroupInputProvider, useGroupInputContext} from './GroupInputContext'; +import Flex from '../Flex/Flex'; const LabelGroupInput: React.FC<LabelGroupInputProps> = ({labelText, errorText, children}: LabelGroupInputProps) => { const {theme} = useTheme(); const {hasAnyFocus, values, hasAnyErrors} = useGroupInputContext(); return ( - <div css={labelInputStyle}> - <div css={labelGroupStyle}> - <label> - <Text size="caption" css={labelTextStyle(theme)}> - {(hasAnyFocus || !Object.values(values).every(value => value === '')) && labelText} - </Text> - </label> + <Flex flexDirection="column" gap="0.375rem"> + <Flex justifyContent="spaceBetween" paddingInline="0.5rem" margin="0 0 0.375rem 0"> + <Text size="caption" css={labelTextStyle(theme)}> + {(hasAnyFocus || !Object.values(values).every(value => value === '')) && labelText} + </Text> {errorText && ( <Text size="caption" css={errorTextStyle(theme)}> {!Object.values(hasAnyErrors).every(error => !error) && errorText} </Text> )} - </div> - <div css={inputGroupStyle}>{children}</div> - </div> + </Flex> + <Flex flexDirection="column" gap="0.5rem"> + {children} + </Flex> + </Flex> ); }; diff --git a/HDesign/src/components/LabelInput/LabelInput.style.ts b/HDesign/src/components/LabelInput/LabelInput.style.ts index 7fcc9eb69..5d5eaed8f 100644 --- a/HDesign/src/components/LabelInput/LabelInput.style.ts +++ b/HDesign/src/components/LabelInput/LabelInput.style.ts @@ -2,27 +2,6 @@ import {css} from '@emotion/react'; import {Theme} from '@/theme/theme.type'; -export const labelInputStyle = css({ - display: 'flex', - flexDirection: 'column', - gap: '0.375rem', -}); - -export const labelGroupStyle = css({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - width: '100%', - - paddingInline: '0.5rem', -}); - -export const inputGroupStyle = css({ - display: 'flex', - flexDirection: 'column', - gap: '0.5rem', -}); - export const labelTextStyle = (theme: Theme) => css({ height: '1.125rem', diff --git a/HDesign/src/components/LabelInput/LabelInput.tsx b/HDesign/src/components/LabelInput/LabelInput.tsx index 38221475f..85baffa78 100644 --- a/HDesign/src/components/LabelInput/LabelInput.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.tsx @@ -1,15 +1,16 @@ /** @jsxImportSource @emotion/react */ -import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import {forwardRef, useImperativeHandle, useRef} from 'react'; import Text from '@components/Text/Text'; import {useTheme} from '@/theme/HDesignProvider'; import Input from '../Input/Input'; -import {LabelInputProps} from './LabelInput.type'; -import {errorTextStyle, inputGroupStyle, labelGroupStyle, labelInputStyle, labelTextStyle} from './LabelInput.style'; +import {errorTextStyle, labelTextStyle} from './LabelInput.style'; import {useLabelInput} from './useLabelInput'; +import {LabelInputProps} from './LabelInput.type'; +import Flex from '../Flex/Flex'; const LabelInput: React.FC<LabelInputProps> = forwardRef<HTMLInputElement, LabelInputProps>(function LabelInput( {labelText, errorText, isError, ...htmlProps}: LabelInputProps, @@ -21,22 +22,19 @@ const LabelInput: React.FC<LabelInputProps> = forwardRef<HTMLInputElement, Label const {hasFocus} = useLabelInput({inputRef}); return ( - <div css={labelInputStyle}> - <div css={labelGroupStyle}> - <label> - <Text size="caption" css={labelTextStyle(theme)}> - {(hasFocus || htmlProps.value) && labelText} - </Text> - </label> - + <Flex flexDirection="column" gap="0.375rem"> + <Flex justifyContent="spaceBetween" paddingInline="0.5rem" margin="0 0 0.375rem 0"> + <Text size="caption" css={labelTextStyle(theme)}> + {(hasFocus || htmlProps.value) && labelText} + </Text> <Text size="caption" css={errorTextStyle(theme)}> {isError && errorText} </Text> - </div> - <div css={inputGroupStyle}> + </Flex> + <Flex flexDirection="column" gap="0.5rem"> <Input ref={inputRef} isError={isError} placeholder={labelText} {...htmlProps} /> - </div> - </div> + </Flex> + </Flex> ); }); diff --git a/HDesign/src/index.tsx b/HDesign/src/index.tsx index f5bef35df..1baddca7a 100644 --- a/HDesign/src/index.tsx +++ b/HDesign/src/index.tsx @@ -9,8 +9,7 @@ import IconButton from '@components/IconButton/IconButton'; import InOutItem from '@components/InOutItem/InOutItem'; import Input from '@components/Input/Input'; import LabelInput from '@components/LabelInput/LabelInput'; -// import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; -import LabelGroupInputContainer from '@components/LabelGroupInput/LabelGroupInput'; +import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; import Search from '@components/Search/Search'; import StepItem from '@components/StepItem/StepItem'; import Switch from '@components/Switch/Switch'; @@ -42,7 +41,7 @@ export { InOutItem, Input, LabelInput, - LabelGroupInputContainer as LabelGroupInput, + LabelGroupInput, Search, StepItem, Switch, From d8468cb349404f4c459684216ade11aa0f01dbf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:16:20 +0900 Subject: [PATCH 37/43] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Soyeon Choe <soi-ha@users.noreply.github.com> Co-authored-by: Pakxe <pakxe@users.noreply.github.com> Co-authored-by: jinhokim98 <jinhokim98@users.noreply.github.com> --- .../AddMemberActionListModalContent.tsx | 9 +++--- .../src/hooks/useDynamicBillActionInput.tsx | 7 ++-- client/src/hooks/useDynamicInput.tsx | 7 ++-- client/src/hooks/useInput/useInput.ts | 32 ------------------- .../CreateEventPage/SetEventNamePage.tsx | 15 +++------ client/src/utils/generateUniqueId.ts | 7 ---- client/src/utils/validate/validatePurchase.ts | 8 ----- 7 files changed, 14 insertions(+), 71 deletions(-) delete mode 100644 client/src/hooks/useInput/useInput.ts delete mode 100644 client/src/utils/generateUniqueId.ts diff --git a/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx index 0d96fb98f..32871d0bf 100644 --- a/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx +++ b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx @@ -7,12 +7,12 @@ import useDynamicInput from '@hooks/useDynamicInput'; import style from './AddMemberActionListModalContent.style'; import {MemberType} from 'types/serviceType'; -interface UpdateParticipantsProps { +interface UpdateMembersProps { inOutAction: MemberType; setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; } -const AddMemberActionListModalContent = ({inOutAction, setOpenBottomSheet}: UpdateParticipantsProps) => { +const AddMemberActionListModalContent = ({inOutAction, setOpenBottomSheet}: UpdateMembersProps) => { const { inputList, inputRefList, @@ -23,9 +23,10 @@ const AddMemberActionListModalContent = ({inOutAction, setOpenBottomSheet}: Upda canSubmit, focusNextInputOnEnter, } = useDynamicInput(validateMemberName); + const {updateMemberList} = useStepList(); - const handleUpdateParticipantsSubmit = () => { + const handleUpdateMemberListSubmit = () => { updateMemberList({memberNameList: getFilledInputList().map(({value}) => value), type: inOutAction}); setOpenBottomSheet(false); }; @@ -54,7 +55,7 @@ const AddMemberActionListModalContent = ({inOutAction, setOpenBottomSheet}: Upda disabled={!canSubmit} variants={'primary'} children={`${inputList.length - 1}명 ${inOutAction === 'OUT' ? '탈주' : '늦참'}`} - onClick={handleUpdateParticipantsSubmit} + onClick={handleUpdateMemberListSubmit} /> </div> ); diff --git a/client/src/hooks/useDynamicBillActionInput.tsx b/client/src/hooks/useDynamicBillActionInput.tsx index f85966e43..f81e0b114 100644 --- a/client/src/hooks/useDynamicBillActionInput.tsx +++ b/client/src/hooks/useDynamicBillActionInput.tsx @@ -35,6 +35,7 @@ const useDynamicBillActionInput = (validateFunc: (inputPair: Bill) => ValidateRe const {title, price} = targetInputPair; + // TODO: (@weadie) 가독성이 안좋다는 리뷰. 함수로 분리 if (isLastInputPairFilled({index, field, value})) { setErrorMessage(''); setInputPairList(prevInputPairList => { @@ -94,11 +95,7 @@ const useDynamicBillActionInput = (validateFunc: (inputPair: Bill) => ValidateRe }; const handleCanSubmit = () => { - if (inputPairList.length > 0 && getFilledInputPairList().length > 0) { - setCanSubmit(true); - } else { - setCanSubmit(false); - } + setCanSubmit(inputPairList.length > 0 && getFilledInputPairList().length > 0); }; const focusNextInputOnEnter = (e: React.KeyboardEvent<HTMLInputElement>, index: number, field: BillInputType) => { diff --git a/client/src/hooks/useDynamicInput.tsx b/client/src/hooks/useDynamicInput.tsx index c9310f86c..8588c2c40 100644 --- a/client/src/hooks/useDynamicInput.tsx +++ b/client/src/hooks/useDynamicInput.tsx @@ -27,6 +27,7 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { const {value} = event.target; const {isValid: isValidInput, errorMessage: validationResultMessage} = validateFunc(value); + // TODO: (@weadie) 가독성이 안좋다는 리뷰. 함수 분리필요 if (isLastInputFilled(index, value)) { // 마지막 인풋이 한 자라도 채워진다면 새로운 인풋을 생성해 간편한 다음 입력을 유도합니다. @@ -71,11 +72,7 @@ const useDynamicInput = (validateFunc: (name: string) => ValidateResult) => { // 현재까지 입력된 값들로 submit을 할 수 있는지 여부를 핸들합니다. const handleCanSubmit = () => { - if (inputList.length > 0 && getFilledInputList().length > 0) { - setCanSubmit(true); - } else { - setCanSubmit(false); - } + setCanSubmit(inputList.length > 0 && getFilledInputList().length > 0); }; const deleteEmptyInputElementOnBlur = () => { diff --git a/client/src/hooks/useInput/useInput.ts b/client/src/hooks/useInput/useInput.ts deleted file mode 100644 index ae248217f..000000000 --- a/client/src/hooks/useInput/useInput.ts +++ /dev/null @@ -1,32 +0,0 @@ -// interface UseInputProps<T> { - -// } - -// const useInput = <T>({}) => { -// const [value, setValue] = useState(); -// const handleChange = (index: number, value: string) => { -// const newInputs = [...inputs]; -// newInputs[index] = value; -// setInputs(newInputs); -// }; - -// const handleBlur = (index: number) => { -// if (inputs[index].trim() === '') { -// setInputs(prev => { -// const newInputs = [...prev]; -// newInputs[index] = ''; -// return newInputs; -// }); -// } else if (inputs[index].trim() !== '' && index === inputs.length - 1) { -// setInputs(prev => { -// const newInputs = [...prev, '']; -// newInputs[index] = inputs[index].trim(); -// return newInputs; -// }); -// } -// }; - -// const getNonEmptyInputs = () => { -// return inputs.filter(input => input.trim() !== ''); -// }; -// } diff --git a/client/src/pages/CreateEventPage/SetEventNamePage.tsx b/client/src/pages/CreateEventPage/SetEventNamePage.tsx index 932a11945..4da51dd98 100644 --- a/client/src/pages/CreateEventPage/SetEventNamePage.tsx +++ b/client/src/pages/CreateEventPage/SetEventNamePage.tsx @@ -31,14 +31,9 @@ const SetEventNamePage = () => { const newValue = event.target.value; const validation = validateEventName(newValue); - if (newValue.length === 0) { - setCanSubmit(false); - } else { - setCanSubmit(true); - } + setCanSubmit(newValue.length !== 0); if (validation.isValid) { - console.log('!!'); setEventName(newValue); setErrorMessage(''); } else { @@ -53,16 +48,16 @@ const SetEventNamePage = () => { </TopNav> <Title title="행사 이름 입력" description="시작할 행사 이름을 입력해 주세요." /> <form onSubmit={submitEventName} style={{padding: '0 1rem'}}> - {/* <LabelInput labelText="행사 이름" errorText={errorMessage}> */} - <Input + <LabelInput + labelText="행사 이름" + errorText={errorMessage} value={eventName} type="text" placeholder="행사 이름" onChange={e => handleChange(e)} isError={!!errorMessage} autoFocus - /> - {/* </LabelInput> */} + ></LabelInput> <FixedButton disabled={!canSubmit}>행동 개시!</FixedButton> </form> </MainLayout> diff --git a/client/src/utils/generateUniqueId.ts b/client/src/utils/generateUniqueId.ts deleted file mode 100644 index ec1564ce8..000000000 --- a/client/src/utils/generateUniqueId.ts +++ /dev/null @@ -1,7 +0,0 @@ -const generateUniqueId = (): string => { - const timestamp = Date.now().toString(); - const randomNum = Math.floor(Math.random() * 1000000).toString(); - return timestamp + randomNum; -}; - -export default generateUniqueId; diff --git a/client/src/utils/validate/validatePurchase.ts b/client/src/utils/validate/validatePurchase.ts index 4a1229d19..026cfb496 100644 --- a/client/src/utils/validate/validatePurchase.ts +++ b/client/src/utils/validate/validatePurchase.ts @@ -4,14 +4,6 @@ import REGEXP from '@constants/regExp'; import {ValidateResult} from './type'; -/** - * TODO: (@soha) 이 함수를 만드신 분께..(웨디가) - * 함수 안에서는 state를 사용할 수 없어용. 그래서 상태를 제거하였습니다. - * 하지만 로직을 깊이 볼 시간이 없어 대충만 만져놓았습니다. - * - * 혹시 문제가 있다면 수정 부탁드립니다. - */ - const validatePurchase = (inputPair: Bill): ValidateResult => { const {title, price} = inputPair; let errorMessage; From 5a10d8120b669fbe8803735915e0462c98c6a1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:17:50 +0900 Subject: [PATCH 38/43] =?UTF-8?q?style:=20lint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Soyeon Choe <soi-ha@users.noreply.github.com> Co-authored-by: Pakxe <pakxe@users.noreply.github.com> Co-authored-by: jinhokim98 <jinhokim98@users.noreply.github.com> --- HDesign/src/components/Input/Input.stories.tsx | 5 ++--- HDesign/src/components/Input/Input.tsx | 7 +++++-- HDesign/src/components/LabelGroupInput/Element.tsx | 2 +- .../components/LabelGroupInput/LabelGroupInput.stories.tsx | 3 ++- HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx | 3 ++- HDesign/src/components/LabelInput/LabelInput.stories.tsx | 3 ++- HDesign/src/components/LabelInput/LabelInput.tsx | 2 +- .../AddBillActionListModalContent.tsx | 7 +++++-- .../AddMemberActionListModalContent.tsx | 3 ++- .../SetInitialMemberListModal.tsx | 2 ++ client/src/pages/EventPage/AdminPage/AdminPage.tsx | 3 ++- client/src/router.tsx | 7 ++++--- 12 files changed, 30 insertions(+), 17 deletions(-) diff --git a/HDesign/src/components/Input/Input.stories.tsx b/HDesign/src/components/Input/Input.stories.tsx index f13deb826..97365fa9d 100644 --- a/HDesign/src/components/Input/Input.stories.tsx +++ b/HDesign/src/components/Input/Input.stories.tsx @@ -1,9 +1,8 @@ -import React, {useEffect} from 'react'; - import type {Meta, StoryObj} from '@storybook/react'; +import React, {useEffect, useState} from 'react'; + import Input from '@components/Input/Input'; -import {useState} from 'react'; const meta = { title: 'Components/Input', diff --git a/HDesign/src/components/Input/Input.tsx b/HDesign/src/components/Input/Input.tsx index 870965be0..cebdfb5a7 100644 --- a/HDesign/src/components/Input/Input.tsx +++ b/HDesign/src/components/Input/Input.tsx @@ -1,10 +1,13 @@ /** @jsxImportSource @emotion/react */ import React, {forwardRef, useImperativeHandle, useRef} from 'react'; + +import {useTheme} from '@/theme/HDesignProvider'; + +import IconButton from '../IconButton/IconButton'; + import {useInput} from './useInput'; import {InputProps} from './Input.type'; import {inputBoxStyle, inputStyle} from './Input.style'; -import {useTheme} from '@/theme/HDesignProvider'; -import IconButton from '../IconButton/IconButton'; export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input( {value: propsValue, onChange, onFocus, onBlur, inputType, isError, placeholder, ...htmlProps}: InputProps, diff --git a/HDesign/src/components/LabelGroupInput/Element.tsx b/HDesign/src/components/LabelGroupInput/Element.tsx index a7c9bc5f9..1043aa313 100644 --- a/HDesign/src/components/LabelGroupInput/Element.tsx +++ b/HDesign/src/components/LabelGroupInput/Element.tsx @@ -17,7 +17,7 @@ const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProp const {setHasAnyFocus, values, setValues, hasAnyErrors, setHasAnyErrors} = useGroupInputContext(); useEffect(() => { - setValues({...values, [elementKey]: `${propsValue}` ?? ''}); + setValues({...values, [elementKey]: `${propsValue}`}); }, [propsValue]); const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx index b4bf68182..b3f48b452 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.stories.tsx @@ -1,9 +1,10 @@ /** @jsxImportSource @emotion/react */ import type {Meta, StoryObj} from '@storybook/react'; -import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; import {useState} from 'react'; +import LabelGroupInput from '@components/LabelGroupInput/LabelGroupInput'; + const meta = { title: 'Components/LabelGroupInput', component: LabelGroupInput, diff --git a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx index d42817f10..e115bc71a 100644 --- a/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx +++ b/HDesign/src/components/LabelGroupInput/LabelGroupInput.tsx @@ -3,11 +3,12 @@ import Text from '@components/Text/Text'; import {useTheme} from '@/theme/HDesignProvider'; +import Flex from '../Flex/Flex'; + import {LabelGroupInputProps} from './LabelGroupInput.type'; import {errorTextStyle, labelTextStyle} from './LabelGroupInput.style'; import Element from './Element'; import {GroupInputProvider, useGroupInputContext} from './GroupInputContext'; -import Flex from '../Flex/Flex'; const LabelGroupInput: React.FC<LabelGroupInputProps> = ({labelText, errorText, children}: LabelGroupInputProps) => { const {theme} = useTheme(); diff --git a/HDesign/src/components/LabelInput/LabelInput.stories.tsx b/HDesign/src/components/LabelInput/LabelInput.stories.tsx index 62742e287..853c0260a 100644 --- a/HDesign/src/components/LabelInput/LabelInput.stories.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.stories.tsx @@ -1,9 +1,10 @@ /** @jsxImportSource @emotion/react */ import type {Meta, StoryObj} from '@storybook/react'; -import LabelInput from '@components/LabelInput/LabelInput'; import {useEffect, useState} from 'react'; +import LabelInput from '@components/LabelInput/LabelInput'; + const meta = { title: 'Components/LabelInput', component: LabelInput, diff --git a/HDesign/src/components/LabelInput/LabelInput.tsx b/HDesign/src/components/LabelInput/LabelInput.tsx index 85baffa78..b7759b361 100644 --- a/HDesign/src/components/LabelInput/LabelInput.tsx +++ b/HDesign/src/components/LabelInput/LabelInput.tsx @@ -6,11 +6,11 @@ import Text from '@components/Text/Text'; import {useTheme} from '@/theme/HDesignProvider'; import Input from '../Input/Input'; +import Flex from '../Flex/Flex'; import {errorTextStyle, labelTextStyle} from './LabelInput.style'; import {useLabelInput} from './useLabelInput'; import {LabelInputProps} from './LabelInput.type'; -import Flex from '../Flex/Flex'; const LabelInput: React.FC<LabelInputProps> = forwardRef<HTMLInputElement, LabelInputProps>(function LabelInput( {labelText, errorText, isError, ...htmlProps}: LabelInputProps, diff --git a/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx index 5d2e3f1d5..873fe8118 100644 --- a/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx +++ b/client/src/components/Modal/SetActionModal/AddBillActionListModalContent/AddBillActionListModalContent.tsx @@ -1,9 +1,12 @@ import {FixedButton, LabelGroupInput} from 'haengdong-design'; -import style from './AddBillActionListModalContent.style'; + import {useStepList} from '@hooks/useStepList/useStepList'; -import useDynamicBillActionInput from '@hooks/useDynamicBillActionInput'; import validatePurchase from '@utils/validate/validatePurchase'; +import useDynamicBillActionInput from '@hooks/useDynamicBillActionInput'; + +import style from './AddBillActionListModalContent.style'; + interface SetPurchaseProps { setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; setOrder: React.Dispatch<React.SetStateAction<number>>; diff --git a/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx index 32871d0bf..59894493c 100644 --- a/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx +++ b/client/src/components/Modal/SetActionModal/AddMemberActionListModalContent/AddMemberActionListModalContent.tsx @@ -2,10 +2,11 @@ import {FixedButton, LabelGroupInput} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; import validateMemberName from '@utils/validate/validateMemberName'; +import {MemberType} from 'types/serviceType'; import useDynamicInput from '@hooks/useDynamicInput'; + import style from './AddMemberActionListModalContent.style'; -import {MemberType} from 'types/serviceType'; interface UpdateMembersProps { inOutAction: MemberType; diff --git a/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx b/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx index a69f09192..4546e0540 100644 --- a/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx +++ b/client/src/components/Modal/SetInitialMemberListModal/SetInitialMemberListModal.tsx @@ -1,8 +1,10 @@ import {Text, BottomSheet, FixedButton, LabelGroupInput} from 'haengdong-design'; + import {useStepList} from '@hooks/useStepList/useStepList'; import validateMemberName from '@utils/validate/validateMemberName'; import useDynamicInput from '@hooks/useDynamicInput'; + import { setInitialMemberListModalInputGroupStyle, setInitialMemberListModalStyle, diff --git a/client/src/pages/EventPage/AdminPage/AdminPage.tsx b/client/src/pages/EventPage/AdminPage/AdminPage.tsx index 6932fc59a..2c4ccd5a9 100644 --- a/client/src/pages/EventPage/AdminPage/AdminPage.tsx +++ b/client/src/pages/EventPage/AdminPage/AdminPage.tsx @@ -6,9 +6,10 @@ import {useStepList} from '@hooks/useStepList/useStepList'; import {requestGetEventName} from '@apis/request/event'; import useEventId from '@hooks/useEventId/useEventId'; -import {ReceiptStyle} from './AdminPage.style'; import {SetActionListModal, SetInitialMemberListModal} from '@components/Modal'; +import {ReceiptStyle} from './AdminPage.style'; + interface ModalBasedOnMemberCountProps { memberNameList: string[]; openBottomSheet: boolean; diff --git a/client/src/router.tsx b/client/src/router.tsx index 15a39cbcd..75777310a 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -1,12 +1,13 @@ import {createBrowserRouter} from 'react-router-dom'; -import {ROUTER_URLS} from '@constants/routerUrls'; +import {AdminPage} from '@pages/EventPage/AdminPage'; +import {HomePage} from '@pages/EventPage/HomePage'; import {CompleteCreateEventPage, SetEventNamePage} from '@pages/CreateEventPage'; import {MainPage} from '@pages/MainPage'; import {EventPage} from '@pages/EventPage'; -import {AdminPage} from '@pages/EventPage/AdminPage'; -import {HomePage} from '@pages/EventPage/HomePage'; + +import {ROUTER_URLS} from '@constants/routerUrls'; import App from './App'; From 332f11f64ff992efc740aacc6f4211a86f627ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:34:20 +0900 Subject: [PATCH 39/43] =?UTF-8?q?fix:=20Toast=202=EB=B2=88=20import=20?= =?UTF-8?q?=EB=90=98=EB=8D=98=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HDesign/src/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/HDesign/src/index.tsx b/HDesign/src/index.tsx index 965f50bf8..f6449f0bb 100644 --- a/HDesign/src/index.tsx +++ b/HDesign/src/index.tsx @@ -19,7 +19,6 @@ import TextButton from '@components/TextButton/TextButton'; import Title from '@components/Title/Title'; import Toast from '@components/Toast/Toast'; import TopNav from '@components/TopNav/TopNav'; -import Toast from '@components/Toast/Toast'; import {ToastProvider, useToast} from '@components/Toast/ToastProvider'; import {MainLayout} from '@layouts/MainLayout'; @@ -52,12 +51,11 @@ export { Text, TextButton, Title, - Toast, TopNav, MainLayout, ContentLayout, - HDesignProvider, + Toast, ToastProvider, useToast, - Toast, + HDesignProvider, }; From 4e32498eb27161f7c9cb68ad09d448b4def7bb62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:44:53 +0900 Subject: [PATCH 40/43] =?UTF-8?q?chore:=20yml=20workflow=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/frontend-pull-request.yml | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/frontend-pull-request.yml b/.github/workflows/frontend-pull-request.yml index c9efc26cc..1ae90a181 100644 --- a/.github/workflows/frontend-pull-request.yml +++ b/.github/workflows/frontend-pull-request.yml @@ -18,17 +18,28 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: '20.15.1' + - name: Cache dependencies + id: cache + uses: actions/cache@v3 + with: + path: '**/node_modules' + key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-npm- + - name: Install dependencies - working-directory: ./client - run: npm install + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd HDesign + npm install - name: Run lint - working-directory: ./client - run: npm run lint + run: npm run lint + working-directory: ./client \ No newline at end of file From 95b03914d72986bde3380999bac602752a062928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:47:31 +0900 Subject: [PATCH 41/43] =?UTF-8?q?chore:=20client=20pr=20workflow=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/frontend-pull-request.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/frontend-pull-request.yml b/.github/workflows/frontend-pull-request.yml index 1ae90a181..ba98eea84 100644 --- a/.github/workflows/frontend-pull-request.yml +++ b/.github/workflows/frontend-pull-request.yml @@ -9,8 +9,7 @@ on: jobs: test: - runs-on: ubuntu-latest log - + runs-on: ubuntu-latest defaults: run: shell: bash From c16864ccf6e416ad75c8274773d78757ac089521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:49:42 +0900 Subject: [PATCH 42/43] =?UTF-8?q?chore:=20client=20workflow=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/frontend-pull-request.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/frontend-pull-request.yml b/.github/workflows/frontend-pull-request.yml index ba98eea84..6e9c24fd0 100644 --- a/.github/workflows/frontend-pull-request.yml +++ b/.github/workflows/frontend-pull-request.yml @@ -36,9 +36,7 @@ jobs: - name: Install dependencies if: steps.cache.outputs.cache-hit != 'true' run: | - cd HDesign npm install - name: Run lint - run: npm run lint - working-directory: ./client \ No newline at end of file + run: npm run lint \ No newline at end of file From 48d1355354806726f6daa8a949ae616ec12ab1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=90=E1=85=A2=E1=84=92=E1=85=AE?= =?UTF-8?q?=E1=86=AB?= <rhymint@gmail.com> Date: Sun, 4 Aug 2024 16:51:34 +0900 Subject: [PATCH 43/43] =?UTF-8?q?chore:=20workflow=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/frontend-pull-request.yml | 24 +++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.github/workflows/frontend-pull-request.yml b/.github/workflows/frontend-pull-request.yml index 6e9c24fd0..238a9a0d1 100644 --- a/.github/workflows/frontend-pull-request.yml +++ b/.github/workflows/frontend-pull-request.yml @@ -9,7 +9,8 @@ on: jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-latest + defaults: run: shell: bash @@ -17,26 +18,17 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v2 - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v2 with: node-version: '20.15.1' - - name: Cache dependencies - id: cache - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-npm- - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: | - npm install + working-directory: ./client + run: npm install - name: Run lint - run: npm run lint \ No newline at end of file + working-directory: ./client + run: npm run lint