From b42d513634ea427815128bccddee5af96c76ca42 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Thu, 12 Dec 2024 14:58:14 +0900 Subject: [PATCH 01/24] =?UTF-8?q?=E2=9C=A8[Feat]=20box,=20title,=20locatio?= =?UTF-8?q?n,=20datetime=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 150 ++++++++++++------- src/components/card/types/interface/card.ts | 29 ++++ src/components/card/types/interface/index.ts | 1 + 3 files changed, 130 insertions(+), 50 deletions(-) create mode 100644 src/components/card/types/interface/card.ts create mode 100644 src/components/card/types/interface/index.ts diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 63e7f6ef..95bb412b 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -10,16 +10,113 @@ import Image from 'next/image'; import { CardContextType, CardProps, - CardBoxProps, CardInfoProps, CardStatusProps, CardHostProps, CardImageProps, CardEndedOverlayProps, } from './types'; +import { twMerge } from 'tailwind-merge'; +import { + CardBoxProps, + CardTitleProps, + CardLocationProps, + CardDateTimeProps, +} from './types/interface'; const CardContext = createContext({ isCanceled: false }); +// Box 컴포넌트 +function CardBox({ children, className = '', ...props }: CardBoxProps) { + return ( +
+ {children} +
+ ); +} + +function CardTitle({ children, className, ...props }: CardTitleProps) { + return ( +

+ {children} +

+ ); +} + +function CardLocation({ + children, + className, + textClassName, + ...props +}: CardLocationProps) { + return ( +
+ + + {children} + +
+ ); +} + +function CardDateTime({ children, className, ...props }: CardDateTimeProps) { + return ( + + {children} + + ); +} + +// Image 컴포넌트 (모임 이미지) +function CardImage({ + url, + alt = '모임 이미지', + isLiked = false, + onLikeClick, + className, + ...props +}: CardImageProps) { + return ( +
+ {alt} +
+ +
+
+ ); +} + +Card.Box = CardBox; +Card.Info = CardInfo; +Card.Status = CardStatus; +Card.EndedOverlay = CardEndedOverlay; +Card.Host = CardHost; +Card.Image = CardImage; +Card.Title = CardTitle; +Card.Location = CardLocation; +Card.DateTime = CardDateTime; + // 메인 Card function Card({ children, @@ -39,24 +136,6 @@ function Card({ ); } -// Box 컴포넌트 (CardInfo + CardStatus) -function CardBox({ - children, - className = '', - onClick, - ...props -}: CardBoxProps) { - return ( -
- {children} -
- ); -} - // Info 컴포넌트 (모임에 관한 정보 - 제목, 위치, 날짜 등) function CardInfo({ title, @@ -122,6 +201,8 @@ function CardStatus({ ); } +export default Card; + // Overlay (모임 취소시 표시되는 오버레이) function CardEndedOverlay({ onDelete }: CardEndedOverlayProps) { const { isCanceled } = useContext(CardContext); @@ -184,34 +265,3 @@ function CardHost({ ); } - -// Image 컴포넌트 (모임 이미지) -function CardImage({ - url, - alt = '모임 이미지', - isLiked = false, - onLikeClick, - className, - ...props -}: CardImageProps) { - return ( -
- {alt} -
- -
-
- ); -} - -Card.Box = CardBox; -Card.Info = CardInfo; -Card.Status = CardStatus; -Card.EndedOverlay = CardEndedOverlay; -Card.Host = CardHost; -Card.Image = CardImage; - -export default Card; diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts new file mode 100644 index 00000000..f51f1c70 --- /dev/null +++ b/src/components/card/types/interface/card.ts @@ -0,0 +1,29 @@ +import { ComponentPropsWithoutRef } from 'react'; + +interface CardBoxProps extends ComponentPropsWithoutRef<'div'> { + children: React.ReactNode; + className?: string; +} + +interface CardTitleProps extends ComponentPropsWithoutRef<'h3'> { + children: React.ReactNode; + className?: string; +} + +interface CardLocationProps extends ComponentPropsWithoutRef<'div'> { + children: React.ReactNode; + className?: string; + textClassName?: string; +} + +interface CardDateTimeProps extends ComponentPropsWithoutRef<'span'> { + children: React.ReactNode; + className?: string; +} + +export type { + CardBoxProps, + CardTitleProps, + CardLocationProps, + CardDateTimeProps, +}; diff --git a/src/components/card/types/interface/index.ts b/src/components/card/types/interface/index.ts new file mode 100644 index 00000000..cb5809fe --- /dev/null +++ b/src/components/card/types/interface/index.ts @@ -0,0 +1 @@ +export * from './card'; From 2bcbc8fcf2ce0d3f3c70ba72625edbd774acd728 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Thu, 12 Dec 2024 16:30:37 +0900 Subject: [PATCH 02/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=98=A4=EB=B2=84?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=20=EB=B0=8F=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.stories.tsx | 104 +++++++++++++++----- src/components/card/Card.tsx | 79 ++++++++------- src/components/card/TmpCard.stories.tsx | 40 ++++++++ src/components/card/types/interface/card.ts | 13 +++ 4 files changed, 177 insertions(+), 59 deletions(-) create mode 100644 src/components/card/TmpCard.stories.tsx diff --git a/src/components/card/Card.stories.tsx b/src/components/card/Card.stories.tsx index 5da96dce..62385de1 100644 --- a/src/components/card/Card.stories.tsx +++ b/src/components/card/Card.stories.tsx @@ -1,39 +1,99 @@ import type { Meta, StoryObj } from '@storybook/react'; import Card from './Card'; -import { mockMeeting } from './mock/mock'; const meta = { - title: 'Components/Card/Base', + title: 'Components/Card', component: Card, parameters: { layout: 'centered', }, - argTypes: { - isCanceled: { - control: 'boolean', - description: '모임 취소 여부', - }, - className: { - control: 'text', - description: '추가 스타일링을 위한 className', - }, - }, + tags: ['autodocs'], } satisfies Meta; export default meta; type Story = StoryObj; -export const Default: Story = { - args: { - isCanceled: false, - }, - render: (args) => ( - - +// Box Story +export const Box: Story = { + render: () => ( + +

Card Box Content

+
+ ), +}; + +// Image Story +export const Image: Story = { + render: () => ( + alert('좋아요 클릭!')} + /> + ), +}; + +// Title Story +export const Title: Story = { + render: () => 모임 제목 예시, +}; + +// Location Story +export const Location: Story = { + render: () => 서울특별시 강남구, +}; + +// DateTime Story +export const DateTime: Story = { + render: () => 2024.03.15 (금) 19:00, +}; + +// Overlay Story +export const Overlay: Story = { + render: () => ( +
+ + alert('삭제 버튼 클릭!')} /> + +
+ ), +}; + +// 각 컴포넌트의 다양한 상태를 보여주는 추가 스토리들 +export const ImageWithLike: Story = { + render: () => ( + alert('좋아요 클릭!')} + /> + ), +}; + +export const BoxWithClick: Story = { + render: () => ( + alert('박스 클릭!')}> +

클릭 가능한 Card Box

+
+ ), +}; + +// 모든 컴포넌트를 조합한 예시 +export const AllComponents: Story = { + render: () => ( + - - - alert('삭제되었습니다')} /> + alert('좋아요 클릭!')} + /> + 모임 제목 예시 + 서울특별시 강남구 + 2024.03.15 (금) 19:00 ), diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 95bb412b..737500b7 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -13,8 +13,6 @@ import { CardInfoProps, CardStatusProps, CardHostProps, - CardImageProps, - CardEndedOverlayProps, } from './types'; import { twMerge } from 'tailwind-merge'; import { @@ -22,6 +20,8 @@ import { CardTitleProps, CardLocationProps, CardDateTimeProps, + CardOverlayProps, + CardImageProps, } from './types/interface'; const CardContext = createContext({ isCanceled: false }); @@ -85,7 +85,6 @@ function CardDateTime({ children, className, ...props }: CardDateTimeProps) { ); } -// Image 컴포넌트 (모임 이미지) function CardImage({ url, alt = '모임 이미지', @@ -96,7 +95,10 @@ function CardImage({ }: CardImageProps) { return (
{alt} @@ -107,15 +109,47 @@ function CardImage({ ); } +// Overlay (모임 취소시 표시되는 오버레이) +function CardOverlay({ onDelete }: CardOverlayProps) { + const { isCanceled } = useContext(CardContext); + + if (!isCanceled) return null; + + const handleDeleteClick = (e: React.MouseEvent) => { + e.stopPropagation(); + onDelete?.(); + }; + + return ( +
+
+

+ {'호스트가 모임을 취소했어요.'} +
+ {'새로운 모임을 찾아볼까요?'} +

+ {/* TODO:: 삭제 버튼 공통 컴포넌트 변경 필요 */} + +
+
+ ); +} + Card.Box = CardBox; -Card.Info = CardInfo; -Card.Status = CardStatus; -Card.EndedOverlay = CardEndedOverlay; -Card.Host = CardHost; Card.Image = CardImage; Card.Title = CardTitle; Card.Location = CardLocation; Card.DateTime = CardDateTime; +Card.Overlay = CardOverlay; + +Card.Info = CardInfo; +Card.Status = CardStatus; +Card.Host = CardHost; // 메인 Card function Card({ @@ -203,35 +237,6 @@ function CardStatus({ export default Card; -// Overlay (모임 취소시 표시되는 오버레이) -function CardEndedOverlay({ onDelete }: CardEndedOverlayProps) { - const { isCanceled } = useContext(CardContext); - - if (!isCanceled) return null; - - const handleDeleteClick = (e: React.MouseEvent) => { - e.stopPropagation(); - onDelete?.(); - }; - - return ( -
-
-

- {'호스트가 모임을 취소했어요.'} -

- {/* TODO:: 삭제 버튼 공통 컴포넌트 변경 필요 */} - -
-
- ); -} - // Host 컴포넌트 (호스트 정보) function CardHost({ nickname, diff --git a/src/components/card/TmpCard.stories.tsx b/src/components/card/TmpCard.stories.tsx new file mode 100644 index 00000000..22710932 --- /dev/null +++ b/src/components/card/TmpCard.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import Card from './Card'; +import { mockMeeting } from './mock/mock'; + +const meta = { + title: 'Components/TMP/Card/Base', + component: Card, + parameters: { + layout: 'centered', + }, + argTypes: { + isCanceled: { + control: 'boolean', + description: '모임 취소 여부', + }, + className: { + control: 'text', + description: '추가 스타일링을 위한 className', + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + isCanceled: false, + }, + render: (args) => ( + + + + + + alert('삭제되었습니다')} /> + + + ), +}; diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts index f51f1c70..2cc6adb9 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/interface/card.ts @@ -21,9 +21,22 @@ interface CardDateTimeProps extends ComponentPropsWithoutRef<'span'> { className?: string; } +interface CardImageProps extends ComponentPropsWithoutRef<'div'> { + url: string; + alt?: string; + isLiked?: boolean; + onLikeClick?: () => void; +} + +interface CardOverlayProps extends ComponentPropsWithoutRef<'div'> { + onDelete?: () => void; +} + export type { CardBoxProps, CardTitleProps, CardLocationProps, CardDateTimeProps, + CardImageProps, + CardOverlayProps, }; From 694f58b12e0e6baffcaf771a2eea2985b50a43d6 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Thu, 12 Dec 2024 16:32:09 +0900 Subject: [PATCH 03/24] =?UTF-8?q?=F0=9F=9A=9A[Rename]=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=BD=94=EB=93=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/TmpCard.stories.tsx | 2 +- src/components/card/full-card/FullCard.stories.tsx | 2 +- src/components/card/my-club-card/MyClubCard.stories.tsx | 2 +- src/components/card/simple-card/SimpleCard.stories.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/card/TmpCard.stories.tsx b/src/components/card/TmpCard.stories.tsx index 22710932..2ff417d2 100644 --- a/src/components/card/TmpCard.stories.tsx +++ b/src/components/card/TmpCard.stories.tsx @@ -3,7 +3,7 @@ import Card from './Card'; import { mockMeeting } from './mock/mock'; const meta = { - title: 'Components/TMP/Card/Base', + title: 'Components/TMPCard/Base', component: Card, parameters: { layout: 'centered', diff --git a/src/components/card/full-card/FullCard.stories.tsx b/src/components/card/full-card/FullCard.stories.tsx index e77b53dd..06bf1df1 100644 --- a/src/components/card/full-card/FullCard.stories.tsx +++ b/src/components/card/full-card/FullCard.stories.tsx @@ -3,7 +3,7 @@ import FullCard from './FullCard'; import { mockFullMeeting } from '../mock/mock'; const meta = { - title: 'Components/Card/FullCard', + title: 'Components/TMPCard/FullCard', component: FullCard, parameters: { layout: 'centered', diff --git a/src/components/card/my-club-card/MyClubCard.stories.tsx b/src/components/card/my-club-card/MyClubCard.stories.tsx index 47ee9add..f2cfcc23 100644 --- a/src/components/card/my-club-card/MyClubCard.stories.tsx +++ b/src/components/card/my-club-card/MyClubCard.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import MyClubCard from './MyClubCard'; const meta = { - title: 'Components/Card/MyClubCard', + title: 'Components/TMPCard/MyClubCard', component: MyClubCard, parameters: { layout: 'centered', diff --git a/src/components/card/simple-card/SimpleCard.stories.tsx b/src/components/card/simple-card/SimpleCard.stories.tsx index 05f42df9..9bf44483 100644 --- a/src/components/card/simple-card/SimpleCard.stories.tsx +++ b/src/components/card/simple-card/SimpleCard.stories.tsx @@ -3,7 +3,7 @@ import SimpleCard from './SimpleCard'; import { mockMeeting } from '../mock/mock'; const meta = { - title: 'Components/Card/SimpleCard', + title: 'Components/TMPCard/SimpleCard', component: SimpleCard, parameters: { layout: 'centered', From 4edb11378460aedb75d01e25b107245ac95b1879 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Thu, 12 Dec 2024 17:38:23 +0900 Subject: [PATCH 04/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EB=94=94=ED=8F=B4=ED=8A=B8=20=EC=B9=B4=EB=93=9C=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.stories.tsx | 32 +---- src/components/card/Card.tsx | 116 +++++++++++++++--- src/components/card/ClubCard.stories.tsx | 66 ++++++++++ src/components/card/types/interface/card.ts | 6 + .../card/types/interface/clubCard.ts | 40 ++++++ src/components/card/types/interface/index.ts | 1 + 6 files changed, 211 insertions(+), 50 deletions(-) create mode 100644 src/components/card/ClubCard.stories.tsx create mode 100644 src/components/card/types/interface/clubCard.ts diff --git a/src/components/card/Card.stories.tsx b/src/components/card/Card.stories.tsx index 62385de1..6965bd21 100644 --- a/src/components/card/Card.stories.tsx +++ b/src/components/card/Card.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import Card from './Card'; const meta = { - title: 'Components/Card', + title: 'Components/Card/Elements', component: Card, parameters: { layout: 'centered', @@ -13,7 +13,6 @@ const meta = { export default meta; type Story = StoryObj; -// Box Story export const Box: Story = { render: () => ( @@ -22,7 +21,6 @@ export const Box: Story = { ), }; -// Image Story export const Image: Story = { render: () => ( 모임 제목 예시, }; -// Location Story export const Location: Story = { render: () => 서울특별시 강남구, }; -// DateTime Story export const DateTime: Story = { render: () => 2024.03.15 (금) 19:00, }; -// Overlay Story export const Overlay: Story = { render: () => (
- - alert('삭제 버튼 클릭!')} /> - + alert('삭제 버튼 클릭!')} />
), }; -// 각 컴포넌트의 다양한 상태를 보여주는 추가 스토리들 export const ImageWithLike: Story = { render: () => ( ), }; - -// 모든 컴포넌트를 조합한 예시 -export const AllComponents: Story = { - render: () => ( - - - alert('좋아요 클릭!')} - /> - 모임 제목 예시 - 서울특별시 강남구 - 2024.03.15 (금) 19:00 - - - ), -}; diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 737500b7..326f3879 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -9,7 +9,6 @@ import { LocationIcon, HostIcon, HeartIcon } from '../../../public/icons'; import Image from 'next/image'; import { CardContextType, - CardProps, CardInfoProps, CardStatusProps, CardHostProps, @@ -22,6 +21,7 @@ import { CardDateTimeProps, CardOverlayProps, CardImageProps, + CardProps, } from './types/interface'; const CardContext = createContext({ isCanceled: false }); @@ -140,6 +140,99 @@ function CardOverlay({ onDelete }: CardOverlayProps) { ); } +function Card(props: CardProps) { + const renderCardContent = () => { + switch (props.variant) { + case 'default': + default: { + const { + imageUrl, + imageAlt, + title, + location, + datetime, + isLiked, + onLikeClick, + current, + max, + participants, + isPast, + isCanceled, + onClick, + onDelete, + status, + } = props; + + return ( +
+
+ +
+ +
+ {title} +
+ {location} + {datetime} +
+
+
+
+
+ + + {participants.map((participant, index) => ( + + ))} + +
+ {status === 'confirmed' && ( + + )} +
+ +
+ {isCanceled && } +
+
+ ); + } + + case 'participated': + case 'hosted': + return null; // 추후 구현 + } + }; + + return ( + +
+ {renderCardContent()} +
+
+ ); +} + Card.Box = CardBox; Card.Image = CardImage; Card.Title = CardTitle; @@ -147,29 +240,12 @@ Card.Location = CardLocation; Card.DateTime = CardDateTime; Card.Overlay = CardOverlay; +/////////////////////////////////////////////////////////////////////// ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ + Card.Info = CardInfo; Card.Status = CardStatus; Card.Host = CardHost; -// 메인 Card -function Card({ - children, - isCanceled = false, - className, - ...props -}: CardProps) { - return ( - -
- {children} -
-
- ); -} - // Info 컴포넌트 (모임에 관한 정보 - 제목, 위치, 날짜 등) function CardInfo({ title, diff --git a/src/components/card/ClubCard.stories.tsx b/src/components/card/ClubCard.stories.tsx new file mode 100644 index 00000000..62e08ec3 --- /dev/null +++ b/src/components/card/ClubCard.stories.tsx @@ -0,0 +1,66 @@ +// 모임 카드 스토리 +import type { Meta, StoryObj } from '@storybook/react'; +import Card from './Card'; + +const meta = { + title: 'Components/Card/Club', + component: Card, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// 기본 모임 카드 스토리 +export const DefaultCard: Story = { + args: { + variant: 'default', + imageUrl: 'https://picsum.photos/400/300', + imageAlt: '모임 이미지', + title: '모임 제목 예시', + location: '서울특별시 강남구', + datetime: '2024.03.15 (금) 19:00', + meetingType: 'FREE', + status: 'confirmed', + isLiked: false, + onLikeClick: () => alert('좋아요 클릭!'), + current: 5, + max: 10, + participants: [ + { src: 'https://picsum.photos/200/200?random=1', alt: '참가자1' }, + { src: 'https://picsum.photos/200/200?random=2', alt: '참가자2' }, + { src: 'https://picsum.photos/200/200?random=3', alt: '참가자3' }, + ], + isPast: false, + isCanceled: false, + onClick: () => alert('카드 클릭!'), + onDelete: () => alert('삭제 버튼 클릭!'), + }, +}; + +// 취소된 모임 카드 스토리 +export const CanceledCard: Story = { + args: { + ...DefaultCard.args, + isCanceled: true, + }, +}; + +// 지난 모임 카드 스토리 +export const PastCard: Story = { + args: { + ...DefaultCard.args, + isPast: true, + }, +}; + +// 확정된 모임 카드 스토리 +export const ConfirmedCard: Story = { + args: { + ...DefaultCard.args, + status: 'confirmed', + }, +}; diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts index 2cc6adb9..203d52b3 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/interface/card.ts @@ -1,3 +1,4 @@ +import { DefaultClubCard } from '@/components/card/types/interface/clubCard'; import { ComponentPropsWithoutRef } from 'react'; interface CardBoxProps extends ComponentPropsWithoutRef<'div'> { @@ -32,6 +33,10 @@ interface CardOverlayProps extends ComponentPropsWithoutRef<'div'> { onDelete?: () => void; } +type CardProps = DefaultClubCard & { + variant?: 'default' | 'participated' | 'hosted'; +}; + export type { CardBoxProps, CardTitleProps, @@ -39,4 +44,5 @@ export type { CardDateTimeProps, CardImageProps, CardOverlayProps, + CardProps, }; diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/interface/clubCard.ts new file mode 100644 index 00000000..feaf5ba0 --- /dev/null +++ b/src/components/card/types/interface/clubCard.ts @@ -0,0 +1,40 @@ +interface ClubCard { + // 이미지 정보 + imageUrl: string; + imageAlt?: string; + + // 모임 정보 + title: string; + location: string; + datetime: string; + meetingType: 'FREE' | 'FIXED'; + + // 개설 현황 + status: 'pending' | 'confirmed' | 'closed'; + + // 액션 + onClick: () => void; +} + +interface DefaultClubCard extends ClubCard { + // 찜 정보 + isLiked: boolean; + onLikeClick: () => void; + + // 참가자 현황 + current: number; + max: number; + participants: Array<{ + src: string; + alt: string; + }>; + + // 상태 정보 + isPast: boolean; + isCanceled: boolean; + + // 블러에서 취소 액션 + onDelete: () => void; +} + +export type { ClubCard, DefaultClubCard }; diff --git a/src/components/card/types/interface/index.ts b/src/components/card/types/interface/index.ts index cb5809fe..cf51da62 100644 --- a/src/components/card/types/interface/index.ts +++ b/src/components/card/types/interface/index.ts @@ -1 +1,2 @@ export * from './card'; +export * from './clubCard'; From 8e8d476747f24427424c3909373cedb261202067 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Thu, 12 Dec 2024 18:03:14 +0900 Subject: [PATCH 05/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EB=94=94=ED=8F=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=B9=B4=EB=93=9C=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 105 ++++++++---------- src/components/card/ClubCard.stories.tsx | 11 +- .../card/types/interface/clubCard.ts | 4 - 3 files changed, 50 insertions(+), 70 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 326f3879..7e49d352 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -31,7 +31,7 @@ function CardBox({ children, className = '', ...props }: CardBoxProps) { return (
+ {alt} +
+ +
+
+ ); +} + function CardTitle({ children, className, ...props }: CardTitleProps) { return (

- {alt} -
- -
-

- ); -} - // Overlay (모임 취소시 표시되는 오버레이) function CardOverlay({ onDelete }: CardOverlayProps) { const { isCanceled } = useContext(CardContext); @@ -155,7 +155,6 @@ function Card(props: CardProps) { onLikeClick, current, max, - participants, isPast, isCanceled, onClick, @@ -165,51 +164,41 @@ function Card(props: CardProps) { return (
-
- -
- -
- {title} -
+ + + +
+
+ {title} + +
+
{location} {datetime}
+
-
- - - {participants.map((participant, index) => ( - - ))} - -
- {status === 'confirmed' && ( - - )} + +
- {isCanceled && }
+ {isCanceled && }
); } diff --git a/src/components/card/ClubCard.stories.tsx b/src/components/card/ClubCard.stories.tsx index 62e08ec3..49f4a4a0 100644 --- a/src/components/card/ClubCard.stories.tsx +++ b/src/components/card/ClubCard.stories.tsx @@ -20,20 +20,15 @@ export const DefaultCard: Story = { variant: 'default', imageUrl: 'https://picsum.photos/400/300', imageAlt: '모임 이미지', - title: '모임 제목 예시', - location: '서울특별시 강남구', - datetime: '2024.03.15 (금) 19:00', + title: '을지로에서 만나는 독서 모임', + location: '을지로 3가', + datetime: '12/14(토) 오전 10:00', meetingType: 'FREE', status: 'confirmed', isLiked: false, onLikeClick: () => alert('좋아요 클릭!'), current: 5, max: 10, - participants: [ - { src: 'https://picsum.photos/200/200?random=1', alt: '참가자1' }, - { src: 'https://picsum.photos/200/200?random=2', alt: '참가자2' }, - { src: 'https://picsum.photos/200/200?random=3', alt: '참가자3' }, - ], isPast: false, isCanceled: false, onClick: () => alert('카드 클릭!'), diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/interface/clubCard.ts index feaf5ba0..57296bb1 100644 --- a/src/components/card/types/interface/clubCard.ts +++ b/src/components/card/types/interface/clubCard.ts @@ -24,10 +24,6 @@ interface DefaultClubCard extends ClubCard { // 참가자 현황 current: number; max: number; - participants: Array<{ - src: string; - alt: string; - }>; // 상태 정보 isPast: boolean; From 41a1dea2229373323f6a8007f026d3f27904c0d9 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Thu, 12 Dec 2024 18:39:39 +0900 Subject: [PATCH 06/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20clubchip?= =?UTF-8?q?=20variant=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chip/club-chip/ClubChip.stories.tsx | 1 + src/components/chip/club-chip/ClubChip.tsx | 54 +++++++++---------- tailwind.config.ts | 8 ++- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/components/chip/club-chip/ClubChip.stories.tsx b/src/components/chip/club-chip/ClubChip.stories.tsx index 74f41fcc..55218fe0 100644 --- a/src/components/chip/club-chip/ClubChip.stories.tsx +++ b/src/components/chip/club-chip/ClubChip.stories.tsx @@ -43,6 +43,7 @@ export const AllStates: Story = { +
), }; diff --git a/src/components/chip/club-chip/ClubChip.tsx b/src/components/chip/club-chip/ClubChip.tsx index 500baa9e..f86d47fe 100644 --- a/src/components/chip/club-chip/ClubChip.tsx +++ b/src/components/chip/club-chip/ClubChip.tsx @@ -1,13 +1,35 @@ import Chip from '../Chip'; import { twMerge } from 'tailwind-merge'; -type ClubChipVariant = 'completed' | 'scheduled' | 'pending' | 'confirmed'; +type ClubChipVariant = + | 'completed' + | 'scheduled' + | 'pending' + | 'confirmed' + | 'closed'; const CLUB_CHIP_TEXT = { completed: '참여완료', scheduled: '참여예정', pending: '개설대기', confirmed: '개설확정', + closed: '모집마감', +} as const; + +const CLUB_CHIP_VARIANT = { + completed: 'square-filled', + scheduled: 'square-filled', + pending: 'square-outlined', + confirmed: 'square-filled', + closed: 'square-filled', +} as const; + +const CLUB_CHIP_STYLE = { + completed: 'bg-gray-normal-01 text-gray-dark-02', + scheduled: 'bg-green-light-03 text-green-dark-01', + pending: 'border-blue-light-active text-blue-light-active', + confirmed: 'bg-blue-light text-blue-light-active', + closed: 'bg-blue-normal text-gray-white', } as const; interface ClubChipProps { @@ -16,37 +38,11 @@ interface ClubChipProps { } function ClubChip({ variant, className }: ClubChipProps) { - const getChipVariant = () => { - switch (variant) { - case 'completed': - return 'square-filled'; - case 'scheduled': - return 'square-filled'; - case 'pending': - return 'square-outlined'; - case 'confirmed': - return 'square-filled'; - } - }; - - const getCustomClassName = () => { - switch (variant) { - case 'completed': - return 'bg-gray-normal-01 text-gray-dark-02'; - case 'scheduled': - return 'bg-green-normal-01 text-gray-white'; - case 'pending': - return 'border-blue-normal-01 text-blue-normal-01'; - case 'confirmed': - return 'bg-blue-normal-01 text-gray-white'; - } - }; - return ( ); } diff --git a/tailwind.config.ts b/tailwind.config.ts index c017cc5e..a9ac2ca6 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -38,7 +38,13 @@ const config: Config = { darker: '#003b33', }, blue: { - 'normal-01': '#007AFF', + light: '#d9ebff', + 'light-active': '#009cf4', + normal: '#007aff', + }, + red: { + pink: '#ff337e', + normal: '#dc2626', }, }, }, From 81c61aac25a1c76e6e93dfcba921d3784285d69b Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Thu, 12 Dec 2024 19:05:22 +0900 Subject: [PATCH 07/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=B9=A9=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/components/card/Card.tsx | 8 +++++--- src/components/card/ClubCard.stories.tsx | 2 +- src/components/card/types/interface/clubCard.ts | 2 +- src/components/chip/club-chip/ClubChip.stories.tsx | 14 ++++++++++++++ src/components/chip/club-chip/ClubChip.tsx | 10 +++++++++- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 7e49d352..1f4f4660 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -23,6 +23,7 @@ import { CardImageProps, CardProps, } from './types/interface'; +import ClubChip from '@/components/chip/club-chip/ClubChip'; const CardContext = createContext({ isCanceled: false }); @@ -31,7 +32,7 @@ function CardBox({ children, className = '', ...props }: CardBoxProps) { return (
{title} - +
{location} @@ -190,7 +192,7 @@ function Card(props: CardProps) { max={max} isPast={isPast} /> - +
void; diff --git a/src/components/chip/club-chip/ClubChip.stories.tsx b/src/components/chip/club-chip/ClubChip.stories.tsx index 55218fe0..e9281ee8 100644 --- a/src/components/chip/club-chip/ClubChip.stories.tsx +++ b/src/components/chip/club-chip/ClubChip.stories.tsx @@ -36,6 +36,18 @@ export const Confirmed: Story = { }, }; +export const FreeBook: Story = { + args: { + variant: 'FREE', + }, +}; + +export const FixedBook: Story = { + args: { + variant: 'FIXED', + }, +}; + export const AllStates: Story = { render: () => (
@@ -44,6 +56,8 @@ export const AllStates: Story = { + +
), }; diff --git a/src/components/chip/club-chip/ClubChip.tsx b/src/components/chip/club-chip/ClubChip.tsx index f86d47fe..63389c03 100644 --- a/src/components/chip/club-chip/ClubChip.tsx +++ b/src/components/chip/club-chip/ClubChip.tsx @@ -6,7 +6,9 @@ type ClubChipVariant = | 'scheduled' | 'pending' | 'confirmed' - | 'closed'; + | 'closed' + | 'FREE' + | 'FIXED'; const CLUB_CHIP_TEXT = { completed: '참여완료', @@ -14,6 +16,8 @@ const CLUB_CHIP_TEXT = { pending: '개설대기', confirmed: '개설확정', closed: '모집마감', + FREE: '자유책', + FIXED: '지정책', } as const; const CLUB_CHIP_VARIANT = { @@ -22,6 +26,8 @@ const CLUB_CHIP_VARIANT = { pending: 'square-outlined', confirmed: 'square-filled', closed: 'square-filled', + FREE: 'rounded-filled', + FIXED: 'rounded-light', } as const; const CLUB_CHIP_STYLE = { @@ -30,6 +36,8 @@ const CLUB_CHIP_STYLE = { pending: 'border-blue-light-active text-blue-light-active', confirmed: 'bg-blue-light text-blue-light-active', closed: 'bg-blue-normal text-gray-white', + FREE: '', + FIXED: '', } as const; interface ClubChipProps { From 5ae61d423e1afde4e89e1fb56acbcdb69acefc31 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 09:36:34 +0900 Subject: [PATCH 08/24] =?UTF-8?q?=F0=9F=92=84[Design=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=9D=BC=EB=B6=80=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EB=B0=8F=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=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/components/card/Card.tsx | 2 +- src/components/card/ClubCard.stories.tsx | 93 +++++++++++++++--------- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 1f4f4660..cdb5f875 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -32,7 +32,7 @@ function CardBox({ children, className = '', ...props }: CardBoxProps) { return (
; -// 기본 모임 카드 스토리 -export const DefaultCard: Story = { - args: { - variant: 'default', - imageUrl: 'https://picsum.photos/400/300', - imageAlt: '모임 이미지', - title: '을지로 독서 모임', - location: '을지로 3가', - datetime: '12/14(토) 오전 10:00', - meetingType: 'FREE', - status: 'confirmed', - isLiked: false, - onLikeClick: () => alert('좋아요 클릭!'), - current: 5, - max: 10, - isPast: false, - isCanceled: false, - onClick: () => alert('카드 클릭!'), - onDelete: () => alert('삭제 버튼 클릭!'), - }, -}; +const defaultArgs = { + variant: 'default', + imageUrl: 'https://picsum.photos/400/300', + imageAlt: '모임 이미지', + title: '을지로 독서 모임', + location: '을지로 3가', + datetime: '12/14(토) 오전 10:00', + meetingType: 'FREE', + status: 'confirmed', + isLiked: false, + onLikeClick: () => alert('좋아요 클릭!'), + current: 5, + max: 10, + isPast: false, + isCanceled: false, + onClick: () => alert('카드 클릭!'), + onDelete: () => alert('삭제 버튼 클릭!'), +} as const; -// 취소된 모임 카드 스토리 -export const CanceledCard: Story = { - args: { - ...DefaultCard.args, - isCanceled: true, +export const Mobile: Story = { + parameters: { + viewport: { + defaultViewport: 'mobile', + }, }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: defaultArgs, }; -// 지난 모임 카드 스토리 -export const PastCard: Story = { - args: { - ...DefaultCard.args, - isPast: true, +export const Tablet: Story = { + parameters: { + viewport: { + defaultViewport: 'tablet', + }, }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: defaultArgs, }; -// 확정된 모임 카드 스토리 -export const ConfirmedCard: Story = { - args: { - ...DefaultCard.args, - status: 'confirmed', +export const Desktop: Story = { + parameters: { + viewport: { + defaultViewport: 'desktop', + }, }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: defaultArgs, }; From 9286909cf53aa1fc2528ffb93dec266eb26d2ca8 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 09:45:55 +0900 Subject: [PATCH 09/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=B1=85=EB=AA=A8?= =?UTF-8?q?=EC=9E=84,=20=EC=B0=9C=ED=95=98=EA=B8=B0=20=EC=B9=B4=EB=93=9C?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 10 +++++----- src/components/card/ClubCard.stories.tsx | 2 +- src/components/card/types/interface/card.ts | 2 +- src/components/chip/club-chip/ClubChip.tsx | 14 ++++++++++++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index cdb5f875..14a9317c 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -144,7 +144,7 @@ function CardOverlay({ onDelete }: CardOverlayProps) { function Card(props: CardProps) { const renderCardContent = () => { switch (props.variant) { - case 'default': + case 'defaultClub': default: { const { imageUrl, @@ -177,7 +177,7 @@ function Card(props: CardProps) {
{title} - +
{location} @@ -192,7 +192,7 @@ function Card(props: CardProps) { max={max} isPast={isPast} /> - +
; const defaultArgs = { - variant: 'default', + variant: 'defaultClub', imageUrl: 'https://picsum.photos/400/300', imageAlt: '모임 이미지', title: '을지로 독서 모임', diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts index 203d52b3..f544b772 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/interface/card.ts @@ -34,7 +34,7 @@ interface CardOverlayProps extends ComponentPropsWithoutRef<'div'> { } type CardProps = DefaultClubCard & { - variant?: 'default' | 'participated' | 'hosted'; + variant?: 'defaultClub' | 'participatedClub' | 'hostedClub'; }; export type { diff --git a/src/components/chip/club-chip/ClubChip.tsx b/src/components/chip/club-chip/ClubChip.tsx index 63389c03..08e2e67e 100644 --- a/src/components/chip/club-chip/ClubChip.tsx +++ b/src/components/chip/club-chip/ClubChip.tsx @@ -40,17 +40,27 @@ const CLUB_CHIP_STYLE = { FIXED: '', } as const; +const PAST_STATUS_STYLE = 'bg-gray-dark-02 text-gray-white'; +const PAST_BOOK_TYPE_STYLE = 'bg-gray-normal-01 text-gray-dark-02'; + interface ClubChipProps { variant: ClubChipVariant; className?: string; + isPast?: boolean; } -function ClubChip({ variant, className }: ClubChipProps) { +function ClubChip({ variant, className, isPast = false }: ClubChipProps) { + const style = isPast + ? ['FREE', 'FIXED'].includes(variant) + ? PAST_BOOK_TYPE_STYLE + : PAST_STATUS_STYLE + : CLUB_CHIP_STYLE[variant]; + return ( ); } From ee8d12b85e90adfd09c7f388fc94d7aeae783769 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 09:57:32 +0900 Subject: [PATCH 10/24] =?UTF-8?q?=F0=9F=94=A5[Remove]=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=20=ED=8C=8C=EC=9D=BC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/TmpCard.stories.tsx | 4 - .../card/my-club-card/MyClubCard.stories.tsx | 79 ------------------- .../card/my-club-card/MyClubCard.tsx | 68 ---------------- .../card/simple-card/SimpleCard.stories.tsx | 59 -------------- .../card/simple-card/SimpleCard.tsx | 35 -------- 5 files changed, 245 deletions(-) delete mode 100644 src/components/card/my-club-card/MyClubCard.stories.tsx delete mode 100644 src/components/card/my-club-card/MyClubCard.tsx delete mode 100644 src/components/card/simple-card/SimpleCard.stories.tsx delete mode 100644 src/components/card/simple-card/SimpleCard.tsx diff --git a/src/components/card/TmpCard.stories.tsx b/src/components/card/TmpCard.stories.tsx index 2ff417d2..e64ca3cb 100644 --- a/src/components/card/TmpCard.stories.tsx +++ b/src/components/card/TmpCard.stories.tsx @@ -13,10 +13,6 @@ const meta = { control: 'boolean', description: '모임 취소 여부', }, - className: { - control: 'text', - description: '추가 스타일링을 위한 className', - }, }, } satisfies Meta; diff --git a/src/components/card/my-club-card/MyClubCard.stories.tsx b/src/components/card/my-club-card/MyClubCard.stories.tsx deleted file mode 100644 index f2cfcc23..00000000 --- a/src/components/card/my-club-card/MyClubCard.stories.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import MyClubCard from './MyClubCard'; - -const meta = { - title: 'Components/TMPCard/MyClubCard', - component: MyClubCard, - parameters: { - layout: 'centered', - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const mockClubMeeting = { - meetingInfo: { - title: '을지로에서 만나는 독서 모임', - location: '을지로 3가', - datetime: '12/14(토) 오전 10:00', - category: '자유책', - }, - imageInfo: { - url: 'https://picsum.photos/seed/bookclub/800/450', - isLiked: true, - onLikeClick: () => alert('좋아요를 눌렀습니다!'), - }, - clubStatus: { - isCompleted: false, - isConfirmed: true, - }, - actions: { - onClick: () => alert('카드를 클릭했습니다!'), - onDelete: () => alert('모임을 삭제했습니다!'), - }, -}; - -export const Default: Story = { - args: { - meeting: mockClubMeeting, - }, - render: (args) => ( -
- -
- ), -}; - -export const Completed: Story = { - args: { - meeting: { - ...mockClubMeeting, - clubStatus: { - ...mockClubMeeting.clubStatus, - isCompleted: true, - }, - }, - }, -}; - -export const Pending: Story = { - args: { - meeting: { - ...mockClubMeeting, - clubStatus: { - ...mockClubMeeting.clubStatus, - isConfirmed: false, - }, - }, - }, -}; - -export const Canceled: Story = { - args: { - meeting: { - ...mockClubMeeting, - isCanceled: true, - }, - }, -}; diff --git a/src/components/card/my-club-card/MyClubCard.tsx b/src/components/card/my-club-card/MyClubCard.tsx deleted file mode 100644 index ffc5c651..00000000 --- a/src/components/card/my-club-card/MyClubCard.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { ComponentPropsWithoutRef } from 'react'; -import Card from '../Card'; -import ClubChip from '@/components/chip/club-chip/ClubChip'; -import Button from '@/components/button/Button'; -import { ClubMeeting } from '@/components/card/types'; -import Chip from '@/components/chip/Chip'; - -interface MyClubCardProps extends ComponentPropsWithoutRef<'article'> { - meeting: ClubMeeting; -} - -function MyClubCard({ meeting, className, ...props }: MyClubCardProps) { - const { - meetingInfo, - imageInfo, - clubStatus, - isCanceled = false, - actions, - } = meeting; - - return ( - -
- - - {/* 첫 번째 줄: ClubChip + Chip */} -
-
- - -
- -
- - {/* 두 번째 줄: 모임 정보 */} -
-

- {meetingInfo.title} -

-
- {meetingInfo.location} - {meetingInfo.datetime} -
-
- - {/* 세 번째 줄: 버튼 */} -
-
- - -
-
-
- ); -} - -export default MyClubCard; diff --git a/src/components/card/simple-card/SimpleCard.stories.tsx b/src/components/card/simple-card/SimpleCard.stories.tsx deleted file mode 100644 index 9bf44483..00000000 --- a/src/components/card/simple-card/SimpleCard.stories.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import SimpleCard from './SimpleCard'; -import { mockMeeting } from '../mock/mock'; - -const meta = { - title: 'Components/TMPCard/SimpleCard', - component: SimpleCard, - parameters: { - layout: 'centered', - }, - argTypes: { - meeting: { - control: 'object', - description: '모임 정보 (meetingInfo, participationStatus, imageInfo 등)', - }, - className: { - control: 'text', - description: '추가 스타일링을 위한 className', - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - meeting: mockMeeting, - }, - render: (args) => ( -
- -
- ), -}; - -export const Mobile: Story = { - ...Default, - parameters: { - viewport: { defaultViewport: 'mobile' }, - }, - render: (args) => ( -
- -
- ), -}; - -export const Desktop: Story = { - ...Default, - parameters: { - viewport: { defaultViewport: 'desktop' }, - }, - render: (args) => ( -
- -
- ), -}; diff --git a/src/components/card/simple-card/SimpleCard.tsx b/src/components/card/simple-card/SimpleCard.tsx deleted file mode 100644 index 933ba801..00000000 --- a/src/components/card/simple-card/SimpleCard.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { ComponentPropsWithoutRef } from 'react'; -import Card from '../Card'; -import { Meeting } from '@/components/card/types'; - -interface SimpleCardProps extends ComponentPropsWithoutRef<'article'> { - meeting: Meeting; -} - -function SimpleCard({ meeting, className, ...props }: SimpleCardProps) { - const { - meetingInfo, - participationStatus, - imageInfo, - isPast = false, - isCanceled = false, - actions, - } = meeting; - - return ( - -
-
- -
- - - - - -
-
- ); -} - -export default SimpleCard; From 59fd23a90f024b7065f18808fff565057a06d7f3 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 10:00:37 +0900 Subject: [PATCH 11/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20=EC=98=A4?= =?UTF-8?q?=EB=B2=84=EB=A0=88=EC=9D=B4=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 14a9317c..0c3a9fc4 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -24,6 +24,7 @@ import { CardProps, } from './types/interface'; import ClubChip from '@/components/chip/club-chip/ClubChip'; +import Button from '@/components/button/Button'; const CardContext = createContext({ isCanceled: false }); @@ -129,13 +130,14 @@ function CardOverlay({ onDelete }: CardOverlayProps) {
{'새로운 모임을 찾아볼까요?'}

- {/* TODO:: 삭제 버튼 공통 컴포넌트 변경 필요 */} - + />
); From 9b3e06ea6ab4ce5e9fcca396c76cc81829c937fd Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 11:06:08 +0900 Subject: [PATCH 12/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EB=82=B4=EA=B0=80=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A0=20=EB=AA=A8=EC=9E=84=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/Button.tsx | 3 +- src/components/card/Card.tsx | 96 +++++++++++++- src/components/card/ClubCard.stories.tsx | 121 +++++++++++++++++- src/components/card/types/interface/card.ts | 12 +- .../card/types/interface/clubCard.ts | 21 ++- 5 files changed, 240 insertions(+), 13 deletions(-) diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx index 9f5e5478..6e86056a 100644 --- a/src/components/button/Button.tsx +++ b/src/components/button/Button.tsx @@ -20,7 +20,7 @@ export default function Button({ isSubmitting, ...buttonProps }: ButtonProps) { - const { disabled } = buttonProps; + const { disabled, className } = buttonProps; const sizeClasses = SIZE[size]; const baseClasses = 'rounded-[12px] font-semibold cursor-pointer'; @@ -54,6 +54,7 @@ export default function Button({ baseClasses, variantClasses, isButtonDisabled && 'cursor-not-allowed', + className, ); return ( diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 0c3a9fc4..2f810af1 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -5,7 +5,12 @@ import AvatarGroup from '../avatar-group/AvatarGroup'; import ConfirmedLabel from '../confirmed-label/ConfirmedLabel'; import ProgressBar from '../progress-bar/ProgressBar'; import Avatar from '../avatar/Avatar'; -import { LocationIcon, HostIcon, HeartIcon } from '../../../public/icons'; +import { + LocationIcon, + HostIcon, + HeartIcon, + RatingIcon, +} from '../../../public/icons'; import Image from 'next/image'; import { CardContextType, @@ -123,7 +128,7 @@ function CardOverlay({ onDelete }: CardOverlayProps) { }; return ( -
+

{'호스트가 모임을 취소했어요.'} @@ -207,7 +212,90 @@ function Card(props: CardProps) { ); } - case 'participatedClub': + case 'participatedClub': { + const { + imageUrl, + imageAlt, + isLiked, + onLikeClick, + isCanceled, + onClick, + onDelete, + status, + meetingType, + isPast, + title, + location, + datetime, + onCancel, + reviewScore, + } = props; + + return ( +

+ + +
+
+ + +
+
+ {title} +
+ {location} + {datetime} +
+
+
+ {!isPast ? ( +
+
+
+ {isCanceled && } +
+ ); + } + case 'hostedClub': return null; // 추후 구현 } @@ -239,7 +327,7 @@ Card.Info = CardInfo; Card.Status = CardStatus; Card.Host = CardHost; -// Info 컴포넌트 (모임에 관한 정보 - 제목, 위치, 날짜 등) +// Info 컴포넌트 (��임에 관한 정보 - 제목, 위치, 날짜 등) function CardInfo({ title, category, diff --git a/src/components/card/ClubCard.stories.tsx b/src/components/card/ClubCard.stories.tsx index ee82df35..50d2a934 100644 --- a/src/components/card/ClubCard.stories.tsx +++ b/src/components/card/ClubCard.stories.tsx @@ -31,9 +31,17 @@ const defaultArgs = { isCanceled: false, onClick: () => alert('카드 클릭!'), onDelete: () => alert('삭제 버튼 클릭!'), + reviewScore: 4.5, } as const; -export const Mobile: Story = { +const participatedArgs = { + ...defaultArgs, + variant: 'participatedClub', + onCancel: () => alert('모임 취소하기 클릭!'), +} as const; + +// Default Club Card Stories +export const DefaultMobile: Story = { parameters: { viewport: { defaultViewport: 'mobile', @@ -49,7 +57,7 @@ export const Mobile: Story = { args: defaultArgs, }; -export const Tablet: Story = { +export const DefaultTablet: Story = { parameters: { viewport: { defaultViewport: 'tablet', @@ -65,7 +73,7 @@ export const Tablet: Story = { args: defaultArgs, }; -export const Desktop: Story = { +export const DefaultDesktop: Story = { parameters: { viewport: { defaultViewport: 'desktop', @@ -80,3 +88,110 @@ export const Desktop: Story = { ], args: defaultArgs, }; + +// Participated Club Card Stories +export const ParticipatedMobile: Story = { + parameters: { + viewport: { + defaultViewport: 'mobile', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: participatedArgs, +}; + +export const ParticipatedTablet: Story = { + parameters: { + viewport: { + defaultViewport: 'tablet', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: participatedArgs, +}; + +export const ParticipatedDesktop: Story = { + parameters: { + viewport: { + defaultViewport: 'desktop', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: participatedArgs, +}; + +// Completed Participated Club Card Stories +export const CompletedParticipatedMobile: Story = { + parameters: { + viewport: { + defaultViewport: 'mobile', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: { + ...participatedArgs, + isPast: true, + }, +}; + +export const CompletedParticipatedTablet: Story = { + parameters: { + viewport: { + defaultViewport: 'tablet', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: { + ...participatedArgs, + isPast: true, + }, +}; + +export const CompletedParticipatedDesktop: Story = { + parameters: { + viewport: { + defaultViewport: 'desktop', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: { + ...participatedArgs, + isPast: true, + }, +}; diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts index f544b772..f7deefc2 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/interface/card.ts @@ -1,4 +1,7 @@ -import { DefaultClubCard } from '@/components/card/types/interface/clubCard'; +import { + DefaultClubCard, + ParticipatedClubCard, +} from '@/components/card/types/interface/clubCard'; import { ComponentPropsWithoutRef } from 'react'; interface CardBoxProps extends ComponentPropsWithoutRef<'div'> { @@ -33,9 +36,10 @@ interface CardOverlayProps extends ComponentPropsWithoutRef<'div'> { onDelete?: () => void; } -type CardProps = DefaultClubCard & { - variant?: 'defaultClub' | 'participatedClub' | 'hostedClub'; -}; +type CardProps = DefaultClubCard & + ParticipatedClubCard & { + variant?: 'defaultClub' | 'participatedClub' | 'hostedClub'; + }; export type { CardBoxProps, diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/interface/clubCard.ts index 3eb3ef10..858dce8e 100644 --- a/src/components/card/types/interface/clubCard.ts +++ b/src/components/card/types/interface/clubCard.ts @@ -33,4 +33,23 @@ interface DefaultClubCard extends ClubCard { onDelete: () => void; } -export type { ClubCard, DefaultClubCard }; +interface ParticipatedClubCard extends ClubCard { + // 찜 정보 + isLiked: boolean; + onLikeClick: () => void; + + // 상태 정보 + isPast: boolean; + isCanceled: boolean; + + // 블러에서 취소 액션 + onDelete: () => void; + + // 모임 취소 액션 + onCancel: () => void; + + // 리뷰 정보 + reviewScore?: number; +} + +export type { ClubCard, DefaultClubCard, ParticipatedClubCard }; From 442a96c552379476e68b7ceea14e794c66d1b429 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 11:35:43 +0900 Subject: [PATCH 13/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EB=82=98=EC=9D=98=20?= =?UTF-8?q?=EB=AA=A8=EC=9E=84=20=EC=B9=B4=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 83 ++++++++++++++++++- src/components/card/ClubCard.stories.tsx | 47 +++++------ src/components/card/types/interface/card.ts | 2 + .../card/types/interface/clubCard.ts | 19 ++++- 4 files changed, 122 insertions(+), 29 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 2f810af1..644cd882 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -213,6 +213,84 @@ function Card(props: CardProps) { } case 'participatedClub': { + const { + imageUrl, + imageAlt, + isLiked, + onLikeClick, + isCanceled, + onClick, + onDelete, + status, + meetingType, + title, + location, + datetime, + isJoined, + onWriteReview, + onCancel, + } = props; + + return ( +
+ + +
+
+
+ + +
+ +
+
+ {title} +
+ {location} + {datetime} +
+
+
+ {isJoined ? ( +
+
+
+ {isCanceled && } +
+ ); + } + + case 'hostedClub': { const { imageUrl, imageAlt, @@ -295,9 +373,6 @@ function Card(props: CardProps) {
); } - - case 'hostedClub': - return null; // 추후 구현 } }; @@ -327,7 +402,7 @@ Card.Info = CardInfo; Card.Status = CardStatus; Card.Host = CardHost; -// Info 컴포넌트 (��임에 관한 정보 - 제목, 위치, 날짜 등) +// Info 컴포넌트 (모임에 관한 정보 - 제목, 위치, 날짜 등) function CardInfo({ title, category, diff --git a/src/components/card/ClubCard.stories.tsx b/src/components/card/ClubCard.stories.tsx index 50d2a934..56df8c8a 100644 --- a/src/components/card/ClubCard.stories.tsx +++ b/src/components/card/ClubCard.stories.tsx @@ -34,10 +34,18 @@ const defaultArgs = { reviewScore: 4.5, } as const; +const hostedArgs = { + ...defaultArgs, + variant: 'hostedClub', + onCancel: () => alert('모임 취소하기 클릭!'), +} as const; + const participatedArgs = { ...defaultArgs, variant: 'participatedClub', - onCancel: () => alert('모임 취소하기 클릭!'), + isJoined: false, + onWriteReview: () => alert('리뷰 작성하기 클릭!'), + onCancel: () => alert('참여 취소하기 클릭!'), } as const; // Default Club Card Stories @@ -89,8 +97,8 @@ export const DefaultDesktop: Story = { args: defaultArgs, }; -// Participated Club Card Stories -export const ParticipatedMobile: Story = { +// Hosted Club Card Stories +export const HostedMobile: Story = { parameters: { viewport: { defaultViewport: 'mobile', @@ -103,10 +111,10 @@ export const ParticipatedMobile: Story = {
), ], - args: participatedArgs, + args: hostedArgs, }; -export const ParticipatedTablet: Story = { +export const HostedTablet: Story = { parameters: { viewport: { defaultViewport: 'tablet', @@ -119,10 +127,10 @@ export const ParticipatedTablet: Story = {
), ], - args: participatedArgs, + args: hostedArgs, }; -export const ParticipatedDesktop: Story = { +export const HostedDesktop: Story = { parameters: { viewport: { defaultViewport: 'desktop', @@ -135,11 +143,11 @@ export const ParticipatedDesktop: Story = {
), ], - args: participatedArgs, + args: hostedArgs, }; -// Completed Participated Club Card Stories -export const CompletedParticipatedMobile: Story = { +// Participated Club Card Stories +export const ParticipatedMobile: Story = { parameters: { viewport: { defaultViewport: 'mobile', @@ -152,13 +160,10 @@ export const CompletedParticipatedMobile: Story = {
), ], - args: { - ...participatedArgs, - isPast: true, - }, + args: participatedArgs, }; -export const CompletedParticipatedTablet: Story = { +export const ParticipatedTablet: Story = { parameters: { viewport: { defaultViewport: 'tablet', @@ -171,13 +176,10 @@ export const CompletedParticipatedTablet: Story = { ), ], - args: { - ...participatedArgs, - isPast: true, - }, + args: participatedArgs, }; -export const CompletedParticipatedDesktop: Story = { +export const ParticipatedDesktop: Story = { parameters: { viewport: { defaultViewport: 'desktop', @@ -190,8 +192,5 @@ export const CompletedParticipatedDesktop: Story = { ), ], - args: { - ...participatedArgs, - isPast: true, - }, + args: participatedArgs, }; diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts index f7deefc2..324e3ea7 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/interface/card.ts @@ -1,5 +1,6 @@ import { DefaultClubCard, + HostedClubCard, ParticipatedClubCard, } from '@/components/card/types/interface/clubCard'; import { ComponentPropsWithoutRef } from 'react'; @@ -37,6 +38,7 @@ interface CardOverlayProps extends ComponentPropsWithoutRef<'div'> { } type CardProps = DefaultClubCard & + HostedClubCard & ParticipatedClubCard & { variant?: 'defaultClub' | 'participatedClub' | 'hostedClub'; }; diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/interface/clubCard.ts index 858dce8e..1bd3d5e6 100644 --- a/src/components/card/types/interface/clubCard.ts +++ b/src/components/card/types/interface/clubCard.ts @@ -38,6 +38,23 @@ interface ParticipatedClubCard extends ClubCard { isLiked: boolean; onLikeClick: () => void; + // 상태 정보 + isJoined: boolean; + isCanceled: boolean; + + // 블러에서 취소 액션 + onDelete: () => void; + + // 버튼 액션 + onWriteReview: () => void; + onCancel: () => void; +} + +interface HostedClubCard extends ClubCard { + // 블러에서 취소 액션 + isLiked: boolean; + onLikeClick: () => void; + // 상태 정보 isPast: boolean; isCanceled: boolean; @@ -52,4 +69,4 @@ interface ParticipatedClubCard extends ClubCard { reviewScore?: number; } -export type { ClubCard, DefaultClubCard, ParticipatedClubCard }; +export type { ClubCard, DefaultClubCard, HostedClubCard, ParticipatedClubCard }; From 4346dd65a700b4057dd0ff3ae85e5f27ed77bfd3 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 11:42:53 +0900 Subject: [PATCH 14/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20=EB=82=B4?= =?UTF-8?q?=EA=B0=80=20=EB=A7=8C=EB=93=A0=EB=AA=A8=EC=9E=84=20=EC=B0=9C,?= =?UTF-8?q?=20=EB=B8=94=EB=9F=AC=20=EA=B8=B0=EB=8A=A5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 22 ++++++------------- .../card/types/interface/clubCard.ts | 8 ------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 644cd882..58f80ae8 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -52,7 +52,7 @@ function CardBox({ children, className = '', ...props }: CardBoxProps) { function CardImage({ url, alt = '모임 이미지', - isLiked = false, + isLiked, onLikeClick, className, ...props @@ -66,9 +66,11 @@ function CardImage({ {...props} > {alt} -
- -
+ {isLiked !== undefined && ( +
+ +
+ )} ); } @@ -294,11 +296,7 @@ function Card(props: CardProps) { const { imageUrl, imageAlt, - isLiked, - onLikeClick, - isCanceled, onClick, - onDelete, status, meetingType, isPast, @@ -311,12 +309,7 @@ function Card(props: CardProps) { return (
- +
@@ -369,7 +362,6 @@ function Card(props: CardProps) {
- {isCanceled && }
); } diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/interface/clubCard.ts index 1bd3d5e6..fa7bdf60 100644 --- a/src/components/card/types/interface/clubCard.ts +++ b/src/components/card/types/interface/clubCard.ts @@ -51,16 +51,8 @@ interface ParticipatedClubCard extends ClubCard { } interface HostedClubCard extends ClubCard { - // 블러에서 취소 액션 - isLiked: boolean; - onLikeClick: () => void; - // 상태 정보 isPast: boolean; - isCanceled: boolean; - - // 블러에서 취소 액션 - onDelete: () => void; // 모임 취소 액션 onCancel: () => void; From cf15300882fa4ecb45729c6f502b3243eae82efe Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 12:00:39 +0900 Subject: [PATCH 15/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EA=B3=B5=ED=86=B5?= =?UTF-8?q?=ED=99=94=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 6 ++-- .../card/types/interface/clubCard.ts | 30 +++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 58f80ae8..162154d2 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -228,7 +228,7 @@ function Card(props: CardProps) { title, location, datetime, - isJoined, + isPast, onWriteReview, onCancel, } = props; @@ -245,7 +245,7 @@ function Card(props: CardProps) {
- +
@@ -258,7 +258,7 @@ function Card(props: CardProps) {
- {isJoined ? ( + {isPast ? (
+ )} + + + ); + } } }; return ( - +
alert('참여 취소하기 클릭!'), } as const; +const detailedArgs = { + ...baseArgs, + variant: 'detailedClub', + isLiked: false, + onLikeClick: () => alert('좋아요 클릭!'), + current: 5, + max: 10, + participants: [ + { + id: '1', + name: '참여자1', + profileImage: 'https://picsum.photos/200/200?random=1', + profileImageAlt: '참여자1 프로필 이미지', + }, + { + id: '2', + name: '참여자2', + profileImage: 'https://picsum.photos/200/200?random=2', + profileImageAlt: '참여자2 프로필 이미지', + }, + { + id: '3', + name: '참여자3', + profileImage: 'https://picsum.photos/200/200?random=3', + profileImageAlt: '참여자3 프로필 이미지', + }, + ], + host: { + id: 'host1', + name: '호스트', + profileImage: 'https://picsum.photos/200/200?random=host', + }, + isHost: false, +} as const; + // Default Club Card Stories export const DefaultMobile: Story = { parameters: { @@ -202,3 +237,52 @@ export const ParticipatedDesktop: Story = { ], args: participatedArgs, }; + +// Detailed Club Card Stories +export const DetailedMobile: Story = { + parameters: { + viewport: { + defaultViewport: 'mobile', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: detailedArgs, +}; + +export const DetailedTablet: Story = { + parameters: { + viewport: { + defaultViewport: 'tablet', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: detailedArgs, +}; + +export const DetailedDesktop: Story = { + parameters: { + viewport: { + defaultViewport: 'desktop', + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: detailedArgs, +}; diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts index 324e3ea7..b6b57d20 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/interface/card.ts @@ -1,5 +1,6 @@ import { DefaultClubCard, + DetailedClubCard, HostedClubCard, ParticipatedClubCard, } from '@/components/card/types/interface/clubCard'; @@ -37,11 +38,14 @@ interface CardOverlayProps extends ComponentPropsWithoutRef<'div'> { onDelete?: () => void; } -type CardProps = DefaultClubCard & - HostedClubCard & - ParticipatedClubCard & { - variant?: 'defaultClub' | 'participatedClub' | 'hostedClub'; - }; +type CardProps = { + variant?: 'defaultClub' | 'participatedClub' | 'hostedClub' | 'detailedClub'; +} & ( + | (DefaultClubCard & { variant?: 'defaultClub' }) + | (HostedClubCard & { variant?: 'hostedClub' }) + | (ParticipatedClubCard & { variant?: 'participatedClub' }) + | (DetailedClubCard & { variant?: 'detailedClub' }) +); export type { CardBoxProps, diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/interface/clubCard.ts index cc28e059..e64966e8 100644 --- a/src/components/card/types/interface/clubCard.ts +++ b/src/components/card/types/interface/clubCard.ts @@ -51,4 +51,36 @@ interface HostedClubCard extends ClubCard { reviewScore?: number; } -export type { ClubCard, DefaultClubCard, HostedClubCard, ParticipatedClubCard }; +interface DetailedClubCard extends ClubCard { + // 찜 정보 + isLiked: boolean; + onLikeClick: () => void; + + // 참가자 정보 + current: number; + max: number; + participants: ReadonlyArray<{ + readonly id?: string; + readonly name: string; + readonly profileImage?: string; + readonly profileImageAlt?: string; + }>; + + // 호스트 정보 + host: { + id?: string; + name: string; + profileImage?: string; + }; + + // 호스트 여부 + isHost: boolean; +} + +export type { + ClubCard, + DefaultClubCard, + HostedClubCard, + ParticipatedClubCard, + DetailedClubCard, +}; From adaaed8605e1ac14f7acef8cc68c9eececb93c93 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 14:14:04 +0900 Subject: [PATCH 18/24] =?UTF-8?q?=F0=9F=92=84[Design]=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=20=EC=B9=B4=EB=93=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 666321f0..31822782 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -396,7 +396,7 @@ function Card(props: CardProps) { alt={imageAlt} isLiked={isLiked} onLikeClick={onLikeClick} - className="h-[230px] md:h-full" + className="md:h-100%" />
From 61ac436d80cf385eefac91640e0ba002b2262ba0 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 16:03:48 +0900 Subject: [PATCH 19/24] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/Button.tsx | 4 + src/components/card/Card.tsx | 196 +++++++++++++----- src/components/card/ClubCard.stories.tsx | 18 ++ src/components/card/types/components.ts | 11 +- src/components/card/types/interface/card.ts | 12 ++ .../card/types/interface/clubCard.ts | 12 ++ src/components/card/types/meeting.ts | 9 - 7 files changed, 189 insertions(+), 73 deletions(-) diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx index 6e86056a..d06e6063 100644 --- a/src/components/button/Button.tsx +++ b/src/components/button/Button.tsx @@ -33,6 +33,10 @@ export default function Button({ : themeColor; const variantClasses = (() => { + if (disabled) { + return `text-gray-dark-02 bg-gray-normal-02`; + } + switch (fillType) { case 'solid': return `text-gray-white ${COLOR_SCHEMES[resolvedColor]['bg']}`; diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index 31822782..b80309da 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -12,12 +12,7 @@ import { RatingIcon, } from '../../../public/icons'; import Image from 'next/image'; -import { - CardContextType, - CardInfoProps, - CardStatusProps, - CardHostProps, -} from './types'; +import { CardContextType, CardInfoProps, CardStatusProps } from './types'; import { twMerge } from 'tailwind-merge'; import { CardBoxProps, @@ -31,6 +26,7 @@ import { HostedClubCard, ParticipatedClubCard, DetailedClubCard, + CardHostInfo, } from './types/interface'; import ClubChip from '@/components/chip/club-chip/ClubChip'; import Button from '@/components/button/Button'; @@ -59,17 +55,24 @@ function CardImage({ isLiked, onLikeClick, className, + isPast, ...props }: CardImageProps) { return (
- {alt} + {alt} {isLiked !== undefined && (
@@ -154,6 +157,43 @@ function CardOverlay({ onDelete }: CardOverlayProps) { ); } +// Host 컴포넌트 (호스트 정보) +function CardHost({ + nickname, + avatar, + className, + isHost, + onClick, + ...props +}: CardHostInfo) { + return ( +
+
+
+ +
+
+ +
+
+ + + {isHost ? '나' : `${nickname}님`} + + 의 모임 + +
+ ); +} + function Card(props: CardProps) { const renderCardContent = () => { switch (props.variant) { @@ -183,6 +223,7 @@ function Card(props: CardProps) { url={imageUrl} alt={imageAlt} isLiked={isLiked} + isPast={isPast} onLikeClick={onLikeClick} /> @@ -387,8 +428,97 @@ function Card(props: CardProps) { status, participants, onClick, + isHost, + isParticipant, + hasWrittenReview, + onCancel, + onParticipate, + onCancelParticipation, + onWriteReview, } = props as DetailedClubCard & { variant: 'detailedClub' }; + const renderButton = () => { + if (isHost) { + return ( +
- {onClick && ( -
-
- )} + +
{renderButton()}
); @@ -488,12 +610,12 @@ Card.Title = CardTitle; Card.Location = CardLocation; Card.DateTime = CardDateTime; Card.Overlay = CardOverlay; +Card.Host = CardHost; /////////////////////////////////////////////////////////////////////// ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ Card.Info = CardInfo; Card.Status = CardStatus; -Card.Host = CardHost; // Info 컴포넌트 (모임에 관한 정보 - 제목, 위치, 날짜 등) function CardInfo({ @@ -561,37 +683,3 @@ function CardStatus({ } export default Card; - -// Host 컴포넌트 (호스트 정보) -function CardHost({ - nickname, - avatar, - className, - onClick, - ...props -}: CardHostProps) { - return ( -
-
-
- -
-
- -
-
- - {nickname}님 - 의 모임 - -
- ); -} diff --git a/src/components/card/ClubCard.stories.tsx b/src/components/card/ClubCard.stories.tsx index def5ecc8..60a7788d 100644 --- a/src/components/card/ClubCard.stories.tsx +++ b/src/components/card/ClubCard.stories.tsx @@ -82,6 +82,18 @@ const detailedArgs = { profileImage: 'https://picsum.photos/200/200?random=3', profileImageAlt: '참여자3 프로필 이미지', }, + { + id: '4', + name: '참여자4', + profileImage: 'https://picsum.photos/200/200?random=4', + profileImageAlt: '참여자4 프로필 이미지', + }, + { + id: '5', + name: '참여자5', + profileImage: 'https://picsum.photos/200/200?random=5', + profileImageAlt: '참여자5 프로필 이미지', + }, ], host: { id: 'host1', @@ -89,6 +101,12 @@ const detailedArgs = { profileImage: 'https://picsum.photos/200/200?random=host', }, isHost: false, + isParticipant: true, + hasWrittenReview: false, + onCancel: () => alert('모임 취소하기 클릭!'), + onParticipate: () => alert('모임 참여하기 클릭!'), + onCancelParticipation: () => alert('참여 취소하기 클릭!'), + onWriteReview: () => alert('리뷰 작성하기 클릭!'), } as const; // Default Club Card Stories diff --git a/src/components/card/types/components.ts b/src/components/card/types/components.ts index 93536406..6d88b86a 100644 --- a/src/components/card/types/components.ts +++ b/src/components/card/types/components.ts @@ -1,11 +1,6 @@ import { ComponentPropsWithoutRef, ReactNode } from 'react'; import { BaseProps } from './base'; -import { - MeetingInfo, - HostInfo, - ParticipationStatus, - ImageInfo, -} from './meeting'; +import { MeetingInfo, ParticipationStatus, ImageInfo } from './meeting'; export interface CardProps extends ComponentPropsWithoutRef<'article'>, @@ -28,10 +23,6 @@ export interface CardStatusProps ParticipationStatus, BaseProps {} -export interface CardHostProps - extends ComponentPropsWithoutRef<'div'>, - HostInfo {} - export interface CardImageProps extends ComponentPropsWithoutRef<'div'>, ImageInfo {} diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/interface/card.ts index b6b57d20..ed659bb2 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/interface/card.ts @@ -31,6 +31,7 @@ interface CardImageProps extends ComponentPropsWithoutRef<'div'> { url: string; alt?: string; isLiked?: boolean; + isPast?: boolean; onLikeClick?: () => void; } @@ -38,6 +39,16 @@ interface CardOverlayProps extends ComponentPropsWithoutRef<'div'> { onDelete?: () => void; } +interface CardHostInfo extends ComponentPropsWithoutRef<'div'> { + nickname: string; + onHostClick?: (e: React.MouseEvent) => void; + isHost: boolean; + avatar?: { + src?: string; + alt?: string; + }; +} + type CardProps = { variant?: 'defaultClub' | 'participatedClub' | 'hostedClub' | 'detailedClub'; } & ( @@ -54,5 +65,6 @@ export type { CardDateTimeProps, CardImageProps, CardOverlayProps, + CardHostInfo, CardProps, }; diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/interface/clubCard.ts index e64966e8..56691bb1 100644 --- a/src/components/card/types/interface/clubCard.ts +++ b/src/components/card/types/interface/clubCard.ts @@ -75,6 +75,18 @@ interface DetailedClubCard extends ClubCard { // 호스트 여부 isHost: boolean; + + // 해당 모임의 참여자인지 여부 + isParticipant: boolean; + + // 리뷰 작성 여부 + hasWrittenReview?: boolean; + + // 액션 핸들러 + onCancel?: () => void; + onParticipate?: () => void; + onCancelParticipation?: () => void; + onWriteReview?: () => void; } export type { diff --git a/src/components/card/types/meeting.ts b/src/components/card/types/meeting.ts index 4f80b06d..cc948d4f 100644 --- a/src/components/card/types/meeting.ts +++ b/src/components/card/types/meeting.ts @@ -5,15 +5,6 @@ export interface MeetingInfo { datetime: string; } -export interface HostInfo { - nickname: string; - onHostClick?: (e: React.MouseEvent) => void; - avatar?: { - src?: string; - alt?: string; - }; -} - export interface ParticipantInfo { src: string; alt: string; From fa23bd1734cde3e147fc06c0e8de93fb4bbb0ee5 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 16:09:36 +0900 Subject: [PATCH 20/24] =?UTF-8?q?=F0=9F=9A=9A[Rename]=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/ClubCard.stories.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/card/ClubCard.stories.tsx b/src/components/card/ClubCard.stories.tsx index 60a7788d..49c556f5 100644 --- a/src/components/card/ClubCard.stories.tsx +++ b/src/components/card/ClubCard.stories.tsx @@ -8,6 +8,16 @@ const meta = { parameters: { layout: 'centered', }, + argTypes: { + meetingType: { + control: 'select', + options: ['FREE', 'FIXED'], + }, + status: { + control: 'select', + options: ['completed', 'scheduled', 'pending', 'confirmed', 'closed'], + }, + }, tags: ['autodocs'], } satisfies Meta; @@ -21,9 +31,9 @@ const baseArgs = { title: '을지로 독서 모임', location: '을지로 3가', datetime: '12/14(토) 오전 10:00', - meetingType: 'FREE', + meetingType: 'FREE' as const, isPast: false, - status: 'confirmed', + status: 'confirmed' as const, onClick: () => alert('카드 클릭!'), } as const; From 7cd2a41c5b6ef6c0db165fea41caec21a5bef7fe Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 16:22:44 +0900 Subject: [PATCH 21/24] =?UTF-8?q?=F0=9F=9A=9A[Rename]=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/Card.test.tsx | 144 ------------------ src/components/card/Card.tsx | 76 +-------- src/components/card/ClubCard.stories.tsx | 8 +- src/components/card/TmpCard.stories.tsx | 36 ----- .../card/full-card/FullCard.stories.tsx | 60 -------- src/components/card/full-card/FullCard.tsx | 52 ------- src/components/card/mock/mock.ts | 68 --------- src/components/card/types/actions.ts | 8 - src/components/card/types/base.ts | 8 - .../card/types/{interface => }/card.ts | 7 +- .../card/types/{interface => }/clubCard.ts | 0 src/components/card/types/components.ts | 32 ---- src/components/card/types/index.ts | 7 +- src/components/card/types/interface/index.ts | 2 - src/components/card/types/meeting.ts | 26 ---- src/components/card/types/models.ts | 32 ---- 16 files changed, 14 insertions(+), 552 deletions(-) delete mode 100644 src/components/card/Card.test.tsx delete mode 100644 src/components/card/TmpCard.stories.tsx delete mode 100644 src/components/card/full-card/FullCard.stories.tsx delete mode 100644 src/components/card/full-card/FullCard.tsx delete mode 100644 src/components/card/mock/mock.ts delete mode 100644 src/components/card/types/actions.ts delete mode 100644 src/components/card/types/base.ts rename src/components/card/types/{interface => }/card.ts (93%) rename src/components/card/types/{interface => }/clubCard.ts (100%) delete mode 100644 src/components/card/types/components.ts delete mode 100644 src/components/card/types/interface/index.ts delete mode 100644 src/components/card/types/meeting.ts delete mode 100644 src/components/card/types/models.ts diff --git a/src/components/card/Card.test.tsx b/src/components/card/Card.test.tsx deleted file mode 100644 index 55ec033a..00000000 --- a/src/components/card/Card.test.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import Card from './Card'; -import '@testing-library/jest-dom'; - -describe('Card', () => { - describe('Card.Box', () => { - it('onClick 핸들러가 호출되어야 함', async () => { - const user = userEvent.setup(); - const handleClick = jest.fn(); - - render( - - 내용 - , - ); - - await user.click(screen.getByText('내용')); - expect(handleClick).toHaveBeenCalled(); - }); - }); - - describe('Card.Image', () => { - it('좋아요 버튼 클릭 시 onLikeClick이 호출되어야 함', async () => { - const user = userEvent.setup(); - const handleLikeClick = jest.fn(); - - render( - - - , - ); - - await user.click(screen.getByRole('button', { name: '좋아요' })); - expect(handleLikeClick).toHaveBeenCalled(); - }); - - it('좋아요 상태에 따라 적절한 aria-label이 표시되어야 함', () => { - const { rerender } = render( - - {}} /> - , - ); - - expect(screen.getByRole('button')).toHaveAttribute( - 'aria-label', - '좋아요', - ); - expect(screen.getByRole('button')).toHaveAttribute( - 'aria-pressed', - 'false', - ); - - rerender( - - {}} /> - , - ); - - expect(screen.getByRole('button')).toHaveAttribute( - 'aria-label', - '좋아요 취소', - ); - expect(screen.getByRole('button')).toHaveAttribute( - 'aria-pressed', - 'true', - ); - }); - }); - - describe('Card.Host', () => { - it('호스트 프로필 클릭 시 onClick이 호출되어야 함', async () => { - const user = userEvent.setup(); - const handleClick = jest.fn(); - - render( - - - , - ); - - await user.click(screen.getByRole('img')); - expect(handleClick).toHaveBeenCalled(); - }); - }); - - describe('Card.EndedOverlay', () => { - it('isCanceled가 true일 때만 오버레이가 표시되어야 함', () => { - const { rerender } = render( - - {}} /> - , - ); - - expect( - screen.queryByText(/호스트가 모임을 취소했어요/), - ).not.toBeInTheDocument(); - - rerender( - - {}} /> - , - ); - - expect( - screen.getByText(/호스트가 모임을 취소했어요/), - ).toBeInTheDocument(); - }); - - it('삭제하기 버튼 클릭 시 onDelete가 호출되어야 함', async () => { - const user = userEvent.setup(); - const handleDelete = jest.fn(); - - render( - - - , - ); - - await user.click(screen.getByText('삭제하기')); - expect(handleDelete).toHaveBeenCalled(); - }); - - it('삭제하기 버튼 클릭 시 이벤트 전파가 중단되어야 함', async () => { - const user = userEvent.setup(); - const handleDelete = jest.fn(); - const handleCardClick = jest.fn(); - - render( - - - , - ); - - await user.click(screen.getByText('삭제하기')); - expect(handleDelete).toHaveBeenCalled(); - expect(handleCardClick).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/src/components/card/Card.tsx b/src/components/card/Card.tsx index b80309da..867e1a34 100644 --- a/src/components/card/Card.tsx +++ b/src/components/card/Card.tsx @@ -1,8 +1,6 @@ import { createContext, useContext } from 'react'; -import Chip from '@/components/chip/Chip'; import ParticipantCounter from '../participant-counter/ParticipantCounter'; import AvatarGroup from '../avatar-group/AvatarGroup'; -import ConfirmedLabel from '../confirmed-label/ConfirmedLabel'; import ProgressBar from '../progress-bar/ProgressBar'; import Avatar from '../avatar/Avatar'; import { @@ -12,7 +10,6 @@ import { RatingIcon, } from '../../../public/icons'; import Image from 'next/image'; -import { CardContextType, CardInfoProps, CardStatusProps } from './types'; import { twMerge } from 'tailwind-merge'; import { CardBoxProps, @@ -27,7 +24,8 @@ import { ParticipatedClubCard, DetailedClubCard, CardHostInfo, -} from './types/interface'; + CardContextType, +} from './types'; import ClubChip from '@/components/chip/club-chip/ClubChip'; import Button from '@/components/button/Button'; @@ -612,74 +610,4 @@ Card.DateTime = CardDateTime; Card.Overlay = CardOverlay; Card.Host = CardHost; -/////////////////////////////////////////////////////////////////////// ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ - -Card.Info = CardInfo; -Card.Status = CardStatus; - -// Info 컴포넌트 (모임에 관한 정보 - 제목, 위치, 날짜 등) -function CardInfo({ - title, - category, - location, - datetime, - isPast = false, - className, - ...props -}: CardInfoProps) { - return ( -
-
-

{title}

- -
-
-
- - {location} -
- {datetime} -
-
- ); -} - -// Status 컴포넌트 (참가지 및 개설 여부 현황) -function CardStatus({ - currentParticipants, - maxParticipants, - isConfirmed = false, - confirmedVariant = 'confirmed', - isPast = false, - participants, - className, - ...props -}: CardStatusProps) { - return ( -
-
-
- - - {participants.map((participant, index) => ( - - ))} - -
- {isConfirmed && ( - - )} -
- -
- ); -} - export default Card; diff --git a/src/components/card/ClubCard.stories.tsx b/src/components/card/ClubCard.stories.tsx index 49c556f5..f5164924 100644 --- a/src/components/card/ClubCard.stories.tsx +++ b/src/components/card/ClubCard.stories.tsx @@ -119,7 +119,7 @@ const detailedArgs = { onWriteReview: () => alert('리뷰 작성하기 클릭!'), } as const; -// Default Club Card Stories +// 책모임, 찜 export const DefaultMobile: Story = { parameters: { viewport: { @@ -168,7 +168,7 @@ export const DefaultDesktop: Story = { args: defaultArgs, }; -// Hosted Club Card Stories +// 내가 만든 모임 export const HostedMobile: Story = { parameters: { viewport: { @@ -217,7 +217,7 @@ export const HostedDesktop: Story = { args: hostedArgs, }; -// Participated Club Card Stories +// 나의 모임 (내가 참여한 모임) export const ParticipatedMobile: Story = { parameters: { viewport: { @@ -266,7 +266,7 @@ export const ParticipatedDesktop: Story = { args: participatedArgs, }; -// Detailed Club Card Stories +// 상세 모임 export const DetailedMobile: Story = { parameters: { viewport: { diff --git a/src/components/card/TmpCard.stories.tsx b/src/components/card/TmpCard.stories.tsx deleted file mode 100644 index e64ca3cb..00000000 --- a/src/components/card/TmpCard.stories.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import Card from './Card'; -import { mockMeeting } from './mock/mock'; - -const meta = { - title: 'Components/TMPCard/Base', - component: Card, - parameters: { - layout: 'centered', - }, - argTypes: { - isCanceled: { - control: 'boolean', - description: '모임 취소 여부', - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - isCanceled: false, - }, - render: (args) => ( - - - - - - alert('삭제되었습니다')} /> - - - ), -}; diff --git a/src/components/card/full-card/FullCard.stories.tsx b/src/components/card/full-card/FullCard.stories.tsx deleted file mode 100644 index 06bf1df1..00000000 --- a/src/components/card/full-card/FullCard.stories.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import FullCard from './FullCard'; -import { mockFullMeeting } from '../mock/mock'; - -const meta = { - title: 'Components/TMPCard/FullCard', - component: FullCard, - parameters: { - layout: 'centered', - }, - argTypes: { - meeting: { - control: 'object', - description: - '모임 정보 (meetingInfo, participationStatus, imageInfo, hostInfo 등)', - }, - className: { - control: 'text', - description: '추가 스타일링을 위한 className', - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - meeting: mockFullMeeting, - }, - render: (args) => ( -
- -
- ), -}; - -export const Mobile: Story = { - ...Default, - parameters: { - viewport: { defaultViewport: 'mobile' }, - }, - render: (args) => ( -
- -
- ), -}; - -export const Desktop: Story = { - ...Default, - parameters: { - viewport: { defaultViewport: 'desktop' }, - }, - render: (args) => ( -
- -
- ), -}; diff --git a/src/components/card/full-card/FullCard.tsx b/src/components/card/full-card/FullCard.tsx deleted file mode 100644 index 63c6e43a..00000000 --- a/src/components/card/full-card/FullCard.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { ComponentPropsWithoutRef } from 'react'; -import Card from '../Card'; -import { FullMeeting } from '../types'; - -interface FullCardProps extends ComponentPropsWithoutRef<'article'> { - meeting: FullMeeting; -} - -function FullCard({ meeting, className, ...props }: FullCardProps) { - const { - meetingInfo, - participationStatus, - imageInfo, - hostInfo, - isPast = false, - isCanceled = false, - actions, - } = meeting; - - return ( - -
-
- -
-
-
- - - - - -
-
- -
-
-
-
- ); -} - -export default FullCard; diff --git a/src/components/card/mock/mock.ts b/src/components/card/mock/mock.ts deleted file mode 100644 index 5d2f913b..00000000 --- a/src/components/card/mock/mock.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Meeting, FullMeeting } from '../types'; - -export const mockMeeting: Meeting = { - meetingInfo: { - title: '을지로에서 만나는 독서 모임', - category: '자유책', - location: '을지로 3가', - datetime: '12/14(토) 오전 10:00', - }, - participationStatus: { - currentParticipants: 17, - maxParticipants: 20, - isConfirmed: true, - confirmedVariant: 'confirmed', - participants: [ - { src: 'https://picsum.photos/seed/1/200', alt: '참가자1' }, - { src: 'https://picsum.photos/seed/2/200', alt: '참가자2' }, - { src: 'https://picsum.photos/seed/3/200', alt: '참가자3' }, - ], - }, - imageInfo: { - url: 'https://picsum.photos/seed/bookclub/800/450', - isLiked: true, - onLikeClick: () => alert('좋아요를 눌렀습니다!'), - }, - isPast: false, - isCanceled: false, - actions: { - onClick: () => alert('카드를 클릭했습니다!'), - onDelete: () => alert('모임을 삭제했습니다!'), - }, -}; - -export const mockFullMeeting: FullMeeting = { - ...mockMeeting, - hostInfo: { - nickname: '호스트', - onHostClick: () => alert('호스트 프로필을 클릭했습니다!'), - // 프로필 이미지 정보 - // avatar: { - // src: 'https://picsum.photos/seed/host/200', - // alt: '호스트 프로필 이미지', - // }, - }, - actions: { - onJoinClick: () => alert('참여하기를 클릭했습니다!'), - }, -}; - -export const mockPastMeeting = { - ...mockMeeting, - isPast: true, -}; - -export const mockPastFullMeeting = { - ...mockFullMeeting, - isPast: true, -}; - -export const mockCanceledMeeting = { - ...mockMeeting, - isCanceled: true, -}; - -export const mockCanceledFullMeeting = { - ...mockFullMeeting, - isCanceled: true, -}; diff --git a/src/components/card/types/actions.ts b/src/components/card/types/actions.ts deleted file mode 100644 index 834f3cb4..00000000 --- a/src/components/card/types/actions.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface SimpleActions { - onClick: () => void; - onDelete: () => void; -} - -export interface FullActions { - onJoinClick?: () => void; -} diff --git a/src/components/card/types/base.ts b/src/components/card/types/base.ts deleted file mode 100644 index 8f4db34a..00000000 --- a/src/components/card/types/base.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface BaseProps { - isPast?: boolean; - isCanceled?: boolean; -} - -export interface CardContextType { - isCanceled: boolean; -} diff --git a/src/components/card/types/interface/card.ts b/src/components/card/types/card.ts similarity index 93% rename from src/components/card/types/interface/card.ts rename to src/components/card/types/card.ts index ed659bb2..710fd308 100644 --- a/src/components/card/types/interface/card.ts +++ b/src/components/card/types/card.ts @@ -3,7 +3,7 @@ import { DetailedClubCard, HostedClubCard, ParticipatedClubCard, -} from '@/components/card/types/interface/clubCard'; +} from '@/components/card/types/clubCard'; import { ComponentPropsWithoutRef } from 'react'; interface CardBoxProps extends ComponentPropsWithoutRef<'div'> { @@ -49,6 +49,10 @@ interface CardHostInfo extends ComponentPropsWithoutRef<'div'> { }; } +interface CardContextType { + isCanceled: boolean; +} + type CardProps = { variant?: 'defaultClub' | 'participatedClub' | 'hostedClub' | 'detailedClub'; } & ( @@ -66,5 +70,6 @@ export type { CardImageProps, CardOverlayProps, CardHostInfo, + CardContextType, CardProps, }; diff --git a/src/components/card/types/interface/clubCard.ts b/src/components/card/types/clubCard.ts similarity index 100% rename from src/components/card/types/interface/clubCard.ts rename to src/components/card/types/clubCard.ts diff --git a/src/components/card/types/components.ts b/src/components/card/types/components.ts deleted file mode 100644 index 6d88b86a..00000000 --- a/src/components/card/types/components.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { ComponentPropsWithoutRef, ReactNode } from 'react'; -import { BaseProps } from './base'; -import { MeetingInfo, ParticipationStatus, ImageInfo } from './meeting'; - -export interface CardProps - extends ComponentPropsWithoutRef<'article'>, - BaseProps { - children?: ReactNode; -} - -export interface CardBoxProps extends ComponentPropsWithoutRef<'div'> { - children: ReactNode; - onClick?: () => void; -} - -export interface CardInfoProps - extends Omit, keyof MeetingInfo>, - MeetingInfo, - BaseProps {} - -export interface CardStatusProps - extends ComponentPropsWithoutRef<'div'>, - ParticipationStatus, - BaseProps {} - -export interface CardImageProps - extends ComponentPropsWithoutRef<'div'>, - ImageInfo {} - -export interface CardEndedOverlayProps extends ComponentPropsWithoutRef<'div'> { - onDelete?: () => void; -} diff --git a/src/components/card/types/index.ts b/src/components/card/types/index.ts index d34a7b38..cf51da62 100644 --- a/src/components/card/types/index.ts +++ b/src/components/card/types/index.ts @@ -1,5 +1,2 @@ -export * from './base'; -export * from './meeting'; -export * from './actions'; -export * from './components'; -export * from './models'; +export * from './card'; +export * from './clubCard'; diff --git a/src/components/card/types/interface/index.ts b/src/components/card/types/interface/index.ts deleted file mode 100644 index cf51da62..00000000 --- a/src/components/card/types/interface/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './card'; -export * from './clubCard'; diff --git a/src/components/card/types/meeting.ts b/src/components/card/types/meeting.ts deleted file mode 100644 index cc948d4f..00000000 --- a/src/components/card/types/meeting.ts +++ /dev/null @@ -1,26 +0,0 @@ -export interface MeetingInfo { - title: string; - category: string; - location: string; - datetime: string; -} - -export interface ParticipantInfo { - src: string; - alt: string; -} - -export interface ParticipationStatus { - currentParticipants: number; - maxParticipants: number; - isConfirmed: boolean; - confirmedVariant: 'confirmed' | 'closed'; - participants: ParticipantInfo[]; -} - -export interface ImageInfo { - url: string; - alt?: string; - isLiked: boolean; - onLikeClick: () => void; -} diff --git a/src/components/card/types/models.ts b/src/components/card/types/models.ts deleted file mode 100644 index f44ca4d6..00000000 --- a/src/components/card/types/models.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { BaseProps } from './base'; -import { - MeetingInfo, - ParticipationStatus, - ImageInfo, - HostInfo, -} from './meeting'; -import { SimpleActions, FullActions } from './actions'; - -export interface Meeting extends BaseProps { - meetingInfo: MeetingInfo; - participationStatus: ParticipationStatus; - imageInfo: ImageInfo; - actions: SimpleActions; -} - -export interface FullMeeting extends Omit { - hostInfo: HostInfo; - actions: FullActions; -} - -export interface ClubStatus { - isCompleted: boolean; - isConfirmed: boolean; -} - -export interface ClubMeeting extends BaseProps { - meetingInfo: MeetingInfo; - imageInfo: ImageInfo; - clubStatus: ClubStatus; - actions: SimpleActions; -} From f6a95423c0f1a8551700bc87334d69d7dd8b4d62 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 16:28:49 +0900 Subject: [PATCH 22/24] =?UTF-8?q?=F0=9F=92=AC[Comment]=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/types/clubCard.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/card/types/clubCard.ts b/src/components/card/types/clubCard.ts index 56691bb1..5298db4b 100644 --- a/src/components/card/types/clubCard.ts +++ b/src/components/card/types/clubCard.ts @@ -83,10 +83,10 @@ interface DetailedClubCard extends ClubCard { hasWrittenReview?: boolean; // 액션 핸들러 - onCancel?: () => void; - onParticipate?: () => void; - onCancelParticipation?: () => void; - onWriteReview?: () => void; + onCancel?: () => void; // 모임 취소 + onParticipate?: () => void; // 참여 + onCancelParticipation?: () => void; // 참여 취소 + onWriteReview?: () => void; // 리뷰 작성 } export type { From e758cf13558aa7b56ee93279be6ca5572934e6a1 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 16:42:02 +0900 Subject: [PATCH 23/24] =?UTF-8?q?button=20=EC=A4=91=EB=B3=B5=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/Button.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx index 746bd5ac..4a1b845d 100644 --- a/src/components/button/Button.tsx +++ b/src/components/button/Button.tsx @@ -19,7 +19,6 @@ export default function Button({ themeColor = 'green-normal-01', lightColor, isSubmitting, - className, ...buttonProps }: ButtonProps) { const { disabled, className } = buttonProps; From 80b51ca7a0a0a2aae29e392b3233c3c554a1e2a9 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Fri, 13 Dec 2024 17:13:14 +0900 Subject: [PATCH 24/24] =?UTF-8?q?=F0=9F=93=9D[Docs]=20=EC=9E=84=EC=8B=9C?= =?UTF-8?q?=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/bookclub/components/Main.tsx | 89 ++++++++++++----------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/features/bookclub/components/Main.tsx b/src/features/bookclub/components/Main.tsx index 6408c5e0..a09ef2e5 100644 --- a/src/features/bookclub/components/Main.tsx +++ b/src/features/bookclub/components/Main.tsx @@ -1,53 +1,54 @@ -import SimpleCard from '@/components/card/simple-card/SimpleCard'; -import { Meeting } from '@/components/card/types'; +// import SimpleCard from '@/components/card/simple-card/SimpleCard'; +// import { Meeting } from '@/components/card/types'; -const mockImgSrc = '/images/profile.png'; +// const mockImgSrc = '/images/profile.png'; -const mockMeeting: Meeting = { - meetingInfo: { - title: '을지로에서 만나는 독서 모임', - category: '자유책', - location: '을지로 3가', - datetime: '12/14(토) 오전 10:00', - }, - participationStatus: { - currentParticipants: 17, - maxParticipants: 20, - isConfirmed: true, - confirmedVariant: 'confirmed', - participants: [ - { - src: mockImgSrc, - alt: '참가자1', - }, - { - src: mockImgSrc, - alt: '참가자2', - }, - { - src: mockImgSrc, - alt: '참가자3', - }, - ], - }, - imageInfo: { - url: mockImgSrc, - isLiked: true, - onLikeClick: () => alert('좋아요를 눌렀습니다!'), - }, - isPast: false, - isCanceled: false, - actions: { - onClick: () => alert('카드를 클릭했습니다!'), - onDelete: () => alert('모임을 삭제했습니다!'), - }, -}; +// const mockMeeting: Meeting = { +// meetingInfo: { +// title: '을지로에서 만나는 독서 모임', +// category: '자유책', +// location: '을지로 3가', +// datetime: '12/14(토) 오전 10:00', +// }, +// participationStatus: { +// currentParticipants: 17, +// maxParticipants: 20, +// isConfirmed: true, +// confirmedVariant: 'confirmed', +// participants: [ +// { +// src: mockImgSrc, +// alt: '참가자1', +// }, +// { +// src: mockImgSrc, +// alt: '참가자2', +// }, +// { +// src: mockImgSrc, +// alt: '참가자3', +// }, +// ], +// }, +// imageInfo: { +// url: mockImgSrc, +// isLiked: true, +// onLikeClick: () => alert('좋아요를 눌렀습니다!'), +// }, +// isPast: false, +// isCanceled: false, +// actions: { +// onClick: () => alert('카드를 클릭했습니다!'), +// onDelete: () => alert('모임을 삭제했습니다!'), +// }, +// }; function Main() { return (
- - + {/* + */} +
hi
); }