Skip to content

Commit 72c7a74

Browse files
committed
Enhance death registration flow with loading state and condition handling
1 parent 84a8093 commit 72c7a74

7 files changed

Lines changed: 151 additions & 62 deletions

File tree

src/app/benefits-demo/benefits-demo.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,10 @@ <h4>Demographics</h4>
411411
color="primary"
412412
(click)="openDeathRegistration()"
413413
class="death-button"
414-
[disabled]="isDeletingPatient"
414+
[disabled]="isDeletingPatient || isOpeningDeathDialog"
415415
>
416-
<mat-icon>bed</mat-icon>
417-
{{ getDeathActionLabel(selectedPatient) }}
416+
<mat-icon>{{ isOpeningDeathDialog ? 'hourglass_empty' : 'bed' }}</mat-icon>
417+
{{ isOpeningDeathDialog ? 'Loading...' : getDeathActionLabel(selectedPatient) }}
418418
</button>
419419
<button
420420
mat-flat-button

src/app/benefits-demo/benefits-demo.component.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class BenefitsDemoComponent implements OnInit, OnDestroy {
5151
isCreatingPatient = false;
5252
creatingPatientMessage = 'Creating patient...';
5353
isDeletingPatient = false;
54+
isOpeningDeathDialog = false;
5455
isSearchingPatients = false;
5556
remoteSearchTotal: number | null = null;
5657
private subscriptions: Subscription[] = [];
@@ -562,11 +563,18 @@ export class BenefitsDemoComponent implements OnInit, OnDestroy {
562563
this.router.navigate(['/clinical-record', this.selectedPatient.id]);
563564
}
564565

565-
openDeathRegistration(): void {
566-
if (!this.selectedPatient) {
566+
async openDeathRegistration(): Promise<void> {
567+
if (!this.selectedPatient || this.isOpeningDeathDialog) {
567568
return;
568569
}
569570

571+
this.isOpeningDeathDialog = true;
572+
try {
573+
await this.patientService.ensureConditionsAndDeathRecordLoaded(this.selectedPatient.id);
574+
} finally {
575+
this.isOpeningDeathDialog = false;
576+
}
577+
570578
const existingRecord = this.patientService.getPatientDeathRecord(this.selectedPatient.id);
571579
const conditions = this.patientService.getPatientConditions(this.selectedPatient.id);
572580

src/app/benefits-demo/clinical-record/clinical-record.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ export class ClinicalRecordComponent implements OnInit, OnDestroy, AfterViewInit
450450
}
451451

452452
async loadClinicalData(patientId: string): Promise<void> {
453+
console.trace(`[loadClinicalData] called for patient ${patientId}`);
453454
if (this.patientService.getCurrentPersistenceMode() === 'fhir') {
454455
this.isLoadingClinicalData = true;
455456

src/app/benefits-demo/death-registration-dialog/death-registration-dialog.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ <h2>{{ getDialogTitle() }}</h2>
6464
<mat-select [(value)]="line.selectedConditionId" (selectionChange)="onExistingConditionSelected(line)">
6565
<mat-option [value]="''">Select condition</mat-option>
6666
<mat-option *ngFor="let condition of data.conditions" [value]="condition.id">
67-
{{ condition.code.text }}
67+
{{ getConditionDisplayText(condition) }}
6868
</mat-option>
6969
</mat-select>
7070
</mat-form-field>
@@ -117,7 +117,7 @@ <h2>{{ getDialogTitle() }}</h2>
117117
<mat-select [(value)]="line.selectedConditionId" (selectionChange)="onExistingConditionSelected(line)">
118118
<mat-option [value]="''">Select condition</mat-option>
119119
<mat-option *ngFor="let condition of data.conditions" [value]="condition.id">
120-
{{ condition.code.text }}
120+
{{ getConditionDisplayText(condition) }}
121121
</mat-option>
122122
</mat-select>
123123
</mat-form-field>

src/app/benefits-demo/death-registration-dialog/death-registration-dialog.component.ts

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ export class DeathRegistrationDialogComponent {
9393
return;
9494
}
9595

96-
this.removeExistingDerivedConditions();
9796
this.patientService.deletePatientDeathRecord(this.data.patient.id);
9897

9998
const revivedPatient: Patient = {
@@ -183,8 +182,6 @@ export class DeathRegistrationDialogComponent {
183182
return;
184183
}
185184

186-
this.removeExistingDerivedConditions();
187-
188185
const enrichedPart1 = await this.createDerivedConditions(
189186
serializedPart1.map(entry => ({ ...entry })),
190187
deceasedDateTime
@@ -248,12 +245,14 @@ export class DeathRegistrationDialogComponent {
248245
this.deceasedTime = existingDateTime;
249246

250247
const part1LineCodes: Part1LineCode[] = ['a', 'b', 'c', 'd'];
248+
const part1Diagnoses = this.getBundleDiagnoses(this.data.existingRecord, 'part1');
249+
const part2Diagnoses = this.getBundleDiagnoses(this.data.existingRecord, 'part2');
250+
251251
this.part1Lines = part1LineCodes.map(lineCode => {
252-
const existingLine = this.getBundleDiagnoses(this.data.existingRecord!, 'part1').find(line => line.line === lineCode);
252+
const existingLine = part1Diagnoses.find(line => line.line === lineCode);
253253
return existingLine ? this.mapDiagnosisToLine(existingLine, lineCode) : this.createPart1Line(lineCode);
254254
});
255255

256-
const part2Diagnoses = this.getBundleDiagnoses(this.data.existingRecord, 'part2');
257256
this.part2Lines = part2Diagnoses.length > 0
258257
? part2Diagnoses.map(line => this.mapDiagnosisToLine(line))
259258
: [this.createPart2Line()];
@@ -284,7 +283,7 @@ export class DeathRegistrationDialogComponent {
284283
return {
285284
line,
286285
sourceType: diagnosis.sourceType,
287-
selectedConditionId: diagnosis.sourceConditionId || '',
286+
selectedConditionId: this.resolveConditionId(diagnosis),
288287
selectedConcept: diagnosis.snomedConceptId
289288
? { code: diagnosis.snomedConceptId, display: diagnosis.snomedDisplay || diagnosis.text }
290289
: null,
@@ -294,6 +293,22 @@ export class DeathRegistrationDialogComponent {
294293
};
295294
}
296295

296+
private resolveConditionId(diagnosis: DeathRecordDiagnosis): string {
297+
if (diagnosis.sourceConditionId && this.data.conditions.some(c => c.id === diagnosis.sourceConditionId)) {
298+
return diagnosis.sourceConditionId;
299+
}
300+
if (diagnosis.snomedConceptId) {
301+
const match = this.data.conditions.find(c => {
302+
const coding = this.patientService.getConditionSnomedCoding(c);
303+
return coding?.code === diagnosis.snomedConceptId;
304+
});
305+
if (match) {
306+
return match.id;
307+
}
308+
}
309+
return diagnosis.sourceConditionId || '';
310+
}
311+
297312
private async serializePart1(): Promise<Array<DeathRecordDiagnosis & { line: Part1LineCode }>> {
298313
const results: Array<DeathRecordDiagnosis & { line: Part1LineCode }> = [];
299314

@@ -418,30 +433,15 @@ export class DeathRegistrationDialogComponent {
418433
}
419434
];
420435

421-
await this.patientService.addPatientConditionAllowDuplicatesEnriched(this.data.patient.id, condition);
422-
diagnosis.derivedConditionId = condition.id;
436+
const savedCondition = await this.patientService.addPatientConditionAllowDuplicatesEnriched(this.data.patient.id, condition);
437+
diagnosis.sourceType = 'existing-condition';
438+
diagnosis.sourceConditionId = savedCondition.id;
439+
diagnosis.derivedConditionId = undefined;
423440
}
424441

425442
return diagnoses;
426443
}
427444

428-
private removeExistingDerivedConditions(): void {
429-
if (!this.data.existingRecord) {
430-
return;
431-
}
432-
433-
const previousDiagnoses = [
434-
...this.getBundleDiagnoses(this.data.existingRecord, 'part1'),
435-
...this.getBundleDiagnoses(this.data.existingRecord, 'part2')
436-
];
437-
438-
previousDiagnoses.forEach(diagnosis => {
439-
if (diagnosis.derivedConditionId) {
440-
this.patientService.deletePatientCondition(this.data.patient.id, diagnosis.derivedConditionId);
441-
}
442-
});
443-
}
444-
445445
private getConditionById(conditionId?: string): Condition | undefined {
446446
if (!conditionId) {
447447
return undefined;
@@ -450,6 +450,17 @@ export class DeathRegistrationDialogComponent {
450450
return this.data.conditions.find(condition => condition.id === conditionId);
451451
}
452452

453+
getConditionDisplayText(condition: Condition): string {
454+
if (condition.code?.text) {
455+
return condition.code.text;
456+
}
457+
const snomed = this.patientService.getConditionSnomedCoding(condition);
458+
if (snomed?.display) {
459+
return snomed.display;
460+
}
461+
return condition.code?.coding?.find(c => c.display)?.display || condition.id || '';
462+
}
463+
453464
private getConditionSnomed(condition: Condition): { code?: string; display?: string } {
454465
const coding = this.patientService.getConditionSnomedCoding(condition) || condition.code?.coding?.[0];
455466

@@ -506,7 +517,7 @@ export class DeathRegistrationDialogComponent {
506517

507518
const entries = [
508519
{ fullUrl: `urn:uuid:${compositionId}`, resource: compositionResource },
509-
{ fullUrl: `urn:uuid:${this.data.patient.id}`, resource: patientResource },
520+
{ fullUrl: `Patient/${this.data.patient.id}`, resource: patientResource },
510521
{ fullUrl: `urn:uuid:${practitionerId}`, resource: practitionerResource },
511522
{ fullUrl: `urn:uuid:${procedureId}`, resource: procedureResource },
512523
...part1Observations.map(resource => ({ fullUrl: `urn:uuid:${resource.id}`, resource })),
@@ -570,7 +581,7 @@ export class DeathRegistrationDialogComponent {
570581
text: 'Death certification'
571582
},
572583
subject: {
573-
reference: `urn:uuid:${this.data.patient.id}`
584+
reference: `Patient/${this.data.patient.id}`
574585
},
575586
performedDateTime: deceasedDateTime,
576587
performer: [
@@ -609,7 +620,7 @@ export class DeathRegistrationDialogComponent {
609620
text: 'Death certificate'
610621
},
611622
subject: {
612-
reference: `urn:uuid:${this.data.patient.id}`
623+
reference: `Patient/${this.data.patient.id}`
613624
},
614625
date: deceasedDateTime,
615626
author: [
@@ -672,7 +683,7 @@ export class DeathRegistrationDialogComponent {
672683
]
673684
},
674685
subject: {
675-
reference: `urn:uuid:${this.data.patient.id}`
686+
reference: `Patient/${this.data.patient.id}`
676687
},
677688
performer: [
678689
{
@@ -737,7 +748,7 @@ export class DeathRegistrationDialogComponent {
737748
]
738749
},
739750
subject: {
740-
reference: `urn:uuid:${this.data.patient.id}`
751+
reference: `Patient/${this.data.patient.id}`
741752
},
742753
performer: [
743754
{

src/app/services/patient-fhir-storage.service.ts

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import {
3535
export class PatientFhirStorageService implements PatientStorageBackend {
3636
private readonly patientPageSize = 20;
3737
private static readonly BUNDLE_PATIENT_REFERENCE_EXTENSION_URL = 'http://snomed.org/fhir/StructureDefinition/patient-reference';
38+
private static readonly DEATH_CERTIFICATE_IDENTIFIER_SYSTEM = 'http://example.org/fhir/identifiers/death-certificate-document';
39+
private static readonly BUNDLE_PATIENT_TAG_SYSTEM = 'http://snomed.org/fhir/tags/patient';
3840

3941
constructor(private fhirService: FhirService) {}
4042

@@ -395,9 +397,9 @@ export class PatientFhirStorageService implements PatientStorageBackend {
395397
async updateServiceRequest(patientId: string, requestId: string, serviceRequest: ServiceRequest): Promise<ServiceRequest> { return await firstValueFrom(this.fhirService.update('ServiceRequest', requestId, serviceRequest)); }
396398
async deleteServiceRequest(patientId: string, requestId: string): Promise<void> { await firstValueFrom(this.fhirService.delete('ServiceRequest', requestId)); }
397399

398-
async getLabOrders(patientId: string): Promise<LaboratoryOrderGroup[]> {
399-
const bundles = await this.fetchPatientBundles(patientId, 'document');
400-
return bundles.map((bundle: any) => ({
400+
async getLabOrders(patientId: string, prefetchedBundles?: any[]): Promise<LaboratoryOrderGroup[]> {
401+
const bundles = prefetchedBundles ?? await this.fetchPatientBundles(patientId, 'document');
402+
return bundles.filter((bundle: any) => !this.isDeathCertificateBundle(bundle)).map((bundle: any) => ({
401403
id: bundle.id,
402404
patientId,
403405
patientDisplay: this.getPatientDisplay(bundle, patientId),
@@ -441,8 +443,7 @@ export class PatientFhirStorageService implements PatientStorageBackend {
441443
async deleteQuestionnaireResponse(patientId: string, responseId: string): Promise<void> { await firstValueFrom(this.fhirService.delete('QuestionnaireResponse', responseId)); }
442444

443445
async getDeathRecord(patientId: string): Promise<DeathRecord | null> {
444-
const bundles = await this.fetchPatientBundles(patientId, 'document');
445-
return (bundles[0] as DeathRecord) || null;
446+
return this.fetchDeathRecordBundle(patientId);
446447
}
447448

448449
async saveDeathRecord(patientId: string, record: DeathRecord): Promise<DeathRecord> {
@@ -503,11 +504,12 @@ export class PatientFhirStorageService implements PatientStorageBackend {
503504
.filter((resource: any) => resource?.resourceType === 'Encounter')
504505
.map((encounter: any) => this.hydrateEncounterFreeText(encounter)) as Encounter[];
505506

506-
const [labOrders, provenance, deathRecord] = await Promise.all([
507-
this.getLabOrders(patientId),
507+
const [documentBundles, provenance, deathRecord] = await Promise.all([
508+
this.fetchPatientBundles(patientId, 'document'),
508509
this.getProvenance(patientId),
509-
this.getDeathRecord(patientId)
510+
this.fetchDeathRecordBundle(patientId)
510511
]);
512+
const labOrders = await this.getLabOrders(patientId, documentBundles);
511513

512514
return {
513515
conditions,
@@ -722,14 +724,43 @@ export class PatientFhirStorageService implements PatientStorageBackend {
722724
}
723725

724726
private async fetchPatientBundles(patientId: string, bundleType?: string): Promise<any[]> {
727+
const patientRef = this.getPatientReference(patientId);
725728
const initialBundle = await firstValueFrom(this.fhirService.search('Bundle', {
726729
type: bundleType,
727-
'composition.patient': this.getPatientReference(patientId),
730+
_tag: `${PatientFhirStorageService.BUNDLE_PATIENT_TAG_SYSTEM}|${patientRef}`,
728731
_count: 200
729732
}));
730733

731734
const combinedBundle = await this.collectPagedBundle(initialBundle);
732-
return this.extractBundleResources<any>(combinedBundle, 'Bundle');
735+
const bundles = this.extractBundleResources<any>(combinedBundle, 'Bundle');
736+
return bundles.filter((bundle) => this.bundleBelongsToPatient(bundle, patientRef));
737+
}
738+
739+
private isDeathCertificateBundle(bundle: any): boolean {
740+
return bundle?.identifier?.system === PatientFhirStorageService.DEATH_CERTIFICATE_IDENTIFIER_SYSTEM;
741+
}
742+
743+
private async fetchDeathRecordBundle(patientId: string): Promise<DeathRecord | null> {
744+
const patientRef = this.getPatientReference(patientId);
745+
const result = await firstValueFrom(this.fhirService.search('Bundle', {
746+
_tag: `${PatientFhirStorageService.BUNDLE_PATIENT_TAG_SYSTEM}|${patientRef}`,
747+
identifier: `${PatientFhirStorageService.DEATH_CERTIFICATE_IDENTIFIER_SYSTEM}|`,
748+
_count: 5
749+
}));
750+
const bundles = this.extractBundleResources<any>(result, 'Bundle');
751+
return (bundles.find(bundle =>
752+
this.isDeathCertificateBundle(bundle) && this.bundleBelongsToPatient(bundle, patientRef)
753+
) as DeathRecord) ?? null;
754+
}
755+
756+
private bundleBelongsToPatient(bundle: any, patientRef: string): boolean {
757+
const extension = (bundle.extension || []).find(
758+
(ext: any) => ext.url === PatientFhirStorageService.BUNDLE_PATIENT_REFERENCE_EXTENSION_URL
759+
);
760+
if (extension?.valueReference?.reference === patientRef) {
761+
return true;
762+
}
763+
return this.getBundlePatientReference(bundle) === patientRef;
733764
}
734765

735766
private extractBundleResources<T>(bundle: any, resourceType?: string): T[] {
@@ -982,18 +1013,33 @@ export class PatientFhirStorageService implements PatientStorageBackend {
9821013
}
9831014
}
9841015

985-
private withBundlePatientReference<T extends Record<string, any>>(resource: T, patientId: string): T & { extension: any[] } {
1016+
private withBundlePatientReference<T extends Record<string, any>>(resource: T, patientId: string): T & { extension: any[]; meta: any } {
9861017
const extensions = Array.isArray(resource['extension']) ? [...resource['extension']] : [];
9871018
const otherExtensions = extensions.filter((extension: any) => extension.url !== PatientFhirStorageService.BUNDLE_PATIENT_REFERENCE_EXTENSION_URL);
9881019

1020+
const patientRef = this.getPatientReference(patientId);
1021+
const existingMeta = resource['meta'] || {};
1022+
const existingTags: any[] = Array.isArray(existingMeta.tag) ? existingMeta.tag : [];
1023+
const otherTags = existingTags.filter((tag: any) => tag.system !== PatientFhirStorageService.BUNDLE_PATIENT_TAG_SYSTEM);
1024+
9891025
return {
9901026
...resource,
1027+
meta: {
1028+
...existingMeta,
1029+
tag: [
1030+
...otherTags,
1031+
{
1032+
system: PatientFhirStorageService.BUNDLE_PATIENT_TAG_SYSTEM,
1033+
code: patientRef
1034+
}
1035+
]
1036+
},
9911037
extension: [
9921038
...otherExtensions,
9931039
{
9941040
url: PatientFhirStorageService.BUNDLE_PATIENT_REFERENCE_EXTENSION_URL,
9951041
valueReference: {
996-
reference: this.getPatientReference(patientId)
1042+
reference: patientRef
9971043
}
9981044
}
9991045
]

0 commit comments

Comments
 (0)