From c884b8929c88fdb96731d2069f5c738a0a47c5dc Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Sun, 27 Oct 2024 16:34:11 +0900 Subject: [PATCH 01/13] =?UTF-8?q?design:=20=EB=AA=A8=EB=8B=AC=20=ED=97=A4?= =?UTF-8?q?=EB=8D=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/asset/svg/ic_block_create.svg | 4 +++ src/common/asset/svg/ic_warning.svg | 3 ++ .../component/Modal/Body/ModalBody.style.ts | 0 src/common/component/Modal/Body/ModalBody.tsx | 0 .../Modal/Footer/ModalFooter.style.ts | 0 .../component/Modal/Footer/ModalFooter.tsx | 0 .../Modal/Header/ModalHeader.style.ts | 8 +++++ .../component/Modal/Header/ModalHeader.tsx | 34 +++++++++++++++++++ .../ModalWrapper.style.ts} | 8 ++--- .../{Modal.tsx => Wrapper/ModalWrapper.tsx} | 2 +- src/common/component/Modal/index.tsx | 0 src/shared/component/Modal/ModalContainer.tsx | 2 +- src/shared/util/modalFooter.ts | 0 src/shared/util/modalHeader.tsx | 34 +++++++++++++++++++ 14 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 src/common/asset/svg/ic_block_create.svg create mode 100644 src/common/asset/svg/ic_warning.svg create mode 100644 src/common/component/Modal/Body/ModalBody.style.ts create mode 100644 src/common/component/Modal/Body/ModalBody.tsx create mode 100644 src/common/component/Modal/Footer/ModalFooter.style.ts create mode 100644 src/common/component/Modal/Footer/ModalFooter.tsx create mode 100644 src/common/component/Modal/Header/ModalHeader.style.ts create mode 100644 src/common/component/Modal/Header/ModalHeader.tsx rename src/common/component/Modal/{Modal.style.ts => Wrapper/ModalWrapper.style.ts} (87%) rename src/common/component/Modal/{Modal.tsx => Wrapper/ModalWrapper.tsx} (97%) create mode 100644 src/common/component/Modal/index.tsx create mode 100644 src/shared/util/modalFooter.ts create mode 100644 src/shared/util/modalHeader.tsx diff --git a/src/common/asset/svg/ic_block_create.svg b/src/common/asset/svg/ic_block_create.svg new file mode 100644 index 000000000..5f7da8779 --- /dev/null +++ b/src/common/asset/svg/ic_block_create.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/common/asset/svg/ic_warning.svg b/src/common/asset/svg/ic_warning.svg new file mode 100644 index 000000000..8d6c0b2a2 --- /dev/null +++ b/src/common/asset/svg/ic_warning.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/component/Modal/Body/ModalBody.style.ts b/src/common/component/Modal/Body/ModalBody.style.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/common/component/Modal/Body/ModalBody.tsx b/src/common/component/Modal/Body/ModalBody.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/common/component/Modal/Footer/ModalFooter.style.ts b/src/common/component/Modal/Footer/ModalFooter.style.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/common/component/Modal/Footer/ModalFooter.tsx b/src/common/component/Modal/Footer/ModalFooter.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/common/component/Modal/Header/ModalHeader.style.ts b/src/common/component/Modal/Header/ModalHeader.style.ts new file mode 100644 index 000000000..8cc12487c --- /dev/null +++ b/src/common/component/Modal/Header/ModalHeader.style.ts @@ -0,0 +1,8 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const infoTextStyle = css({ + color: theme.colors.gray_800, + fontWeight: 400, +}); diff --git a/src/common/component/Modal/Header/ModalHeader.tsx b/src/common/component/Modal/Header/ModalHeader.tsx new file mode 100644 index 000000000..5b9f62faf --- /dev/null +++ b/src/common/component/Modal/Header/ModalHeader.tsx @@ -0,0 +1,34 @@ +import { useModalContentType } from '@/shared/store/modal'; +import { getHeaderContent } from '@/shared/util/modalHeader'; + +import Flex from '../../Flex/Flex'; +import Text from '../../Text/Text'; +import { infoTextStyle } from './ModalHeader.style'; + +interface ModalHeaderProps { + step?: number; + totalSteps?: number; +} + +const ModalHeader = ({ step, totalSteps = 4 }: ModalHeaderProps) => { + const contentType = useModalContentType(); + const { icon, title, infoText } = getHeaderContent(contentType!, step, totalSteps); + + return ( + + {icon && {icon}} + + + {title} + + {infoText && ( + + {infoText} + + )} + + + ); +}; + +export default ModalHeader; diff --git a/src/common/component/Modal/Modal.style.ts b/src/common/component/Modal/Wrapper/ModalWrapper.style.ts similarity index 87% rename from src/common/component/Modal/Modal.style.ts rename to src/common/component/Modal/Wrapper/ModalWrapper.style.ts index 136a84cb1..3f3c20ac1 100644 --- a/src/common/component/Modal/Modal.style.ts +++ b/src/common/component/Modal/Wrapper/ModalWrapper.style.ts @@ -22,12 +22,12 @@ export const dialogStyle = css({ position: 'fixed', top: '50%', left: '50%', - width: '51.1rem', + width: '37.6rem', + height: '55.4rem', zIndex: theme.zIndex.overlayTop, - paddingTop: '4.8rem', - paddingBottom: '4.8rem', - borderRadius: '16px', + padding: '2rem 2rem 3.2rem 2rem', + borderRadius: '8px', border: 'none', outline: 'none', background: theme.colors.white, diff --git a/src/common/component/Modal/Modal.tsx b/src/common/component/Modal/Wrapper/ModalWrapper.tsx similarity index 97% rename from src/common/component/Modal/Modal.tsx rename to src/common/component/Modal/Wrapper/ModalWrapper.tsx index 9406b8064..6a44ee558 100644 --- a/src/common/component/Modal/Modal.tsx +++ b/src/common/component/Modal/Wrapper/ModalWrapper.tsx @@ -4,7 +4,7 @@ import { ReactNode, useCallback, useEffect } from 'react'; import { createPortal } from 'react-dom'; -import { backgroundStyle, dialogStyle } from '@/common/component/Modal/Modal.style'; +import { backgroundStyle, dialogStyle } from '@/common/component/Modal/Wrapper/ModalWrapper.style'; interface ModalProps { isOpen: boolean; diff --git a/src/common/component/Modal/index.tsx b/src/common/component/Modal/index.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/shared/component/Modal/ModalContainer.tsx b/src/shared/component/Modal/ModalContainer.tsx index 97978320b..0d65645c3 100644 --- a/src/shared/component/Modal/ModalContainer.tsx +++ b/src/shared/component/Modal/ModalContainer.tsx @@ -1,4 +1,4 @@ -import Modal from '@/common/component/Modal/Modal'; +import Modal from '@/common/component/Modal/Wrapper/ModalWrapper'; import { BlockFlow } from '@/page/archiving/index/component/TimeBlockModal'; diff --git a/src/shared/util/modalFooter.ts b/src/shared/util/modalFooter.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/shared/util/modalHeader.tsx b/src/shared/util/modalHeader.tsx new file mode 100644 index 000000000..f9263b936 --- /dev/null +++ b/src/shared/util/modalHeader.tsx @@ -0,0 +1,34 @@ +import BlockIcon from '@/common/asset/svg/ic_block_create.svg?react'; +import WarningIcon from '@/common/asset/svg/ic_warning.svg?react'; + +export const getHeaderContent = (contentType: string, step?: number, totalSteps?: number) => { + switch (contentType) { + case 'create-workspace': + return { + icon: step && totalSteps ? `${step}/${totalSteps}` : null, + title: '새로운 워크스페이스 생성하기', + infoText: + step === 1 + ? '워크스페이스의 이름을 입력해주세요' + : step === 2 + ? '워크스페이스의 카테고리를 선택해주세요' + : step === 3 + ? '워크스페이스 이미지를 등록해주세요' + : '완료하기', + }; + case 'create-block': + return { + icon: , + title: '타임블록 생성하기', + infoText: step === 1 ? '블록명을 입력해주세요' : '블록에 필요한 문서를 업로드해주세요', + }; + case 'delete': + return { + icon: , + title: '삭제 확인', + infoText: '삭제할 항목을 확인해주세요.', + }; + default: + return { icon: null, title: '', infoText: '' }; + } +}; From 0891ffa69c977e2b11c21365877b83fea207fb74 Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Sun, 27 Oct 2024 16:40:40 +0900 Subject: [PATCH 02/13] =?UTF-8?q?design:=20=EB=AA=A8=EB=8B=AC=20=EB=B0=94?= =?UTF-8?q?=EB=94=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/component/Modal/Body/ModalBody.style.ts | 6 ++++++ src/common/component/Modal/Body/ModalBody.tsx | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/common/component/Modal/Body/ModalBody.style.ts b/src/common/component/Modal/Body/ModalBody.style.ts index e69de29bb..7935e1350 100644 --- a/src/common/component/Modal/Body/ModalBody.style.ts +++ b/src/common/component/Modal/Body/ModalBody.style.ts @@ -0,0 +1,6 @@ +import { css } from '@emotion/react'; + +export const containerStyle = css({ + width: '33.6rem', + height: '36.4rem', +}); diff --git a/src/common/component/Modal/Body/ModalBody.tsx b/src/common/component/Modal/Body/ModalBody.tsx index e69de29bb..1c5802dde 100644 --- a/src/common/component/Modal/Body/ModalBody.tsx +++ b/src/common/component/Modal/Body/ModalBody.tsx @@ -0,0 +1,11 @@ +import { ReactNode } from 'react'; + +import { containerStyle } from './ModalBody.style'; + +interface ModalBodyProps { + children: ReactNode; +} + +const ModalBody = ({ children }: ModalBodyProps) =>
{children}
; + +export default ModalBody; From 236ffda6e03712546994ee625d5b43f3fa62eb3e Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Sat, 2 Nov 2024 00:22:18 +0900 Subject: [PATCH 03/13] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=9E=AC=EC=A0=95=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/Modal/Footer/ModalFooter.tsx | 29 ++++++++++ .../component/Modal/Wrapper/ModalWrapper.tsx | 4 +- src/common/component/Modal/index.tsx | 10 ++++ src/shared/component/Modal/ModalContainer.tsx | 2 +- .../category/WorkSpaceCategory.tsx | 50 ++++++++--------- .../complete/WorkSpaceComplete.tsx | 27 +++++----- .../WorkSpaceModal/image/WorkSpaceImage.tsx | 53 +++++++++---------- .../WorkSpaceModal/name/WorkSpaceName.tsx | 31 ++++------- src/shared/util/modalFooter.ts | 0 src/shared/util/modalFooter.tsx | 36 +++++++++++++ src/story/shared/WorkSpaceModal.stories.tsx | 35 ------------ 11 files changed, 149 insertions(+), 128 deletions(-) delete mode 100644 src/shared/util/modalFooter.ts create mode 100644 src/shared/util/modalFooter.tsx delete mode 100644 src/story/shared/WorkSpaceModal.stories.tsx diff --git a/src/common/component/Modal/Footer/ModalFooter.tsx b/src/common/component/Modal/Footer/ModalFooter.tsx index e69de29bb..89cf555da 100644 --- a/src/common/component/Modal/Footer/ModalFooter.tsx +++ b/src/common/component/Modal/Footer/ModalFooter.tsx @@ -0,0 +1,29 @@ +import { useCloseModal, useModalContentType } from '@/shared/store/modal'; +import { getFooterContent } from '@/shared/util/modalFooter'; + +import Button from '../../Button/Button'; +import Flex from '../../Flex/Flex'; + +interface ModalFooterProps { + step: number; + buttonClick?: () => void; + isButtonActive?: boolean; +} + +const ModalFooter = ({ step, buttonClick, isButtonActive }: ModalFooterProps) => { + const contentType = useModalContentType(); + const closeModal = useCloseModal(); + const footerButtons = getFooterContent(contentType!, step, buttonClick, closeModal, isButtonActive); + + return ( + + {footerButtons.map((button, index) => ( + + ))} + + ); +}; + +export default ModalFooter; diff --git a/src/common/component/Modal/Wrapper/ModalWrapper.tsx b/src/common/component/Modal/Wrapper/ModalWrapper.tsx index 6a44ee558..e61ed1845 100644 --- a/src/common/component/Modal/Wrapper/ModalWrapper.tsx +++ b/src/common/component/Modal/Wrapper/ModalWrapper.tsx @@ -12,7 +12,7 @@ interface ModalProps { onClose?: () => void; } -const Modal = ({ isOpen, children, onClose }: ModalProps) => { +const ModalWrapper = ({ isOpen, children, onClose }: ModalProps) => { const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (e.key === 'Escape') { @@ -52,4 +52,4 @@ const Modal = ({ isOpen, children, onClose }: ModalProps) => { ); }; -export default Modal; +export default ModalWrapper; diff --git a/src/common/component/Modal/index.tsx b/src/common/component/Modal/index.tsx index e69de29bb..d2193c721 100644 --- a/src/common/component/Modal/index.tsx +++ b/src/common/component/Modal/index.tsx @@ -0,0 +1,10 @@ +import ModalBody from './Body/ModalBody'; +import ModalFooter from './Footer/ModalFooter'; +import ModalHeader from './Header/ModalHeader'; +import ModalWrapper from './Wrapper/ModalWrapper'; + +export const Modal = Object.assign(ModalWrapper, { + Header: ModalHeader, + Body: ModalBody, + Footer: ModalFooter, +}); diff --git a/src/shared/component/Modal/ModalContainer.tsx b/src/shared/component/Modal/ModalContainer.tsx index 0d65645c3..9ab0fde95 100644 --- a/src/shared/component/Modal/ModalContainer.tsx +++ b/src/shared/component/Modal/ModalContainer.tsx @@ -1,4 +1,4 @@ -import Modal from '@/common/component/Modal/Wrapper/ModalWrapper'; +import { Modal } from '@/common/component/Modal'; import { BlockFlow } from '@/page/archiving/index/component/TimeBlockModal'; diff --git a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx index acf483013..6174238d2 100644 --- a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx +++ b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import Button from '@/common/component/Button/Button'; import Flex from '@/common/component/Flex/Flex'; +import { Modal } from '@/common/component/Modal'; import Select from '@/common/component/Select/Select'; import { useOutsideClick, useOverlay } from '@/common/hook'; @@ -62,33 +63,28 @@ const WorkSpaceCategory = ({ isVisible }: WorkSpaceCategoryProps) => { const isButtonActive = selected.trim().length > 0; return ( - - -
- ({ value: str }))} + className="select-container" + /> +
+ + + ); }; diff --git a/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx b/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx index 4f63b5f1b..8cc592b17 100644 --- a/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx +++ b/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx @@ -3,9 +3,11 @@ import { css } from '@emotion/react'; import completePng from '@/common/asset/img/workspace_complete.png'; import complete from '@/common/asset/img/workspace_complete.webp'; import Flex from '@/common/component/Flex/Flex'; +import { Modal } from '@/common/component/Modal'; import WorkSapceInfo from '@/shared/component/WorkSpaceModal/info/WorkSpaceInfo'; import { sectionStyle } from '@/shared/component/WorkSpaceModal/name/WorkSpaceName.style'; +import { useCloseModal } from '@/shared/store/modal'; interface WorkSpaceCompleteProps { isVisible: boolean; @@ -14,20 +16,19 @@ interface WorkSpaceCompleteProps { const WorkSpaceComplete = ({ isVisible }: WorkSpaceCompleteProps) => { if (!isVisible) return null; + const closeModal = useCloseModal(); + return ( - - - - - 워크 스페이스 완료 이미지 - - + <> + + + + + 워크 스페이스 완료 이미지 + + + + ); }; diff --git a/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx b/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx index 0b5a81176..ecc61eba1 100644 --- a/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx +++ b/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx @@ -3,6 +3,7 @@ import TeamProfileDelete from '@/common/asset/svg/ic_team_profile_delete.svg?rea import Button from '@/common/component/Button/Button'; import Flex from '@/common/component/Flex/Flex'; import Label from '@/common/component/Label/Label'; +import { Modal } from '@/common/component/Modal'; import { buttonCompleteStyle, @@ -43,34 +44,30 @@ const WorkSpaceImage = ({ isVisible }: WorkSpaceImageProps) => { if (!isVisible) return null; return ( - - -
- {fileURL ? ( - 프로필 이미지 - ) : ( - - )} - {fileURL && } -
- - -
+ <> + + +
+ {fileURL ? ( + 프로필 이미지 + ) : ( + + )} + {fileURL && } +
+ +
+ + ); }; diff --git a/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx b/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx index 3ed9617ed..18cb43132 100644 --- a/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx +++ b/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx @@ -1,15 +1,9 @@ import { useState } from 'react'; -import Button from '@/common/component/Button/Button'; -import Flex from '@/common/component/Flex/Flex'; import Input from '@/common/component/Input/Input'; +import { Modal } from '@/common/component/Modal'; -import WorkSapceInfo from '@/shared/component/WorkSpaceModal/info/WorkSpaceInfo'; -import { - buttonStyle, - inputWrapperStyle, - sectionStyle, -} from '@/shared/component/WorkSpaceModal/name/WorkSpaceName.style'; +import { inputWrapperStyle } from '@/shared/component/WorkSpaceModal/name/WorkSpaceName.style'; import { useWorkSpaceContext } from '@/shared/hook/common/useWorkSpaceContext'; interface WorkSpaceNameProps { @@ -34,25 +28,18 @@ const WorkSpaceName = ({ isVisible }: WorkSpaceNameProps) => { if (!isVisible) return null; return ( - - -
+ <> + + -
- -
+ + + ); }; diff --git a/src/shared/util/modalFooter.ts b/src/shared/util/modalFooter.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/shared/util/modalFooter.tsx b/src/shared/util/modalFooter.tsx new file mode 100644 index 000000000..894bd163e --- /dev/null +++ b/src/shared/util/modalFooter.tsx @@ -0,0 +1,36 @@ +export interface FooterButton { + text: string; + onClick?: () => void; + variant: 'primary' | 'secondary' | 'tertiary' | 'outline' | 'underline'; + disabled?: boolean; +} + +export const getFooterContent = ( + contentType: string, + step: number, + buttonClick?: () => void, + closeModal?: () => void, + isButtonActive: boolean = true +): FooterButton[] => { + switch (contentType) { + case 'create-workspace': + return [ + { text: step === 4 ? '완료' : '다음으로', onClick: buttonClick, variant: 'primary', disabled: !isButtonActive }, + step > 2 + ? ({ text: '이전으로', onClick: buttonClick, variant: 'secondary', disabled: true } as FooterButton) + : false, + ].filter(Boolean) as FooterButton[]; + case 'create-block': + return [ + { text: '저장', onClick: buttonClick, variant: 'primary' }, + { text: '취소', onClick: closeModal, variant: 'secondary' }, + ]; + case 'delete': + return [ + { text: '삭제', onClick: buttonClick, variant: 'primary' }, + { text: '취소', onClick: closeModal, variant: 'secondary' }, + ]; + default: + return []; + } +}; diff --git a/src/story/shared/WorkSpaceModal.stories.tsx b/src/story/shared/WorkSpaceModal.stories.tsx deleted file mode 100644 index 67694e04c..000000000 --- a/src/story/shared/WorkSpaceModal.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; - -import WorkSpaceComplete from '@/shared/component/WorkSpaceModal/complete/WorkSpaceComplete'; -import WorkSpaceName from '@/shared/component/WorkSpaceModal/name/WorkSpaceName'; -import { WorkSpaceProvider } from '@/shared/hook/common/useWorkSpaceContext'; - -const meta: Meta = { - title: 'Shared/WorkSpaceModal', - component: WorkSpaceProvider, - parameters: { - layout: 'centered', - }, - decorators: [ - (Story) => ( - - - - ), - ], -}; - -export default meta; -type Story = StoryObj; - -export const Name: Story = { - render: () => { - return ; - }, -}; - -export const Complete: Story = { - render: () => { - return ; - }, -}; From 74690693c69a59665c65c15df89ac6d90a967776 Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Mon, 4 Nov 2024 10:20:50 +0900 Subject: [PATCH 04/13] =?UTF-8?q?feat:=20=EC=9B=8C=ED=81=AC=EC=8A=A4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=83=9D=EC=84=B1=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/common/asset/svg/ic_team_profile_add.svg | 6 +++--- .../asset/svg/ic_team_profile_delete.svg | 6 +++--- src/common/asset/svg/ic_workspace_success.svg | 3 +++ .../component/Modal/Body/ModalBody.style.ts | 6 ++++-- src/common/component/Modal/Body/ModalBody.tsx | 7 ++++++- .../Modal/Footer/ModalFooter.style.ts | 0 .../component/Modal/Footer/ModalFooter.tsx | 8 ++++++- .../Modal/Header/ModalHeader.style.ts | 13 ++++++++++++ .../component/Modal/Header/ModalHeader.tsx | 10 ++++++--- .../Modal/Wrapper/ModalWrapper.style.ts | 4 +++- .../category/WorkSpaceCategory.tsx | 2 +- .../image/WorkSpaceImage.style.ts | 12 ++++++----- .../WorkSpaceModal/image/WorkSpaceImage.tsx | 4 +++- src/shared/util/modalFooter.tsx | 12 ++++++----- src/shared/util/modalHeader.tsx | 21 ++++++++++++++----- 15 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 src/common/asset/svg/ic_workspace_success.svg delete mode 100644 src/common/component/Modal/Footer/ModalFooter.style.ts diff --git a/src/common/asset/svg/ic_team_profile_add.svg b/src/common/asset/svg/ic_team_profile_add.svg index 5190041d4..79159526d 100644 --- a/src/common/asset/svg/ic_team_profile_add.svg +++ b/src/common/asset/svg/ic_team_profile_add.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/src/common/asset/svg/ic_team_profile_delete.svg b/src/common/asset/svg/ic_team_profile_delete.svg index fe12e3904..b3761b592 100644 --- a/src/common/asset/svg/ic_team_profile_delete.svg +++ b/src/common/asset/svg/ic_team_profile_delete.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/src/common/asset/svg/ic_workspace_success.svg b/src/common/asset/svg/ic_workspace_success.svg new file mode 100644 index 000000000..72b1fd271 --- /dev/null +++ b/src/common/asset/svg/ic_workspace_success.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/component/Modal/Body/ModalBody.style.ts b/src/common/component/Modal/Body/ModalBody.style.ts index 7935e1350..311cf8d40 100644 --- a/src/common/component/Modal/Body/ModalBody.style.ts +++ b/src/common/component/Modal/Body/ModalBody.style.ts @@ -1,6 +1,8 @@ import { css } from '@emotion/react'; export const containerStyle = css({ - width: '33.6rem', - height: '36.4rem', + width: '100%', + height: '100%', + + paddingTop: '2rem', }); diff --git a/src/common/component/Modal/Body/ModalBody.tsx b/src/common/component/Modal/Body/ModalBody.tsx index 1c5802dde..5add8a688 100644 --- a/src/common/component/Modal/Body/ModalBody.tsx +++ b/src/common/component/Modal/Body/ModalBody.tsx @@ -1,11 +1,16 @@ import { ReactNode } from 'react'; +import Flex from '../../Flex/Flex'; import { containerStyle } from './ModalBody.style'; interface ModalBodyProps { children: ReactNode; } -const ModalBody = ({ children }: ModalBodyProps) =>
{children}
; +const ModalBody = ({ children }: ModalBodyProps) => ( + + {children} + +); export default ModalBody; diff --git a/src/common/component/Modal/Footer/ModalFooter.style.ts b/src/common/component/Modal/Footer/ModalFooter.style.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/common/component/Modal/Footer/ModalFooter.tsx b/src/common/component/Modal/Footer/ModalFooter.tsx index 89cf555da..011dcc7b7 100644 --- a/src/common/component/Modal/Footer/ModalFooter.tsx +++ b/src/common/component/Modal/Footer/ModalFooter.tsx @@ -18,7 +18,13 @@ const ModalFooter = ({ step, buttonClick, isButtonActive }: ModalFooterProps) => return ( {footerButtons.map((button, index) => ( - ))} diff --git a/src/common/component/Modal/Header/ModalHeader.style.ts b/src/common/component/Modal/Header/ModalHeader.style.ts index 8cc12487c..802f0e06b 100644 --- a/src/common/component/Modal/Header/ModalHeader.style.ts +++ b/src/common/component/Modal/Header/ModalHeader.style.ts @@ -6,3 +6,16 @@ export const infoTextStyle = css({ color: theme.colors.gray_800, fontWeight: 400, }); + +export const iconTextStyle = css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + + color: theme.colors.gray_800, + fontWeight: 500, + width: '4rem', + height: '4rem', + + textAlign: 'center', +}); diff --git a/src/common/component/Modal/Header/ModalHeader.tsx b/src/common/component/Modal/Header/ModalHeader.tsx index 5b9f62faf..ad7580309 100644 --- a/src/common/component/Modal/Header/ModalHeader.tsx +++ b/src/common/component/Modal/Header/ModalHeader.tsx @@ -3,7 +3,7 @@ import { getHeaderContent } from '@/shared/util/modalHeader'; import Flex from '../../Flex/Flex'; import Text from '../../Text/Text'; -import { infoTextStyle } from './ModalHeader.style'; +import { iconTextStyle, infoTextStyle } from './ModalHeader.style'; interface ModalHeaderProps { step?: number; @@ -15,8 +15,12 @@ const ModalHeader = ({ step, totalSteps = 4 }: ModalHeaderProps) => { const { icon, title, infoText } = getHeaderContent(contentType!, step, totalSteps); return ( - - {icon && {icon}} + + {icon && ( + + {icon} + + )} {title} diff --git a/src/common/component/Modal/Wrapper/ModalWrapper.style.ts b/src/common/component/Modal/Wrapper/ModalWrapper.style.ts index 3f3c20ac1..e35bddf72 100644 --- a/src/common/component/Modal/Wrapper/ModalWrapper.style.ts +++ b/src/common/component/Modal/Wrapper/ModalWrapper.style.ts @@ -18,7 +18,9 @@ export const backgroundStyle = css({ }); export const dialogStyle = css({ - display: 'block', + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', position: 'fixed', top: '50%', left: '50%', diff --git a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx index 6174238d2..c76cc1110 100644 --- a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx +++ b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx @@ -66,7 +66,7 @@ const WorkSpaceCategory = ({ isVisible }: WorkSpaceCategoryProps) => { <> -
+
- - {formData.blockName.length} / 25 - - - + + + + + {formData.blockName.length} / 25 + + + - - - setFormData({ startDate: date })} - onSetEndDate={(date) => setFormData({ endDate: date })} - onSetIsDateRangeValid={setIsDateRangeValid} - /> - - - - - + + + + + + + ); }; diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Block/Date/BlockDate.style.ts b/src/page/archiving/index/component/TimeBlockModal/component/Block/Date/BlockDate.style.ts deleted file mode 100644 index c837bdec2..000000000 --- a/src/page/archiving/index/component/TimeBlockModal/component/Block/Date/BlockDate.style.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { css } from '@emotion/react'; - -import { theme } from '@/common/style/theme/theme'; - -export const textStyle = css({ - color: theme.colors.gray_500, - fontSize: '2rem', -}); - -export const supportStyle = css({ - marginTop: '0.8rem', - ...theme.text.body07, - fontWeight: 400, -}); diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Block/Date/BlockDate.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Block/Date/BlockDate.tsx deleted file mode 100644 index 2c6b594e4..000000000 --- a/src/page/archiving/index/component/TimeBlockModal/component/Block/Date/BlockDate.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { css } from '@emotion/react'; - -import Flex from '@/common/component/Flex/Flex'; -import Input from '@/common/component/Input/Input'; -import SupportingText from '@/common/component/SupportingText/SupportingText'; - -import { - supportStyle, - textStyle, -} from '@/page/archiving/index/component/TimeBlockModal/component/Block/Date/BlockDate.style'; -import useDateRange from '@/page/archiving/index/component/TimeBlockModal/hook/common/useDateRange'; - -interface BlockDateProps { - startDate: string; - endDate: string; - onSetStartDate: (date: string) => void; - onSetEndDate: (date: string) => void; - onSetIsDateRangeValid: (isValid: boolean) => void; -} - -const BlockDate = ({ startDate, endDate, onSetStartDate, onSetEndDate, onSetIsDateRangeValid }: BlockDateProps) => { - const { dates, validation, handleChange } = useDateRange( - startDate, - endDate, - (date: string) => onSetStartDate(date), - (date: string) => onSetEndDate(date), - onSetIsDateRangeValid - ); - - const inputStyle = (value: string) => css` - text-align: ${value.length === 10 ? 'center' : 'left'}; - `; - - return ( - <> - - handleChange('startDate', e.target.value, validation.isEndDateValid, true)} - maxLength={10} - isError={validation.isStartDateError} - /> -

~

- handleChange('endDate', e.target.value, validation.isStartDateValid, false)} - maxLength={10} - isError={validation.isEndDateError} - /> -
- -
- {(validation.isStartDateError || validation.isEndDateError) && ( - - {validation.errorMessage} - - )} -
- - ); -}; - -export default BlockDate; diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Block/Icon/BlockIcon.style.ts b/src/page/archiving/index/component/TimeBlockModal/component/Block/Icon/BlockIcon.style.ts index c46b957b5..a5d2329c3 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Block/Icon/BlockIcon.style.ts +++ b/src/page/archiving/index/component/TimeBlockModal/component/Block/Icon/BlockIcon.style.ts @@ -7,15 +7,16 @@ export const iconStyle = css({ justifyContent: 'center', alignItems: 'center', - width: '5rem', - height: '5rem', + width: '4rem', + height: '4rem', borderRadius: '100%', - border: `1.2px solid ${theme.colors.gray_400}`, overflow: 'hidden', cursor: 'pointer', + backgroundColor: theme.colors.gray_100, + '&:hover': { backgroundColor: theme.colors.blue_100, }, @@ -32,5 +33,7 @@ export const boxStyle = css({ justifyContent: 'center', flexDirection: 'row', + width: '100%', + gap: '1.2rem', }); diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox.tsx index 34e649bc5..89824c64a 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox.tsx @@ -10,7 +10,7 @@ interface BlockBoxProps { const BlockBox = ({ title, children }: BlockBoxProps) => { return ( - + {title} diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.style.ts b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.style.ts index df9f06239..2780e2b5e 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.style.ts +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.style.ts @@ -9,17 +9,28 @@ export const boxStyle = css({ color: theme.colors.gray_500, }); -export const textStyle = css({ - color: theme.colors.gray_500, +export const colorStyle = css({ + color: theme.colors.gray_800, }); -export const buttonStyle = css({ - padding: '0', - width: '6.4rem', +export const text1Style = css([ + colorStyle, + { + fontWeight: 500, + marginTop: '1.2rem', + }, +]); - textDecoration: 'underline', - ...theme.text.body06, - fontWeight: 600, +export const text2Style = css([ + colorStyle, + { + marginTop: '1.6rem', + }, +]); - color: theme.colors.gray_500, -}); +export const text3Style = css([ + colorStyle, + { + marginTop: '0.5rem', + }, +]); diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.tsx index fee47f946..3581cd78c 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.tsx @@ -1,12 +1,15 @@ import { Dispatch, SetStateAction } from 'react'; +import UploadIcon from '@/common/asset/svg/ic_upload_file.svg?react'; import Button from '@/common/component/Button/Button'; import Flex from '@/common/component/Flex/Flex'; import Text from '@/common/component/Text/Text'; import { boxStyle, - buttonStyle, + text1Style, + text2Style, + text3Style, } from '@/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd.style'; import useFile from '@/page/archiving/index/component/TimeBlockModal/hook/common/useFile'; @@ -33,27 +36,36 @@ const BlockAdd = ({ files, onFilesChange, setFileUrls, setUploadStatus }: BlockA direction: 'column', justify: 'center', align: 'center', - padding: '3.2rem 6.35rem', + padding: '3.2rem 0rem', width: '100%', }} css={boxStyle} onDragOver={handleDragOver} onDrop={(event) => handleDrop(event)}> - - - 업로드할 파일을 여기로 드래그 하세요 - - 또는 - - 하여 - - - 업로드할 파일을 선택하세요 + + + + + 업로드할 파일을 끌어다 놓으세요. + + JPEG, PNG, PDF, Word 형식의 파일을 업로드할 수 있습니다. + + + 최대 파일 크기는 50MB입니다. + + ); diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.style.ts b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.style.ts index 0991959a7..9570d6196 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.style.ts +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.style.ts @@ -2,16 +2,6 @@ import { css } from '@emotion/react'; import { theme } from '@/common/style/theme/theme'; -export const borderStyle = css({ - borderRadius: '8px', - border: `1px solid ${theme.colors.gray_300}`, - - width: '37.5rem', -}); - export const textStyle = css({ color: theme.colors.gray_800, - fontWeight: 500, - - maxWidth: '30rem', }); diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.tsx index bcd6c0d38..52b0db6c8 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.tsx @@ -1,32 +1,46 @@ import Delete from '@/common/asset/svg/ic_file_delete.svg?react'; +import File from '@/common/asset/svg/ic_file_preview.svg?react'; import Flex from '@/common/component/Flex/Flex'; -import Spinner from '@/common/component/Spinner/Spinner'; import Text from '@/common/component/Text/Text'; -import { - borderStyle, - textStyle, -} from '@/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.style'; +import { textStyle } from '@/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.style'; +import '@/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem.style'; interface BlockItemProps { title: string; + fileSize: string; + uploadedSize: string; onDelete: () => void; isUploading: boolean; } -const BlockItem = ({ title, onDelete, isUploading }: BlockItemProps) => { +const BlockItem = ({ title, fileSize, uploadedSize, onDelete }: BlockItemProps) => { + /* 추가해야 할 것 : 프로그래스바 ==> 서버로직 짤때 컴포넌트로 따로 빼서 적용할 것!*/ + return ( - - {title} - - {isUploading ? ( - - ) : ( - - )} + styles={{ + direction: 'row', + align: 'center', + justify: 'space-between', + padding: '0.7rem 0rem', + width: '100%', + }}> + + + + {title} + + {fileSize} 중 {uploadedSize} + + + + + ); }; diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts index 96e3fafc3..8d5ca02bf 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts @@ -5,14 +5,14 @@ import { theme } from '@/common/style/theme/theme'; export const scrollStyle = css({ display: 'flex', flexDirection: 'column', - maxHeight: '18rem', - width: '38.5rem', + maxHeight: '12rem', + width: '100%', - gap: '0.8rem', + gap: '1.2rem', position: 'relative', - paddingRight: '1rem', overflowY: 'auto', + overflowX: 'hidden', boxSizing: 'content-box', @@ -34,8 +34,6 @@ export const flexStyle = css({ flexDirection: 'column', justifyContent: 'space-between', alignItems: 'center', - height: '55.11rem', - paddingLeft: '6.8rem', - paddingRight: '6.8rem', - gap: '2.4rem', + width: '100%', + gap: '2rem', }); diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx index 98c50f9ae..8ccb5e0a7 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx @@ -3,6 +3,7 @@ import { useLocation } from 'react-router-dom'; import Button from '@/common/component/Button/Button'; import Flex from '@/common/component/Flex/Flex'; +import { Modal } from '@/common/component/Modal'; import BlockAdd from '@/page/archiving/index/component/TimeBlockModal/component/Upload/File/Add/BlockAdd'; import BlockItem from '@/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem'; @@ -105,37 +106,42 @@ const UploadModal = ({ isVisible }: UploadModalProps) => { }; return ( - - - - -
- {files.map((file) => ( - handleDelete(file.name)} - isUploading={!uploadStatus[file.name]} + <> + + + + + - ))} -
-
- -
+
+ {files.map((file) => ( + handleDelete(file.name)} + /* 임의의 값 넣었음! 추후 서버 로직 다시 짤때 바꿀것!!*/ + fileSize="2.4MB" + uploadedSize="0.2MB" + isUploading={!uploadStatus[file.name]} + /> + ))} +
+
+
+ + + ); }; diff --git a/src/page/archiving/index/component/TimeBlockModal/constant/iconBlock.tsx b/src/page/archiving/index/component/TimeBlockModal/constant/iconBlock.tsx index ea03ef68b..b1229a0e5 100644 --- a/src/page/archiving/index/component/TimeBlockModal/constant/iconBlock.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/constant/iconBlock.tsx @@ -1,40 +1,35 @@ import { ReactNode } from 'react'; -import Accounting from '@/common/asset/svg/ic_accounting.svg?react'; -import Event from '@/common/asset/svg/ic_event.svg?react'; -import Meeting from '@/common/asset/svg/ic_meeting.svg?react'; -import Notice from '@/common/asset/svg/ic_notice.svg?react'; -import Study from '@/common/asset/svg/ic_study.svg?react'; -import Task from '@/common/asset/svg/ic_task.svg?react'; +import Event from '@/common/asset/svg/ic_event_gray.svg?react'; +import Meeting from '@/common/asset/svg/ic_meeting_gray.svg?react'; +import Notice from '@/common/asset/svg/ic_notice_gray.svg?react'; +import Recruiting from '@/common/asset/svg/ic_recruiting_gray.svg?react'; +import Study from '@/common/asset/svg/ic_study_gray.svg?react'; -type BlockIconType = { +type BlockIcon = { name: string; icon: ReactNode; }; -export const BLOCK_ICON: BlockIconType[] = [ +export const BLOCK_ICON: BlockIcon[] = [ { name: 'MEETING', - icon: , + icon: , }, { - name: 'ACCOUNTING', - icon: , + name: 'EVENT', + icon: , }, { - name: 'TASK', - icon: , + name: 'STUDY', + icon: , }, { name: 'NOTICE', - icon: , - }, - { - name: 'STUDY', - icon: , + icon: , }, { - name: 'EVENT', - icon: , + name: 'RECRUITING', + icon: , }, ]; diff --git a/src/page/archiving/index/component/TimeBlockModal/hook/common/useDateRange.tsx b/src/page/archiving/index/component/TimeBlockModal/hook/common/useDateRange.tsx deleted file mode 100644 index 526747e8f..000000000 --- a/src/page/archiving/index/component/TimeBlockModal/hook/common/useDateRange.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { useState } from 'react'; - -import { ERROR } from '@/page/archiving/index/component/TimeBlockModal/constant/error'; -import { formatDateString, isValidDate, parseDate } from '@/page/archiving/index/component/TimeBlockModal/util/date'; - -const useDateRange = ( - initialStartDate = '', - initialEndDate = '', - onSetStartDate: (date: string) => void, - onSetEndDate: (date: string) => void, - onSetIsDateRangeValid: (isValid: boolean) => void -) => { - const [dates, setDates] = useState({ startDate: initialStartDate, endDate: initialEndDate }); - const [validation, setValidation] = useState({ - isStartDateValid: true, - isEndDateValid: true, - isStartDateError: false, - isEndDateError: false, - errorMessage: '', - }); - const [isDateRangeValid, setIsDateRangeValid] = useState(false); - - const handleChange = (dateType: 'startDate' | 'endDate', value: string, otherIsValid: boolean, isStart: boolean) => { - const input = value.replace(/\D/g, ''); - const formattedDate = input.length <= 8 ? formatDateString(input) : input; - setDates((prev) => { - const newDates = { ...prev, [dateType]: formattedDate }; - onSetStartDate(newDates.startDate); - onSetEndDate(newDates.endDate); - return newDates; - }); - - const isValid = input.length === 8 && isValidDate(formattedDate); - setValidation((prev) => ({ - ...prev, - [isStart ? 'isStartDateValid' : 'isEndDateValid']: isValid, - })); - - if (!input) { - setValidation((prev) => ({ - ...prev, - [isStart ? 'isStartDateError' : 'isEndDateError']: true, - errorMessage: isStart ? ERROR.START : ERROR.END, - })); - setIsDateRangeValid(false); - onSetIsDateRangeValid(false); - } else if (formattedDate.length === 10) { - if (!isValid || !otherIsValid) { - setValidation((prev) => ({ - ...prev, - [isStart ? 'isStartDateError' : 'isEndDateError']: true, - errorMessage: ERROR.OTHER, - })); - setIsDateRangeValid(false); - onSetIsDateRangeValid(false); - } else { - checkDateRange(formattedDate, dates.startDate, dates.endDate, isStart); - } - } else { - setValidation((prev) => ({ - ...prev, - [isStart ? 'isStartDateError' : 'isEndDateError']: false, - errorMessage: '', - })); - setIsDateRangeValid(false); - onSetIsDateRangeValid(false); - } - }; - - const checkDateRange = (formattedDate: string, startDate: string, endDate: string, isStart: boolean) => { - const start = parseDate(isStart ? formattedDate : startDate); - const end = parseDate(isStart ? endDate : formattedDate); - if (start && end) { - const isValidRange = start <= end; - setIsDateRangeValid(isValidRange); - onSetIsDateRangeValid(isValidRange); - if (!isValidRange) { - setValidation({ - isStartDateValid: true, - isEndDateValid: true, - isStartDateError: isStart, - isEndDateError: !isStart, - errorMessage: ERROR.OTHER, - }); - } else { - setValidation({ - isStartDateValid: true, - isEndDateValid: true, - isStartDateError: false, - isEndDateError: false, - errorMessage: '', - }); - } - } else { - setIsDateRangeValid(true); - onSetIsDateRangeValid(true); - setValidation({ - isStartDateValid: true, - isEndDateValid: true, - isStartDateError: false, - isEndDateError: false, - errorMessage: '', - }); - } - }; - - return { - dates, - validation, - isDateRangeValid, - handleChange, - }; -}; - -export default useDateRange; diff --git a/src/shared/util/modalFooter.tsx b/src/shared/util/modalFooter.tsx index 9de228303..fa52679e6 100644 --- a/src/shared/util/modalFooter.tsx +++ b/src/shared/util/modalFooter.tsx @@ -22,11 +22,14 @@ export const getFooterContent = ( disabled: !isButtonActive, }, ].filter(Boolean) as FooterButton[]; + + /* 디자인 확정시 추후 수정 필요 */ case 'create-block': return [ - { text: '저장', onClick: buttonClick, variant: 'primary' }, - { text: '취소', onClick: closeModal, variant: 'secondary' }, + { text: '취소', onClick: closeModal, variant: 'outline' }, + { text: '다음으로', onClick: buttonClick, variant: 'primary' }, ]; + case 'deleted': return [ { text: '취소', onClick: closeModal, variant: 'outline' }, diff --git a/src/shared/util/modalHeader.tsx b/src/shared/util/modalHeader.tsx index 8b8389666..2b2cb7c66 100644 --- a/src/shared/util/modalHeader.tsx +++ b/src/shared/util/modalHeader.tsx @@ -27,12 +27,15 @@ export const getHeaderContent = (contentType: string, step?: number, totalSteps? ? '우리 동아리 프로필에 표시할 이미지를 등록해주세요' : '이제 워크스페이스를 사용할 수 있습니다.', }; + + /* 디자인 확정시 추후 수정 필요 */ case 'create-block': return { icon: , - title: '타임블록 생성하기', - infoText: step === 1 ? '블록명을 입력해주세요' : '블록에 필요한 문서를 업로드해주세요', + title: '타임블록 생성', + infoText: step === 1 ? '타임라인에 생성할 블록 정보를 입력해주세요' : '타임라인에 업로드할 파일을 선택하세요', }; + case 'deleted': return { icon: , From f5817792effd8f468db1c8b58c6060236c3e59e7 Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Mon, 4 Nov 2024 21:25:41 +0900 Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=ED=8C=80=EC=9B=90=EC=B4=88?= =?UTF-8?q?=EB=8C=80=20=EB=B7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/asset/svg/ic_invite.svg | 3 + src/common/asset/svg/ic_invite_delete.svg | 3 + src/common/asset/svg/ic_invite_profile.svg | 5 ++ .../CommandButton/CommandButton.style.ts | 2 +- .../component/Modal/Footer/ModalFooter.tsx | 4 +- .../component/DeleteModal/DeletedModal.tsx | 4 +- .../InviteItem/InviteItem.style.ts | 7 ++ .../InviteModal/InviteItem/InviteItem.tsx | 39 +++++++++ .../InviteModal/InviteModal.style.ts | 48 +++++++++++ .../component/InviteModal/InviteModal.tsx | 84 +++++++++++++++++++ src/shared/component/Modal/ModalContainer.tsx | 3 + src/shared/util/modalFooter.tsx | 6 ++ src/shared/util/modalHeader.tsx | 7 ++ src/story/shared/NewModals.stories.tsx | 36 ++++++++ 14 files changed, 246 insertions(+), 5 deletions(-) create mode 100644 src/common/asset/svg/ic_invite.svg create mode 100644 src/common/asset/svg/ic_invite_delete.svg create mode 100644 src/common/asset/svg/ic_invite_profile.svg create mode 100644 src/shared/component/InviteModal/InviteItem/InviteItem.style.ts create mode 100644 src/shared/component/InviteModal/InviteItem/InviteItem.tsx create mode 100644 src/shared/component/InviteModal/InviteModal.style.ts create mode 100644 src/shared/component/InviteModal/InviteModal.tsx create mode 100644 src/story/shared/NewModals.stories.tsx diff --git a/src/common/asset/svg/ic_invite.svg b/src/common/asset/svg/ic_invite.svg new file mode 100644 index 000000000..a34cfa7b8 --- /dev/null +++ b/src/common/asset/svg/ic_invite.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/asset/svg/ic_invite_delete.svg b/src/common/asset/svg/ic_invite_delete.svg new file mode 100644 index 000000000..a6c49cb06 --- /dev/null +++ b/src/common/asset/svg/ic_invite_delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/asset/svg/ic_invite_profile.svg b/src/common/asset/svg/ic_invite_profile.svg new file mode 100644 index 000000000..b9b54f8c8 --- /dev/null +++ b/src/common/asset/svg/ic_invite_profile.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/common/component/CommandButton/CommandButton.style.ts b/src/common/component/CommandButton/CommandButton.style.ts index c71714495..13f15bc1d 100644 --- a/src/common/component/CommandButton/CommandButton.style.ts +++ b/src/common/component/CommandButton/CommandButton.style.ts @@ -72,7 +72,7 @@ export const childrenStyle = css({ display: 'flex', alignItems: 'center', - padding: '0.4rem', + //padding: '0.4rem', gap: '0.4rem', }); diff --git a/src/common/component/Modal/Footer/ModalFooter.tsx b/src/common/component/Modal/Footer/ModalFooter.tsx index 011dcc7b7..637dff5f0 100644 --- a/src/common/component/Modal/Footer/ModalFooter.tsx +++ b/src/common/component/Modal/Footer/ModalFooter.tsx @@ -5,7 +5,7 @@ import Button from '../../Button/Button'; import Flex from '../../Flex/Flex'; interface ModalFooterProps { - step: number; + step?: number; buttonClick?: () => void; isButtonActive?: boolean; } @@ -13,7 +13,7 @@ interface ModalFooterProps { const ModalFooter = ({ step, buttonClick, isButtonActive }: ModalFooterProps) => { const contentType = useModalContentType(); const closeModal = useCloseModal(); - const footerButtons = getFooterContent(contentType!, step, buttonClick, closeModal, isButtonActive); + const footerButtons = getFooterContent(contentType!, step!, buttonClick, closeModal, isButtonActive); return ( diff --git a/src/shared/component/DeleteModal/DeletedModal.tsx b/src/shared/component/DeleteModal/DeletedModal.tsx index 6d390011a..cd3d7ff0b 100644 --- a/src/shared/component/DeleteModal/DeletedModal.tsx +++ b/src/shared/component/DeleteModal/DeletedModal.tsx @@ -24,7 +24,7 @@ const DeletedModal = () => { return ( <> - + @@ -35,7 +35,7 @@ const DeletedModal = () => { - + ); }; diff --git a/src/shared/component/InviteModal/InviteItem/InviteItem.style.ts b/src/shared/component/InviteModal/InviteItem/InviteItem.style.ts new file mode 100644 index 000000000..9570d6196 --- /dev/null +++ b/src/shared/component/InviteModal/InviteItem/InviteItem.style.ts @@ -0,0 +1,7 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const textStyle = css({ + color: theme.colors.gray_800, +}); diff --git a/src/shared/component/InviteModal/InviteItem/InviteItem.tsx b/src/shared/component/InviteModal/InviteItem/InviteItem.tsx new file mode 100644 index 000000000..ac80ce54b --- /dev/null +++ b/src/shared/component/InviteModal/InviteItem/InviteItem.tsx @@ -0,0 +1,39 @@ +import Delete from '@/common/asset/svg/ic_invite_delete.svg?react'; +import InviteProfile from '@/common/asset/svg/ic_invite_profile.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Text from '@/common/component/Text/Text'; + +import { textStyle } from './InviteItem.style'; + +interface InviteItemProps { + title: string; + onDelete: () => void; +} + +const InviteItem = ({ title, onDelete }: InviteItemProps) => { + return ( + + + + + {title} + + + + + + ); +}; + +export default InviteItem; diff --git a/src/shared/component/InviteModal/InviteModal.style.ts b/src/shared/component/InviteModal/InviteModal.style.ts new file mode 100644 index 000000000..8c1f9f3d8 --- /dev/null +++ b/src/shared/component/InviteModal/InviteModal.style.ts @@ -0,0 +1,48 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const inputWrapperStyle = css({ + maxWidth: '24.7rem', + height: '4rem', +}); + +export const textStyle = css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + + color: theme.colors.gray_800, + width: '100%', + height: '100%', + textAlign: 'center', +}); + +export const scrollStyle = css({ + display: 'flex', + flexDirection: 'column', + height: '30.4rem', + width: '100%', + + gap: '1.6rem', + position: 'relative', + + overflowY: 'auto', + overflowX: 'hidden', + + boxSizing: 'content-box', + + '&::-webkit-scrollbar': { + width: '0.4rem', + height: '5rem', + }, + + '&::-webkit-scrollbar-thumb': { + backgroundColor: theme.colors.gray_300, + borderRadius: '4px', + }, + + '&::-webkit-scrollbar-track': { + backgroundColor: 'transparent', + }, +}); diff --git a/src/shared/component/InviteModal/InviteModal.tsx b/src/shared/component/InviteModal/InviteModal.tsx new file mode 100644 index 000000000..77d003177 --- /dev/null +++ b/src/shared/component/InviteModal/InviteModal.tsx @@ -0,0 +1,84 @@ +import { useState } from 'react'; + +import CommandButton from '@/common/component/CommandButton/CommandButton'; +import Flex from '@/common/component/Flex/Flex'; +import Input from '@/common/component/Input/Input'; +import { Modal } from '@/common/component/Modal'; +import Text from '@/common/component/Text/Text'; + +import { useCloseModal } from '@/shared/store/modal'; + +import InviteItem from './InviteItem/InviteItem'; +import { inputWrapperStyle, scrollStyle, textStyle } from './InviteModal.style'; + +const InviteModal = () => { + const [inputValue, setInputValue] = useState(''); + const [inviteList, setInviteList] = useState([]); + + const closeModal = useCloseModal(); + + const isButtonActive = inputValue.trim().length > 0; + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + }; + + const handleAddInvite = () => { + if (inputValue.trim() && !inviteList.includes(inputValue)) { + setInviteList([...inviteList, inputValue.trim()]); + setInputValue(''); + } + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && isButtonActive) { + handleAddInvite(); + } + }; + + const handleDeleteInvite = (email: string) => { + setInviteList(inviteList.filter((item) => item !== email)); + }; + + return ( + <> + + + + + + + 추가 + + + +
+ {inviteList.length > 0 ? ( + inviteList.map((email) => ( + handleDeleteInvite(email)} /> + )) + ) : ( + + 초대된 팀원이 없습니다. + + )} +
+
+
+ + + ); +}; + +export default InviteModal; diff --git a/src/shared/component/Modal/ModalContainer.tsx b/src/shared/component/Modal/ModalContainer.tsx index ef0dfc2a3..a05fecfca 100644 --- a/src/shared/component/Modal/ModalContainer.tsx +++ b/src/shared/component/Modal/ModalContainer.tsx @@ -8,6 +8,7 @@ import { WorkSpaceProvider } from '@/shared/hook/common/useWorkSpaceContext'; import { useCloseModal, useModalContentType, useModalIsOpen } from '@/shared/store/modal'; import DeletedModal from '../DeleteModal/DeletedModal'; +import InviteModal from '../InviteModal/InviteModal'; const ModalContainer = () => { const isOpen = useModalIsOpen(); @@ -30,6 +31,8 @@ const ModalContainer = () => { ); case 'deleted': return ; + case 'invite': + return ; default: return null; } diff --git a/src/shared/util/modalFooter.tsx b/src/shared/util/modalFooter.tsx index fa52679e6..5fe57b252 100644 --- a/src/shared/util/modalFooter.tsx +++ b/src/shared/util/modalFooter.tsx @@ -35,6 +35,12 @@ export const getFooterContent = ( { text: '취소', onClick: closeModal, variant: 'outline' }, { text: '삭제', onClick: buttonClick, variant: 'primary' }, ]; + + case 'invite': + return [ + { text: '취소', onClick: closeModal, variant: 'outline' }, + { text: '초대', onClick: buttonClick, variant: 'primary' }, + ]; default: return []; } diff --git a/src/shared/util/modalHeader.tsx b/src/shared/util/modalHeader.tsx index 2b2cb7c66..8f80fee3c 100644 --- a/src/shared/util/modalHeader.tsx +++ b/src/shared/util/modalHeader.tsx @@ -1,4 +1,5 @@ import BlockIcon from '@/common/asset/svg/ic_block_create.svg?react'; +import InviteIcon from '@/common/asset/svg/ic_invite.svg?react'; import WarningIcon from '@/common/asset/svg/ic_warning.svg?react'; import SuccessIcon from '@/common/asset/svg/ic_workspace_success.svg?react'; @@ -42,6 +43,12 @@ export const getHeaderContent = (contentType: string, step?: number, totalSteps? title: '주의!', infoText: '삭제할 항목을 확인해주세요.', }; + case 'invite': + return { + icon: , + title: '팀원 초대', + infoText: '워크스페이스에 팀원을 초대할 수 있습니다.', + }; default: return { icon: null, title: '', infoText: '' }; } diff --git a/src/story/shared/NewModals.stories.tsx b/src/story/shared/NewModals.stories.tsx new file mode 100644 index 000000000..9a6ff7d93 --- /dev/null +++ b/src/story/shared/NewModals.stories.tsx @@ -0,0 +1,36 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import ModalContainer from '@/shared/component/Modal/ModalContainer'; +import { useOpenModal } from '@/shared/store/modal'; + +const meta: Meta = { + title: 'Shared/Modal/ModalsTest', + component: ModalContainer, + parameters: { + layout: 'centered', + }, + args: { + isOpen: false, + }, + argTypes: { + children: { + control: false, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const ModalsTest: Story = { + render: () => { + const openModal = useOpenModal(); + + return ( + <> + + + + ); + }, +}; From ee695ef75cd2c65d283648d81078803a7f23dbc2 Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Mon, 4 Nov 2024 22:33:14 +0900 Subject: [PATCH 08/13] =?UTF-8?q?feat:=20=ED=8C=80=EC=9B=90=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=AA=A8=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/asset/svg/ic_member_tag.svg | 3 + ...nviteItem.style.ts => MemberItem.style.ts} | 0 .../{InviteItem.tsx => MemberItem.tsx} | 8 +-- .../component/InviteModal/InviteModal.tsx | 10 +-- .../MemberTagItem/MemberTagItem.style.ts | 7 ++ .../MemberTagItem/MemberTagItem.tsx | 52 ++++++++++++++ .../MemberTagModal/MemberTagModal.style.ts | 0 .../MemberTagModal/MemberTagModal.tsx | 67 +++++++++++++++++++ src/shared/component/Modal/ModalContainer.tsx | 3 + src/shared/constant/index.ts | 13 ++++ src/shared/util/modalFooter.tsx | 7 +- src/shared/util/modalHeader.tsx | 7 ++ src/story/shared/NewModals.stories.tsx | 1 + 13 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 src/common/asset/svg/ic_member_tag.svg rename src/shared/component/InviteModal/InviteItem/{InviteItem.style.ts => MemberItem.style.ts} (100%) rename src/shared/component/InviteModal/InviteItem/{InviteItem.tsx => MemberItem.tsx} (83%) create mode 100644 src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts create mode 100644 src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx create mode 100644 src/shared/component/MemberTagModal/MemberTagModal.style.ts create mode 100644 src/shared/component/MemberTagModal/MemberTagModal.tsx diff --git a/src/common/asset/svg/ic_member_tag.svg b/src/common/asset/svg/ic_member_tag.svg new file mode 100644 index 000000000..0bf5a532b --- /dev/null +++ b/src/common/asset/svg/ic_member_tag.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/shared/component/InviteModal/InviteItem/InviteItem.style.ts b/src/shared/component/InviteModal/InviteItem/MemberItem.style.ts similarity index 100% rename from src/shared/component/InviteModal/InviteItem/InviteItem.style.ts rename to src/shared/component/InviteModal/InviteItem/MemberItem.style.ts diff --git a/src/shared/component/InviteModal/InviteItem/InviteItem.tsx b/src/shared/component/InviteModal/InviteItem/MemberItem.tsx similarity index 83% rename from src/shared/component/InviteModal/InviteItem/InviteItem.tsx rename to src/shared/component/InviteModal/InviteItem/MemberItem.tsx index ac80ce54b..c7ecde3a2 100644 --- a/src/shared/component/InviteModal/InviteItem/InviteItem.tsx +++ b/src/shared/component/InviteModal/InviteItem/MemberItem.tsx @@ -3,14 +3,14 @@ import InviteProfile from '@/common/asset/svg/ic_invite_profile.svg?react'; import Flex from '@/common/component/Flex/Flex'; import Text from '@/common/component/Text/Text'; -import { textStyle } from './InviteItem.style'; +import { textStyle } from './MemberItem.style'; -interface InviteItemProps { +interface MemberItemProps { title: string; onDelete: () => void; } -const InviteItem = ({ title, onDelete }: InviteItemProps) => { +const MemberItem = ({ title, onDelete }: MemberItemProps) => { return ( { ); }; -export default InviteItem; +export default MemberItem; diff --git a/src/shared/component/InviteModal/InviteModal.tsx b/src/shared/component/InviteModal/InviteModal.tsx index 77d003177..6665095bc 100644 --- a/src/shared/component/InviteModal/InviteModal.tsx +++ b/src/shared/component/InviteModal/InviteModal.tsx @@ -8,7 +8,7 @@ import Text from '@/common/component/Text/Text'; import { useCloseModal } from '@/shared/store/modal'; -import InviteItem from './InviteItem/InviteItem'; +import MemberItem from './InviteItem/MemberItem'; import { inputWrapperStyle, scrollStyle, textStyle } from './InviteModal.style'; const InviteModal = () => { @@ -30,12 +30,6 @@ const InviteModal = () => { } }; - const handleKeyPress = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && isButtonActive) { - handleAddInvite(); - } - }; - const handleDeleteInvite = (email: string) => { setInviteList(inviteList.filter((item) => item !== email)); }; @@ -66,7 +60,7 @@ const InviteModal = () => {
{inviteList.length > 0 ? ( inviteList.map((email) => ( - handleDeleteInvite(email)} /> + handleDeleteInvite(email)} /> )) ) : ( diff --git a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts new file mode 100644 index 000000000..ba216db2c --- /dev/null +++ b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts @@ -0,0 +1,7 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const emailStyle = css({ + color: theme.colors.gray_800, +}); diff --git a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx new file mode 100644 index 000000000..970cf4e75 --- /dev/null +++ b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx @@ -0,0 +1,52 @@ +import Delete from '@/common/asset/svg/ic_invite_delete.svg?react'; +import Profile from '@/common/asset/svg/ic_invite_profile.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Text from '@/common/component/Text/Text'; + +import { emailStyle } from './MemberTagItem.style'; + +interface MemberTagItemProps { + name: string; + email: string; + profileImg?: string; + onDelete: () => void; +} + +const MemberTagItem = ({ name, email, profileImg, onDelete }: MemberTagItemProps) => { + return ( + + + {profileImg ? ( + {`${name} + ) : ( + + )} + + {name} + + {email} + + + + + + + ); +}; + +export default MemberTagItem; diff --git a/src/shared/component/MemberTagModal/MemberTagModal.style.ts b/src/shared/component/MemberTagModal/MemberTagModal.style.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/shared/component/MemberTagModal/MemberTagModal.tsx b/src/shared/component/MemberTagModal/MemberTagModal.tsx new file mode 100644 index 000000000..5c6b0c84c --- /dev/null +++ b/src/shared/component/MemberTagModal/MemberTagModal.tsx @@ -0,0 +1,67 @@ +import { useState } from 'react'; + +import SearchIcon from '@/common/asset/svg/ic_search.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Input from '@/common/component/Input/Input'; +import { Modal } from '@/common/component/Modal'; +import Text from '@/common/component/Text/Text'; + +import { MEMBER_DATA } from '@/shared/constant'; +import { useCloseModal } from '@/shared/store/modal'; + +import { scrollStyle, textStyle } from '../InviteModal/InviteModal.style'; +import MemberTagItem from './MemberTagItem/MemberTagItem'; + +const MemberTagModal = () => { + const [inputValue, setInputValue] = useState(''); + const [memberTagList, setMemberTagList] = useState([]); + + const closeModal = useCloseModal(); + + /* 드롭다운 검색 기능 구현할때 버튼활성화 조건 변경하기 */ + const isButtonActive = inputValue.trim().length > 0; + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + }; + + const handleDeleteMemberTag = (email: string) => { + setMemberTagList(memberTagList.filter((item) => item !== email)); + }; + + return ( + <> + + + + } + value={inputValue} + onChange={handleInputChange} + /> +
+ {MEMBER_DATA.length > 0 ? ( + MEMBER_DATA.map((data) => ( + handleDeleteMemberTag(data.email)} + /> + )) + ) : ( + + 태그된 팀원이 없습니다. + + )} +
+
+
+ + + ); +}; + +export default MemberTagModal; diff --git a/src/shared/component/Modal/ModalContainer.tsx b/src/shared/component/Modal/ModalContainer.tsx index a05fecfca..1ae80bae5 100644 --- a/src/shared/component/Modal/ModalContainer.tsx +++ b/src/shared/component/Modal/ModalContainer.tsx @@ -9,6 +9,7 @@ import { useCloseModal, useModalContentType, useModalIsOpen } from '@/shared/sto import DeletedModal from '../DeleteModal/DeletedModal'; import InviteModal from '../InviteModal/InviteModal'; +import MemberTagModal from '../MemberTagModal/MemberTagModal'; const ModalContainer = () => { const isOpen = useModalIsOpen(); @@ -33,6 +34,8 @@ const ModalContainer = () => { return ; case 'invite': return ; + case 'member-tag': + return ; default: return null; } diff --git a/src/shared/constant/index.ts b/src/shared/constant/index.ts index 1cfebf6b2..a56a7b10a 100644 --- a/src/shared/constant/index.ts +++ b/src/shared/constant/index.ts @@ -30,3 +30,16 @@ export const DELETED_TITLE = { } as const; export const DELETED_DETAIL = '휴지통에서 지워진 파일은 영구삭제되며 되돌릴 수 없습니다' as const; + +export const MEMBER_DATA = [ + { + name: '이채원', + email: 'cindy1769@daum.net', + profileUrl: 'https://github.com/user-attachments/assets/a9c876cd-9d07-49db-94f1-353e5c4a5ee3', + }, + { + name: '이채원2', + email: 'cindy1769@naver.com', + profileUrl: 'https://github.com/user-attachments/assets/a9c876cd-9d07-49db-94f1-353e5c4a5ee3', + }, +] as const; diff --git a/src/shared/util/modalFooter.tsx b/src/shared/util/modalFooter.tsx index 5fe57b252..53c11b15e 100644 --- a/src/shared/util/modalFooter.tsx +++ b/src/shared/util/modalFooter.tsx @@ -39,7 +39,12 @@ export const getFooterContent = ( case 'invite': return [ { text: '취소', onClick: closeModal, variant: 'outline' }, - { text: '초대', onClick: buttonClick, variant: 'primary' }, + { text: '초대', onClick: buttonClick, variant: 'primary', disabled: !isButtonActive }, + ]; + case 'member-tag': + return [ + { text: '취소', onClick: closeModal, variant: 'outline' }, + { text: '완료', onClick: buttonClick, variant: 'primary' }, ]; default: return []; diff --git a/src/shared/util/modalHeader.tsx b/src/shared/util/modalHeader.tsx index 8f80fee3c..ed2073b96 100644 --- a/src/shared/util/modalHeader.tsx +++ b/src/shared/util/modalHeader.tsx @@ -1,5 +1,6 @@ import BlockIcon from '@/common/asset/svg/ic_block_create.svg?react'; import InviteIcon from '@/common/asset/svg/ic_invite.svg?react'; +import MemberTagIcon from '@/common/asset/svg/ic_member_tag.svg?react'; import WarningIcon from '@/common/asset/svg/ic_warning.svg?react'; import SuccessIcon from '@/common/asset/svg/ic_workspace_success.svg?react'; @@ -49,6 +50,12 @@ export const getHeaderContent = (contentType: string, step?: number, totalSteps? title: '팀원 초대', infoText: '워크스페이스에 팀원을 초대할 수 있습니다.', }; + case 'member-tag': + return { + icon: , + title: '팀원 태그', + infoText: '관련된 팀원을 태그할 수 있습니다.', + }; default: return { icon: null, title: '', infoText: '' }; } diff --git a/src/story/shared/NewModals.stories.tsx b/src/story/shared/NewModals.stories.tsx index 9a6ff7d93..fef10a509 100644 --- a/src/story/shared/NewModals.stories.tsx +++ b/src/story/shared/NewModals.stories.tsx @@ -29,6 +29,7 @@ export const ModalsTest: Story = { return ( <> + ); From 8ff0005cd6926ca4066077e19ce39f4854197c9e Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Tue, 5 Nov 2024 03:52:55 +0900 Subject: [PATCH 09/13] =?UTF-8?q?feat:=20=ED=99=9C=EB=8F=99=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=B7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/asset/svg/ic_activity_tag.svg | 3 + src/common/asset/svg/ic_calendar_tag.svg | 10 +++ .../ActivityTagItem/ActivityTagItem.style.ts | 59 ++++++++++++++++ .../ActivityTagItem/ActivityTagItem.tsx | 57 +++++++++++++++ .../ActivityTagModal/ActivityTagModal.tsx | 69 +++++++++++++++++++ .../MemberTagModal/MemberTagModal.style.ts | 0 .../MemberTagModal/MemberTagModal.tsx | 10 +-- src/shared/component/Modal/ModalContainer.tsx | 3 + src/shared/constant/icon.tsx | 35 ++++++++++ src/shared/constant/index.ts | 24 +++++++ src/shared/util/modalFooter.tsx | 5 ++ src/shared/util/modalHeader.tsx | 7 ++ src/story/shared/NewModals.stories.tsx | 1 + 13 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 src/common/asset/svg/ic_activity_tag.svg create mode 100644 src/common/asset/svg/ic_calendar_tag.svg create mode 100644 src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.style.ts create mode 100644 src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx create mode 100644 src/shared/component/ActivityTagModal/ActivityTagModal.tsx delete mode 100644 src/shared/component/MemberTagModal/MemberTagModal.style.ts create mode 100644 src/shared/constant/icon.tsx diff --git a/src/common/asset/svg/ic_activity_tag.svg b/src/common/asset/svg/ic_activity_tag.svg new file mode 100644 index 000000000..0bf5a532b --- /dev/null +++ b/src/common/asset/svg/ic_activity_tag.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/asset/svg/ic_calendar_tag.svg b/src/common/asset/svg/ic_calendar_tag.svg new file mode 100644 index 000000000..b2406f936 --- /dev/null +++ b/src/common/asset/svg/ic_calendar_tag.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.style.ts b/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.style.ts new file mode 100644 index 000000000..df9c1bbbe --- /dev/null +++ b/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.style.ts @@ -0,0 +1,59 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const iconBackStyle = css({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + + width: '4rem', + height: '4rem', + + borderRadius: '50%', + backgroundColor: theme.colors.gray_100, +}); + +export const textStyle = css({ + color: theme.colors.gray_800, +}); + +export const tagStyle = (backgroundColor: string) => + css({ + padding: '0.4rem 0.8rem', + borderRadius: '10px', + fontWeight: 500, + + backgroundColor: (() => { + switch (backgroundColor) { + case '#D3EFFA': + return theme.colors.sky_100; + case '#F8E1F5': + return theme.colors.pink_100; + case '#F8E2CB': + return theme.colors.yellow_100; + case '#DCD8FA': + return theme.colors.purple_100; + case '#C4F2E5': + return theme.colors.green_100; + default: + return theme.colors.gray_100; + } + })(), + color: (() => { + switch (backgroundColor) { + case '#D3EFFA': + return theme.colors.sky_200; + case '#F8E1F5': + return theme.colors.pink_200; + case '#F8E2CB': + return theme.colors.yellow_200; + case '#DCD8FA': + return theme.colors.purple_200; + case '#C4F2E5': + return theme.colors.green_200; + default: + return theme.colors.gray_200; + } + })(), + }); diff --git a/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx b/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx new file mode 100644 index 000000000..a82fb7967 --- /dev/null +++ b/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx @@ -0,0 +1,57 @@ +import CalenderIcon from '@/common/asset/svg/ic_calendar_tag.svg?react'; +import Delete from '@/common/asset/svg/ic_invite_delete.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Text from '@/common/component/Text/Text'; + +import { TAG_ICON } from '@/shared/constant/icon'; + +import { iconBackStyle, tagStyle, textStyle } from './ActivityTagItem.style'; + +interface ActivityTagItemProps { + title: string; + date: string; + tag: string; + color: string; + onDelete: () => void; +} + +const ActivityTagItem = ({ title, date, tag, color, onDelete }: ActivityTagItemProps) => { + const icon = TAG_ICON.find((iconItem) => iconItem.name === tag)?.icon; + + return ( + + +
{icon}
+ + {title} + + + + {date} + + + +
+ + + + {tag.toLowerCase()} + + + +
+ ); +}; + +export default ActivityTagItem; diff --git a/src/shared/component/ActivityTagModal/ActivityTagModal.tsx b/src/shared/component/ActivityTagModal/ActivityTagModal.tsx new file mode 100644 index 000000000..59d9b7805 --- /dev/null +++ b/src/shared/component/ActivityTagModal/ActivityTagModal.tsx @@ -0,0 +1,69 @@ +import { useState } from 'react'; + +import SearchIcon from '@/common/asset/svg/ic_search.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Input from '@/common/component/Input/Input'; +import { Modal } from '@/common/component/Modal'; +import Text from '@/common/component/Text/Text'; + +import { ACITIVITY_TAG_DATA } from '@/shared/constant'; +import { useCloseModal } from '@/shared/store/modal'; + +import { scrollStyle, textStyle } from '../InviteModal/InviteModal.style'; +import ActivityTagItem from './ActivityTagItem/ActivityTagItem'; + +const ActivityTagModal = () => { + const [inputValue, setInputValue] = useState(''); + + const [activityTags, setActivityTags] = useState(() => [...ACITIVITY_TAG_DATA]); + + const closeModal = useCloseModal(); + + /* 드롭다운 검색 기능 추가할때 버튼활성화 조건 바꾸기! */ + const isButtonActive = inputValue.trim().length > 0; + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + }; + + const handleDeleteActivityTag = (id: number) => { + setActivityTags((prevTags) => prevTags.filter((tag) => tag.id !== id)); + }; + + return ( + <> + + + + } + value={inputValue} + onChange={handleInputChange} + /> +
+ {activityTags.length > 0 ? ( + activityTags.map((data) => ( + handleDeleteActivityTag(data.id)} + /> + )) + ) : ( + + 태그된 활동이 없습니다. + + )} +
+
+
+ + + ); +}; + +export default ActivityTagModal; diff --git a/src/shared/component/MemberTagModal/MemberTagModal.style.ts b/src/shared/component/MemberTagModal/MemberTagModal.style.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/shared/component/MemberTagModal/MemberTagModal.tsx b/src/shared/component/MemberTagModal/MemberTagModal.tsx index 5c6b0c84c..6465b76a1 100644 --- a/src/shared/component/MemberTagModal/MemberTagModal.tsx +++ b/src/shared/component/MemberTagModal/MemberTagModal.tsx @@ -14,7 +14,7 @@ import MemberTagItem from './MemberTagItem/MemberTagItem'; const MemberTagModal = () => { const [inputValue, setInputValue] = useState(''); - const [memberTagList, setMemberTagList] = useState([]); + const [memberTagList, setMemberTagList] = useState(MEMBER_DATA.map((member) => ({ ...member }))); const closeModal = useCloseModal(); @@ -26,7 +26,7 @@ const MemberTagModal = () => { }; const handleDeleteMemberTag = (email: string) => { - setMemberTagList(memberTagList.filter((item) => item !== email)); + setMemberTagList(memberTagList.filter((item) => item.email !== email)); }; return ( @@ -41,10 +41,10 @@ const MemberTagModal = () => { onChange={handleInputChange} />
- {MEMBER_DATA.length > 0 ? ( - MEMBER_DATA.map((data) => ( + {memberTagList.length > 0 ? ( + memberTagList.map((data) => ( { return ; case 'member-tag': return ; + case 'activity-tag': + return ; default: return null; } diff --git a/src/shared/constant/icon.tsx b/src/shared/constant/icon.tsx new file mode 100644 index 000000000..77ab05ff9 --- /dev/null +++ b/src/shared/constant/icon.tsx @@ -0,0 +1,35 @@ +import { ReactNode } from 'react'; + +import Event from '@/common/asset/svg/ic_event_gray.svg?react'; +import Meeting from '@/common/asset/svg/ic_meeting_gray.svg?react'; +import Notice from '@/common/asset/svg/ic_notice_gray.svg?react'; +import Recruiting from '@/common/asset/svg/ic_recruiting_gray.svg?react'; +import Study from '@/common/asset/svg/ic_study_gray.svg?react'; + +type TAGIcon = { + name: string; + icon: ReactNode; +}; + +export const TAG_ICON: TAGIcon[] = [ + { + name: 'MEETING', + icon: , + }, + { + name: 'EVENT', + icon: , + }, + { + name: 'STUDY', + icon: , + }, + { + name: 'NOTICE', + icon: , + }, + { + name: 'RECRUITING', + icon: , + }, +]; diff --git a/src/shared/constant/index.ts b/src/shared/constant/index.ts index a56a7b10a..0f16c7dcf 100644 --- a/src/shared/constant/index.ts +++ b/src/shared/constant/index.ts @@ -43,3 +43,27 @@ export const MEMBER_DATA = [ profileUrl: 'https://github.com/user-attachments/assets/a9c876cd-9d07-49db-94f1-353e5c4a5ee3', }, ] as const; + +export const ACITIVITY_TAG_DATA = [ + { + id: 1, + tag: 'MEETING', + title: 'UX 스터디', + date: '2024.09.25', + color: '#D3EFFA', + }, + { + id: 2, + tag: 'STUDY', + title: 'UX 스터디', + date: '2024.09.25', + color: '#F8E1F5', + }, + { + id: 3, + tag: 'NOTICE', + title: 'UX 스터디', + date: '2024.09.25', + color: '#C4F2E5', + }, +] as const; diff --git a/src/shared/util/modalFooter.tsx b/src/shared/util/modalFooter.tsx index 53c11b15e..11d4d82cd 100644 --- a/src/shared/util/modalFooter.tsx +++ b/src/shared/util/modalFooter.tsx @@ -46,6 +46,11 @@ export const getFooterContent = ( { text: '취소', onClick: closeModal, variant: 'outline' }, { text: '완료', onClick: buttonClick, variant: 'primary' }, ]; + case 'activity-tag': + return [ + { text: '취소', onClick: closeModal, variant: 'outline' }, + { text: '완료', onClick: buttonClick, variant: 'primary' }, + ]; default: return []; } diff --git a/src/shared/util/modalHeader.tsx b/src/shared/util/modalHeader.tsx index ed2073b96..a3d08f17b 100644 --- a/src/shared/util/modalHeader.tsx +++ b/src/shared/util/modalHeader.tsx @@ -1,3 +1,4 @@ +import ActivityTagIcon from '@/common/asset/svg/ic_activity_tag.svg?react'; import BlockIcon from '@/common/asset/svg/ic_block_create.svg?react'; import InviteIcon from '@/common/asset/svg/ic_invite.svg?react'; import MemberTagIcon from '@/common/asset/svg/ic_member_tag.svg?react'; @@ -56,6 +57,12 @@ export const getHeaderContent = (contentType: string, step?: number, totalSteps? title: '팀원 태그', infoText: '관련된 팀원을 태그할 수 있습니다.', }; + case 'activity-tag': + return { + icon: , + title: '활동 태그', + infoText: '타임라인에 저장된 활동을 태그할 수 있습니다.', + }; default: return { icon: null, title: '', infoText: '' }; } diff --git a/src/story/shared/NewModals.stories.tsx b/src/story/shared/NewModals.stories.tsx index fef10a509..170dbfbb1 100644 --- a/src/story/shared/NewModals.stories.tsx +++ b/src/story/shared/NewModals.stories.tsx @@ -30,6 +30,7 @@ export const ModalsTest: Story = { <> + ); From 65e6bf9235b443c1a86a39f1ed96a4589f43a7e0 Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Tue, 5 Nov 2024 04:25:59 +0900 Subject: [PATCH 10/13] =?UTF-8?q?fix:=20css=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/component/Modal/Body/ModalBody.tsx | 2 +- src/common/component/Modal/Footer/ModalFooter.tsx | 6 +++--- src/common/component/Modal/Header/ModalHeader.style.ts | 5 +++-- src/common/component/Modal/Header/ModalHeader.tsx | 8 ++++---- src/common/component/Modal/Wrapper/ModalWrapper.style.ts | 8 +++++++- src/common/component/Modal/index.tsx | 8 ++++---- .../TimeBlockModal/component/Block/BlockModal.tsx | 7 +------ .../TimeBlockModal/component/Box/BlockBox.style.ts | 0 .../TimeBlockModal/component/Upload/UploadModal.tsx | 2 -- .../ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx | 8 ++++++-- .../component/ActivityTagModal/ActivityTagModal.tsx | 5 ++--- src/shared/component/DeleteModal/DeletedModal.tsx | 3 +-- .../component/InviteModal/InviteItem/MemberItem.tsx | 2 +- src/shared/component/InviteModal/InviteModal.style.ts | 4 +++- src/shared/component/InviteModal/InviteModal.tsx | 5 ++--- .../MemberTagModal/MemberTagItem/MemberTagItem.style.ts | 7 ------- .../MemberTagModal/MemberTagItem/MemberTagItem.tsx | 4 ++-- src/shared/component/MemberTagModal/MemberTagModal.tsx | 5 ++--- src/shared/component/Modal/ModalContainer.tsx | 9 ++++----- .../WorkSpaceModal/category/WorkSpaceCategory.tsx | 4 ---- .../WorkSpaceModal/complete/WorkSpaceComplete.tsx | 3 --- .../component/WorkSpaceModal/image/WorkSpaceImage.tsx | 5 ----- src/shared/util/modalFooter.tsx | 4 ---- 23 files changed, 46 insertions(+), 68 deletions(-) delete mode 100644 src/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox.style.ts delete mode 100644 src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts diff --git a/src/common/component/Modal/Body/ModalBody.tsx b/src/common/component/Modal/Body/ModalBody.tsx index 8afee368e..8811a06f5 100644 --- a/src/common/component/Modal/Body/ModalBody.tsx +++ b/src/common/component/Modal/Body/ModalBody.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; -import Flex from '../../Flex/Flex'; +import Flex from '@/common/component/Flex/Flex'; interface ModalBodyProps { children: ReactNode; diff --git a/src/common/component/Modal/Footer/ModalFooter.tsx b/src/common/component/Modal/Footer/ModalFooter.tsx index 637dff5f0..8e3dc96cc 100644 --- a/src/common/component/Modal/Footer/ModalFooter.tsx +++ b/src/common/component/Modal/Footer/ModalFooter.tsx @@ -1,9 +1,9 @@ +import Button from '@/common/component/Button/Button'; +import Flex from '@/common/component/Flex/Flex'; + import { useCloseModal, useModalContentType } from '@/shared/store/modal'; import { getFooterContent } from '@/shared/util/modalFooter'; -import Button from '../../Button/Button'; -import Flex from '../../Flex/Flex'; - interface ModalFooterProps { step?: number; buttonClick?: () => void; diff --git a/src/common/component/Modal/Header/ModalHeader.style.ts b/src/common/component/Modal/Header/ModalHeader.style.ts index 66bcac938..f83d4af51 100644 --- a/src/common/component/Modal/Header/ModalHeader.style.ts +++ b/src/common/component/Modal/Header/ModalHeader.style.ts @@ -11,10 +11,11 @@ export const iconTextStyle = css({ alignItems: 'center', justifyContent: 'center', - color: theme.colors.gray_800, - fontWeight: 500, width: '4rem', height: '4rem', + fontWeight: 500, + color: theme.colors.gray_800, + textAlign: 'center', }); diff --git a/src/common/component/Modal/Header/ModalHeader.tsx b/src/common/component/Modal/Header/ModalHeader.tsx index ad7580309..da78432f1 100644 --- a/src/common/component/Modal/Header/ModalHeader.tsx +++ b/src/common/component/Modal/Header/ModalHeader.tsx @@ -1,10 +1,10 @@ +import Flex from '@/common/component/Flex/Flex'; +import { iconTextStyle, infoTextStyle } from '@/common/component/Modal/Header/ModalHeader.style'; +import Text from '@/common/component/Text/Text'; + import { useModalContentType } from '@/shared/store/modal'; import { getHeaderContent } from '@/shared/util/modalHeader'; -import Flex from '../../Flex/Flex'; -import Text from '../../Text/Text'; -import { iconTextStyle, infoTextStyle } from './ModalHeader.style'; - interface ModalHeaderProps { step?: number; totalSteps?: number; diff --git a/src/common/component/Modal/Wrapper/ModalWrapper.style.ts b/src/common/component/Modal/Wrapper/ModalWrapper.style.ts index e35bddf72..02ee6436e 100644 --- a/src/common/component/Modal/Wrapper/ModalWrapper.style.ts +++ b/src/common/component/Modal/Wrapper/ModalWrapper.style.ts @@ -7,12 +7,15 @@ export const backgroundStyle = css({ display: 'flex', justifyContent: 'center', alignItems: 'center', + position: 'fixed', top: 0, left: 0, zIndex: theme.zIndex.overlayHigh, + width: '100vw', height: '100vh', + backgroundColor: 'rgba(0, 0, 0, 0.5)', animation: `${fadeIn} 0.2s ease-in`, }); @@ -21,17 +24,20 @@ export const dialogStyle = css({ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', + position: 'fixed', top: '50%', left: '50%', + zIndex: theme.zIndex.overlayTop, + width: '37.6rem', height: '55.4rem', - zIndex: theme.zIndex.overlayTop, padding: '2rem 2rem 3.2rem 2rem', borderRadius: '8px', border: 'none', outline: 'none', + background: theme.colors.white, transform: 'translate(-50%, -50%)', }); diff --git a/src/common/component/Modal/index.tsx b/src/common/component/Modal/index.tsx index d2193c721..eaf7385d9 100644 --- a/src/common/component/Modal/index.tsx +++ b/src/common/component/Modal/index.tsx @@ -1,7 +1,7 @@ -import ModalBody from './Body/ModalBody'; -import ModalFooter from './Footer/ModalFooter'; -import ModalHeader from './Header/ModalHeader'; -import ModalWrapper from './Wrapper/ModalWrapper'; +import ModalBody from '@/common/component/Modal/Body/ModalBody'; +import ModalFooter from '@/common/component/Modal/Footer/ModalFooter'; +import ModalHeader from '@/common/component/Modal/Header/ModalHeader'; +import ModalWrapper from '@/common/component/Modal/Wrapper/ModalWrapper'; export const Modal = Object.assign(ModalWrapper, { Header: ModalHeader, diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx index 45e5f9bbe..0165d2cc1 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx @@ -1,21 +1,16 @@ import { useState } from 'react'; -import Button from '@/common/component/Button/Button'; import DatePicker from '@/common/component/DatePicker'; import Flex from '@/common/component/Flex/Flex'; import Input from '@/common/component/Input/Input'; import { Modal } from '@/common/component/Modal'; import Text from '@/common/component/Text/Text'; -import { - buttonStyle, - textStyle, -} from '@/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.style'; +import { textStyle } from '@/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.style'; import BlockIcon from '@/page/archiving/index/component/TimeBlockModal/component/Block/Icon/BlockIcon'; import BlockBox from '@/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox'; import { BLOCK_ICON } from '@/page/archiving/index/component/TimeBlockModal/constant/iconBlock'; -import WorkSapceInfo from '@/shared/component/WorkSpaceModal/info/WorkSpaceInfo'; import { useBlockContext } from '@/shared/hook/common/useBlockContext'; interface BlockModalProps { diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox.style.ts b/src/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox.style.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx index 8ccb5e0a7..4bec98827 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx @@ -1,7 +1,6 @@ import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; -import Button from '@/common/component/Button/Button'; import Flex from '@/common/component/Flex/Flex'; import { Modal } from '@/common/component/Modal'; @@ -16,7 +15,6 @@ import { usePostTimeBlockMutation } from '@/page/archiving/index/component/TimeB import { formatDatePost } from '@/page/archiving/index/component/TimeBlockModal/util/date'; import { Files } from '@/shared/api/time-blocks/team/time-block/type'; -import WorkSapceInfo from '@/shared/component/WorkSpaceModal/info/WorkSpaceInfo'; import { useBlockContext } from '@/shared/hook/common/useBlockContext'; import { useCloseModal } from '@/shared/store/modal'; import { useToastAction } from '@/shared/store/toast'; diff --git a/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx b/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx index a82fb7967..a2a1eeecf 100644 --- a/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx +++ b/src/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.tsx @@ -3,10 +3,13 @@ import Delete from '@/common/asset/svg/ic_invite_delete.svg?react'; import Flex from '@/common/component/Flex/Flex'; import Text from '@/common/component/Text/Text'; +import { + iconBackStyle, + tagStyle, + textStyle, +} from '@/shared/component/ActivityTagModal/ActivityTagItem/ActivityTagItem.style'; import { TAG_ICON } from '@/shared/constant/icon'; -import { iconBackStyle, tagStyle, textStyle } from './ActivityTagItem.style'; - interface ActivityTagItemProps { title: string; date: string; @@ -25,6 +28,7 @@ const ActivityTagItem = ({ title, date, tag, color, onDelete }: ActivityTagItemP align: 'center', justify: 'space-between', width: '100%', + padding: '0.4rem 0rem', }}> { const [inputValue, setInputValue] = useState(''); diff --git a/src/shared/component/DeleteModal/DeletedModal.tsx b/src/shared/component/DeleteModal/DeletedModal.tsx index cd3d7ff0b..3a4b2e21b 100644 --- a/src/shared/component/DeleteModal/DeletedModal.tsx +++ b/src/shared/component/DeleteModal/DeletedModal.tsx @@ -2,11 +2,10 @@ import Flex from '@/common/component/Flex/Flex'; import { Modal } from '@/common/component/Modal'; import Text from '@/common/component/Text/Text'; +import { detailStyle, titleStyle } from '@/shared/component/DeleteModal/DeletedModal.style'; import { DELETED_DETAIL, DELETED_TITLE } from '@/shared/constant'; import { useCloseModal, useModalData } from '@/shared/store/modal'; -import { detailStyle, titleStyle } from './DeletedModal.style'; - const DeletedModal = () => { const closeModal = useCloseModal(); const modalData = useModalData(); diff --git a/src/shared/component/InviteModal/InviteItem/MemberItem.tsx b/src/shared/component/InviteModal/InviteItem/MemberItem.tsx index c7ecde3a2..d60a57adb 100644 --- a/src/shared/component/InviteModal/InviteItem/MemberItem.tsx +++ b/src/shared/component/InviteModal/InviteItem/MemberItem.tsx @@ -3,7 +3,7 @@ import InviteProfile from '@/common/asset/svg/ic_invite_profile.svg?react'; import Flex from '@/common/component/Flex/Flex'; import Text from '@/common/component/Text/Text'; -import { textStyle } from './MemberItem.style'; +import { textStyle } from '@/shared/component/InviteModal/InviteItem/MemberItem.style'; interface MemberItemProps { title: string; diff --git a/src/shared/component/InviteModal/InviteModal.style.ts b/src/shared/component/InviteModal/InviteModal.style.ts index 8c1f9f3d8..02a237d37 100644 --- a/src/shared/component/InviteModal/InviteModal.style.ts +++ b/src/shared/component/InviteModal/InviteModal.style.ts @@ -12,15 +12,17 @@ export const textStyle = css({ alignItems: 'center', justifyContent: 'center', - color: theme.colors.gray_800, width: '100%', height: '100%', + + color: theme.colors.gray_800, textAlign: 'center', }); export const scrollStyle = css({ display: 'flex', flexDirection: 'column', + height: '30.4rem', width: '100%', diff --git a/src/shared/component/InviteModal/InviteModal.tsx b/src/shared/component/InviteModal/InviteModal.tsx index 6665095bc..59d0b42cb 100644 --- a/src/shared/component/InviteModal/InviteModal.tsx +++ b/src/shared/component/InviteModal/InviteModal.tsx @@ -6,11 +6,10 @@ import Input from '@/common/component/Input/Input'; import { Modal } from '@/common/component/Modal'; import Text from '@/common/component/Text/Text'; +import MemberItem from '@/shared/component/InviteModal/InviteItem/MemberItem'; +import { inputWrapperStyle, scrollStyle, textStyle } from '@/shared/component/InviteModal/InviteModal.style'; import { useCloseModal } from '@/shared/store/modal'; -import MemberItem from './InviteItem/MemberItem'; -import { inputWrapperStyle, scrollStyle, textStyle } from './InviteModal.style'; - const InviteModal = () => { const [inputValue, setInputValue] = useState(''); const [inviteList, setInviteList] = useState([]); diff --git a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts deleted file mode 100644 index ba216db2c..000000000 --- a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.style.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { css } from '@emotion/react'; - -import { theme } from '@/common/style/theme/theme'; - -export const emailStyle = css({ - color: theme.colors.gray_800, -}); diff --git a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx index 970cf4e75..2ff0f17c3 100644 --- a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx +++ b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx @@ -3,7 +3,7 @@ import Profile from '@/common/asset/svg/ic_invite_profile.svg?react'; import Flex from '@/common/component/Flex/Flex'; import Text from '@/common/component/Text/Text'; -import { emailStyle } from './MemberTagItem.style'; +import { textStyle } from '@/shared/component/InviteModal/InviteItem/MemberItem.style'; interface MemberTagItemProps { name: string; @@ -38,7 +38,7 @@ const MemberTagItem = ({ name, email, profileImg, onDelete }: MemberTagItemProps )} {name} - + {email} diff --git a/src/shared/component/MemberTagModal/MemberTagModal.tsx b/src/shared/component/MemberTagModal/MemberTagModal.tsx index 6465b76a1..d26ce54b5 100644 --- a/src/shared/component/MemberTagModal/MemberTagModal.tsx +++ b/src/shared/component/MemberTagModal/MemberTagModal.tsx @@ -6,12 +6,11 @@ import Input from '@/common/component/Input/Input'; import { Modal } from '@/common/component/Modal'; import Text from '@/common/component/Text/Text'; +import { scrollStyle, textStyle } from '@/shared/component/InviteModal/InviteModal.style'; +import MemberTagItem from '@/shared/component/MemberTagModal/MemberTagItem/MemberTagItem'; import { MEMBER_DATA } from '@/shared/constant'; import { useCloseModal } from '@/shared/store/modal'; -import { scrollStyle, textStyle } from '../InviteModal/InviteModal.style'; -import MemberTagItem from './MemberTagItem/MemberTagItem'; - const MemberTagModal = () => { const [inputValue, setInputValue] = useState(''); const [memberTagList, setMemberTagList] = useState(MEMBER_DATA.map((member) => ({ ...member }))); diff --git a/src/shared/component/Modal/ModalContainer.tsx b/src/shared/component/Modal/ModalContainer.tsx index 9ffd0c34d..bd4d13c3b 100644 --- a/src/shared/component/Modal/ModalContainer.tsx +++ b/src/shared/component/Modal/ModalContainer.tsx @@ -2,16 +2,15 @@ import { Modal } from '@/common/component/Modal'; import { BlockFlow } from '@/page/archiving/index/component/TimeBlockModal'; +import ActivityTagModal from '@/shared/component/ActivityTagModal/ActivityTagModal'; +import DeletedModal from '@/shared/component/DeleteModal/DeletedModal'; +import InviteModal from '@/shared/component/InviteModal/InviteModal'; +import MemberTagModal from '@/shared/component/MemberTagModal/MemberTagModal'; import { WorkSpaceFlow } from '@/shared/component/WorkSpaceModal'; import { BlockProvider } from '@/shared/hook/common/useBlockContext'; import { WorkSpaceProvider } from '@/shared/hook/common/useWorkSpaceContext'; import { useCloseModal, useModalContentType, useModalIsOpen } from '@/shared/store/modal'; -import ActivityTagModal from '../ActivityTagModal/ActivityTagModal'; -import DeletedModal from '../DeleteModal/DeletedModal'; -import InviteModal from '../InviteModal/InviteModal'; -import MemberTagModal from '../MemberTagModal/MemberTagModal'; - const ModalContainer = () => { const isOpen = useModalIsOpen(); const contentType = useModalContentType(); diff --git a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx index c76cc1110..9348ac5f2 100644 --- a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx +++ b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx @@ -1,13 +1,9 @@ import { useEffect, useState } from 'react'; -import Button from '@/common/component/Button/Button'; -import Flex from '@/common/component/Flex/Flex'; import { Modal } from '@/common/component/Modal'; import Select from '@/common/component/Select/Select'; import { useOutsideClick, useOverlay } from '@/common/hook'; -import WorkSapceInfo from '@/shared/component/WorkSpaceModal/info/WorkSpaceInfo'; -import { buttonStyle, sectionStyle } from '@/shared/component/WorkSpaceModal/name/WorkSpaceName.style'; import useCategoryListQuery from '@/shared/hook/api/useCategoryListQuery'; import { useWorkSpaceContext } from '@/shared/hook/common/useWorkSpaceContext'; diff --git a/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx b/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx index 8cc592b17..1d6dbfd8e 100644 --- a/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx +++ b/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx @@ -2,11 +2,8 @@ import { css } from '@emotion/react'; import completePng from '@/common/asset/img/workspace_complete.png'; import complete from '@/common/asset/img/workspace_complete.webp'; -import Flex from '@/common/component/Flex/Flex'; import { Modal } from '@/common/component/Modal'; -import WorkSapceInfo from '@/shared/component/WorkSpaceModal/info/WorkSpaceInfo'; -import { sectionStyle } from '@/shared/component/WorkSpaceModal/name/WorkSpaceName.style'; import { useCloseModal } from '@/shared/store/modal'; interface WorkSpaceCompleteProps { diff --git a/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx b/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx index 84409f7dd..5c0341f5d 100644 --- a/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx +++ b/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx @@ -1,19 +1,14 @@ import TeamProfileAdd from '@/common/asset/svg/ic_team_profile_add.svg?react'; import TeamProfileDelete from '@/common/asset/svg/ic_team_profile_delete.svg?react'; -import Button from '@/common/component/Button/Button'; -import Flex from '@/common/component/Flex/Flex'; import Label from '@/common/component/Label/Label'; import { Modal } from '@/common/component/Modal'; import { - buttonCompleteStyle, imageAddStyle, imageBoxStyle, imageDeleteStyle, } from '@/shared/component/WorkSpaceModal/image/WorkSpaceImage.style'; import useImageUpload from '@/shared/component/WorkSpaceModal/image/hook/useImageUpload'; -import WorkSapceInfo from '@/shared/component/WorkSpaceModal/info/WorkSpaceInfo'; -import { sectionStyle } from '@/shared/component/WorkSpaceModal/name/WorkSpaceName.style'; import { usePostTeamMutation } from '@/shared/hook/api/usePostTeamMutation'; import { useWorkSpaceContext } from '@/shared/hook/common/useWorkSpaceContext'; diff --git a/src/shared/util/modalFooter.tsx b/src/shared/util/modalFooter.tsx index 11d4d82cd..0c5517f2a 100644 --- a/src/shared/util/modalFooter.tsx +++ b/src/shared/util/modalFooter.tsx @@ -42,10 +42,6 @@ export const getFooterContent = ( { text: '초대', onClick: buttonClick, variant: 'primary', disabled: !isButtonActive }, ]; case 'member-tag': - return [ - { text: '취소', onClick: closeModal, variant: 'outline' }, - { text: '완료', onClick: buttonClick, variant: 'primary' }, - ]; case 'activity-tag': return [ { text: '취소', onClick: closeModal, variant: 'outline' }, From 8ba85d2b026f7e38cee52e9d79680e5c840e22fc Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Tue, 19 Nov 2024 21:52:22 +0900 Subject: [PATCH 11/13] =?UTF-8?q?feat:=20=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=ED=94=BD=EC=BB=A4=20=EB=82=A0=EC=A7=9C=20=EB=B0=9B=EC=95=84?= =?UTF-8?q?=EC=98=A4=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/component/DatePicker/index.tsx | 5 +-- .../component/Modal/Body/ModalBody.style.ts | 8 ----- src/common/hook/useDatePicker.ts | 10 +++++- .../component/Block/BlockModal.tsx | 6 +++- src/story/common/DatePicker.stories.tsx | 34 +++++++++++++++++-- 5 files changed, 49 insertions(+), 14 deletions(-) delete mode 100644 src/common/component/Modal/Body/ModalBody.style.ts diff --git a/src/common/component/DatePicker/index.tsx b/src/common/component/DatePicker/index.tsx index beb745296..bc82b82c8 100644 --- a/src/common/component/DatePicker/index.tsx +++ b/src/common/component/DatePicker/index.tsx @@ -8,12 +8,13 @@ import { useOverlay } from '@/common/hook/useOverlay'; interface DatePickerProps { variant: 'single' | 'range'; triggerWidth?: string; + onChange: (selectedDate: Date | null, endDate: Date | null) => void; } -const DatePicker = ({ variant, triggerWidth = '10.3rem' }: DatePickerProps) => { +const DatePicker = ({ variant, triggerWidth = '10.3rem', onChange }: DatePickerProps) => { const { isOpen, close, toggle } = useOverlay(); const ref = useOutsideClick(close); - const { selectedDate, endDate, handleSelectDate, clearDates } = useDatePicker(variant); + const { selectedDate, endDate, handleSelectDate, clearDates } = useDatePicker(variant, onChange); const handleInputClick = () => { // 캘린더가 닫혀 있고, 시작날짜와 종료날짜가 모두 선택된 경우에만 날짜 초기화 diff --git a/src/common/component/Modal/Body/ModalBody.style.ts b/src/common/component/Modal/Body/ModalBody.style.ts deleted file mode 100644 index 311cf8d40..000000000 --- a/src/common/component/Modal/Body/ModalBody.style.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { css } from '@emotion/react'; - -export const containerStyle = css({ - width: '100%', - height: '100%', - - paddingTop: '2rem', -}); diff --git a/src/common/hook/useDatePicker.ts b/src/common/hook/useDatePicker.ts index a9c4c4ffc..7369151c5 100644 --- a/src/common/hook/useDatePicker.ts +++ b/src/common/hook/useDatePicker.ts @@ -2,7 +2,10 @@ import { isBefore } from 'date-fns'; import { useState } from 'react'; -export const useDatePicker = (variant: 'single' | 'range') => { +export const useDatePicker = ( + variant: 'single' | 'range', + onChange: (selectedDate: Date | null, endDate: Date | null) => void +) => { const [selectedDate, setSelectedDate] = useState(null); const [endDate, setEndDate] = useState(null); @@ -11,22 +14,27 @@ export const useDatePicker = (variant: 'single' | 'range') => { if (!selectedDate || (selectedDate && endDate)) { setSelectedDate(date); setEndDate(null); + onChange(date, null); } else { if (isBefore(date, selectedDate)) { setEndDate(selectedDate); setSelectedDate(date); + onChange(date, selectedDate); } else { setEndDate(date); + onChange(selectedDate, date); } } } else { setSelectedDate(date); + onChange(date, null); } }; const clearDates = () => { setSelectedDate(null); setEndDate(null); + onChange(null, null); }; return { diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx index 0165d2cc1..7b21265e5 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx @@ -42,6 +42,10 @@ const BlockModal = ({ isVisible }: BlockModalProps) => { } }; + const handleDateChange = () => { + // 날짜 선택 코드 추후 작성 필요 + }; + if (!isVisible) return null; return ( @@ -75,7 +79,7 @@ const BlockModal = ({ isVisible }: BlockModalProps) => { - + diff --git a/src/story/common/DatePicker.stories.tsx b/src/story/common/DatePicker.stories.tsx index 02c36abfb..d30152647 100644 --- a/src/story/common/DatePicker.stories.tsx +++ b/src/story/common/DatePicker.stories.tsx @@ -1,5 +1,7 @@ import { Meta, StoryObj } from '@storybook/react'; +import { useState } from 'react'; + import DatePicker from '@/common/component/DatePicker'; const meta: Meta = { @@ -15,9 +17,37 @@ export default meta; type Story = StoryObj; export const SingleDatePicker: Story = { - render: () => , + render: () => { + const [selectedDate, setSelectedDate] = useState(null); + const [endDate, setEndDate] = useState(null); + + const handleDateChange = (start: Date | null, end: Date | null) => { + setSelectedDate(start); + setEndDate(end); + }; + + return ( + <> + + + ); + }, }; export const RangeDatePicker: Story = { - render: () => , + render: () => { + const [selectedDate, setSelectedDate] = useState(null); + const [endDate, setEndDate] = useState(null); + + const handleDateChange = (start: Date | null, end: Date | null) => { + setSelectedDate(start); + setEndDate(end); + }; + + return ( + <> + + + ); + }, }; From 153ab5f806600f227302027348e943733d43d792 Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Tue, 19 Nov 2024 22:14:17 +0900 Subject: [PATCH 12/13] =?UTF-8?q?feat:=20=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=ED=94=BD=EC=BB=A4=20placeholder=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/component/DatePicker/index.tsx | 30 +++++++++++++++++------ src/common/hook/useDatePicker.ts | 8 +++--- src/story/common/DatePicker.stories.tsx | 15 ++++++++++-- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/common/component/DatePicker/index.tsx b/src/common/component/DatePicker/index.tsx index bc82b82c8..759340fa7 100644 --- a/src/common/component/DatePicker/index.tsx +++ b/src/common/component/DatePicker/index.tsx @@ -9,15 +9,31 @@ interface DatePickerProps { variant: 'single' | 'range'; triggerWidth?: string; onChange: (selectedDate: Date | null, endDate: Date | null) => void; + defaultSelectedDate?: Date; + defaultEndDate?: Date; } -const DatePicker = ({ variant, triggerWidth = '10.3rem', onChange }: DatePickerProps) => { +const DatePicker = ({ + variant, + triggerWidth = '10.3rem', + onChange, + defaultSelectedDate, + defaultEndDate, +}: DatePickerProps) => { const { isOpen, close, toggle } = useOverlay(); const ref = useOutsideClick(close); - const { selectedDate, endDate, handleSelectDate, clearDates } = useDatePicker(variant, onChange); + + const initialSelectedDate = defaultSelectedDate ? new Date(defaultSelectedDate) : null; + const initialEndDate = defaultEndDate ? new Date(defaultEndDate) : null; + + const { selectedDate, endDate, handleSelectDate, clearDates } = useDatePicker( + variant, + onChange, + initialSelectedDate, + initialEndDate + ); const handleInputClick = () => { - // 캘린더가 닫혀 있고, 시작날짜와 종료날짜가 모두 선택된 경우에만 날짜 초기화 if (!isOpen && selectedDate && endDate) { clearDates(); } @@ -27,16 +43,16 @@ const DatePicker = ({ variant, triggerWidth = '10.3rem', onChange }: DatePickerP return (
{isOpen && ( diff --git a/src/common/hook/useDatePicker.ts b/src/common/hook/useDatePicker.ts index 7369151c5..c9a999357 100644 --- a/src/common/hook/useDatePicker.ts +++ b/src/common/hook/useDatePicker.ts @@ -4,10 +4,12 @@ import { useState } from 'react'; export const useDatePicker = ( variant: 'single' | 'range', - onChange: (selectedDate: Date | null, endDate: Date | null) => void + onChange: (selectedDate: Date | null, endDate: Date | null) => void, + initialSelectedDate: Date | null = null, + initialEndDate: Date | null = null ) => { - const [selectedDate, setSelectedDate] = useState(null); - const [endDate, setEndDate] = useState(null); + const [selectedDate, setSelectedDate] = useState(initialSelectedDate); + const [endDate, setEndDate] = useState(initialEndDate); const handleSelectDate = (date: Date) => { if (variant === 'range') { diff --git a/src/story/common/DatePicker.stories.tsx b/src/story/common/DatePicker.stories.tsx index d30152647..99f1d65fb 100644 --- a/src/story/common/DatePicker.stories.tsx +++ b/src/story/common/DatePicker.stories.tsx @@ -28,7 +28,12 @@ export const SingleDatePicker: Story = { return ( <> - + ); }, @@ -46,7 +51,13 @@ export const RangeDatePicker: Story = { return ( <> - + ); }, From 489927b0e18d300da6e3d5cff180ef4fd8eb0ff5 Mon Sep 17 00:00:00 2001 From: cindy-chaewon Date: Wed, 27 Nov 2024 21:48:08 +0900 Subject: [PATCH 13/13] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 394 +++++++++++++++++- src/App.tsx | 5 +- .../CommandButton/CommandButton.style.ts | 2 - src/common/component/Modal/index.tsx | 7 +- src/common/style/scroll.ts | 21 +- .../component/Block/BlockModal.tsx | 14 +- .../component/Upload/UploadModal.style.ts | 17 +- .../component/Upload/UploadModal.tsx | 20 +- .../index/component/TimeBlockModal/index.tsx | 15 +- .../component/DeleteModal/DeletedModal.tsx | 2 +- .../component/InviteModal/InviteModal.tsx | 2 +- .../MemberItem.style.ts | 0 .../{InviteItem => Member}/MemberItem.tsx | 2 +- .../MemberTagItem/MemberTagItem.tsx | 2 +- .../component/Modal/Body/ModalBody.tsx | 4 +- .../component/Modal/Footer/ModalFooter.tsx | 16 +- .../Modal/Header/ModalHeader.style.ts | 0 .../component/Modal/Header/ModalHeader.tsx | 20 +- .../{ModalContainer.tsx => ModalFunnel.tsx} | 36 +- .../category/WorkSpaceCategory.tsx | 12 +- .../complete/WorkSpaceComplete.tsx | 8 +- .../WorkSpaceModal/image/WorkSpaceImage.tsx | 12 +- src/shared/component/WorkSpaceModal/index.tsx | 23 +- .../WorkSpaceModal/name/WorkSpaceName.tsx | 14 +- src/shared/constant/modal.tsx | 152 +++++++ src/shared/hook/common/funnelContext.tsx | 62 +++ src/shared/hook/common/useBlockContext.tsx | 22 +- .../hook/common/useWorkSpaceContext.tsx | 22 +- src/shared/util/funnelStep.tsx | 14 + src/story/shared/DeleteModal.stories.tsx | 8 +- src/story/shared/NewModals.stories.tsx | 8 +- 32 files changed, 775 insertions(+), 162 deletions(-) rename src/shared/component/InviteModal/{InviteItem => Member}/MemberItem.style.ts (100%) rename src/shared/component/InviteModal/{InviteItem => Member}/MemberItem.tsx (91%) rename src/{common => shared}/component/Modal/Body/ModalBody.tsx (64%) rename src/{common => shared}/component/Modal/Footer/ModalFooter.tsx (56%) rename src/{common => shared}/component/Modal/Header/ModalHeader.style.ts (100%) rename src/{common => shared}/component/Modal/Header/ModalHeader.tsx (53%) rename src/shared/component/Modal/{ModalContainer.tsx => ModalFunnel.tsx} (66%) create mode 100644 src/shared/constant/modal.tsx create mode 100644 src/shared/hook/common/funnelContext.tsx create mode 100644 src/shared/util/funnelStep.tsx diff --git a/package.json b/package.json index 12d342f81..6a207b91a 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@sentry/react": "^8.22.0", "@tanstack/react-query": "^5.49.2", "@tanstack/react-query-devtools": "^5.49.2", + "@toss/use-funnel": "^1.4.2", "axios": "^1.7.2", "framer-motion": "^11.11.9", "mime": "^4.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a00f42ec2..f2892bd56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: '@tanstack/react-query-devtools': specifier: ^5.49.2 version: 5.49.2(@tanstack/react-query@5.49.2(react@18.3.1))(react@18.3.1) + '@toss/use-funnel': + specifier: ^1.4.2 + version: 1.4.2(next@13.5.7(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-query@3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) axios: specifier: ^1.7.2 version: 1.7.2 @@ -115,7 +118,7 @@ importers: version: 4.3.1(vite@5.3.2(@types/node@20.14.9)) '@vitejs/plugin-react-swc': specifier: ^3.5.0 - version: 3.7.0(vite@5.3.2(@types/node@20.14.9)) + version: 3.7.0(@swc/helpers@0.5.2)(vite@5.3.2(@types/node@20.14.9)) chromatic: specifier: ^11.5.4 version: 11.5.4 @@ -1134,6 +1137,63 @@ packages: resolution: {integrity: sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==} engines: {node: '>=18'} + '@next/env@13.5.7': + resolution: {integrity: sha512-uVuRqoj28Ys/AI/5gVEgRAISd0KWI0HRjOO1CTpNgmX3ZsHb5mdn14Y59yk0IxizXdo7ZjsI2S7qbWnO+GNBcA==} + + '@next/swc-darwin-arm64@13.5.7': + resolution: {integrity: sha512-7SxmxMex45FvKtRoP18eftrDCMyL6WQVYJSEE/s7A1AW/fCkznxjEShKet2iVVzf89gWp8HbXGaL4hCaseux6g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@13.5.7': + resolution: {integrity: sha512-6iENvgyIkGFLFszBL4b1VfEogKC3TDPEB6/P/lgxmgXVXIV09Q4or1MVn+U/tYyYmm7oHMZ3oxGpHAyJ80nA6g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@13.5.7': + resolution: {integrity: sha512-P42jDX56wu9zEdVI+Xv4zyTeXB3DpqgE1Gb4bWrc0s2RIiDYr6uKBprnOs1hCGIwfVyByxyTw5Va66QCdFFNUg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@13.5.7': + resolution: {integrity: sha512-A06vkj+8X+tLRzSja5REm/nqVOCzR+x5Wkw325Q/BQRyRXWGCoNbQ6A+BR5M86TodigrRfI3lUZEKZKe3QJ9Bg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@13.5.7': + resolution: {integrity: sha512-UdHm7AlxIbdRdMsK32cH0EOX4OmzAZ4Xm+UVlS0YdvwLkI3pb7AoBEoVMG5H0Wj6Wpz6GNkrFguHTRLymTy6kw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@13.5.7': + resolution: {integrity: sha512-c50Y8xBKU16ZGj038H6C13iedRglxvdQHD/1BOtes56gwUrIRDX2Nkzn3mYtpz3Wzax0gfAF9C0Nqljt93IxvA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@13.5.7': + resolution: {integrity: sha512-NcUx8cmkA+JEp34WNYcKW6kW2c0JBhzJXIbw+9vKkt9m/zVJ+KfizlqmoKf04uZBtzFN6aqE2Fyv2MOd021WIA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-ia32-msvc@13.5.7': + resolution: {integrity: sha512-wXp+/3NVcuyJDED6gJiLXs5dqHaWO7moAB6aBtjlKZvsxBDxpcyjsfRbtHPeYtaT20zCkmPs69H0K25lrVZmlA==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@next/swc-win32-x64-msvc@13.5.7': + resolution: {integrity: sha512-PLyD3Dl6jTTkLG8AoqhPGd5pXtSs8wbqIhWPQt3yEMfnYld/dGYuF2YPs3YHaVFrijCIF9pXY3+QOyvP23Zn7g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1682,6 +1742,9 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.2': + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + '@swc/types@0.1.9': resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==} @@ -1733,6 +1796,40 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' + '@toss/assert@1.2.2': + resolution: {integrity: sha512-qK2G1LzI2ghY0aUOsz9mFiy2v/eNlMHG5qXdSogfGFLxHqFZ6KQWJQpnb9eN+dyHYIudBVWPZhbkljqnT8R3/g==} + + '@toss/react@1.8.1': + resolution: {integrity: sha512-jH3oo/7yctexuutj/YgQrddaK1bU2s5659dkJIXOe23bEjkY+lbhvEz2FLEhRjSo6k6ktPagpxO4AcdhCi5k5A==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18 + + '@toss/storage@1.4.1': + resolution: {integrity: sha512-jvnBXAQ/Fqqdt+gqYKeHYk7SzR2LX/FC50JIoVI0RldG1mDh1tebOqD7XkrZ89q77t/RwTuh60+8fjZDgend/g==} + + '@toss/use-funnel@1.4.2': + resolution: {integrity: sha512-qgfYhdoJh07D4+kyRgRE3Du5wEdecFOR9Ht3MjgFMYAJyGplNbD++hpD/FcXrGT5oa14KG0bB5bpp60KLmPkFw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + peerDependencies: + next: ^11.1 || ^12 || ^13 + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + react-query: ^3 + + '@toss/use-query-param@1.3.1': + resolution: {integrity: sha512-GRA+6st46/88KgmP9PGx8mV9sxxkewwLMzBl25TpXPjnqHz+tiZdybVZMQ5UHj0Kzf7bZnHGF3kd5oko492vxA==} + peerDependencies: + next: ^11.1 || ^12 || ^13 + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + + '@toss/utility-types@1.2.1': + resolution: {integrity: sha512-1y8s1bvmuhuMX/d6qR9mmvcgFZIKYIQqJbAIshlGArXkjk/ec67gXc5uByEV1Y7in9ZhrGNRmjD8DTH0988vpQ==} + + '@toss/utils@1.6.1': + resolution: {integrity: sha512-x6m8jLKWtAmCbxTLXbgTzJ5wZyRSUQPLpR/oLJP1ZK9ytXcRf03oA46W/+78kErUkEw/yQz2L+t2xFDHSeZ6IQ==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + '@trivago/prettier-plugin-sort-imports@4.3.0': resolution: {integrity: sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==} peerDependencies: @@ -2156,6 +2253,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -2177,6 +2278,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + broadcast-channel@3.7.0: + resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} + browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} @@ -2191,6 +2295,10 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -2252,6 +2360,9 @@ packages: citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -2264,6 +2375,9 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2385,6 +2499,10 @@ packages: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + date-fns@3.6.0: resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} @@ -2462,6 +2580,9 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2935,6 +3056,9 @@ packages: peerDependencies: glob: ^7.1.6 + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -3258,6 +3382,9 @@ packages: javascript-natural-sort@0.7.1: resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3358,6 +3485,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -3422,6 +3552,9 @@ packages: peerDependencies: react: '>= 0.14.0' + match-sorter@6.3.4: + resolution: {integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -3447,6 +3580,9 @@ packages: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} + microseconds@0.2.0: + resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -3538,6 +3674,9 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + nano-time@1.0.0: + resolution: {integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3553,6 +3692,21 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next@13.5.7: + resolution: {integrity: sha512-W7KIRTE+hPcgGdq89P3mQLDX3m7pJ6nxSyC+YxYaUExE+cS4UledB+Ntk98tKoyhsv6fjb2TRAnD7VDvoqmeFg==} + engines: {node: '>=16.14.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + sass: + optional: true + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -3632,6 +3786,9 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} + oblivious-set@1.0.0: + resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} + ohash@1.1.3: resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} @@ -3782,6 +3939,10 @@ packages: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} @@ -3898,6 +4059,18 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-query@3.39.3: + resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -3970,6 +4143,9 @@ packages: rehype-slug@6.0.0: resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} + remove-accents@0.5.0: + resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4155,6 +4331,10 @@ packages: resolution: {integrity: sha512-8j30wDxQmkcqI0fWcSYFsUCjErsY1yTWbTW+yjbwM8DyW18Cud6CwbFRCxjFsH+2M0CjP6Pqs/m1PGI0vcQscQ==} hasBin: true + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} @@ -4215,6 +4395,19 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + styled-jsx@5.1.1: + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} @@ -4444,6 +4637,9 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unload@2.2.0: + resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -4531,6 +4727,10 @@ packages: walk-up-path@3.0.1: resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} + watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -5786,6 +5986,35 @@ snapshots: outvariant: 1.4.2 strict-event-emitter: 0.5.1 + '@next/env@13.5.7': {} + + '@next/swc-darwin-arm64@13.5.7': + optional: true + + '@next/swc-darwin-x64@13.5.7': + optional: true + + '@next/swc-linux-arm64-gnu@13.5.7': + optional: true + + '@next/swc-linux-arm64-musl@13.5.7': + optional: true + + '@next/swc-linux-x64-gnu@13.5.7': + optional: true + + '@next/swc-linux-x64-musl@13.5.7': + optional: true + + '@next/swc-win32-arm64-msvc@13.5.7': + optional: true + + '@next/swc-win32-ia32-msvc@13.5.7': + optional: true + + '@next/swc-win32-x64-msvc@13.5.7': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6407,7 +6636,7 @@ snapshots: '@swc/core-win32-x64-msvc@1.6.6': optional: true - '@swc/core@1.6.6': + '@swc/core@1.6.6(@swc/helpers@0.5.2)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.9 @@ -6422,9 +6651,14 @@ snapshots: '@swc/core-win32-arm64-msvc': 1.6.6 '@swc/core-win32-ia32-msvc': 1.6.6 '@swc/core-win32-x64-msvc': 1.6.6 + '@swc/helpers': 0.5.2 '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.2': + dependencies: + tslib: 2.6.3 + '@swc/types@0.1.9': dependencies: '@swc/counter': 0.1.3 @@ -6470,6 +6704,51 @@ snapshots: dependencies: '@testing-library/dom': 10.1.0 + '@toss/assert@1.2.2': + dependencies: + '@toss/utils': 1.6.1 + + '@toss/react@1.8.1(react@18.3.1)': + dependencies: + '@babel/runtime': 7.24.7 + '@toss/storage': 1.4.1 + '@toss/utils': 1.6.1 + classnames: 2.5.1 + lodash.debounce: 4.0.8 + lodash.throttle: 4.1.1 + react: 18.3.1 + + '@toss/storage@1.4.1': + dependencies: + '@babel/runtime': 7.24.7 + + '@toss/use-funnel@1.4.2(next@13.5.7(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-query@3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@toss/assert': 1.2.2 + '@toss/react': 1.8.1(react@18.3.1) + '@toss/storage': 1.4.1 + '@toss/use-query-param': 1.3.1(next@13.5.7(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@toss/utils': 1.6.1 + fast-deep-equal: 3.1.3 + next: 13.5.7(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-query: 3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + '@toss/use-query-param@1.3.1(next@13.5.7(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + next: 13.5.7(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@toss/utility-types@1.2.1': {} + + '@toss/utils@1.6.1': + dependencies: + '@babel/runtime': 7.24.7 + '@toss/utility-types': 1.2.1 + date-fns: 2.30.0 + '@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.3.2)': dependencies: '@babel/generator': 7.17.7 @@ -6749,9 +7028,9 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react-swc@3.7.0(vite@5.3.2(@types/node@20.14.9))': + '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.2)(vite@5.3.2(@types/node@20.14.9))': dependencies: - '@swc/core': 1.6.6 + '@swc/core': 1.6.6(@swc/helpers@0.5.2) vite: 5.3.2(@types/node@20.14.9) transitivePeerDependencies: - '@swc/helpers' @@ -7001,6 +7280,8 @@ snapshots: base64-js@1.5.1: {} + big-integer@1.6.52: {} + binary-extensions@2.3.0: {} bl@4.1.0: @@ -7039,6 +7320,17 @@ snapshots: dependencies: fill-range: 7.1.1 + broadcast-channel@3.7.0: + dependencies: + '@babel/runtime': 7.24.7 + detect-node: 2.1.0 + js-sha3: 0.8.0 + microseconds: 0.2.0 + nano-time: 1.0.0 + oblivious-set: 1.0.0 + rimraf: 3.0.2 + unload: 2.2.0 + browser-assert@1.2.1: {} browserslist@4.23.1: @@ -7055,6 +7347,10 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + bytes@3.1.2: {} call-bind@1.0.7: @@ -7121,6 +7417,8 @@ snapshots: dependencies: consola: 3.2.3 + classnames@2.5.1: {} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -7129,6 +7427,8 @@ snapshots: cli-width@4.1.0: {} + client-only@0.0.1: {} + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -7250,6 +7550,10 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.24.7 + date-fns@3.6.0: {} debug@2.6.9: @@ -7321,6 +7625,8 @@ snapshots: detect-indent@6.1.0: {} + detect-node@2.1.0: {} + diff-sequences@29.6.3: {} dir-glob@3.0.1: @@ -7993,6 +8299,8 @@ snapshots: '@types/glob': 7.2.0 glob: 7.2.3 + glob-to-regexp@0.4.1: {} + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -8290,6 +8598,8 @@ snapshots: javascript-natural-sort@0.7.1: {} + js-sha3@0.8.0: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -8394,6 +8704,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash.throttle@4.1.1: {} + lodash@4.17.21: {} log-symbols@4.1.0: @@ -8456,6 +8768,11 @@ snapshots: dependencies: react: 18.3.1 + match-sorter@6.3.4: + dependencies: + '@babel/runtime': 7.24.7 + remove-accents: 0.5.0 + media-typer@0.3.0: {} memoizerific@1.11.3: @@ -8475,6 +8792,8 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + microseconds@0.2.0: {} + mime-db@1.52.0: {} mime-types@2.1.35: @@ -8557,6 +8876,10 @@ snapshots: mute-stream@1.0.0: {} + nano-time@1.0.0: + dependencies: + big-integer: 1.6.52 + nanoid@3.3.7: {} natural-compare@1.4.0: {} @@ -8565,6 +8888,31 @@ snapshots: neo-async@2.6.2: {} + next@13.5.7(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 13.5.7 + '@swc/helpers': 0.5.2 + busboy: 1.6.0 + caniuse-lite: 1.0.30001639 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.3.1) + watchpack: 2.4.0 + optionalDependencies: + '@next/swc-darwin-arm64': 13.5.7 + '@next/swc-darwin-x64': 13.5.7 + '@next/swc-linux-arm64-gnu': 13.5.7 + '@next/swc-linux-arm64-musl': 13.5.7 + '@next/swc-linux-x64-gnu': 13.5.7 + '@next/swc-linux-x64-musl': 13.5.7 + '@next/swc-win32-arm64-msvc': 13.5.7 + '@next/swc-win32-ia32-msvc': 13.5.7 + '@next/swc-win32-x64-msvc': 13.5.7 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -8650,6 +8998,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + oblivious-set@1.0.0: {} + ohash@1.1.3: {} on-finished@2.4.1: @@ -8789,6 +9139,12 @@ snapshots: possible-typed-array-names@1.0.0: {} + postcss@8.4.31: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + postcss@8.4.39: dependencies: nanoid: 3.3.7 @@ -8909,6 +9265,15 @@ snapshots: react-is@18.3.1: {} + react-query@3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.24.7 + broadcast-channel: 3.7.0 + match-sorter: 6.3.4 + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-refresh@0.14.2: {} react-router-dom@6.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -9009,6 +9374,8 @@ snapshots: hast-util-to-string: 3.0.0 unist-util-visit: 5.0.0 + remove-accents@0.5.0: {} + require-directory@2.1.1: {} requireindex@1.2.0: {} @@ -9246,6 +9613,8 @@ snapshots: - supports-color - utf-8-validate + streamsearch@1.1.0: {} + strict-event-emitter@0.5.1: {} string-width@4.2.3: @@ -9321,6 +9690,13 @@ snapshots: strip-json-comments@3.1.1: {} + styled-jsx@5.1.1(@babel/core@7.24.7)(react@18.3.1): + dependencies: + client-only: 0.0.1 + react: 18.3.1 + optionalDependencies: + '@babel/core': 7.24.7 + stylis@4.2.0: {} supports-color@5.5.0: @@ -9535,6 +9911,11 @@ snapshots: universalify@2.0.1: {} + unload@2.2.0: + dependencies: + '@babel/runtime': 7.24.7 + detect-node: 2.1.0 + unpipe@1.0.0: {} unplugin@1.0.1: @@ -9615,6 +9996,11 @@ snapshots: walk-up-path@3.0.1: {} + watchpack@2.4.0: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + wcwidth@1.0.1: dependencies: defaults: 1.0.4 diff --git a/src/App.tsx b/src/App.tsx index 74fdba759..cedc6fcdb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,12 +10,13 @@ import ErrorBoundary from '@/common/component/ErrorBoundary/ErrorBoundary'; import { HTTPError } from '@/shared/api/HTTPError'; import Header from '@/shared/component/Header/Header'; import Login from '@/shared/component/Login/Login'; -import ModalContainer from '@/shared/component/Modal/ModalContainer'; import SNB from '@/shared/component/SideNavBar/LeftSidebar'; import { HTTP_STATUS_CODE } from '@/shared/constant/api'; import { PATH } from '@/shared/constant/path'; import ErrorPage from '@/shared/page/errorPage/ErrorPage'; +import ModalFunnel from './shared/component/Modal/ModalFunnel'; + const App = () => { const navigate = useNavigate(); @@ -48,7 +49,7 @@ const App = () => { return ( - +
diff --git a/src/common/component/CommandButton/CommandButton.style.ts b/src/common/component/CommandButton/CommandButton.style.ts index 13f15bc1d..11a6b0f5f 100644 --- a/src/common/component/CommandButton/CommandButton.style.ts +++ b/src/common/component/CommandButton/CommandButton.style.ts @@ -72,7 +72,5 @@ export const childrenStyle = css({ display: 'flex', alignItems: 'center', - //padding: '0.4rem', - gap: '0.4rem', }); diff --git a/src/common/component/Modal/index.tsx b/src/common/component/Modal/index.tsx index eaf7385d9..d7125b0a9 100644 --- a/src/common/component/Modal/index.tsx +++ b/src/common/component/Modal/index.tsx @@ -1,8 +1,9 @@ -import ModalBody from '@/common/component/Modal/Body/ModalBody'; -import ModalFooter from '@/common/component/Modal/Footer/ModalFooter'; -import ModalHeader from '@/common/component/Modal/Header/ModalHeader'; import ModalWrapper from '@/common/component/Modal/Wrapper/ModalWrapper'; +import ModalBody from '@/shared/component/Modal/Body/ModalBody'; +import ModalFooter from '@/shared/component/Modal/Footer/ModalFooter'; +import ModalHeader from '@/shared/component/Modal/Header/ModalHeader'; + export const Modal = Object.assign(ModalWrapper, { Header: ModalHeader, Body: ModalBody, diff --git a/src/common/style/scroll.ts b/src/common/style/scroll.ts index 72658c9b3..8dd3d5104 100644 --- a/src/common/style/scroll.ts +++ b/src/common/style/scroll.ts @@ -2,12 +2,15 @@ import { css } from '@emotion/react'; import { theme } from '@/common/style/theme/theme'; -export const scrollStyle = css` - ::-webkit-scrollbar { - width: 0.8rem; - } - ::-webkit-scrollbar-thumb { - background: ${theme.colors.gray_300}; - border-radius: 10rem; - } -`; +export const scrollStyle = { + '::-webkit-scrollbar': { + width: '0.8rem', + }, + '::-webkit-scrollbar-thumb': { + background: theme.colors.gray_300, + borderRadius: '10rem', + }, + '::-webkit-scrollbar-track': { + background: 'transparent', + }, +}; diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx index 7b21265e5..9149f8b1e 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal.tsx @@ -11,16 +11,14 @@ import BlockIcon from '@/page/archiving/index/component/TimeBlockModal/component import BlockBox from '@/page/archiving/index/component/TimeBlockModal/component/Box/BlockBox'; import { BLOCK_ICON } from '@/page/archiving/index/component/TimeBlockModal/constant/iconBlock'; +import { useFunnel } from '@/shared/hook/common/funnelContext'; import { useBlockContext } from '@/shared/hook/common/useBlockContext'; -interface BlockModalProps { - isVisible: boolean; -} +const BlockModal = () => { + const [selectedIcon, setSelectedIcon] = useState(-1); -const BlockModal = ({ isVisible }: BlockModalProps) => { - const [selectedIcon, setSelectedIcon] = useState(-1); - - const { formData, setFormData, nextStep } = useBlockContext(); + const { formData, setFormData } = useBlockContext(); + const { nextStep } = useFunnel(); const handleBlockNameChange = (e: React.ChangeEvent) => { if (e.target.value.length <= 25) { @@ -46,8 +44,6 @@ const BlockModal = ({ isVisible }: BlockModalProps) => { // 날짜 선택 코드 추후 작성 필요 }; - if (!isVisible) return null; - return ( <> diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts index 8d5ca02bf..1336e63a7 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style.ts @@ -1,8 +1,8 @@ import { css } from '@emotion/react'; -import { theme } from '@/common/style/theme/theme'; +import { scrollStyle } from '@/common/style/scroll'; -export const scrollStyle = css({ +export const scrollContainerStyle = css({ display: 'flex', flexDirection: 'column', maxHeight: '12rem', @@ -16,18 +16,7 @@ export const scrollStyle = css({ boxSizing: 'content-box', - '&::-webkit-scrollbar': { - width: '1rem', - }, - - '&::-webkit-scrollbar-thumb': { - backgroundColor: theme.colors.gray_300, - borderRadius: '4px', - }, - - '&::-webkit-scrollbar-track': { - backgroundColor: 'transparent', - }, + ...scrollStyle, }); export const flexStyle = css({ diff --git a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx index 4bec98827..9e5cf477a 100644 --- a/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.tsx @@ -8,7 +8,7 @@ import BlockAdd from '@/page/archiving/index/component/TimeBlockModal/component/ import BlockItem from '@/page/archiving/index/component/TimeBlockModal/component/Upload/File/List/BlockItem'; import { flexStyle, - scrollStyle, + scrollContainerStyle, } from '@/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal.style'; import { useDeleteFileMutation } from '@/page/archiving/index/component/TimeBlockModal/hook/api/useDeleteFileMutation'; import { usePostTimeBlockMutation } from '@/page/archiving/index/component/TimeBlockModal/hook/api/usePostTimeBlockMutation'; @@ -19,15 +19,11 @@ import { useBlockContext } from '@/shared/hook/common/useBlockContext'; import { useCloseModal } from '@/shared/store/modal'; import { useToastAction } from '@/shared/store/toast'; -interface UploadModalProps { - isVisible: boolean; -} - -const UploadModal = ({ isVisible }: UploadModalProps) => { +const UploadModal = () => { const location = useLocation(); const searchParams = new URLSearchParams(location.search); const teamId = searchParams.get('teamId'); - const { formData, reset } = useBlockContext(); + const { formData, resetFormData } = useBlockContext(); const closeModal = useCloseModal(); const [files, setFiles] = useState([]); @@ -82,8 +78,6 @@ const UploadModal = ({ isVisible }: UploadModalProps) => { } }; - if (!isVisible) return null; - const data = { name: formData.blockName, color: formData.blockColor, @@ -98,7 +92,7 @@ const UploadModal = ({ isVisible }: UploadModalProps) => { onSuccess: () => { createToast('활동 블록이 생성되었습니다', 'success'); closeModal(); - reset(); + resetFormData(); }, }); }; @@ -107,7 +101,7 @@ const UploadModal = ({ isVisible }: UploadModalProps) => { <> - + { setFileUrls={setFileUrls} setUploadStatus={setUploadStatus} /> -
+
{files.map((file) => ( handleDelete(file.name)} /* 임의의 값 넣었음! 추후 서버 로직 다시 짤때 바꿀것!!*/ diff --git a/src/page/archiving/index/component/TimeBlockModal/index.tsx b/src/page/archiving/index/component/TimeBlockModal/index.tsx index d53868f48..ecf2827aa 100644 --- a/src/page/archiving/index/component/TimeBlockModal/index.tsx +++ b/src/page/archiving/index/component/TimeBlockModal/index.tsx @@ -1,15 +1,22 @@ import BlockModal from '@/page/archiving/index/component/TimeBlockModal/component/Block/BlockModal'; import UploadModal from '@/page/archiving/index/component/TimeBlockModal/component/Upload/UploadModal'; -import { useBlockContext } from '@/shared/hook/common/useBlockContext'; +import { useFunnel } from '@/shared/hook/common/funnelContext'; +import { FunnelStep } from '@/shared/util/funnelStep'; export const BlockFlow = () => { - const { step } = useBlockContext(); + const { setTotalSteps } = useFunnel(); + + setTotalSteps(2); return ( <> - - + + + + + + ); }; diff --git a/src/shared/component/DeleteModal/DeletedModal.tsx b/src/shared/component/DeleteModal/DeletedModal.tsx index 3a4b2e21b..b5ed7594b 100644 --- a/src/shared/component/DeleteModal/DeletedModal.tsx +++ b/src/shared/component/DeleteModal/DeletedModal.tsx @@ -11,7 +11,7 @@ const DeletedModal = () => { const modalData = useModalData(); const itemType = modalData?.itemType; - const title = itemType ? DELETED_TITLE[itemType.toUpperCase() as keyof typeof DELETED_TITLE] : ''; + const title = itemType && DELETED_TITLE[itemType.toUpperCase() as keyof typeof DELETED_TITLE]; const handleDelete = () => { //api 로직 추가하기 diff --git a/src/shared/component/InviteModal/InviteModal.tsx b/src/shared/component/InviteModal/InviteModal.tsx index 59d0b42cb..c2feb4590 100644 --- a/src/shared/component/InviteModal/InviteModal.tsx +++ b/src/shared/component/InviteModal/InviteModal.tsx @@ -6,8 +6,8 @@ import Input from '@/common/component/Input/Input'; import { Modal } from '@/common/component/Modal'; import Text from '@/common/component/Text/Text'; -import MemberItem from '@/shared/component/InviteModal/InviteItem/MemberItem'; import { inputWrapperStyle, scrollStyle, textStyle } from '@/shared/component/InviteModal/InviteModal.style'; +import MemberItem from '@/shared/component/InviteModal/Member/MemberItem'; import { useCloseModal } from '@/shared/store/modal'; const InviteModal = () => { diff --git a/src/shared/component/InviteModal/InviteItem/MemberItem.style.ts b/src/shared/component/InviteModal/Member/MemberItem.style.ts similarity index 100% rename from src/shared/component/InviteModal/InviteItem/MemberItem.style.ts rename to src/shared/component/InviteModal/Member/MemberItem.style.ts diff --git a/src/shared/component/InviteModal/InviteItem/MemberItem.tsx b/src/shared/component/InviteModal/Member/MemberItem.tsx similarity index 91% rename from src/shared/component/InviteModal/InviteItem/MemberItem.tsx rename to src/shared/component/InviteModal/Member/MemberItem.tsx index d60a57adb..0bba93b0d 100644 --- a/src/shared/component/InviteModal/InviteItem/MemberItem.tsx +++ b/src/shared/component/InviteModal/Member/MemberItem.tsx @@ -3,7 +3,7 @@ import InviteProfile from '@/common/asset/svg/ic_invite_profile.svg?react'; import Flex from '@/common/component/Flex/Flex'; import Text from '@/common/component/Text/Text'; -import { textStyle } from '@/shared/component/InviteModal/InviteItem/MemberItem.style'; +import { textStyle } from '@/shared/component/InviteModal/Member/MemberItem.style'; interface MemberItemProps { title: string; diff --git a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx index 2ff0f17c3..493c3854d 100644 --- a/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx +++ b/src/shared/component/MemberTagModal/MemberTagItem/MemberTagItem.tsx @@ -3,7 +3,7 @@ import Profile from '@/common/asset/svg/ic_invite_profile.svg?react'; import Flex from '@/common/component/Flex/Flex'; import Text from '@/common/component/Text/Text'; -import { textStyle } from '@/shared/component/InviteModal/InviteItem/MemberItem.style'; +import { textStyle } from '@/shared/component/InviteModal/Member/MemberItem.style'; interface MemberTagItemProps { name: string; diff --git a/src/common/component/Modal/Body/ModalBody.tsx b/src/shared/component/Modal/Body/ModalBody.tsx similarity index 64% rename from src/common/component/Modal/Body/ModalBody.tsx rename to src/shared/component/Modal/Body/ModalBody.tsx index 8811a06f5..fc4a729a1 100644 --- a/src/common/component/Modal/Body/ModalBody.tsx +++ b/src/shared/component/Modal/Body/ModalBody.tsx @@ -7,7 +7,9 @@ interface ModalBodyProps { } const ModalBody = ({ children }: ModalBodyProps) => ( - + {children} ); diff --git a/src/common/component/Modal/Footer/ModalFooter.tsx b/src/shared/component/Modal/Footer/ModalFooter.tsx similarity index 56% rename from src/common/component/Modal/Footer/ModalFooter.tsx rename to src/shared/component/Modal/Footer/ModalFooter.tsx index 8e3dc96cc..8565068a9 100644 --- a/src/common/component/Modal/Footer/ModalFooter.tsx +++ b/src/shared/component/Modal/Footer/ModalFooter.tsx @@ -1,8 +1,8 @@ import Button from '@/common/component/Button/Button'; import Flex from '@/common/component/Flex/Flex'; +import { MODAL_CONTENTS, isModalContentType } from '@/shared/constant/modal'; import { useCloseModal, useModalContentType } from '@/shared/store/modal'; -import { getFooterContent } from '@/shared/util/modalFooter'; interface ModalFooterProps { step?: number; @@ -10,21 +10,25 @@ interface ModalFooterProps { isButtonActive?: boolean; } -const ModalFooter = ({ step, buttonClick, isButtonActive }: ModalFooterProps) => { +const ModalFooter = ({ step = 1, buttonClick, isButtonActive = true }: ModalFooterProps) => { const contentType = useModalContentType(); const closeModal = useCloseModal(); - const footerButtons = getFooterContent(contentType!, step!, buttonClick, closeModal, isButtonActive); + + if (!isModalContentType(contentType)) return null; + + const modalContent = MODAL_CONTENTS[contentType]; + const buttons = modalContent.buttons[step - 1]; return ( - {footerButtons.map((button, index) => ( + {buttons.map((button: (typeof buttons)[number], index: number) => ( ))} diff --git a/src/common/component/Modal/Header/ModalHeader.style.ts b/src/shared/component/Modal/Header/ModalHeader.style.ts similarity index 100% rename from src/common/component/Modal/Header/ModalHeader.style.ts rename to src/shared/component/Modal/Header/ModalHeader.style.ts diff --git a/src/common/component/Modal/Header/ModalHeader.tsx b/src/shared/component/Modal/Header/ModalHeader.tsx similarity index 53% rename from src/common/component/Modal/Header/ModalHeader.tsx rename to src/shared/component/Modal/Header/ModalHeader.tsx index da78432f1..68b818f7e 100644 --- a/src/common/component/Modal/Header/ModalHeader.tsx +++ b/src/shared/component/Modal/Header/ModalHeader.tsx @@ -1,24 +1,30 @@ import Flex from '@/common/component/Flex/Flex'; -import { iconTextStyle, infoTextStyle } from '@/common/component/Modal/Header/ModalHeader.style'; import Text from '@/common/component/Text/Text'; +import { iconTextStyle, infoTextStyle } from '@/shared/component/Modal/Header/ModalHeader.style'; +import { MODAL_CONTENTS, isModalContentType } from '@/shared/constant/modal'; import { useModalContentType } from '@/shared/store/modal'; -import { getHeaderContent } from '@/shared/util/modalHeader'; interface ModalHeaderProps { step?: number; totalSteps?: number; } -const ModalHeader = ({ step, totalSteps = 4 }: ModalHeaderProps) => { +const ModalHeader = ({ step = 1, totalSteps = 4 }: ModalHeaderProps) => { const contentType = useModalContentType(); - const { icon, title, infoText } = getHeaderContent(contentType!, step, totalSteps); + + if (!isModalContentType(contentType)) return null; + + const modalContent = MODAL_CONTENTS[contentType]; + const { icon, title, infoText } = modalContent.headers[step - 1]; + + const displayIcon = typeof icon === 'function' ? icon(step, totalSteps) : icon; // 함수인지 확인 후 호출 return ( - - {icon && ( + + {displayIcon && ( - {icon} + {displayIcon} )} diff --git a/src/shared/component/Modal/ModalContainer.tsx b/src/shared/component/Modal/ModalFunnel.tsx similarity index 66% rename from src/shared/component/Modal/ModalContainer.tsx rename to src/shared/component/Modal/ModalFunnel.tsx index bd4d13c3b..9b49af405 100644 --- a/src/shared/component/Modal/ModalContainer.tsx +++ b/src/shared/component/Modal/ModalFunnel.tsx @@ -6,12 +6,14 @@ import ActivityTagModal from '@/shared/component/ActivityTagModal/ActivityTagMod import DeletedModal from '@/shared/component/DeleteModal/DeletedModal'; import InviteModal from '@/shared/component/InviteModal/InviteModal'; import MemberTagModal from '@/shared/component/MemberTagModal/MemberTagModal'; -import { WorkSpaceFlow } from '@/shared/component/WorkSpaceModal'; +import { WorkSpaceFlow } from '@/shared/component/WorkSpaceModal/index'; +import { FunnelProvider } from '@/shared/hook/common/funnelContext'; import { BlockProvider } from '@/shared/hook/common/useBlockContext'; import { WorkSpaceProvider } from '@/shared/hook/common/useWorkSpaceContext'; import { useCloseModal, useModalContentType, useModalIsOpen } from '@/shared/store/modal'; +import { FunnelStep } from '@/shared/util/funnelStep'; -const ModalContainer = () => { +const ModalFunnel = () => { const isOpen = useModalIsOpen(); const contentType = useModalContentType(); const closeModal = useCloseModal(); @@ -31,25 +33,41 @@ const ModalContainer = () => { ); case 'deleted': - return ; + return ( + + + + ); case 'invite': - return ; + return ( + + + + ); case 'member-tag': - return ; + return ( + + + + ); case 'activity-tag': - return ; + return ( + + + + ); default: return null; } }; - if (!isOpen || !contentType) return null; + if (!isOpen) return null; return ( - {renderContent()} + {renderContent()} ); }; -export default ModalContainer; +export default ModalFunnel; diff --git a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx index 9348ac5f2..055d66442 100644 --- a/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx +++ b/src/shared/component/WorkSpaceModal/category/WorkSpaceCategory.tsx @@ -5,16 +5,14 @@ import Select from '@/common/component/Select/Select'; import { useOutsideClick, useOverlay } from '@/common/hook'; import useCategoryListQuery from '@/shared/hook/api/useCategoryListQuery'; +import { useFunnel } from '@/shared/hook/common/funnelContext'; import { useWorkSpaceContext } from '@/shared/hook/common/useWorkSpaceContext'; -interface WorkSpaceCategoryProps { - isVisible: boolean; -} - -const WorkSpaceCategory = ({ isVisible }: WorkSpaceCategoryProps) => { +const WorkSpaceCategory = () => { const { isOpen, close, toggle } = useOverlay(); - const { setFormData, nextStep } = useWorkSpaceContext(); + const { setFormData } = useWorkSpaceContext(); + const { nextStep } = useFunnel(); const ref = useOutsideClick(close); @@ -44,8 +42,6 @@ const WorkSpaceCategory = ({ isVisible }: WorkSpaceCategoryProps) => { }; }, [isOpen, close, ref]); - if (!isVisible) return null; - const handleSelect = (id: string) => { setSelected(id); setFormData({ category: id }); diff --git a/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx b/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx index 1d6dbfd8e..ec335749f 100644 --- a/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx +++ b/src/shared/component/WorkSpaceModal/complete/WorkSpaceComplete.tsx @@ -6,13 +6,7 @@ import { Modal } from '@/common/component/Modal'; import { useCloseModal } from '@/shared/store/modal'; -interface WorkSpaceCompleteProps { - isVisible: boolean; -} - -const WorkSpaceComplete = ({ isVisible }: WorkSpaceCompleteProps) => { - if (!isVisible) return null; - +const WorkSpaceComplete = () => { const closeModal = useCloseModal(); return ( diff --git a/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx b/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx index 5c0341f5d..81965ff0e 100644 --- a/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx +++ b/src/shared/component/WorkSpaceModal/image/WorkSpaceImage.tsx @@ -10,16 +10,14 @@ import { } from '@/shared/component/WorkSpaceModal/image/WorkSpaceImage.style'; import useImageUpload from '@/shared/component/WorkSpaceModal/image/hook/useImageUpload'; import { usePostTeamMutation } from '@/shared/hook/api/usePostTeamMutation'; +import { useFunnel } from '@/shared/hook/common/funnelContext'; import { useWorkSpaceContext } from '@/shared/hook/common/useWorkSpaceContext'; -interface WorkSpaceImageProps { - isVisible: boolean; -} - -const WorkSpaceImage = ({ isVisible }: WorkSpaceImageProps) => { +const WorkSpaceImage = () => { const { fileURL, imgUploadInput, handleImageChange, handleImageRemove } = useImageUpload(); - const { nextStep, formData } = useWorkSpaceContext(); + const { formData } = useWorkSpaceContext(); const { mutate: postTeamMutate } = usePostTeamMutation(); + const { nextStep } = useFunnel(); const handleSave = () => { postTeamMutate( @@ -36,8 +34,6 @@ const WorkSpaceImage = ({ isVisible }: WorkSpaceImageProps) => { ); }; - if (!isVisible) return null; - const isButtonActive = !!fileURL; return ( diff --git a/src/shared/component/WorkSpaceModal/index.tsx b/src/shared/component/WorkSpaceModal/index.tsx index 25598aaa1..0e41e6057 100644 --- a/src/shared/component/WorkSpaceModal/index.tsx +++ b/src/shared/component/WorkSpaceModal/index.tsx @@ -2,17 +2,28 @@ import WorkSpaceCategory from '@/shared/component/WorkSpaceModal/category/WorkSp import WorkSpaceComplete from '@/shared/component/WorkSpaceModal/complete/WorkSpaceComplete'; import WorkSpaceImage from '@/shared/component/WorkSpaceModal/image/WorkSpaceImage'; import WorkSpaceName from '@/shared/component/WorkSpaceModal/name/WorkSpaceName'; -import { useWorkSpaceContext } from '@/shared/hook/common/useWorkSpaceContext'; +import { useFunnel } from '@/shared/hook/common/funnelContext'; +import { FunnelStep } from '@/shared/util/funnelStep'; export const WorkSpaceFlow = () => { - const { step } = useWorkSpaceContext(); + const { setTotalSteps } = useFunnel(); + + setTotalSteps(4); return ( <> - - - - + + + + + + + + + + + + ); }; diff --git a/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx b/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx index 18cb43132..24c49a277 100644 --- a/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx +++ b/src/shared/component/WorkSpaceModal/name/WorkSpaceName.tsx @@ -4,17 +4,17 @@ import Input from '@/common/component/Input/Input'; import { Modal } from '@/common/component/Modal'; import { inputWrapperStyle } from '@/shared/component/WorkSpaceModal/name/WorkSpaceName.style'; +import { useFunnel } from '@/shared/hook/common/funnelContext'; import { useWorkSpaceContext } from '@/shared/hook/common/useWorkSpaceContext'; -interface WorkSpaceNameProps { - isVisible: boolean; -} - -const WorkSpaceName = ({ isVisible }: WorkSpaceNameProps) => { +const WorkSpaceName = () => { const [inputValue, setInputValue] = useState(''); - const { setFormData, nextStep } = useWorkSpaceContext(); + const { setFormData } = useWorkSpaceContext(); + + const { nextStep } = useFunnel(); const handleNext = () => { + console.log('Next button clicked'); // 디버깅용 setFormData({ name: inputValue }); nextStep(); }; @@ -25,8 +25,6 @@ const WorkSpaceName = ({ isVisible }: WorkSpaceNameProps) => { setInputValue(e.target.value); }; - if (!isVisible) return null; - return ( <> diff --git a/src/shared/constant/modal.tsx b/src/shared/constant/modal.tsx new file mode 100644 index 000000000..757815b31 --- /dev/null +++ b/src/shared/constant/modal.tsx @@ -0,0 +1,152 @@ +import ActivityTagIcon from '@/common/asset/svg/ic_activity_tag.svg?react'; +import BlockIcon from '@/common/asset/svg/ic_block_create.svg?react'; +import InviteIcon from '@/common/asset/svg/ic_invite.svg?react'; +import MemberTagIcon from '@/common/asset/svg/ic_member_tag.svg?react'; +import WarningIcon from '@/common/asset/svg/ic_warning.svg?react'; +import SuccessIcon from '@/common/asset/svg/ic_workspace_success.svg?react'; + +type ModalContentType = 'create-workspace' | 'create-block' | 'deleted' | 'invite' | 'member-tag' | 'activity-tag'; + +interface ModalHeader { + icon: React.ReactNode | ((step: number, totalSteps: number) => React.ReactNode); + title: string; + infoText: string; +} + +interface ModalButton { + text: string; + variant: 'primary' | 'secondary' | 'tertiary' | 'outline' | 'underline'; + disabled?: boolean; +} + +interface ModalContent { + steps: number; + headers: ModalHeader[]; + buttons: ModalButton[][]; +} + +export const isModalContentType = (type: string | null): type is ModalContentType => { + return type !== null && type in MODAL_CONTENTS; +}; + +export const MODAL_CONTENTS: Record = { + 'create-workspace': { + steps: 4, + headers: [ + { + icon: (step: number, totalSteps: number) => + step === totalSteps ? : {`${step}/${totalSteps}`}, + title: '워크스페이스 이름 입력', + infoText: '워크스페이스 이름을 입력해주세요.', + }, + { + icon: (step: number, totalSteps: number) => + step === totalSteps ? : {`${step}/${totalSteps}`}, + title: '카테고리 선택', + infoText: '카테고리를 선택해주세요.', + }, + { + icon: (step: number, totalSteps: number) => + step === totalSteps ? : {`${step}/${totalSteps}`}, + title: '프로필 이미지 등록', + infoText: '프로필 이미지를 등록해주세요.', + }, + { + icon: (step: number, totalSteps: number) => + step === totalSteps ? : {`${step}/${totalSteps}`}, + title: '완료', + infoText: '워크스페이스 생성이 완료되었습니다.', + }, + ], + buttons: [ + [ + { text: '건너뛰기', variant: 'outline' }, + { text: '다음으로', variant: 'primary' }, + ], + [ + { text: '건너뛰기', variant: 'outline' }, + { text: '다음으로', variant: 'primary' }, + ], + [ + { text: '건너뛰기', variant: 'outline' }, + { text: '다음으로', variant: 'primary' }, + ], + [{ text: '확인', variant: 'primary' }], + ], + }, + 'create-block': { + steps: 2, + headers: [ + { icon: , title: '타임블록 생성', infoText: '블록 정보를 입력해주세요.' }, + { icon: , title: '파일 업로드', infoText: '파일을 업로드해주세요.' }, + ], + buttons: [ + [ + { text: '취소', variant: 'outline' }, + { text: '다음으로', variant: 'primary' }, + ], + [ + { text: '취소', variant: 'outline' }, + { text: '완료', variant: 'primary' }, + ], + ], + }, + deleted: { + steps: 1, + headers: [{ icon: , title: '삭제 확인', infoText: '정말 삭제하시겠습니까?' }], + buttons: [ + [ + { text: '취소', variant: 'outline' }, + { text: '삭제', variant: 'primary' }, + ], + ], + }, + invite: { + steps: 1, + headers: [ + { + icon: , + title: '팀원 초대', + infoText: '워크스페이스에 팀원을 초대할 수 있습니다.', + }, + ], + buttons: [ + [ + { text: '취소', variant: 'outline' }, + { text: '초대', variant: 'primary' }, + ], + ], + }, + 'member-tag': { + steps: 1, + headers: [ + { + icon: , + title: '팀원 태그', + infoText: '관련된 팀원을 태그할 수 있습니다.', + }, + ], + buttons: [ + [ + { text: '취소', variant: 'outline' }, + { text: '완료', variant: 'primary' }, + ], + ], + }, + 'activity-tag': { + steps: 1, + headers: [ + { + icon: , + title: '활동 태그', + infoText: '타임라인에 저장된 활동을 태그할 수 있습니다.', + }, + ], + buttons: [ + [ + { text: '취소', variant: 'outline' }, + { text: '완료', variant: 'primary' }, + ], + ], + }, +}; diff --git a/src/shared/hook/common/funnelContext.tsx b/src/shared/hook/common/funnelContext.tsx new file mode 100644 index 000000000..dfdd5404b --- /dev/null +++ b/src/shared/hook/common/funnelContext.tsx @@ -0,0 +1,62 @@ +import { ReactNode, createContext, useCallback, useContext, useEffect, useState } from 'react'; + +interface FunnelContextType { + currentStep: number; + totalSteps: number; + nextStep: () => void; + prevStep: () => void; + resetSteps: () => void; + setTotalSteps: (steps: number) => void; +} + +const FunnelContext = createContext(undefined); + +export const useFunnel = () => { + const context = useContext(FunnelContext); + if (!context) { + throw new Error('Error: FunnelContext must be used within a FunnelProvider'); + } + return context; +}; + +export const FunnelProvider = ({ children }: { children: ReactNode }) => { + const [currentStep, setCurrentStep] = useState(1); + const [totalSteps, setTotalStepsState] = useState(1); + + const nextStep = useCallback(() => { + setCurrentStep((prev) => { + const next = prev < totalSteps ? prev + 1 : prev; + console.log('Next step:', next); // 디버깅용 + return next; + }); + }, [totalSteps]); + + const prevStep = useCallback(() => { + setCurrentStep((prev) => { + const prevStep = prev > 1 ? prev - 1 : prev; + console.log('Previous step:', prevStep); // 디버깅용 + return prevStep; + }); + }, []); + + const resetSteps = useCallback(() => { + console.log('Reset steps'); // 디버깅용 + setCurrentStep(1); + }, []); + + const setTotalSteps = useCallback((steps: number) => { + console.log('Setting total steps:', steps); // 디버깅용 + setTotalStepsState(steps); + setCurrentStep(1); + }, []); + + useEffect(() => { + console.log('Current step updated:', currentStep); // 디버깅용 + }, [currentStep]); + + return ( + + {children} + + ); +}; diff --git a/src/shared/hook/common/useBlockContext.tsx b/src/shared/hook/common/useBlockContext.tsx index 5ea4406c0..e24e36a87 100644 --- a/src/shared/hook/common/useBlockContext.tsx +++ b/src/shared/hook/common/useBlockContext.tsx @@ -9,11 +9,9 @@ interface BlockFormData { } interface BlockContextType { - step: number; - nextStep: () => void; - reset: () => void; formData: BlockFormData; setFormData: (data: Partial) => void; + resetFormData: () => void; } const BlockContext = createContext(undefined); @@ -21,13 +19,12 @@ const BlockContext = createContext(undefined); export const useBlockContext = () => { const context = useContext(BlockContext); if (!context) { - throw new Error('Error useBlockContext'); + throw new Error('Error: BlockContext must be used within a BlockProvider'); } return context; }; export const BlockProvider = ({ children }: { children: ReactNode }) => { - const [step, setStep] = useState(1); const [formData, setFormDataState] = useState({ blockName: '', blockType: '', @@ -36,10 +33,11 @@ export const BlockProvider = ({ children }: { children: ReactNode }) => { endDate: '', }); - const nextStep = useCallback(() => setStep((prev) => prev + 1), []); + const setFormData = useCallback((data: Partial) => { + setFormDataState((prev) => ({ ...prev, ...data })); + }, []); - const reset = useCallback(() => { - setStep(1); + const resetFormData = useCallback(() => { setFormDataState({ blockName: '', blockType: '', @@ -49,11 +47,5 @@ export const BlockProvider = ({ children }: { children: ReactNode }) => { }); }, []); - const setFormData = useCallback((data: Partial) => { - setFormDataState((prev) => ({ ...prev, ...data })); - }, []); - - return ( - {children} - ); + return {children}; }; diff --git a/src/shared/hook/common/useWorkSpaceContext.tsx b/src/shared/hook/common/useWorkSpaceContext.tsx index a9e5a4fad..6043c5e8e 100644 --- a/src/shared/hook/common/useWorkSpaceContext.tsx +++ b/src/shared/hook/common/useWorkSpaceContext.tsx @@ -7,11 +7,9 @@ interface WorkSpaceFormData { } interface WorkSpaceContextType { - step: number; - nextStep: () => void; - reset: () => void; formData: WorkSpaceFormData; setFormData: (data: Partial) => void; + resetFormData: () => void; } const WorkSpaceContext = createContext(undefined); @@ -19,23 +17,23 @@ const WorkSpaceContext = createContext(undefin export const useWorkSpaceContext = () => { const context = useContext(WorkSpaceContext); if (!context) { - throw new Error('Error useWorkSpaceContext'); + throw new Error('Error: WorkSpaceContext must be used within a WorkSpaceProvider'); } return context; }; export const WorkSpaceProvider = ({ children }: { children: ReactNode }) => { - const [step, setStep] = useState(1); const [formData, setFormDataState] = useState({ name: '', category: '', fileUrlData: '', }); - const nextStep = useCallback(() => setStep((prev) => prev + 1), []); + const setFormData = useCallback((data: Partial) => { + setFormDataState((prev) => ({ ...prev, ...data })); + }, []); - const reset = useCallback(() => { - setStep(1); + const resetFormData = useCallback(() => { setFormDataState({ name: '', category: '', @@ -43,13 +41,7 @@ export const WorkSpaceProvider = ({ children }: { children: ReactNode }) => { }); }, []); - const setFormData = useCallback((data: Partial) => { - setFormDataState((prev) => ({ ...prev, ...data })); - }, []); - return ( - - {children} - + {children} ); }; diff --git a/src/shared/util/funnelStep.tsx b/src/shared/util/funnelStep.tsx new file mode 100644 index 000000000..2e59f6079 --- /dev/null +++ b/src/shared/util/funnelStep.tsx @@ -0,0 +1,14 @@ +import { ReactNode } from 'react'; + +import { useFunnel } from '@/shared/hook/common/funnelContext'; + +interface FunnelStepProps { + step: number; + children: ReactNode; +} + +export const FunnelStep = ({ step, children }: FunnelStepProps) => { + const { currentStep } = useFunnel(); + console.log(`FunnelStep step: ${step}, currentStep: ${currentStep}`); // 디버깅용 + return currentStep === step ? <>{children} : null; +}; diff --git a/src/story/shared/DeleteModal.stories.tsx b/src/story/shared/DeleteModal.stories.tsx index 4e9191117..55073b827 100644 --- a/src/story/shared/DeleteModal.stories.tsx +++ b/src/story/shared/DeleteModal.stories.tsx @@ -1,11 +1,11 @@ import { Meta, StoryObj } from '@storybook/react'; -import ModalContainer from '@/shared/component/Modal/ModalContainer'; +import ModalFunnel from '@/shared/component/Modal/ModalFunnel'; import { useOpenModal } from '@/shared/store/modal'; -const meta: Meta = { +const meta: Meta = { title: 'Shared/Modal/Deleted', - component: ModalContainer, + component: ModalFunnel, parameters: { layout: 'centered', }, @@ -30,7 +30,7 @@ export const Deleted: Story = { <> - + ); }, diff --git a/src/story/shared/NewModals.stories.tsx b/src/story/shared/NewModals.stories.tsx index 170dbfbb1..862da34d6 100644 --- a/src/story/shared/NewModals.stories.tsx +++ b/src/story/shared/NewModals.stories.tsx @@ -1,11 +1,11 @@ import { Meta, StoryObj } from '@storybook/react'; -import ModalContainer from '@/shared/component/Modal/ModalContainer'; +import ModalFunnel from '@/shared/component/Modal/ModalFunnel'; import { useOpenModal } from '@/shared/store/modal'; -const meta: Meta = { +const meta: Meta = { title: 'Shared/Modal/ModalsTest', - component: ModalContainer, + component: ModalFunnel, parameters: { layout: 'centered', }, @@ -31,7 +31,7 @@ export const ModalsTest: Story = { - + ); },