haixunMaster/app/(dashboard)/connections/page.tsx

176 lines
5.3 KiB
TypeScript

"use client";
import { useCallback, useEffect, useState } from "react";
import { Loader2, PlugZap } from "lucide-react";
import { AccountConnectionCard } from "@/components/accounts/account-connection-card";
import { DeleteAccountCard } from "@/components/accounts/delete-account-card";
import { ExtensionSyncCard } from "@/components/settings/extension-sync-card";
import {
ThreadsConnectionSettings,
type ThreadsConnectionSettingsData,
} from "@/components/settings/threads-connection-settings";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { EmptyState } from "@/components/layout/empty-state";
import { PageHeader } from "@/components/layout/page-header";
import { notify } from "@/lib/notifications/store";
import { parseFetchJson } from "@/lib/utils";
interface ConnectionPageData {
accountId: string | null;
accountName: string | null;
connection: ThreadsConnectionSettingsData | null;
}
export default function ConnectionsPage() {
const [data, setData] = useState<ConnectionPageData | null>(null);
const [draft, setDraft] = useState<ThreadsConnectionSettingsData | null>(null);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const load = useCallback(async () => {
setLoading(true);
try {
const res = await fetch("/api/accounts/connection");
const json = await parseFetchJson<ConnectionPageData & { error?: string }>(res);
if (!res.ok) {
throw new Error(json.error ?? "無法載入連線設定");
}
setData(json);
setDraft(json.connection ?? null);
} catch (error) {
setData(null);
setDraft(null);
notify({
type: "error",
title: "無法載入連線設定",
message: error instanceof Error ? error.message : "請稍後再試",
});
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
load();
}, [load]);
function patchConnection(patch: Partial<ThreadsConnectionSettingsData>) {
setDraft((prev) => (prev ? { ...prev, ...patch } : prev));
}
async function handleSave() {
if (!draft || !data?.accountId) return;
setSaving(true);
try {
const res = await fetch("/api/accounts/connection", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(draft),
});
const json = await parseFetchJson<{
connection?: ThreadsConnectionSettingsData;
error?: string;
}>(res);
if (!res.ok) {
throw new Error(json.error ?? "儲存失敗");
}
setData((prev) =>
prev
? {
...prev,
connection: json.connection ?? null,
}
: prev
);
setDraft(json.connection ?? null);
notify({ type: "success", title: "連線設定已儲存" });
} catch (error) {
notify({
type: "error",
title: "儲存失敗",
message: error instanceof Error ? error.message : "請稍後再試",
});
} finally {
setSaving(false);
}
}
if (loading) {
return (
<div>
<PageHeader title="連線設定" description="管理 Threads 連線、Chrome 同步與資料來源。" />
<div className="space-y-4">
<div className="skeleton h-12 animate-pulse" />
<div className="skeleton h-48 animate-pulse" />
<div className="skeleton h-64 animate-pulse" />
</div>
</div>
);
}
if (!data) {
return (
<div>
<PageHeader title="連線設定" description="管理 Threads 連線、Chrome 同步與資料來源。" />
<EmptyState
icon={PlugZap}
title="無法載入連線設定"
description="請確認服務正常運作後重試。"
action={<Button variant="outline" onClick={load}></Button>}
/>
</div>
);
}
if (!data.accountId || !draft) {
return (
<div>
<PageHeader title="連線設定" description="管理 Threads 連線、Chrome 同步與資料來源。" />
<EmptyState
icon={PlugZap}
title="請先建立經營帳號"
description="在側欄的帳號切換器新增帳號後,即可設定連線。"
/>
</div>
);
}
return (
<div>
<PageHeader
eyebrow="SYSTEM"
title="連線設定"
description="管理 Threads 連線、Chrome Extension 同步與資料來源。依側欄帳號各自設定。"
action={
<Button onClick={handleSave} disabled={saving}>
{saving ? <Loader2 className="h-4 w-4 animate-spin" /> : <PlugZap className="h-4 w-4" />}
</Button>
}
/>
<div className="mb-5 flex items-center gap-2">
<Badge variant="success"></Badge>
<span className="text-sm font-medium">{data.accountName}</span>
</div>
<div className="space-y-5">
<ExtensionSyncCard />
<ThreadsConnectionSettings
settings={draft}
accountName={data.accountName}
onChange={patchConnection}
/>
<AccountConnectionCard />
<DeleteAccountCard
accountId={data.accountId}
accountName={data.accountName ?? "目前帳號"}
onDeleted={load}
/>
</div>
</div>
);
}