4.7 KiB
Dashboard Metric Cards
The ShopProQuote dashboard (dashboard.html / dashboard.js) has a set of Quick Metric cards that display live counts from the repair orders, appointments, tasks, and quotes collections.
Architecture
HTML — Card Structure
Cards live in a Tailwind grid inside <!-- Quick Metrics -->:
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-6 mb-8">
Each card follows this exact template:
<!-- Card Label -->
<div class="metric-card rounded-xl p-6 shadow-lg">
<div class="flex items-center justify-between mb-4">
<div class="p-2 bg-COLOR-100 dark:bg-COLOR-900 rounded-lg">
<!-- SVG icon matching the metric theme -->
<svg class="w-6 h-6 text-COLOR-600 dark:text-COLOR-400" ...>...</svg>
</div>
<span id="UNIQUE-ID-count" class="text-3xl font-bold text-gray-900 dark:text-white">0</span>
</div>
<h3 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-2">Card Title</h3>
<div class="text-xs text-gray-500 dark:text-gray-500" id="UNIQUE-ID-breakdown">
<!-- Optional sub-items with status-indicator spans -->
<span class="status-indicator status-COLOR"></span>Status label
</div>
</div>
Key:
- Each card gets a unique
idon its count<span>(e.g.,parts-on-order-count,ready-for-pickup-count) - Color variants:
bg-{color}-100,text-{color}-600(light) /bg-{color}-900,text-{color}-400(dark) - Status indicators use the existing class system:
status-overdue(red),status-warning(amber),status-good(green),status-info(blue) - When adding cards, count the total. If > 4, bump
lg:grid-cols-4tolg:grid-cols-6(orlg:grid-cols-3for two rows of 3).
JS — Data Population (renderMetrics())
The renderMetrics() function in dashboard.js (~line 1528) populates all metric cards. It's called from renderDashboard() on every data refresh.
Pattern for adding a new card:
// 1. Compute the value from dashboardData
const myMetric = dashboardData.repairOrders.filter(ro =>
(ro.status || '').toLowerCase() === 'my-status-value'
).length;
// 2. Log for debugging
log('My Metric count:', myMetric);
// 3. Update DOM with null-safe access
const myMetricEl = document.getElementById('my-metric-count');
if (myMetricEl) myMetricEl.textContent = myMetric;
Available data sources for filtering:
dashboardData.repairOrders— objects with.status(string),.writeupTime(Date),.promisedTime(Date),.customerNamedashboardData.appointments— objects with.status,.appointmentDateTime(Date),.customerNameAvailable data sources for filtering:dashboardData.repairOrders— objects with.workStatus(string: diagnosing/in-progress/waiting-for-approval/waiting-for-parts/waiting-for-pickup/completed),.status(string: waiter/drop-off — customer service status, NOT work progress),.writeupTime(Date),.promisedTime(Date),.customerName
CRITICAL: ro.status ≠ ro.workStatus
ro.status= customer service status (waiter,drop-off) — used for the red/blue service badge on RO cardsro.workStatus= work progress status (diagnosing,in-progress,waiting-for-approval,waiting-for-parts,waiting-for-pickup,completed) — used for work progress badges and ALL completion/dashboard filtering logic
NEVER filter by ro.status === 'completed' — it will always match nothing because status only stores waiter/drop-off. Always use ro.workStatus === 'completed'.
Common repair order workStatus values used in filters:
'diagnosing','in-progress','waiting-for-approval','waiting-for-parts','waiting-for-pickup','completed'
Click Handlers
Metric cards can optionally be made clickable. Follow the existing pattern in addMetricClickHandlers() (~line 1888):
const myCard = document.querySelector('#my-metric-count')?.closest('.metric-card');
if (myCard) {
myCard.style.cursor = 'pointer';
myCard.addEventListener('click', () => {
window.location.href = 'target-page.html';
});
}
Adding a New Metric Card — Full Recipe
- HTML: Add the card
<div>inside the Quick Metrics grid, following the template above. Ensure the count<span>has a unique ID. - Grid: If total cards exceed the current
lg:grid-cols-Nvalue, bump it. - JS: In
renderMetrics(), after the existing quote section (~line 1674), add filter logic + DOM update. - Click handler (optional): If the card should navigate somewhere, add it in
addMetricClickHandlers(). - Verify: The card will populate automatically on next data refresh (periodic auto-refresh + real-time PocketBase subscriptions).