Sprint 3: Alpine.js Dashboards — Login, CS/FM/CEO Views, Ticket Management #3

Merged
abiba-bot merged 7 commits from fm/denya-onecare-s3 into main 2026-07-23 19:36:35 +00:00
3 changed files with 17 additions and 10 deletions
Showing only changes of commit a9c17d0703 - Show all commits
+14 -7
View File
@@ -218,15 +218,22 @@
});
this.charts.monthlyMax = Math.max(...this.charts.monthlyTrend.map(m => m.count), 1);
// By property
// By property — load units once and compute from ticket data
const propData = { east: 0, west: 0 };
all.forEach(t => { /* would need unit join — estimate from ticket IDs */ });
try {
const east = await app().apiGet('/api/tickets?property=East&page_size=1');
const west = await app().apiGet('/api/tickets?property=West&page_size=1');
propData.east = east?.total || 0;
propData.west = west?.total || 0;
} catch (e) { /* api may not support property filter directly */ }
const units = await app().apiGet('/api/tickets/units');
if (units && units.length > 0) {
const unitPropertyMap = {};
units.forEach(u => { unitPropertyMap[u.id] = u.property; });
all.forEach(t => {
if (t.unit_id && unitPropertyMap[t.unit_id]) {
const p = unitPropertyMap[t.unit_id].toLowerCase();
if (p === 'east') propData.east++;
else if (p === 'west') propData.west++;
}
});
}
} catch (e) { console.error('Property stats error', e); }
this.charts.byProperty = { east: propData.east, west: propData.west, max: Math.max(propData.east, propData.west, 1) };
// By category — use categories endpoint
+2 -2
View File
@@ -115,7 +115,7 @@
</div>
<div class="flex justify-between">
<dt class="text-sm text-gray-500">Assigned To</dt>
<dd class="text-sm font-medium text-gray-900" x-text="ticket.assigned_technician?.full_name || 'Unassigned'"></dd>
<dd class="text-sm font-medium text-gray-900" x-text="ticket.assigned_technician_name || 'Unassigned'"></dd>
</div>
<div class="flex justify-between">
<dt class="text-sm text-gray-500">Reporter</dt>
@@ -402,7 +402,7 @@
this.noteForm.note = '';
app().showToast('Note added', 'success');
} catch (e) {
app().showToast(e.message, 'error');
// No need to show error here — api() base method already shows it
} finally {
this.noteSubmitting = false;
}
+1 -1
View File
@@ -104,7 +104,7 @@
<option value="walk-in">Walk-in</option>
<option value="whatsapp">WhatsApp</option>
<option value="agent">Agent</option>
<option value="qr">QR Code</option>
<!-- QR Code excluded per Sprint 3 scope -->
</select>
</div>
</div>