feature: Sepsis Engine Refactor: Remove SIRS, Rewire qSOFA + Bundle
Frontend: GCS Entry, SOFA Display, Sepsis UI Refactor
This commit is contained in:
@@ -1,16 +1,27 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import Card from '@/components/ui/Card.vue'
|
||||
import Badge from '@/components/ui/Badge.vue'
|
||||
import Button from '@/components/ui/Button.vue'
|
||||
import FeedbackButtons from '@/components/feedback/FeedbackButtons.vue'
|
||||
import { alertTypeLabel } from '@/api/normalize'
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
alert: { type: Object, required: true },
|
||||
})
|
||||
|
||||
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',
|
||||
}
|
||||
return hints[props.alert.alertType] ?? null
|
||||
})
|
||||
|
||||
function severityVariant(severity) {
|
||||
return severity === 'Critical' ? 'critical' : 'warning'
|
||||
}
|
||||
@@ -35,11 +46,12 @@ function formatTime(iso) {
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<Badge :variant="severityVariant(alert.severity)">{{ alert.severity }}</Badge>
|
||||
<Badge variant="info" size="xs">{{ alert.status }}</Badge>
|
||||
<Badge v-if="actionHint" variant="info" size="xs">{{ actionHint }}</Badge>
|
||||
</div>
|
||||
<h3 class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">
|
||||
{{ alertTypeLabel(alert.alertType) }}
|
||||
@@ -80,4 +92,4 @@ function formatTime(iso) {
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
</template>
|
||||
@@ -12,14 +12,60 @@ const props = defineProps({
|
||||
const CORRELATION_WINDOW_MS = 90 * 60 * 1000
|
||||
|
||||
const reasoningMap = {
|
||||
WarningHeartRate: { label: 'Heart Rate Warning', explain: (a) => `Heart rate ${extractValue(a)} is in the warning range (91–110 bpm or 41–50 bpm).` },
|
||||
WarningSystolicBp: { label: 'Systolic BP Warning', explain: (a) => `Systolic BP ${extractValue(a)} is in the warning range (101–110 mmHg).` },
|
||||
WarningTempC: { label: 'Temperature Warning', explain: (a) => `Temperature ${extractValue(a)} is in the warning range.` },
|
||||
SepsisWarning: { label: 'SIRS / Sepsis Alert', explain: () => '≥2 of 4 SIRS criteria met: temperature, heart rate, respiratory rate, WBC.' },
|
||||
QsofaWarning: { label: 'qSOFA Alert', explain: () => '≥2 of 3 qSOFA criteria met: RR ≥22, SBP ≤100, altered mentation (AVPU ≥1).' },
|
||||
News2Warning: { label: 'NEWS2 Medium Risk', explain: () => 'NEWS2 composite score 5–6 indicating medium clinical risk.' },
|
||||
News2Emergency: { label: 'NEWS2 High Risk', explain: () => 'NEWS2 composite score ≥7 or any single parameter scored 3.' },
|
||||
RapidDeterioration: { label: 'Rapid Deterioration', explain: () => 'Vital sign trajectory shows rapid change within the sliding window.' },
|
||||
WarningHeartRate: {
|
||||
label: 'Heart Rate Warning',
|
||||
explain: (a) => `Heart rate ${extractValue(a)} is in the warning range (91–110 bpm or 41–50 bpm).`,
|
||||
},
|
||||
WarningSystolicBp: {
|
||||
label: 'Systolic BP Warning',
|
||||
explain: (a) => `Systolic BP ${extractValue(a)} is in the warning range (101–110 mmHg).`,
|
||||
},
|
||||
WarningTempC: {
|
||||
label: 'Temperature Warning',
|
||||
explain: (a) => `Temperature ${extractValue(a)} is in the warning range.`,
|
||||
},
|
||||
SepsisWarning: {
|
||||
label: 'SIRS / Sepsis Alert (Legacy)',
|
||||
explain: () =>
|
||||
'Historical alert: ≥2 of 4 SIRS criteria met. New sepsis detection uses SOFA delta ≥ 2.',
|
||||
},
|
||||
QsofaWarning: {
|
||||
label: 'qSOFA Alert (Legacy)',
|
||||
explain: () => 'Historical alert: ≥2 of 3 qSOFA criteria met.',
|
||||
},
|
||||
QsofaScreen: {
|
||||
label: 'qSOFA Screen',
|
||||
explain: () =>
|
||||
'Bedside screen positive (≥2/3). Recommend ordering SOFA labs to evaluate organ dysfunction.',
|
||||
},
|
||||
SofaSepsis: {
|
||||
label: 'Sepsis Alert (SOFA)',
|
||||
explain: (a) => explainSofaAlert(a, true),
|
||||
},
|
||||
SofaWarning: {
|
||||
label: 'SOFA Warning',
|
||||
explain: (a) => explainSofaAlert(a, false),
|
||||
},
|
||||
GcsCritical: {
|
||||
label: 'GCS Critical',
|
||||
explain: (a) => explainGcsAlert(a),
|
||||
},
|
||||
GcsWarning: {
|
||||
label: 'GCS Warning',
|
||||
explain: (a) => explainGcsAlert(a),
|
||||
},
|
||||
News2Warning: {
|
||||
label: 'NEWS2 Medium Risk',
|
||||
explain: () => 'NEWS2 composite score 5–6 indicating medium clinical risk.',
|
||||
},
|
||||
News2Emergency: {
|
||||
label: 'NEWS2 High Risk',
|
||||
explain: () => 'NEWS2 composite score ≥7 or any single parameter scored 3.',
|
||||
},
|
||||
RapidDeterioration: {
|
||||
label: 'Rapid Deterioration',
|
||||
explain: () => 'Vital sign trajectory shows rapid change within the sliding window.',
|
||||
},
|
||||
}
|
||||
|
||||
const recentMedications = computed(() => {
|
||||
@@ -32,11 +78,45 @@ 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.',
|
||||
}
|
||||
return hints[props.alert.alertType] ?? null
|
||||
})
|
||||
|
||||
function extractValue(alert) {
|
||||
const match = alert.details?.match(/(\d+\.?\d*)/)
|
||||
return match ? match[1] : '—'
|
||||
}
|
||||
|
||||
function explainGcsAlert(alert) {
|
||||
const e = alert.details?.match(/E[=:]?\s*(\d)/i)?.[1]
|
||||
const v = alert.details?.match(/V[=:]?\s*(\d)/i)?.[1]
|
||||
const m = alert.details?.match(/M[=:]?\s*(\d)/i)?.[1]
|
||||
if (e && v && m) return `GCS components: Eye ${e}, Verbal ${v}, Motor ${m}.`
|
||||
return alert.details ?? 'GCS threshold crossed.'
|
||||
}
|
||||
|
||||
function explainSofaAlert(alert, isSepsis) {
|
||||
const delta = alert.details?.match(/delta\s*[+:]?\s*(\d+)/i)?.[1]
|
||||
const baseline = alert.details?.match(/baseline\s*(\d+)/i)?.[1]
|
||||
const current = alert.details?.match(/current\s*(\d+)/i)?.[1]
|
||||
const parts = []
|
||||
if (baseline && current && delta) {
|
||||
parts.push(`SOFA score increased from ${baseline} to ${current} (delta +${delta}).`)
|
||||
} else if (delta) {
|
||||
parts.push(`SOFA delta +${delta} from baseline.`)
|
||||
}
|
||||
const organs = alert.details?.match(/organs?:\s*([^.]+)/i)?.[1]
|
||||
if (organs) parts.push(`Organ dysfunction: ${organs.trim()}.`)
|
||||
if (isSepsis) parts.push('Meets sepsis criteria: suspected infection + SOFA delta ≥ 2.')
|
||||
return parts.length ? parts.join(' ') : (alert.details ?? 'SOFA threshold crossed.')
|
||||
}
|
||||
|
||||
function formatMed(med) {
|
||||
return `${med.drugName} ${med.dose}${med.doseUnit} (${med.route})`
|
||||
}
|
||||
@@ -58,6 +138,13 @@ function formatMed(med) {
|
||||
{{ reasoningMap[alert.alertType]?.explain(alert) ?? alert.details }}
|
||||
</p>
|
||||
|
||||
<p
|
||||
v-if="actionHint"
|
||||
class="rounded bg-blue-50 px-4 py-2 text-sm text-blue-800 dark:bg-blue-950/40 dark:text-blue-200"
|
||||
>
|
||||
{{ actionHint }}
|
||||
</p>
|
||||
|
||||
<div
|
||||
v-if="recentMedications.length"
|
||||
class="rounded bg-amber-50 p-4 text-sm text-amber-900 dark:bg-amber-950/40 dark:text-amber-200"
|
||||
@@ -70,7 +157,10 @@ function formatMed(med) {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="alert.details" class="rounded bg-gray-50 p-4 text-xs font-mono text-gray-700 dark:bg-gray-800 dark:text-gray-300">
|
||||
<div
|
||||
v-if="alert.details"
|
||||
class="rounded bg-gray-50 p-4 text-xs font-mono text-gray-700 dark:bg-gray-800 dark:text-gray-300"
|
||||
>
|
||||
{{ alert.details }}
|
||||
</div>
|
||||
|
||||
@@ -87,4 +177,4 @@ function formatMed(med) {
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
</template>
|
||||
Reference in New Issue
Block a user