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
9 changes: 9 additions & 0 deletions apps/client/src/assets/chippi_remindx.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions apps/client/src/assets/chippi_x.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions apps/client/src/layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<div className="flex h-screen">
<Sidebar />
<main className="flex-1 overflow-y-auto">
<main className="bg-gray-bg flex-1 overflow-y-auto">
<Outlet />
</main>
</div>
Expand Down
109 changes: 63 additions & 46 deletions apps/client/src/pages/myBookmark/MyBookmark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -25,83 +32,94 @@ 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 (
<div className="flex flex-col py-[5.2rem] pl-[8rem]">
<p className="head3">나의 북마크</p>
<div className="flex h-screen flex-col py-[5.2rem] pl-[8rem]">
<div className="flex items-center gap-[0.4rem]">
<div className="flex items-center gap-[0.4rem]">
<p className="head3">나의 북마크</p>
{category && (
<Icon
name="ic_arrow_down_disable"
width={24}
height={24}
rotate={270}
color="black"
/>
)}
</div>
<p className="head3 text-main500">{category || ''}</p>
</div>
Comment on lines +58 to +69
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

헤더 카테고리 표시 변수 교체

표시용은 categoryName으로 교체해 의미를 명확히 하세요.

-          {category && (
+          {categoryName && (
             <Icon
               name="ic_arrow_down_disable"
               width={24}
               height={24}
               rotate={270}
               color="black"
             />
           )}
-        <p className="head3 text-main500">{category || ''}</p>
+        <p className="head3 text-main500">{categoryName || ''}</p>
📝 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
{category && (
<Icon
name="ic_arrow_down_disable"
width={24}
height={24}
rotate={270}
color="black"
/>
)}
</div>
<p className="head3 text-main500">{category || ''}</p>
</div>
{categoryName && (
<Icon
name="ic_arrow_down_disable"
width={24}
height={24}
rotate={270}
color="black"
/>
)}
</div>
<p className="head3 text-main500">{categoryName || ''}</p>
</div>
🤖 Prompt for AI Agents
In apps/client/src/pages/myBookmark/MyBookmark.tsx around lines 38 to 49, the
header is using the prop/state variable category for display; change both the
conditional render (the Icon block) and the paragraph text to use categoryName
instead of category, keeping the existing fallback (empty string) for the
paragraph and preserving the Icon render condition to only show when
categoryName is truthy.


<div className="mt-[3rem] flex gap-[2.4rem]">
<Badge
text="전체보기"
countNum={readArticles?.totalArticle || 0}
countNum={articles?.totalArticle || 0}
onClick={() => handleBadgeClick('all')}
isActive={activeBadge === 'all'}
/>
<Badge
text="안 읽음"
countNum={readArticles?.totalUnreadArticle || 0}
countNum={articles?.totalUnreadArticle || 0}
onClick={() => handleBadgeClick('notRead')}
isActive={activeBadge === 'notRead'}
/>
</div>
Comment on lines 71 to 84
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

뱃지 카운트 소스 잘못 지정 및 카테고리 연동 필요

  • "안 읽음" 뱃지 카운트가 articles에서 오고 있어 잘못된 값입니다.
  • 카테고리 필터가 있을 때 "전체보기" 카운트는 categoryArticles를 사용해야 일관됩니다.
-<Badge
-  text="전체보기"
-  countNum={articles?.totalArticle || 0}
+<Badge
+  text="전체보기"
+  countNum={(categoryId ? categoryArticles?.totalArticle : articles?.totalArticle) || 0}
   onClick={() => handleBadgeClick('all')}
   isActive={activeBadge === 'all')}
 />
 <Badge
   text="안 읽음"
-  countNum={articles?.totalUnreadArticle || 0}
+  countNum={unreadArticles?.totalArticle || 0}
   onClick={() => handleBadgeClick('notRead')}
   isActive={activeBadge === 'notRead')}
 />
📝 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
<div className="mt-[3rem] flex gap-[2.4rem]">
<Badge
text="전체보기"
countNum={readArticles?.totalArticle || 0}
countNum={articles?.totalArticle || 0}
onClick={() => handleBadgeClick('all')}
isActive={activeBadge === 'all'}
/>
<Badge
text="안 읽음"
countNum={readArticles?.totalUnreadArticle || 0}
countNum={articles?.totalUnreadArticle || 0}
onClick={() => handleBadgeClick('notRead')}
isActive={activeBadge === 'notRead'}
/>
</div>
<div className="mt-[3rem] flex gap-[2.4rem]">
<Badge
text="전체보기"
countNum={(categoryId ? categoryArticles?.totalArticle : articles?.totalArticle) || 0}
onClick={() => handleBadgeClick('all')}
isActive={activeBadge === 'all'}
/>
<Badge
text="안 읽음"
countNum={unreadArticles?.totalArticle || 0}
onClick={() => handleBadgeClick('notRead')}
isActive={activeBadge === 'notRead'}
/>
</div>
🤖 Prompt for AI Agents
In apps/client/src/pages/myBookmark/MyBookmark.tsx around lines 51-64, the badge
count sources are wrong: the "안 읽음" badge is using articles instead of the
unread count from the currently active data set, and "전체보기" should show counts
from categoryArticles when a category filter is applied. Update the countNum
props so both badges derive their values from categoryArticles when a category
is selected (e.g., categoryArticles?.totalArticle and
categoryArticles?.totalUnreadArticle) otherwise fall back to
articles?.totalArticle and articles?.totalUnreadArticle, and keep null-safe
fallbacks to 0.


<div className="scrollbar-hide mt-[2.6rem] flex max-w-[104rem] flex-wrap gap-[1.6rem] overflow-y-auto scroll-smooth">
{/* TODO: API 연결 후 수정 */}
{activeBadge === 'all' &&
readArticles?.articles.map((article) => (
{articlesToDisplay && articlesToDisplay.length > 0 ? (
<div className="scrollbar-hide mt-[2.6rem] flex h-screen max-w-[104rem] flex-wrap gap-[1.6rem] overflow-y-auto scroll-smooth">
{articlesToDisplay.map((article) => (
<Card
key={article.articleId}
type="bookmark"
title={article.url}
content={article.memo}
// category={article.category.categoryName}
date={new Date(article.createdAt).toLocaleDateString('ko-KR')}
onClick={() => {}}
onOptionsClick={(e) =>
openMenu(article.articleId, e.currentTarget)
}
/>
))}

{activeBadge === 'notRead' &&
unreadArticles?.articles.map((article) => (
<Card
key={article.articleId}
type="bookmark"
title={article.url}
content={article.memo}
// category={article.}
category={article.category.categoryName}
date={new Date(article.createdAt).toLocaleDateString('ko-KR')}
onClick={() => {}}
onOptionsClick={(e) =>
openMenu(article.articleId, e.currentTarget)
}
/>
))}
</div>
) : (
<NoArticles />
)}

<OptionsMenuPortal
open={menu.open}
style={style ?? undefined}
containerRef={containerRef}
categoryId={menu.categoryId}
getCategoryName={getBookmarkTitle}
onEdit={() => {
setIsEditOpen(true);
closeMenu();
}}
onDelete={(id) => {
console.log('delete', id);
closeMenu();
}}
onClose={closeMenu}
/>
</div>
<OptionsMenuPortal
open={menu.open}
style={style ?? undefined}
containerRef={containerRef}
categoryId={menu.categoryId}
getCategoryName={getBookmarkTitle}
onEdit={() => {
setIsEditOpen(true);
closeMenu();
}}
onDelete={(id) => {
console.log('delete', id);
closeMenu();
}}
onClose={closeMenu}
/>

{isEditOpen && (
<div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
Expand All @@ -110,7 +128,6 @@ const MyBookmark = () => {
onClick={() => setIsEditOpen(false)}
/>
<div className="absolute inset-0 flex items-center justify-center p-4">
{/* 필요하면 menu.categoryId를 모달에 전달 */}
<CardEditModal onClose={() => setIsEditOpen(false)} />
</div>
</div>
Expand Down
11 changes: 11 additions & 0 deletions apps/client/src/pages/myBookmark/apis/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Comment on lines +17 to +26
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

null 카테고리 방지 및 쿼리스트링 조합 방식 개선 제안

  • categoryId가 null일 때 ...categoryId=null로 호출될 수 있습니다. 안전하게 가드하고, 템플릿 문자열 대신 axios params를 사용해 인코딩/누락 처리 이점을 얻는 편이 좋습니다.

다음 diff 적용 제안:

 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}`
-  );
+  if (!categoryId) {
+    throw new Error('categoryId is required');
+  }
+  const { data } = await apiRequest.get('/api/v1/articles/category', {
+    params: { categoryId, page, size },
+  });
   return data.data;
 };

추가로, 반환 타입을 명확히 하려면 API envelope 타입을 도입하거나 캐스팅을 통해 CategoryBookmarkArticleResponse를 지정하는 것도 고려해주세요.

📝 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
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;
};
export const getCategoryBookmarkArticles = async (
categoryId: string | null,
page: number,
size: number
) => {
if (!categoryId) {
throw new Error('categoryId is required');
}
const { data } = await apiRequest.get('/api/v1/articles/category', {
params: { categoryId, page, size },
});
return data.data;
};
🤖 Prompt for AI Agents
In apps/client/src/pages/myBookmark/apis/axios.ts around lines 17-26, the
current implementation interpolates categoryId into the querystring which can
produce categoryId=null; change to call apiRequest.get with axios params (e.g.,
{ params: { page, size, ...(categoryId ? { categoryId } : {}) } }) so null is
omitted and axios handles encoding; also annotate the function return type by
mapping the response envelope to CategoryBookmarkArticleResponse (or cast
data.data) before returning.

💡 Verification agent

🧩 Analysis chain

URL 파라미터 네이밍 일관성 확인 필요 (id vs categoryId)

라우팅에서는 /my-bookmarks?id=...를 사용하고, API에서는 categoryId를 기대합니다. 코드베이스 전반의 파라미터 키가 혼용되지 않는지 점검 부탁드립니다.

다음 스크립트로 사용처를 빠르게 확인할 수 있습니다:


🏁 Script executed:

#!/bin/bash
# id / categoryId 파라미터 사용 현황 점검
rg -n --type ts --type tsx -C2 '\bcreateSearchParams\(|useSearchParams\(|categoryId\b|\bid='

Length of output: 27


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# TS/TSX 파일에서 createSearchParams, useSearchParams, categoryId, id= 사용 현황 재검색
echo "검색 시작: createSearchParams, useSearchParams, categoryId, id= (ts/tsx/js/jsx)..."

rg -n -C2 -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' -g '*.ts' -g '*.tsx' -g '*.js' -g '*.jsx' '\b(createSearchParams\(|useSearchParams\(|categoryId\b|\bid=)' || true

# 문자열로 포함된 쿼리 파라미터도 확인
echo "검색 추가: 파일 전체에서 '?id=' 포함 여부 확인..."
rg -n -C2 -g '!**/node_modules/**' '\?id=' || true

Length of output: 9237


쿼리 파라미터 키 불일치 — 긴급 수정 필요

라우팅은 id 및 category(name)을 쿼리로 보내는데, MyBookmark는 'category'를 categoryId로 사용하고 API는 'categoryId'를 기대합니다. 이로 인해 올바른 카테고리 ID가 API에 전달되지 않습니다.

  • 발견 위치:

    • apps/client/src/shared/hooks/useSidebarNav.ts — navigate(/my-bookmarks?id=${id}&category=${name})
    • apps/client/src/pages/myBookmark/MyBookmark.tsx — searchParams.get('category')를 categoryId로 사용
    • apps/client/src/pages/myBookmark/apis/axios.ts — getCategoryBookmarkArticles가 ?categoryId=${categoryId}로 호출
    • apps/client/src/pages/myBookmark/apis/queries.ts — queryKey / enabled: !!categoryId 사용
  • 권장 조치:

    • 일관화(택1): MyBookmark에서 searchParams.get('id')로 실제 ID를 읽어 API에 전달하거나, useSidebarNav에서 쿼리 키를 categoryId로 변경.
    • API 전송 전 string → number 변환 확인 및 enabled 조건이 의도대로 동작하는지 검증.

19 changes: 18 additions & 1 deletion apps/client/src/pages/myBookmark/apis/queries.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -25,3 +30,15 @@ export const useGetBookmarkUnreadArticles = (
queryFn: () => getBookmarkUnreadArticles(page, size),
});
};

export const useGetCategoryBookmarkArticles = (
categoryId: string | null,
page: number,
size: number
): UseQueryResult<CategoryBookmarkArticleResponse, AxiosError> => {
return useQuery({
queryKey: ['categoryBookmarkArticles', categoryId, page, size],
queryFn: () => getCategoryBookmarkArticles(categoryId, page, size),
enabled: !!categoryId,
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import chippiNoArticles from '@assets/chippi_x.svg';
import { Icon } from '@pinback/design-system/icons';

const NoArticles = () => {
return (
<div className="mx-auto mt-[14rem] flex flex-col items-center">
<img src={chippiNoArticles} alt="No Articles" />

<p className="head2 mt-[1.6rem]">첫 북마크가 저장되면 여기에 모여요 ✨</p>
<div className="mt-[0.8rem] flex items-center">
<p className="body1-m text-font-gray-3 gap-[0.4rem]">
원하는 페이지에서
</p>
<div className="flex items-center text-center">
<Icon name="ic_extension" width={28} height={28} />
<p className="body1-m text-font-gray-3">아이콘을 눌러주세요.</p>
</div>
</div>
</div>
);
};

export default NoArticles;
11 changes: 11 additions & 0 deletions apps/client/src/pages/myBookmark/types/api.ts
Original file line number Diff line number Diff line change
@@ -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;
79 changes: 44 additions & 35 deletions apps/client/src/pages/remind/Remind.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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');
Comment on lines 24 to +26
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

카테고리명 조회가 목데이터에 의존 — 실제 API 데이터로 교체 필요

getItemTitleREMIND_MOCK_DATA를 사용하고 있어 실제 categoryId와 매칭되지 않을 가능성이 큽니다. 옵션 메뉴에 잘못된 이름(혹은 빈 문자열)이 전달될 수 있습니다. API 응답(data.articles) 기반으로 조회하도록 교체해주세요.

적용 예시:

- const getItemTitle = (id: number | null) =>
-   id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? '');
+ const getCategoryName = (id: number | null) =>
+   id == null
+     ? ''
+     : data?.articles?.find((a) => a.category.categoryId === id)?.category
+         .categoryName ?? '';

추가로, 상단의 REMIND_MOCK_DATA import는 제거해주세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/client/src/pages/remind/Remind.tsx around lines 24-26, getItemTitle
currently looks up REMIND_MOCK_DATA which can mismatch real categoryIds; change
it to look up the title from the API response (data.articles or the correct
articles/categories array available in this component) using the passed id,
return empty string when not found, and remove the REMIND_MOCK_DATA import at
the top; ensure you handle the case where data is undefined (safe optional
chaining) and keep the function signature the same.

const formattedDate = formatLocalDateTime();

const { data } = useGetRemindArticles(
Expand All @@ -31,10 +33,13 @@ const Remind = () => {
10
);

const handleBadgeClick = (badgeType: string) => {
const handleBadgeClick = (badgeType: 'read' | 'notRead') => {
setActiveBadge(badgeType);
};

const EmptyStateComponent =
activeBadge === 'read' ? <NoReadArticles /> : <NoUnreadArticles />;

return (
<div className="flex flex-col py-[5.2rem] pl-[8rem]">
<p className="head3">리마인드</p>
Expand All @@ -53,39 +58,43 @@ const Remind = () => {
/>
</div>

<div className="scrollbar-hide mt-[2.6rem] flex max-w-[104rem] flex-wrap gap-[1.6rem] overflow-y-auto scroll-smooth">
{/* TODO: API 연결 후 수정 */}
{data?.articles?.map((article) => (
<Card
key={article.articleId}
type="remind"
title={article.url}
content={article.memo}
timeRemaining={article.remindAt}
category={article.category.categoryName}
onOptionsClick={(e) =>
openMenu(article.category.categoryId, e.currentTarget)
}
/>
))}
{data?.articles && data.articles.length > 0 ? (
<div className="scrollbar-hide mt-[2.6rem] flex max-w-[104rem] flex-wrap gap-[1.6rem] overflow-y-auto scroll-smooth">
{data.articles.map((article) => (
<Card
key={article.articleId}
type="remind"
title={article.url}
content={article.memo}
timeRemaining={article.remindAt}
category={article.category.categoryName}
{...(activeBadge === 'notRead' && {
onOptionsClick: (e) =>
openMenu(article.category.categoryId, e.currentTarget),
})}
/>
))}
</div>
) : (
EmptyStateComponent
)}

<OptionsMenuPortal
open={menu.open}
style={style ?? undefined}
containerRef={containerRef}
categoryId={menu.categoryId}
getCategoryName={getItemTitle}
onEdit={() => {
setIsEditOpen(true);
closeMenu();
}}
onDelete={(id) => {
console.log('delete', id);
closeMenu();
}}
onClose={closeMenu}
/>
</div>
<OptionsMenuPortal
open={menu.open}
style={style ?? undefined}
containerRef={containerRef}
categoryId={menu.categoryId}
getCategoryName={getItemTitle}
onEdit={() => {
setIsEditOpen(true);
closeMenu();
}}
onDelete={(id) => {
console.log('delete', id);
closeMenu();
}}
onClose={closeMenu}
/>
Comment on lines +82 to +97
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

옵션 메뉴에 실제 카테고리명 전달 + style 전달 단순화

상단 수정에 맞춰 getCategoryName을 넘기고, style ?? undefined는 불필요합니다.

-      <OptionsMenuPortal
-        open={menu.open}
-        style={style ?? undefined}
+      <OptionsMenuPortal
+        open={menu.open}
+        style={style}
         containerRef={containerRef}
         categoryId={menu.categoryId}
-        getCategoryName={getItemTitle}
+        getCategoryName={getCategoryName}
📝 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
<OptionsMenuPortal
open={menu.open}
style={style ?? undefined}
containerRef={containerRef}
categoryId={menu.categoryId}
getCategoryName={getItemTitle}
onEdit={() => {
setIsEditOpen(true);
closeMenu();
}}
onDelete={(id) => {
console.log('delete', id);
closeMenu();
}}
onClose={closeMenu}
/>
<OptionsMenuPortal
open={menu.open}
style={style}
containerRef={containerRef}
categoryId={menu.categoryId}
getCategoryName={getCategoryName}
onEdit={() => {
setIsEditOpen(true);
closeMenu();
}}
onDelete={(id) => {
console.log('delete', id);
closeMenu();
}}
onClose={closeMenu}
/>
🤖 Prompt for AI Agents
In apps/client/src/pages/remind/Remind.tsx around lines 82 to 97, the
OptionsMenuPortal should receive the actual category name and the style prop
should be simplified; replace the getCategoryName={getItemTitle} prop with
categoryName={getItemTitle(menu.categoryId)} (or the equivalent call to resolve
the name before passing) and change style={style ?? undefined} to simply
style={style} (or remove the prop when style is falsy) so the component gets a
string name and a direct style value.


{isEditOpen && (
<div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
Expand Down
Loading
Loading