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
56 changes: 56 additions & 0 deletions apps/client/src/pages/myBookmark/MyBookmark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,26 @@ import {
useGetBookmarkArticles,
useGetBookmarkUnreadArticles,
} from './apis/queries';
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';

Comment on lines +7 to 12
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue

REMIND_MOCK_DATA ์˜์กด ์ œ๊ฑฐ ๋ฐ ๋ถ๋งˆํฌ ๋ฐ์ดํ„ฐ๋กœ ์ด๋ฆ„/ํƒ€์ดํ‹€ ๋งคํ•‘ํ•˜์„ธ์š”.

๋ถ๋งˆํฌ ํŽ˜์ด์ง€์—์„œ ๋ฆฌ๋งˆ์ธ๋“œ ๋ชฉ์—…์„ ์ฐธ์กฐํ•˜๋ฉด ์ž˜๋ชป๋œ ํ…์ŠคํŠธ๊ฐ€ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ๋ถ๋งˆํฌ API ์‘๋‹ต(articles)์˜ articleIdโ†’ํ‘œ์‹œ ๋ฌธ์ž์—ด(URL/์ œ๋ชฉ) ๋งคํ•‘์„ ๊ตฌ์„ฑํ•ด ์‚ฌ์šฉํ•˜์„ธ์š”.

์ ์šฉ ์˜ˆ:

-import { useState } from 'react';
+import { useMemo, useState } from 'react';
@@
-import { REMIND_MOCK_DATA } from '@pages/remind/constants';
-  const getBookmarkTitle = (id: number | null) =>
-    id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? '');
+  const getBookmarkTitle = (id: number | null) =>
+    id == null ? '' : (titleById.get(id) ?? '');

์ถ”๊ฐ€๋กœ ์•„๋ž˜ ๋งคํ•‘์„ ํŒŒ์ผ ๋‚ด ์ ์ ˆํ•œ ์œ„์น˜์— ์„ ์–ธํ•˜์„ธ์š”(์˜ˆ: ์ฟผ๋ฆฌ ํ›… ์•„๋ž˜):

// ์ถ”๊ฐ€ ์ฝ”๋“œ (์„ ํƒํ•œ ์œ„์น˜์— ์‚ฝ์ž…)
const titleById = useMemo(() => {
  const m = new Map<number, string>();
  readArticles?.articles?.forEach((a) => m.set(a.articleId, a.url || a.memo || ''));
  unreadArticles?.articles?.forEach((a) => {
    if (!m.has(a.articleId)) m.set(a.articleId, a.url || a.memo || '');
  });
  return m;
}, [readArticles?.articles, unreadArticles?.articles]);
๐Ÿค– Prompt for AI Agents
apps/client/src/pages/myBookmark/MyBookmark.tsx lines 7-12: The file imports
REMIND_MOCK_DATA and uses mock reminder labels causing wrong text on the
bookmark page; remove that dependency and instead build a runtime map from the
real bookmark API responses (readArticles and unreadArticles) that maps
articleId โ†’ display string (prefer url || memo || title or empty string) using
useMemo, then replace all usages of REMIND_MOCK_DATA lookups with lookups into
this titleById map so displayed names come from actual article data.

const MyBookmark = () => {
const [activeBadge, setActiveBadge] = useState<'all' | 'notRead'>('all');
const [isEditOpen, setIsEditOpen] = useState(false);

const {
state: menu,
open: openMenu,
close: closeMenu,
style,
containerRef,
} = useAnchoredMenu((anchor) => belowOf(anchor, 8));

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);
Expand All @@ -18,6 +35,7 @@ const MyBookmark = () => {
return (
<div className="flex flex-col py-[5.2rem] pl-[8rem]">
<p className="head3">๋‚˜์˜ ๋ถ๋งˆํฌ</p>

<div className="mt-[3rem] flex gap-[2.4rem]">
<Badge
text="์ „์ฒด๋ณด๊ธฐ"
Expand All @@ -44,6 +62,10 @@ const MyBookmark = () => {
content={article.memo}
// category={article.category.categoryName}
date={new Date(article.createdAt).toLocaleDateString('ko-KR')}
onClick={() => {}}
onOptionsClick={(e) =>
openMenu(article.articleId, e.currentTarget)
}
/>
))}

Expand All @@ -56,9 +78,43 @@ const MyBookmark = () => {
content={article.memo}
// category={article.}
date={new Date(article.createdAt).toLocaleDateString('ko-KR')}
onClick={() => {}}
onOptionsClick={(e) =>
openMenu(article.articleId, e.currentTarget)
}
/>
))}

<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>

{isEditOpen && (
<div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
<div
className="absolute inset-0 bg-black/60 backdrop-blur-[2px]"
onClick={() => setIsEditOpen(false)}
/>
<div className="absolute inset-0 flex items-center justify-center p-4">
{/* ํ•„์š”ํ•˜๋ฉด menu.categoryId๋ฅผ ๋ชจ๋‹ฌ์— ์ „๋‹ฌ */}
<CardEditModal onClose={() => setIsEditOpen(false)} />
</div>
</div>
)}
</div>
);
};
Expand Down
51 changes: 50 additions & 1 deletion apps/client/src/pages/remind/Remind.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import { Badge, Card } from '@pinback/design-system/ui';
import { useState } from 'react';
import { Badge, Card } 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';
Comment on lines +2 to +7
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue

REMIND_MOCK_DATA ์ œ๊ฑฐ ๋ฐ ์นดํ…Œ๊ณ ๋ฆฌ ์ด๋ฆ„์€ ์‹ค์ œ ์‘๋‹ต์œผ๋กœ ๋งคํ•‘ํ•˜์„ธ์š”.

์ด ํŒŒ์ผ์—์„œ๋Š” ๋ฉ”๋‰ด๊ฐ€ ์นดํ…Œ๊ณ ๋ฆฌ ๊ธฐ์ค€์œผ๋กœ ์—ด๋ฆฌ๋ฏ€๋กœ, ๋ชฉ์—…์˜ โ€œ์•„์ดํ…œ idโ€๋กœ ์ œ๋ชฉ์„ ์ฐพ๋Š” ํ˜„ ๊ตฌํ˜„์€ ๋ถˆ์ผ์น˜์ž…๋‹ˆ๋‹ค. ์‘๋‹ต์˜ categoryId โ†’ categoryName ๋งต์„ ๊ตฌ์„ฑํ•ด ์‚ฌ์šฉํ•˜์„ธ์š”.

-import { Badge, Card } from '@pinback/design-system/ui';
+import { Badge, Card } from '@pinback/design-system/ui';
@@
-import { REMIND_MOCK_DATA } from './constants';
-  const getItemTitle = (id: number | null) =>
-    id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? '');
+  const getCategoryName = (id: number | null) =>
+    id == null ? '' : (categoryNameById.get(id) ?? '');

์ถ”๊ฐ€๋กœ ์•„๋ž˜ ๋งคํ•‘์„ ์„ ์–ธํ•˜์„ธ์š”:

// ์ถ”๊ฐ€ ์ฝ”๋“œ (์„ ํƒํ•œ ์œ„์น˜์— ์‚ฝ์ž…)
import { useMemo } from 'react';

const categoryNameById = useMemo(() => {
  const m = new Map<number, string>();
  data?.articles?.forEach((a) => m.set(a.category.categoryId, a.category.categoryName));
  return m;
}, [data?.articles]);

๊ทธ๋ฆฌ๊ณ  Portal prop ์ด๋ฆ„๊ณผ ํ˜ธ์ถœ๋ถ€๋ฅผ ๋งž์ถฐ ์ฃผ์„ธ์š”:

-          getCategoryName={getItemTitle}
+          getCategoryName={getCategoryName}
๐Ÿค– Prompt for AI Agents
In apps/client/src/pages/remind/Remind.tsx around lines 2 to 7, remove the
REMIND_MOCK_DATA import and stop using mock item ids to derive menu titles;
instead import useMemo and build a Map of categoryId โ†’ categoryName from the
real response (iterate data?.articles and map each a.category.categoryId to
a.category.categoryName) and use that map to look up titles when opening the
category-based menu; add the useMemo declaration as shown in the review and
depend on [data?.articles]; finally, align the Portal prop name and all its call
sites so the OptionsMenuPortal prop and its invocation match (rename either prop
or callers consistently).

import { useGetRemindArticles } from './apis/queries';
import { formatLocalDateTime } from '@shared/utils/formatDateTime';

const Remind = () => {
const [isEditOpen, setIsEditOpen] = useState(false);

const {
state: menu,
open: openMenu,
close: closeMenu,
style,
containerRef,
} = useAnchoredMenu((anchor) => belowOf(anchor, 8));

const getItemTitle = (id: number | null) =>
id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? '');
const [activeBadge, setActiveBadge] = useState('notRead');
const formattedDate = formatLocalDateTime();

Expand Down Expand Up @@ -46,9 +63,41 @@ const Remind = () => {
content={article.memo}
timeRemaining={article.remindAt}
category={article.category.categoryName}
onOptionsClick={(e) =>
openMenu(article.category.categoryId, e.currentTarget)
}
/>
))}

<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>

{isEditOpen && (
<div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
<div
className="absolute inset-0 bg-black/60 backdrop-blur-[2px]"
onClick={() => setIsEditOpen(false)}
/>
<div className="absolute inset-0 flex items-center justify-center p-4">
<CardEditModal onClose={() => setIsEditOpen(false)} />
</div>
</div>
)}
Comment on lines +90 to 100
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

๐Ÿ› ๏ธ Refactor suggestion

๋ชจ๋‹ฌ z-index ์ƒํ–ฅ

Portal์ด zIndex 10000์ด๋ฏ€๋กœ ๋ชจ๋‹ฌ ๋ž˜ํผ๋ฅผ ๊ทธ๋ณด๋‹ค ๋†’๊ฒŒ ์žก์•„ ๋‘๋Š” ํŽธ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

-        <div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
+        <div className="fixed inset-0 z-[11000]" aria-modal="true" role="dialog">
๐Ÿ“ 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
{isEditOpen && (
<div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
<div
className="absolute inset-0 bg-black/60 backdrop-blur-[2px]"
onClick={() => setIsEditOpen(false)}
/>
<div className="absolute inset-0 flex items-center justify-center p-4">
<CardEditModal onClose={() => setIsEditOpen(false)} />
</div>
</div>
)}
{isEditOpen && (
<div className="fixed inset-0 z-[11000]" aria-modal="true" role="dialog">
<div
className="absolute inset-0 bg-black/60 backdrop-blur-[2px]"
onClick={() => setIsEditOpen(false)}
/>
<div className="absolute inset-0 flex items-center justify-center p-4">
<CardEditModal onClose={() => setIsEditOpen(false)} />
</div>
</div>
)}
๐Ÿค– Prompt for AI Agents
In apps/client/src/pages/remind/Remind.tsx around lines 90 to 100, the modal
wrapper uses z-[1000] which is lower than the portal z-index (10000); update the
wrapper's z-index to be higher than 10000 (e.g., z-[10001] or a suitably higher
Tailwind z class) so the modal appears above the portal, ensuring the backdrop
and modal content render on top; adjust the className on the outer fixed div
accordingly and keep the rest of the structure intact.

</div>
);
};
Expand Down
5 changes: 5 additions & 0 deletions apps/client/src/shared/utils/anchorPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ export function rightOf(anchor: HTMLElement, gap = 8) {
const r = anchor.getBoundingClientRect();
return { top: r.top, left: r.right + gap };
}

export const belowOf = (anchor: HTMLElement, gap = 8) => {
const r = anchor.getBoundingClientRect();
return { top: r.bottom + gap, left: r.left };
};
18 changes: 14 additions & 4 deletions packages/design-system/src/components/card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ type BaseProps = {
content?: string;
category?: string;
imageUrl?: string;
onClick?: () => void;
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
};

type RemindProps = BaseProps & {
Expand All @@ -22,14 +24,22 @@ type BookmarkProps = BaseProps & {

export type CardProps = RemindProps | BookmarkProps;

const Card = (props: CardProps) => {
const { type } = props;
const Card = (
props: CardProps & {
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
}
) => {
const { type, onOptionsClick } = props;

return (
<>
{type === 'remind' && <RemindCard {...props} />}
{type === 'remind' && (
<RemindCard {...props} onOptionsClick={onOptionsClick} />
)}

{type === 'bookmark' && <MyBookmarkCard {...props} />}
{type === 'bookmark' && (
<MyBookmarkCard {...props} onOptionsClick={onOptionsClick} />
)}
</>
);
};
Expand Down
4 changes: 4 additions & 0 deletions packages/design-system/src/components/card/MyBookmarkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ interface MyBookmarkCardProps {
category?: string;
imageUrl?: string;
date: string;
onClick?: () => void;
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
Comment on lines +11 to +12
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

์ œ๊ฐ€ ํ•ด๋‹น ์˜ต์…˜ ํด๋ฆญ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋†“์ณค๋„ค์š”..๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!!
onClick์€ ์ „์ฒด ์นด๋“œ๋ฅผ ๋ˆ„๋ฅผ ๋•Œ ์‹คํ–‰๋˜๋Š” ๊ฑด๊ฐ€์š”??

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

๋„ต๋„ต-!

}

const MyBookmarkCard = ({
Expand All @@ -16,6 +18,7 @@ const MyBookmarkCard = ({
category,
imageUrl,
date,
onOptionsClick,
}: MyBookmarkCardProps) => {
return (
<BaseCard>
Expand All @@ -38,6 +41,7 @@ const MyBookmarkCard = ({
type="button"
aria-label="์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ธ"
className="cursor-pointer self-start"
onClick={(e) => onOptionsClick?.(e)}
>
<Icon name="ic_details_category" />
</button>
Expand Down
5 changes: 4 additions & 1 deletion packages/design-system/src/components/card/RemindCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ interface RemindCardProps {
category?: string;
imageUrl?: string;
timeRemaining: string;
onClick?: () => void;
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
}

const RemindCard = ({
Expand All @@ -16,6 +18,7 @@ const RemindCard = ({
category,
imageUrl,
timeRemaining,
onOptionsClick,
}: RemindCardProps) => {
return (
<BaseCard>
Expand Down Expand Up @@ -46,6 +49,7 @@ const RemindCard = ({
type="button"
aria-label="์นดํ…Œ๊ณ ๋ฆฌ ์ƒ์„ธ"
className="cursor-pointer self-start"
onClick={(e) => onOptionsClick?.(e)}
>
<Icon name="ic_details_category" />
</button>
Expand All @@ -54,7 +58,6 @@ const RemindCard = ({
{content}
</p>

{/* TODO: ์นดํ…Œ๊ณ ๋ฆฌ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ต์ฒด */}
<span className="bg-category-red-bg caption2-sb text-category-red-text h-[2.2rem] w-[6.2rem] rounded-[0.4rem] px-[0.8rem] py-[0.2rem]">
{category}
</span>
Expand Down
Loading