feat(web): make the site workbench the home of sites
- 站点工作台: 登记站点 dialog (built-in site definitions or custom). - Site detail tabs: 整理视图 / 原站视图 / 快照与 Feed / 账号 (account list + credential form). - 来源健康 (was 来源/Feed/快照): cross-site health table + all feeds; site names deep-link to the site's 快照与 Feed tab. Feed table extracted to components/workbench/feed-table.tsx. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
909cd2ad49
commit
ddb15145d8
7 files changed
+805
-296
No files matched your search
@@ -1,18 +1,21 @@
|
||||
import type { ResolutionStatus, WorkbenchRelease } from "@umt/contracts";
|
||||
import { ArrowLeft, Bot, Camera, Globe, Radar, SearchX, TriangleAlert } from "lucide-react";
|
||||
import { ArrowLeft, Bot, Camera, Globe, Plus, Radar, SearchX, TriangleAlert } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Link, useParams } from "react-router";
|
||||
import { Link, useParams, useSearchParams } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import { useInventory } from "~/api/inventory.ts";
|
||||
import { type ReleaseFilters, useSiteReleases } from "~/api/releases.ts";
|
||||
import { snapshotStale, useSite, useSiteAccounts, useSnapshotSite } from "~/api/sites.ts";
|
||||
import { openAgentDrawer } from "~/components/agent/agent-store.ts";
|
||||
import { DetailSection } from "~/components/console/detail-sheet.tsx";
|
||||
import { EmptyState, ErrorState, LoadingState } from "~/components/console/empty-state.tsx";
|
||||
import { FilterBar, FilterSelect } from "~/components/console/filter-bar.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 { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs.tsx";
|
||||
import { FeedTable } from "~/components/workbench/feed-table.tsx";
|
||||
import { ReleaseDetailSheet } from "~/components/workbench/release-detail-sheet.tsx";
|
||||
import {
|
||||
accountSummary,
|
||||
@@ -24,6 +27,8 @@ import {
|
||||
SITE_HEALTH,
|
||||
} from "~/components/workbench/release-fields.ts";
|
||||
import { ReleaseRow } from "~/components/workbench/release-list.tsx";
|
||||
import { SiteAccountsPanel } from "~/components/workbench/site-accounts.tsx";
|
||||
import { SnapshotPanel } from "~/components/workbench/snapshot-panel.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
|
||||
const PROMOS = [
|
||||
@@ -84,8 +89,16 @@ function useDebounced<T>(value: T, ms = 400) {
|
||||
return v;
|
||||
}
|
||||
|
||||
const TABS = ["organized", "origin", "snapshots", "accounts"] as const;
|
||||
|
||||
export const meta = () => [{ title: "站点 · UHD 媒体自动化控制台" }];
|
||||
|
||||
export default function SiteWorkbenchPage() {
|
||||
const { siteId = "" } = useParams();
|
||||
const [params, setParams] = useSearchParams();
|
||||
const rawTab = params.get("tab") ?? "";
|
||||
const tab = (TABS as readonly string[]).includes(rawTab) ? rawTab : "organized";
|
||||
const [creatingFeed, setCreatingFeed] = useState(false);
|
||||
const site = useSite(siteId);
|
||||
const accounts = useSiteAccounts(siteId);
|
||||
const snap = useSnapshotSite();
|
||||
@@ -169,9 +182,9 @@ export default function SiteWorkbenchPage() {
|
||||
actions={
|
||||
<>
|
||||
<Button asChild variant="ghost" size="sm">
|
||||
<Link to="/console/sites">
|
||||
<Link to={consolePath("sites")}>
|
||||
<ArrowLeft />
|
||||
站点
|
||||
站点工作台
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -185,10 +198,24 @@ export default function SiteWorkbenchPage() {
|
||||
}
|
||||
/>
|
||||
|
||||
<Tabs defaultValue="organized">
|
||||
<TabsList>
|
||||
<Tabs
|
||||
value={tab}
|
||||
onValueChange={(v) =>
|
||||
setParams(
|
||||
(p) => {
|
||||
if (v === "organized") p.delete("tab");
|
||||
else p.set("tab", v);
|
||||
return p;
|
||||
},
|
||||
{ replace: true },
|
||||
)
|
||||
}
|
||||
>
|
||||
<TabsList aria-label="站点分节">
|
||||
<TabsTrigger value="organized">整理视图</TabsTrigger>
|
||||
<TabsTrigger value="origin">原站视图</TabsTrigger>
|
||||
<TabsTrigger value="snapshots">快照与 Feed</TabsTrigger>
|
||||
<TabsTrigger value="accounts">账号</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="organized">
|
||||
@@ -337,6 +364,25 @@ export default function SiteWorkbenchPage() {
|
||||
description={<>计划在 M2 提供。资源详情里有站点 ID 和来源时间,可据此到原站查看。</>}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="snapshots">
|
||||
<div className="space-y-8">
|
||||
{s ? <SnapshotPanel site={s} /> : <LoadingState rows={3} />}
|
||||
<DetailSection
|
||||
title="Feed"
|
||||
actions={
|
||||
<Button size="sm" variant="outline" onClick={() => setCreatingFeed(true)}>
|
||||
<Plus />
|
||||
新建 Feed
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<FeedTable siteId={siteId} creating={creatingFeed} onCreatingChange={setCreatingFeed} />
|
||||
</DetailSection>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="accounts">{s ? <SiteAccountsPanel site={s} /> : <LoadingState rows={3} />}</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<ReleaseDetailSheet
|
||||
|
||||
Reference in new issue
Block a user