1- import { useMemo , useRef , useState , useEffect } from 'react' ;
1+ import { useGetRemindArticles } from '@pages/remind/apis/queries' ;
2+ import NoReadArticles from '@pages/remind/components/noReadArticles/NoReadArticles' ;
3+ import NoUnreadArticles from '@pages/remind/components/noUnreadArticles/NoUnreadArticles' ;
24import {
35 Badge ,
6+ Card ,
47 PopupContainer ,
58 trackPageView ,
69} from '@pinback/design-system/ui' ;
710import CardEditModal from '@shared/components/cardEditModal/CardEditModal' ;
811import OptionsMenuPortal from '@shared/components/sidebar/OptionsMenuPortal' ;
912import { useAnchoredMenu } from '@shared/hooks/useAnchoredMenu' ;
1013import { belowOf } from '@shared/utils/anchorPosition' ;
11- import { REMIND_MOCK_DATA } from './constants' ;
12- import { useGetRemindArticles } from '@pages/remind/apis/queries' ;
1314import { formatLocalDateTime } from '@shared/utils/formatDateTime' ;
14- import NoReadArticles from '@pages/remind/components/noReadArticles/NoReadArticles' ;
15- import NoUnreadArticles from '@pages/remind/components/noUnreadArticles/NoUnreadArticles' ;
15+ import { useEffect , useMemo , useRef , useState } from 'react' ;
16+ import { REMIND_MOCK_DATA } from './constants' ;
17+
1618import {
17- usePutArticleReadStatus ,
1819 useDeleteRemindArticle ,
1920 useGetArticleDetail ,
21+ usePutArticleReadStatus ,
2022} from '@shared/apis/queries' ;
21- import { useQueryClient } from '@tanstack/react-query' ;
22- import NoRemindArticles from './components/noRemindArticles/NoRemindArticles' ;
23- import FetchCard from './components/fetchCard/FetchCard' ;
24- import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll' ;
23+ import JobSelectionFunnel from '@shared/components/jobSelectionFunnel/JobSelectionFunnel' ;
2524import TooltipCard from '@shared/components/tooltipCard/TooltipCard' ;
25+ import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll' ;
26+ import { useQueryClient } from '@tanstack/react-query' ;
2627import Footer from './components/footer/Footer' ;
27- import JobSelectionFunnel from '@shared /components/jobSelectionFunnel/JobSelectionFunnel ' ;
28+ import NoRemindArticles from '. /components/noRemindArticles/NoRemindArticles ' ;
2829
2930const Remind = ( ) => {
3031 useEffect ( ( ) => {
3132 trackPageView ( '대시보드 페이지 방문' ) ;
3233 } , [ ] ) ;
33-
3434 const [ isEditOpen , setIsEditOpen ] = useState ( false ) ;
3535 const [ activeBadge , setActiveBadge ] = useState < 'read' | 'notRead' > ( 'notRead' ) ;
3636 const [ isDeleteOpen , setIsDeleteOpen ] = useState ( false ) ;
@@ -69,13 +69,18 @@ const Remind = () => {
6969 containerRef,
7070 } = useAnchoredMenu ( ( anchor ) => belowOf ( anchor , 8 ) ) ;
7171
72+ /**
73+ * 24시간 유효한 리마인드만 표시
74+ */
75+
7276 const articlesToDisplay =
7377 data ?. pages
7478 . flatMap ( ( page ) => page . articles )
7579 . filter ( ( article ) => {
7680 const now = new Date ( ) . getTime ( ) ;
81+
7782 const remindTime = new Date ( article . remindAt ) . getTime ( ) ;
78- // 만료 시간 = 리마인드 시간 + 24시간
83+
7984 const expirationTime = remindTime + 24 * 60 * 60 * 1000 ;
8085
8186 return now >= remindTime && now < expirationTime ;
@@ -87,13 +92,21 @@ const Remind = () => {
8792 const handleDeleteArticle = ( id : number ) => {
8893 deleteArticle ( id , {
8994 onSuccess : ( ) => {
90- queryClient . invalidateQueries ( { queryKey : [ 'remindArticles' ] } ) ;
91- queryClient . invalidateQueries ( { queryKey : [ 'arcons' ] } ) ;
95+ queryClient . invalidateQueries ( {
96+ queryKey : [ 'remindArticles' ] ,
97+ } ) ;
98+
99+ queryClient . invalidateQueries ( {
100+ queryKey : [ 'arcons' ] ,
101+ } ) ;
102+
92103 setIsDeleteOpen ( false ) ;
104+
93105 setDeleteTargetId ( null ) ;
106+
94107 closeMenu ( ) ;
95- close ( ) ;
96108 } ,
109+
97110 onError : ( error ) => {
98111 console . error ( '아티클 삭제 실패:' , error ) ;
99112 } ,
@@ -106,6 +119,7 @@ const Remind = () => {
106119
107120 const EmptyStateComponent = ( ) => {
108121 const firstPageData = data ?. pages [ 0 ] ;
122+
109123 if (
110124 firstPageData ?. readArticleCount === 0 &&
111125 firstPageData ?. unreadArticleCount === 0
@@ -116,67 +130,87 @@ const Remind = () => {
116130 return activeBadge === 'read' ? < NoReadArticles /> : < NoUnreadArticles /> ;
117131 } ;
118132
119- // TODO: 로딩 상태 디자인 필요
120133 if ( isPending ) {
121134 return < div > Loading...</ div > ;
122135 }
123136
124- // TODO: 임시
125- // const unreadArticleCount = data?.pages[0]?.unreadArticleCount || 0;
126- // const readArticleCount = data?.pages[0]?.readArticleCount || 0;
127-
128137 return (
129138 < div className = "flex h-screen flex-col pl-[8rem] pr-[5rem] pt-[5.2rem]" >
130139 < p className = "head3" > 리마인드</ p >
140+
131141 < div className = "mt-[3rem] flex gap-[2.4rem]" >
132142 < Badge
133143 text = "안 읽음"
134- // countNum={unreadArticleCount}
135144 onClick = { ( ) => handleBadgeClick ( 'notRead' ) }
136145 isActive = { activeBadge === 'notRead' }
137146 />
147+
138148 < Badge
139149 text = "읽음"
140- // countNum={readArticleCount}
141150 onClick = { ( ) => handleBadgeClick ( 'read' ) }
142151 isActive = { activeBadge === 'read' }
143152 />
144153 </ div >
154+
145155 < TooltipCard />
146156
147157 { articlesToDisplay . length > 0 ? (
148158 < div
149159 ref = { scrollContainerRef }
150160 className = "scrollbar-hide mt-[2.6rem] flex flex-wrap gap-[1.6rem] overflow-y-auto scroll-smooth"
151161 >
152- { articlesToDisplay . map ( ( article ) => (
153- < FetchCard
154- key = { article . articleId }
155- article = { article }
156- onClick = { ( ) => {
157- window . open ( article . url , '_blank' ) ;
158-
159- updateToReadStatus ( article . articleId , {
160- onSuccess : ( ) => {
161- queryClient . invalidateQueries ( {
162- queryKey : [ 'remindArticles' ] ,
163- } ) ;
164- queryClient . invalidateQueries ( {
165- queryKey : [ 'arcons' ] ,
166- } ) ;
167- } ,
168- onError : ( error ) => {
169- console . error ( error ) ;
170- } ,
171- } ) ;
172- } }
173- onOptionsClick = { ( e ) => {
174- e . stopPropagation ( ) ;
175- openMenu ( article . articleId , e . currentTarget ) ;
176- } }
177- />
178- ) ) }
179- < div ref = { observerRef } style = { { height : '1px' , width : '100%' } } />
162+ { articlesToDisplay . map ( ( article ) => {
163+ const displayTitle = article . title ?. trim ( )
164+ ? article . title
165+ : '제목 없음' ;
166+
167+ const displayImageUrl = article . thumbnailUrl || undefined ;
168+
169+ return (
170+ < Card
171+ key = { article . articleId }
172+ type = "remind"
173+ title = { displayTitle }
174+ imageUrl = { displayImageUrl }
175+ content = { article . memo }
176+ timeRemaining = { article . remindAt }
177+ category = { article . category . categoryName }
178+ categoryColor = { article . category . categoryColor }
179+ onClick = { ( ) => {
180+ window . open ( article . url , '_blank' ) ;
181+
182+ updateToReadStatus ( article . articleId , {
183+ onSuccess : ( ) => {
184+ queryClient . invalidateQueries ( {
185+ queryKey : [ 'remindArticles' ] ,
186+ } ) ;
187+
188+ queryClient . invalidateQueries ( {
189+ queryKey : [ 'arcons' ] ,
190+ } ) ;
191+ } ,
192+
193+ onError : ( error ) => {
194+ console . error ( error ) ;
195+ } ,
196+ } ) ;
197+ } }
198+ onOptionsClick = { ( e ) => {
199+ e . stopPropagation ( ) ;
200+
201+ openMenu ( article . articleId , e . currentTarget ) ;
202+ } }
203+ />
204+ ) ;
205+ } ) }
206+
207+ < div
208+ ref = { observerRef }
209+ style = { {
210+ height : '1px' ,
211+ width : '100%' ,
212+ } }
213+ />
180214 </ div >
181215 ) : (
182216 < EmptyStateComponent />
@@ -251,7 +285,6 @@ const Remind = () => {
251285 < div className = "fixed inset-0 z-[2000] flex items-center justify-center bg-black/40 p-4" >
252286 < JobSelectionFunnel
253287 onComplete = { ( ) => {
254- // TODO: 관심 직무 핀 API 연동 필요
255288 setShowJobSelectionFunnel ( false ) ;
256289 } }
257290 />
0 commit comments