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:
rootandClaude Opus 5.5 committed 2026-09-28 02:46:55 +08:00
1 parent 909cd2ad49
commit ddb15145d8
7 files changed
+805 -296

No files matched your search

+10 -1
View File
@@ -1,5 +1,5 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { cursorPage, Id, Site, SiteAccount, SiteDefinitionView, SiteSnapshot } from "@umt/contracts";
import { cursorPage, Id, Site, SiteAccount, type SiteCreate, SiteDefinitionView, SiteSnapshot } from "@umt/contracts";
import { z } from "zod";
import { TTL_SECOND } from "~/lib/api.ts";
import { apiMutate, apiParsed } from "./parse.ts";
@@ -99,3 +99,12 @@ export function snapshotStale(site: Pick<Site, "lastSnapshotAt" | "snapshotInter
const interval = (site.snapshotIntervalSec ?? 3600) * 1000;
return now - new Date(site.lastSnapshotAt).getTime() > interval * 2;
}
/** 登记站点(POST /sites) */
export function useCreateSite() {
const qc = useQueryClient();
return useMutation({
mutationFn: (body: SiteCreate) => apiMutate("/api/v1/sites", { body }).then((d) => Site.parse(d)),
onSuccess: () => qc.invalidateQueries({ queryKey: siteKeys.all }),
});
}