// frontend/components/settings/ConnectorList.tsx — 설정의 커넥터 그리드(메일·연결 탭 공유) "use client"; import { Button } from "@/components/Button"; import { Icon } from "@/components/Icon"; import type { IconName } from "@/components/icons/paths"; import { AddAccountMenu } from "@/components/connectors/AddAccountMenu"; import { ConnectorCard } from "@/components/connectors/ConnectorCard"; import { connectorsApi } from "@/lib/connectors/api"; import { useConnectors } from "@/lib/hooks/useConnectors"; import type { ConnectorStatus } from "@/lib/types"; import type { ToastFn } from "./SettingsClient"; export function ConnectorList({ title, subtitle, icon, filter, toast, showSyncAll = false, emptyText = "표시할 연결이 없어요.", addDomain, redirectAfter = "/settings", }: { title: string; subtitle: string; icon: IconName; filter: (c: ConnectorStatus) => boolean; toast: ToastFn; showSyncAll?: boolean; emptyText?: string; addDomain?: string; // 설정 시 "계정 추가"(OAuth) 메뉴 노출 redirectAfter?: string; // OAuth 콜백 후 복귀 경로 }) { const { data, isLoading, error, refresh } = useConnectors(); const all = data ?? []; const items = all.filter(filter); const connected = items.filter((c) => c.on).length; const find = (id: string) => all.find((x) => x.id === id); const onSync = async (id: string) => { const c = find(id); try { const res = await connectorsApi.sync(id); await refresh(); toast(`${c?.name ?? "소스"} · 새 항목 ${res.upserted}건`, c?.tone ?? "blue"); } catch { toast("동기화에 실패했어요", "coral"); } }; const onDisconnect = async (id: string) => { const c = find(id); try { await connectorsApi.disconnect(id); await refresh(); toast(`${c?.name ?? "소스"} 연결을 해제했어요`, "faint"); } catch { toast("연결 해제에 실패했어요", "coral"); } }; const onImport = async (id: string, file: File) => { try { const res = await connectorsApi.importFile(id, file); await refresh(); toast(res.detail || `${res.imported}건 가져왔어요`, "green"); } catch { toast("이 소스는 파일 가져오기를 지원하지 않아요", "amber"); } }; const onSyncAll = async () => { try { const res = await connectorsApi.syncAll(); await refresh(); const total = res.reduce((n, r) => n + (r.upserted ?? 0), 0); toast(`전체 동기화 완료 · 새 항목 ${total}건`, "green"); } catch { toast("전체 동기화에 실패했어요", "coral"); } }; return (

{title}

{subtitle}

{connected}/{items.length} 연결됨 {showSyncAll && items.length > 0 && ( )} {addDomain && ( toast(m, "coral")} /> )}
{error && (
연결 상태를 불러오지 못했어요
)} {isLoading && !data &&
불러오는 중…
} {!isLoading && items.length === 0 && (
{emptyText}
)} {items.length > 0 && (
{items.map((c) => ( onImport(c.id, file)} /> ))}
)}
); }