From c68d4d8a70043e17d53ba06ce7ecfd4742b2dadc Mon Sep 17 00:00:00 2001 From: bowoon <105543811+Bowoon1216@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:34:29 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20Dashboard=20Page=20=ED=8D=BC=EB=B8=94?= =?UTF-8?q?=EB=A6=AC=EC=8B=B1=20(#310)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #244 init:초기세팅 * feat: list item의 tag 컴포넌트 * feat: tag storybook 생성 * feat: listItem 폴더 추가 * feat: ListItem 대충 틀 잡기 * feat: tag line 로직 구현 * feat: date 부분 구현 * feat: ListItem 전체 스타일링 * feat: ListItem storybook update * feat: fileGrid small size 적용 * feat: folderGrid small size 적용 * chore: root 설정 * feat: fileGrid width 조wjd * refactor: ContentBox minHeight 제거 * feat: content box로 구조잡기 * feat: contentBox variant handover 추가 및 fileGrid minWidht설정 * feat: fileView publishing * style: 잔잔바리 수정 * feat:인수인계노트 content 추가 * style: contentBox 조정 * style: handover scroll 조정 * chore: Dashboard naming 수정 * feat: timeline section 구현 * style: 인수인계노트 부분 height 안 맞는 문제 css 선택자로 해결 * feat: ListItem의 tag logic 마지막 tag 확인안하는 이슈 해결 * feat: timeline 부분 헤더 버튼 덮어쓰기 * fix: 쓸데없는 임포트 제거 * refactor: fileGrid variant 생성 * refactor: key 설정(+타입), 로직 분리 * fix: build error 해결 * refactor/fix: tags 너비 초과 로직 구현 * refactor: ArchivingPage 제거 * fix: tag 너비 초과 로직에서 너비 이슈 수정 * feat: timeline 제작 및 드로어 오픈 설정 * fix: build error 수정 * refactor: 각 section 컴포넌트로 분리 * fix: build error 잡기! --------- Co-authored-by: Jeongbowoon --- package.json | 1 + src/common/asset/svg/ic_calendar_ver2.svg | 10 ++ src/common/asset/svg/ic_note.svg | 3 + src/common/router/Router.tsx | 4 +- src/page/archiving/index/ArchivingPage.tsx | 25 ++++- src/page/dashboard/DashboardPage.style.ts | 24 +++++ src/page/dashboard/DashboardPage.tsx | 64 ++++++++++++ .../dashboard/component/File/FileSection.tsx | 22 +++++ .../Handover/HandoverSection.style.ts | 18 ++++ .../component/Handover/HandoverSection.tsx | 19 ++++ .../Handover/ListItem/ListItem.style.ts | 53 ++++++++++ .../component/Handover/ListItem/ListItem.tsx | 72 ++++++++++++++ .../Timeline/TimelineSection.style.ts | 18 ++++ .../component/Timeline/TimelineSection.tsx | 61 ++++++++++++ src/page/dashboard/constant/notes.ts | 97 +++++++++++++++++++ src/page/dashboard/type/listTag.ts | 5 + src/page/dashboard/type/note.ts | 9 ++ src/page/dashboard/util/alignTags.ts | 27 ++++++ src/page/dashboard/util/color.ts | 22 +++++ .../component/ContentBox/ContentBox.style.ts | 4 +- .../component/ContentBox/ContentBox.tsx | 4 +- src/shared/component/FileGrid/FileGrid.tsx | 45 +++++---- src/shared/component/FileGrid/FolderGrid.tsx | 39 ++++---- src/shared/component/FileGrid/index.style.ts | 27 +++--- src/shared/type/content.ts | 2 +- .../dashboard/component/ListItem.stories.tsx | 60 ++++++++++++ src/story/shared/FileGrid.stories.tsx | 15 +++ src/story/shared/FolderGrid.stories.tsx | 14 +++ 28 files changed, 707 insertions(+), 57 deletions(-) create mode 100644 src/common/asset/svg/ic_calendar_ver2.svg create mode 100644 src/common/asset/svg/ic_note.svg create mode 100644 src/page/dashboard/DashboardPage.style.ts create mode 100644 src/page/dashboard/DashboardPage.tsx create mode 100644 src/page/dashboard/component/File/FileSection.tsx create mode 100644 src/page/dashboard/component/Handover/HandoverSection.style.ts create mode 100644 src/page/dashboard/component/Handover/HandoverSection.tsx create mode 100644 src/page/dashboard/component/Handover/ListItem/ListItem.style.ts create mode 100644 src/page/dashboard/component/Handover/ListItem/ListItem.tsx create mode 100644 src/page/dashboard/component/Timeline/TimelineSection.style.ts create mode 100644 src/page/dashboard/component/Timeline/TimelineSection.tsx create mode 100644 src/page/dashboard/constant/notes.ts create mode 100644 src/page/dashboard/type/listTag.ts create mode 100644 src/page/dashboard/type/note.ts create mode 100644 src/page/dashboard/util/alignTags.ts create mode 100644 src/page/dashboard/util/color.ts create mode 100644 src/story/page/dashboard/component/ListItem.stories.tsx diff --git a/package.json b/package.json index 4cd81abeb..09e8b68cc 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "axios": "^1.7.2", "framer-motion": "^11.11.11", "mime": "^4.0.4", + "framer-motion": "^11.11.11", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.53.1", diff --git a/src/common/asset/svg/ic_calendar_ver2.svg b/src/common/asset/svg/ic_calendar_ver2.svg new file mode 100644 index 000000000..93e2d4629 --- /dev/null +++ b/src/common/asset/svg/ic_calendar_ver2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/common/asset/svg/ic_note.svg b/src/common/asset/svg/ic_note.svg new file mode 100644 index 000000000..1b8bc581d --- /dev/null +++ b/src/common/asset/svg/ic_note.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/router/Router.tsx b/src/common/router/Router.tsx index 80b8ea6c6..b469f66ef 100644 --- a/src/common/router/Router.tsx +++ b/src/common/router/Router.tsx @@ -19,6 +19,8 @@ import { TermPage, } from '@/common/router/lazy'; +import DashboardPage from '@/page/dashboard/DashboardPage'; + import { PATH } from '@/shared/constant/path'; const Public = () => { @@ -114,7 +116,7 @@ const router = createBrowserRouter([ path: PATH.DASHBOARD, element: ( -

대쉬보드 페이지입니다.

+
), }, diff --git a/src/page/archiving/index/ArchivingPage.tsx b/src/page/archiving/index/ArchivingPage.tsx index c015b7d20..99ec741df 100644 --- a/src/page/archiving/index/ArchivingPage.tsx +++ b/src/page/archiving/index/ArchivingPage.tsx @@ -1,4 +1,5 @@ -import { Suspense } from 'react'; +import { Suspense, useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; import Button from '@/common/component/Button/Button'; import Flex from '@/common/component/Flex/Flex'; @@ -8,8 +9,10 @@ import DateProvider from '@/page/archiving/index/DateProvider'; import TimeLine from '@/page/archiving/index/component/TimeLine'; import TimeLineHeader from '@/page/archiving/index/component/TimeLine/TimeLineHeader/TimeLineHeader'; import { useInteractTimeline } from '@/page/archiving/index/hook/common/useInteractTimeline'; +import { Block } from '@/page/archiving/index/type/blockType'; import ContentBox from '@/shared/component/ContentBox/ContentBox'; +import { useDrawerAction } from '@/shared/store/drawer'; import { useOpenModal } from '@/shared/store/modal'; const ArchivingPage = () => { @@ -18,6 +21,24 @@ const ArchivingPage = () => { const openModal = useOpenModal(); + const location = useLocation(); + const selectedBlockFromDashboard: Block = location.state?.selectedBlock; + const finalSelectedBlock = selectedBlockFromDashboard || selectedBlock; + + const { openDrawer } = useDrawerAction(); + + useEffect(() => { + if (selectedBlockFromDashboard) { + /** TODO: 추후 block id에 따른 API 응답으로 데이터 넣기 */ + openDrawer({ + title: selectedBlockFromDashboard.name, + startDate: '2024-09-13', + endDate: '2024-09-24', + files: [], + }); + } + }, [openDrawer, selectedBlockFromDashboard]); + const handleOpenBlockModal = () => { openModal('create-block'); }; @@ -38,7 +59,7 @@ const ArchivingPage = () => { {/** fallback UI 디자인 나올 시에 TimeLine 크기만큼 채워서 Layout 안움직이도록 */} - + diff --git a/src/page/dashboard/DashboardPage.style.ts b/src/page/dashboard/DashboardPage.style.ts new file mode 100644 index 000000000..d3f74f7d4 --- /dev/null +++ b/src/page/dashboard/DashboardPage.style.ts @@ -0,0 +1,24 @@ +import { css } from '@emotion/react'; + +export const contentBoxStyle = css({ + '& > header': { height: '6.1rem' }, + '& > div': { marginTop: '0' }, +}); + +export const dashboradScrollStyle = css({ + '::-webkit-scrollbar': { + width: '0.6rem', + height: '0.6rem', + }, +}); + +export const handoverBoxStyle = css({ + width: '30%', + height: '64rem', + + paddingRight: '1rem', + + '&>div:last-child': { + height: '85%', + }, +}); diff --git a/src/page/dashboard/DashboardPage.tsx b/src/page/dashboard/DashboardPage.tsx new file mode 100644 index 000000000..9eebae0f2 --- /dev/null +++ b/src/page/dashboard/DashboardPage.tsx @@ -0,0 +1,64 @@ +import { useNavigate } from 'react-router-dom'; + +import Button from '@/common/component/Button/Button'; +import Flex from '@/common/component/Flex/Flex'; + +import { contentBoxStyle, handoverBoxStyle } from '@/page/dashboard/DashboardPage.style'; +import FileSection from '@/page/dashboard/component/File/FileSection'; +import HandoverSection from '@/page/dashboard/component/Handover/HandoverSection'; +import TimelineSection from '@/page/dashboard/component/Timeline/TimelineSection'; + +import ContentBox from '@/shared/component/ContentBox/ContentBox'; +import { PATH } from '@/shared/constant/path'; + +const DashboardPage = () => { + const navigate = useNavigate(); + + const handleNav = (path: string) => { + navigate(path); + }; + + return ( + + + handleNav(PATH.DRIVE)}> + 전체보기 + + } + css={[{ height: '21.6rem' }, contentBoxStyle]}> + + + + handleNav(PATH.ARCHIVING)}> + 전체보기 + + } + css={[{ height: '40rem' }, contentBoxStyle]}> + + + + + handleNav(PATH.HANDOVER)}> + 전체보기 + + } + css={[handoverBoxStyle, contentBoxStyle]}> + + + + ); +}; + +export default DashboardPage; diff --git a/src/page/dashboard/component/File/FileSection.tsx b/src/page/dashboard/component/File/FileSection.tsx new file mode 100644 index 000000000..8058bfed1 --- /dev/null +++ b/src/page/dashboard/component/File/FileSection.tsx @@ -0,0 +1,22 @@ +import Flex from '@/common/component/Flex/Flex'; +import { scrollStyle } from '@/common/style/scroll'; + +import { dashboradScrollStyle } from '@/page/dashboard/DashboardPage.style'; + +import FileGrid from '@/shared/component/FileGrid/FileGrid'; + +import { FileData } from '@/mock/data/drive'; + +const FileSection = () => { + return ( + + {FileData.map((file) => { + return ( + + ); + })} + + ); +}; + +export default FileSection; diff --git a/src/page/dashboard/component/Handover/HandoverSection.style.ts b/src/page/dashboard/component/Handover/HandoverSection.style.ts new file mode 100644 index 000000000..616eb62ad --- /dev/null +++ b/src/page/dashboard/component/Handover/HandoverSection.style.ts @@ -0,0 +1,18 @@ +import { css } from '@emotion/react'; + +import { scrollStyle } from '@/common/style/scroll'; + +import { dashboradScrollStyle } from '@/page/dashboard/DashboardPage.style'; + +export const listItemStyle = css( + { + flexDirection: 'column', + gap: '0.8rem', + padding: '0 0.4rem', + height: '100%', + + overflowY: 'scroll', + }, + scrollStyle, + dashboradScrollStyle +); diff --git a/src/page/dashboard/component/Handover/HandoverSection.tsx b/src/page/dashboard/component/Handover/HandoverSection.tsx new file mode 100644 index 000000000..83371c8ca --- /dev/null +++ b/src/page/dashboard/component/Handover/HandoverSection.tsx @@ -0,0 +1,19 @@ +import Flex from '@/common/component/Flex/Flex'; + +import { listItemStyle } from '@/page/dashboard/component/Handover/HandoverSection.style'; +import ListItem from '@/page/dashboard/component/Handover/ListItem/ListItem'; +import { Notes } from '@/page/dashboard/constant/notes'; + +const HandoverSection = () => { + return ( + + {Notes.map((note) => { + return ( + + ); + })} + + ); +}; + +export default HandoverSection; diff --git a/src/page/dashboard/component/Handover/ListItem/ListItem.style.ts b/src/page/dashboard/component/Handover/ListItem/ListItem.style.ts new file mode 100644 index 000000000..7c1cb240a --- /dev/null +++ b/src/page/dashboard/component/Handover/ListItem/ListItem.style.ts @@ -0,0 +1,53 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const containerStyle = css({ + flexDirection: 'column', + alignItems: 'center', + + width: '100%', + padding: '1.6rem', + + backgroundColor: theme.colors.gray_100, + borderRadius: '8px', + + ':hover': { + backgroundColor: theme.colors.gray_200, + + transition: 'all ease 0.5s', + }, +}); + +export const titleStyle = css({ + width: '100%', + paddingBottom: '0.6rem', + + color: theme.colors.black, +}); + +export const contentStyle = css({ + width: '100%', + paddingBottom: '1.6rem', + + color: theme.colors.gray_800, + + overflow: 'hidden', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', +}); + +export const detailContainerStyle = css({ + justifyContent: 'space-between', + alignItems: 'center', + + width: '100%', + + overflow: 'hidden', +}); + +export const detailStyle = css({ + alignItems: 'center', + + gap: '0.4rem', +}); diff --git a/src/page/dashboard/component/Handover/ListItem/ListItem.tsx b/src/page/dashboard/component/Handover/ListItem/ListItem.tsx new file mode 100644 index 000000000..4b8d0b160 --- /dev/null +++ b/src/page/dashboard/component/Handover/ListItem/ListItem.tsx @@ -0,0 +1,72 @@ +import { HTMLAttributes, useRef } from 'react'; + +import Calender from '@/common/asset/svg/ic_calendar_ver2.svg?react'; +import Flex from '@/common/component/Flex/Flex'; +import Tag from '@/common/component/Tag/Tag'; +import Text from '@/common/component/Text/Text'; +import { theme } from '@/common/style/theme/theme'; + +import { + containerStyle, + contentStyle, + detailContainerStyle, + detailStyle, + titleStyle, +} from '@/page/dashboard/component/Handover/ListItem/ListItem.style'; +import { ListTag } from '@/page/dashboard/type/listTag'; +import { getVisibleTags } from '@/page/dashboard/util/alignTags'; +import { alignColor } from '@/page/dashboard/util/color'; + +export interface ListItemProps extends HTMLAttributes { + title: string; + content: string; + date: Date; + tags?: ListTag[]; +} + +const ListItem = ({ title, content, date, tags = [], ...props }: ListItemProps) => { + const tagContanierRef = useRef(null); + const visibleTags = getVisibleTags(tags, 210, 4); + + return ( + + + {title} + + + {content} + + + +
+ {visibleTags.map((tag) => { + return ( + + {tag.content} + + ); + })} + {visibleTags.length < tags.length && ( + + +{tags.length - visibleTags.length} + + )} +
+ + + + {`${date.getFullYear()}.${date.getMonth() + 1}.${date.getDate()}`} + + +
+
+ ); +}; + +export default ListItem; diff --git a/src/page/dashboard/component/Timeline/TimelineSection.style.ts b/src/page/dashboard/component/Timeline/TimelineSection.style.ts new file mode 100644 index 000000000..da41b09df --- /dev/null +++ b/src/page/dashboard/component/Timeline/TimelineSection.style.ts @@ -0,0 +1,18 @@ +import { css } from '@emotion/react'; + +import { scrollStyle } from '@/common/style/scroll'; + +import { dashboradScrollStyle } from '@/page/dashboard/DashboardPage.style'; + +export const timelineContentStyle = css( + { + minHeight: '23.6rem', + height: '23.6rem', + + padding: '0.6rem 0', + + borderRadius: '8px', + }, + scrollStyle, + dashboradScrollStyle +); diff --git a/src/page/dashboard/component/Timeline/TimelineSection.tsx b/src/page/dashboard/component/Timeline/TimelineSection.tsx new file mode 100644 index 000000000..985b85f31 --- /dev/null +++ b/src/page/dashboard/component/Timeline/TimelineSection.tsx @@ -0,0 +1,61 @@ +import { useNavigate } from 'react-router-dom'; + +import DateProvider from '@/page/archiving/index/DateProvider'; +import Day from '@/page/archiving/index/component/TimeLine/Day/Day'; +import { dayBodyStyle } from '@/page/archiving/index/component/TimeLine/Day/Day.style'; +import TimeBlock from '@/page/archiving/index/component/TimeLine/TimeBlock/TimeBlock'; +import TimeLineHeader from '@/page/archiving/index/component/TimeLine/TimeLineHeader/TimeLineHeader'; +import { useGetTimeBlockQuery } from '@/page/archiving/index/hook/api/useGetTimeBlockQuery'; +import { useDate } from '@/page/archiving/index/hook/common/useDate'; +import { Block } from '@/page/archiving/index/type/blockType'; +import { alignBlocks, createTimeBlock } from '@/page/archiving/index/util/block'; +import { timelineContentStyle } from '@/page/dashboard/component/Timeline/TimelineSection.style'; + +import { PATH } from '@/shared/constant/path'; + +const TimelineSection = () => { + const navigate = useNavigate(); + + const teamId = localStorage.getItem('teamId'); + const { currentYear, currentMonth, endDay } = useDate(teamId!); + + const { data } = useGetTimeBlockQuery(+teamId!, 'executive', currentYear, currentMonth); + + const timeBlocks: Block[] = data.timeBlocks; + const blockFloors = alignBlocks(timeBlocks, endDay, currentMonth, currentYear); + + return ( + + + +
+ {timeBlocks.map((block) => { + const { startDate, endDate } = block; + const { startDate: blockStartDate, endDate: blockEndDate } = createTimeBlock({ + startDate: new Date(startDate), + endDate: new Date(endDate), + currentYear, + currentMonth, + }); + + return ( + { + navigate(PATH.ARCHIVING, { state: { selectedBlock: block } }); + }}> + {block.name} + + ); + })} +
+
+ ); +}; + +export default TimelineSection; diff --git a/src/page/dashboard/constant/notes.ts b/src/page/dashboard/constant/notes.ts new file mode 100644 index 000000000..84062a10b --- /dev/null +++ b/src/page/dashboard/constant/notes.ts @@ -0,0 +1,97 @@ +import { Note } from '@/page/dashboard/type/note'; + +export const Notes: Note[] = [ + { + noteId: 1, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [ + { tagId: 1, content: 'OT준비', bgColor: '#E2E8F8' }, + { tagId: 2, content: '먀옹', bgColor: '#F8E2CB' }, + { tagId: 3, content: '매옹', bgColor: '#F8E2CB' }, + { tagId: 4, content: '난나난', bgColor: '#F8E2CB' }, + ], + }, + { + noteId: 2, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [ + { tagId: 1, content: '고양이', bgColor: '#F8E2CB' }, + { tagId: 2, content: 'D.C.', bgColor: '#F8E1F5' }, + { tagId: 3, content: 'DormCat', bgColor: '#C4F2E5' }, + { tagId: 4, content: 'I love', bgColor: '#F8E2CB' }, + ], + }, + { + noteId: 3, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [ + { tagId: 1, content: '귀여운', bgColor: '#F8E1F5' }, + { tagId: 2, content: '봄 MT', bgColor: '#F8E1F5' }, + { tagId: 3, content: '가을 MT', bgColor: '#F8E1F5' }, + { tagId: 4, content: '학과 엠티', bgColor: '#F8E1F5' }, + { tagId: 5, content: '집부 엠티', bgColor: '#F8E1F5' }, + { tagId: 6, content: '공대 엠티', bgColor: '#F8E1F5' }, + ], + }, + { + noteId: 4, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [ + { tagId: 1, content: '낄낄', bgColor: '#DCD8FA' }, + { tagId: 2, content: '엠티 기간', bgColor: '#F8E2CB' }, + { tagId: 3, content: '엠티 준비 위원회 회의', bgColor: '#DCD8FA' }, + { tagId: 4, content: '엠엠', bgColor: '#F8E2CB' }, + { tagId: 5, content: 'study', bgColor: '#DCD8FA' }, + { tagId: 6, content: 'event', bgColor: '#F8E2CB' }, + { tagId: 7, content: 'study', bgColor: '#DCD8FA' }, + { tagId: 8, content: 'event', bgColor: '#F8E2CB' }, + ], + }, + { + noteId: 5, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [{ tagId: 1, content: '시험기간', bgColor: '#F8E1F5' }], + }, + { + noteId: 6, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [ + { tagId: 1, content: 'study', bgColor: '#F8E1F5' }, + { tagId: 2, content: 'notice', bgColor: '#C4F2E5' }, + ], + }, + { + noteId: 7, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [ + { tagId: 1, content: 'recruiting', bgColor: '#E2E8F8' }, + { tagId: 2, content: 'event', bgColor: '#F8E2CB' }, + { tagId: 3, content: 'event', bgColor: '#F8E2CB' }, + { tagId: 4, content: 'event', bgColor: '#F8E2CB' }, + ], + }, + { + noteId: 8, + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다.티키의 3번째 정기 회의록입니다.', + date: new Date(), + tags: [ + { tagId: 1, content: 'study', bgColor: '#F8E1F5' }, + { tagId: 2, content: 'notice', bgColor: '#C4F2E5' }, + ], + }, +]; diff --git a/src/page/dashboard/type/listTag.ts b/src/page/dashboard/type/listTag.ts new file mode 100644 index 000000000..32ad54fc7 --- /dev/null +++ b/src/page/dashboard/type/listTag.ts @@ -0,0 +1,5 @@ +export type ListTag = { + tagId: number; + content: string; + bgColor: string; +}; diff --git a/src/page/dashboard/type/note.ts b/src/page/dashboard/type/note.ts new file mode 100644 index 000000000..60b919125 --- /dev/null +++ b/src/page/dashboard/type/note.ts @@ -0,0 +1,9 @@ +import { ListTag } from '@/page/dashboard/type/listTag'; + +export type Note = { + noteId: number; + title: string; + content: string; + date: Date; + tags: ListTag[]; +}; diff --git a/src/page/dashboard/util/alignTags.ts b/src/page/dashboard/util/alignTags.ts new file mode 100644 index 000000000..229a411d3 --- /dev/null +++ b/src/page/dashboard/util/alignTags.ts @@ -0,0 +1,27 @@ +import { ListTag } from '@/page/dashboard/type/listTag'; + +export const getVisibleTags = (tags: ListTag[], maxWidth: number, tagGap: number): ListTag[] => { + let totalWidth = 0; + const visibleTags: ListTag[] = []; + + for (const tag of tags) { + //임시 요소 생성 + const tempElement = document.createElement('span'); + tempElement.style.display = 'inline-block'; + tempElement.style.visibility = 'hidden'; + tempElement.style.position = 'absolute'; + tempElement.innerText = tag.content; + + //임시 요소 추가 후 너비 측정 + document.body.appendChild(tempElement); + const tagWidth = tempElement.offsetWidth; + document.body.removeChild(tempElement); + + //현재 태그까지 추가 후 너비 초과 검사 + if (totalWidth + tagWidth * 2 + tagGap > maxWidth) break; + totalWidth += tagWidth * 2 + tagGap; + visibleTags.push(tag); + } + + return visibleTags; +}; diff --git a/src/page/dashboard/util/color.ts b/src/page/dashboard/util/color.ts new file mode 100644 index 000000000..69913149c --- /dev/null +++ b/src/page/dashboard/util/color.ts @@ -0,0 +1,22 @@ +import { theme } from '@/common/style/theme/theme'; + +export const alignColor = (color: string) => { + switch (color) { + case '#FFE6E8': + return theme.colors.red_200; + case '#F8E2CB': + return theme.colors.yellow_200; + case '#C4F2E5': + return theme.colors.green_200; + case '#DCD8FA': + return theme.colors.purple_200; + case '#E2E8F8': + return theme.colors.blue_200; + case '#F8E1F5': + return theme.colors.pink_200; + case '#D3EFFA': + return theme.colors.sky_200; + default: + return theme.colors.white; + } +}; diff --git a/src/shared/component/ContentBox/ContentBox.style.ts b/src/shared/component/ContentBox/ContentBox.style.ts index cacaf48f3..2b8a821bf 100644 --- a/src/shared/component/ContentBox/ContentBox.style.ts +++ b/src/shared/component/ContentBox/ContentBox.style.ts @@ -6,7 +6,8 @@ export const sectionStyle = css({ position: 'relative', width: '100%', - minHeight: 'calc(100vh - 11.6rem - 4.8rem - 2rem)', + + height: 'calc(100vh - 11.6rem - 4.8rem - 2rem)', padding: '0 1.6rem', @@ -65,7 +66,6 @@ export const contentOptionStyle = css({ export const contentStyle = css({ width: '100%', - /** 100vh - (상단 헤더 높이 + 헤더옵션 높이 + 컨텐츠옵션 높이 + 레이아웃 패딩바텀 + 레이아웃 패딩탑 + 컨텐츠 박스 border 사이즈 + 컨텐츠스타일의 마진탑) */ height: 'calc(100vh - 11.6rem - 7.2rem - 6.4rem - 4.8rem - 2rem - 0.2rem - 0.8rem)', marginTop: '0.8rem', diff --git a/src/shared/component/ContentBox/ContentBox.tsx b/src/shared/component/ContentBox/ContentBox.tsx index eff3da6a0..6141c4dcd 100644 --- a/src/shared/component/ContentBox/ContentBox.tsx +++ b/src/shared/component/ContentBox/ContentBox.tsx @@ -1,6 +1,7 @@ import { ComponentPropsWithoutRef, ReactNode } from 'react'; import IcFile from '@/common/asset/svg/ic_file.svg?react'; +import ICNote from '@/common/asset/svg/ic_note.svg?react'; import IcTimeLine from '@/common/asset/svg/ic_timeline.svg?react'; import IcDeleted from '@/common/asset/svg/ic_trash.svg?react'; import Heading from '@/common/component/Heading/Heading'; @@ -18,7 +19,7 @@ import { import { Content } from '@/shared/type/content'; interface ContentBoxProps extends ComponentPropsWithoutRef<'section'> { - /** ContentBox를 활용할 도메인, timeline | file | deleted */ + /** ContentBox를 활용할 도메인, timeline | file | deleted | handover*/ variant: Content; title: string; @@ -33,6 +34,7 @@ const ICON_BY_VARIANT = { timeline: , file: , deleted: , + handover: , }; const ContentBox = ({ diff --git a/src/shared/component/FileGrid/FileGrid.tsx b/src/shared/component/FileGrid/FileGrid.tsx index 156bfd0ff..68567c220 100644 --- a/src/shared/component/FileGrid/FileGrid.tsx +++ b/src/shared/component/FileGrid/FileGrid.tsx @@ -21,7 +21,8 @@ import { import { File } from '@/shared/type/file'; import { getFileVolume } from '@/shared/util/file'; -type FileGridProps = { +export type FileGridProps = { + variant?: 'primary' | 'secondary'; title: string; /** API 명세에 따라 달라질 수 있음 + 추후 삭제 */ type: File['type']; @@ -45,7 +46,7 @@ const getIconByType = (type: string) => { } }; -const FileGrid = ({ title, type, volume }: FileGridProps) => { +const FileGrid = ({ title, type, volume, variant = 'primary' }: FileGridProps) => { const { isOpen, close, toggle } = useOverlay(); const optionRef = useRef(null); @@ -60,33 +61,35 @@ const FileGrid = ({ title, type, volume }: FileGridProps) => { }; return ( -
-
{getIconByType(type)}
+
+
{getIconByType(type)}
{title} - -
- -
- - {}}> - 파일 다운로드 - - {}}> - 휴지통으로 이동 - - {}}> - 인수인계 노트 보기 - - -
+ {variant === 'primary' && ( + +
+ +
+ + {}}> + 파일 다운로드 + + {}}> + 휴지통으로 이동 + + {}}> + 인수인계 노트 보기 + + +
+ )}
diff --git a/src/shared/component/FileGrid/FolderGrid.tsx b/src/shared/component/FileGrid/FolderGrid.tsx index b5be1fa2f..cad382f86 100644 --- a/src/shared/component/FileGrid/FolderGrid.tsx +++ b/src/shared/component/FileGrid/FolderGrid.tsx @@ -12,6 +12,7 @@ import { OPTION_ICON } from '@/shared/component/FileGrid/icon'; import { cardStyle, iconWrapperStyle, nameStyle, optionTextStyle } from '@/shared/component/FileGrid/index.style'; type FolderGridProps = { + variant?: 'primary' | 'secondary'; title: string; /** API 명세에 따라 달라질 수 있음 + 추후 삭제 */ @@ -23,32 +24,36 @@ type FolderGridProps = { */ }; -const FolderGrid = ({ title }: FolderGridProps) => { +const FolderGrid = ({ title, variant = 'primary' }: FolderGridProps) => { const { isOpen, close, toggle } = useOverlay(); return ( -
-
{}
+
+
{}
{title} - - - - console.log('select')}> - 이름 변경 - - console.log('select')}> - 폴더 전체 다운로드 - - console.log('select')}> - 휴지통으로 이동 - - - + {variant === 'primary' && ( + + + + console.log('select')}> + 이름 변경 + + console.log('select')}> + 폴더 전체 다운로드 + + console.log('select')}> + 휴지통으로 이동 + + + + )}
); diff --git a/src/shared/component/FileGrid/index.style.ts b/src/shared/component/FileGrid/index.style.ts index ff91d99bc..ec7228f65 100644 --- a/src/shared/component/FileGrid/index.style.ts +++ b/src/shared/component/FileGrid/index.style.ts @@ -2,23 +2,26 @@ import { css } from '@emotion/react'; import { theme } from '@/common/style/theme/theme'; -export const cardStyle = css({ - width: '100%', - height: '16rem', +export const cardStyle = (isSmall: boolean) => + css({ + minWidth: '16rem', + width: isSmall ? '16rem' : '100%', + height: isSmall ? '12.2rem' : '16rem', - padding: '2.4rem 2rem', + padding: isSmall ? '1.2rem' : '2.4rem 2rem', - border: `1px solid ${theme.colors.gray_300}`, - borderRadius: '12px', + border: `1px solid ${theme.colors.gray_300}`, + borderRadius: '12px', - cursor: 'pointer', -}); + cursor: 'pointer', + }); -export const iconWrapperStyle = css({ - width: '100%', +export const iconWrapperStyle = (isSmall: boolean) => + css({ + width: '100%', - padding: '1.2rem 0 2rem 0', -}); + padding: isSmall ? '0 0 2rem' : '1.2rem 0 2rem 0', + }); export const nameStyle = css({ ...theme.text.body06, diff --git a/src/shared/type/content.ts b/src/shared/type/content.ts index a4b88a56f..117a9b946 100644 --- a/src/shared/type/content.ts +++ b/src/shared/type/content.ts @@ -1 +1 @@ -export type Content = 'timeline' | 'file' | 'deleted'; +export type Content = 'timeline' | 'file' | 'deleted' | 'handover'; diff --git a/src/story/page/dashboard/component/ListItem.stories.tsx b/src/story/page/dashboard/component/ListItem.stories.tsx new file mode 100644 index 000000000..0cb0effa7 --- /dev/null +++ b/src/story/page/dashboard/component/ListItem.stories.tsx @@ -0,0 +1,60 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ListItem from '@/page/dashboard/component/Handover/ListItem/ListItem'; + +const meta = { + title: 'page/entree/ListItem', + component: ListItem, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + title: { + control: { type: 'text' }, + }, + content: { + control: { type: 'text' }, + }, + date: { + control: false, + }, + tags: { + control: false, + }, + }, + args: { + title: '3차 정기 회의', + content: '티키의 3번째 정기 회의록입니다. 티키의 3번째 정기 회의록입니다.', + date: new Date(), + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + tags: [ + { tagId: 0, content: 'meeting', bgColor: '#FFE6E8' }, + { tagId: 1, content: 'study', bgColor: '#F8E2CB' }, + { tagId: 2, content: 'meeting', bgColor: '#FFE6E8' }, + { tagId: 3, content: 'study', bgColor: '#F8E2CB' }, + { tagId: 4, content: 'meeting', bgColor: '#FFE6E8' }, + { tagId: 5, content: 'study', bgColor: '#F8E2CB' }, + ], + }, + argTypes: {}, +}; + +export const ZeroFile: Story = { + args: {}, + argTypes: {}, +}; + +export const SomeFile: Story = { + args: { + tags: [{ tagId: 0, content: 'meeting', bgColor: '#FFE6E8' }], + }, + argTypes: {}, +}; diff --git a/src/story/shared/FileGrid.stories.tsx b/src/story/shared/FileGrid.stories.tsx index 3ad71f13f..bc56746a6 100644 --- a/src/story/shared/FileGrid.stories.tsx +++ b/src/story/shared/FileGrid.stories.tsx @@ -45,3 +45,18 @@ export const Image: Story = { type: 'pdf', }, }; + +export const Secondary: Story = { + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: { + variant: 'secondary', + title: '이채원', + type: 'pdf', + }, +}; diff --git a/src/story/shared/FolderGrid.stories.tsx b/src/story/shared/FolderGrid.stories.tsx index 19ec4d658..c02610ee2 100644 --- a/src/story/shared/FolderGrid.stories.tsx +++ b/src/story/shared/FolderGrid.stories.tsx @@ -25,3 +25,17 @@ export const Default: Story = { title: '폴더 이름', }, }; + +export const Secondary: Story = { + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: { + variant: 'secondary', + title: '폴더 이름', + }, +};