diff --git a/src/App.tsx b/src/App.tsx index 11b1bc0f..8e295566 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,10 +8,10 @@ import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import ErrorBoundary from '@/common/component/ErrorBoundary/ErrorBoundary'; import { HTTPError } from '@/shared/api/HTTPError'; -import GlobalDrawer from '@/shared/component/GlobalDrawer/GlobalDrawer'; import Header from '@/shared/component/Header/Header'; import ModalContainer from '@/shared/component/Modal/ModalContainer'; import SideNavBar from '@/shared/component/SideNavBar/SideNavBar'; +import TimeBlockDrawer from '@/shared/component/TimeBlockDrawer/TimeBlockDrawer'; import { HTTP_STATUS_CODE } from '@/shared/constant/api'; import { PATH } from '@/shared/constant/path'; import ErrorPage from '@/shared/page/errorPage/ErrorPage'; @@ -54,7 +54,7 @@ const App = () => {
- + ); diff --git a/src/common/asset/svg/ic_close.svg b/src/common/asset/svg/ic_close.svg new file mode 100644 index 00000000..52e3a0b6 --- /dev/null +++ b/src/common/asset/svg/ic_close.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/asset/svg/ic_cloud_upload.svg b/src/common/asset/svg/ic_cloud_upload.svg new file mode 100644 index 00000000..85751022 --- /dev/null +++ b/src/common/asset/svg/ic_cloud_upload.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/asset/svg/ic_group.svg b/src/common/asset/svg/ic_group.svg new file mode 100644 index 00000000..749a7340 --- /dev/null +++ b/src/common/asset/svg/ic_group.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/common/asset/svg/ic_note_black.svg b/src/common/asset/svg/ic_note_black.svg new file mode 100644 index 00000000..7a5f639a --- /dev/null +++ b/src/common/asset/svg/ic_note_black.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/asset/svg/ic_paper.svg b/src/common/asset/svg/ic_paper.svg new file mode 100644 index 00000000..d61abf28 --- /dev/null +++ b/src/common/asset/svg/ic_paper.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/asset/svg/ic_subdirectory_arrow_right.svg b/src/common/asset/svg/ic_subdirectory_arrow_right.svg new file mode 100644 index 00000000..056aea43 --- /dev/null +++ b/src/common/asset/svg/ic_subdirectory_arrow_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/component/Button/Button.style.ts b/src/common/component/Button/Button.style.ts index 9109be35..e070c503 100644 --- a/src/common/component/Button/Button.style.ts +++ b/src/common/component/Button/Button.style.ts @@ -22,6 +22,8 @@ export const buttonStyle = css({ '&:disabled:not(:focus)': { backgroundColor: theme.colors.gray_100, color: theme.colors.gray_500, + + cursor: 'default', }, transition: 'all .2s ease-in', @@ -53,6 +55,14 @@ export const variantStyle = (variant: Required['variant']) => { backgroundColor: theme.colors.gray_200, }, }), + fourth: css({ + color: theme.colors.gray_800, + backgroundColor: theme.colors.white, + + '&:hover': { + backgroundColor: theme.colors.gray_100, + }, + }), outline: css({ color: theme.colors.gray_800, backgroundColor: theme.colors.white, diff --git a/src/common/component/Button/Button.tsx b/src/common/component/Button/Button.tsx index a6876c13..c49e7468 100644 --- a/src/common/component/Button/Button.tsx +++ b/src/common/component/Button/Button.tsx @@ -5,7 +5,7 @@ import { Size } from '@/common/type/design'; import { buttonStyle, sizeStyle, variantStyle } from './Button.style'; export interface ButtonProps extends ButtonHTMLAttributes { - variant?: 'primary' | 'secondary' | 'tertiary' | 'outline' | 'text'; + variant?: 'primary' | 'secondary' | 'tertiary' | 'fourth' | 'outline' | 'text'; size?: Extract; } diff --git a/src/common/component/CommandButton/CommandButton.style.ts b/src/common/component/CommandButton/CommandButton.style.ts index dc855d4c..92003608 100644 --- a/src/common/component/CommandButton/CommandButton.style.ts +++ b/src/common/component/CommandButton/CommandButton.style.ts @@ -40,7 +40,10 @@ export const commonStyle = css({ }); export const keyStyle = ( - variant: Extract<'primary' | 'tertiary' | 'outline', Omit['variant']> + variant: Extract< + 'primary' | 'tertiary' | 'fourth' | 'outline', + Omit['variant'] + > ) => { const style = { primary: css({ @@ -59,6 +62,14 @@ export const keyStyle = ( color: theme.colors.gray_500, }, }), + fourth: css({ + backgroundColor: theme.colors.gray_100, + + '&:disabled:not(:focus)': { + backgroundColor: theme.colors.gray_100, + color: theme.colors.gray_500, + }, + }), outline: css({ backgroundColor: theme.colors.gray_100, @@ -92,6 +103,12 @@ export const sizeStyle = (size: Required['size']) => { small: css({ padding: '0.7rem 1rem 0.7rem 1.4rem', + ...theme.text.body08, + }), + /** Button_24 */ + xSmall: css({ + padding: '0.6rem 1rem', + ...theme.text.body08, }), }; diff --git a/src/common/component/CommandButton/CommandButton.tsx b/src/common/component/CommandButton/CommandButton.tsx index b7311913..a57344f7 100644 --- a/src/common/component/CommandButton/CommandButton.tsx +++ b/src/common/component/CommandButton/CommandButton.tsx @@ -12,8 +12,8 @@ import { } from '@/common/component/CommandButton/CommandButton.style'; export interface CommandButtonProps extends ButtonProps { - variant?: Extract; - size?: Extract; + variant?: Extract; + size?: Extract; commandKey: string; isCommand?: boolean; isFrontIcon?: boolean; diff --git a/src/page/archiving/index/ArchivingPage.tsx b/src/page/archiving/index/ArchivingPage.tsx index 3218d060..b0f4901e 100644 --- a/src/page/archiving/index/ArchivingPage.tsx +++ b/src/page/archiving/index/ArchivingPage.tsx @@ -34,8 +34,12 @@ const ArchivingPage = () => { /** TODO: 추후 block id에 따른 API 응답으로 데이터 넣기 */ openDrawer({ title: selectedBlockFromDashboard.name, - startDate: '2024-09-13', - endDate: '2024-09-24', + startDate: new Date('2024-09-13'), + endDate: new Date('2024-09-24'), + blockType: 'MEETING', + color: '#FFE6E8', + taggedMembers: [], + handoverNotes: [], files: [], }); } diff --git a/src/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.style.ts b/src/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.style.ts new file mode 100644 index 00000000..962604c7 --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.style.ts @@ -0,0 +1,11 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const periodStyle = css({ + color: theme.colors.gray_800, +}); + +export const titleInputStyle = css({ + width: '21.4rem', +}); diff --git a/src/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.tsx b/src/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.tsx new file mode 100644 index 00000000..8280dbf5 --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.tsx @@ -0,0 +1,33 @@ +import DatePicker from '@/common/component/DatePicker'; +import Flex from '@/common/component/Flex/Flex'; +import Heading from '@/common/component/Heading/Heading'; +import Input from '@/common/component/Input/Input'; +import Text from '@/common/component/Text/Text'; + +import { periodStyle, titleInputStyle } from '@/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.style'; +import { formattingDate } from '@/page/archiving/index/util/date'; + +interface BlockInfoProps { + title: string; + startDate: Date; + endDate: Date; + isEditable: boolean; +} + +const BlockInfo = ({ title, startDate, endDate, isEditable }: BlockInfoProps) => { + return ( + + {isEditable ? : {title}} + + {isEditable ? ( + + ) : ( + + {formattingDate(startDate)} ~ {formattingDate(endDate)} + + )} + + ); +}; + +export default BlockInfo; diff --git a/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.style.ts b/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.style.ts new file mode 100644 index 00000000..163bd544 --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.style.ts @@ -0,0 +1,11 @@ +import { css } from '@emotion/react'; + +export const handoverNoteItemStyle = css({ + display: 'block', + + maxWidth: '20.6rem', + + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', +}); diff --git a/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.tsx b/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.tsx new file mode 100644 index 00000000..34548bc6 --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.tsx @@ -0,0 +1,23 @@ +import IcArrowRight from '@/common/asset/svg/ic_subdirectory_arrow_right.svg?react'; +import Button from '@/common/component/Button/Button'; +import Flex from '@/common/component/Flex/Flex'; + +import { handoverNoteItemStyle } from '@/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.style'; + +interface TaggedNoteItemProps { + title: string; + isEditable: boolean; +} + +const TaggedNoteItem = ({ title, isEditable }: TaggedNoteItemProps) => { + return ( + + + + + ); +}; + +export default TaggedNoteItem; diff --git a/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNotes.tsx b/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNotes.tsx new file mode 100644 index 00000000..be917a1a --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNotes.tsx @@ -0,0 +1,37 @@ +import IcNote from '@/common/asset/svg/ic_note_black.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Text from '@/common/component/Text/Text'; + +import TaggedNoteItem from '@/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem'; +import { listHeaderStyle } from '@/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style'; + +interface TaggedNotesProps { + isEditable: boolean; +} + +// TODO: API 추가 +const HANDOVER_NOTE_LIST = [ + { id: 1, title: '세상에서 제일 긴 제목을 지을거에요 나는 하하하하하하하ㅏ핳하' }, + { id: 2, title: 'OT 인수인계' }, + { id: 3, title: '엄마 나는 토스에 가고 싶어요' }, +]; + +const TaggedNotes = ({ isEditable }: TaggedNotesProps) => { + return ( + + + + + 태그된 인수인계 노트 + + + + {HANDOVER_NOTE_LIST.map((data) => ( + + ))} + + + ); +}; + +export default TaggedNotes; diff --git a/src/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style.ts b/src/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style.ts new file mode 100644 index 00000000..c31beeb7 --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style.ts @@ -0,0 +1,27 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const closeBtnStyle = css({ + position: 'absolute', + + right: '2.2rem', + top: '3.2rem', + + cursor: 'pointer', +}); + +export const circleStyle = (color: string) => + css({ + alignItems: 'center', + justifyContent: 'center', + + padding: '0.6rem', + + borderRadius: '18px', + backgroundColor: color, + }); + +export const listHeaderStyle = css({ + color: theme.colors.gray_800, +}); diff --git a/src/page/archiving/index/component/TimeBlockBar/TimeBlockBar.tsx b/src/page/archiving/index/component/TimeBlockBar/TimeBlockBar.tsx new file mode 100644 index 00000000..452d239e --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/TimeBlockBar.tsx @@ -0,0 +1,56 @@ +import { useState } from 'react'; + +import CloseButton from '@/common/asset/svg/ic_close.svg?react'; +import CommandButton from '@/common/component/CommandButton/CommandButton'; +import Flex from '@/common/component/Flex/Flex'; + +import BlockInfo from '@/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo'; +import TaggedNotes from '@/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNotes'; +import { circleStyle, closeBtnStyle } from '@/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style'; +import UploadedFiles from '@/page/archiving/index/component/TimeBlockBar/UploadedFiles/UploadedFiles'; +import { BLOCK_ICON } from '@/page/archiving/index/constant/icon'; + +import { DrawerContent } from '@/shared/store/drawer'; + +interface TimeBlockBarProps { + content: DrawerContent; + onCloseDrawer: () => void; +} + +const TimeBlockBar = ({ content, onCloseDrawer }: TimeBlockBarProps) => { + const [isEditable, setIsEditable] = useState(false); + + const handleEditClick = () => { + setIsEditable((prevState) => !prevState); + }; + + return ( + content && ( +
+ + + + + {BLOCK_ICON.find((icon) => icon.name === content.blockType)?.icon(content.color)} + + + {isEditable ? '저장' : '수정하기'} + + + + + + + + +
+ ) + ); +}; + +export default TimeBlockBar; diff --git a/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem.style.ts b/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem.style.ts new file mode 100644 index 00000000..be85e05f --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem.style.ts @@ -0,0 +1,47 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const containerStyle = css({ + gap: '1.2rem', + alignItems: 'center', + justifyContent: 'space-between', + + width: '100%', + padding: '0.6rem', + + backgroundColor: theme.colors.gray_100, + borderRadius: '8px', + + cursor: 'pointer', + + '&:hover': { + backgroundColor: theme.colors.gray_200, + }, +}); + +export const circleStyle = css({ + justifyContent: 'center', + alignItems: 'center', + + padding: '0.8rem', + + backgroundColor: theme.colors.white, + borderRadius: '16px', +}); + +export const fileTitleStyle = css({ + maxWidth: '15rem', + + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', +}); + +export const fileCapacityStyle = css({ + color: theme.colors.gray_800, +}); + +export const closeBtnStyle = css({ + cursor: 'pointer', +}); diff --git a/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem.tsx b/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem.tsx new file mode 100644 index 00000000..20d570c9 --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem.tsx @@ -0,0 +1,42 @@ +import CloseButton from '@/common/asset/svg/ic_close.svg?react'; +import IcPDF from '@/common/asset/svg/ic_pdf_file.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Text from '@/common/component/Text/Text'; + +import { + circleStyle, + closeBtnStyle, + containerStyle, + fileCapacityStyle, + fileTitleStyle, +} from '@/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem.style'; + +interface FileItemProps { + title: string; + capacity: string; + isEditable: boolean; +} + +const FileItem = ({ title, capacity, isEditable }: FileItemProps) => { + return ( +
  • + + + + + + + {title} + + + {capacity} + + + + + {isEditable && } +
  • + ); +}; + +export default FileItem; diff --git a/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/UploadedFiles.tsx b/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/UploadedFiles.tsx new file mode 100644 index 00000000..2103196d --- /dev/null +++ b/src/page/archiving/index/component/TimeBlockBar/UploadedFiles/UploadedFiles.tsx @@ -0,0 +1,45 @@ +import IcCloudUpload from '@/common/asset/svg/ic_cloud_upload.svg?react'; +import IcPaper from '@/common/asset/svg/ic_paper.svg?react'; +import Button from '@/common/component/Button/Button'; +import Flex from '@/common/component/Flex/Flex'; +import Text from '@/common/component/Text/Text'; + +import { listHeaderStyle } from '@/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style'; +import FileItem from '@/page/archiving/index/component/TimeBlockBar/UploadedFiles/FileItem/FileItem'; + +interface UploadedFileProps { + isEditable: boolean; +} + +// TODO: API 추가 +const UPLOADED_FILE_LIST = [ + { id: 1, title: '세상에서 제일 긴 제목을 지을거에요 나는', capacity: '2.4 MB' }, + { id: 2, title: 'OT 인수인계', capacity: '2.9 MB' }, + { id: 3, title: '엄마 나는 토스에 가고 싶어요', capacity: '10.7 MB' }, +]; + +const UploadedFile = ({ isEditable }: UploadedFileProps) => { + return ( + + + + + 업로드된 파일 + + + {isEditable && ( + + )} + + {UPLOADED_FILE_LIST.map((data) => ( + + ))} + + + ); +}; + +export default UploadedFile; diff --git a/src/page/archiving/index/hook/common/useInteractTimeline.ts b/src/page/archiving/index/hook/common/useInteractTimeline.ts index 59e20ac2..13500494 100644 --- a/src/page/archiving/index/hook/common/useInteractTimeline.ts +++ b/src/page/archiving/index/hook/common/useInteractTimeline.ts @@ -21,8 +21,12 @@ export const useInteractTimeline = () => { /** TODO: 추후 block id에 따른 API 응답으로 데이터 넣기 */ openDrawer({ title: 'OT 준비', - startDate: '2024-09-13', - endDate: '2024-09-24', + startDate: new Date('2024.09.13'), + endDate: new Date('2024.09.15'), + blockType: 'MEETING', + color: '#FFE6E8', + taggedMembers: [], + handoverNotes: [], files: [], }); }; diff --git a/src/shared/component/GlobalDrawer/GlobalDrawer.style.ts b/src/shared/component/TimeBlockDrawer/TimeBlockDrawer.style.ts similarity index 82% rename from src/shared/component/GlobalDrawer/GlobalDrawer.style.ts rename to src/shared/component/TimeBlockDrawer/TimeBlockDrawer.style.ts index a4247f9e..fb4cbd3c 100644 --- a/src/shared/component/GlobalDrawer/GlobalDrawer.style.ts +++ b/src/shared/component/TimeBlockDrawer/TimeBlockDrawer.style.ts @@ -4,17 +4,18 @@ import { theme } from '@/common/style/theme/theme'; export const containerStyle = (isOpen: boolean) => css({ + flexShrink: 0, position: 'sticky', right: 0, - zIndex: theme.zIndex.overlayMiddle, width: isOpen ? '27rem' : 0, height: 'calc(100vh)', + padding: '0 1.6rem', overflow: 'hidden', - borderRadius: '16px', + borderLeft: `1px solid ${theme.colors.gray_300}`, boxShadow: '0px 2px 10px 0px rgba(0, 0, 0, 0.10)', backgroundColor: theme.colors.white, diff --git a/src/shared/component/GlobalDrawer/GlobalDrawer.tsx b/src/shared/component/TimeBlockDrawer/TimeBlockDrawer.tsx similarity index 63% rename from src/shared/component/GlobalDrawer/GlobalDrawer.tsx rename to src/shared/component/TimeBlockDrawer/TimeBlockDrawer.tsx index 6cad31ce..877d0d1b 100644 --- a/src/shared/component/GlobalDrawer/GlobalDrawer.tsx +++ b/src/shared/component/TimeBlockDrawer/TimeBlockDrawer.tsx @@ -1,14 +1,15 @@ import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; -import Heading from '@/common/component/Heading/Heading'; import { useOutsideClick } from '@/common/hook'; -import { containerStyle } from '@/shared/component/GlobalDrawer/GlobalDrawer.style'; +import TimeBlockBar from '@/page/archiving/index/component/TimeBlockBar/TimeBlockBar'; + +import { containerStyle } from '@/shared/component/TimeBlockDrawer/TimeBlockDrawer.style'; import { PATH } from '@/shared/constant/path'; import { useDrawerAction, useDrawerContent, useDrawerIsOpen } from '@/shared/store/drawer'; -const GlobalDrawer = () => { +const TimeBlockDrawer = () => { const isOpen = useDrawerIsOpen(); const { closeDrawer } = useDrawerAction(); const content = useDrawerContent(); @@ -25,13 +26,12 @@ const GlobalDrawer = () => { }, [isOpen, closeDrawer, pathname]); return ( - + content && ( + + ) ); }; -export default GlobalDrawer; +export default TimeBlockDrawer; diff --git a/src/shared/store/drawer.ts b/src/shared/store/drawer.ts index 3edf9810..6ce877d9 100644 --- a/src/shared/store/drawer.ts +++ b/src/shared/store/drawer.ts @@ -2,10 +2,14 @@ import { create } from 'zustand'; import { File } from '@/shared/type/file'; -type DrawerContent = { +export type DrawerContent = { title: string; - startDate: string; - endDate: string; + startDate: Date; + endDate: Date; + blockType: string; + color: string; + taggedMembers: []; + handoverNotes: []; files: File[]; } | null;