Group repeat pseudonymization runs by project path

This commit is contained in:
muena
2026-09-25 16:17:03 +02:00
parent c3ef1a6aed
commit 5877b7cf5f
4 changed files with 91 additions and 15 deletions

View File

@@ -4,7 +4,7 @@ import { homedir } from "node:os";
import { basename, dirname, join } from "node:path";
import { useEffect, useState } from "react";
import { depseudonymizeDocument } from "./velum";
import { isRestorableDocument, listAllRuns, resolveWithin, restorePath, restoreText } from "./lib/velum-project";
import { canonicalProjectPath, isRestorableDocument, listAllRuns, resolveWithin, restorePath, restoreText } from "./lib/velum-project";
import type { ProjectRun } from "./lib/velum-project";
import { canonicalPerson, mergePeople } from "./lib/project-review";
import { planProjectRevision, reviseProjectRun } from "./lib/project-revision";
@@ -189,6 +189,22 @@ function FilePicker(props: { run: ProjectRun; directory: string }) {
</List>;
}
function ProjectHistory(props: { projectName: string; entries: SavedRun[] }) {
const { push } = useNavigation();
return <List navigationTitle={`${props.projectName}: frühere Stände`}
searchBarPlaceholder="Stand suchen">
<List.Section title="Frühere Stände · nur für Klartext-Export">
{props.entries.map(({ run, directory }) => <List.Item key={run.id} icon={Icon.Clock}
title={new Date(run.createdAt).toLocaleString("de-AT")}
subtitle={`${run.files.length} Dateien`}
actions={<ActionPanel>
<Action title="Dateien als Klartext exportieren" icon={Icon.Download}
onAction={() => push(<FilePicker run={run} directory={directory} />)} />
</ActionPanel>} />)}
</List.Section>
</List>;
}
export default function Command() {
const { push } = useNavigation();
const [runs, setRuns] = useState<SavedRun[]>([]);
@@ -198,21 +214,27 @@ export default function Command() {
title: "Projektstände nicht lesbar", message: error.message })).finally(() => setBusy(false));
}, []);
async function refresh() { setRuns(await listAllRuns()); }
const latest = new Set<string>();
return <List isLoading={busy} navigationTitle="Projektstand nachbearbeiten oder rückübersetzen" searchBarPlaceholder="Projekt suchen">
const projects = new Map<string, SavedRun[]>();
for (const entry of runs) {
const path = canonicalProjectPath(entry.run.projectPath);
if (!projects.has(path)) projects.set(path, []);
projects.get(path)!.push(entry);
}
return <List isLoading={busy} navigationTitle="Projekte nachbearbeiten oder rückübersetzen" searchBarPlaceholder="Projekt suchen">
<List.EmptyView title="Noch keine Projektstände" description="Zuerst Dateien mit Velum pseudonymisieren." />
{runs.map(({ run, directory }) => {
const isLatest = !latest.has(run.projectPath);
latest.add(run.projectPath);
return <List.Item key={run.id} icon={Icon.Folder}
title={run.projectName} subtitle={run.projectPath}
{[...projects].map(([path, entries]) => {
const [{ run, directory }] = entries;
return <List.Item key={path} icon={Icon.Folder}
title={run.projectName} subtitle={path}
accessories={[{ text: `${run.files.length} Dateien` }, { text: new Date(run.createdAt).toLocaleString("de-AT") },
...(isLatest ? [{ text: "Aktueller Stand" }] : [])]}
...(entries.length > 1 ? [{ text: `${entries.length} Stände` }] : [])]}
actions={<ActionPanel>
{isLatest && <Action title="Zuordnungen nachbearbeiten" icon={Icon.Pencil}
onAction={() => push(<RevisionReview saved={{ run, directory }} onSaved={refresh} />)} />}
<Action title="Dateien als Klartext exportieren" icon={Icon.Download}
<Action title="Aktuellen Stand nachbearbeiten" icon={Icon.Pencil}
onAction={() => push(<RevisionReview saved={{ run, directory }} onSaved={refresh} />)} />
<Action title="Aktuelle Dateien als Klartext exportieren" icon={Icon.Download}
onAction={() => push(<FilePicker run={run} directory={directory} />)} />
{entries.length > 1 && <Action title="Frühere Stände anzeigen" icon={Icon.Clock}
onAction={() => push(<ProjectHistory projectName={run.projectName} entries={entries.slice(1)} />)} />}
</ActionPanel>} />;
})}
</List>;