From c7377bfb0c3a7dc084f02615119f8ece82e336e8 Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 14 Jan 2026 16:26:58 +0100 Subject: [PATCH] Fix inconsistent and confusing project statistics display Backend: - Count print job records instead of summing quantities for stats - Include "archived" status as successful (completed + archived) - Include all failure states (failed, aborted, cancelled, stopped) - Add completed_count and failed_count to ProjectListResponse Frontend ProjectsPage: - Show completed_count instead of total_items in progress displays - Progress bar now shows "X / Y completed" to match percentage - Add failed count indicator (red) when failures exist - Footer stats show completed (green checkmark) and failed (red) separately Frontend ProjectDetailPage: - Replace confusing "Total Items" card with "Print Jobs" card - Show "X successful" as main value, "Y failed" below in red - Show "Z in progress" in yellow for unfinished prints - Progress bar text changed from "items" to "completed" - Add hint prop to StatCard component for tooltips This ensures stats are consistent between: - Project list page and detail page - Stats cards and archive list counts - Progress percentages and displayed fractions Fixes #85 --- frontend/src/api/client.ts | 4 ++- frontend/src/pages/ProjectDetailPage.tsx | 44 +++++++++++++---------- frontend/src/pages/ProjectsPage.tsx | 45 ++++++++++++++++-------- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index a5f33dd98..dfc42343e 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -412,7 +412,9 @@ export interface ProjectListItem { target_count: number | null; created_at: string; archive_count: number; // Number of print jobs - total_items: number; // Sum of quantities (total items printed) + total_items: number; // Sum of quantities (total items printed, including failed) + completed_count: number; // Sum of quantities for completed prints only + failed_count: number; // Sum of quantities for failed prints queue_count: number; progress_percent: number | null; archives: ArchivePreview[]; diff --git a/frontend/src/pages/ProjectDetailPage.tsx b/frontend/src/pages/ProjectDetailPage.tsx index 8a94f84ba..1970dc7cb 100644 --- a/frontend/src/pages/ProjectDetailPage.tsx +++ b/frontend/src/pages/ProjectDetailPage.tsx @@ -79,18 +79,20 @@ function StatCard({ label, value, subValue, + hint, color = 'text-bambu-gray', }: { icon: React.ElementType; label: string; value: string | number; subValue?: string; + hint?: string; color?: string; }) { return ( -
+
@@ -435,9 +437,6 @@ export function ProjectDetailPage() { const stats = project.stats; const progressPercent = stats?.progress_percent ?? 0; - const successRate = stats && stats.total_items > 0 - ? ((stats.completed_prints / stats.total_items) * 100).toFixed(0) - : null; return (
@@ -486,7 +485,7 @@ export function ProjectDetailPage() {
Progress - {stats?.completed_prints || 0} / {project.target_count} items + {stats?.completed_prints || 0} / {project.target_count} completed
@@ -515,20 +514,25 @@ export function ProjectDetailPage() { {/* Stats grid */} {stats && (
- - 0 ? `${stats.failed_prints} failed` : (successRate ? `${successRate}% success` : undefined)} - color="text-blue-400" - /> + + +
+
+ +
+
+

Print Jobs

+

{stats.completed_prints} successful

+ {stats.failed_prints > 0 && ( +

{stats.failed_prints} failed

+ )} + {stats.total_archives - stats.completed_prints - stats.failed_prints > 0 && ( +

{stats.total_archives - stats.completed_prints - stats.failed_prints} in progress

+ )} +
+
+
+
- {project.total_items}/{project.target_count} items + {project.completed_count}/{project.target_count} completed - ) : project.total_items > 0 ? ( + ) : project.completed_count > 0 ? ( - {project.total_items} item{project.total_items !== 1 ? 's' : ''} + {project.completed_count} completed ) : null} {isCompleted && ( @@ -377,7 +377,7 @@ function ProjectCard({ project, onClick, onEdit, onDelete }: ProjectCardProps) {
Progress = 100 ? 'text-bambu-green font-medium' : 'text-white'}> - {project.total_items} / {project.target_count} + {project.completed_count} / {project.target_count}
@@ -392,16 +392,27 @@ function ProjectCard({ project, onClick, onEdit, onDelete }: ProjectCardProps) { }} />
-
- {progressPercent.toFixed(0)}% complete +
+ + {project.failed_count > 0 && `${project.failed_count} failed`} + + {progressPercent.toFixed(0)}% complete
- ) : project.total_items > 0 ? ( + ) : project.completed_count > 0 || project.failed_count > 0 ? (
-
- - {project.total_items} item{project.total_items !== 1 ? 's' : ''} completed -
+ {project.completed_count > 0 && ( +
+ + {project.completed_count} completed +
+ )} + {project.failed_count > 0 && ( +
+ + {project.failed_count} failed +
+ )} {project.queue_count > 0 && (
@@ -456,10 +467,16 @@ function ProjectCard({ project, onClick, onEdit, onDelete }: ProjectCardProps) { {/* Stats footer */}
-
- - {project.total_items} +
+ + {project.completed_count}
+ {project.failed_count > 0 && ( +
+ + {project.failed_count} +
+ )} {project.queue_count > 0 && (