test: add dashboard redesign tests (refs #201)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 3m22s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 51s
Deploy to Staging / Verify Staging (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-02-15 11:03:52 -06:00
parent 654a7f0fc3
commit f6684e72c0
9 changed files with 748 additions and 65 deletions

View File

@@ -0,0 +1,71 @@
/**
* @ai-summary Pure function to compute per-vehicle health status from maintenance and document data
*/
import { MaintenanceSchedule } from '../../maintenance/types/maintenance.types';
import { DocumentRecord } from '../../documents/types/documents.types';
import { VehicleHealth, AttentionItem } from '../types';
/**
* Compute health status and attention items for a single vehicle.
* Pure function -- no React dependencies, easily unit-testable.
*/
export function computeVehicleHealth(
schedules: MaintenanceSchedule[],
documents: DocumentRecord[],
): { health: VehicleHealth; attentionItems: AttentionItem[] } {
const now = new Date();
const items: AttentionItem[] = [];
// Maintenance schedule attention items
for (const schedule of schedules) {
if (!schedule.nextDueDate || !schedule.isActive) continue;
const dueDate = new Date(schedule.nextDueDate);
const daysUntil = Math.floor((dueDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
const label = schedule.subtypes.length > 0
? schedule.subtypes[0]
: schedule.category.replace(/_/g, ' ');
if (daysUntil < 0) {
items.push({ label, urgency: 'overdue', daysUntilDue: daysUntil, source: 'maintenance' });
} else if (daysUntil <= 14) {
items.push({ label, urgency: 'due-soon', daysUntilDue: daysUntil, source: 'maintenance' });
} else if (daysUntil <= 30) {
items.push({ label, urgency: 'upcoming', daysUntilDue: daysUntil, source: 'maintenance' });
}
}
// Document expiry attention items (insurance, registration)
for (const doc of documents) {
if (!doc.expirationDate) continue;
const expiryDate = new Date(doc.expirationDate);
const daysUntil = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
const label = doc.documentType === 'insurance' ? 'Insurance' : 'Registration';
if (daysUntil < 0) {
items.push({ label, urgency: 'overdue', daysUntilDue: daysUntil, source: 'document' });
} else if (daysUntil <= 14) {
items.push({ label, urgency: 'due-soon', daysUntilDue: daysUntil, source: 'document' });
} else if (daysUntil <= 30) {
items.push({ label, urgency: 'upcoming', daysUntilDue: daysUntil, source: 'document' });
}
}
// Sort: overdue first (most overdue at top), then due-soon by proximity, then upcoming
const urgencyOrder = { overdue: 0, 'due-soon': 1, upcoming: 2 };
items.sort((a, b) => {
const urgencyDiff = urgencyOrder[a.urgency] - urgencyOrder[b.urgency];
if (urgencyDiff !== 0) return urgencyDiff;
return a.daysUntilDue - b.daysUntilDue;
});
// Determine health color
const hasOverdue = items.some(i => i.urgency === 'overdue');
const hasDueSoon = items.some(i => i.urgency === 'due-soon');
let health: VehicleHealth = 'green';
if (hasOverdue) health = 'red';
else if (hasDueSoon) health = 'yellow';
return { health, attentionItems: items.slice(0, 3) };
}