fix discrepancy view
Continuous Integration / backend-tests (push) Successful in 47s
Continuous Integration / frontend-check (push) Successful in 23s
Continuous Integration / e2e-tests (push) Failing after 8m11s

This commit is contained in:
2026-05-01 16:47:58 -04:00
parent d6250986b8
commit 1b696114de
5 changed files with 226 additions and 66 deletions
@@ -10,13 +10,14 @@
CheckSquare,
Square
} from "lucide-svelte";
import { onMount } from 'svelte';
import { Button } from "$lib/components/ui/button";
import { Checkbox } from "$lib/components/ui/checkbox";
import { ScrollArea } from "$lib/components/ui/scroll-area";
import { Input } from "$lib/components/ui/input";
import FileBrowserTreeItem from "./FileBrowserTreeItem.svelte";
import FileBrowserRowItem from "./FileBrowserRowItem.svelte";
import type { FileItem, Breadcrumb } from "$lib/types";
import type { FileItem, TreeNode, Breadcrumb } from "$lib/types";
import { cn } from "$lib/utils";
import {
getSystemTreeSystemTreeGet,
@@ -185,14 +186,35 @@
hasChildren: true
});
const discrepancyRoot = $derived({
const discrepancyRoot = $state<TreeNode>({
name: "Discrepancies",
path: "ROOT",
expanded: true,
expanded: false,
children: [],
hasChildren: true
});
// Load discrepancy tree on mount
onMount(async () => {
if (mode === "discrepancies") {
try {
const response = await getDiscrepanciesTreeGet({ query: { path: "ROOT" } });
if (response.data && Array.isArray(response.data)) {
discrepancyRoot.children = response.data.map((d: any) => ({
name: d.name,
path: d.path,
children: d.children || [],
expanded: false,
hasChildren: d.has_children
}));
discrepancyRoot.expanded = true;
}
} catch (error) {
console.error("Failed to load discrepancy tree:", error);
}
}
});
const activeRoot = $derived(
mode === "host" ? sourceDataRoot :
mode === "index" ? virtualIndexRoot :
+60 -17
View File
@@ -35,19 +35,8 @@
const response = await listDiscrepanciesSystemDiscrepanciesGet();
if (response.data) {
discrepancies = response.data;
// Convert discrepancies to FileItem format for FileBrowser
files = response.data.map((d: DiscrepancySchema) => ({
name: d.path.split('/').pop() || d.path,
path: d.path,
type: 'file',
size: d.size,
mtime: d.mtime ? new Date(d.mtime).getTime() / 1000 : undefined,
discrepancy_id: d.id,
is_deleted: d.is_deleted,
has_versions: d.has_versions,
ignored: false
}));
}
await loadFiles(currentPath);
} catch (error) {
console.error("Failed to load discrepancies:", error);
toast.error("Failed to load discrepancies");
@@ -56,6 +45,41 @@
}
}
async function loadFiles(path: string) {
try {
const response = await browseDiscrepanciesGet({ query: { path } });
if (response.data?.files) {
files = response.data.files.map((d: any) => {
// Check if it's a directory (has "type" property) or a file (has "id")
if (d.type === 'directory') {
// It's a directory
return {
name: d.name || d.path.split('/').pop() || d.path,
path: d.path,
type: 'directory',
discrepancy_count: d.discrepancy_count || 0
};
} else {
// It's a file (DiscrepancySchema)
return {
name: d.path.split('/').pop() || d.path,
path: d.path,
type: 'file',
size: d.size,
mtime: d.mtime ? new Date(d.mtime).getTime() / 1000 : undefined,
discrepancy_id: d.id,
is_deleted: d.is_deleted,
has_versions: d.has_versions,
ignored: false
};
}
});
}
} catch (error) {
console.error("Failed to browse discrepancies:", error);
}
}
async function undoDismiss(item: FileItem) {
if (!item.discrepancy_id) return;
try {
@@ -82,13 +106,22 @@
}
}
async function navigateTo(path: string) {
function navigateTo(path: string) {
currentPath = path;
loadFiles(path);
}
const missingItems = $derived(discrepancies.filter(d => d.is_deleted));
const pendingItems = $derived(discrepancies.filter(d => !d.is_deleted));
// Statistics
const missingWithNoBackup = $derived(
discrepancies.filter(d => d.is_deleted && !d.has_versions).length
);
const missingWithBackup = $derived(
discrepancies.filter(d => d.is_deleted && d.has_versions).length
);
onMount(loadDiscrepancies);
</script>
@@ -96,7 +129,7 @@
<title>Discrepancies - TapeHoard</title>
</svelte:head>
<div class="flex flex-col gap-6 flex-1 min-h-0 animate-in fade-in duration-700">
<div class="flex flex-col gap-6 h-full animate-in fade-in duration-700">
<PageHeader
title="Discrepancies"
description="Files missing from disk or confirmed deleted"
@@ -130,12 +163,22 @@
{:else}
<!-- Summary Statistics -->
<div class="grid grid-cols-2 gap-3 shrink-0">
<StatCard label="Missing from disk" value={missingItems.length} subLabel="Files the scanner did not find" variant="error" />
<StatCard label="Pending confirmation" value={pendingItems.length} subLabel="Tracked files not yet confirmed" variant="warning" />
<StatCard
label="Missing with no backup"
value={missingWithNoBackup}
subLabel="Files missing from disk with no copies on archive media"
variant="error"
/>
<StatCard
label="Missing with backup"
value={missingWithBackup}
subLabel="Files missing from disk but have copies on archive media"
variant="warning"
/>
</div>
<!-- FileBrowser Component in discrepancies mode -->
<div class="flex-1 min-h-0 overflow-hidden">
<div class="flex-1 min-h-[600px] bg-bg-secondary border border-border-color shadow-2xl rounded-lg flex flex-col relative overflow-hidden">
<FileBrowser
bind:currentPath={currentPath}
files={files}