From 6b830a7813509889e8c2248b45358d8e8ab6f53b Mon Sep 17 00:00:00 2001 From: joarthvr Date: Wed, 27 Nov 2024 15:43:22 +0900 Subject: [PATCH 01/28] =?UTF-8?q?chore:=20sweetAlert2=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 ++- yarn.lock | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 908b017..cef19d1 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "react-router-dom": "^6.28.0", "react-select": "^5.8.3", "sass": "^1.81.0", + "sweetalert2": "^11.14.5", "zustand": "^5.0.1" }, "devDependencies": { @@ -121,4 +122,4 @@ "public" ] } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index b95f105..b00ad9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9077,6 +9077,11 @@ svg-tags@^1.0.0: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== +sweetalert2@^11.14.5: + version "11.14.5" + resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-11.14.5.tgz#a3adf9d8c6c8eb8eacfd57e2261503f2181abb8d" + integrity sha512-8MWk5uc/r6bWhiJWkUXyEuApfXAhSCZT8FFX7pZXL7YwaPxq+9Ynhi2dUzWkOFn9jvLjKj22CXuccZ+IHcnjvQ== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" From 12430757d259c2cecbc20fe8518987095d046ed6 Mon Sep 17 00:00:00 2001 From: joarthvr Date: Wed, 27 Nov 2024 15:44:39 +0900 Subject: [PATCH 02/28] =?UTF-8?q?feat:=20sweetAlert2=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/ui/SweetAlert/alerts.ts | 56 +++++++++++++ src/shared/ui/SweetAlert/config.ts | 126 +++++++++++++++++++++++++++++ src/shared/ui/index.ts | 2 +- 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/shared/ui/SweetAlert/alerts.ts create mode 100644 src/shared/ui/SweetAlert/config.ts diff --git a/src/shared/ui/SweetAlert/alerts.ts b/src/shared/ui/SweetAlert/alerts.ts new file mode 100644 index 0000000..8e1be92 --- /dev/null +++ b/src/shared/ui/SweetAlert/alerts.ts @@ -0,0 +1,56 @@ +import type { SweetAlertResult } from 'sweetalert2'; +import Swal from 'sweetalert2'; + +import type { SwalAlertOptions } from './config'; +import { SWAL_OPTIONS } from './config'; + +// 기본 확인 다이얼로그 +export const customConfirm = (options: SwalAlertOptions = {}) => { + return Swal.fire({ + ...SWAL_OPTIONS.confirm, + ...options, + }); +}; + +// 토스트 메시지 +export const customToast = (options: SwalAlertOptions = {}) => { + return Swal.fire({ + ...SWAL_OPTIONS.toast, + ...options, + }); +}; + +// 에러 알림 +export const errorAlert = (options: SwalAlertOptions = {}) => { + return Swal.fire({ + ...SWAL_OPTIONS.error, + ...options, + }); +}; + +// 삭제 확인 +export const deleteConfirm = (message = '삭제하시겠습니까?') => { + return Swal.fire({ + ...SWAL_OPTIONS.warning, + title: message, + text: '이 작업은 되돌릴 수 없습니다!', + }); +}; + +// 성공 알림 +export const successAlert = (options: SwalAlertOptions = {}) => { + return Swal.fire({ + ...SWAL_OPTIONS.success, + ...options, + }); +}; + +type PromptResult = SweetAlertResult; + +// 프롬프트 +export const customPrompt = (options: SwalAlertOptions = {}): Promise => { + return Swal.fire({ + ...SWAL_OPTIONS.prompt, + ...options, + }); +}; diff --git a/src/shared/ui/SweetAlert/config.ts b/src/shared/ui/SweetAlert/config.ts new file mode 100644 index 0000000..fc9de76 --- /dev/null +++ b/src/shared/ui/SweetAlert/config.ts @@ -0,0 +1,126 @@ +// sweetalert.ts +import type { SweetAlertOptions } from 'sweetalert2'; + +type SwalIconType = 'warning' | 'error' | 'success' | 'info' | 'question'; +type SwalPosition = + | 'top' + | 'top-start' + | 'top-end' + | 'center' + | 'center-start' + | 'center-end' + | 'bottom' + | 'bottom-start' + | 'bottom-end'; + +export type SwalAlertOptions = Omit & { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + inputValidator?: (value: any) => Promise | string | null; +}; + +export const SWAL_OPTIONS = { + confirm: { + icon: 'question' as SwalIconType, + showCancelButton: true, + confirmButtonColor: '#0b0b0b', + cancelButtonColor: '#999a99', + confirmButtonText: '확인', + cancelButtonText: '취소', + backdrop: `rgba(51, 53, 51, 0.5)`, + padding: '2rem', + customClass: { + container: 'swal2-container', + popup: 'swal2-popup custom-swal-font', + title: 'swal2-title custom-swal-font', + confirmButton: 'swal2-confirm custom-swal-font', + cancelButton: 'swal2-cancel custom-swal-font', + }, + }, + + toast: { + icon: 'success' as SwalIconType, + toast: true, + position: 'top-end' as SwalPosition, + showConfirmButton: false, + timer: 1500, + timerProgressBar: true, + background: 'rgba(255, 255, 255, 0.95)', + customClass: { + popup: 'swal2-popup swal2-toast custom-swal-font', + title: 'swal2-title custom-swal-font', + }, + }, + + error: { + icon: 'error' as SwalIconType, + confirmButtonColor: 'gray', + confirmButtonText: '확인', + backdrop: `rgba(51, 53, 51, 0.5)`, + padding: '2rem', + customClass: { + container: 'swal2-container', + popup: 'swal2-popup custom-swal-font', + title: 'swal2-title custom-swal-font', + confirmButton: 'swal2-confirm custom-swal-font', + }, + }, + + warning: { + icon: 'warning' as SwalIconType, + showCancelButton: true, + confirmButtonColor: '#0b0b0b', + cancelButtonColor: '#999a99', + confirmButtonText: '확인', + cancelButtonText: '취소', + backdrop: `rgba(51, 53, 51, 0.5)`, + padding: '2rem', + customClass: { + container: 'swal2-container', + popup: 'swal2-popup custom-swal-font', + title: 'swal2-title custom-swal-font', + confirmButton: 'swal2-confirm custom-swal-font', + cancelButton: 'swal2-cancel custom-swal-font', + }, + }, + + success: { + icon: 'success' as SwalIconType, + confirmButtonText: '확인', + backdrop: `rgba(51, 53, 51, 0.5)`, + padding: '2rem', + customClass: { + container: 'swal2-container', + popup: 'swal2-popup custom-swal-font', + title: 'swal2-title custom-swal-font', + confirmButton: 'swal2-confirm custom-swal-font', + }, + }, + + prompt: { + icon: 'question' as SwalIconType, + input: 'textarea' as const, + inputPlaceholder: '', + showCancelButton: true, + confirmButtonColor: '#0b0b0b', + cancelButtonColor: '#999a99', + confirmButtonText: '신고 하기', + cancelButtonText: '취소', + backdrop: `rgba(51, 53, 51, 0.5)`, + padding: '2rem', + customClass: { + container: 'swal2-container', + popup: 'swal2-popup custom-swal-font', + title: 'swal2-title custom-swal-font', + confirmButton: 'swal2-confirm custom-swal-font', + cancelButton: 'swal2-cancel custom-swal-font', + input: 'swal2-input custom-swal-font', + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + inputValidator: (value: any) => { + if (!value?.trim()) { + return '내용을 입력해주세요'; + } + return null; + }, + }, +} as const; diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts index 0f9c022..77c17ab 100644 --- a/src/shared/ui/index.ts +++ b/src/shared/ui/index.ts @@ -11,4 +11,4 @@ export { Switch } from './Switch/Switch'; export { Tag } from './Tag/Tag'; export { TripleDot } from './TripleDot/TripleDot'; export * from './SidebarFilter'; - +export * from './SweetAlert/alerts'; From e3fce7fb8a5ff4adef95b7bcbc20dfbcc2948651 Mon Sep 17 00:00:00 2001 From: joarthvr Date: Wed, 27 Nov 2024 15:44:55 +0900 Subject: [PATCH 03/28] =?UTF-8?q?style:=20sweetAlert=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/styles/globals.scss | 111 ++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/app/styles/globals.scss b/src/app/styles/globals.scss index e56e359..88d7ed9 100644 --- a/src/app/styles/globals.scss +++ b/src/app/styles/globals.scss @@ -1,6 +1,117 @@ @use './variables'; @import 'https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable.min.css'; +// Sweetalert2 폰트 & 스타일링 +.swal2-popup { + background-color: variables.$secondary-color !important; + border-radius: 1rem !important; + font-family: 'Pretendard Variable', Pretendard, sans-serif !important; +} + +.swal2-title, +.swal2-content, +.swal2-html-container { + color: variables.$primary-color !important; + font-family: 'Pretendard Variable', Pretendard, sans-serif !important; +} + +.swal2-confirm { + background-color: variables.$deep-black !important; + color: variables.$secondary-color !important; + border-radius: 0.5rem !important; + font-family: 'Pretendard Variable', Pretendard, sans-serif !important; + + &:focus { + box-shadow: 0 0 0 3px rgba(variables.$deep-black, 0.3) !important; + } +} + +.swal2-cancel { + background-color: variables.$third-color !important; + color: variables.$secondary-color !important; + border-radius: 0.5rem !important; + font-family: 'Pretendard Variable', Pretendard, sans-serif !important; + + &:focus { + box-shadow: 0 0 0 3px rgba(variables.$third-color, 0.3) !important; + } +} + +.swal2-input, +.swal2-textarea { + font-family: 'Pretendard Variable', Pretendard, sans-serif !important; + border-color: rgba(variables.$primary-color, 0.2) !important; + + &:focus { + box-shadow: 0 0 0 3px rgba(variables.$deep-black, 0.1) !important; + border-color: rgba(variables.$deep-black, 0.4) !important; + } +} + +// 토스트 메시지 스타일링 +.swal2-toast { + background-color: rgba(variables.$secondary-color, 0.95) !important; + box-shadow: 0 0 1rem rgba(variables.$primary-color, 0.1) !important; + font-family: 'Pretendard Variable', Pretendard, sans-serif !important; +} + +// 아이콘 색상 커스텀 +.swal2-icon { + &.swal2-success { + border-color: variables.$green !important; + color: variables.$green !important; + + .swal2-success-line-tip, + .swal2-success-line-long { + background-color: variables.$green !important; + } + + .swal2-success-ring { + border-color: rgba(variables.$green, 0.3) !important; + } + } + + &.swal2-error { + border-color: variables.$red !important; + color: variables.$red !important; + + .swal2-x-mark-line-left, + .swal2-x-mark-line-right { + background-color: variables.$red !important; + } + } + + &.swal2-warning { + border-color: variables.$yellow !important; + color: variables.$yellow !important; + } + + &.swal2-info { + border-color: variables.$blue !important; + color: variables.$blue !important; + } + + &.swal2-question { + border-color: variables.$purple !important; + color: variables.$purple !important; + } +} + +// 오버레이 배경 커스텀 +.swal2-container.swal2-backdrop-show { + background-color: variables.$modal-bg !important; +} + +// 프로그레스 바 커스텀 (토스트용) +.swal2-timer-progress-bar { + background-color: rgba(variables.$primary-color, 0.2) !important; +} + +.custom-swal-font { + font-family: 'Pretendard Variable', Pretendard, sans-serif !important; + font-weight: 700; +} + html, body, div, From f9ba2540509ddaee874199b1ef0e78d8d3161f14 Mon Sep 17 00:00:00 2001 From: joarthvr Date: Wed, 27 Nov 2024 15:45:26 +0900 Subject: [PATCH 04/28] feat: WriteGatheringPage --- src/app/appRouter.tsx | 11 ++++++++++- .../WriteGatheringPage/WriteGatheringPage.module.scss | 0 src/pages/WriteGatheringPage/WriteGatheringPage.tsx | 10 ++++++++++ src/pages/index.ts | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/pages/WriteGatheringPage/WriteGatheringPage.module.scss create mode 100644 src/pages/WriteGatheringPage/WriteGatheringPage.tsx diff --git a/src/app/appRouter.tsx b/src/app/appRouter.tsx index cb21899..4eb8014 100644 --- a/src/app/appRouter.tsx +++ b/src/app/appRouter.tsx @@ -1,6 +1,11 @@ import { createBrowserRouter } from 'react-router-dom'; -import { GatheringListPage, WriteArchivePage, DetailArchivePage } from '@/pages'; +import { + GatheringListPage, + WriteArchivePage, + DetailArchivePage, + WriteGatheringPage, +} from '@/pages'; import { Layout } from '@/widgets'; const AppRouter = () => { @@ -28,6 +33,10 @@ const AppRouter = () => { path: '/gathering', element: , }, + { + path: '/gathering/write', + element: , + }, { path: '/user', element: <>{/** userPage */}, diff --git a/src/pages/WriteGatheringPage/WriteGatheringPage.module.scss b/src/pages/WriteGatheringPage/WriteGatheringPage.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/WriteGatheringPage/WriteGatheringPage.tsx b/src/pages/WriteGatheringPage/WriteGatheringPage.tsx new file mode 100644 index 0000000..b32987d --- /dev/null +++ b/src/pages/WriteGatheringPage/WriteGatheringPage.tsx @@ -0,0 +1,10 @@ +import styles from './WriteGatheringPage.module.scss'; + +import { customConfirm } from '@/shared/ui'; +export const WriteGatheringPage = () => { + const result = customConfirm({ + title: '내용 초기화', + text: '작성 중인 내용을 초기화하시겠습니까?', + }); + return
WriteGatheringPage
; +}; diff --git a/src/pages/index.ts b/src/pages/index.ts index 49fd253..62ca421 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -1,3 +1,4 @@ export * from './GatheringListPage'; export { WriteArchivePage } from './WriteArchivePage/WriteArchivePage'; export { DetailArchivePage } from './DetailArchivePage/DetailArchivePage'; +export { WriteGatheringPage } from './WriteGatheringPage/WriteGatheringPage'; From 5c11a1f9739097745da18791132909eff4c50d4b Mon Sep 17 00:00:00 2001 From: joarthvr Date: Wed, 27 Nov 2024 21:22:10 +0900 Subject: [PATCH 05/28] =?UTF-8?q?fix:=20=EC=8A=A4=EB=8B=88=ED=8E=AB=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=3D=20=EB=B9=BC?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/typescriptreact.code-snippets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/typescriptreact.code-snippets b/.vscode/typescriptreact.code-snippets index 9f82511..6a4b9ca 100644 --- a/.vscode/typescriptreact.code-snippets +++ b/.vscode/typescriptreact.code-snippets @@ -5,7 +5,7 @@ "body": [ "", "import styles from './${1:${TM_FILENAME_BASE}}.module.scss'", - "interface ${1:${TM_FILENAME_BASE}}Props = {", + "interface ${1:${TM_FILENAME_BASE}}Props {", " $2", "}", "", From 5cba5035b84b36d4df955dabc492702a686ee150 Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 10:49:52 +0900 Subject: [PATCH 06/28] =?UTF-8?q?chore:=20=EB=A6=AC=EC=95=A1=ED=8A=B8=20?= =?UTF-8?q?=ED=9B=85=20=ED=8F=BC=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +++ yarn.lock | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index cef19d1..63e3d58 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@fortawesome/react-fontawesome": "^0.2.2", "@tanstack/react-query": "^5.60.6", "@tanstack/react-query-devtools": "^5.60.6", + "@types/react-datepicker": "^7.0.0", "@uiw/react-codemirror": "^4.23.6", "axios": "^1.7.7", "classnames": "^2.5.1", @@ -34,7 +35,9 @@ "marked": "^15.0.2", "marked-highlight": "^2.2.1", "react": "^18.3.1", + "react-datepicker": "^7.5.0", "react-dom": "^18.3.1", + "react-hook-form": "^7.53.2", "react-router-dom": "^6.28.0", "react-select": "^5.8.3", "sass": "^1.81.0", diff --git a/yarn.lock b/yarn.lock index b00ad9d..c28bdf3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1665,7 +1665,7 @@ dependencies: "@floating-ui/utils" "^0.2.8" -"@floating-ui/dom@^1.0.1": +"@floating-ui/dom@^1.0.0", "@floating-ui/dom@^1.0.1": version "1.6.12" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.12.tgz#6333dcb5a8ead3b2bf82f33d6bc410e95f54e556" integrity sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w== @@ -1673,6 +1673,22 @@ "@floating-ui/core" "^1.6.0" "@floating-ui/utils" "^0.2.8" +"@floating-ui/react-dom@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31" + integrity sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A== + dependencies: + "@floating-ui/dom" "^1.0.0" + +"@floating-ui/react@^0.26.23": + version "0.26.28" + resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.28.tgz#93f44ebaeb02409312e9df9507e83aab4a8c0dc7" + integrity sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw== + dependencies: + "@floating-ui/react-dom" "^2.1.2" + "@floating-ui/utils" "^0.2.8" + tabbable "^6.0.0" + "@floating-ui/utils@^0.2.8": version "0.2.8" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" @@ -3142,6 +3158,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== +"@types/react-datepicker@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-7.0.0.tgz#1d36553a92546246f1e10d862b3cfa7d295dff01" + integrity sha512-4tWwOUq589tozyQPBVEqGNng5DaZkomx5IVNuur868yYdgjH6RaL373/HKiVt1IDoNNXYiTGspm1F7kjrarM8Q== + dependencies: + react-datepicker "*" + "@types/react-dom@^18.3.1": version "18.3.1" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" @@ -4173,6 +4196,11 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -4492,6 +4520,11 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +date-fns@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" + integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== + debug@2.6.9, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -8089,6 +8122,16 @@ react-confetti@^6.1.0: dependencies: tween-functions "^1.2.0" +react-datepicker@*, react-datepicker@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-7.5.0.tgz#e7b1014a6dbd3b314839a5c57a6dacfbb16074e4" + integrity sha512-6MzeamV8cWSOcduwePHfGqY40acuGlS1cG//ePHT6bVbLxWyqngaStenfH03n1wbzOibFggF66kWaBTb1SbTtQ== + dependencies: + "@floating-ui/react" "^0.26.23" + clsx "^2.1.1" + date-fns "^3.6.0" + prop-types "^15.8.1" + react-docgen-typescript@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz#4611055e569edc071204aadb20e1c93e1ab1659c" @@ -8118,6 +8161,11 @@ react-docgen@^7.0.0: loose-envify "^1.1.0" scheduler "^0.23.2" +react-hook-form@^7.53.2: + version "7.53.2" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.2.tgz#6fa37ae27330af81089baadd7f322cc987b8e2ac" + integrity sha512-YVel6fW5sOeedd1524pltpHX+jgU2u3DSDtXEaBORNdqiNrsX/nUI/iGXONegttg0mJVnfrIkiV0cmTU6Oo2xw== + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -9095,6 +9143,11 @@ synckit@^0.9.1: "@pkgr/core" "^0.1.0" tslib "^2.6.2" +tabbable@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" + integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew== + table@^6.8.2: version "6.8.2" resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" From bdc1b78ca0470ded2b85e66aae79f1f12df8333a Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 10:51:36 +0900 Subject: [PATCH 07/28] =?UTF-8?q?feat:=20=EA=B2=8C=EB=8D=94=EB=A7=81?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=EB=90=A0=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=20=ED=83=80=EC=9E=85=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/gathering/model/options.ts | 34 +++++++++++++--- src/features/gathering/model/types.ts | 53 +++++++++++++++---------- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/features/gathering/model/options.ts b/src/features/gathering/model/options.ts index 6278db2..5c353b3 100644 --- a/src/features/gathering/model/options.ts +++ b/src/features/gathering/model/options.ts @@ -1,24 +1,24 @@ import type { GatheringFilterOptions } from './types'; export const gatheringFilterOptions: GatheringFilterOptions = { - processType: [ + contact: [ { value: '온라인', label: '온라인' }, { value: '오프라인', label: '오프라인' }, - { value: '온라인&오프라인', label: '온라인 & 오프라인' }, + { value: '온라인&오프라인', label: '온라인&오프라인' }, ], - category: [ + sort: [ { value: '스터디', label: '스터디' }, { value: '프로젝트', label: '프로젝트' }, { value: '동아리', label: '동아리' }, { value: '기타', label: '기타' }, ], - term: [ + period: [ { value: '1개월', label: '1개월' }, { value: '3개월', label: '3개월' }, { value: '6개월', label: '6개월' }, { value: '6개월 이상', label: '6개월 이상' }, ], - recruitment: [ + personnel: [ { value: '1', label: '1' }, { value: '2', label: '2' }, { value: '3', label: '3' }, @@ -36,4 +36,26 @@ export const gatheringFilterOptions: GatheringFilterOptions = { { value: '기획자', label: '기획자' }, { value: '마케터', label: '마케터' }, ], -} as const; + subject: { + project: [ + { value: '개발', label: '개발' }, + { value: '디자인', label: '디자인' }, + { value: '기획', label: '기획' }, + { value: '마케팅', label: '마케팅' }, + { value: '기타', label: '기타' }, + ], + study: [ + { value: '개발', label: '개발' }, + { value: '디자인', label: '디자인' }, + { value: '어학', label: '어학' }, + { value: '기타', label: '기타' }, + ], + club: [ + { value: '취미', label: '취미' }, + { value: '운동', label: '운동' }, + { value: '음악', label: '음악' }, + { value: '기타', label: '기타' }, + ], + etc: [{ value: '기타', label: '기타' }], + }, +}; diff --git a/src/features/gathering/model/types.ts b/src/features/gathering/model/types.ts index fa96ea1..6a147f3 100644 --- a/src/features/gathering/model/types.ts +++ b/src/features/gathering/model/types.ts @@ -1,28 +1,39 @@ -export type GatheringProcessType = '온라인' | '오프라인' | '온라인&오프라인'; -export type GatheringCategory = '스터디' | '프로젝트' | '동아리' | '기타'; -export type GatheringTerm = '1개월' | '3개월' | '6개월' | '6개월 이상'; -export type GatheringRecruitment = - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' - | '10명 이상'; +export type GatheringContactType = '온라인' | '오프라인' | '온라인&오프라인'; +export type GatheringSortType = '스터디' | '프로젝트' | '동아리' | '기타'; +export type GatheringSubjectType = '개발' | '디자인' | '기획' | '마케팅'; +export type GatheringPeriod = '1개월' | '3개월' | '6개월' | '6개월 이상'; +export type GatheringPersonnel = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10명 이상'; export type GatheringPosition = '개발자' | '디자이너' | '기획자' | '마케터'; -export interface SelectOption { - value: T; +export type SelectOption = { + value: string; label: string; +}; + +export interface GatheringFormData { + contact: string; + sort: string; + subject: string; + period: string; + personnel: string; + position: string[]; + gatheringTag: string[]; + title: string; + url: string; + content: string; + deadLine: string ; } export interface GatheringFilterOptions { - processType: SelectOption[]; - category: SelectOption[]; - term: SelectOption[]; - recruitment: SelectOption[]; - position: SelectOption[]; + contact: SelectOption[]; + sort: SelectOption[]; + period: SelectOption[]; + personnel: SelectOption[]; + position: SelectOption[]; + subject: { + project: SelectOption[]; + study: SelectOption[]; + club: SelectOption[]; + etc: SelectOption[]; + }; } From b96368ed89b96f0d7fa7a04a5faacf14193d247e Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 11:01:57 +0900 Subject: [PATCH 08/28] =?UTF-8?q?feat:=20=EA=B2=8C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=EC=85=80=EB=A0=89=ED=8A=B8=20=EB=B2=84=ED=8A=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gathering/ui/GatheringSelect.module.scss | 27 +++++++ src/features/gathering/ui/GatheringSelect.tsx | 72 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/features/gathering/ui/GatheringSelect.module.scss create mode 100644 src/features/gathering/ui/GatheringSelect.tsx diff --git a/src/features/gathering/ui/GatheringSelect.module.scss b/src/features/gathering/ui/GatheringSelect.module.scss new file mode 100644 index 0000000..fa1dfab --- /dev/null +++ b/src/features/gathering/ui/GatheringSelect.module.scss @@ -0,0 +1,27 @@ +.select { + :global { + .select__control { + border-color: #e2e8f0; + box-shadow: none; + + &:hover { + border-color: #cbd5e1; + } + + &--error { + border-color: red; + } + } + } +} +.container { + display: flex; + flex-direction: column; + width: 100%; + gap: 1.2rem; +} +.label { + font-size: 1.125rem; + font-weight: 600; + color: $primary-color; +} diff --git a/src/features/gathering/ui/GatheringSelect.tsx b/src/features/gathering/ui/GatheringSelect.tsx new file mode 100644 index 0000000..58b0e4e --- /dev/null +++ b/src/features/gathering/ui/GatheringSelect.tsx @@ -0,0 +1,72 @@ +import type { Control } from 'react-hook-form'; +import { Controller } from 'react-hook-form'; +import type { Props as SelectProps } from 'react-select'; +import Select from 'react-select'; + +import styles from './GatheringSelect.module.scss'; +import type { GatheringFormData, SelectOption } from '../model/types'; + +export type GatheringSelectProps = { + name: keyof GatheringFormData; + label: string; + options: SelectOption[]; + control: Control; + isMulti?: boolean; + placeholder?: string; + isRequired?: boolean; + isDisabled?: boolean; +} & Omit, 'name' | 'options'>; + +export const GatheringSelect = ({ + name, + label, + options, + control, + isMulti = false, + placeholder = '선택해주세요', + isRequired = false, + isDisabled = false, + ...selectProps +}: GatheringSelectProps) => { + return ( +
+ + ( +
+ + {...field} + {...selectProps} + className={`react-select-container `} + classNamePrefix='react-select' + isDisabled={isDisabled} + isMulti={isMulti} + onChange={newValue => { + if (isMulti) { + const values = (newValue as SelectOption[])?.map(item => item.value) || []; + field.onChange(values); + } else { + const value = (newValue as SelectOption)?.value || ''; + field.onChange(value); + } + }} + options={options} + placeholder={placeholder} + value={ + isMulti + ? options.filter(option => (field.value as string[])?.includes(option.value)) + : options.find(option => option.value === field.value) + } + /> +
+ )} + rules={{ required: isRequired }} + /> +
+ ); +}; From e641a3ce0a2e2c569af5aa0608e98333c6e23fc2 Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 11:02:31 +0900 Subject: [PATCH 09/28] =?UTF-8?q?feat:=20=EA=B2=8C=EB=8D=94=EB=A7=81=20dea?= =?UTF-8?q?dline=20=EC=98=B5=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/GatheringDatePicker.module.scss | 0 .../gathering/ui/GatheringDatePicker.tsx | 79 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 src/features/gathering/ui/GatheringDatePicker.module.scss create mode 100644 src/features/gathering/ui/GatheringDatePicker.tsx diff --git a/src/features/gathering/ui/GatheringDatePicker.module.scss b/src/features/gathering/ui/GatheringDatePicker.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/features/gathering/ui/GatheringDatePicker.tsx b/src/features/gathering/ui/GatheringDatePicker.tsx new file mode 100644 index 0000000..dfadfdc --- /dev/null +++ b/src/features/gathering/ui/GatheringDatePicker.tsx @@ -0,0 +1,79 @@ +import { ko } from 'date-fns/locale'; +import DatePicker from 'react-datepicker'; +import { Controller } from 'react-hook-form'; +import 'react-datepicker/dist/react-datepicker.css'; +import type { Control } from 'react-hook-form'; + +import type { GatheringFormData } from '../model/types'; + +export interface GatheringDatePickerProps { + name: keyof GatheringFormData; + label: string; + control: Control; + isRequired?: boolean; + placeholder?: string; +} + +export const GatheringDatePicker = ({ + name, + label, + control, + isRequired = false, + placeholder = '날짜를 선택해주세요', +}: GatheringDatePickerProps) => { + return ( +
+ + { + const handleDateChange = (date: Date | null) => { + if (date) { + const formattedDate = new Date(date.setHours(0, 0, 0, 0)).toISOString().split('T')[0]; + onChange(formattedDate); + } else { + onChange(''); + } + }; + + return ( +
+ + {error &&

{error.message}

} +
+ ); + }} + rules={{ + required: isRequired && '날짜를 선택해주세요', + validate: { + futureDate: (value: string | string[]) => { + if (!value || Array.isArray(value)) return true; + const date = new Date(value); + const today = new Date(); + today.setHours(0, 0, 0, 0); + return date >= today || '오늘 이후의 날짜를 선택해주세요'; + }, + }, + }} + /> +
+ ); +}; From 5f3590d0dd578d05af7884a70d00f2d555673d5c Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 11:02:47 +0900 Subject: [PATCH 10/28] =?UTF-8?q?feat:=20=EA=B2=8C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EC=98=B5=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/GatheringLinkInput.module.scss | 37 +++++++++++ .../gathering/ui/GatheringLinkInput.tsx | 63 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/features/gathering/ui/GatheringLinkInput.module.scss create mode 100644 src/features/gathering/ui/GatheringLinkInput.tsx diff --git a/src/features/gathering/ui/GatheringLinkInput.module.scss b/src/features/gathering/ui/GatheringLinkInput.module.scss new file mode 100644 index 0000000..ca159c5 --- /dev/null +++ b/src/features/gathering/ui/GatheringLinkInput.module.scss @@ -0,0 +1,37 @@ +.container { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.label { + font-size: 0.875rem; + font-weight: 500; + + .required { + color: #ef4444; + margin-left: 0.25rem; + } +} + +.input { + width: 100%; + padding: 0.5rem 0.75rem; + border: 1px solid #d1d5db; + border-radius: 0.375rem; + + &:focus { + outline: none; + // ring: 2px solid #3b82f6; + } + + &.error { + border-color: #ef4444; + } +} + +.errorMessage { + color: #ef4444; + font-size: 0.875rem; + margin-top: 0.25rem; +} diff --git a/src/features/gathering/ui/GatheringLinkInput.tsx b/src/features/gathering/ui/GatheringLinkInput.tsx new file mode 100644 index 0000000..cfdf8e4 --- /dev/null +++ b/src/features/gathering/ui/GatheringLinkInput.tsx @@ -0,0 +1,63 @@ +import cn from 'classnames'; +import { Controller } from 'react-hook-form'; +import type { Control } from 'react-hook-form'; + +import styles from './GatheringLinkInput.module.scss'; +import type { GatheringFormData } from '../model/types'; + +export interface GatheringLinkInputProps { + control: Control; + name: 'url'; + label: string; + isRequired?: boolean; + placeholder?: string; +} + +export const GatheringLinkInput = ({ + control, + name, + label, + isRequired = false, + placeholder = '링크를 입력해주세요', +}: GatheringLinkInputProps) => { + const validateUrl = (url: string) => { + if (!url) return true; + try { + new URL(url); + return true; + } catch { + return '올바른 URL 형식이 아닙니다'; + } + }; + + return ( +
+ + ( +
+ + {error &&

{error.message}

} +
+ )} + rules={{ + required: isRequired && '링크를 입력해주세요', + validate: validateUrl, + }} + /> +
+ ); +}; From d5ca0a72de3ab2fa1f8429a44020e77c98586413 Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 11:03:05 +0900 Subject: [PATCH 11/28] =?UTF-8?q?feat:=20=EA=B2=8C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=EC=98=B5=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/GatheringTagInput.module.scss | 72 ++++++++++++ .../gathering/ui/GatheringTagInput.tsx | 105 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 src/features/gathering/ui/GatheringTagInput.module.scss create mode 100644 src/features/gathering/ui/GatheringTagInput.tsx diff --git a/src/features/gathering/ui/GatheringTagInput.module.scss b/src/features/gathering/ui/GatheringTagInput.module.scss new file mode 100644 index 0000000..239bcdd --- /dev/null +++ b/src/features/gathering/ui/GatheringTagInput.module.scss @@ -0,0 +1,72 @@ +.container { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.label { + font-size: 0.875rem; + font-weight: 500; + + .required { + color: #ef4444; + margin-left: 0.25rem; + } +} + +.tagsContainer { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.tag { + display: flex; + align-items: center; + background-color: #dbeafe; + color: #1e40af; + padding: 0.25rem 0.5rem; + border-radius: 0.375rem; + gap: 0.25rem; + + .removeButton { + color: #2563eb; + margin-left: 0.25rem; + cursor: pointer; + border: none; + background: none; + padding: 0 0.25rem; + + &:hover { + color: #1e40af; + } + } +} + +.input { + width: 100%; + padding: 0.5rem 0.75rem; + border: 1px solid #d1d5db; + border-radius: 0.375rem; + + &:focus { + outline: none; + ring: 2px solid #3b82f6; + } + + &.error { + border-color: #ef4444; + } +} + +.errorMessage { + color: #ef4444; + font-size: 0.875rem; + margin-top: 0.25rem; +} +.hint { + font-size: 0.75rem; + color: #6b7280; + margin-top: 0.25rem; +} \ No newline at end of file diff --git a/src/features/gathering/ui/GatheringTagInput.tsx b/src/features/gathering/ui/GatheringTagInput.tsx new file mode 100644 index 0000000..848890b --- /dev/null +++ b/src/features/gathering/ui/GatheringTagInput.tsx @@ -0,0 +1,105 @@ +import cn from 'classnames'; +import type { KeyboardEvent } from 'react'; +import { useState } from 'react'; +import { Controller } from 'react-hook-form'; +import type { Control } from 'react-hook-form'; + +import styles from './GatheringTagInput.module.scss'; +import type { GatheringFormData } from '../model/types'; + +export interface GatheringTagInputProps { + control: Control; + name: 'gatheringTag'; + label: string; + isRequired?: boolean; + placeholder?: string; +} + +type HandleChangeFunction = (value: string[]) => void; + +export const GatheringTagInput = ({ + control, + name, + label, + isRequired = false, + placeholder = '태그를 입력해주세요', +}: GatheringTagInputProps) => { + const [inputValue, setInputValue] = useState(''); + + const handleKeyDown = ( + e: KeyboardEvent, + onChange: HandleChangeFunction, + currentValue: string[], + ) => { + if (e.key === 'Enter' && inputValue.trim()) { + e.preventDefault(); + if (currentValue.length >= 3) { + return; + } + if (!currentValue.includes(inputValue.trim())) { + onChange([...currentValue, inputValue.trim()]); + } + setInputValue(''); + } + }; + + const removeTag = ( + tagToRemove: string, + onChange: HandleChangeFunction, + currentValue: string[], + ) => { + onChange(currentValue.filter(tag => tag !== tagToRemove)); + }; + + return ( +
+ + ( +
+
+ {Array.isArray(value) && + value.map((tag: string) => ( + + {tag} + + + ))} +
+ = 3} + onChange={e => { + setInputValue(e.target.value); + }} + onKeyDown={e => { + handleKeyDown(e, onChange, value || []); + }} + placeholder={value.length >= 3 ? '태그는 최대 3개까지 입력 가능합니다' : placeholder} + type='text' + value={inputValue} + /> +

{`태그 ${value.length}/3`}

+ {error &&

{error.message}

} +
+ )} + rules={{ required: isRequired && '태그를 입력해주세요' }} + /> +
+ ); +}; From 6f7bafd74fbf0df457edad0fe8c7714773406a01 Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 11:03:21 +0900 Subject: [PATCH 12/28] =?UTF-8?q?feat:=20=EA=B2=8C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=98=B5=EC=85=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gathering/ui/GatheringTitIeInput.tsx | 64 +++++++++++++++++++ .../ui/GatheringTitleInput.module.scss | 56 ++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 src/features/gathering/ui/GatheringTitIeInput.tsx create mode 100644 src/features/gathering/ui/GatheringTitleInput.module.scss diff --git a/src/features/gathering/ui/GatheringTitIeInput.tsx b/src/features/gathering/ui/GatheringTitIeInput.tsx new file mode 100644 index 0000000..2bf44cf --- /dev/null +++ b/src/features/gathering/ui/GatheringTitIeInput.tsx @@ -0,0 +1,64 @@ +import cn from 'classnames'; +import { Controller } from 'react-hook-form'; +import type { Control } from 'react-hook-form'; + +import styles from './GatheringTitleInput.module.scss'; +import type { GatheringFormData } from '../model/types'; + +export interface GatheringTitleInputProps { + control: Control; + name: 'title'; + label: string; + isRequired?: boolean; + placeholder?: string; + maxLength?: number; +} + +export const GatheringTitleInput = ({ + control, + name, + label, + isRequired = false, + placeholder = '제목을 입력해주세요', + maxLength = 50, +}: GatheringTitleInputProps) => { + return ( +
+ + ( +
+ +
+ + {value?.length || 0}/{maxLength} + + {error &&

{error.message}

} +
+
+ )} + rules={{ + required: isRequired && '제목을 입력해주세요', + maxLength: { + value: maxLength, + message: `제목은 ${maxLength}자 이내로 입력해주세요`, + }, + }} + /> +
+ ); +}; diff --git a/src/features/gathering/ui/GatheringTitleInput.module.scss b/src/features/gathering/ui/GatheringTitleInput.module.scss new file mode 100644 index 0000000..07771de --- /dev/null +++ b/src/features/gathering/ui/GatheringTitleInput.module.scss @@ -0,0 +1,56 @@ +// GatheringTitleInput.module.scss +.container { + display: flex; + flex-direction: column; + gap: 0.5rem; + width: 100%; +} + +.label { + font-size: 0.875rem; + font-weight: 500; + + .required { + color: #ef4444; + margin-left: 0.25rem; + } +} + +.input { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid #d1d5db; + border-radius: 0.375rem; + font-size: 1rem; + + &:focus { + outline: none; + border-color: #3b82f6; + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1); + } + + &.error { + border-color: #ef4444; + } + + &::placeholder { + color: #9ca3af; + } +} + +.inputInfo { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.25rem; +} + +.charCount { + font-size: 0.75rem; + color: #6b7280; +} + +.errorMessage { + color: #ef4444; + font-size: 0.75rem; +} \ No newline at end of file From b1a87e9e38c589b302d2491896f0885971028a2c Mon Sep 17 00:00:00 2001 From: joarthvr Date: Thu, 28 Nov 2024 11:04:14 +0900 Subject: [PATCH 13/28] =?UTF-8?q?refactor:=20=EA=B2=8C=EB=8D=94=EB=A7=81?= =?UTF-8?q?=20=EC=98=B5=EC=85=98=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EA=B3=B5?= =?UTF-8?q?=EC=9C=A0=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=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 --- src/shared/ui/SelectBtn/SelectBtn.tsx | 10 +- .../ui/SidebarFilter/ui/SidebarFilter.tsx | 117 +++++++++--------- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/src/shared/ui/SelectBtn/SelectBtn.tsx b/src/shared/ui/SelectBtn/SelectBtn.tsx index 44f6681..7564c28 100644 --- a/src/shared/ui/SelectBtn/SelectBtn.tsx +++ b/src/shared/ui/SelectBtn/SelectBtn.tsx @@ -40,14 +40,18 @@ export const SelectBtn = ({ noOptionsMessage = '옵션이 없습니다', }: SelectBtnProps) => { const [selectedValue, setSelectedValue] = useState | MultiValue