Frontend
Frontend architecture, component responsibilities, data fetching, filter flow, heatmap rendering, and export behavior for Antimicrobiogram.
Frontend
This page explains the Antimicrobiogram frontend from the point of view of an engineer reading the React code.
Primary files
| File | Responsibility |
|---|---|
apps/livehealth-frontend/src/components/Finance/MIS/Components/TestValues/index.tsx | Parent container, tab wiring, feature gating |
apps/livehealth-frontend/src/components/Finance/MIS/Components/TestValues/components/OrganismAntibioticResultsView.tsx | Main Antimicrobiogram UI |
apps/livehealth-frontend/src/components/Finance/MIS/Components/TestValues/helpers.ts | Fetch helpers, heatmap builders, export helpers |
apps/livehealth-frontend/src/components/Finance/MIS/Components/TestValues/constants.ts | Colors, labels, date format constants |
apps/livehealth-frontend/src/components/Finance/MIS/Components/TestValues/utils/interface.ts | Type contracts used by the screen |
Feature gating
The frontend checks one session-level switch:
Boolean(sessionState?.is_antimicrobiogram_enabled)That single flag controls whether the Organism/Antibiotic Results tab is added to the Test Values tab list.
This is a clean product choice because it keeps the rest of the screen untouched for labs that do not use the feature.
Parent screen behavior
The Antimicrobiogram UI is mounted from TestValues/index.tsx.
That parent file does three important things:
- decides whether the
Organism/Antibiotic Resultsmain tab should exist - owns the sub-tab state for:
MicrobiologyMolecularAntimicrobiogram
- hands rendering over to
OrganismAntibioticResultsViewwhen the tab is active
This means the feature does not live in isolation. It is intentionally part of a broader organism/antibiotic result exploration surface.
Main component state
OrganismAntibioticResultsView owns the key runtime state for the feature.
Data state
rawResults, sensitivityResults, dataLoading
Filter state
selectedSample, selectedService, selectedOrganization, selectedPatient, dateField, startDate, endDate
Export state
exporting, downloadOpen
Grid state
antimicrobiogramGridApiRef
This is a good state split. It keeps fetching, filtering, and export concerns separate enough to reason about without making the component over-abstracted.
Fetch lifecycle
The fetch flow is driven by loadOrganismAntibioticResults(...), which wraps fetchOrganismAntibioticResults(...).
Common request behavior
Every request sends:
response_type=both- date range as Unix seconds
- optional
sample_type
The endpoint is:
/api-v3/report/organism-antibiotic-results/
Result-type behavior by sub-tab
| Sub-tab | Request behavior |
|---|---|
Microbiology | sends result_type=MICROBIOLOGY |
Molecular | sends result_type=MOLECULAR |
Antimicrobiogram | does not send result_type, so the backend can return both source families |
Date behavior
Raw result tabs can switch between:
report_datebill_time
The Antimicrobiogram heatmap always uses:
report_date_fromreport_date_to
That keeps the matrix experience simpler.
useEffect-driven refresh model
The screen refetches when important inputs change:
- sub-tab
- date field
- date range
- selected sample
- selected service
- selected organization
This means the page behaves like a report screen, not like a static cached table.
Filter behavior
Not every filter behaves the same way, and that is intentional.
| Filter | Raw result tabs | Antimicrobiogram |
|---|---|---|
| Date range | server refetch | server refetch |
| Sample type | server refetch | server refetch |
| Service | client filter | server refetch |
| Patient | client filter | not shown |
| Organization | not shown | server refetch |
This pattern makes sense:
- raw tabs behave like a data explorer
- Antimicrobiogram behaves like a scoped report
Default organization selection
The Antimicrobiogram sub-tab is organization-wise, so the frontend needs a sensible default.
The flow is:
- fetch the result set
- derive organization options from
organization_idandorganization_name - sort the options
- if no organization is selected yet, select the first available one
- refetch with
organization_id
This gives a nice first-load experience because the user does not land on an empty or ambiguous lab-wide view. The page immediately narrows itself to a specific organization with data.
Option building
The frontend builds filter options from the fetched rows using buildUniqueOptions(...).
It derives:
- patient options
- service options
- sample options
- organization options
This means the filters are data-backed and only show values that actually exist in the currently fetched result set.
Heatmap rendering
The Antimicrobiogram matrix is built from the backend sensitivity payload, then narrowed using the filtered result set.
The transformation steps are:
- gather all visible antibiotics across the sensitivity rows
- sort antibiotic names alphabetically
- assign synthetic column ids like
antibiotic_0,antibiotic_1, and so on - create one row per organism
- attach the full aggregated antibiotic cell object into the correct synthetic field
Each cell object carries:
nametotal_testedsensitive_countresistant_countintermediate_countsensitivity_pctresistant_pctintermediate_pct
That is a nice design because the renderer can stay focused on display while the data object still keeps the deeper grouped values attached to it.
Heatmap headers
The heatmap has custom headers for both the organism column and the antibiotic columns.
Organism header
The organism header shows:
Organism GroupN=<total>
Antibiotic headers
Each antibiotic column header shows the antibiotic name in a compact heatmap-style cell.
This makes the matrix dense but still readable.
Color system
The heatmap colors are defined in constants.ts.
| Range | Meaning | Color family |
|---|---|---|
>= 90% | high sensitivity | green |
70% - 89% | medium sensitivity | amber |
< 70% | low sensitivity | red |
| no data | empty | white |
The same thresholds drive:
- cell background colors
- cell text colors
- legend swatches
- Excel export cell styles
That consistency is important. The export looks and feels like the on-screen report instead of becoming a disconnected spreadsheet dump.
Export behavior
Excel export is implemented through the visible ag-Grid instance and exportAntimicrobiogramToExcel(...).
What gets exported
- all heatmap columns
- matrix cell values as formatted strings such as
46.02% (N=415) - metadata rows
- legend rows
Metadata rows included
- organization name
- period
- sample type
- service
- generated-on timestamp
Sheet details
- file name prefix =
Antimicrobiogram_YYYYMMDD_HHmm - sheet name =
Antimicrobiogram
This gives the exported sheet enough context to be meaningful even after it leaves the app.
Why the frontend implementation is good
The frontend design is strong for a few reasons:
- it keeps feature gating simple through session state
- it reuses one view for raw rows and matrix rendering
- it uses the backend summary table rather than trying to aggregate raw report values in the browser
- it makes organization scoping automatic
- it keeps export logic close to the rendered grid
In short, the frontend is doing exactly what it should do: present, filter, and export already shaped data instead of owning the reporting computation itself.
Quick engineer mental model
If you are new to the code, this is the easiest way to think about the UI:
index.tsxdecides whether the feature exists on the pageOrganismAntibioticResultsView.tsxowns the runtime behaviorhelpers.tsturns the API payload into a heatmap-friendly shape- ag-Grid renders the rows and columns
- the same grid powers Excel export
That is the full frontend story.