Conversation
WalkthroughMyBookmark와 Remind 페이지에 카드별 앵커드 옵션 메뉴와 카드 편집 모달을 추가했다. 디자인 시스템의 Card/RemindCard/MyBookmarkCard에 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 사용자
participant C as Card (Remind/MyBookmark)
participant P as Page 컴포넌트
participant H as useAnchoredMenu
participant M as OptionsMenuPortal
participant E as CardEditModal
U->>C: 옵션 버튼 클릭
C-->>P: onOptionsClick(e)
P->>H: openMenu({ id, anchor: e.currentTarget, pos: belowOf })
H-->>P: menu state (open, top,left,context)
P->>M: 렌더(제목, id, onEdit, onDelete, onClose)
alt Edit 선택
U->>M: Edit 클릭
M-->>P: onEdit()
P->>H: closeMenu()
P->>E: isEditOpen = true (모달 오픈)
U->>E: 저장/닫기
E-->>P: onClose()
P->>E: isEditOpen = false
else Delete 선택
U->>M: Delete 클릭
M-->>P: onDelete()
P->>H: closeMenu()
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/design-system/src/components/card/RemindCard.tsx (1)
48-55: 옵션 버튼 클릭 시 카드 onClick 동시 실행 가능성 → 이벤트 전파 차단 필요옵션 버튼 클릭이 상위(Card/컨테이너)의 onClick까지 전파될 수 있어 UX가 깨질 수 있습니다. 전파를 명시적으로 막아주세요. 또한 aria-label은 실제 동작에 맞게 수정 권장.
- <button + <button type="button" - aria-label="카테고리 상세" + aria-label="옵션 메뉴 열기" className="cursor-pointer self-start" - onClick={(e) => onOptionsClick?.(e)} + onClick={(e) => { + e.stopPropagation(); + onOptionsClick?.(e); + }} >packages/design-system/src/components/card/MyBookmarkCard.tsx (1)
40-47: 옵션 버튼 클릭 시 상위 onClick 전파 차단 필요 + 라벨 정정RemindCard와 동일 이슈입니다. 전파 차단 및 접근성 라벨 보정 제안.
- <button + <button type="button" - aria-label="카테고리 상세" + aria-label="옵션 메뉴 열기" className="cursor-pointer self-start" - onClick={(e) => onOptionsClick?.(e)} + onClick={(e) => { + e.stopPropagation(); + onOptionsClick?.(e); + }} >
🧹 Nitpick comments (16)
apps/client/src/shared/utils/anchorPosition.ts (1)
6-9: belowOf 구현은 적절. 다만 뷰포트 충돌/클리핑 대응 추가 권장현재는 우하단으로만 배치되어 우/하단 가장자리에서 메뉴가 화면 밖으로 벗어날 수 있습니다. 추후 flip(위/아래 전환) + clamp(좌/우 경계 보정)를 넣거나 Floating UI 같은 라이브러리로 대체를 고려해주세요.
예시(간단 보정 타입 추가만):
+export type AnchorPos = { top: number; left: number }; -export const belowOf = (anchor: HTMLElement, gap = 8) => { +export const belowOf = (anchor: HTMLElement, gap = 8): AnchorPos => { const r = anchor.getBoundingClientRect(); return { top: r.bottom + gap, left: r.left }; };packages/design-system/src/components/card/RemindCard.tsx (2)
34-36: 이미지 대체 텍스트 누락동적 이미지에 alt가 없어 스크린리더 접근성이 떨어집니다. 제목 기반 혹은 빈 alt라도 지정해주세요.
- <img src={imageUrl} className="h-full w-full object-cover" /> + <img src={imageUrl} alt={title || ''} className="h-full w-full object-cover" />
11-13: onClick Prop 선언만 있고 미사용현재 카드 자체 클릭 동작이 연결되어 있지 않습니다. 의도라면 타입에서 제거, 사용할 예정이라면 루트 컨테이너(BaseCard 등)에 전달해주세요.
-const RemindCard = ({ +const RemindCard = ({ title, content, category, imageUrl, timeRemaining, - onOptionsClick, + onOptionsClick, + onClick, }: RemindCardProps) => { return ( - <BaseCard> + <BaseCard onClick={onClick}>Also applies to: 21-22
packages/design-system/src/components/card/Card.tsx (2)
27-31: onOptionsClick 타입 중복 선언 제거BaseProps에 이미 존재하므로 함수 시그니처에서 재정의는 불필요합니다.
-const Card = ( - props: CardProps & { - onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void; - } -) => { +const Card = (props: CardProps) => {
36-42: 중복 전달 제거
{...props}에onOptionsClick이 포함되어 있어 명시적 재전달은 중복입니다.- {type === 'remind' && ( - <RemindCard {...props} onOptionsClick={onOptionsClick} /> - )} + {type === 'remind' && <RemindCard {...props} />} - {type === 'bookmark' && ( - <MyBookmarkCard {...props} onOptionsClick={onOptionsClick} /> - )} + {type === 'bookmark' && <MyBookmarkCard {...props} />}packages/design-system/src/components/card/MyBookmarkCard.tsx (2)
26-28: 이미지 대체 텍스트 누락동적 이미지 alt 추가를 권장합니다.
- <img src={imageUrl} className="h-full w-full object-cover" /> + <img src={imageUrl} alt={title || ''} className="h-full w-full object-cover" />
11-13: onClick Prop 미사용의도 확인 필요. 사용할 계획이라면 루트 컨테이너에 전달하세요.
-const MyBookmarkCard = ({ +const MyBookmarkCard = ({ title, content, category, imageUrl, date, - onOptionsClick, + onOptionsClick, + onClick, }: MyBookmarkCardProps) => { return ( - <BaseCard> + <BaseCard onClick={onClick}>Also applies to: 21-22
apps/client/src/pages/myBookmark/MyBookmark.tsx (4)
52-54: 의미 없는 카드 onClick 전달 제거빈 핸들러는 유지 비용만 늘립니다. 필요 시점에 추가하는 편이 낫습니다.
- onClick={() => {}} onOptionsClick={(e) => openMenu(data.id, e.currentTarget)}
64-71: 메뉴 닫기 이중 호출 제거OptionsMenuPortal 내부에서 onClose를 이미 호출합니다. 중복 close는 불필요합니다.
- onEdit={() => { - setIsEditOpen(true); - closeMenu(); - }} - onDelete={(id) => { - console.log('delete', id); - closeMenu(); - }} + onEdit={() => setIsEditOpen(true)} + onDelete={(id) => { + console.log('delete', id); + }}
75-86: 모달 z-index가 메뉴(10000)보다 낮음메뉴가 우선 렌더되면 레이어링 깜빡임이 발생할 수 있습니다. 모달 z를 더 높게 가져가세요.
- {isEditOpen && ( - <div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog"> + {isEditOpen && ( + <div className="fixed inset-0 z-[11000]" aria-modal="true" role="dialog">추가로 ESC 닫기, 포커스 트랩은 추후 접근성 보강 시 고려 바랍니다.
2-2: 데이터 소스 확인 요청: REMIND_MOCK_DATA 사용북마크 페이지에서 리마인드 목업을 사용하고 있습니다. 의도된 임시 연결인지, 별도 북마크 데이터 소스로 교체해야 하는지 확인이 필요합니다.
원하시면 북마크 전용 목업/타입 분리를 위한 작은 PR도 도와드릴게요.
Also applies to: 21-22
apps/client/src/pages/remind/Remind.tsx (5)
13-19: 앵커 메뉴 UX 보완: ESC 닫기·경계 충돌 처리·rAF 스로틀 제안현재 훅은 스크롤/리사이즈/바깥클릭만 처리합니다. 다음을 추가하면 견고해집니다.
- Escape로 닫기(keydown)
- 메뉴가 뷰포트 밖으로 나갈 때 좌표 클램프/플립
- 스크롤 중 연속 reflow를 rAF로 스로틀
예시(훅 수정안):
// useAnchoredMenu.ts 일부 const rafRef = useRef<number | null>(null); useEffect(() => { if (!state.open) return; const syncPos = () => { if (rafRef.current != null) return; rafRef.current = requestAnimationFrame(() => { rafRef.current = null; if (!state.anchorEl) return; const base = getPos(state.anchorEl); const menuEl = containerRef.current; if (!menuEl) { setState((s) => ({ ...s, pos: base })); return; } const w = menuEl.offsetWidth; const h = menuEl.offsetHeight; const left = Math.max(8, Math.min(window.innerWidth - w - 8, base.left)); const top = Math.max(8, Math.min(window.innerHeight - h - 8, base.top)); setState((s) => ({ ...s, pos: { top, left } })); }); }; const onKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') close(); }; window.addEventListener('scroll', syncPos, true); window.addEventListener('resize', syncPos); document.addEventListener('keydown', onKeyDown); // ... return () => { window.removeEventListener('scroll', syncPos, true); window.removeEventListener('resize', syncPos); document.removeEventListener('keydown', onKeyDown); if (rafRef.current != null) cancelAnimationFrame(rafRef.current); }; }, [state.open, state.anchorEl, close, getPos]);원하시면 훅/유틸에 위 개선을 반영해 별 PR로 올려드릴게요.
21-22: getItemTitle는 매 렌더 O(n) 탐색 → Map 캐시로 O(1)로 개선카드 수가 늘면 find 비용이 누적됩니다. Map을 메모이즈해 상수시간 조회로 바꾸는 것을 권장합니다.
적용 diff(해당 범위):
- const getItemTitle = (id: number | null) => - id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? ''); + const getItemTitle = (id: number | null) => + id == null ? '' : (titleById.get(id) ?? '');추가 코드(파일 상단 import, 그리고 컴포넌트 내부 어디든 1회 정의):
// import { useMemo, useState } from 'react'; const titleById = useMemo( () => new Map(REMIND_MOCK_DATA.map((d) => [d.id, d.title] as const)), [] );
51-51: 옵션 버튼 클릭 버블링/타입 확인Card 자체 onClick이 있다면 옵션 버튼 클릭이 버블링되어 카드 클릭이 함께 실행될 수 있습니다. 내부 버튼에서 e.stopPropagation() 처리되어 있는지 확인 부탁드립니다. 또한 명시적으로 타입을 적어 가독성을 높일 수 있습니다.
onOptionsClick={(e: React.MouseEvent<HTMLElement>) => openMenu(data.id, e.currentTarget as HTMLElement) }
55-70: 메뉴 콜백 시그니처/순서, 스타일 전달 간소화 + z-index 정책 제안
- style ?? undefined는 중복입니다.
- onEdit에서 메뉴를 먼저 닫고 모달을 여는 순서가 플리커를 줄입니다.
- Portal z-index(10000)와 모달 오버레이(1000)가 충돌할 수 있으니 계층 정책을 정수 단위 토큰으로 통일하는 것을 추천합니다.
적용 diff:
<OptionsMenuPortal open={menu.open} - style={style ?? undefined} + style={style} containerRef={containerRef} categoryId={menu.categoryId} getCategoryName={getItemTitle} - onEdit={() => { - setIsEditOpen(true); - closeMenu(); - }} - onDelete={(id) => { + onEdit={(id, _name) => { + closeMenu(); + setIsEditOpen(true); + }} + onDelete={(id, _name) => { console.log('delete', id); closeMenu(); }} onClose={closeMenu} />추가로, z-index를 design token(e.g., --z-menu, --z-modal)로 관리하면 충돌 예방에 좋습니다.
73-83: 중복 dialog 역할 제거·ESC 닫기·모달이 메뉴보다 위에 렌더되도록 조정외부 래퍼와 CardEditModal 모두 role="dialog"/aria-modal="true"를 가지면 스크린 리더 혼란이 생길 수 있습니다. 래퍼의 역할은 제거하고, ESC 닫기 및 z-index를 메뉴(10000)보다 높게 올리는 것을 권장합니다.
적용 diff:
- {isEditOpen && ( - <div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog"> + {isEditOpen && ( + <div + className="fixed inset-0 z-[11000]" + tabIndex={-1} + onKeyDown={(e) => { + if (e.key === 'Escape') setIsEditOpen(false); + }} + > <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> )}보완사항(옵션):
- 모달 오픈 시 body 스크롤 잠금
// 컴포넌트 내 useEffect(() => { if (!isEditOpen) return; const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = prev; }; }, [isEditOpen]);
- 포커스 트랩/초점 복귀는 디자인시스템 Modal 컴포넌트가 있다면 그걸 사용하는 것이 가장 안전합니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/client/src/pages/myBookmark/MyBookmark.tsx(2 hunks)apps/client/src/pages/remind/Remind.tsx(2 hunks)apps/client/src/shared/utils/anchorPosition.ts(1 hunks)packages/design-system/src/components/card/Card.tsx(2 hunks)packages/design-system/src/components/card/MyBookmarkCard.tsx(3 hunks)packages/design-system/src/components/card/RemindCard.tsx(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/client/src/pages/myBookmark/MyBookmark.tsx (5)
apps/client/src/shared/hooks/useAnchoredMenu.ts (1)
useAnchoredMenu(13-86)apps/client/src/shared/utils/anchorPosition.ts (1)
belowOf(6-9)apps/client/src/pages/remind/constants/index.ts (1)
REMIND_MOCK_DATA(1-114)apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx (1)
OptionsMenuPortal(15-45)apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (1)
CardEditModal(22-169)
apps/client/src/pages/remind/Remind.tsx (4)
apps/client/src/shared/hooks/useAnchoredMenu.ts (1)
useAnchoredMenu(13-86)apps/client/src/shared/utils/anchorPosition.ts (1)
belowOf(6-9)apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx (1)
OptionsMenuPortal(15-45)apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (1)
CardEditModal(22-169)
🔇 Additional comments (4)
apps/client/src/pages/remind/Remind.tsx (4)
2-6: 새 Portal/앵커 메뉴 도입 방향성 좋습니다디자인시스템/Portal/훅 조합으로 레이아웃 이슈를 분리한 점 좋습니다. 임포트 구성도 문제 없어 보입니다.
10-11: 배지 상태를 유니온 타입으로 고정한 점 👍의도 명확하고 오타/분기 실수를 줄여줍니다. 모달 상태도 단순합니다.
31-31: 배지 토글만 있고 목록 필터링/카운트 동기화는 없음 — 의도 확인 요청현재 activeBadge는 UI 하이라이트만 변경합니다. 실제 리스트 필터/카운트 연동이 스코프 외면 OK, 아니라면 데이터 필터링과 countNum 일치 여부 확인 부탁드립니다.
Also applies to: 37-37
42-42: 카드 그리드 스크롤 컨테이너 구성 적절Portal 고정 포지셔닝과 캡처 스크롤 리스너 조합과 잘 맞습니다.
constantly-dev
left a comment
There was a problem hiding this comment.
수고하셨습니다 이제 API 연결만 하면 되겠네요!!!
조금만 더 화이팅~~ 👍
| content={data.content} | ||
| category={data.category} | ||
| date="2024.08.15" | ||
| onClick={() => {}} |
| onClick?: () => void; | ||
| onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void; |
There was a problem hiding this comment.
제가 해당 옵션 클릭 인터페이스를 놓쳤네요..감사합니다!!
onClick은 전체 카드를 누를 때 실행되는 건가요??
into feat/#81/card-edit-delete
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/client/src/pages/myBookmark/MyBookmark.tsx (1)
41-52: 안 읽음 배지 카운트 소스 변경 필요MyBookmark.tsx에서 "안 읽음" 배지가 readArticles?.totalUnreadArticle을 참조하고 있으므로 unreadArticles?.totalUnreadArticle로 교체하세요.
- countNum={readArticles?.totalUnreadArticle || 0} + countNum={unreadArticles?.totalUnreadArticle || 0}
♻️ Duplicate comments (1)
apps/client/src/pages/myBookmark/MyBookmark.tsx (1)
65-69: 빈 onClick 핸들러 제거 또는 TODO 명시빈 함수는 불필요한 할당을 유발합니다. 필요 없다면 prop 자체를 제거하거나 TODO로 명확히 남겨주세요.
- onClick={() => {}}
🧹 Nitpick comments (2)
apps/client/src/pages/myBookmark/MyBookmark.tsx (1)
88-103: 작은 정리: style ?? undefined 제거 및 onEdit 실행 순서 조정 권장
style ?? undefined는 중복입니다.- 모달 열 때 메뉴를 먼저 닫으면 z-index 간 레이스를 줄일 수 있습니다.
- style={style ?? undefined} + style={style} @@ - onEdit={() => { - setIsEditOpen(true); - closeMenu(); - }} + onEdit={() => { + closeMenu(); + setIsEditOpen(true); + }}apps/client/src/pages/remind/Remind.tsx (1)
72-87: 작은 정리: style ?? undefined 제거 및 onEdit 순서 조정 권장MyBookmark와 동일한 사유입니다.
- style={style ?? undefined} + style={style} @@ - onEdit={() => { - setIsEditOpen(true); - closeMenu(); - }} + onEdit={() => { + closeMenu(); + setIsEditOpen(true); + }}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/client/src/pages/myBookmark/MyBookmark.tsx(4 hunks)apps/client/src/pages/remind/Remind.tsx(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/client/src/pages/myBookmark/MyBookmark.tsx (5)
apps/client/src/shared/hooks/useAnchoredMenu.ts (1)
useAnchoredMenu(13-86)apps/client/src/shared/utils/anchorPosition.ts (1)
belowOf(6-9)apps/client/src/pages/remind/constants/index.ts (1)
REMIND_MOCK_DATA(1-114)apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx (1)
OptionsMenuPortal(15-45)apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (1)
CardEditModal(22-169)
apps/client/src/pages/remind/Remind.tsx (5)
apps/client/src/shared/hooks/useAnchoredMenu.ts (1)
useAnchoredMenu(13-86)apps/client/src/shared/utils/anchorPosition.ts (1)
belowOf(6-9)apps/client/src/pages/remind/constants/index.ts (1)
REMIND_MOCK_DATA(1-114)apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx (1)
OptionsMenuPortal(15-45)apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (1)
CardEditModal(22-169)
🔇 Additional comments (4)
apps/client/src/pages/myBookmark/MyBookmark.tsx (2)
17-23: 앵커드 메뉴 훅 연동 방식 깔끔합니다.Portal + fixed 좌표로 스크롤/리사이즈 대응까지 정리된 패턴이라 재사용성 좋습니다.
106-117: 모달 z-index를 메뉴보다 높게 설정하세요파일: apps/client/src/pages/myBookmark/MyBookmark.tsx (lines 106–117)
OptionsMenuPortal(zIndex 10000)보다 모달 래퍼(z-[1000])가 낮아 메뉴가 모달 위로 올라올 수 있으니 z 값을 증가시켜 충돌을 방지하세요.- <div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog"> + <div className="fixed inset-0 z-[11000]" aria-modal="true" role="dialog">접근성(스크린리더/포커스 트랩) 요구가 있으면 모달 오픈 시 body 스크롤 잠금도 함께 검토하세요.
apps/client/src/pages/remind/Remind.tsx (2)
14-20: 앵커드 메뉴 훅 연동이 일관적이고 재사용성 좋습니다.Remind/MyBookmark 양쪽 모두 동일 패턴으로 맞춘 점 좋습니다.
66-68: openMenu 인자는 적절 — timeRemaining 포맷 확인 필요
- openMenu(article.category.categoryId, e.currentTarget)는 의도에 부합합니다. (apps/client/src/pages/remind/Remind.tsx:66-68)
- timeRemaining={article.remindAt}이 디자인 시스템 Card가 기대하는 형식(예: '2시간 남음' 문자열 vs ISO 8601 Datetime)과 일치하는지 확인하십시오. 레포지토리 검사 스크립트가 실패해 props 정의를 확인하지 못했습니다 — components/Card 또는 components/RemindCard에서 timeRemaining prop 타입을 확인하거나 파일 경로를 제공해 재검증하십시오.
| 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'; | ||
|
|
There was a problem hiding this comment.
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.
| 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'; |
There was a problem hiding this comment.
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).
| <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> | ||
| )} | ||
| </div> |
There was a problem hiding this comment.
🛠️ 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.
| {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.
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
📷 Screenshot
2025-09-12.9.34.41.mov
Summary by CodeRabbit
New Features
Style