mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
Statistics now aggregate over PrintLogEntry (one row per print event,
the same table backing the global Print Log) rather than PrintArchive
(one row per file). A reprint creates a new PrintLogEntry instead of
overwriting the source archive's runtime fields, so:
- a 100 g successful print + a 10 g failed reprint correctly sums to
110 g / 2 prints / 1 successful / 1 failed in Quick Stats and the
Prometheus /metrics endpoint (previously the failed reprint silently
replaced the source archive's data; totals dropped from 100 g to 10 g)
- the archive's card cost/energy_kwh are preserved on reprints (only
the first run writes them); per-run actuals live on PrintLogEntry
- failed/cancelled/stopped reprints record partial-aware filament: sum
of tracked spool deltas when inventory is set up, else estimate
scaled to progress%, else None — prevents the full slicer estimate
from inflating totals on a print that stopped at 10 % progress
PrintLogEntry gains six columns: archive_id (nullable FK, ON DELETE
SET NULL so log entries survive archive deletion preserving #1343
soft-delete-vs-stats decoupling), cost, energy_kwh, energy_cost,
failure_reason, created_by_id. Idempotent SQLite + Postgres migrations.
New per-archive surface:
- archive list response carries run_count / last_run_at /
total_filament_actual_grams / successful_run_count / failed_run_count
via a single batch JOIN, no N+1
- new GET /archives/{id}/runs endpoint returns every PrintLogEntry for
the archive (ARCHIVES_READ permission, newest-first ordering)
- archive cards render an orange "N prints" badge for archives with
more than one run; clicking the badge opens a dedicated PrintLogModal
with date/status/duration/filament/cost columns plus failure_reason
under failed runs. Also reachable via the context menu's new "Print
Log" entry (works for single-run archives too), and embedded at the
top of the Edit Archive modal for context.
The purge_stats=true delete path now hard-deletes linked PrintLogEntry
rows up front so the archive's contribution truly leaves the totals;
without it, ON DELETE SET NULL would orphan the runs and leave them
counting toward stats.
54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { X, History } from 'lucide-react';
|
|
import { PrintLogTable } from './PrintLogTable';
|
|
|
|
interface PrintLogModalProps {
|
|
archiveId: number;
|
|
archiveName: string | null;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function PrintLogModal({ archiveId, archiveName, onClose }: PrintLogModalProps) {
|
|
const { t } = useTranslation();
|
|
|
|
useEffect(() => {
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
if (e.key === 'Escape') onClose();
|
|
};
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
}, [onClose]);
|
|
|
|
return (
|
|
<div
|
|
className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4"
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className="bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary w-full max-w-2xl max-h-[85vh] flex flex-col"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<div className="flex items-center justify-between px-6 py-4 border-b border-bambu-dark-tertiary">
|
|
<div className="flex items-center gap-2 min-w-0">
|
|
<History className="w-5 h-5 text-bambu-green flex-shrink-0" />
|
|
<h2 className="text-lg font-semibold text-white truncate" title={archiveName || ''}>
|
|
{t('archives.runLog.modalTitle', { name: archiveName || t('archives.runLog.modalTitleFallback') })}
|
|
</h2>
|
|
</div>
|
|
<button
|
|
onClick={onClose}
|
|
className="text-bambu-gray hover:text-white transition-colors"
|
|
title={t('common.close')}
|
|
>
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
<div className="p-6 overflow-y-auto flex-1">
|
|
<PrintLogTable archiveId={archiveId} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|