176 lines
5.3 KiB
TypeScript
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>
|
|
);
|
|
} |