+ {/* Header */}
+
+
동기화 현황
+ setGuideOpen(true)} />
+
+
+ {/* ── Domain Tabs ── */}
+
+ {data.domains.map((d) => {
+ const stuckCount = d.tables.filter((t) => t.stuck).length;
+ return (
+
+ );
+ })}
+
+
+ {/* ── Table Accordions ── */}
+ {activeDomainGroup && (
+
+ {activeDomainGroup.tables.map((table) => (
+
toggleAccordion(table.tableKey)}
+ onDetailTabChange={(tab) => setDetailTab(table.tableKey, tab)}
+ onReset={() => { setResetTableKey(table.tableKey); setResetConfirmOpen(true); }}
+ />
+ ))}
+
+ )}
+
+ {/* Reset Confirm Modal */}
+
setResetConfirmOpen(false)}
+ />
+
+ setGuideOpen(false)}
+ />
+
+ );
+}
+
+// ── Sub Components ────────────────────────────────────────────
+
+interface TableAccordionProps {
+ table: SyncTableStatus;
+ expanded: boolean;
+ detailTab: 'preview' | 'stuck';
+ onToggle: () => void;
+ onDetailTabChange: (tab: 'preview' | 'stuck') => void;
+ onReset: () => void;
+}
+
+function TableAccordion({ table, expanded, detailTab, onToggle, onDetailTabChange, onReset }: TableAccordionProps) {
+ return (
+
+ {/* Accordion header */}
+
+
+ {/* Accordion body */}
+ {expanded && (
+
+ {/* Stats row */}
+
+
+
+
+
+
최근 동기화
+
{formatRelativeTime(table.lastSyncTime)}
+
+
+
+ {/* Detail sub-tabs */}
+
+
+
+
+
+ {detailTab === 'stuck' && table.stuck && (
+
+ )}
+
+
+ {/* Tab content */}
+ {detailTab === 'preview' && (
+
+ )}
+ {detailTab === 'stuck' && (
+
+ )}
+
+ )}
+
+ );
+}
+
+interface MiniStatProps {
+ label: string;
+ value: number;
+ color: string;
+ warn?: boolean;
+}
+
+function MiniStat({ label, value, color, warn }: MiniStatProps) {
+ return (
+