Skip to main content

Module: Dashboard

The landing page after login. Gives a clinic staffer a one-glance picture of the day.

Purpose

  • Answer "what's going on right now?" — upcoming appointments, outstanding invoices, pending reminders.
  • Provide quick stats that indicate clinic health.

What the user sees

  • Top row — four stat cards:
    • Total Patients — count of active pets.
    • Appointments This Week — count of appointments.scheduled_start BETWEEN this_monday AND next_monday.
    • Pending RevenueSUM(balance_due) across non-void invoices.
    • Completed Visits — count of appointments.status = 'completed'.
  • Upcoming appointments — next 5 rows with patient, client, vet, time, status badge.
  • Recent invoices — last 5 invoices with status badges.
  • Reminders widget — pending reminders for the next 7 days (sidebar column).

Data sources

StatSource call
Total Patientsservices.patients.list({ tenantId }) — response meta count
Appointmentsservices.appointments.list({ tenantId }) — filtered client-side
Pending Revenueservices.invoices.list({ tenantId }) — summed client-side
Completed Visitsservices.appointments.list({ tenantId }) — filtered by status == completed

A backend-side GET /tenant/{tenant}/dashboard endpoint already exists (Clinic/DashboardController::show) and returns the counts more efficiently. The SPA should migrate to calling that endpoint to avoid loading three lists when only aggregates are shown.

Implementation pointer

ui/src/components/dashboard/Dashboard.js. The top of the component fires three useEffect fetches on mount, normalizes them via lib/normalizers.js, and derives everything else in memoized selectors.

Empty state

When the tenant has no data yet (freshly onboarded clinic), the dashboard renders zeros. A future enhancement: replace zeros with onboarding call-to-action cards ("Add your first pet", "Create your first appointment") that deep-link into the relevant module.