Skip to content
12 changes: 6 additions & 6 deletions apps/client/src/pages/level/Level.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Icon } from '@pinback/design-system/icons';
import { cn } from '@pinback/design-system/utils';
import LevelScene from '@pages/level/components/LevelScene';
import { getTreeLevel } from '@shared/utils/treeLevel';
import { TreeLevel } from '@pages/level/types/treeLevelType';
import { Icon } from '@pinback/design-system/icons';
import { Badge } from '@pinback/design-system/ui';
import { useGetArcons } from '@shared/apis/queries';
import { lazy, Suspense } from 'react';
import { cn } from '@pinback/design-system/utils';
import { useGetAcorns } from '@shared/apis/queries';
import { Balloon } from '@shared/components/balloon/Balloon';
import { getTreeLevel } from '@shared/utils/treeLevel';
import { lazy, Suspense } from 'react';

const LevelInfoCard = lazy(
() => import('@pages/level/components/LevelInfoCard')
);
const NextAcornTime = lazy(() => import('./components/NextAcornTime'));

export default function Level() {
const { data, isPending, isError } = useGetArcons();
const { data, isPending, isError } = useGetAcorns();

if (isPending) return <div />;
if (isError) return <div />;
Expand Down
28 changes: 14 additions & 14 deletions apps/client/src/pages/myBookmark/MyBookmark.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { PopupContainer } from '@pinback/design-system/ui';
import { useState, useRef, Suspense } from 'react';
import { useSearchParams } from 'react-router-dom';
import MyBookmarkContent from '@pages/myBookmark/components/myBookmarkContent/MyBookmarkContent';
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 { Icon } from '@pinback/design-system/icons';
import { useQueryClient } from '@tanstack/react-query';
import { PopupContainer } from '@pinback/design-system/ui';
import {
useGetArticleDetail,
useDeleteRemindArticle,
useGetArticleDetail,
usePutArticleReadStatus,
} from '@shared/apis/queries';
import TooltipCard from '@shared/components/tooltipCard/TooltipCard';
import ArticlesLoadingBoundary from '@shared/components/articlesLoadingBoundary/ArticlesLoadingBoundary';
import ArticlesErrorBoundary from '@shared/components/articlesErrorBoundary/ArticlesErrorBoundary';
import ArticlesLoadingBoundary from '@shared/components/articlesLoadingBoundary/ArticlesLoadingBoundary';
import CardEditModal from '@shared/components/cardEditModal/CardEditModal';
import OptionsMenuPortal from '@shared/components/sidebar/OptionsMenuPortal';
import TooltipCard from '@shared/components/tooltipCard/TooltipCard';
import { useAnchoredMenu } from '@shared/hooks/useAnchoredMenu';
import { belowOf } from '@shared/utils/anchorPosition';
import { useQueryClient } from '@tanstack/react-query';
import { Suspense, useRef, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import MyBookmarkContent from '@pages/myBookmark/components/myBookmarkContent/MyBookmarkContent';
import { useSearchParams } from 'react-router-dom';
import Footer from './components/footer/Footer';

const MyBookmark = () => {
Expand Down Expand Up @@ -58,7 +58,7 @@ const MyBookmark = () => {
queryClient.invalidateQueries({
queryKey: ['categoryBookmarkArticles'],
});
queryClient.invalidateQueries({ queryKey: ['arcons'] });
queryClient.invalidateQueries({ queryKey: ['acorns'] });
setIsDeleteOpen(false);
setDeleteTargetId(null);
closeMenu();
Expand Down Expand Up @@ -163,7 +163,7 @@ const MyBookmark = () => {
/>
<div className="absolute inset-0 flex items-center justify-center p-4">
<CardEditModal
key={articleDetail.id}
key={articleDetail.articleId}
onClose={() => setIsEditOpen(false)}
prevData={articleDetail}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useMyBookmarkContentData } from '@pages/myBookmark/hooks/useMyBookmarkContentData';
import NoArticles from '@pages/myBookmark/components/NoArticles/NoArticles';
import NoUnreadArticles from '@pages/myBookmark/components/noUnreadArticles/NoUnreadArticles';
import { MutableRefObject } from 'react';
import { useMyBookmarkContentData } from '@pages/myBookmark/hooks/useMyBookmarkContentData';
import { Badge, Card } from '@pinback/design-system/ui';
import { MutableRefObject } from 'react';

interface MyBookmarkContentProps {
activeBadge: 'all' | 'notRead';
Expand All @@ -25,12 +25,7 @@ const MyBookmarkContent = ({
queryClient,
scrollContainerRef,
}: MyBookmarkContentProps) => {
const {
view,
list,
counts,
pagination,
} = useMyBookmarkContentData({
const { view, list, counts, pagination } = useMyBookmarkContentData({
activeBadge,
category,
categoryId,
Expand Down Expand Up @@ -82,7 +77,9 @@ const MyBookmarkContent = ({
: article.category?.categoryName
}
categoryColor={
view.isCategoryView ? undefined : article.category?.categoryColor
view.isCategoryView
? undefined
: article.category?.categoryColor
}
date={new Date(article.createdAt).toLocaleDateString('ko-KR')}
onClick={() => {
Expand All @@ -101,7 +98,7 @@ const MyBookmarkContent = ({
queryClient.invalidateQueries({
queryKey: ['categoryBookmarkArticles'],
});
queryClient.invalidateQueries({ queryKey: ['arcons'] });
queryClient.invalidateQueries({ queryKey: ['acorns'] });
},
onError: (error: any) => {
console.error(error);
Expand Down
113 changes: 60 additions & 53 deletions apps/client/src/pages/remind/Remind.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import { useMemo, useRef, useState, useEffect } from 'react';
import { useGetRemindArticles } from '@pages/remind/apis/queries';
import NoReadArticles from '@pages/remind/components/noReadArticles/NoReadArticles';
import NoUnreadArticles from '@pages/remind/components/noUnreadArticles/NoUnreadArticles';
import {
Badge,
Card,
PopupContainer,
trackPageView,
} from '@pinback/design-system/ui';
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 { REMIND_MOCK_DATA } from './constants';
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';
import { useEffect, useMemo, useRef, useState } from 'react';
import { REMIND_MOCK_DATA } from './constants';

import {
usePutArticleReadStatus,
useDeleteRemindArticle,
useGetArticleDetail,
usePutArticleReadStatus,
} from '@shared/apis/queries';
import { useQueryClient } from '@tanstack/react-query';
import NoRemindArticles from './components/noRemindArticles/NoRemindArticles';
import FetchCard from './components/fetchCard/FetchCard';
import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll';
import JobSelectionFunnel from '@shared/components/jobSelectionFunnel/JobSelectionFunnel';
import TooltipCard from '@shared/components/tooltipCard/TooltipCard';
import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll';
import { useQueryClient } from '@tanstack/react-query';
import Footer from './components/footer/Footer';
import JobSelectionFunnel from '@shared/components/jobSelectionFunnel/JobSelectionFunnel';
import NoRemindArticles from './components/noRemindArticles/NoRemindArticles';

const Remind = () => {
useEffect(() => {
trackPageView('대시보드 페이지 방문');
}, []);

const [isEditOpen, setIsEditOpen] = useState(false);
const [activeBadge, setActiveBadge] = useState<'read' | 'notRead'>('notRead');
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
Expand Down Expand Up @@ -75,9 +75,7 @@ const Remind = () => {
.filter((article) => {
const now = new Date().getTime();
const remindTime = new Date(article.remindAt).getTime();
// 만료 시간 = 리마인드 시간 + 24시간
const expirationTime = remindTime + 24 * 60 * 60 * 1000;

return now >= remindTime && now < expirationTime;
}) ?? [];

Expand All @@ -87,13 +85,17 @@ const Remind = () => {
const handleDeleteArticle = (id: number) => {
deleteArticle(id, {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['remindArticles'] });
queryClient.invalidateQueries({ queryKey: ['arcons'] });
queryClient.invalidateQueries({
queryKey: ['remindArticles'],
});
queryClient.invalidateQueries({
queryKey: ['acorns'],
});
Comment on lines 88 to 93
Copy link

@coderabbitai coderabbitai bot Feb 26, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all usages of 'arcons' and 'acorns' query keys

echo "=== All 'arcons' usages ==="
rg -n "arcons" --type ts --type tsx

echo ""
echo "=== All 'acorns' usages ==="
rg -n "acorns" --type ts --type tsx

echo ""
echo "=== Checking MyBookmark.tsx specifically ==="
rg -n "queryKey" apps/client/src/pages/MyBookmark.tsx 2>/dev/null || rg -n "queryKey" --type tsx | grep -i bookmark

Repository: Pinback-Team/pinback-client

Length of output: 274


🏁 Script executed:

#!/bin/bash
# Search without file type restrictions

echo "=== All 'arcons' usages ==="
rg "arcons" --line-number

echo ""
echo "=== All 'acorns' usages ==="
rg "acorns" --line-number

echo ""
echo "=== Check if files exist ==="
find apps/client/src/pages -name "Remind.tsx" -o -name "MyBookmark.tsx"

echo ""
echo "=== Content of Remind.tsx around lines 95-101 ==="
sed -n '90,105p' apps/client/src/pages/remind/Remind.tsx 2>/dev/null || echo "File not found at that path"

echo ""
echo "=== Check for queryKey 'arcons' or 'acorns' in MyBookmark ==="
find . -name "MyBookmark*" -type f | head -5

Repository: Pinback-Team/pinback-client

Length of output: 2574


🏁 Script executed:

#!/bin/bash
# Check the query definition for 'arcons'

echo "=== Content of queries.ts around line 67 ==="
sed -n '60,75p' apps/client/src/shared/apis/queries.ts

echo ""
echo "=== Full context of 'arcons' usage in queries.ts ==="
rg -B5 -A5 "'arcons'" apps/client/src/shared/apis/queries.ts

Repository: Pinback-Team/pinback-client

Length of output: 787


쿼리 키 'arcons' 오타 수정 필요

'arcons''acorns'의 오타입니다. 쿼리 정의(useGetArcons), 반환 타입(AcornsResponse), 실제 함수 호출(getAcorns())은 모두 'acorns'를 사용하고 있어, 쿼리 키도 일관되게 'acorns'로 통일해야 합니다. 현재 다음 5개 위치에서 잘못된 쿼리 키를 사용하고 있습니다:

  • apps/client/src/shared/apis/queries.ts (쿼리 정의)
  • apps/client/src/pages/remind/Remind.tsx (2개 위치)
  • apps/client/src/pages/myBookmark/MyBookmark.tsx
  • apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx
✏️ 쿼리 키 수정 제안
         queryClient.invalidateQueries({
-          queryKey: ['arcons'],
+          queryKey: ['acorns'],
         });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
queryClient.invalidateQueries({
queryKey: ['remindArticles'],
});
queryClient.invalidateQueries({
queryKey: ['arcons'],
});
queryClient.invalidateQueries({
queryKey: ['acorns'],
});
queryClient.invalidateQueries({
queryKey: ['acorns'],
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/client/src/pages/remind/Remind.tsx` around lines 95 - 101, The query key
string 'arcons' is a typo and must be corrected to 'acorns' to match the
existing query definitions and types (useGetArcons, AcornsResponse,
getAcorns()); update every occurrence where the key is used (e.g., the two
queryClient.invalidateQueries calls in Remind.tsx and the other listed places)
so the queryKey arrays use ['acorns'] instead of ['arcons'], ensuring
consistency with the useGetArcons/query definition and any related cache
invalidation logic.

Copy link
Member

Choose a reason for hiding this comment

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

이건 몰랐네요 ㅎㅎㅎㅎㅎ,,,

Copy link

Choose a reason for hiding this comment

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

@constantly-dev 넵! 작은 오타지만 쿼리 캐싱에 영향을 줄 수 있어서 5개 위치 모두 수정해주시면 좋을 것 같아요 😊

수정이 필요한 위치들:

  • apps/client/src/shared/apis/queries.ts (쿼리 정의)
  • apps/client/src/pages/remind/Remind.tsx (2곳)
  • apps/client/src/pages/myBookmark/MyBookmark.tsx
  • apps/client/src/pages/myBookmark/components/myBookmarkContent/MyBookmarkContent.tsx

🧠 Learnings used
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.

Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 5
File: apps/extension/src/index.css:1-1
Timestamp: 2025-08-19T17:18:57.678Z
Learning: constantly-dev는 디자인 시스템 설정 PR 머지 후 `import 'pinback/tailwind-config/shared-styles.css';`를 `app.css`나 `index.css`에서 사용하여 공유 스타일을 관리할 계획입니다.

setIsDeleteOpen(false);
setDeleteTargetId(null);
closeMenu();
close();
},

Comment on lines 94 to 98
Copy link
Member

Choose a reason for hiding this comment

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

공백 추가된건 prettier인가요?

onError: (error) => {
console.error('아티클 삭제 실패:', error);
},
Expand All @@ -106,6 +108,7 @@ const Remind = () => {

const EmptyStateComponent = () => {
const firstPageData = data?.pages[0];

if (
firstPageData?.readArticleCount === 0 &&
firstPageData?.unreadArticleCount === 0
Expand All @@ -116,67 +119,72 @@ const Remind = () => {
return activeBadge === 'read' ? <NoReadArticles /> : <NoUnreadArticles />;
};

// TODO: 로딩 상태 디자인 필요
if (isPending) {
return <div>Loading...</div>;
}

// TODO: 임시
// const unreadArticleCount = data?.pages[0]?.unreadArticleCount || 0;
// const readArticleCount = data?.pages[0]?.readArticleCount || 0;

return (
<div className="flex h-screen flex-col pl-[8rem] pr-[5rem] pt-[5.2rem]">
<p className="head3">리마인드</p>

<div className="mt-[3rem] flex gap-[2.4rem]">
<Badge
text="안 읽음"
// countNum={unreadArticleCount}
onClick={() => handleBadgeClick('notRead')}
isActive={activeBadge === 'notRead'}
/>

<Badge
text="읽음"
// countNum={readArticleCount}
onClick={() => handleBadgeClick('read')}
isActive={activeBadge === 'read'}
/>
</div>

<TooltipCard />

{articlesToDisplay.length > 0 ? (
<div
ref={scrollContainerRef}
className="scrollbar-hide mt-[2.6rem] flex flex-wrap gap-[1.6rem] overflow-y-auto scroll-smooth"
>
{articlesToDisplay.map((article) => (
<FetchCard
key={article.articleId}
article={article}
onClick={() => {
window.open(article.url, '_blank');
{articlesToDisplay.map((article) => {
const displayTitle = article.title?.trim()
? article.title
: '제목 없음';

updateToReadStatus(article.articleId, {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['remindArticles'],
});
queryClient.invalidateQueries({
queryKey: ['arcons'],
});
},
onError: (error) => {
console.error(error);
},
});
}}
onOptionsClick={(e) => {
e.stopPropagation();
openMenu(article.articleId, e.currentTarget);
}}
/>
))}
<div ref={observerRef} style={{ height: '1px', width: '100%' }} />
const displayImageUrl = article.thumbnailUrl || undefined;
Copy link
Member

Choose a reason for hiding this comment

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

이미지 없을때 fallback image 처리해야겠네요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

card 컴포넌트에서 이미지 없을시 로고를 띄우고 있는걸로 아는데 따로 또 fallback 처리를 해야할까요?


return (
<Card
key={article.articleId}
type="remind"
title={displayTitle}
imageUrl={displayImageUrl}
content={article.memo}
timeRemaining={article.remindAt}
category={article.category.categoryName}
categoryColor={article.category.categoryColor}
onClick={() => {
window.open(article.url, '_blank');
updateToReadStatus(article.articleId);
}}
onOptionsClick={(e) => {
e.stopPropagation();

openMenu(article.articleId, e.currentTarget);
}}
/>
);
})}

<div
ref={observerRef}
style={{
height: '1px',
width: '100%',
}}
/>
</div>
) : (
<EmptyStateComponent />
Expand Down Expand Up @@ -239,7 +247,7 @@ const Remind = () => {
/>
<div className="absolute inset-0 flex items-center justify-center p-4">
<CardEditModal
key={articleDetail.id}
key={articleDetail.articleId}
onClose={() => setIsEditOpen(false)}
prevData={articleDetail}
/>
Expand All @@ -251,7 +259,6 @@ const Remind = () => {
<div className="fixed inset-0 z-[2000] flex items-center justify-center bg-black/40 p-4">
<JobSelectionFunnel
onComplete={() => {
// TODO: 관심 직무 핀 API 연동 필요
setShowJobSelectionFunnel(false);
}}
/>
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/pages/remind/apis/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const getRemindArticles = async (
page: number,
size: number
) => {
const { data } = await apiRequest.get(`/api/v2/articles/remind`, {
const { data } = await apiRequest.get(`/api/v3/articles/remind`, {
params: {
now: nowDate,
'read-status': readStatus,
Expand Down
39 changes: 0 additions & 39 deletions apps/client/src/pages/remind/components/fetchCard/FetchCard.tsx

This file was deleted.

Loading
Loading