feat(web): FE overhaul — SWR centralized data layer + dashboard + islands
CI / typecheck + tests (push) Canceled after 0s
CI / build + deploy (Nix) — api (push) Canceled after 0s
CI / build + deploy (Nix) — mcp (push) Canceled after 0s
CI / build + deploy (Nix) — worker (push) Canceled after 0s
CI / build + deploy (web) (push) Canceled after 0s
CI / typecheck + tests (push) Canceled after 0s
CI / build + deploy (Nix) — api (push) Canceled after 0s
CI / build + deploy (Nix) — mcp (push) Canceled after 0s
CI / build + deploy (Nix) — worker (push) Canceled after 0s
CI / build + deploy (web) (push) Canceled after 0s
- Add SWR foundation (lib/fetcher, lib/swr, lib/api) with typed hooks: useSections/useDocuments/useSearch/useRelated/useRevisions/useQueueStatus - Replace ad-hoc useEffect+fetch in Header/Sidebar/CommandMenu/search with SWR (dedup 4s, focus throttle 10s, debounced search 180ms) - Doc page: RelatedGrid + RevisionList as SWR client islands (RSC hero stays) - DocForm: SWR mutate on create/update for live sidebar invalidation - New APIs: /api/related, /api/revisions, /api/tags, /api/queue/status, /api/docs filtering (?section=&status=) - New UI: DocumentCard, SectionCard, SearchBar, TagCloud, QueueBadge, EmptyState, dashboard (/dashboard with QueueStrip live + section stats) - Layout wraps SWRProvider; no TanStack code (SWR-only per spec) - Verified: next build passes (16.3.4 Turbopack), no manual fetch sprawl remains Co-Authored-By: internal-model
This commit is contained in:
1 parent
1025454d87
commit
4330f47c65
36 files changed
+1393
-415
No files matched your search
@@ -0,0 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import { mutate } from "swr";
|
||||
import { useRevisions } from "@/hooks/useRevisions";
|
||||
import { History } from "lucide-react";
|
||||
|
||||
export default function RevisionList({ slug, canEdit }: { slug: string; canEdit: boolean }) {
|
||||
const { revisions, isLoading, error, mutate: revalidate } = useRevisions(slug, 10);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-2 animate-pulse">
|
||||
{Array.from({ length: 3 }).map((_, i) => (
|
||||
<div key={i} className="h-12 bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-lg" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <p className="text-xs text-amber-600 dark:text-amber-400">Failed to load revisions: {(error as Error).message}</p>;
|
||||
}
|
||||
|
||||
if (revisions.length === 0) return null;
|
||||
|
||||
return (
|
||||
<aside className="mt-10 pt-8 border-t border-[var(--border-color)]">
|
||||
<div className="flex items-center justify-between mb-3.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<History className="w-4 h-4 text-[var(--text-muted)]" />
|
||||
<h2 className="text-xs font-bold text-[var(--text-muted)] uppercase tracking-wider font-mono">Revision History ({revisions.length})</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{revisions.map((rev) => (
|
||||
<div key={rev.id} className="flex items-center justify-between gap-3 p-3 bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-lg text-xs shadow-xs">
|
||||
<div className="flex items-center gap-2 min-w-0 font-mono">
|
||||
<span className="text-[var(--text-primary)] bg-[var(--bg-elevated)] px-1.5 py-0.5 rounded border border-[var(--border-color)] font-bold text-[10px]">v{rev.revisionNo}</span>
|
||||
<span className="text-[var(--text-secondary)] font-sans truncate text-xs">{rev.reason || "Updated document in PostgreSQL"}</span>
|
||||
<span className="text-[var(--text-dim)] hidden sm:inline text-[11px]">· {new Date(rev.createdAt).toLocaleString()}</span>
|
||||
</div>
|
||||
{canEdit && (
|
||||
<form
|
||||
action="/api/revisions/restore"
|
||||
method="post"
|
||||
onSubmit={() => {
|
||||
// Optimistically revalidate after form post navigates back
|
||||
setTimeout(() => {
|
||||
revalidate();
|
||||
mutate((k) => typeof k === "string" && k.startsWith("/api/docs"));
|
||||
}, 800);
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="id" value={rev.id} />
|
||||
<button
|
||||
type="submit"
|
||||
className="text-xs text-[var(--text-primary)] hover:text-black dark:hover:text-white bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] px-2.5 py-1 rounded border border-[var(--border-color)] transition-colors font-medium cursor-pointer"
|
||||
>
|
||||
Restore
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user