134 lines
3.9 KiB
Vue
134 lines
3.9 KiB
Vue
<template>
|
|
<div class="h-full min-h-0 flex flex-col">
|
|
<AppHeader title="Clinical Approval" tour-anchor="approval-header">
|
|
<template #subtitle>
|
|
<span v-if="currentBatch" class="text-sm text-ink-secondary">
|
|
Batch {{ currentBatch.id.substring(0, 8) }}…
|
|
· {{ formatBatchType(currentBatch.batchType) }}
|
|
</span>
|
|
</template>
|
|
<template #actions>
|
|
<TourHelpButton />
|
|
</template>
|
|
</AppHeader>
|
|
|
|
<WorkstationLayout :has-batch="!!batchId">
|
|
<template #queue>
|
|
<h2 class="text-xl font-semibold mb-4 text-ink-strong">Clinical Approval Queue</h2>
|
|
<p class="text-sm text-ink-secondary mb-4">
|
|
Verified batches awaiting clinical sign-off before promotion to live tables.
|
|
</p>
|
|
<BatchList
|
|
:batches="batchStore.batches"
|
|
:loading="batchStore.loading"
|
|
:error="batchStore.error"
|
|
empty-title="No batches are waiting for clinical approval."
|
|
empty-description="Verified batches appear here after verification is complete."
|
|
@select="openBatch"
|
|
@retry="loadQueue"
|
|
/>
|
|
</template>
|
|
|
|
<template #rail>
|
|
<WorkstationQueueRail
|
|
title="Approval queue"
|
|
:batches="batchStore.batches"
|
|
:selected-id="batchId"
|
|
:loading="batchStore.loading"
|
|
@select="openBatch"
|
|
@back="router.push('/approval')"
|
|
/>
|
|
</template>
|
|
|
|
<template #scan>
|
|
<ScanViewer
|
|
:url="documentUrl"
|
|
:loading="documentLoading"
|
|
:error="documentError"
|
|
@retry="refreshUrl"
|
|
/>
|
|
</template>
|
|
|
|
<template #form>
|
|
<ApprovalForm
|
|
v-if="batchId"
|
|
:batch="currentBatch"
|
|
:batch-id="batchId"
|
|
:draft="draft"
|
|
@back="router.push('/approval')"
|
|
@view-history="(patientId) => router.push(`/patients/${patientId}/history`)"
|
|
@create-correction="createCorrection"
|
|
@rejected="router.push('/approval')"
|
|
/>
|
|
</template>
|
|
</WorkstationLayout>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, watch } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { useBatchStore } from '../stores/batches'
|
|
import { usePresignedUrl } from '../composables/usePresignedUrl'
|
|
import AppHeader from '../components/AppHeader.vue'
|
|
import TourHelpButton from '../components/TourHelpButton.vue'
|
|
import ScanViewer from '../components/ScanViewer.vue'
|
|
import BatchList from '../components/BatchList.vue'
|
|
import WorkstationLayout from '../components/WorkstationLayout.vue'
|
|
import WorkstationQueueRail from '../components/WorkstationQueueRail.vue'
|
|
import ApprovalForm from '../components/ApprovalForm.vue'
|
|
|
|
const props = defineProps<{ batchId?: string }>()
|
|
|
|
const batchStore = useBatchStore()
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const batchId = computed(() => props.batchId ?? (route.params.batchId as string | undefined))
|
|
const currentBatch = computed(() => batchStore.currentBatch)
|
|
const draft = computed(() => batchStore.currentDraft)
|
|
const { documentUrl, documentError, documentLoading, refreshUrl } = usePresignedUrl(batchId)
|
|
|
|
function openBatch(id: string) {
|
|
router.push(`/approval/${id}`)
|
|
}
|
|
|
|
function formatBatchType(type: string): string {
|
|
return type.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
|
|
}
|
|
|
|
function createCorrection() {
|
|
if (!batchId.value) return
|
|
router.push({
|
|
path: '/intake',
|
|
query: {
|
|
supersedesBatchId: batchId.value,
|
|
patientId: currentBatch.value?.patientId ?? undefined,
|
|
batchType: currentBatch.value?.batchType ?? undefined,
|
|
},
|
|
})
|
|
}
|
|
|
|
watch(
|
|
batchId,
|
|
async (id) => {
|
|
if (id) {
|
|
await batchStore.getBatch(id)
|
|
await batchStore.getDraft(id)
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
onMounted(async () => {
|
|
await loadQueue()
|
|
})
|
|
|
|
async function loadQueue() {
|
|
await batchStore.listClinicalApprovalQueue({
|
|
page: 1,
|
|
pageSize: 50,
|
|
})
|
|
}
|
|
</script>
|