From fa335dcb2d16bc3fe9560d69b368292083793ec3 Mon Sep 17 00:00:00 2001 From: LSJ Date: Mon, 6 May 2024 16:03:03 +0900 Subject: [PATCH 1/4] feat: create RowButtonBlock components --- components/atoms/blocks/RowButtonBlock.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 components/atoms/blocks/RowButtonBlock.tsx diff --git a/components/atoms/blocks/RowButtonBlock.tsx b/components/atoms/blocks/RowButtonBlock.tsx new file mode 100644 index 000000000..c9fd7b776 --- /dev/null +++ b/components/atoms/blocks/RowButtonBlock.tsx @@ -0,0 +1,21 @@ +import Link from "next/link"; +import styled from "styled-components"; + +interface RowButtonBlockProps { + text: string; + url?: string; + func?: () => void; +} + +function RowButtonBlock({ text, url, func }: RowButtonBlockProps) { + return ; +} + +const Button = styled.button` + width: 100%; + padding: var(--gap-4) var(--gap-4); + text-align: start; + border-bottom: var(--border); +`; + +export default RowButtonBlock; From a7ae98d625735d8ba56e2b610cb64c2846a11fb8 Mon Sep 17 00:00:00 2001 From: LSJ Date: Mon, 6 May 2024 16:51:21 +0900 Subject: [PATCH 2/4] feat: create RowButtonBlock components --- components/atoms/blocks/RowButtonBlock.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/components/atoms/blocks/RowButtonBlock.tsx b/components/atoms/blocks/RowButtonBlock.tsx index c9fd7b776..0cd1976a3 100644 --- a/components/atoms/blocks/RowButtonBlock.tsx +++ b/components/atoms/blocks/RowButtonBlock.tsx @@ -1,4 +1,4 @@ -import Link from "next/link"; +import Link, { LinkProps } from "next/link"; import styled from "styled-components"; interface RowButtonBlockProps { @@ -8,7 +8,9 @@ interface RowButtonBlockProps { } function RowButtonBlock({ text, url, func }: RowButtonBlockProps) { - return ; + return ( + <>{url ? {text} : } + ); } const Button = styled.button` @@ -18,4 +20,11 @@ const Button = styled.button` border-bottom: var(--border); `; +const CustomLink = styled(Link)` + display: block; + padding: var(--gap-4) var(--gap-4); + text-align: start; + border-bottom: var(--border); +`; + export default RowButtonBlock; From 645ac3b10fece9ab70c27c2c5582b4c6be14fb01 Mon Sep 17 00:00:00 2001 From: LSJ Date: Tue, 7 May 2024 01:59:57 +0900 Subject: [PATCH 3/4] temp --- design/attendance/AttendanceBar.tsx | 100 ++++++++++++ design/attendance/AttendanceModal.tsx | 173 +++++++++++++++++++++ modals/pop-up/LastWeekAttendPopUp.tsx | 2 +- stories/designs/attendanceModal.stories.ts | 33 ++++ stories/molecules/Accordion.stories.ts | 2 +- 5 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 design/attendance/AttendanceBar.tsx create mode 100644 design/attendance/AttendanceModal.tsx create mode 100644 stories/designs/attendanceModal.stories.ts diff --git a/design/attendance/AttendanceBar.tsx b/design/attendance/AttendanceBar.tsx new file mode 100644 index 000000000..712751726 --- /dev/null +++ b/design/attendance/AttendanceBar.tsx @@ -0,0 +1,100 @@ +import { Badge, Progress } from "@chakra-ui/react"; +import { faQuestionCircle } from "@fortawesome/pro-light-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useState } from "react"; +import styled from "styled-components"; + +import { BADGE_COLOR_MAPPINGS, BADGE_INFO } from "../../constants/serviceConstants/badgeConstants"; +import { SCHEME_TO_COLOR } from "../../constants/styles"; +import BadgeInfoModal from "../../modals/store/badgeInfoModal/BadgeInfoModal"; + +interface IAttendanceBar { + myScore: number; + hasQuestion?: boolean; +} + +function AttendanceBar({ myScore, hasQuestion = true }: IAttendanceBar) { + const userBadge = { badge: "아메리카노", nextBadge: "망고" }; + + const [isBadgeModal, setIsBadgeModal] = useState(false); + + const { badge, nextBadge } = userBadge; + + const badgeColor = BADGE_COLOR_MAPPINGS[userBadge.badge]; + + const getBadgePoint = () => { + for (let i = 0; i < BADGE_INFO.length; i++) { + const badgeInfo = BADGE_INFO[i]; + if (badgeInfo.badge === nextBadge) { + return { + nextBadgePoint: badgeInfo.minScore, + badgeGap: badgeInfo.minScore - BADGE_INFO[i - 1].minScore, + }; + } + } + }; + const { nextBadgePoint, badgeGap } = getBadgePoint() || {}; + + return ( + <> + + +
+ + {badge} + + {myScore}점 + {hasQuestion && ( + setIsBadgeModal(true)}> + + + )} +
+ {nextBadge && ( +
+ {nextBadgePoint}점 + + {nextBadge} + +
+ )} +
+ +
+ + {isBadgeModal && } + + ); +} + +const Layout = styled.div` + margin-bottom: var(--gap-3); +`; +const Grade = styled.div` + display: flex; + justify-content: space-between; + margin-bottom: var(--gap-3); + align-items: center; + > div { + display: flex; + align-items: center; + } +`; + +const BadgeName = styled.span<{ color: string }>` + color: ${(props) => props.color}; + font-weight: 600; +`; + +const IconWrapper = styled.button` + color: var(--gray-2); + font-size: 14px; + margin-left: var(--gap-2); +`; + +export default AttendanceBar; diff --git a/design/attendance/AttendanceModal.tsx b/design/attendance/AttendanceModal.tsx new file mode 100644 index 000000000..508f7a3a7 --- /dev/null +++ b/design/attendance/AttendanceModal.tsx @@ -0,0 +1,173 @@ +import dayjs from "dayjs"; +import styled from "styled-components"; + +import Avatar from "../../components/atoms/Avatar"; +import { PopOverIcon } from "../../components/atoms/Icons/PopOverIcon"; +import { IFooterOptions, ModalLayout } from "../../modals/Modals"; +import { DispatchBoolean } from "../../types/hooks/reactTypes"; +import AttendanceBar from "./AttendanceBar"; + +function AttendanceModal({ + isExist, + setIsModal, +}: { + isExist?: boolean; + setIsModal: DispatchBoolean; +}) { + const today = dayjs(); + const firstDayOfMonth = today.startOf("month"); + const differenceInDays = today.diff(firstDayOfMonth, "day"); + const weekNumber = Math.floor(differenceInDays / 7) + 1; + + const footerOptions: IFooterOptions = { + main: {}, + isFull: true, + }; + + return ( + <> + {isExist && ( + + + + 임시 달성시 +10 포인트, 거북이 아바타 해금! + + + 이승주 (동아리원) + + + + + + + + {weekNumber}주차 스터디 투표 + 2 회 + + + {weekNumber}주차 스터디 출석 + 2 회 + + +
+ 이번 달 스터디 점수 + +
+ 40 점 +
+ + 다음 참여 정산일 + {dayjs().add(1, "month").month() + 1}월 1일 + + + 보유 보증금 + 2000원 + +
+
+ + {true ? ( +
+ 🎉신규 가입을 환영해요🎉 +
+ 앞으로 열심히 활동해봐요~! +
+ ) : false ? ( +
+ 이번 달에 아직 스터디에 참여하지 않았어요. +
{-dayjs().add(1, "month").date(1).diff(dayjs(), "day")}일 뒤에 경고를 + 받습니다. +
+ ) : ( +
+ 🎉잘 하고 있어요🎉 +
+ 이번주도 열심히 파이팅~! +
+ )} +
+
+ )} + + ); +} + +const Message = styled.div` + padding: var(--gap-2) var(--gap-3); + color: var(--gray-2); + border-radius: var(--rounded); + background-color: var(--gray-8); +`; + +const ProfileWrapper = styled.div` + padding: 8px 0; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: var(--border); + > span:first-child { + font-weight: 500; + font-size: 16px; + } +`; + +const ScoreBarWrapper = styled.div` + padding: var(--gap-2) 0; + border-bottom: var(--border); + display: flex; + flex-direction: column; + > span { + font-size: 12px; + color: var(--gray-3); + margin-left: auto; + } +`; + +const Container = styled.div` + padding: var(--gap-3) 0; + display: flex; + flex-direction: row; + height: 100%; +`; + +const Info = styled.div` + width: 100%; + display: flex; + flex-direction: column; +`; + +const SkeletonText = styled.div` + width: 60px; +`; + +const ImageWrapper = styled.div` + margin-left: auto; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + > span { + display: inline-block; + margin-top: var(--gap-1); + } +`; + +const Item = styled.div` + display: flex; + justify-content: space-between; + font-size: 14px; + padding: var(--gap-1) 0; + > span:last-child { + font-weight: 600; + } +`; + +export default AttendanceModal; diff --git a/modals/pop-up/LastWeekAttendPopUp.tsx b/modals/pop-up/LastWeekAttendPopUp.tsx index 0f9f85796..a30e98da3 100644 --- a/modals/pop-up/LastWeekAttendPopUp.tsx +++ b/modals/pop-up/LastWeekAttendPopUp.tsx @@ -124,7 +124,7 @@ function LastWeekAttendPopUp({ setIsModal }: IModal) { setIsModal={setIsModal} > - + {nextBadge ? ( {nextBadge} 달성시 +10 포인트, {nextAvatar[String(nextBadgePoint)]} 아바타 해금! diff --git a/stories/designs/attendanceModal.stories.ts b/stories/designs/attendanceModal.stories.ts new file mode 100644 index 000000000..1c2a0791b --- /dev/null +++ b/stories/designs/attendanceModal.stories.ts @@ -0,0 +1,33 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; +import AttendanceModal from "../../design/attendance/AttendanceModal"; + +// Meta 타입에 typeof를 사용하여 컴포넌트 타입을 정확히 지정합니다. +const meta: Meta = { + title: "DESIGNS/AttendanceModal", + component: AttendanceModal, + parameters: {}, + tags: ["autodocs"], +}; + +export default meta; + +// StoryObj를 사용할 때는 컴포넌트의 Props 타입을 정확하게 매핑합니다. +type AttendanceModalProps = { + isExist?: boolean; + setIsModal: (exist: boolean) => void; +}; + +export const Primary: StoryObj = { + render: (args) => { + const [isExist, setIsExist] = useState(args.isExist ?? false); // args.isExist가 undefined일 경우 false를 기본값으로 사용 + + // isExist 상태를 토글하는 함수 + const setIsModal = () => setIsExist(!isExist); + + return
; + }, + args: { + isExist: false, + }, +}; diff --git a/stories/molecules/Accordion.stories.ts b/stories/molecules/Accordion.stories.ts index 92388a4db..271bed560 100644 --- a/stories/molecules/Accordion.stories.ts +++ b/stories/molecules/Accordion.stories.ts @@ -4,7 +4,7 @@ import Accordion from "../../components/molecules/Accordion"; import { ACCORDION_CONTENT_FEE } from "../../constants/contentsText/accordionContents"; const meta = { - title: "ATOMS/molecules/Accordion", + title: "MOLECULES/Accordion", component: Accordion, parameters: {}, tags: ["autodocs"], From 120b16fd72dea7b1b9a79a555f11317c0ceb279b Mon Sep 17 00:00:00 2001 From: LSJ Date: Tue, 7 May 2024 14:10:39 +0900 Subject: [PATCH 4/4] feat: add attendanceModal to storybook --- design/attendance/AttendanceModal.tsx | 148 ++++++++++----------- stories/designs/attendanceModal.stories.ts | 33 ++--- 2 files changed, 80 insertions(+), 101 deletions(-) diff --git a/design/attendance/AttendanceModal.tsx b/design/attendance/AttendanceModal.tsx index 508f7a3a7..fcb071c36 100644 --- a/design/attendance/AttendanceModal.tsx +++ b/design/attendance/AttendanceModal.tsx @@ -4,16 +4,9 @@ import styled from "styled-components"; import Avatar from "../../components/atoms/Avatar"; import { PopOverIcon } from "../../components/atoms/Icons/PopOverIcon"; import { IFooterOptions, ModalLayout } from "../../modals/Modals"; -import { DispatchBoolean } from "../../types/hooks/reactTypes"; import AttendanceBar from "./AttendanceBar"; -function AttendanceModal({ - isExist, - setIsModal, -}: { - isExist?: boolean; - setIsModal: DispatchBoolean; -}) { +function AttendanceModal({ type }: { type: 1 | 2 | 3 }) { const today = dayjs(); const firstDayOfMonth = today.startOf("month"); const differenceInDays = today.diff(firstDayOfMonth, "day"); @@ -26,76 +19,73 @@ function AttendanceModal({ return ( <> - {isExist && ( - - - - 임시 달성시 +10 포인트, 거북이 아바타 해금! - - - 이승주 (동아리원) - - - - - - - - {weekNumber}주차 스터디 투표 - 2 회 - - - {weekNumber}주차 스터디 출석 - 2 회 - - -
- 이번 달 스터디 점수 - -
- 40 점 -
- - 다음 참여 정산일 - {dayjs().add(1, "month").month() + 1}월 1일 - - - 보유 보증금 - 2000원 - -
-
- - {true ? ( -
- 🎉신규 가입을 환영해요🎉 -
- 앞으로 열심히 활동해봐요~! + {}} + > + + + 임시 달성시 +10 포인트, 거북이 아바타 해금! + + + 이승주 {type === 1 ? "(동아리원)" : "(수습멤버)"} + + + + + + + + {weekNumber}주차 스터디 투표 + 2 회 + + + {weekNumber}주차 스터디 출석 + 2 회 + + +
+ 이번 달 스터디 점수 +
- ) : false ? ( -
- 이번 달에 아직 스터디에 참여하지 않았어요. -
{-dayjs().add(1, "month").date(1).diff(dayjs(), "day")}일 뒤에 경고를 - 받습니다. -
- ) : ( -
- 🎉잘 하고 있어요🎉 -
- 이번주도 열심히 파이팅~! -
- )} - -
- )} + 40 점 + + + 다음 참여 정산일 + {dayjs().add(1, "month").month() + 1}월 1일 + + + 보유 보증금 + 2000원 + + + + + {type === 2 ? ( +
+ 🎉신규 가입을 환영해요🎉 +
+ 앞으로 열심히 활동해봐요~! +
+ ) : type === 1 ? ( +
+ 이번 달 스터디에 참여하지 않았어요. +
{-dayjs().add(1, "month").date(1).diff(dayjs(), "day")}일 뒤에 경고를 받습니다. +
+ ) : ( +
+ 🎉잘 하고 있어요🎉 +
+ 이번주도 열심히 파이팅~! +
+ )} +
+ ); } @@ -144,10 +134,6 @@ const Info = styled.div` flex-direction: column; `; -const SkeletonText = styled.div` - width: 60px; -`; - const ImageWrapper = styled.div` margin-left: auto; display: flex; diff --git a/stories/designs/attendanceModal.stories.ts b/stories/designs/attendanceModal.stories.ts index 1c2a0791b..6fbc41245 100644 --- a/stories/designs/attendanceModal.stories.ts +++ b/stories/designs/attendanceModal.stories.ts @@ -1,33 +1,26 @@ -import { Meta, StoryObj } from "@storybook/react"; -import { useState } from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + import AttendanceModal from "../../design/attendance/AttendanceModal"; -// Meta 타입에 typeof를 사용하여 컴포넌트 타입을 정확히 지정합니다. -const meta: Meta = { +const meta = { title: "DESIGNS/AttendanceModal", component: AttendanceModal, parameters: {}, tags: ["autodocs"], -}; + argTypes: {}, + args: {}, +} satisfies Meta; export default meta; +type Story = StoryObj; -// StoryObj를 사용할 때는 컴포넌트의 Props 타입을 정확하게 매핑합니다. -type AttendanceModalProps = { - isExist?: boolean; - setIsModal: (exist: boolean) => void; -}; - -export const Primary: StoryObj = { - render: (args) => { - const [isExist, setIsExist] = useState(args.isExist ?? false); // args.isExist가 undefined일 경우 false를 기본값으로 사용 - - // isExist 상태를 토글하는 함수 - const setIsModal = () => setIsExist(!isExist); - - return
; +export const Primary: Story = { + args: { + type: 1, }, +}; +export const Secondary: Story = { args: { - isExist: false, + type: 2, }, };