feat: expose file attachment stats as item properties (#37)

Add file_count and files_total_size to item API responses, computed
via batch query on item_files table (no migration needed).

- Add BatchGetFileStats() to audit_queries.go (follows BatchCheckBOM pattern)
- Add file stats to ItemResponse, HandleListItems, HandleGetItem, HandleGetItemByUUID
- Add 'Files' column to ItemTable (default visible in vertical mode)
- Add has_files computed field to audit completeness scoring (weight 1 for manufactured)
This commit is contained in:
Forbes
2026-02-08 19:25:46 -06:00
parent 9be6f45f09
commit 50985ed805
5 changed files with 118 additions and 3 deletions

View File

@@ -19,6 +19,8 @@ export interface Item {
sourcing_link?: string;
long_description?: string;
standard_cost?: number;
file_count: number;
files_total_size: number;
properties?: Record<string, unknown>;
}

View File

@@ -12,6 +12,7 @@ export const ALL_COLUMNS: ColumnDef[] = [
{ key: "item_type", label: "Type" },
{ key: "description", label: "Description" },
{ key: "revision", label: "Rev" },
{ key: "files", label: "Files" },
{ key: "projects", label: "Projects" },
{ key: "created", label: "Created" },
{ key: "actions", label: "Actions" },
@@ -28,6 +29,7 @@ export const DEFAULT_COLUMNS_V = [
"item_type",
"description",
"revision",
"files",
"created",
"actions",
];
@@ -67,6 +69,12 @@ function copyPN(pn: string) {
void navigator.clipboard.writeText(pn);
}
function formatSize(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
export function ItemTable({
items,
loading,
@@ -120,6 +128,10 @@ export function ItemTable({
av = a.current_revision;
bv = b.current_revision;
break;
case "files":
av = a.file_count;
bv = b.file_count;
break;
case "created":
av = a.created_at;
bv = b.created_at;
@@ -271,6 +283,20 @@ export function ItemTable({
Rev {item.current_revision}
</td>
);
case "files":
return (
<td
key={col.key}
style={{ ...tdStyle, textAlign: "center" }}
title={
item.file_count > 0
? `${item.file_count} file${item.file_count !== 1 ? "s" : ""}, ${formatSize(item.files_total_size)}`
: "No files"
}
>
{item.file_count > 0 ? item.file_count : "—"}
</td>
);
case "projects":
return (
<td key={col.key} style={tdStyle}>