W6: export panel
components-export: ExportPanel (scope picker + request) + ExportJobRow (status, download on ready, error verbatim; timer lives in the app per the polling contract). Demo Export tab polls getExport app-side. Lib confirmed self-contained (react + lucide only); README/tsconfig updated. Typechecks clean. Co-Authored-By: Claude Fable 5 (build) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
ClaimsReviewQueue,
|
||||
CollectionForm,
|
||||
CollectionList,
|
||||
ExportPanel,
|
||||
ObjectList,
|
||||
ObjectViewer,
|
||||
MockKnowledgeTransport,
|
||||
@@ -14,6 +15,8 @@ import {
|
||||
type Collection,
|
||||
type CollectionInput,
|
||||
type CollectionPatch,
|
||||
type ExportJob,
|
||||
type ExportScope,
|
||||
type KnowledgeTransport,
|
||||
type ObjectOutline,
|
||||
type ObjectSummary,
|
||||
@@ -27,7 +30,9 @@ type View =
|
||||
| { kind: "object"; id: string };
|
||||
|
||||
export default function KnowledgeDemo() {
|
||||
const [tab, setTab] = useState<"browse" | "review">("browse");
|
||||
const [tab, setTab] = useState<"browse" | "review" | "export">("browse");
|
||||
const [exports, setExports] = useState<ExportJob[]>([]);
|
||||
const [exportBusy, setExportBusy] = useState(false);
|
||||
const [view, setView] = useState<View>({ kind: "collections" });
|
||||
const [collections, setCollections] = useState<Collection[]>([]);
|
||||
const [pending, setPending] = useState<Record<string, number>>({});
|
||||
@@ -56,6 +61,23 @@ export default function KnowledgeDemo() {
|
||||
void refresh();
|
||||
}
|
||||
|
||||
// App-side polling of in-flight exports (the timer lives here, not in the lib).
|
||||
async function requestExport(scope: ExportScope) {
|
||||
setExportBusy(true);
|
||||
const job = await transport.createExport(scope);
|
||||
setExports((prev) => [job, ...prev]);
|
||||
setExportBusy(false);
|
||||
poll(job.id);
|
||||
}
|
||||
function poll(id: string) {
|
||||
const tick = async () => {
|
||||
const job = await transport.getExport(id);
|
||||
setExports((prev) => prev.map((e) => (e.id === id ? job : e)));
|
||||
if (job.status === "pending" || job.status === "running") setTimeout(tick, 1500);
|
||||
};
|
||||
setTimeout(tick, 800);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void refresh();
|
||||
}, []);
|
||||
@@ -90,8 +112,19 @@ export default function KnowledgeDemo() {
|
||||
<TabBtn active={tab === "review"} onClick={() => setTab("review")}>
|
||||
Review{reviewCount > 0 ? ` (${reviewCount})` : ""}
|
||||
</TabBtn>
|
||||
<TabBtn active={tab === "export"} onClick={() => setTab("export")}>Export</TabBtn>
|
||||
</div>
|
||||
|
||||
{tab === "export" && (
|
||||
<ExportPanel
|
||||
collections={collections}
|
||||
jobs={exports}
|
||||
busy={exportBusy}
|
||||
onRequest={requestExport}
|
||||
onDownload={(job) => job.download_url && window.open(job.download_url)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{tab === "review" && (
|
||||
<ClaimsReviewQueue
|
||||
claims={claims}
|
||||
|
||||
Reference in New Issue
Block a user