Skip to main content

Frontend Structure

ui/
├── public/
│ └── index.html
├── src/
│ ├── App.js # Root component, module switcher
│ ├── index.js # ReactDOM.render
│ ├── index.css # Tailwind directives + globals
│ ├── reportWebVitals.js # CRA default
│ ├── setupTests.js # Jest setup
│ ├── contexts/
│ │ ├── AuthContext.js # Current user, login/logout
│ │ └── TenantContext.js # Active clinic, tenants list
│ ├── hooks/
│ │ ├── usePatients.js
│ │ └── useAppointments.js
│ ├── services/
│ │ ├── index.js # createApiServices() factory
│ │ ├── mockData.js # Static mock data
│ │ ├── core/
│ │ │ ├── httpClient.js # fetch wrapper
│ │ │ └── apiResult.js # { ok, data, error } wrapper
│ │ └── adapters/
│ │ ├── http/
│ │ │ ├── auth.service.js
│ │ │ ├── patients.service.js
│ │ │ ├── appointments.service.js
│ │ │ ├── invoices.service.js
│ │ │ ├── pharmacy.service.js
│ │ │ └── schedules.service.js
│ │ └── mock/
│ │ ├── mockDb.js
│ │ ├── auth.service.js
│ │ ├── patients.service.js
│ │ ├── appointments.service.js
│ │ ├── invoices.service.js
│ │ ├── pharmacy.service.js
│ │ └── schedules.service.js
│ ├── lib/
│ │ ├── api.js # base URL resolution, token reader
│ │ └── normalizers.js # snake_case → UI shape
│ └── components/
│ ├── auth/AuthModule.js # Login/register screen
│ ├── layout/Sidebar.js # Main nav
│ ├── dashboard/
│ │ ├── Dashboard.js
│ │ └── RemindersWidget.js
│ ├── patients/
│ │ ├── PatientsModule.js
│ │ ├── PatientForm.js
│ │ ├── PatientProfile.js
│ │ ├── PatientDetails.js
│ │ └── PatientEditForm.js
│ ├── appointments/
│ │ ├── AppointmentsModule.js
│ │ └── AppointmentForm.js
│ ├── prescriptions/
│ │ ├── PrescriptionsModule.js
│ │ ├── PrescriptionForm.js
│ │ └── DispenseMedicine.js
│ ├── reminders/RemindersModule.js
│ ├── medical/MedicalModule.js
│ ├── inventory/
│ │ ├── InventoryDashboard.js
│ │ ├── MedicineInventory.js
│ │ ├── AddMedicineForm.js
│ │ ├── StockAdjustmentForm.js
│ │ ├── StockAdjustmentModal.js
│ │ ├── StockHistoryModal.js
│ │ └── StockMovementHistory.js
│ ├── billing/BillingModule.js
│ ├── reports/ReportsModule.js
│ ├── tenant/TenantSwitcher.js
│ └── common/
│ ├── Badge.js
│ ├── Button.js
│ └── Table.js
├── tailwind.config.js
├── postcss.config.js
└── package.json

Module switcher

App.js keeps the currently active feature in local state:

const [activeModule, setActiveModule] = useState('dashboard');
// Sidebar calls setActiveModule(...) when you click a nav item
// The main area renders:
// dashboard → <Dashboard />
// patients → <PatientsModule />
// appointments → <AppointmentsModule />
// prescriptions→ <PrescriptionsModule />
// reminders → <RemindersModule />
// medical → <MedicalModule />
// inventory → <InventoryDashboard />
// billing → <BillingModule />
// reports → <ReportsModule />

There is no URL routing today — navigation is via state. Swapping in react-router is a drop-in change because every module is self-contained.

File-type conventions

  • *Module.js — top-level feature page (loads data, renders table/forms).
  • *Form.js — standalone form (often shown in a modal).
  • *Profile.js, *Details.js — detail views.
  • *Modal.js — modal-specific components.
  • *.service.js — adapter-layer services under services/adapters/.
  • use*.js — custom hooks.

Import aliases

No jsconfig.json aliases are configured today. Imports use relative paths (../../services). If this gets painful, a jsconfig.json with "baseUrl": "src" turns them into absolute imports without any build change.

Environment variables

Read at build time (CRA convention — must start with REACT_APP_):

VariableDefaultPurpose
REACT_APP_API_BASE_URLDynamic from window.locationAPI root (e.g. https://app.vetty.example.com/api/v1)
REACT_APP_API_MODEhttpSet to mock for offline/demo

Drop them into ui/.env.local for local development. Remember that CRA rebuilds when these change — restart npm start.