Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/app/schedule/(components)/current.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client';

import { useRouter } from 'next/navigation';

import Card from '@/components/shared/card';

const MOCK_MEETINGS = [
{
id: 1,
title: '네즈코와 무한성에서 정모 하실 분',
images: [],
location: '네즈코 호수공원',
dateTime: '25. 11. 28 - 10:00',
nickName: 'Hope Lee',
participantCount: 8,
maxParticipants: 10,
tags: ['#태그', '#태그', '#태그'],
},
];

export default function Current() {
const router = useRouter();

return (
<section className='flex w-full flex-col gap-4 px-4 py-4'>
{MOCK_MEETINGS.map((meeting) => (
<Card
key={meeting.id}
dateTime={meeting.dateTime}
images={meeting.images}
leaveAndChatActions={{
onLeave: () => console.log('모임 탈퇴', meeting.id),
onChat: () => router.push(`/chat/${meeting.id}`),
}}
location={meeting.location}
maxParticipants={meeting.maxParticipants}
nickName={meeting.nickName}
participantCount={meeting.participantCount}
tags={meeting.tags}
title={meeting.title}
onClick={() => router.push(`/meetup/${meeting.id}`)}
/>
))}
</section>
);
}
46 changes: 46 additions & 0 deletions src/app/schedule/(components)/history.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client';

import { useRouter } from 'next/navigation';

import Card from '@/components/shared/card';

const MOCK_MEETINGS = [
{
id: 3,
title: '동네 책모임 신규 멤버 구함',
images: [],
location: '망원동 카페 거리',
dateTime: '25. 12. 10 - 19:30',
nickName: 'Book Lover',
participantCount: 3,
maxParticipants: 8,
tags: ['#책모임', '#수다환영'],
},
];

export default function History() {
const router = useRouter();

return (
<section className='flex w-full flex-col gap-4 px-4 py-4'>
{MOCK_MEETINGS.map((meeting) => (
<Card
key={meeting.id}
dateTime={meeting.dateTime}
images={meeting.images}
leaveAndChatActions={{
onLeave: () => console.log('모임 탈퇴', meeting.id),
onChat: () => router.push(`/chat/${meeting.id}`),
}}
location={meeting.location}
maxParticipants={meeting.maxParticipants}
nickName={meeting.nickName}
participantCount={meeting.participantCount}
tags={meeting.tags}
title={meeting.title}
onClick={() => router.push(`/meetup/${meeting.id}`)}
/>
))}
</section>
);
}
46 changes: 46 additions & 0 deletions src/app/schedule/(components)/my.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client';

import { useRouter } from 'next/navigation';

import Card from '@/components/shared/card';

const MOCK_MEETINGS = [
{
id: 2,
title: '주말 러닝 크루 모집',
images: [],
location: '한강공원 잠실지구',
dateTime: '25. 12. 05 - 07:30',
nickName: 'Minseo Kim',
participantCount: 5,
maxParticipants: 12,
tags: ['#러닝', '#아침운동'],
},
];

export default function My() {
const router = useRouter();

return (
<section className='flex w-full flex-col gap-4 px-4 py-4'>
{MOCK_MEETINGS.map((meeting) => (
<Card
key={meeting.id}
dateTime={meeting.dateTime}
images={meeting.images}
leaveAndChatActions={{
onLeave: () => console.log('모임 탈퇴', meeting.id),
onChat: () => router.push(`/chat/${meeting.id}`),
}}
location={meeting.location}
maxParticipants={meeting.maxParticipants}
nickName={meeting.nickName}
participantCount={meeting.participantCount}
tags={meeting.tags}
title={meeting.title}
onClick={() => router.push(`/meetup/${meeting.id}`)}
/>
))}
</section>
);
}
18 changes: 0 additions & 18 deletions src/app/schedule/current/page.tsx

This file was deleted.

18 changes: 0 additions & 18 deletions src/app/schedule/history/page.tsx

This file was deleted.

18 changes: 0 additions & 18 deletions src/app/schedule/my/page.tsx

This file was deleted.

29 changes: 27 additions & 2 deletions src/app/schedule/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
import { redirect } from 'next/navigation';
'use client';

import { useSearchParams } from 'next/navigation';

import { TabNavigation } from '@/components/shared';

import Current from './(components)/current';
import History from './(components)/history';
import My from './(components)/my';

const SCHEDULE_TABS = [
{ label: '현재 모임', value: 'current' },
{ label: '나의 모임', value: 'my' },
{ label: '모임 이력', value: 'history' },
];

export default function SchedulePage() {
redirect('/schedule/current');
const searchParams = useSearchParams();
const tab = searchParams.get('tab') || 'current';

return (
<div className='min-h-screen bg-[#F1F5F9]'>
<TabNavigation basePath='/schedule' tabs={SCHEDULE_TABS} />

{tab === 'current' && <Current />}
{tab === 'my' && <My />}
{tab === 'history' && <History />}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const CardParticipationRow = ({
progress,
}: CardParticipationRowProps) => {
return (
<div className='mt-[14px] flex items-center'>
<div className='mt-3 flex h-[18px] items-center'>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위 훈수

Suggested change
<div className='mt-3 flex h-[18px] items-center'>
<div className='mt-3 flex h-4.5 items-center'>

아래 훈수

<div className='flex min-w-0 flex-1 items-center gap-1.5'>
<Icon id='users' width={12} className='shrink-0 text-gray-600' height={12} />
<div className='h-[6px] w-[200px] overflow-hidden rounded-full bg-gray-200 sm:w-[210px]'>
Expand Down
74 changes: 61 additions & 13 deletions src/components/shared/card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Image from 'next/image';

import { useState } from 'react';

import { Button } from '@/components/ui';
import { cn } from '@/lib/utils';

import { CardInfoRow } from './card-info-row';
Expand All @@ -22,6 +23,22 @@ type CardProps = {
maxParticipants: number;
profileImage?: string | null;
onClick?: () => void;
leaveAndChatActions?: {
onLeave: () => void;
onChat: () => void;
};
};

const PROFILE_IMAGE_SIZE = 16;

const calculateProgress = (count: number, max: number): number => {
const safeMax = max > 0 ? max : 1;
const rawProgress = (count / safeMax) * 100;
return Math.min(100, Math.max(0, rawProgress));
};

const convertToCardTags = (tags: string[]): CardTag[] => {
return tags.map((tag, index) => ({ id: index, label: tag }));
};

const Card = ({
Expand All @@ -35,21 +52,21 @@ const Card = ({
maxParticipants,
profileImage,
onClick,
leaveAndChatActions,
}: CardProps) => {
const safeMaxCount = maxParticipants > 0 ? maxParticipants : 1;
const rawProgress = (participantCount / safeMaxCount) * 100;
const progress = Math.min(100, Math.max(0, rawProgress));
const [imageError, setImageError] = useState(false);

const Wrapper: 'button' | 'div' = onClick ? 'button' : 'div';
const thumbnail = images?.[0];
const hasThumbnail = !!thumbnail && !imageError;
const cardTags: CardTag[] = tags.map((tag, index) => ({ id: index, label: tag }));
const cardTags = convertToCardTags(tags);
const progress = calculateProgress(participantCount, maxParticipants);

return (
<Wrapper
className={cn('bg-mono-white flex h-[162px] w-full rounded-3xl p-4 text-left shadow-sm')}
type={onClick ? 'button' : undefined}
<div
className={cn(
'bg-mono-white flex w-full rounded-3xl p-4 shadow-sm',
onClick && 'cursor-pointer',
)}
onClick={onClick}
>
<div className='flex min-w-0 gap-4'>
Expand All @@ -64,25 +81,42 @@ const Card = ({
<div className='mt-3 flex items-center gap-1.5'>
{profileImage ? (
<Image
width={16}
width={PROFILE_IMAGE_SIZE}
className='rounded-full object-cover'
alt={nickName}
height={16}
height={PROFILE_IMAGE_SIZE}
src={profileImage}
/>
) : (
<div className='h-4 w-4 rounded-full bg-gray-600' />
<div
className='rounded-full bg-gray-600'
style={{ width: PROFILE_IMAGE_SIZE, height: PROFILE_IMAGE_SIZE }}
/>
)}
<span className='text-text-xs-medium text-gray-900'>{nickName}</span>
</div>

{leaveAndChatActions && (
<Button
className='mt-3'
size='xs'
variant='leave'
onClick={(e) => {
e.stopPropagation();
leaveAndChatActions.onLeave();
}}
>
모임 탈퇴
</Button>
)}
</div>

<div className='flex min-w-0 flex-1 flex-col'>
<h3 className='text-text-md-semibold w-full truncate text-gray-900'>{title}</h3>

<CardTags tags={cardTags} />

<div className='mt-3 flex flex-col gap-1'>
<div className='mt-[13px] flex flex-col gap-1'>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

훈수

Suggested change
<div className='mt-[13px] flex flex-col gap-1'>
<div className='mt-3.25 flex flex-col gap-1'>

<CardInfoRow iconId='map-pin' label={location} />
<CardInfoRow iconId='calendar' label={dateTime} />
</div>
Expand All @@ -92,9 +126,23 @@ const Card = ({
participantCount={participantCount}
progress={progress}
/>

{leaveAndChatActions && (
<Button
className='mt-3'
size='xs'
variant='chat'
onClick={(e) => {
e.stopPropagation();
leaveAndChatActions.onChat();
}}
>
채팅 입장
</Button>
)}
</div>
</div>
</Wrapper>
</div>
);
};

Expand Down
Loading
Loading