[Feature/#209] AI 요약하기 디자인 구조 변경#223
Conversation
📝 WalkthroughWalkthrough대시보드 오버뷰의 AI 요약 기능을 드로어 패널에서 카드형 UI로 전환합니다. 데이터 모델, 공유 스켈레톤, 카드 컴포넌트, 인쇄/PDF 기능을 추가하고 대시보드에 통합한 뒤, 섹션별 로딩 상태를 통일하며 스타일 토큰을 확장합니다. ChangesAI 요약 카드 기능 구현 및 대시보드 통합
Sequence DiagramsequenceDiagram
participant User
participant OverviewDashboard
participant OverviewAiSummaryCard
participant AiReportBody
participant downloadAiSummaryPdf
participant PrintReport
participant Browser as Browser Print Dialog
User->>OverviewDashboard: 대시보드 접속
OverviewDashboard->>OverviewAiSummaryCard: 카드 렌더링
OverviewAiSummaryCard->>User: 요약 하이라이트 표시
alt 사용자가 펼치기 클릭
User->>OverviewAiSummaryCard: 토글 버튼 클릭
OverviewAiSummaryCard->>AiReportBody: AnimatePresence 트리거
AiReportBody->>User: 전략/경고/인사이트 섹션 애니메이션 표시
end
alt 사용자가 PDF 다운로드 클릭
User->>OverviewAiSummaryCard: PDF 다운로드 버튼 클릭
OverviewAiSummaryCard->>downloadAiSummaryPdf: TAiReportPrintDocument 전달
downloadAiSummaryPdf->>PrintReport: 정적 마크업 렌더링
PrintReport->>downloadAiSummaryPdf: HTML 반환
downloadAiSummaryPdf->>Browser: 숨김 iframe 주입 및 print() 호출
Browser->>User: 인쇄 대화 표시
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 배포 완료
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/types/dashboard/overview.ts (1)
56-74: 💤 Low value전략 제안과 주의사항 타입 구조 중복
strategySuggestion과warning필드가 동일한{ title: string; content: string }구조를 가지고 있어요. 의미적으로 분리하려는 의도라면 괜찮지만, 공통 타입으로 추출하면 유지보수가 더 쉬울 수 있습니다.♻️ 선택적 리팩토링 제안
+// AI 요약 리포트 섹션 (공통) +export interface IAiReportContentBlock { + title: string; + content: string; +} + // AI 요약 리포트 응답 -export interface IAiReportSection { - title: string; - content: string; -} +export type IAiReportSection = IAiReportContentBlock; export interface IAiReportResponse { label: string; title: string; - strategySuggestion: { - title: string; - content: string; - }; + strategySuggestion: IAiReportContentBlock; sections: IAiReportSection[]; - warning: { - title: string; - content: string; - }; + warning: IAiReportContentBlock; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/types/dashboard/overview.ts` around lines 56 - 74, Extract the duplicated { title: string; content: string } shape into a shared interface (e.g., ITextBlock or ITitleContent) and replace the inline types used by IAiReportResponse.strategySuggestion and IAiReportResponse.warning with that new interface; update IAiReportSection if appropriate to reuse the same interface (references: IAiReportResponse, strategySuggestion, warning, IAiReportSection) so the common structure is defined once for easier maintenance.src/pages/dashboard/overview/OverviewAiSummaryCard.tsx (2)
256-267: 💤 Low value토글 핸들러 구조 단순화 가능
handleOpen,handleCollapse,handleToggle세 개의 콜백으로 분리되어 있는데,handleToggle하나로 충분해 보여요. 현재handleOpen과handleCollapse를 별도로 사용하는 곳이 없다면 제거해도 됩니다.♻️ 단순화 제안
const prefersReducedMotion = useReducedMotion(); const [isExpanded, setIsExpanded] = useState(false); - const handleOpen = useCallback(() => { - setIsExpanded(true); - }, []); - - const handleCollapse = useCallback(() => { - setIsExpanded(false); - }, []); - - const handleToggle = useCallback(() => { - if (isExpanded) handleCollapse(); - else handleOpen(); - }, [handleCollapse, handleOpen, isExpanded]); + const handleToggle = useCallback(() => { + setIsExpanded((prev) => !prev); + }, []);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/dashboard/overview/OverviewAiSummaryCard.tsx` around lines 256 - 267, The code currently defines three callbacks (handleOpen, handleCollapse, handleToggle) but only handleToggle is used; simplify by removing handleOpen and handleCollapse and replace handleToggle with a single useCallback that toggles state via setIsExpanded(prev => !prev) (update its dependency array to [setIsExpanded] or [] if setState is stable), and update any references to handleOpen/handleCollapse to use handleToggle; keep function name handleToggle and ensure it's exported/used wherever the prior handlers were referenced.
311-327: ⚡ Quick win
layoutprop 성능 영향 검토 필요Line 313에서
motion.div에layoutprop을 사용하고 있는데, 이미height를 명시적으로 애니메이션하고 있어요 (line 319, 322).layoutprop은 매 프레임마다 레이아웃 재계산을 유발할 수 있어서, 명시적인 height 애니메이션만으로 충분하다면 제거하는 게 성능상 유리합니다.🔍 확인 방법
layoutprop을 제거해보고 애니메이션이 정상 동작하는지 테스트해보세요:<motion.div key="ai-summary-panel" - layout initial={만약 layout prop이 특정 이유로 필요하다면 (예: 내부 콘텐츠 크기 변화 대응), 주석으로 이유를 남겨두면 좋겠어요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/dashboard/overview/OverviewAiSummaryCard.tsx` around lines 311 - 327, The motion.div with key "ai-summary-panel" is using the layout prop while also animating explicit height in initial/animate/exit (uses prefersReducedMotion, panelTransition, panelExitTransition); remove the layout prop from the motion.div to avoid forced layout recalculations and verify the height/opacity animations still behave correctly, and if you determine layout is necessary for internal content resizing, leave it but add a comment on why layout is required (or consider replacing layout with an explicit measurement strategy).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/dashboard/overview/OverviewAiSummaryCard.tsx`:
- Line 9: OverviewAiSummaryCard currently imports aiReportMockData directly
which couples the component to test data; remove the import and make the
component accept the report data via props (e.g., add a prop like aiReport:
AiReportType) or call a new hook (e.g., useAiReport) that returns { data,
isLoading } so the component no longer depends on aiReportMockData; update the
component signature (OverviewAiSummaryCard) to use the prop or hook and adjust
any callers to pass the mock (or switch to the hook) and create
hooks/dashboard/useAiReport.ts if using the hook approach to encapsulate
fetching.
---
Nitpick comments:
In `@src/pages/dashboard/overview/OverviewAiSummaryCard.tsx`:
- Around line 256-267: The code currently defines three callbacks (handleOpen,
handleCollapse, handleToggle) but only handleToggle is used; simplify by
removing handleOpen and handleCollapse and replace handleToggle with a single
useCallback that toggles state via setIsExpanded(prev => !prev) (update its
dependency array to [setIsExpanded] or [] if setState is stable), and update any
references to handleOpen/handleCollapse to use handleToggle; keep function name
handleToggle and ensure it's exported/used wherever the prior handlers were
referenced.
- Around line 311-327: The motion.div with key "ai-summary-panel" is using the
layout prop while also animating explicit height in initial/animate/exit (uses
prefersReducedMotion, panelTransition, panelExitTransition); remove the layout
prop from the motion.div to avoid forced layout recalculations and verify the
height/opacity animations still behave correctly, and if you determine layout is
necessary for internal content resizing, leave it but add a comment on why
layout is required (or consider replacing layout with an explicit measurement
strategy).
In `@src/types/dashboard/overview.ts`:
- Around line 56-74: Extract the duplicated { title: string; content: string }
shape into a shared interface (e.g., ITextBlock or ITitleContent) and replace
the inline types used by IAiReportResponse.strategySuggestion and
IAiReportResponse.warning with that new interface; update IAiReportSection if
appropriate to reuse the same interface (references: IAiReportResponse,
strategySuggestion, warning, IAiReportSection) so the common structure is
defined once for easier maintenance.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6d61dbea-a7a1-4f30-8a1d-aa9a706dc503
⛔ Files ignored due to path filters (1)
src/assets/icon/common/lightbulb.svgis excluded by!**/*.svgand included bysrc/**
📒 Files selected for processing (5)
src/pages/dashboard/overview/OverviewAiSummaryCard.tsxsrc/pages/dashboard/overview/OverviewCampaignSnapshotCard.tsxsrc/pages/dashboard/overview/OverviewDashboard.tsxsrc/pages/dashboard/overview/aiReport.mock.tssrc/types/dashboard/overview.ts
|
P4: UI 깔끔한 것 같습니다! 혹시 링크 공유, 저장 기능은 제외하는 것일까요?! |
헉! 펼쳤을 때 오른쪽 상단에 추가하겠습니다! |
|
P3: 지금 AI성과요약 카드 열면 애니메이션으로 부드럽게 열리는데, 혹시 카드안에 '전략제안'과 '주의가 필요한 부분'도 더보기 눌렀을때 애니메이션으로 부드럽게 열리도록 똑같이 하면 어떨까요? |
더보기가 굳이 불필요한 요소인 것 같아서 제거하려고 합니다! 텍스트 크기는 전체적으로 크게 수정하겠습니다. 카드 제목보다는 작게 유지해야 위계가 드러날 것 같아서 맞춰서 수정하겠습니다! |
|
좋습니다! 그러면 더보기 없이 바로 텍스트가 다나오도록 변경되는걸까요? |
넵 맞습니다! |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsx (1)
10-10: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winMock 데이터 직접 의존성 제거 필요
이전 리뷰에서 지적된 내용과 동일하게, 컴포넌트가
aiReportMockData를 직접 import해서 사용하고 있어요. 이 구조는 컴포넌트 재사용성을 떨어뜨리고 실제 API 연동 시 수정 범위가 커집니다.데이터를 props로 받거나 커스텀 훅(
useAiReport등)에서 fetch하도록 분리하는 것을 권장해요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsx` at line 10, OverviewAiSummaryCard currently imports aiReportMockData directly; remove this hard dependency by making the component accept the data via props or by delegating fetch to a custom hook. Update the OverviewAiSummaryCard component signature to accept a prop (e.g., aiReport: AiReportType) and replace all uses of aiReportMockData with that prop, or create/use a hook named useAiReport that returns the data and loading/error states and call it from the parent (not the component) if you want composition; ensure PropTypes/TypeScript types (AiReportType) and default/optional handling are added where referenced in OverviewAiSummaryCard so the component no longer imports aiReportMockData.
🧹 Nitpick comments (6)
src/pages/dashboard/overview/sections/OverviewBudgetSection.tsx (1)
14-16: 💤 Low valueimport 경로 스타일 일관성
Line 14는 절대 경로(
@/components/...)를, Line 16은 상대 경로(./OverviewCampaignSnapshotCard)를 사용하고 있습니다.같은 파일 내에서 import 경로 스타일이 혼재되어 있는데, 일관성을 위해 한 가지 스타일로 통일하는 것이 좋습니다. 일반적으로:
- 같은 디렉토리나 형제 디렉토리의 파일은 상대 경로
- 다른 도메인/공통 컴포넌트는 절대 경로
를 사용하는 것이 관례입니다. 현재 구조에서는 둘 다 타당하지만, 프로젝트의 import 컨벤션을 확인하고 따르는 것을 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/dashboard/overview/sections/OverviewBudgetSection.tsx` around lines 14 - 16, Imports in this file mix absolute and relative styles: OverviewBudgetGaugeSkeleton is imported via "`@/components/`..." while OverviewCampaignSnapshotCard uses a relative "./" path; pick the project's convention and make them consistent (prefer using relative imports for sibling/nearby components like OverviewCampaignSnapshotCard and any local skeletons), update the import for OverviewBudgetGaugeSkeleton or OverviewCampaignSnapshotCard so both use the same style and ensure the module resolution still works for the chosen form.src/components/dashboard/overview/skeleton/OverviewSkeleton.tsx (2)
5-5: 💤 Low value외부 모듈 의존성 검토 필요
PLATFORM_ROAS_TABLE_COL을PlatformRoasTable에서 직접 import하고 있는데, 스켈레톤 컴포넌트가 실제 데이터 컴포넌트의 내부 구현 상수에 의존하는 구조입니다.향후
PlatformRoasTable의 그리드 레이아웃이 변경되면 이 스켈레톤도 함께 수정해야 하므로, 두 컴포넌트 간 결합도가 높아집니다. 가능하다면 공통 상수 파일로 분리하거나, 스켈레톤 컴포넌트 내부에서 동일한 그리드 정의를 복제하는 것을 고려해보세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dashboard/overview/skeleton/OverviewSkeleton.tsx` at line 5, OverviewSkeleton currently imports PLATFORM_ROAS_TABLE_COL from PlatformRoasTable, creating tight coupling; either extract the grid constant into a shared module (e.g., create and export PLATFORM_ROAS_TABLE_COL from a new shared/constants or layout file and update both PlatformRoasTable and OverviewSkeleton to import it) or duplicate the grid definition inside OverviewSkeleton so it no longer imports PlatformRoasTable directly; update imports in PlatformRoasTable and OverviewSkeleton to reference the new shared export if you choose extraction.
8-16: ⚡ Quick win접근성 개선 제안
스켈레톤 컴포넌트들에 스크린 리더를 위한 로딩 상태 표시가 없습니다. 시각적으로는 로딩 중임을 알 수 있지만, 스크린 리더 사용자에게는 현재 상태가 전달되지 않습니다.
각 스켈레톤 컴포넌트의 최상위 요소에
role="status"및aria-live="polite"또는aria-busy="true"와 시각적으로 숨겨진 "데이터 로딩 중" 텍스트를 추가하는 것을 권장합니다.♿ 접근성 개선 예시
export function OverviewKpiCardSkeleton() { return ( - <div className="flex flex-col gap-3 rounded-3xl border border-surface-100/40 bg-surface-100/80 px-7 py-5 shadow-card backdrop-blur-sm"> + <div + className="flex flex-col gap-3 rounded-3xl border border-surface-100/40 bg-surface-100/80 px-7 py-5 shadow-card backdrop-blur-sm" + role="status" + aria-label="KPI 카드 로딩 중" + > <Skeleton className="h-4 w-16" /> <Skeleton className="h-6 w-24" /> <Skeleton className="h-6 w-14 rounded-full" /> </div> ); }다른 스켈레톤 컴포넌트들도 동일하게 적용할 수 있습니다.
Also applies to: 19-21, 24-54, 57-89, 92-99
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dashboard/overview/skeleton/OverviewSkeleton.tsx` around lines 8 - 16, The OverviewKpiCardSkeleton currently has no accessible loading semantics; update the top-level div in OverviewKpiCardSkeleton to include role="status" and either aria-live="polite" or aria-busy="true" and add a visually hidden text node (e.g., a span with screen-reader-only CSS content like "데이터 로딩 중") so screen readers announce the loading state; apply the same pattern to the other skeleton components in this file (the other top-level skeleton wrappers referenced in the diff ranges) to ensure consistent accessible loading indicators.src/pages/dashboard/overview/sections/OverviewCampaignSnapshotCard.tsx (1)
24-28: ⚡ Quick winPLATFORM_MAP 타입 선언을 개선하면 타입 안정성이 향상됩니다.
현재
PLATFORM_MAP이Record<string, string>으로 선언되어 있어서, TypeScript 입장에서는PLATFORM_MAP[key]가string | undefined를 반환할 수 있다고 봅니다. 함수의 반환 타입은string | null이므로 타입 계약이 정확하지 않습니다.다만 라인 39의 타입 가드
.filter((name): name is string => name != null)가 undefined를 명시적으로 걸러내기 때문에 런타임에서는 안전합니다.🛡️ 제안: PLATFORM_MAP 타입을 보다 명확하게
- export const PLATFORM_MAP: Record<string, string> = { + export const PLATFORM_MAP: Record<TProviderType, string> = { GOOGLE: "Google", NAVER: "NAVER", META: "Meta", };또는 함수 내에서 안전하게 처리:
function providerDisplayName(raw: string): string | null { const key = raw.toUpperCase() as TProviderType; if (!PROVIDERS.includes(key)) return null; - return PLATFORM_MAP[key]; + const displayName = PLATFORM_MAP[key]; + return displayName ?? null; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/dashboard/overview/sections/OverviewCampaignSnapshotCard.tsx` around lines 24 - 28, PLATFORM_MAP is typed too loosely (Record<string,string>) so PLATFORM_MAP[key] is string | undefined; tighten its type to either Record<TProviderType, string> if every TProviderType is present, or Partial<Record<TProviderType, string>> if some keys may be missing, and update providerDisplayName to handle undefined explicitly (e.g., look up PLATFORM_MAP[key] and return it or null) so the return type string | null is honored; reference symbols: PLATFORM_MAP, providerDisplayName, TProviderType, PROVIDERS.src/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsx (1)
260-276: 💤 Low value
layoutprop 사용 검토
motion.div에layoutprop이 설정되어 있는데, 이 prop은 모든 자식 요소의 레이아웃 변경을 추적하므로 성능 비용이 있을 수 있어요.현재 애니메이션은
height와opacity만 변경하고 있으므로,layoutprop 없이도 동일한 효과를 낼 수 있습니다. 복잡한 레이아웃 재배치가 필요한 경우가 아니라면 제거를 고려해 보세요.♻️ 제안하는 수정
<motion.div key="ai-summary-panel" - layout initial={ prefersReducedMotion ? { opacity: 1, height: "auto" } : { opacity: 0, height: 0 } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsx` around lines 260 - 276, The motion.div with key "ai-summary-panel" currently includes the layout prop which forces Framer Motion to track children layout changes and can hurt performance; remove the layout prop from that motion.div (while keeping initial, animate, exit, transition, panelExitTransition, panelTransition and the prefersReducedMotion handling) so the component continues animating height and opacity without the extra layout tracking; verify the visual behavior still matches expectations after removal.src/pages/dashboard/overview/ai-summary/downloadAiSummaryPdf.ts (1)
51-56: ⚡ Quick win
afterprint미발생 케이스 대비 정리(fallback cleanup)를 추가해 주세요.현재는
afterprint이벤트에만 정리가 걸려 있어, 이벤트가 누락되는 환경에서 iframe이 남을 수 있습니다. 타임아웃 기반 fallback 정리를 같이 두는 편이 안전합니다.제안 diff
const cleanup = () => { iframe.remove(); printWindow.removeEventListener("afterprint", cleanup); }; printWindow.addEventListener("afterprint", cleanup); + const fallbackCleanupTimer = window.setTimeout(cleanup, 60_000); const runPrint = () => { printWindow.focus(); printWindow.print(); }; @@ - if (printDoc.fonts) { + if (printDoc.fonts) { void printDoc.fonts.ready.then(() => { setTimeout(runPrint, 100); }); } else { setTimeout(runPrint, 300); } + + const originalCleanup = cleanup; + const safeCleanup = () => { + window.clearTimeout(fallbackCleanupTimer); + originalCleanup(); + }; + printWindow.removeEventListener("afterprint", cleanup); + printWindow.addEventListener("afterprint", safeCleanup);Also applies to: 62-68
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/dashboard/overview/ai-summary/downloadAiSummaryPdf.ts` around lines 51 - 56, The cleanup currently runs only in the afterprint handler (cleanup, printWindow, iframe), so add a timeout-based fallback that removes the iframe and detaches the afterprint listener if afterprint never fires: create a timer (e.g., via setTimeout) when adding printWindow.addEventListener("afterprint", cleanup) that calls the same cleanup after a short delay, and ensure the cleanup function clears that timer so it doesn't run twice; apply the same pattern to the other print block (the second cleanup/addEventListener pair) so both code paths have a timeout fallback and the timer is cleared inside cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsx`:
- Line 224: There is a typo in the Tailwind gradient class: replace any
occurrence of "bg-linear-to-r" with the correct "bg-gradient-to-r"; update the
className in the OverviewAiSummaryCard component (OverviewAiSummaryCard.tsx,
around the span with className containing bg-linear-to-r), and make the same
replacement in the LandingPage component (LandingPage.tsx) and the Button
component (Button.tsx) so all gradient usages use "bg-gradient-to-r".
In `@src/styles/aiReport.print.css`:
- Around line 2-9: The :root block defines hard-coded HEX color variables
(--color-text-title, --color-text-body, --color-text-muted, --color-surface-100,
--color-surface-200, --color-surface-400); remove these HEX declarations and
replace usages with the corresponding `@theme` tokens from tokens.css (do not
rename tokens or create new ones) so aiReport.print.css references the existing
theme color tokens instead of defining new hex values.
---
Duplicate comments:
In `@src/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsx`:
- Line 10: OverviewAiSummaryCard currently imports aiReportMockData directly;
remove this hard dependency by making the component accept the data via props or
by delegating fetch to a custom hook. Update the OverviewAiSummaryCard component
signature to accept a prop (e.g., aiReport: AiReportType) and replace all uses
of aiReportMockData with that prop, or create/use a hook named useAiReport that
returns the data and loading/error states and call it from the parent (not the
component) if you want composition; ensure PropTypes/TypeScript types
(AiReportType) and default/optional handling are added where referenced in
OverviewAiSummaryCard so the component no longer imports aiReportMockData.
---
Nitpick comments:
In `@src/components/dashboard/overview/skeleton/OverviewSkeleton.tsx`:
- Line 5: OverviewSkeleton currently imports PLATFORM_ROAS_TABLE_COL from
PlatformRoasTable, creating tight coupling; either extract the grid constant
into a shared module (e.g., create and export PLATFORM_ROAS_TABLE_COL from a new
shared/constants or layout file and update both PlatformRoasTable and
OverviewSkeleton to import it) or duplicate the grid definition inside
OverviewSkeleton so it no longer imports PlatformRoasTable directly; update
imports in PlatformRoasTable and OverviewSkeleton to reference the new shared
export if you choose extraction.
- Around line 8-16: The OverviewKpiCardSkeleton currently has no accessible
loading semantics; update the top-level div in OverviewKpiCardSkeleton to
include role="status" and either aria-live="polite" or aria-busy="true" and add
a visually hidden text node (e.g., a span with screen-reader-only CSS content
like "데이터 로딩 중") so screen readers announce the loading state; apply the same
pattern to the other skeleton components in this file (the other top-level
skeleton wrappers referenced in the diff ranges) to ensure consistent accessible
loading indicators.
In `@src/pages/dashboard/overview/ai-summary/downloadAiSummaryPdf.ts`:
- Around line 51-56: The cleanup currently runs only in the afterprint handler
(cleanup, printWindow, iframe), so add a timeout-based fallback that removes the
iframe and detaches the afterprint listener if afterprint never fires: create a
timer (e.g., via setTimeout) when adding
printWindow.addEventListener("afterprint", cleanup) that calls the same cleanup
after a short delay, and ensure the cleanup function clears that timer so it
doesn't run twice; apply the same pattern to the other print block (the second
cleanup/addEventListener pair) so both code paths have a timeout fallback and
the timer is cleared inside cleanup.
In `@src/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsx`:
- Around line 260-276: The motion.div with key "ai-summary-panel" currently
includes the layout prop which forces Framer Motion to track children layout
changes and can hurt performance; remove the layout prop from that motion.div
(while keeping initial, animate, exit, transition, panelExitTransition,
panelTransition and the prefersReducedMotion handling) so the component
continues animating height and opacity without the extra layout tracking; verify
the visual behavior still matches expectations after removal.
In `@src/pages/dashboard/overview/sections/OverviewBudgetSection.tsx`:
- Around line 14-16: Imports in this file mix absolute and relative styles:
OverviewBudgetGaugeSkeleton is imported via "`@/components/`..." while
OverviewCampaignSnapshotCard uses a relative "./" path; pick the project's
convention and make them consistent (prefer using relative imports for
sibling/nearby components like OverviewCampaignSnapshotCard and any local
skeletons), update the import for OverviewBudgetGaugeSkeleton or
OverviewCampaignSnapshotCard so both use the same style and ensure the module
resolution still works for the chosen form.
In `@src/pages/dashboard/overview/sections/OverviewCampaignSnapshotCard.tsx`:
- Around line 24-28: PLATFORM_MAP is typed too loosely (Record<string,string>)
so PLATFORM_MAP[key] is string | undefined; tighten its type to either
Record<TProviderType, string> if every TProviderType is present, or
Partial<Record<TProviderType, string>> if some keys may be missing, and update
providerDisplayName to handle undefined explicitly (e.g., look up
PLATFORM_MAP[key] and return it or null) so the return type string | null is
honored; reference symbols: PLATFORM_MAP, providerDisplayName, TProviderType,
PROVIDERS.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 63832e36-4fe9-4637-8ee7-f1b1857d26d9
📒 Files selected for processing (20)
src/components/dashboard/overview/skeleton/OverviewSkeleton.tsxsrc/pages/dashboard/overview/OverviewAiDrawer.tsxsrc/pages/dashboard/overview/OverviewAiReportPanel.tsxsrc/pages/dashboard/overview/OverviewDashboard.tsxsrc/pages/dashboard/overview/ai-summary/OverviewAiSummaryCard.tsxsrc/pages/dashboard/overview/ai-summary/aiReport.mock.tssrc/pages/dashboard/overview/ai-summary/aiReport.utils.tssrc/pages/dashboard/overview/ai-summary/downloadAiSummaryPdf.tssrc/pages/dashboard/overview/ai-summary/print/OverviewAiSummaryPrintReport.tsxsrc/pages/dashboard/overview/sections/OverviewBudgetSection.tsxsrc/pages/dashboard/overview/sections/OverviewCampaignSnapshotCard.tsxsrc/pages/dashboard/overview/sections/OverviewKpiSection.tsxsrc/pages/dashboard/overview/sections/OverviewPlatformSection.tsxsrc/pages/dashboard/platform/PlatformDashboard.tsxsrc/stories/Typography.stories.tsxsrc/styles/aiReport.print.csssrc/styles/print.csssrc/styles/tokens.csssrc/styles/utilities.csssrc/types/dashboard/overview.ts
💤 Files with no reviewable changes (2)
- src/pages/dashboard/overview/OverviewAiReportPanel.tsx
- src/pages/dashboard/overview/OverviewAiDrawer.tsx
✅ Files skipped from review due to trivial changes (1)
- src/pages/dashboard/overview/ai-summary/print/OverviewAiSummaryPrintReport.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/types/dashboard/overview.ts

#209
🚨 관련 이슈
✨ 변경사항
✏️ 작업 내용
파일구조
스크린샷
저장되는 report
whereyouad-report.pdf
😅 미완성 작업
📢 논의 사항 및 참고 사항
(@dnd-kit - 통합 대시보드 카드 순서를 사용자가 직접 변경할 수 있게 합니다. 대시보드 카드 커스텀)
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항