dont load directory treemap by default
Continuous Integration / backend-tests (push) Successful in 48s
Continuous Integration / frontend-check (push) Successful in 25s
Continuous Integration / e2e-tests (push) Failing after 7m49s

This commit is contained in:
2026-05-01 20:10:58 -04:00
parent c56973c254
commit 901b17f7cd
10 changed files with 296 additions and 98 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+120 -60
View File
@@ -170,6 +170,10 @@ export type DashboardStatsSchema = {
* Unprotected Data Size
*/
unprotected_data_size: number;
/**
* Discrepancies Count
*/
discrepancies_count: number;
/**
* Media Distribution
*/
@@ -684,6 +688,10 @@ export type TreeNodeSchema = {
* Has Children
*/
has_children?: boolean;
/**
* Children
*/
children?: Array<TreeNodeSchema>;
};
/**
@@ -1354,66 +1362,6 @@ export type GetSystemTreeSystemTreeGetResponses = {
export type GetSystemTreeSystemTreeGetResponse = GetSystemTreeSystemTreeGetResponses[keyof GetSystemTreeSystemTreeGetResponses];
export type GetDiscrepanciesTreeGetData = {
body?: never;
path?: never;
query?: {
/**
* Path
*/
path?: string | null;
};
url: '/system/discrepancies/tree';
};
export type GetDiscrepanciesTreeGetErrors = {
/**
* Validation Error
*/
422: HttpValidationError;
};
export type GetDiscrepanciesTreeGetError = GetDiscrepanciesTreeGetErrors[keyof GetDiscrepanciesTreeGetErrors];
export type GetDiscrepanciesTreeGetResponses = {
/**
* Successful Response
*/
200: Array<TreeNodeSchema>;
};
export type GetDiscrepanciesTreeGetResponse = GetDiscrepanciesTreeGetResponses[keyof GetDiscrepanciesTreeGetResponses];
export type BrowseDiscrepanciesGetData = {
body?: never;
path?: never;
query?: {
/**
* Path
*/
path?: string | null;
};
url: '/system/discrepancies/browse';
};
export type BrowseDiscrepanciesGetErrors = {
/**
* Validation Error
*/
422: HttpValidationError;
};
export type BrowseDiscrepanciesGetError = BrowseDiscrepanciesGetErrors[keyof BrowseDiscrepanciesGetErrors];
export type BrowseDiscrepanciesGetResponses = {
/**
* Successful Response
*/
200: BrowseResponseSchema;
};
export type BrowseDiscrepanciesGetResponse = BrowseDiscrepanciesGetResponses[keyof BrowseDiscrepanciesGetResponses];
export type ListDiscrepanciesSystemDiscrepanciesGetData = {
body?: never;
path?: never;
@@ -1557,6 +1505,34 @@ export type DismissDiscrepancySystemDiscrepanciesFileIdDismissPostResponses = {
200: unknown;
};
export type UndoDismissDiscrepancySystemDiscrepanciesFileIdUndoDismissPostData = {
body?: never;
path: {
/**
* File Id
*/
file_id: number;
};
query?: never;
url: '/system/discrepancies/{file_id}/undo-dismiss';
};
export type UndoDismissDiscrepancySystemDiscrepanciesFileIdUndoDismissPostErrors = {
/**
* Validation Error
*/
422: HttpValidationError;
};
export type UndoDismissDiscrepancySystemDiscrepanciesFileIdUndoDismissPostError = UndoDismissDiscrepancySystemDiscrepanciesFileIdUndoDismissPostErrors[keyof UndoDismissDiscrepancySystemDiscrepanciesFileIdUndoDismissPostErrors];
export type UndoDismissDiscrepancySystemDiscrepanciesFileIdUndoDismissPostResponses = {
/**
* Successful Response
*/
200: unknown;
};
export type DeleteFileRecordSystemDiscrepanciesFileIdDeleteData = {
body?: never;
path: {
@@ -1585,6 +1561,76 @@ export type DeleteFileRecordSystemDiscrepanciesFileIdDeleteResponses = {
200: unknown;
};
export type GetDiscrepanciesTreeSystemDiscrepanciesTreeGetData = {
body?: never;
path?: never;
query?: {
/**
* Path
*
* Root path to get tree for
*/
path?: string | null;
};
url: '/system/discrepancies/tree';
};
export type GetDiscrepanciesTreeSystemDiscrepanciesTreeGetErrors = {
/**
* Validation Error
*/
422: HttpValidationError;
};
export type GetDiscrepanciesTreeSystemDiscrepanciesTreeGetError = GetDiscrepanciesTreeSystemDiscrepanciesTreeGetErrors[keyof GetDiscrepanciesTreeSystemDiscrepanciesTreeGetErrors];
export type GetDiscrepanciesTreeSystemDiscrepanciesTreeGetResponses = {
/**
* Response Get Discrepancies Tree System Discrepancies Tree Get
*
* Successful Response
*/
200: Array<TreeNodeSchema>;
};
export type GetDiscrepanciesTreeSystemDiscrepanciesTreeGetResponse = GetDiscrepanciesTreeSystemDiscrepanciesTreeGetResponses[keyof GetDiscrepanciesTreeSystemDiscrepanciesTreeGetResponses];
export type BrowseDiscrepanciesSystemDiscrepanciesBrowseGetData = {
body?: never;
path?: never;
query?: {
/**
* Path
*
* Directory path to browse
*/
path?: string | null;
};
url: '/system/discrepancies/browse';
};
export type BrowseDiscrepanciesSystemDiscrepanciesBrowseGetErrors = {
/**
* Validation Error
*/
422: HttpValidationError;
};
export type BrowseDiscrepanciesSystemDiscrepanciesBrowseGetError = BrowseDiscrepanciesSystemDiscrepanciesBrowseGetErrors[keyof BrowseDiscrepanciesSystemDiscrepanciesBrowseGetErrors];
export type BrowseDiscrepanciesSystemDiscrepanciesBrowseGetResponses = {
/**
* Response Browse Discrepancies System Discrepancies Browse Get
*
* Successful Response
*/
200: {
[key: string]: unknown;
};
};
export type BrowseDiscrepanciesSystemDiscrepanciesBrowseGetResponse = BrowseDiscrepanciesSystemDiscrepanciesBrowseGetResponses[keyof BrowseDiscrepanciesSystemDiscrepanciesBrowseGetResponses];
export type ListStorageProvidersInventoryProvidersGetData = {
body?: never;
path?: never;
@@ -1788,6 +1834,20 @@ export type GetSystemAnalyticsInventoryInsightsGetResponses = {
200: unknown;
};
export type GetDirectoryTreemapInventoryDirectoriesGetData = {
body?: never;
path?: never;
query?: never;
url: '/inventory/directories';
};
export type GetDirectoryTreemapInventoryDirectoriesGetResponses = {
/**
* Successful Response
*/
200: unknown;
};
export type DetectUnregisteredMediaInventoryDetectGetData = {
body?: never;
path?: never;
@@ -24,8 +24,8 @@
getArchiveTreeInventoryTreeGet,
browseSystemPathSystemBrowseGet,
browseArchiveIndexInventoryBrowseGet,
getDiscrepanciesTreeGet,
browseDiscrepanciesGet,
getDiscrepanciesTreeSystemDiscrepanciesTreeGet,
browseDiscrepanciesSystemDiscrepanciesBrowseGet,
} from "$lib/api";
let {
@@ -198,7 +198,7 @@
onMount(async () => {
if (mode === "discrepancies") {
try {
const response = await getDiscrepanciesTreeGet({ query: { path: "ROOT" } });
const response = await getDiscrepanciesTreeSystemDiscrepanciesTreeGet({ query: { path: "ROOT" } });
if (response.data && Array.isArray(response.data)) {
discrepancyRoot.children = response.data.map((d: any) => ({
name: d.name,
@@ -5,7 +5,7 @@
import type { TreeNode } from "$lib/types";
import { cn } from "$lib/utils";
import FileBrowserTreeItem from "./FileBrowserTreeItem.svelte";
import { getSystemTreeSystemTreeGet, getArchiveTreeInventoryTreeGet, getDiscrepanciesTreeGet } from "$lib/api";
import { getSystemTreeSystemTreeGet, getArchiveTreeInventoryTreeGet, getDiscrepanciesTreeSystemDiscrepanciesTreeGet } from "$lib/api";
let {
node,
@@ -62,7 +62,7 @@
try {
let response;
if (mode === "discrepancies") {
response = await getDiscrepanciesTreeGet({
response = await getDiscrepanciesTreeSystemDiscrepanciesTreeGet({
query: { path: node.path }
});
} else {
+8
View File
@@ -162,6 +162,14 @@
<p class="text-[10px] font-medium text-text-secondary uppercase opacity-40">Pending archival</p>
</div>
</div>
<div class="space-y-4">
<div class="flex flex-col gap-1.5">
<span class="text-xs text-text-secondary opacity-60 block">Missing files</span>
<h4 class="text-3xl font-bold text-orange-400 mono tabular-nums">{(stats.discrepancies_count || 0).toLocaleString()}</h4>
<p class="text-[10px] font-medium text-text-secondary uppercase opacity-40">Unresolved discrepancies</p>
</div>
</div>
</div>
<div class="mt-8 pt-6 border-t border-border-color/30 grid grid-cols-2 gap-6">
@@ -14,8 +14,8 @@
batchHardDeleteSystemDiscrepanciesBatchDeletePost,
addFileToRecoveryQueueRestoresQueueFileFileIdPost,
batchAddToRecoveryQueueRestoresQueueBatchPost,
browseDiscrepanciesGet,
getDiscrepanciesTreeGet,
browseDiscrepanciesSystemDiscrepanciesBrowseGet,
getDiscrepanciesTreeSystemDiscrepanciesTreeGet,
type DiscrepancySchema,
} from '$lib/api';
import { type FileItem } from '$lib/types';
@@ -47,9 +47,9 @@
async function loadFiles(path: string) {
try {
const response = await browseDiscrepanciesGet({ query: { path } });
if (response.data?.files) {
files = response.data.files.map((d: any) => {
const response = await browseDiscrepanciesSystemDiscrepanciesBrowseGet({ query: { path } });
if (response.data && (response.data as any).files) {
files = (response.data as any).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
+42 -8
View File
@@ -18,13 +18,16 @@
import StatCard from '$lib/components/ui/StatCard.svelte';
import ProgressBar from '$lib/components/ui/ProgressBar.svelte';
import Treemap from '$lib/components/Treemap.svelte';
import { getSystemAnalyticsInventoryInsightsGet } from '$lib/api';
import { getSystemAnalyticsInventoryInsightsGet, getDirectoryTreemapInventoryDirectoriesGet } from '$lib/api';
import { cn, formatSize } from '$lib/utils';
import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation';
let insights = $state<any>(null);
let loading = $state(true);
let dirTreemapLoaded = $state(false);
let dirTreemapLoading = $state(false);
let dirTreemapData = $state<any[]>([]);
async function loadInsights() {
loading = true;
@@ -38,6 +41,22 @@
}
}
async function loadDirTreemap() {
if (dirTreemapLoaded) return;
dirTreemapLoading = true;
try {
const response = await getDirectoryTreemapInventoryDirectoriesGet();
if (response.data) {
dirTreemapData = mapDirectoryTree(response.data as any[]);
dirTreemapLoaded = true;
}
} catch (error) {
toast.error("Failed to load directory treemap");
} finally {
dirTreemapLoading = false;
}
}
function mapDirectoryTree(nodes: any[]): any[] {
if (!nodes) return [];
return nodes.filter((n: any) => n.size > 0).map((n: any) => ({
@@ -150,14 +169,29 @@
<!-- DIRECTORY BREAKDOWN (Full Width) -->
<Card class="p-5 shadow-xl flex flex-col gap-8 lg:col-span-2">
<SectionHeader title="Space by directory" icon={FolderTree} iconColor="text-emerald-500" />
<div class="flex-1 flex flex-col min-h-0 min-h-[500px]">
<Treemap
items={mapDirectoryTree(insights.directories)}
onSelect={handleDirectorySelect}
/>
<div class="flex items-center justify-between">
<SectionHeader title="Space by directory" icon={FolderTree} iconColor="text-emerald-500" />
{#if !dirTreemapLoaded}
<Button variant="outline" size="sm" onclick={loadDirTreemap} disabled={dirTreemapLoading}>
{dirTreemapLoading ? 'Loading...' : 'Load directory treemap'}
</Button>
{/if}
</div>
{#if dirTreemapLoaded}
<div class="flex-1 flex flex-col min-h-0 min-h-[500px]">
<Treemap
items={dirTreemapData}
onSelect={handleDirectorySelect}
/>
</div>
{:else}
<div class="flex-1 flex items-center justify-center min-h-[300px] border-2 border-dashed border-border-color rounded-xl">
<Button variant="outline" onclick={loadDirTreemap} disabled={dirTreemapLoading}>
{dirTreemapLoading ? 'Loading...' : 'Click to load directory treemap'}
</Button>
</div>
{/if}
</Card>
</div>
</div>