Dashboard
The operational landing screen for authenticated staff — KPIs, financial summary, quick actions, pending approvals, active groups, inventory utilisation, and an inline general-ledger search.
Page
src/pages/Dashboard.tsx— mounted at/app.
Route (src/App.tsx:149):
<Route path="/app"
element={<ProtectedRoute allowedRoles={['admin', 'staff']}>
<Dashboard />
</ProtectedRoute>} />
Permission drift
The route uses allowedRoles=['admin','staff'] instead of requiredPermissions={['dashboard.view']}. dashboard.view is seeded (migration 20260416030000) but not yet enforced by the route. Tracked as drift item #2 in docs/PERMISSIONS.md §8.
Widgets (top to bottom)
Primary KPI row
Four KPICard tiles (Dashboard.tsx:339):
| Tile | Source | Drills into |
|---|---|---|
| Total Bookings | Count of bookings |
/sales/bookings |
| Pending Approvals | Bookings where opsApprovalStatus === 'pending' |
/approvals |
| Active Groups | Groups where status in ('open','planning') |
/groups |
| Total Revenue | Sum of verified/refunded payment amounts + outstanding balance subtitle | /finance |
Financial summary row (when available)
Four more tiles (Dashboard.tsx:372) pulled from journal lines + accounts:
| Tile | Source |
|---|---|
| Monthly Revenue | Sum (credit − debit) of this-month INCOME journal lines |
| Monthly Expenses | Sum (debit − credit) of this-month EXPENSE lines |
| Net Profit | Monthly Revenue − Monthly Expenses |
| Outstanding Receivables | Σ balanceAmount across all bookings |
Requires GET /finance/journals/lines?limit=5000 and GET /finance/accounts; silently hidden if the requests fail.
FinanceHeadlineRow
src/components/dashboard/FinanceHeadlineRow.tsx — additional finance headlines (today's vs yesterday's cash-in / cash-out etc.). Gracefully hides when the underlying RPC is unavailable.
Quick Actions grid
Nine action tiles (Dashboard.tsx:410). The first four open in-place dialogs; the rest are links:
| Tile | Target | Dialog / Route |
|---|---|---|
| Add Account | dialog | POST /finance/accounts |
| Add Customer | dialog | CustomerFormDialog (customers.create) |
| Record Payment | dialog | POST /finance/payments (finance.create) |
| Journal Entry | dialog | POST /finance/journals — quick 2-line Dr/Cr (finance.create) |
| Bookings | link | /sales/bookings |
| Vouchers | link | /finance?tab=vouchers |
| Accounts | link | /finance?tab=accounts |
| Groups | link | /groups |
| Reports | link | /finance?tab=reports |
Ledger search
A typeahead over GL accounts that opens a full account ledger dialog (date, voucher no., memo, debit, credit, running balance, closing totals). Pulls GET /finance/journals and flattens the line array per account. Display names for AGR- / AGP- / CUS- / SUP- accounts strip the "Receivable — " / "Payable — " prefix (getDisplayName, Dashboard.tsx:256).
Tickets-due-soon banner
Conditional amber banner if any tickets fall within 2 days of their nameUpdateDeadline and aren't yet issued. Links to /tickets.
Pending approvals + recent activity
- Pending Operations Approval — first 5 rows from
bookings.filter(b => b.opsApprovalStatus === 'pending'). Click-through to/approvals. - Recent Activity — 5 latest audit log rows rendered as a
Timeline.
Active travel groups
Capacity/progress tiles for any group where status !== 'completed'. Shows booked / total capacity and departure date.
Inventory utilisation
Three progress-bar lists from GET /inventory/utilization:
- Airline Seats
- Hotel Rooms
- Food / Meals
Gracefully hides when the endpoint is unavailable.
Quick stats row
Three small cards: Visas in Pipeline, Tickets Due Soon, Departures This Month.
Role-specific variants
There is one dashboard implementation served to all staff roles; differentiation is by permission-filtered nav, not by rendering different tiles.
- Agent portal dashboard lives at
/partner(src/pages/partner/AgentPortal.tsx) and is a separate component. - Customer portal dashboard lives at
/customer(src/pages/customer/CustomerPortal.tsx).
Data sources
| Source | Fetcher |
|---|---|
| Bookings | fetchBookings() — src/services/bookingService.ts |
| Payments | fetchPayments() |
| Groups | fetchGroups() |
| Visa cases | fetchVisaCases() |
| Ticket records | fetchTicketRecords() |
| Audit logs | fetchAuditLogs() |
| Inventory utilisation | GET /inventory/utilization |
| Journal lines | GET /finance/journals/lines?limit=5000 |
| Accounts | GET /finance/accounts |
| Finance journals (ledger drill-in) | GET /finance/journals |
Permissions
| Action | Permission | Enforcement |
|---|---|---|
View /app |
dashboard.view (seeded) / allowedRoles=['admin','staff'] (actual) |
Route guard — see drift note above |
| Quick-add account | finance.create |
Server |
| Quick-add customer | customers.create |
Server |
| Quick-record payment | finance.create |
Server |
| Quick journal entry | finance.create |
Server |
| Open ledger | finance.view |
Server |
Related
- Approvals queue →
docs/features/approvals.md. - Finance module (all finance tiles link here) →
docs/features/finance/index.md. - Tickets name-update deadline rule →
docs/features/tickets.md.