From 8b1026b5b1e6bb6bb4cbdb49a607257f20711a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=9A=B0=EC=B0=AC?= Date: Mon, 9 Sep 2024 00:35:43 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=9D=BD=EC=9D=8C=20api?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99=20=EC=B6=94=EA=B0=80=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: mainListTap 하단 border set Width as screen * feat: header height값 수정 * feat: Digest탭의 빈 페이지 구현 * feat: 메일 읽기 api 연동 추가 * feat: 메일 삭제 UI 및 API 연결 --------- Co-authored-by: moonki --- src/api/hooks/useMailReadMutation.ts | 27 +++++++++++++++ src/app/article/[id]/ArticleFooter.tsx | 33 +++++++++++++++++++ src/app/article/[id]/page.tsx | 13 +++++--- src/app/main/DigestTab/index.tsx | 21 ++++++++++-- src/app/main/layout.tsx | 2 +- src/assets/icons/DeleteIcon.svg | 9 +++++ src/assets/icons/ListIcon.svg | 5 +++ src/components/Article/ArticleCard.tsx | 12 +------ src/components/EmptyMailView.tsx | 30 +++++++++++++++++ src/components/Header/MainPageHeader.tsx | 4 +-- src/components/ListTap/MainListTap.tsx | 2 +- .../Domain/DomainInteraction.tsx | 1 + src/mocks/handlers.ts | 8 +++++ 13 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 src/api/hooks/useMailReadMutation.ts create mode 100644 src/app/article/[id]/ArticleFooter.tsx create mode 100644 src/assets/icons/DeleteIcon.svg create mode 100644 src/assets/icons/ListIcon.svg create mode 100644 src/components/EmptyMailView.tsx diff --git a/src/api/hooks/useMailReadMutation.ts b/src/api/hooks/useMailReadMutation.ts new file mode 100644 index 0000000..cc8a356 --- /dev/null +++ b/src/api/hooks/useMailReadMutation.ts @@ -0,0 +1,27 @@ +import { useMutation } from '@tanstack/react-query'; +import { axiosInstance } from '@/api/axiosInstance'; +import { AxiosError, AxiosResponse } from 'axios'; + +interface patchParam { + mailId: string; +} + +export const patchFn = ({ mailId }: patchParam) => { + return axiosInstance.patch(`/inbox/mails/${mailId}`, { action: 'read' }); +}; + +export const deleteFn = ({ mailId }: patchParam) => { + return axiosInstance.delete(`/inbox/mails/${mailId}`); +}; + +export const useMailReadMutation = () => { + return useMutation({ + mutationFn: patchFn, + }); +}; + +export const useMailDeleteMutation = () => { + return useMutation({ + mutationFn: deleteFn, + }); +}; diff --git a/src/app/article/[id]/ArticleFooter.tsx b/src/app/article/[id]/ArticleFooter.tsx new file mode 100644 index 0000000..d881b29 --- /dev/null +++ b/src/app/article/[id]/ArticleFooter.tsx @@ -0,0 +1,33 @@ +import { useMailDeleteMutation } from '@/api/hooks/useMailReadMutation'; +import DeleteIcon from '@/assets/icons/DeleteIcon.svg'; +import ListIcon from '@/assets/icons/ListIcon.svg'; +import Image from 'next/image'; + +interface ArticleFooterProps { + mailId: string; +} + +const ArticleFooter = ({ mailId }: ArticleFooterProps) => { + const deleteMutation = useMailDeleteMutation(); + + const handleDeleteMail = (mailId: string) => { + deleteMutation.mutate({ mailId }); + }; + return ( +
+
+ handleDeleteMail(mailId)} + > + delete + + + list + +
+
+ ); +}; + +export default ArticleFooter; diff --git a/src/app/article/[id]/page.tsx b/src/app/article/[id]/page.tsx index 7a2df64..d2a9cda 100644 --- a/src/app/article/[id]/page.tsx +++ b/src/app/article/[id]/page.tsx @@ -4,19 +4,24 @@ import type { pageProps } from '@/types/page'; import ArticleContent from '@/app/article/[id]/ArticleContent'; import MainPageHeader from '@/components/Header/MainPageHeader'; import { useMailByIdQuery } from '@/api/hooks/useFetchMailQuery'; +import ArticleFooter from './ArticleFooter'; -interface ArticlePageProps extends pageProps { +interface ArticlePageProps { params: { id: string }; } -const ArticlePage = ({ searchParams, params }: ArticlePageProps) => { +const ArticlePage = ({ params }: ArticlePageProps) => { const { data } = useMailByIdQuery(params.id); return (
- {/* TODO: 개별 mail 받아와서 데이터 넘겨줘야함~~ */} - {data && } + {data && ( + <> + + + + )}
); }; diff --git a/src/app/main/DigestTab/index.tsx b/src/app/main/DigestTab/index.tsx index 44c6c14..b85d949 100644 --- a/src/app/main/DigestTab/index.tsx +++ b/src/app/main/DigestTab/index.tsx @@ -6,17 +6,34 @@ import { useEffect, useState } from 'react'; import ArticleCard from '@/components/Article/ArticleCard'; import RecommendArea from '../TodayTab/RecommendArea'; import { useUnreadQuery } from '@/api/hooks/useFetchMailQuery'; +import EmptyMailView from '@/components/EmptyMailView'; +import { useMailReadMutation } from '@/api/hooks/useMailReadMutation'; const DigestTab = () => { const [selectedTab, setSelectedTab] = useState<'all' | 'unread'>('unread'); const { data, isFetched } = useUnreadQuery({}); + const readMutation = useMailReadMutation(); + + const handleReadMail = (mailId: string) => { + readMutation.mutate({ mailId }); + }; return isFetched ? (
-
+
- {data?.map(article => )} + {data?.length === 0 ? ( +
+ +
+ ) : ( + data?.map(article => ( +
handleReadMail(article.mailId)}> + +
+ )) + )}
diff --git a/src/app/main/layout.tsx b/src/app/main/layout.tsx index 9e62813..3608ea9 100644 --- a/src/app/main/layout.tsx +++ b/src/app/main/layout.tsx @@ -8,7 +8,7 @@ export default function MainPageLayout({ children: React.ReactNode; }>) { return ( -
+
}> diff --git a/src/assets/icons/DeleteIcon.svg b/src/assets/icons/DeleteIcon.svg new file mode 100644 index 0000000..11b8897 --- /dev/null +++ b/src/assets/icons/DeleteIcon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icons/ListIcon.svg b/src/assets/icons/ListIcon.svg new file mode 100644 index 0000000..1ed0002 --- /dev/null +++ b/src/assets/icons/ListIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/Article/ArticleCard.tsx b/src/components/Article/ArticleCard.tsx index ea96b2f..41d3cf2 100644 --- a/src/components/Article/ArticleCard.tsx +++ b/src/components/Article/ArticleCard.tsx @@ -9,17 +9,7 @@ interface ArticleCardProps extends MailDataType { currentTab?: string; } -const ArticleCard = ({ - subject, - mailId, - snippet, - date, - // thumbnail, - isRead, - payload, - from, - isToday = false, -}: ArticleCardProps) => { +const ArticleCard = ({ subject, mailId, snippet, date, isRead, payload, from, isToday = false }: ArticleCardProps) => { return ( { + return ( +
+
+ EmptyView +
+ 뉴스레터를 모두 읽었어요 + + 더 많은 뉴스레터를 읽고 싶다면 + + 탐색 탭으로 이동 + + +
+
+
+ ); +}; + +export default EmptyMailView; diff --git a/src/components/Header/MainPageHeader.tsx b/src/components/Header/MainPageHeader.tsx index 139ff74..d20445d 100644 --- a/src/components/Header/MainPageHeader.tsx +++ b/src/components/Header/MainPageHeader.tsx @@ -6,12 +6,12 @@ import Image from 'next/image'; const MainPageHeader = () => { return ( -
+
ServiceIcon - + 마이 페이지 diff --git a/src/components/ListTap/MainListTap.tsx b/src/components/ListTap/MainListTap.tsx index f64d316..1410bd7 100644 --- a/src/components/ListTap/MainListTap.tsx +++ b/src/components/ListTap/MainListTap.tsx @@ -25,7 +25,7 @@ const MainListTap = () => { }, [searchParams]); return ( -
+
handleClickListItem('오늘의 인사이트')} diff --git a/src/components/PageInteraction/Domain/DomainInteraction.tsx b/src/components/PageInteraction/Domain/DomainInteraction.tsx index bb5691e..a5d202c 100644 --- a/src/components/PageInteraction/Domain/DomainInteraction.tsx +++ b/src/components/PageInteraction/Domain/DomainInteraction.tsx @@ -11,6 +11,7 @@ interface DomainInteractionAreaProps { const DomainInteractionArea = ({ domainArticleData }: DomainInteractionAreaProps) => { const [currentTab, setCurrentTab] = useState('unread'); + return (
diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index 49926ba..26ca029 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -169,6 +169,14 @@ export const handlers = [ }); }), + http.patch('/inbox/mails/:id', () => { + return HttpResponse.json({}, { status: 204 }); + }), + + http.delete('/inbox/mails/:id', () => { + return HttpResponse.json({ status: 204 }); + }), + http.get('/users', () => { return HttpResponse.json({ user_id: 'RandomString',