# 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 ``: ```html
``` Each card follows this exact template: ```html
...
0

Card Title

Status label
``` **Key:** - Each card gets a unique `id` on its count `` (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-4` to `lg:grid-cols-6` (or `lg:grid-cols-3` for 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:** ```js // 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), `.customerName` - `dashboardData.appointments` — objects with `.status`, `.appointmentDateTime` (Date), `.customerName` **Available 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 cards - `ro.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): ```js 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 1. **HTML**: Add the card `
` inside the Quick Metrics grid, following the template above. Ensure the count `` has a unique ID. 2. **Grid**: If total cards exceed the current `lg:grid-cols-N` value, bump it. 3. **JS**: In `renderMetrics()`, after the existing quote section (~line 1674), add filter logic + DOM update. 4. **Click handler (optional)**: If the card should navigate somewhere, add it in `addMetricClickHandlers()`. 5. **Verify**: The card will populate automatically on next data refresh (periodic auto-refresh + real-time PocketBase subscriptions).