Skip to main content

Components

Top-level feature pages are the *Module.js files; everything else is either a subcomponent of a module or a shared primitive in components/common/.

Top-level layout

App.js

Holds the module switch and the chrome (sidebar + header). Pseudocode:

function App() {
const { isAuthenticated } = useAuth();
const [activeModule, setActiveModule] = useState('dashboard');

if (!isAuthenticated) return <AuthModule />;

return (
<div className="flex min-h-screen">
<Sidebar active={activeModule} onSelect={setActiveModule} />
<main className="flex-1 p-6">
{activeModule === 'dashboard' && <Dashboard />}
{activeModule === 'patients' && <PatientsModule />}
{activeModule === 'appointments' && <AppointmentsModule />}
{activeModule === 'prescriptions' && <PrescriptionsModule />}
{activeModule === 'reminders' && <RemindersModule />}
{activeModule === 'medical' && <MedicalModule />}
{activeModule === 'inventory' && <InventoryDashboard />}
{activeModule === 'billing' && <BillingModule />}
{activeModule === 'reports' && <ReportsModule />}
</main>
</div>
);
}

components/auth/AuthModule.js

Unauthenticated entry point. Two tabs: Login and Register. Calls useAuth().login(email, password) or the owner-registration service.

components/layout/Sidebar.js

  • Reads the current tenant from TenantContext to render clinic name/logo at the top.
  • Renders nav items with icons and an active highlight.
  • Footer shows clinic address/phone/email.
  • Exposes a callback onSelect(moduleKey) so App can update state.

components/tenant/TenantSwitcher.js

Dropdown in the top-right of App.js. Lists every tenant from TenantContext and calls switchTenant(id) on click.

Dashboard module

Dashboard.js

  • On mount, calls services.patients.list(), services.appointments.list(), services.invoices.list() in parallel.
  • Normalizes results with lib/normalizers.js.
  • Derives stats:
    • Total patients.
    • Appointments this week.
    • Pending revenue = sum of invoice.balance.
    • Completed visits.
  • Renders RemindersWidget in the side column.

RemindersWidget.js

Displays the upcoming reminders for the current tenant. Stubbed against services.patients/mock data today; ready to be wired to a dedicated reminders endpoint.

Patients module

PatientsModule.js

  • Table of pets with columns: Name, Species, Breed, Age, Owner, Status, Last Visit, Actions.
  • "Add Patient" opens PatientForm in a modal.
  • Row click opens PatientProfile.
  • Patient creation/update goes through services.patients.create(...) / services.patients.update(...) and updates local state on success.

PatientForm.js

Form for both "Add" and "Edit" modes. Uses useState for each field; no form library. Submits to the right service method based on whether an id is present.

PatientProfile.js

Detail pane — shows demographics, medical flags, allergies, appointments history (fetched from services.appointments filtered to the pet).

PatientDetails.js / PatientEditForm.js

Alternate/legacy detail and edit components. One can be retired during consolidation.

Appointments module

AppointmentsModule.js

Paginated list of upcoming and past appointments with status badges. Row actions let a staffer check in, complete or cancel an appointment — each maps to services.appointments.updateStatus(...).

AppointmentForm.js

  • Scheduling form — pet picker, client picker (auto-filled from pet), vet picker, appointment type, start/end date-time, reason.
  • booking_source defaults to clinic_portal.

Billing module

BillingModule.js

  • Two tabs: Invoices and Payments.
  • Invoice list shows number, client, grand total, balance, status badge.
  • Inline "Record payment" action creates a Payment against the invoice via services.invoices.recordPayment().

Inventory module

InventoryDashboard.js

Parent layout with nav tabs: Medicines, Movements, Add medicine.

MedicineInventory.js

Table of current stock. Fetches from services.pharmacy.

AddMedicineForm.js, StockAdjustmentForm.js, StockAdjustmentModal.js, StockHistoryModal.js, StockMovementHistory.js

Form and modal components for CRUD on inventory items and stock movements.

Prescriptions module

PrescriptionsModule.js

List of prescriptions per patient.

PrescriptionForm.js / DispenseMedicine.js

Issue a prescription and mark it as dispensed against inventory.

Reminders module

RemindersModule.js

List of reminders with filters by type (vaccination, deworming, follow_up, payment_due, general) and status (pending, sent, dismissed, failed). Allows marking as "dismissed" or "sent".

Medical records module

MedicalModule.js

Stub today — placeholder for clinical notes and treatment plans. A future expansion target.

Reports module

ReportsModule.js

Stub today. Expected to surface aggregate metrics per month (revenue, appointments, species mix).

Shared primitives

common/Badge.js

Renders a pill with colour derived from the status:

<Badge status="paid" /> // green
<Badge status="partial" /> // amber
<Badge status="void" /> // gray

common/Button.js

A styled button wrapping <button>. Variants: primary, secondary, ghost, danger.

common/Table.js

Reusable table component with:

  • columns — array of { key, label, render?(row) }.
  • rows — data.
  • onRowClick?(row) — optional.
  • Basic pagination when rows.length > pageSize.

Patterns

  • Data fetching happens in the top-level module component and is passed to children as props. Sub-components don't call services themselves.
  • Forms use local useState; no Formik/React Hook Form. For a future large-form overhaul, adopting RHF would reduce boilerplate.
  • Modals are implemented as portals to <body> via React's native createPortal. CSS comes from Tailwind utilities (fixed inset-0 bg-black/50 …).
  • Empty / loading / error states are handled inline inside each module with conditional rendering. Extracting a <DataState> wrapper would be a nice refactor.