import type { Site, SiteAccount } from "@umt/contracts"; import { ArrowRight, Camera, Globe, Plus, RefreshCw } from "lucide-react"; import { useState } from "react"; import { Link } from "react-router"; import { toast } from "sonner"; import { snapshotStale, useSiteAccounts, useSites, useSnapshotSite, useToggleSite } from "~/api/sites.ts"; import { EmptyState, ErrorState, LoadingState } from "~/components/console/empty-state.tsx"; import { consolePath } from "~/components/console/nav.ts"; import { PageHeader } from "~/components/console/page-header.tsx"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { Button } from "~/components/ui/button.tsx"; import { Switch } from "~/components/ui/switch.tsx"; import { accountSummary, circuitLabel, relTime, SITE_HEALTH } from "~/components/workbench/release-fields.ts"; import { ADAPTER_LABEL, SiteRegisterDialog } from "~/components/workbench/site-register-dialog.tsx"; import { errorMessage } from "~/lib/api.ts"; function SiteCard({ site, accounts }: { site: Site; accounts: SiteAccount[] }) { const toggle = useToggleSite(); const snap = useSnapshotSite(); const health = SITE_HEALTH[site.healthStatus]; const account = accountSummary(accounts); const stale = site.snapshotEnabled && snapshotStale(site); const circuit = circuitLabel(site); return (
{site.name}

{site.key} · {ADAPTER_LABEL[site.adapter]}

toggle.mutate( { id: site.id, enabled: v }, { onSuccess: () => toast.success(v ? `已启用 ${site.name}` : `已停用 ${site.name}`), onError: (e) => toast.error(errorMessage(e)), }, ) } />
{health.label} 账号:{account.label} {circuit ? {circuit} : null}
最近快照
{site.snapshotEnabled ? relTime(site.lastSnapshotAt) : "未开启快照"} {stale ? 已过期 : null}
快照周期
{site.snapshotIntervalSec ? `${Math.round(site.snapshotIntervalSec / 60)} 分钟` : "默认"}
); } export const meta = () => [{ title: "站点工作台 · UHD 媒体自动化控制台" }]; export default function SitesPage() { const sites = useSites(); const [registering, setRegistering] = useState(false); const accounts = useSiteAccounts(); const bySite = new Map(); for (const a of accounts.data?.items ?? []) bySite.set(a.siteId, [...(bySite.get(a.siteId) ?? []), a]); return ( <> } /> {sites.isPending ? (
) : sites.error && !sites.data ? ( void sites.refetch()} /> ) : !sites.data?.items.length ? ( setRegistering(true)}> 登记站点 } /> ) : (
{sites.data.items.map((s) => ( ))}
)} {registering ? : null} ); }