Sprint 3: Alpine.js Dashboards — Login, CS/FM/CEO Views, Ticket Management #3
@@ -218,15 +218,22 @@
|
|||||||
});
|
});
|
||||||
this.charts.monthlyMax = Math.max(...this.charts.monthlyTrend.map(m => m.count), 1);
|
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 };
|
const propData = { east: 0, west: 0 };
|
||||||
all.forEach(t => { /* would need unit join — estimate from ticket IDs */ });
|
|
||||||
try {
|
try {
|
||||||
const east = await app().apiGet('/api/tickets?property=East&page_size=1');
|
const units = await app().apiGet('/api/tickets/units');
|
||||||
const west = await app().apiGet('/api/tickets?property=West&page_size=1');
|
if (units && units.length > 0) {
|
||||||
propData.east = east?.total || 0;
|
const unitPropertyMap = {};
|
||||||
propData.west = west?.total || 0;
|
units.forEach(u => { unitPropertyMap[u.id] = u.property; });
|
||||||
} catch (e) { /* api may not support property filter directly */ }
|
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) };
|
this.charts.byProperty = { east: propData.east, west: propData.west, max: Math.max(propData.east, propData.west, 1) };
|
||||||
|
|
||||||
// By category — use categories endpoint
|
// By category — use categories endpoint
|
||||||
|
|||||||
@@ -115,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<dt class="text-sm text-gray-500">Assigned To</dt>
|
<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>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<dt class="text-sm text-gray-500">Reporter</dt>
|
<dt class="text-sm text-gray-500">Reporter</dt>
|
||||||
@@ -402,7 +402,7 @@
|
|||||||
this.noteForm.note = '';
|
this.noteForm.note = '';
|
||||||
app().showToast('Note added', 'success');
|
app().showToast('Note added', 'success');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
app().showToast(e.message, 'error');
|
// No need to show error here — api() base method already shows it
|
||||||
} finally {
|
} finally {
|
||||||
this.noteSubmitting = false;
|
this.noteSubmitting = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
<option value="walk-in">Walk-in</option>
|
<option value="walk-in">Walk-in</option>
|
||||||
<option value="whatsapp">WhatsApp</option>
|
<option value="whatsapp">WhatsApp</option>
|
||||||
<option value="agent">Agent</option>
|
<option value="agent">Agent</option>
|
||||||
<option value="qr">QR Code</option>
|
<!-- QR Code excluded per Sprint 3 scope -->
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user