diff --git a/VigilCareClinicalAPI/Domains/Json/AlertSeverityJsonConverter.cs b/VigilCareClinicalAPI/Domains/Json/AlertSeverityJsonConverter.cs new file mode 100644 index 0000000..ca6948b --- /dev/null +++ b/VigilCareClinicalAPI/Domains/Json/AlertSeverityJsonConverter.cs @@ -0,0 +1,11 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +public sealed class AlertSeverityJsonConverter : JsonConverter +{ + public override AlertSeverity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => AlertSeverityExtensions.FromDbString(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, AlertSeverity value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToDbString()); +} diff --git a/VigilCareClinicalAPI/Domains/Json/AlertStatusJsonConverter.cs b/VigilCareClinicalAPI/Domains/Json/AlertStatusJsonConverter.cs new file mode 100644 index 0000000..6a7d4d9 --- /dev/null +++ b/VigilCareClinicalAPI/Domains/Json/AlertStatusJsonConverter.cs @@ -0,0 +1,11 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +public sealed class AlertStatusJsonConverter : JsonConverter +{ + public override AlertStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => AlertStatusExtensions.FromDbString(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, AlertStatus value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToDbString()); +} diff --git a/VigilCareClinicalAPI/Domains/Json/AlertTypeJsonConverter.cs b/VigilCareClinicalAPI/Domains/Json/AlertTypeJsonConverter.cs new file mode 100644 index 0000000..9e954b0 --- /dev/null +++ b/VigilCareClinicalAPI/Domains/Json/AlertTypeJsonConverter.cs @@ -0,0 +1,11 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +public sealed class AlertTypeJsonConverter : JsonConverter +{ + public override AlertType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => AlertTypeExtensions.FromDbString(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, AlertType value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToDbString()); +} diff --git a/VigilCareClinicalAPI/Migrations/20260622130402_AddPatientNameSearchToken.cs b/VigilCareClinicalAPI/Migrations/20260622130402_AddPatientNameSearchToken.cs index 2f230ec..5fefee6 100644 --- a/VigilCareClinicalAPI/Migrations/20260622130402_AddPatientNameSearchToken.cs +++ b/VigilCareClinicalAPI/Migrations/20260622130402_AddPatientNameSearchToken.cs @@ -44,7 +44,10 @@ namespace VigilCareClinicalAPI.Migrations table: "patients"); migrationBuilder.Sql( - "ALTER TABLE patients ALTER COLUMN date_of_birth TYPE date USING date_of_birth::date;"); + """ + UPDATE patients SET date_of_birth = '1900-01-01' WHERE date_of_birth !~ '^\d{4}-\d{2}-\d{2}$'; + ALTER TABLE patients ALTER COLUMN date_of_birth TYPE date USING date_of_birth::date; + """); } } } diff --git a/VigilCareClinicalAPI/Migrations/20260622143154_WidenPhiEncryptedColumns.cs b/VigilCareClinicalAPI/Migrations/20260622143154_WidenPhiEncryptedColumns.cs index cb67853..057d69a 100644 --- a/VigilCareClinicalAPI/Migrations/20260622143154_WidenPhiEncryptedColumns.cs +++ b/VigilCareClinicalAPI/Migrations/20260622143154_WidenPhiEncryptedColumns.cs @@ -56,6 +56,14 @@ namespace VigilCareClinicalAPI.Migrations /// protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.Sql( + """ + UPDATE patients SET last_name = LEFT(last_name, 100); + UPDATE patients SET first_name = LEFT(first_name, 100); + UPDATE patients SET emergency_contact_phone = LEFT(emergency_contact_phone, 20); + UPDATE patients SET emergency_contact_name = LEFT(emergency_contact_name, 200); + """); + migrationBuilder.AlterColumn( name: "last_name", table: "patients", diff --git a/VigilCareClinicalAPI/Program.cs b/VigilCareClinicalAPI/Program.cs index 084eaf6..0339807 100644 --- a/VigilCareClinicalAPI/Program.cs +++ b/VigilCareClinicalAPI/Program.cs @@ -258,9 +258,12 @@ try opts.JsonSerializerOptions.Converters.Add(new BloodTypeJsonConverter()); opts.JsonSerializerOptions.Converters.Add(new AuditActionJsonConverter()); opts.JsonSerializerOptions.Converters.Add(new SepsisBundleComplianceStatusJsonConverter()); - opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + opts.JsonSerializerOptions.Converters.Add(new AlertTypeJsonConverter()); + opts.JsonSerializerOptions.Converters.Add(new AlertSeverityJsonConverter()); + opts.JsonSerializerOptions.Converters.Add(new AlertStatusJsonConverter()); opts.JsonSerializerOptions.Converters.Add(new ObservationSourceJsonConverter()); opts.JsonSerializerOptions.Converters.Add(new DepartmentJsonConverter()); + opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => diff --git a/vigilcare-dashboard/src/__tests__/AcknowledgeModal.test.js b/vigilcare-dashboard/src/__tests__/AcknowledgeModal.test.js index b369965..9fa9dd4 100644 --- a/vigilcare-dashboard/src/__tests__/AcknowledgeModal.test.js +++ b/vigilcare-dashboard/src/__tests__/AcknowledgeModal.test.js @@ -6,9 +6,9 @@ import { useAuthStore } from '@/stores/auth' const alert = { id: 'alert-1', - alertType: 'SofaSepsis', - severity: 'Critical', - status: 'Open', + alertType: 'SOFA_SEPSIS', + severity: 'CRITICAL', + status: 'OPEN', details: 'SOFA delta >= 2', } diff --git a/vigilcare-dashboard/src/__tests__/AlertCard.test.js b/vigilcare-dashboard/src/__tests__/AlertCard.test.js index ee86fa2..40c5518 100644 --- a/vigilcare-dashboard/src/__tests__/AlertCard.test.js +++ b/vigilcare-dashboard/src/__tests__/AlertCard.test.js @@ -5,9 +5,9 @@ import AlertCard from '@/components/alerts/AlertCard.vue' const openAlert = { id: 'alert-1', - alertType: 'SepsisWarning', - severity: 'Critical', - status: 'Open', + alertType: 'SEPSIS_WARNING', + severity: 'CRITICAL', + status: 'OPEN', details: 'SIRS criteria met', triggeredAt: '2026-06-19T12:00:00Z', } @@ -15,7 +15,7 @@ const openAlert = { const resolvedAlert = { ...openAlert, id: 'alert-2', - status: 'Resolved', + status: 'RESOLVED', } describe('AlertCard', () => { @@ -25,7 +25,7 @@ describe('AlertCard', () => { it('showsAlertTypeAndSeverity', () => { const wrapper = mount(AlertCard, { props: { alert: openAlert } }) - expect(wrapper.text()).toContain('Critical') + expect(wrapper.text()).toContain('CRITICAL') expect(wrapper.text()).toContain('Sepsis Warning (SIRS — Legacy)') }) @@ -55,7 +55,7 @@ describe('AlertCard', () => { props: { alert: { ...openAlert, - status: 'Acknowledged', + status: 'ACKNOWLEDGED', acknowledgedBy: 'Demo Nurse (NURSE)', acknowledgedAt: '2026-06-19T12:30:00Z', }, diff --git a/vigilcare-dashboard/src/__tests__/AlertLabels.test.js b/vigilcare-dashboard/src/__tests__/AlertLabels.test.js index 549ddc3..5236bc9 100644 --- a/vigilcare-dashboard/src/__tests__/AlertLabels.test.js +++ b/vigilcare-dashboard/src/__tests__/AlertLabels.test.js @@ -3,19 +3,19 @@ import { alertTypeLabel, bundleTriggerLabel } from '@/api/normalize' describe('AlertLabels', () => { it('sofaSepsisLabel', () => { - expect(alertTypeLabel('SofaSepsis')).toBe('Sepsis Alert (SOFA)') + expect(alertTypeLabel('SOFA_SEPSIS')).toBe('Sepsis Alert (SOFA)') }) it('legacySepsisLabel', () => { - expect(alertTypeLabel('SepsisWarning')).toBe('Sepsis Warning (SIRS — Legacy)') + expect(alertTypeLabel('SEPSIS_WARNING')).toBe('Sepsis Warning (SIRS — Legacy)') }) it('qsofaScreenLabel', () => { - expect(alertTypeLabel('QsofaScreen')).toBe('qSOFA Screen') + expect(alertTypeLabel('QSOFA_SCREEN')).toBe('qSOFA Screen') }) it('gcsCriticalLabel', () => { - expect(alertTypeLabel('GcsCritical')).toBe('GCS Critical (≤ 8)') + expect(alertTypeLabel('GCS_CRITICAL')).toBe('GCS Critical (≤ 8)') }) it('bundleTriggerSofa', () => { diff --git a/vigilcare-dashboard/src/__tests__/AlertReasoning.test.js b/vigilcare-dashboard/src/__tests__/AlertReasoning.test.js index 299076b..dd3aa87 100644 --- a/vigilcare-dashboard/src/__tests__/AlertReasoning.test.js +++ b/vigilcare-dashboard/src/__tests__/AlertReasoning.test.js @@ -5,16 +5,16 @@ import AlertReasoning from '@/components/alerts/AlertReasoning.vue' const sepsisAlert = { id: 'alert-1', - alertType: 'SepsisWarning', - severity: 'Critical', + alertType: 'SEPSIS_WARNING', + severity: 'CRITICAL', details: 'SIRS criteria met', triggeredAt: '2026-06-19T12:00:00Z', } const qsofaScreenAlert = { id: 'alert-2', - alertType: 'QsofaScreen', - severity: 'Warning', + alertType: 'QSOFA_SCREEN', + severity: 'WARNING', details: 'qSOFA screen positive', triggeredAt: '2026-06-19T12:30:00Z', } @@ -42,7 +42,7 @@ describe('AlertReasoning', () => { const unknown = { id: 'alert-3', alertType: 'CustomUnknown', - severity: 'Warning', + severity: 'WARNING', details: 'Something unusual happened', triggeredAt: '2026-06-19T13:00:00Z', } @@ -54,8 +54,8 @@ describe('AlertReasoning', () => { it('showsStructuredExplanationWhenPresent', () => { const news2Alert = { id: 'alert-4', - alertType: 'News2Emergency', - severity: 'Critical', + alertType: 'NEWS2_EMERGENCY', + severity: 'CRITICAL', details: 'NEWS2 score 8 (HIGH): RESP_RATE=3, SPO2=2', triggeredAt: '2026-06-19T14:00:00Z', explanation: { diff --git a/vigilcare-dashboard/src/__tests__/CriticalAlertBanner.test.js b/vigilcare-dashboard/src/__tests__/CriticalAlertBanner.test.js index ccae152..f932b61 100644 --- a/vigilcare-dashboard/src/__tests__/CriticalAlertBanner.test.js +++ b/vigilcare-dashboard/src/__tests__/CriticalAlertBanner.test.js @@ -18,9 +18,9 @@ describe('CriticalAlertBanner', () => { const alertStore = useAlertStore() alertStore.bannerAlerts = [{ id: 'alert-1', - alertType: 'SofaSepsis', - severity: 'Critical', - status: 'Open', + alertType: 'SOFA_SEPSIS', + severity: 'CRITICAL', + status: 'OPEN', details: 'SOFA delta >= 2', explanation: { narrativeSummary: 'SOFA 6 — respiratory (+2), renal (+2).', diff --git a/vigilcare-dashboard/src/__tests__/EncounterTimeline.test.js b/vigilcare-dashboard/src/__tests__/EncounterTimeline.test.js index 3104c30..d432f67 100644 --- a/vigilcare-dashboard/src/__tests__/EncounterTimeline.test.js +++ b/vigilcare-dashboard/src/__tests__/EncounterTimeline.test.js @@ -13,9 +13,9 @@ const events = [ { type: 'alert', timestamp: '2026-06-23T11:00:00Z', - alertType: 'QsofaScreen', - severity: 'Warning', - status: 'Open', + alertType: 'QSOFA_SCREEN', + severity: 'WARNING', + status: 'OPEN', }, { type: 'medication', diff --git a/vigilcare-dashboard/src/__tests__/FeedbackButtons.test.js b/vigilcare-dashboard/src/__tests__/FeedbackButtons.test.js index 5e3f025..1f58517 100644 --- a/vigilcare-dashboard/src/__tests__/FeedbackButtons.test.js +++ b/vigilcare-dashboard/src/__tests__/FeedbackButtons.test.js @@ -23,8 +23,8 @@ vi.mock('@/stores/alertQuality', () => ({ const defaultProps = { alertId: 'alert-1', - alertType: 'SepsisWarning', - severity: 'Critical', + alertType: 'SEPSIS_WARNING', + severity: 'CRITICAL', } function ratingButtons(wrapper) { diff --git a/vigilcare-dashboard/src/__tests__/FeedbackSummary.test.js b/vigilcare-dashboard/src/__tests__/FeedbackSummary.test.js index 40fecec..e80f9ba 100644 --- a/vigilcare-dashboard/src/__tests__/FeedbackSummary.test.js +++ b/vigilcare-dashboard/src/__tests__/FeedbackSummary.test.js @@ -10,8 +10,8 @@ const { mockSummaryStats, mockByAlertType } = vi.hoisted(() => ({ falsePositiveRate: 33, }, mockByAlertType: { - SepsisWarning: [{ rating: 'useful' }, { rating: 'would-act' }], - WarningHeartRate: [{ rating: 'false-positive' }], + SEPSIS_WARNING: [{ rating: 'useful' }, { rating: 'would-act' }], + WARNING_HEART_RATE: [{ rating: 'false-positive' }], }, })) diff --git a/vigilcare-dashboard/src/__tests__/criticalAlertDetect.test.js b/vigilcare-dashboard/src/__tests__/criticalAlertDetect.test.js index aa920a0..c9ea46f 100644 --- a/vigilcare-dashboard/src/__tests__/criticalAlertDetect.test.js +++ b/vigilcare-dashboard/src/__tests__/criticalAlertDetect.test.js @@ -3,23 +3,23 @@ import { detectNewCriticalAlerts, isNotifiableCriticalAlert } from '@/composable const criticalOpen = { id: 'a1', - severity: 'Critical', - status: 'Open', - alertType: 'SofaSepsis', + severity: 'CRITICAL', + status: 'OPEN', + alertType: 'SOFA_SEPSIS', } const criticalAcknowledged = { id: 'a2', - severity: 'Critical', - status: 'Acknowledged', - alertType: 'News2Emergency', + severity: 'CRITICAL', + status: 'ACKNOWLEDGED', + alertType: 'NEWS2_EMERGENCY', } const warningOpen = { id: 'a3', - severity: 'Warning', - status: 'Open', - alertType: 'News2Warning', + severity: 'WARNING', + status: 'OPEN', + alertType: 'NEWS2_WARNING', } describe('criticalAlertDetect', () => { @@ -39,7 +39,7 @@ describe('criticalAlertDetect', () => { it('detectsNewCriticalAlertsAfterSeed', () => { const seeded = detectNewCriticalAlerts([criticalOpen], [], false) const next = detectNewCriticalAlerts( - [criticalOpen, { ...criticalOpen, id: 'a4', alertType: 'GcsCritical' }], + [criticalOpen, { ...criticalOpen, id: 'a4', alertType: 'GCS_CRITICAL' }], seeded.nextSeenIds, seeded.seeded, ) diff --git a/vigilcare-dashboard/src/__tests__/handoffReport.test.js b/vigilcare-dashboard/src/__tests__/handoffReport.test.js index 4e48813..cb6060d 100644 --- a/vigilcare-dashboard/src/__tests__/handoffReport.test.js +++ b/vigilcare-dashboard/src/__tests__/handoffReport.test.js @@ -29,7 +29,7 @@ const enrichment = { admissionReason: 'Sepsis workup', patient: { allergies: 'Penicillin' }, }, - openAlerts: [{ alertType: 'News2Emergency' }], + openAlerts: [{ alertType: 'NEWS2_EMERGENCY' }], pendingOrders: [{ description: 'Blood cultures', status: 'Pending' }], vitals: extractLatestVitals([ { observationCode: 'HEART_RATE', value: 110, unit: 'bpm', recordedAt: '2026-06-23T10:00:00Z' }, diff --git a/vigilcare-dashboard/src/__tests__/useAlertStore.test.js b/vigilcare-dashboard/src/__tests__/useAlertStore.test.js index dddd58e..4864752 100644 --- a/vigilcare-dashboard/src/__tests__/useAlertStore.test.js +++ b/vigilcare-dashboard/src/__tests__/useAlertStore.test.js @@ -11,9 +11,9 @@ describe('alert store notifications', () => { const store = useAlertStore() const critical = { id: 'alert-1', - severity: 'Critical', - status: 'Open', - alertType: 'SofaSepsis', + severity: 'CRITICAL', + status: 'OPEN', + alertType: 'SOFA_SEPSIS', } expect(store.applyPollResults([critical])).toEqual([]) @@ -21,9 +21,9 @@ describe('alert store notifications', () => { const next = { id: 'alert-2', - severity: 'Critical', - status: 'Open', - alertType: 'GcsCritical', + severity: 'CRITICAL', + status: 'OPEN', + alertType: 'GCS_CRITICAL', } const newAlerts = store.applyPollResults([critical, next]) expect(newAlerts.map(alert => alert.id)).toEqual(['alert-2']) diff --git a/vigilcare-dashboard/src/__tests__/useFeedbackStore.test.js b/vigilcare-dashboard/src/__tests__/useFeedbackStore.test.js index dbd4d87..78e9c4b 100644 --- a/vigilcare-dashboard/src/__tests__/useFeedbackStore.test.js +++ b/vigilcare-dashboard/src/__tests__/useFeedbackStore.test.js @@ -14,7 +14,7 @@ describe('useFeedbackStore', () => { it('addFeedback_createsEntry', () => { const store = useFeedbackStore() - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'useful') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'useful') expect(store.entries).toHaveLength(1) expect(store.entries[0].alertId).toBe('a1') @@ -23,8 +23,8 @@ describe('useFeedbackStore', () => { it('addFeedback_updatesExisting', () => { const store = useFeedbackStore() - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'useful') - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'false-positive') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'useful') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'false-positive') expect(store.entries).toHaveLength(1) expect(store.entries[0].rating).toBe('false-positive') @@ -32,10 +32,10 @@ describe('useFeedbackStore', () => { it('stats_computesCorrectly', () => { const store = useFeedbackStore() - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'useful') - store.addFeedback('a2', 'SepsisWarning', 'Critical', 'would-act') - store.addFeedback('a3', 'WarningHeartRate', 'Warning', 'false-positive') - store.addFeedback('a4', 'WarningHeartRate', 'Warning', 'too-early') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'useful') + store.addFeedback('a2', 'SEPSIS_WARNING', 'CRITICAL', 'would-act') + store.addFeedback('a3', 'WARNING_HEART_RATE', 'WARNING', 'false-positive') + store.addFeedback('a4', 'WARNING_HEART_RATE', 'WARNING', 'too-early') expect(store.stats.total).toBe(4) expect(store.stats.useful).toBe(2) @@ -46,18 +46,18 @@ describe('useFeedbackStore', () => { it('byAlertType_groupsCorrectly', () => { const store = useFeedbackStore() - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'useful') - store.addFeedback('a2', 'WarningHeartRate', 'Warning', 'false-positive') - store.addFeedback('a3', 'SepsisWarning', 'Critical', 'too-early') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'useful') + store.addFeedback('a2', 'WARNING_HEART_RATE', 'WARNING', 'false-positive') + store.addFeedback('a3', 'SEPSIS_WARNING', 'CRITICAL', 'too-early') expect(Object.keys(store.byAlertType)).toHaveLength(2) - expect(store.byAlertType.SepsisWarning).toHaveLength(2) - expect(store.byAlertType.WarningHeartRate).toHaveLength(1) + expect(store.byAlertType.SEPSIS_WARNING).toHaveLength(2) + expect(store.byAlertType.WARNING_HEART_RATE).toHaveLength(1) }) it('exportAsJson_generatesValidJson', () => { const store = useFeedbackStore() - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'useful') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'useful') let capturedBlob = null const click = vi.fn() @@ -81,7 +81,7 @@ describe('useFeedbackStore', () => { it('clearAll_emptiesEntries', () => { const store = useFeedbackStore() - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'useful') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'useful') store.clearAll() expect(store.entries).toHaveLength(0) @@ -89,7 +89,7 @@ describe('useFeedbackStore', () => { it('persistsToLocalStorage', () => { const store = useFeedbackStore() - store.addFeedback('a1', 'SepsisWarning', 'Critical', 'useful') + store.addFeedback('a1', 'SEPSIS_WARNING', 'CRITICAL', 'useful') const stored = JSON.parse(localStorage.getItem('vigilcare-feedback')) expect(stored).toHaveLength(1) diff --git a/vigilcare-dashboard/src/api/normalize.js b/vigilcare-dashboard/src/api/normalize.js index 0134980..465190c 100644 --- a/vigilcare-dashboard/src/api/normalize.js +++ b/vigilcare-dashboard/src/api/normalize.js @@ -1,43 +1,43 @@ const ALERT_TYPE_LABELS = { - SepsisWarning: 'Sepsis Warning (SIRS — Legacy)', - QsofaWarning: 'qSOFA Alert (Legacy)', - QsofaScreen: 'qSOFA Screen', - SofaSepsis: 'Sepsis Alert (SOFA)', - SofaWarning: 'SOFA Warning', - GcsCritical: 'GCS Critical (≤ 8)', - GcsWarning: 'GCS Warning (9–12)', - News2Warning: 'NEWS2 Warning', - News2Emergency: 'NEWS2 Emergency', - RapidDeterioration: 'Rapid Deterioration', - CriticalHeartRate: 'Critical Heart Rate', - CriticalTempC: 'Critical Temperature', - CriticalPotassiumMeqL: 'Critical Potassium', - CriticalSpo2: 'Critical SpO₂', - CriticalRespRate: 'Critical Respiratory Rate', - CriticalWbcKUl: 'Critical WBC', - CriticalSystolicBp: 'Critical Systolic BP', - CriticalDiastolicBp: 'Critical Diastolic BP', - CriticalLactateMmolL: 'Critical Lactate', - CriticalAvpu: 'Critical AVPU', - CriticalGlucoseMgDl: 'Critical Glucose', - WarningHeartRate: 'Warning Heart Rate', - WarningTempC: 'Warning Temperature', - WarningPotassiumMeqL: 'Warning Potassium', - WarningSpo2: 'Warning SpO₂', - WarningRespRate: 'Warning Respiratory Rate', - WarningWbcKUl: 'Warning WBC', - WarningSystolicBp: 'Warning Systolic BP', - WarningDiastolicBp: 'Warning Diastolic BP', - WarningLactateMmolL: 'Warning Lactate', - WarningGlucoseMgDl: 'Warning Glucose', - CriticalPao2MmHg: 'Critical PaO₂', - WarningPao2MmHg: 'Warning PaO₂', - CriticalPlateletKUl: 'Critical Platelets', - WarningPlateletKUl: 'Warning Platelets', - CriticalBilirubinMgDl: 'Critical Bilirubin', - WarningBilirubinMgDl: 'Warning Bilirubin', - CriticalCreatinineMgDl: 'Critical Creatinine', - WarningCreatinineMgDl: 'Warning Creatinine', + SEPSIS_WARNING: 'Sepsis Warning (SIRS — Legacy)', + QSOFA_WARNING: 'qSOFA Alert (Legacy)', + QSOFA_SCREEN: 'qSOFA Screen', + SOFA_SEPSIS: 'Sepsis Alert (SOFA)', + SOFA_WARNING: 'SOFA Warning', + GCS_CRITICAL: 'GCS Critical (≤ 8)', + GCS_WARNING: 'GCS Warning (9–12)', + NEWS2_WARNING: 'NEWS2 Warning', + NEWS2_EMERGENCY: 'NEWS2 Emergency', + RAPID_DETERIORATION: 'Rapid Deterioration', + CRITICAL_HEART_RATE: 'Critical Heart Rate', + CRITICAL_TEMP_C: 'Critical Temperature', + CRITICAL_POTASSIUM_MEQ_L: 'Critical Potassium', + CRITICAL_SPO2: 'Critical SpO₂', + CRITICAL_RESP_RATE: 'Critical Respiratory Rate', + CRITICAL_WBC_K_UL: 'Critical WBC', + CRITICAL_SYSTOLIC_BP: 'Critical Systolic BP', + CRITICAL_DIASTOLIC_BP: 'Critical Diastolic BP', + CRITICAL_LACTATE_MMOL_L: 'Critical Lactate', + CRITICAL_AVPU: 'Critical AVPU', + CRITICAL_GLUCOSE_MG_DL: 'Critical Glucose', + WARNING_HEART_RATE: 'Warning Heart Rate', + WARNING_TEMP_C: 'Warning Temperature', + WARNING_POTASSIUM_MEQ_L: 'Warning Potassium', + WARNING_SPO2: 'Warning SpO₂', + WARNING_RESP_RATE: 'Warning Respiratory Rate', + WARNING_WBC_K_UL: 'Warning WBC', + WARNING_SYSTOLIC_BP: 'Warning Systolic BP', + WARNING_DIASTOLIC_BP: 'Warning Diastolic BP', + WARNING_LACTATE_MMOL_L: 'Warning Lactate', + WARNING_GLUCOSE_MG_DL: 'Warning Glucose', + CRITICAL_PAO2_MMHG: 'Critical PaO₂', + WARNING_PAO2_MMHG: 'Warning PaO₂', + CRITICAL_PLATELET_K_UL: 'Critical Platelets', + WARNING_PLATELET_K_UL: 'Warning Platelets', + CRITICAL_BILIRUBIN_MG_DL: 'Critical Bilirubin', + WARNING_BILIRUBIN_MG_DL: 'Warning Bilirubin', + CRITICAL_CREATININE_MG_DL: 'Critical Creatinine', + WARNING_CREATININE_MG_DL: 'Warning Creatinine', } const BUNDLE_TRIGGER_LABELS = { @@ -49,7 +49,7 @@ const BUNDLE_TRIGGER_LABELS = { export function alertTypeLabel(type) { if (!type) return '' return ALERT_TYPE_LABELS[type] - ?? type.replace(/([A-Z])/g, '_$1').slice(1).toUpperCase() + ?? type.replace(/_/g, ' ') } export function bundleTriggerLabel(triggerType) { @@ -58,13 +58,7 @@ export function bundleTriggerLabel(triggerType) { } export function alertStatusToApiFilter(status) { - const map = { - Open: 'OPEN', - Acknowledged: 'ACKNOWLEDGED', - Resolved: 'RESOLVED', - Escalated: 'ESCALATED', - } - return map[status] ?? null + return status ?? null } const OBSERVATION_LABELS = { diff --git a/vigilcare-dashboard/src/components/alerts/AcknowledgeModal.vue b/vigilcare-dashboard/src/components/alerts/AcknowledgeModal.vue index e9357df..cde5db7 100644 --- a/vigilcare-dashboard/src/components/alerts/AcknowledgeModal.vue +++ b/vigilcare-dashboard/src/components/alerts/AcknowledgeModal.vue @@ -35,7 +35,7 @@ const notePreview = computed(() => ) function severityVariant(severity) { - return severity === 'Critical' ? 'critical' : 'warning' + return severity === 'CRITICAL' ? 'critical' : 'warning' } function onConfirm() { diff --git a/vigilcare-dashboard/src/components/alerts/AlertCard.vue b/vigilcare-dashboard/src/components/alerts/AlertCard.vue index ed51fbd..73617bd 100644 --- a/vigilcare-dashboard/src/components/alerts/AlertCard.vue +++ b/vigilcare-dashboard/src/components/alerts/AlertCard.vue @@ -17,24 +17,24 @@ const emit = defineEmits(['acknowledge', 'resolve']) const actionHint = computed(() => { const hints = { - QsofaScreen: 'Recommend SOFA labs', - SofaSepsis: 'Review organ breakdown · Initiate bundle', - GcsCritical: 'Urgent neuro assessment', - GcsWarning: 'Monitor consciousness', + QSOFA_SCREEN: 'Recommend SOFA labs', + SOFA_SEPSIS: 'Review organ breakdown · Initiate bundle', + GCS_CRITICAL: 'Urgent neuro assessment', + GCS_WARNING: 'Monitor consciousness', } return hints[props.alert.alertType] ?? null }) function showActions(status) { - return status !== 'Resolved' + return status !== 'RESOLVED' } function canAcknowledge(status) { - return status === 'Open' || status === 'Escalated' + return status === 'OPEN' || status === 'ESCALATED' } function canResolve(status) { - return status === 'Acknowledged' + return status === 'ACKNOWLEDGED' } function formatTime(iso) { @@ -93,7 +93,7 @@ function formatTime(iso) {
-const activeFilter = defineModel({ type: String, default: 'Open' }) +const activeFilter = defineModel({ type: String, default: 'OPEN' }) const tabs = [ - { label: 'Open', value: 'Open', badgeVariant: 'critical' }, - { label: 'Acknowledged', value: 'Acknowledged', badgeVariant: 'warning' }, - { label: 'Resolved', value: 'Resolved', badgeVariant: 'success' }, - { label: 'Escalated', value: 'Escalated', badgeVariant: 'info' }, + { label: 'Open', value: 'OPEN', badgeVariant: 'critical' }, + { label: 'Acknowledged', value: 'ACKNOWLEDGED', badgeVariant: 'warning' }, + { label: 'Resolved', value: 'RESOLVED', badgeVariant: 'success' }, + { label: 'Escalated', value: 'ESCALATED', badgeVariant: 'info' }, ] diff --git a/vigilcare-dashboard/src/components/alerts/AlertReasoning.vue b/vigilcare-dashboard/src/components/alerts/AlertReasoning.vue index 59ebdd9..4087489 100644 --- a/vigilcare-dashboard/src/components/alerts/AlertReasoning.vue +++ b/vigilcare-dashboard/src/components/alerts/AlertReasoning.vue @@ -19,57 +19,57 @@ const props = defineProps({ const CORRELATION_WINDOW_MS = 90 * 60 * 1000 const reasoningMap = { - WarningHeartRate: { + WARNING_HEART_RATE: { label: 'Heart Rate Warning', explain: (a) => `Heart rate ${extractValue(a)} is in the warning range (91–110 bpm or 41–50 bpm).`, }, - WarningSystolicBp: { + WARNING_SYSTOLIC_BP: { label: 'Systolic BP Warning', explain: (a) => `Systolic BP ${extractValue(a)} is in the warning range (101–110 mmHg).`, }, - WarningTempC: { + WARNING_TEMP_C: { label: 'Temperature Warning', explain: (a) => `Temperature ${extractValue(a)} is in the warning range.`, }, - SepsisWarning: { + SEPSIS_WARNING: { label: 'SIRS / Sepsis Alert (Legacy)', explain: () => 'Historical alert: ≥2 of 4 SIRS criteria met. New sepsis detection uses SOFA delta ≥ 2.', }, - QsofaWarning: { + QSOFA_WARNING: { label: 'qSOFA Alert (Legacy)', explain: () => 'Historical alert: ≥2 of 3 qSOFA criteria met.', }, - QsofaScreen: { + QSOFA_SCREEN: { label: 'qSOFA Screen', explain: () => 'Bedside screen positive (≥2/3). Recommend ordering SOFA labs to evaluate organ dysfunction.', }, - SofaSepsis: { + SOFA_SEPSIS: { label: 'Sepsis Alert (SOFA)', explain: (a) => explainSofaAlert(a, true), }, - SofaWarning: { + SOFA_WARNING: { label: 'SOFA Warning', explain: (a) => explainSofaAlert(a, false), }, - GcsCritical: { + GCS_CRITICAL: { label: 'GCS Critical', explain: (a) => explainGcsAlert(a), }, - GcsWarning: { + GCS_WARNING: { label: 'GCS Warning', explain: (a) => explainGcsAlert(a), }, - News2Warning: { + NEWS2_WARNING: { label: 'NEWS2 Medium Risk', explain: () => 'NEWS2 composite score 5–6 indicating medium clinical risk.', }, - News2Emergency: { + NEWS2_EMERGENCY: { label: 'NEWS2 High Risk', explain: () => 'NEWS2 composite score ≥7 or any single parameter scored 3.', }, - RapidDeterioration: { + RAPID_DETERIORATION: { label: 'Rapid Deterioration', explain: () => 'Vital sign trajectory shows rapid change within the sliding window.', }, @@ -104,10 +104,10 @@ const recentMedications = computed(() => { const actionHint = computed(() => { const hints = { - QsofaScreen: 'Recommend ordering SOFA labs (PaO₂, platelets, bilirubin, creatinine).', - SofaSepsis: 'Review organ dysfunction and confirm sepsis bundle initiation.', - GcsCritical: 'Urgent neurological assessment — GCS ≤ 8.', - GcsWarning: 'Monitor consciousness closely — GCS 9–12.', + QSOFA_SCREEN: 'Recommend ordering SOFA labs (PaO₂, platelets, bilirubin, creatinine).', + SOFA_SEPSIS: 'Review organ dysfunction and confirm sepsis bundle initiation.', + GCS_CRITICAL: 'Urgent neurological assessment — GCS ≤ 8.', + GCS_WARNING: 'Monitor consciousness closely — GCS 9–12.', } return hints[props.alert.alertType] ?? null }) @@ -155,7 +155,7 @@ function formatMed(med) {
- + {{ alert.severity }}

diff --git a/vigilcare-dashboard/src/components/patient/AlertsList.vue b/vigilcare-dashboard/src/components/patient/AlertsList.vue index 977e3e7..38b5e42 100644 --- a/vigilcare-dashboard/src/components/patient/AlertsList.vue +++ b/vigilcare-dashboard/src/components/patient/AlertsList.vue @@ -30,7 +30,7 @@ function loadEncounterAlerts() { usePolling(loadEncounterAlerts, 5_000) const visibleAlerts = computed(() => - alerts.value.filter(a => a.status !== 'Resolved'), + alerts.value.filter(a => a.status !== 'RESOLVED'), ) async function handleAcknowledge(note) { @@ -89,7 +89,7 @@ async function resolve(alertId) {