Skip to content

UHM-9498: build O3 audit trail app - #43

Merged
cioan merged 3 commits into
mainfrom
UHM-9498
Sep 3, 2026
Merged

cioan merged 3 commits into
mainfrom
UHM-9498

Conversation

@cioan

@cioan cioan commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

A new O3 app that pretty much mirrors the leagacy UI manage encounters admin pages. Please see the README below for full details of all the changes.

Screenshots

Screenshot 2026-09-01 at 1 41 39 PM Screenshot 2026-09-02 at 8 06 07 AM Screenshot 2026-09-02 at 8 06 19 AM Screenshot 2026-09-02 at 8 06 39 AM

@cioan
cioan requested a review from chibongho September 2, 2026 12:08

@chibongho chibongho left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of my comments are performance-related, so fine as a first-pass and fix later. However, I think the PatientActivity table can miss displaying data if the patient has too many encounters.

* dropdown never offers a type that would return nothing. Read unfiltered, so choosing a type
* does not narrow the choices left.
*/
export function usePatientEncounterTypes(patient: AuditPatient | undefined, includeDeleted: boolean) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for now, but we should consider having a server-side endpoint for this instead of figuring that out client-side.

* summarised per user and listed in order.
*
* The encounter rows carry their own `auditInfo`, but the observations do not come with them —
* they have to be read per encounter, since that is the only obs search that honours

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is completely right. Obs has auditInfo, but I think it's true that calling the Encounter endpoint with includeAll does not also transitively do includeAll on Obs. Might want to fix to not confuse the AI agent.

Longer term, might not be a bad idea to consider making includeAll transitive (or making a new param that does so). This will allow us to fetch the encounters + obs in one request, instead of making N requests to fetch obs of N encounters.

isLoading: isLoadingEncounters,
} = useAllPatientEncounters(patient, includeDeleted, filters);

const scannedEncounters = useMemo(() => encounters.slice(0, scanLimit), [encounters, scanLimit]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems bad. We should not get all the encounters of the patient, and then just use 50 of them.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Thanks @chibongho !

* server's configured URI prefix, which is not always reachable from the browser, so this walks
* `startIndex` itself instead of following the link.
*/
async function fetchAllPages<T>(url: string): Promise<Array<T>> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing useOpenmrsFetchAll() function should do this already.

const [pageSize, setPageSize] = useState(config.encountersPageSize ?? 10);
const pageSizes = useMemo(() => Array.from(new Set([pageSize, 10, 20, 50])).sort((a, b) => a - b), [pageSize]);

const { encounters, totalCount, error, isLoading } = usePatientEncounters(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are displaying these in a paginated table, I recommend making usePatientEncounters use useOpenmrsPaginatiion() to fetch paginated data from the server instead of fetchAllData()

const config = useConfig<Config>();
const [selectedUserUuid, setSelectedUserUuid] = useState<string | null>(null);
const [page, setPage] = useState(1);
const { events, userActivity, scannedCount, matchedCount, error, isLoading } = usePatientActivity(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think scanLimit puts a hard limit of how many encounters we fetch data for, which means it's possible we'll never see some of the patient's data if the patient has more than 50 (or activityScanLimit) encounters.

Since an "AuditEvent" could be coming from either an encounter or an obs, and we sort the Array<AudiEvent> by timestamp, I don't think we have a good way to correctly show the data without fetching all of them. This can get fairly expensive on the client side, since we make 1 request per encounter. We should consider having a server endpoint for this.

@cioan

cioan commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

Thanks @chibongho ! I have updated the PR to address your observations. I have removed the scan limit and since this is still a prototype app, I have modified the Patient activity tab to load all encounters and obs, so the numbers are accurate. When we demoed this to the ZL team this afternoon, the only observation/question we got was "Where can we test this?". So, maybe we would just deploy this new app to the test servers and once the users have a chance to interact with this app we will address their feedback and also make any performance improvements that we need to do? First I would like to confirm that these are the type of features that they need, it was not clear to me from the teams meeting call today.

@chibongho

Copy link
Copy Markdown
Collaborator

Agree, this can go in for now with the scan limit removal.

@cioan

cioan commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

Thanks @chibongho !

@cioan
cioan merged commit bfb4f7d into main Sep 3, 2026
3 checks passed
@cioan
cioan deleted the UHM-9498 branch September 3, 2026 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants