Skip to content

Commit ee687d9

Browse files
committed
Add immunization support across entry, record, and timeline
1 parent aa8aff7 commit ee687d9

22 files changed

Lines changed: 873 additions & 32 deletions

src/app/benefits-demo/ai-assisted-entry/ai-assisted-entry.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ export class AiAssistedEntryComponent implements OnInit, OnDestroy {
298298
conditions: conditionsToSave,
299299
procedures: procedureToSave ? [procedureToSave] : [],
300300
medications: medicationsToSave,
301+
immunizations: [],
301302
allergies: [],
302303
provenance: aiDerivedProvenance
303304
});

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,18 @@
5757
</mat-form-field>
5858
</div>
5959
}
60-
@if (entryType === 'condition' || entryType === 'procedure') {
60+
@if (entryType === 'condition' || entryType === 'procedure' || entryType === 'immunization') {
6161
<div class="date-section">
6262
<mat-form-field appearance="outline">
63-
<mat-label>{{ entryType === 'condition' ? 'Condition date' : 'Procedure date' }}</mat-label>
63+
<mat-label>
64+
@if (entryType === 'condition') {
65+
Condition date
66+
} @else if (entryType === 'procedure') {
67+
Procedure date
68+
} @else {
69+
Immunization date
70+
}
71+
</mat-label>
6472
<input
6573
matInput
6674
[matDatepicker]="clinicalDatePicker"
@@ -125,6 +133,18 @@
125133
</mat-form-field>
126134
</div>
127135
}
136+
@if (entryType === 'immunization') {
137+
<div class="dosage-row">
138+
<mat-form-field appearance="outline">
139+
<mat-label>Immunization status</mat-label>
140+
<mat-select [(ngModel)]="immunizationStatus">
141+
<mat-option value="completed">Completed</mat-option>
142+
<mat-option value="not-done">Not done</mat-option>
143+
<mat-option value="entered-in-error">Entered in error</mat-option>
144+
</mat-select>
145+
</mat-form-field>
146+
</div>
147+
}
128148
<div class="action-section">
129149
<button mat-flat-button color="accent" (click)="addItem()" [disabled]="!selectedConcept || loading || saving">
130150
<span class="entry-button-label">

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

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
22
import { PatientService } from '../../services/patient.service';
33
import { TerminologyService } from '../../services/terminology.service';
4-
import type { Condition, MedicationStatement, Procedure } from '../../model';
4+
import type { Condition, Immunization, MedicationStatement, Procedure } from '../../model';
55

6-
export type ClinicalEntryType = 'condition' | 'procedure' | 'medication';
6+
export type ClinicalEntryType = 'condition' | 'procedure' | 'medication' | 'immunization';
77

88
@Component({
99
selector: 'app-clinical-entry',
@@ -30,6 +30,7 @@ export class ClinicalEntryComponent implements AfterViewInit {
3030
medicationDoseUnit: string = '';
3131
medicationPeriod: number | null = null;
3232
medicationPeriodUnit: 'h' | 'd' | 'wk' = 'h';
33+
immunizationStatus: Immunization['status'] = 'completed';
3334
loading = false;
3435
showAddForm = false;
3536
readonly medicationDoseUnitOptions = ['tablet', 'capsule', 'drop', 'puff', 'mL', 'mg', 'g', 'unit'];
@@ -50,6 +51,11 @@ export class ClinicalEntryComponent implements AfterViewInit {
5051
ecl: '< 373873005 |Pharmaceutical / biologic product| : 762949000 |Has precise active ingredient (attribute)| = *',
5152
title: 'Search for medication...',
5253
placeholder: 'Enter medication name...'
54+
},
55+
immunization: {
56+
ecl: '< 787859002 |Vaccine product (medicinal product)|',
57+
title: 'Search for vaccine...',
58+
placeholder: 'Enter vaccine name...'
5359
}
5460
};
5561

@@ -66,7 +72,8 @@ export class ClinicalEntryComponent implements AfterViewInit {
6672
const titles = {
6773
condition: 'Add Condition',
6874
procedure: 'Add Procedure',
69-
medication: 'Add Medication'
75+
medication: 'Add Medication',
76+
immunization: 'Add Immunization'
7077
};
7178
return titles[this.entryType];
7279
}
@@ -119,6 +126,9 @@ export class ClinicalEntryComponent implements AfterViewInit {
119126
case 'medication':
120127
await this.addMedication();
121128
break;
129+
case 'immunization':
130+
await this.addImmunization();
131+
break;
122132
}
123133
} finally {
124134
setTimeout(() => {
@@ -183,6 +193,22 @@ export class ClinicalEntryComponent implements AfterViewInit {
183193
this.itemAdded.emit(newMedication);
184194
}
185195

196+
private async addImmunization() {
197+
const newImmunization: Immunization = this.patientService.createImmunizationFromClinicalEntryConcept(
198+
this.patientId,
199+
{
200+
code: this.selectedConcept.code,
201+
display: this.selectedConcept.display
202+
},
203+
{
204+
occurrenceDateTime: this.getEntryDateValue(),
205+
status: this.immunizationStatus
206+
}
207+
);
208+
209+
this.itemAdded.emit(newImmunization);
210+
}
211+
186212
resetAndCloseForm(): void {
187213
this.resetForm();
188214
}
@@ -196,6 +222,7 @@ export class ClinicalEntryComponent implements AfterViewInit {
196222
this.medicationDoseUnit = '';
197223
this.medicationPeriod = null;
198224
this.medicationPeriodUnit = 'h';
225+
this.immunizationStatus = 'completed';
199226
this.showAddForm = false;
200227
}
201228

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,6 +2201,18 @@
22012201
background-color: var(--kind-medication);
22022202
}
22032203

2204+
.secondary-column-header {
2205+
margin-top: 12px;
2206+
}
2207+
2208+
.secondary-column-header h4 mat-icon {
2209+
color: #0d6efd;
2210+
}
2211+
2212+
.secondary-column-header .item-count {
2213+
background-color: #0d6efd;
2214+
}
2215+
22042216
/* Compact Clinical List Styles */
22052217
.clinical-list.compact {
22062218
padding: 0;

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,19 @@ <h5 class="summary-heading-medications">Medications</h5>
372372
</ng-template>
373373
</div>
374374

375+
<div class="clinical-summary-section">
376+
<h5 class="summary-heading-medications">Immunizations</h5>
377+
<div class="clinical-summary-list" *ngIf="immunizations.length > 0; else noImmunizations">
378+
<div class="clinical-summary-item"
379+
*ngFor="let immunization of getSortedImmunizations()">
380+
<div class="item-name">{{ immunization.vaccineCode.text || immunization.vaccineCode.coding?.[0]?.display }}</div>
381+
</div>
382+
</div>
383+
<ng-template #noImmunizations>
384+
<div class="no-clinical-data">No immunizations recorded</div>
385+
</ng-template>
386+
</div>
387+
375388
<div class="clinical-summary-section">
376389
<h5 class="summary-heading-allergies">Allergic to:</h5>
377390
<div class="clinical-summary-list" *ngIf="allergies.length > 0; else noAllergies">
@@ -414,6 +427,7 @@ <h5>CDS</h5>
414427
[conditions]="conditions"
415428
[procedures]="procedures"
416429
[medications]="medications"
430+
[immunizations]="immunizations"
417431
[allergies]="allergies"
418432
[encounters]="encounters">
419433
</app-clinical-timeline>
@@ -438,6 +452,7 @@ <h5>CDS</h5>
438452
[conditions]="conditions"
439453
[procedures]="procedures"
440454
[medications]="medications"
455+
[immunizations]="immunizations"
441456
[allergies]="allergies"
442457
(openProblemsList)="selectClinicalView('problems')">
443458
</app-problems-list-simplified>
@@ -463,6 +478,7 @@ <h5>CDS</h5>
463478
[conditions]="conditions"
464479
[procedures]="procedures"
465480
[medications]="medications"
481+
[immunizations]="immunizations"
466482
[allergies]="allergies"
467483
(openProblemsList)="selectClinicalView('problems')">
468484
</app-problems-list-simplified>
@@ -512,6 +528,15 @@ <h3>Add Problem</h3>
512528
<mat-icon>medication</mat-icon>
513529
Add Medication
514530
</button>
531+
<button mat-raised-button
532+
color="primary"
533+
(click)="toggleImmunizationEntry()"
534+
[disabled]="isProcessingNewEvent"
535+
class="entry-toggle-button medication-button"
536+
[class.active]="immunizationEntry?.showAddForm">
537+
<mat-icon>vaccines</mat-icon>
538+
Add Immunization
539+
</button>
515540
</div>
516541
</div>
517542

@@ -548,6 +573,16 @@ <h3>Add Problem</h3>
548573
(itemAdded)="onMedicationAdded($event)"
549574
(formOpened)="onClinicalEntryFormOpened($event)">
550575
</app-clinical-entry>
576+
577+
<app-clinical-entry
578+
#immunizationEntry
579+
[patientId]="patient.id"
580+
entryType="immunization"
581+
[hideButton]="true"
582+
[saving]="savingEntryType === 'immunization'"
583+
(itemAdded)="onImmunizationAdded($event)"
584+
(formOpened)="onClinicalEntryFormOpened($event)">
585+
</app-clinical-entry>
551586
</div>
552587
</div>
553588

@@ -690,7 +725,7 @@ <h4><mat-icon>healing</mat-icon> Procedures</h4>
690725
<h4><mat-icon>medication</mat-icon> Medications</h4>
691726
<span class="item-count">{{ medications.length }}</span>
692727
</div>
693-
<div class="clinical-list compact" *ngIf="medications.length > 0; else noMedications">
728+
<div class="clinical-list compact" *ngIf="medications.length > 0; else noMedications">
694729
<div class="clinical-item compact" *ngFor="let medication of getSortedMedications()">
695730
<div class="item-header compact">
696731
<span class="item-title compact">{{ medication.medicationCodeableConcept?.text }}</span>
@@ -728,6 +763,38 @@ <h4><mat-icon>medication</mat-icon> Medications</h4>
728763
<ng-template #noMedications>
729764
<div class="no-clinical-data compact">No medications recorded</div>
730765
</ng-template>
766+
767+
<div class="column-header secondary-column-header">
768+
<h4><mat-icon>vaccines</mat-icon> Immunizations</h4>
769+
<span class="item-count">{{ immunizations.length }}</span>
770+
</div>
771+
<div class="clinical-list compact" *ngIf="immunizations.length > 0; else noImmunizationsList">
772+
<div class="clinical-item compact" *ngFor="let immunization of getSortedImmunizations()">
773+
<div class="item-header compact">
774+
<span class="item-title compact">{{ immunization.vaccineCode.text || immunization.vaccineCode.coding?.[0]?.display }}</span>
775+
<div class="item-header-actions compact">
776+
<button mat-icon-button
777+
class="delete-button compact"
778+
(click)="deleteImmunization(immunization.id)"
779+
title="Delete immunization">
780+
<mat-icon>delete_outline</mat-icon>
781+
</button>
782+
</div>
783+
</div>
784+
<div class="item-details compact inline">
785+
<span class="item-concept-id compact" *ngIf="immunization.vaccineCode.coding?.[0]?.code">
786+
{{ immunization.vaccineCode.coding?.[0]?.code }}
787+
</span>
788+
<span class="item-status compact">{{ immunization.status }}</span>
789+
<span class="item-status compact" *ngIf="immunization.occurrenceDateTime">
790+
{{ immunization.occurrenceDateTime | date:'MMM d, y' }}
791+
</span>
792+
</div>
793+
</div>
794+
</div>
795+
<ng-template #noImmunizationsList>
796+
<div class="no-clinical-data compact">No immunizations recorded</div>
797+
</ng-template>
731798
</div>
732799
</div>
733800
</div>
@@ -809,6 +876,7 @@ <h4><mat-icon>receipt_long</mat-icon> Saved Orders</h4>
809876
[conditions]="conditions"
810877
[procedures]="procedures"
811878
[medications]="medications"
879+
[immunizations]="immunizations"
812880
[allergies]="allergies"
813881
(openProblemsList)="selectClinicalView('problems')">
814882
</app-problems-list-simplified>
@@ -831,6 +899,7 @@ <h4><mat-icon>receipt_long</mat-icon> Saved Orders</h4>
831899
[conditions]="conditions"
832900
[procedures]="procedures"
833901
[medications]="medications"
902+
[immunizations]="immunizations"
834903
[allergies]="allergies"
835904
(openProblemsList)="selectClinicalView('problems')">
836905
</app-problems-list-simplified>

0 commit comments

Comments
 (0)