Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { useInfiniteQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { BellIcon, Button, DotIcon, NotificationCard, Popover, useToast } from '@what-today/design-system';
import {
BellIcon,
Button,
DotIcon,
NotificationCard,
NotificationCardSkeleton,
Popover,
useToast,
} from '@what-today/design-system';
import type { AxiosError } from 'axios';
import { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
Expand Down Expand Up @@ -129,10 +137,17 @@ export default function NotificationPopover({ isMobile }: NotificationPopoverPro
</Button>
</Popover.Trigger>
<Popover.Content className='mt-8 rounded-2xl border border-gray-100 bg-white p-10 shadow-sm'>
<h1 className='my-8 ml-auto px-16 font-bold text-gray-950'>알림 {data?.pages[0].totalCount}개</h1>
<h1 className='my-8 ml-auto px-16 font-bold text-gray-950'>알림 {data?.pages[0].totalCount ?? 0}개</h1>

<div ref={scrollContainerRef} className='relative max-h-400 w-300 overflow-y-scroll'>
{isLoading && <p className='text-md my-70 text-center text-gray-400'>Loading...</p>}
<section ref={scrollContainerRef} className='relative max-h-400 w-300 overflow-y-scroll'>
{isLoading && (
<>
<NotificationCardSkeleton />
<NotificationCardSkeleton />
<NotificationCardSkeleton />
<NotificationCardSkeleton />
</>
)}
<div className='divide-y divide-gray-50'>
{data?.pages.map((page) =>
page.notifications.map((notification) => (
Expand All @@ -152,7 +167,8 @@ export default function NotificationPopover({ isMobile }: NotificationPopoverPro
<p className='text-md my-70 text-center text-gray-400'>알림이 없습니다.</p>
)}
<div ref={observerRef} className='h-6 w-full' /> {/* 무한 스크롤 감지용 */}
</div>
{isFetchingNextPage && <NotificationCardSkeleton />}
</section>
</Popover.Content>
</Popover.Root>
);
Expand Down
47 changes: 30 additions & 17 deletions packages/design-system/src/components/NotificationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,16 @@ const parseNotificationContent = (content: string): ParsedNotification | null =>
export default function NotificationCard({ content, onDelete, onClickDetail }: NotificationCardProps) {
const parsedNotification = parseNotificationContent(content);

const handleClickStop = (e: React.MouseEvent) => {
e.stopPropagation();
};

// parsedNotification 실패시 기본 content를 메시지로 보여줍니다.
if (!parsedNotification) {
return (
<div className='text-md flex flex-col gap-4 p-16'>
<div className='flex items-center justify-between'>
<h1 className='text-md font-semibold text-gray-900 md:text-lg'>알림</h1>
<h1 className='text-md font-semibold md:text-lg'>알림</h1>
<Button className='h-fit w-fit p-0' variant='none' onClick={onDelete}>
<DeleteIcon className='size-10' color='var(--color-gray-300)' />
</Button>
Expand All @@ -78,29 +82,38 @@ export default function NotificationCard({ content, onDelete, onClickDetail }: N
const { title, date, confirm } = parsedNotification;

return (
<div className='text-md flex flex-col gap-8 p-16'>
<div
className='text-md flex cursor-pointer flex-col gap-4 p-16 md:gap-8'
onClick={(e) => {
handleClickStop(e);
onClickDetail();
}}
>
<div className='flex items-center justify-between'>
<div className='flex gap-8'>
{confirm ? <UserBadge status='confirmed' /> : <UserBadge status='declined' />}
<h1 className='text-md font-semibold text-gray-900 md:text-lg'>
{confirm ? '예약이 승인되었어요!' : '예약이 거절되었어요.'}
</h1>
<h1 className='body-text font-semibold'>{confirm ? '예약이 승인되었어요!' : '예약이 거절되었어요.'}</h1>
</div>
<Button className='h-fit w-fit p-0' variant='none' onClick={onDelete}>
<Button
className='h-fit w-fit p-0'
Copy link
Member

Choose a reason for hiding this comment

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

size='none' 사용하시면 className='h-fit w-fit p-0' 대체 됩니다! 혹시나 위에 95번째줄 라인 반영하시면 같이 수정 부탁드려요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

className을 빼면 사진과 같이 스타일이 깨져서 이 부분은 그대로 두었습니다..!
image

variant='none'
onClick={(e) => {
handleClickStop(e);
onDelete();
}}
>
<DeleteIcon className='size-10' color='var(--color-gray-300)' />
</Button>
</div>
<div className='cursor-pointer' onClick={onClickDetail}>
<div className='flex items-center gap-4 text-gray-600'>
<DocumentIcon className='size-14' />
<p>{title}</p>
</div>
<div className='flex items-center gap-4 text-gray-600'>
<ClockIcon className='size-14' />
<p>
{date.year}년 {date.month}월 {date.day}일 {date.startTime}~{date.endTime}
</p>
</div>
<div className='caption-text flex items-center gap-4 text-gray-400'>
<DocumentIcon className='size-14' />
<p>{title}</p>
</div>
<div className='caption-text flex items-center gap-4 text-gray-400'>
<ClockIcon className='size-14' />
<p>
{date.year}년 {date.month}월 {date.day}일 {date.startTime}~{date.endTime}
</p>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export default function NotificationCardSkeleton() {
return (
<div className='text-md flex animate-pulse cursor-pointer flex-col gap-4 p-16 md:gap-8'>
{/* 상단: 상태 뱃지 + 문구 + 삭제 버튼 */}
<div className='flex items-center justify-between'>
<div className='flex items-center gap-8'>
<div className='h-26 w-60 rounded-full bg-gray-100' />
<div className='my-2 h-22 w-100 rounded bg-gray-100' />
</div>
<div className='size-22 animate-pulse rounded-full bg-gray-100' />
</div>

{/* 제목 (아이콘 + 텍스트) */}
<div className='my-2 flex items-center gap-4'>
<div className='size-16 rounded-full bg-gray-100' />
<div className='h-20 w-full max-w-[200px] rounded bg-gray-100' />
</div>

{/* 날짜 및 시간 (아이콘 + 텍스트) */}
<div className='my-2 flex items-center gap-4'>
<div className='size-16 rounded-full bg-gray-100' />
<div className='h-20 w-full max-w-[170px] rounded bg-gray-100' />
</div>
</div>
);
}
1 change: 1 addition & 0 deletions packages/design-system/src/components/skeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as ExperienceCardSkeleton } from './ExperienceCardSkeleton';
export { default as NotificationCardSkeleton } from './NotificationCardSkeleton';
2 changes: 2 additions & 0 deletions packages/design-system/src/pages/NotificationCardDoc.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useNavigate } from 'react-router-dom';

import { NotificationCardSkeleton } from '@/components';
import Playground from '@/layouts/Playground';

import NotificationCard from '../components/NotificationCard';
Expand Down Expand Up @@ -55,6 +56,7 @@ Header의 알림창 팝오버에서 보여지며, 알림은 개별 삭제 가능
onClickDetail={() => navigate('/mypage/reservations-list')}
onDelete={() => alert('삭제 API 요청')}
/>
<NotificationCardSkeleton />
<NotificationCard
content='메시지 파싱에 실패하면 이렇게 보여집니다.'
onClickDetail={() => navigate('/mypage/reservations-list')}
Expand Down