134 lines
4.5 KiB
Vue
134 lines
4.5 KiB
Vue
<script setup>
|
|
import { computed } from 'vue'
|
|
import Badge from '@/components/ui/Badge.vue'
|
|
import {
|
|
formatGcsClassification,
|
|
formatLastObservation,
|
|
formatLengthOfStay,
|
|
observationStaleness,
|
|
patientRoom,
|
|
stalenessClass,
|
|
} from '@/composables/wardFormat'
|
|
import { formatDepartment } from '@/composables/sepsisFormat'
|
|
import { isSimulatedPatient } from '@/api/normalize'
|
|
|
|
const props = defineProps({
|
|
patient: { type: Object, required: true },
|
|
tabindex: { type: [Number, String], default: 0 },
|
|
})
|
|
|
|
const emit = defineEmits(['activate'])
|
|
|
|
const simulated = computed(() => isSimulatedPatient(props.patient))
|
|
|
|
const riskVariant = computed(() => {
|
|
const score = props.patient.news2Score ?? 0
|
|
if (score >= 7) return 'critical'
|
|
if (score >= 5) return 'warning'
|
|
return 'success'
|
|
})
|
|
|
|
const sofaVariant = computed(() => {
|
|
const delta = props.patient.sofaDelta
|
|
if (delta != null && delta >= 2) return 'critical'
|
|
if (delta != null && delta === 1) return 'warning'
|
|
return 'info'
|
|
})
|
|
|
|
const gcsVariant = computed(() => {
|
|
const score = props.patient.gcsScore
|
|
if (score == null) return 'info'
|
|
if (score <= 8) return 'critical'
|
|
if (score <= 12) return 'warning'
|
|
return 'success'
|
|
})
|
|
|
|
const vitalsStaleness = computed(() =>
|
|
observationStaleness(props.patient.lastObservationAt, props.patient.status),
|
|
)
|
|
|
|
const rowLabel = computed(() =>
|
|
`Patient ${props.patient.firstName} ${props.patient.lastName}, room ${patientRoom(props.patient)}${simulated.value ? ', simulated' : ''}`,
|
|
)
|
|
|
|
function onKeydown(event) {
|
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
event.preventDefault()
|
|
emit('activate')
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<tr
|
|
data-patient-row
|
|
:tabindex="tabindex"
|
|
:aria-label="rowLabel"
|
|
@keydown="onKeydown"
|
|
@click="emit('activate')"
|
|
>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-sm text-ink-secondary">
|
|
{{ patientRoom(patient) }}
|
|
</td>
|
|
<td class="px-3 py-2.5">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<div class="text-sm font-medium text-ink">
|
|
{{ patient.firstName }} {{ patient.lastName }}
|
|
</div>
|
|
<span
|
|
v-if="simulated"
|
|
class="inline-flex items-center rounded-sm px-1.5 py-0.5 text-[10px] font-bold uppercase bg-amber-100 text-amber-900 dark:bg-amber-900/40 dark:text-amber-200"
|
|
title="Simulated patient"
|
|
>
|
|
SIM
|
|
</span>
|
|
</div>
|
|
<div class="text-xs text-ink-muted">{{ patient.mrn }}</div>
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-sm text-ink-secondary">
|
|
{{ formatDepartment(patient.department) }}
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-right">
|
|
<Badge :variant="riskVariant">{{ patient.news2Score ?? '—' }}</Badge>
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-right text-sm">
|
|
<div class="flex flex-col items-end gap-1">
|
|
<span class="font-medium text-ink">{{ patient.sofaScore ?? '—' }}</span>
|
|
<Badge v-if="patient.sofaDelta != null && patient.sofaDelta > 0" :variant="sofaVariant" size="xs">
|
|
Δ+{{ patient.sofaDelta }}
|
|
</Badge>
|
|
</div>
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-right text-sm">
|
|
<div class="flex flex-col items-end gap-1">
|
|
<Badge :variant="gcsVariant">{{ patient.gcsScore ?? '—' }}</Badge>
|
|
<span v-if="patient.gcsClassification" class="text-xs text-ink-muted">
|
|
{{ formatGcsClassification(patient.gcsClassification) }}
|
|
</span>
|
|
</div>
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-right text-sm text-ink-secondary">
|
|
{{ patient.qsofaScore ?? '—' }}
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-sm text-ink-secondary">
|
|
{{ patient.attendingPhysician ?? '—' }}
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-sm text-ink-secondary">
|
|
{{ formatLengthOfStay(patient.admittedAt) }}
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5 text-sm" :class="stalenessClass(vitalsStaleness)">
|
|
{{ formatLastObservation(patient.lastObservationAt) }}
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5">
|
|
<Badge v-if="patient.sepsisActive" variant="critical">Active</Badge>
|
|
<span v-else class="text-sm text-ink-muted">No</span>
|
|
</td>
|
|
<td class="whitespace-nowrap px-3 py-2.5">
|
|
<Badge v-if="patient.openAlertCount > 0" :variant="patient.openAlertCount > 2 ? 'critical' : 'warning'">
|
|
{{ patient.openAlertCount }}
|
|
</Badge>
|
|
<span v-else class="text-sm text-ink-muted">0</span>
|
|
</td>
|
|
</tr>
|
|
</template>
|