diff --git a/apps/client/src/assets/chippi_remindx.svg b/apps/client/src/assets/chippi_remindx.svg new file mode 100644 index 00000000..a3dc6dc1 --- /dev/null +++ b/apps/client/src/assets/chippi_remindx.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/client/src/assets/chippi_x.svg b/apps/client/src/assets/chippi_x.svg new file mode 100644 index 00000000..073d3f54 --- /dev/null +++ b/apps/client/src/assets/chippi_x.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/client/src/layout/Layout.tsx b/apps/client/src/layout/Layout.tsx index f358f379..07ebd2aa 100644 --- a/apps/client/src/layout/Layout.tsx +++ b/apps/client/src/layout/Layout.tsx @@ -1,12 +1,12 @@ import { Outlet } from 'react-router-dom'; -import { Sidebar } from '../shared/components/sidebar/Sidebar'; +import { Sidebar } from '@shared/components/sidebar/Sidebar'; const Layout = () => { return ( <>
-
+
diff --git a/apps/client/src/pages/myBookmark/MyBookmark.tsx b/apps/client/src/pages/myBookmark/MyBookmark.tsx index 73d07309..b33285b9 100644 --- a/apps/client/src/pages/myBookmark/MyBookmark.tsx +++ b/apps/client/src/pages/myBookmark/MyBookmark.tsx @@ -3,15 +3,22 @@ import { useState } from 'react'; import { useGetBookmarkArticles, useGetBookmarkUnreadArticles, -} from './apis/queries'; + useGetCategoryBookmarkArticles, +} from '@pages/myBookmark/apis/queries'; +import { useSearchParams } from 'react-router-dom'; import { REMIND_MOCK_DATA } from '@pages/remind/constants'; import CardEditModal from '@shared/components/cardEditModal/CardEditModal'; import OptionsMenuPortal from '@shared/components/sidebar/OptionsMenuPortal'; import { useAnchoredMenu } from '@shared/hooks/useAnchoredMenu'; import { belowOf } from '@shared/utils/anchorPosition'; +import NoArticles from '@pages/myBookmark/components/NoArticles/NoArticles'; +import { Icon } from '@pinback/design-system/icons'; const MyBookmark = () => { const [activeBadge, setActiveBadge] = useState<'all' | 'notRead'>('all'); + const [searchParams] = useSearchParams(); + const category = searchParams.get('category'); + const categoryId = searchParams.get('id'); const [isEditOpen, setIsEditOpen] = useState(false); const { @@ -25,58 +32,66 @@ const MyBookmark = () => { const getBookmarkTitle = (id: number | null) => id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? ''); - const { data: readArticles } = useGetBookmarkArticles(1, 10); - const { data: unreadArticles } = useGetBookmarkUnreadArticles(1, 10); + const { data: articles } = useGetBookmarkArticles(0, 20); + const { data: unreadArticles } = useGetBookmarkUnreadArticles(0, 20); + const { data: categoryArticles } = useGetCategoryBookmarkArticles( + categoryId, + 1, + 10 + ); + + const articlesToDisplay = + activeBadge === 'all' ? articles?.articles : unreadArticles?.articles; + + // 임시 콘솔 + console.log('categoryArticles', categoryArticles); const handleBadgeClick = (badgeType: 'all' | 'notRead') => { setActiveBadge(badgeType); }; return ( -
-

나의 북마크

+
+
+
+

나의 북마크

+ {category && ( + + )} +
+

{category || ''}

+
handleBadgeClick('all')} isActive={activeBadge === 'all'} /> handleBadgeClick('notRead')} isActive={activeBadge === 'notRead'} />
-
- {/* TODO: API 연결 후 수정 */} - {activeBadge === 'all' && - readArticles?.articles.map((article) => ( + {articlesToDisplay && articlesToDisplay.length > 0 ? ( +
+ {articlesToDisplay.map((article) => ( {}} - onOptionsClick={(e) => - openMenu(article.articleId, e.currentTarget) - } - /> - ))} - - {activeBadge === 'notRead' && - unreadArticles?.articles.map((article) => ( - {}} onOptionsClick={(e) => @@ -84,24 +99,27 @@ const MyBookmark = () => { } /> ))} +
+ ) : ( + + )} - { - setIsEditOpen(true); - closeMenu(); - }} - onDelete={(id) => { - console.log('delete', id); - closeMenu(); - }} - onClose={closeMenu} - /> -
+ { + setIsEditOpen(true); + closeMenu(); + }} + onDelete={(id) => { + console.log('delete', id); + closeMenu(); + }} + onClose={closeMenu} + /> {isEditOpen && (
@@ -110,7 +128,6 @@ const MyBookmark = () => { onClick={() => setIsEditOpen(false)} />
- {/* 필요하면 menu.categoryId를 모달에 전달 */} setIsEditOpen(false)} />
diff --git a/apps/client/src/pages/myBookmark/apis/axios.ts b/apps/client/src/pages/myBookmark/apis/axios.ts index deb77aac..5146cd69 100644 --- a/apps/client/src/pages/myBookmark/apis/axios.ts +++ b/apps/client/src/pages/myBookmark/apis/axios.ts @@ -13,3 +13,14 @@ export const getBookmarkUnreadArticles = async (page: number, size: number) => { ); return data.data; }; + +export const getCategoryBookmarkArticles = async ( + categoryId: string | null, + page: number, + size: number +) => { + const { data } = await apiRequest.get( + `/api/v1/articles/category?categoryId=${categoryId}&page=${page}&size=${size}` + ); + return data.data; +}; diff --git a/apps/client/src/pages/myBookmark/apis/queries.ts b/apps/client/src/pages/myBookmark/apis/queries.ts index 31be0362..75d3295a 100644 --- a/apps/client/src/pages/myBookmark/apis/queries.ts +++ b/apps/client/src/pages/myBookmark/apis/queries.ts @@ -1,8 +1,13 @@ import { useQuery, UseQueryResult } from '@tanstack/react-query'; import { AxiosError } from 'axios'; -import { getBookmarkArticles, getBookmarkUnreadArticles } from './axios'; +import { + getBookmarkArticles, + getBookmarkUnreadArticles, + getCategoryBookmarkArticles, +} from './axios'; import { BookmarkArticleResponse, + CategoryBookmarkArticleResponse, UnreadBookmarkArticleResponse, } from '@pages/myBookmark/types/api'; @@ -25,3 +30,15 @@ export const useGetBookmarkUnreadArticles = ( queryFn: () => getBookmarkUnreadArticles(page, size), }); }; + +export const useGetCategoryBookmarkArticles = ( + categoryId: string | null, + page: number, + size: number +): UseQueryResult => { + return useQuery({ + queryKey: ['categoryBookmarkArticles', categoryId, page, size], + queryFn: () => getCategoryBookmarkArticles(categoryId, page, size), + enabled: !!categoryId, + }); +}; diff --git a/apps/client/src/pages/myBookmark/components/NoArticles/NoArticles.tsx b/apps/client/src/pages/myBookmark/components/NoArticles/NoArticles.tsx new file mode 100644 index 00000000..a6656650 --- /dev/null +++ b/apps/client/src/pages/myBookmark/components/NoArticles/NoArticles.tsx @@ -0,0 +1,23 @@ +import chippiNoArticles from '@assets/chippi_x.svg'; +import { Icon } from '@pinback/design-system/icons'; + +const NoArticles = () => { + return ( +
+ No Articles + +

첫 북마크가 저장되면 여기에 모여요 ✨

+
+

+ 원하는 페이지에서 +

+
+ +

아이콘을 눌러주세요.

+
+
+
+ ); +}; + +export default NoArticles; diff --git a/apps/client/src/pages/myBookmark/types/api.ts b/apps/client/src/pages/myBookmark/types/api.ts index 2b38e679..151fa48b 100644 --- a/apps/client/src/pages/myBookmark/types/api.ts +++ b/apps/client/src/pages/myBookmark/types/api.ts @@ -1,20 +1,31 @@ +interface Category { + categoryId: number; + categoryName: string; + categoryColor: string; +} + interface BookmarkArticle { articleId: number; url: string; memo: string; createdAt: string; isRead: boolean; + category: Category; } // 북마크 전체 조회 export interface BookmarkArticleResponse { totalArticle: number; totalUnreadArticle: number; + isNewUser: boolean; articles: BookmarkArticle[]; } // 북마크 안 읽음 조회 export interface UnreadBookmarkArticleResponse { + totalArticle: number; totalUnreadArticle: number; articles: BookmarkArticle[]; } + +export type CategoryBookmarkArticleResponse = UnreadBookmarkArticleResponse; diff --git a/apps/client/src/pages/remind/Remind.tsx b/apps/client/src/pages/remind/Remind.tsx index ae4c76f6..b5e481b3 100644 --- a/apps/client/src/pages/remind/Remind.tsx +++ b/apps/client/src/pages/remind/Remind.tsx @@ -5,8 +5,10 @@ import OptionsMenuPortal from '@shared/components/sidebar/OptionsMenuPortal'; import { useAnchoredMenu } from '@shared/hooks/useAnchoredMenu'; import { belowOf } from '@shared/utils/anchorPosition'; import { REMIND_MOCK_DATA } from './constants'; -import { useGetRemindArticles } from './apis/queries'; +import { useGetRemindArticles } from '@pages/remind/apis/queries'; import { formatLocalDateTime } from '@shared/utils/formatDateTime'; +import NoReadArticles from '@pages/remind/components/noReadArticles/NoReadArticles'; +import NoUnreadArticles from '@pages/remind/components/noUnreadArticles/NoUnreadArticles'; const Remind = () => { const [isEditOpen, setIsEditOpen] = useState(false); @@ -21,7 +23,7 @@ const Remind = () => { const getItemTitle = (id: number | null) => id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? ''); - const [activeBadge, setActiveBadge] = useState('notRead'); + const [activeBadge, setActiveBadge] = useState<'read' | 'notRead'>('notRead'); const formattedDate = formatLocalDateTime(); const { data } = useGetRemindArticles( @@ -31,10 +33,13 @@ const Remind = () => { 10 ); - const handleBadgeClick = (badgeType: string) => { + const handleBadgeClick = (badgeType: 'read' | 'notRead') => { setActiveBadge(badgeType); }; + const EmptyStateComponent = + activeBadge === 'read' ? : ; + return (

리마인드

@@ -53,39 +58,43 @@ const Remind = () => { />
-
- {/* TODO: API 연결 후 수정 */} - {data?.articles?.map((article) => ( - - openMenu(article.category.categoryId, e.currentTarget) - } - /> - ))} + {data?.articles && data.articles.length > 0 ? ( +
+ {data.articles.map((article) => ( + + openMenu(article.category.categoryId, e.currentTarget), + })} + /> + ))} +
+ ) : ( + EmptyStateComponent + )} - { - setIsEditOpen(true); - closeMenu(); - }} - onDelete={(id) => { - console.log('delete', id); - closeMenu(); - }} - onClose={closeMenu} - /> -
+ { + setIsEditOpen(true); + closeMenu(); + }} + onDelete={(id) => { + console.log('delete', id); + closeMenu(); + }} + onClose={closeMenu} + /> {isEditOpen && (
diff --git a/apps/client/src/pages/remind/apis/axios.ts b/apps/client/src/pages/remind/apis/axios.ts index 49153a29..1b6857af 100644 --- a/apps/client/src/pages/remind/apis/axios.ts +++ b/apps/client/src/pages/remind/apis/axios.ts @@ -7,7 +7,7 @@ export const getRemindArticles = async ( size: number ) => { const { data } = await apiRequest.get( - `/api/v1/articles/remind?now=${nowDate}&readStatus=${readStatus}&page=${page}&size=${size}` + `/api/v1/articles/remind?now=${nowDate}&read-status=${readStatus}&page=${page}&size=${size}` ); return data.data; }; diff --git a/apps/client/src/pages/remind/components/noReadArticles/NoReadArticles.tsx b/apps/client/src/pages/remind/components/noReadArticles/NoReadArticles.tsx new file mode 100644 index 00000000..6fabb36a --- /dev/null +++ b/apps/client/src/pages/remind/components/noReadArticles/NoReadArticles.tsx @@ -0,0 +1,16 @@ +import chippiNoArticles from '@assets/chippi_x.svg'; + +const NoReadArticles = () => { + return ( +
+ No Articles + +

앗..

+

+ 저장된 정보가 없어요 +

+
+ ); +}; + +export default NoReadArticles; diff --git a/apps/client/src/pages/remind/components/noUnreadArticles/NoUnreadArticles.tsx b/apps/client/src/pages/remind/components/noUnreadArticles/NoUnreadArticles.tsx new file mode 100644 index 00000000..10c09db3 --- /dev/null +++ b/apps/client/src/pages/remind/components/noUnreadArticles/NoUnreadArticles.tsx @@ -0,0 +1,16 @@ +import chippiNoRemindArticles from '@assets/chippi_remindx.svg'; + +const NoUnreadArticles = () => { + return ( +
+ No Articles + +

저장된 정보를 모두 꺼내봤어요!

+

+ 치삐가 다음 도토리를 기다리고 있어요 +

+
+ ); +}; + +export default NoUnreadArticles; diff --git a/apps/client/src/shared/components/sidebar/Sidebar.tsx b/apps/client/src/shared/components/sidebar/Sidebar.tsx index 1458eb31..c2500500 100644 --- a/apps/client/src/shared/components/sidebar/Sidebar.tsx +++ b/apps/client/src/shared/components/sidebar/Sidebar.tsx @@ -131,7 +131,7 @@ export function Sidebar() { active={selectedCategoryId === category.id} onClick={(id) => { closeMenu(); - selectCategory(id); + selectCategory(id, category.name); }} onOptionsClick={(id, el) => openMenu(id, el)} /> diff --git a/apps/client/src/shared/hooks/useSidebarNav.ts b/apps/client/src/shared/hooks/useSidebarNav.ts index 541010e7..e04ab3ac 100644 --- a/apps/client/src/shared/hooks/useSidebarNav.ts +++ b/apps/client/src/shared/hooks/useSidebarNav.ts @@ -23,10 +23,10 @@ export function useSidebarNav() { }, [navigate]); const selectCategory = useCallback( - (id: number) => { + (id: number, name: string) => { setActiveTab('mybookmark'); setSelectedCategoryId(id); - navigate(`/my-bookmarks?categoryId=${id}`); + navigate(`/my-bookmarks?id=${id}&category=${name}`); }, [navigate] ); diff --git a/packages/design-system/src/components/card/BaseCard.tsx b/packages/design-system/src/components/card/BaseCard.tsx index 543f9695..b48bcae1 100644 --- a/packages/design-system/src/components/card/BaseCard.tsx +++ b/packages/design-system/src/components/card/BaseCard.tsx @@ -4,7 +4,7 @@ interface BaseCardProps { const BaseCard = ({ children }: BaseCardProps) => { return ( -
+
{children}
); diff --git a/packages/design-system/src/icons/components/icon.tsx b/packages/design-system/src/icons/components/icon.tsx index 7bfa73df..4a70ae14 100644 --- a/packages/design-system/src/icons/components/icon.tsx +++ b/packages/design-system/src/icons/components/icon.tsx @@ -27,6 +27,7 @@ type IconColor = | 'gray900' | 'white-bg' | 'gray-bg' + | 'black' | 'font-black-1' | 'font-gray-2' | 'font-gray-3' diff --git a/packages/design-system/src/icons/iconNames.ts b/packages/design-system/src/icons/iconNames.ts index 004c5ef8..4d2c73c9 100644 --- a/packages/design-system/src/icons/iconNames.ts +++ b/packages/design-system/src/icons/iconNames.ts @@ -10,15 +10,16 @@ export const iconNames = [ 'ic_close', 'ic_details_category', 'ic_details_disable', + 'ic_extension', 'ic_info', 'ic_plus', - 'main_logo', 'logo', + 'main_header_logo', + 'main_logo', 'tooltip_1', 'tooltip_2', 'tooltip_3', 'tooltip_4', 'tooltip_5', - 'main_header_logo', ] as const; export type IconName = (typeof iconNames)[number]; diff --git a/packages/design-system/src/icons/source/ic_extension.svg b/packages/design-system/src/icons/source/ic_extension.svg new file mode 100644 index 00000000..4fc80b86 --- /dev/null +++ b/packages/design-system/src/icons/source/ic_extension.svg @@ -0,0 +1,4 @@ + + + +