feature: Degraded Operations Visibility

This commit is contained in:
voltsrage
2026-06-23 23:18:31 +08:00
parent 940e27c0ac
commit 4399996448
81 changed files with 3949 additions and 323 deletions
@@ -4,6 +4,7 @@ import { useRoute } from 'vue-router'
import { storeToRefs } from 'pinia'
import { usePolling } from '@/composables/usePolling'
import { useReplayControls } from '@/composables/useReplayControls'
import { useRoleAccess } from '@/composables/roleAccess'
import { useAlertStore } from '@/stores/alerts'
import { useScoringStore } from '@/stores/scoring'
import * as encountersApi from '@/api/encounters'
@@ -21,11 +22,14 @@ import GcsHistory from '@/components/charts/GcsHistory.vue'
import QsofaHistory from '@/components/charts/QsofaHistory.vue'
import SofaHistory from '@/components/charts/SofaHistory.vue'
import EncounterTimeline from '@/components/patient/EncounterTimeline.vue'
import DischargeSummaryPanel from '@/components/patient/DischargeSummaryPanel.vue'
import ReplayControls from '@/components/replay/ReplayControls.vue'
import AlertReasoning from '@/components/alerts/AlertReasoning.vue'
import CollapsibleSection from '@/components/ui/CollapsibleSection.vue'
import Skeleton from '@/components/ui/Skeleton.vue'
const route = useRoute()
const { nursePatientLayout, physicianPatientLayout } = useRoleAccess()
const alertStore = useAlertStore()
const scoringStore = useScoringStore()
const { alerts } = storeToRefs(alertStore)
@@ -66,6 +70,8 @@ const openAlerts = computed(() =>
alerts.value.filter(a => a.status === 'Open' || a.status === 'Escalated'),
)
const isDischarged = computed(() => encounter.value?.status === 'Discharged')
const replayObservations = computed(() =>
observations.value.filter(o => isAtOrBefore(o.recordedAt)),
)
@@ -198,26 +204,43 @@ onBeforeUnmount(() => {
<template>
<Skeleton v-if="loading && !encounter" :rows="6" />
<div v-else-if="encounter" class="w-full min-w-0 space-y-8">
<div v-else-if="encounter" class="w-full min-w-0 space-y-6 lg:space-y-8">
<div class="flex flex-wrap items-center gap-4">
<RouterLink to="/ward" class="text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
<RouterLink
to="/ward"
class="inline-flex min-h-11 items-center text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
>
&larr; Ward
</RouterLink>
</div>
<PatientBanner :encounter="encounter" />
<div class="grid min-w-0 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div class="space-y-4">
<DischargeSummaryPanel
:encounter-id="route.params.encounterId"
:discharged="isDischarged"
/>
<div class="flex flex-col gap-4 lg:grid lg:grid-cols-3">
<div
class="order-1 space-y-4"
:class="{
'lg:order-3': nursePatientLayout,
'lg:order-1': physicianPatientLayout || (!nursePatientLayout && !physicianPatientLayout),
}"
>
<ScoresPanel />
<GcsHistory v-if="replayGcsHistory.length" :history="replayGcsHistory" />
</div>
<VitalsPanel
class="order-2 min-w-0 lg:order-2"
:encounter-id="route.params.encounterId"
:observations="replayObservations"
@recorded="loadAll"
/>
<AlertsList
class="order-3 min-w-0 lg:order-3"
:class="{ 'lg:order-1': nursePatientLayout }"
:encounter-id="route.params.encounterId"
:selected-id="selectedAlert?.id"
@select="onSelectAlert"
@@ -230,12 +253,14 @@ onBeforeUnmount(() => {
:medications="medications"
/>
<div class="grid min-w-0 gap-4 lg:grid-cols-2">
<div class="space-y-4">
<div class="flex flex-col gap-4 lg:grid lg:grid-cols-2">
<div class="order-2 space-y-4 lg:order-1">
<SofaScorePanel :encounter-id="route.params.encounterId" />
<SofaHistory v-if="replaySofaHistory.length" :history="replaySofaHistory" />
</div>
<OrdersPanel :orders="orders" />
<div class="order-1 lg:order-2">
<OrdersPanel :orders="orders" />
</div>
</div>
<SepsisBundlePanel
@@ -244,9 +269,19 @@ onBeforeUnmount(() => {
:sofa="sofa"
/>
<EncounterTimeline :events="replayTimelineEvents" />
<div class="hidden lg:block">
<EncounterTimeline :events="replayTimelineEvents" />
</div>
<CollapsibleSection
class="lg:hidden"
section-id="encounter-timeline"
title="Encounter timeline"
:default-open="false"
>
<EncounterTimeline :events="replayTimelineEvents" />
</CollapsibleSection>
<div id="clinical-review" class="w-full min-w-0 space-y-8">
<div id="clinical-review" class="hidden w-full min-w-0 space-y-6 lg:block lg:space-y-8">
<TrendsGrid :observations="replayObservations" :medications="replayMedications" />
<News2History v-if="replayNews2History.length" :history="replayNews2History" />
<QsofaHistory v-if="replayQsofaHistory.length" :history="replayQsofaHistory" />
@@ -262,5 +297,37 @@ onBeforeUnmount(() => {
@jump-to-alert="jumpToNextAlert"
/>
</div>
<CollapsibleSection
class="lg:hidden"
section-id="clinical-review"
title="Vital trends & clinical review"
:default-open="false"
>
<div class="space-y-6">
<TrendsGrid :observations="replayObservations" :medications="replayMedications" />
<News2History v-if="replayNews2History.length" :history="replayNews2History" />
<QsofaHistory v-if="replayQsofaHistory.length" :history="replayQsofaHistory" />
</div>
</CollapsibleSection>
<CollapsibleSection
class="lg:hidden"
section-id="replay-controls"
title="Scenario replay"
:default-open="false"
>
<ReplayControls
:alerts="openAlerts"
:is-paused="isPaused"
:progress="progress"
:formatted-time="formattedTime"
:speed="speed"
@pause="pause()"
@resume="resume()"
@set-speed="setSpeed"
@jump-to-alert="jumpToNextAlert"
/>
</CollapsibleSection>
</div>
</template>