feature: Digitization Workstation UI
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<AppHeader title="Intake" />
|
||||
|
||||
<div class="page-container flex-1">
|
||||
<h1 class="text-2xl font-bold mb-6">Upload Scanned Document</h1>
|
||||
|
||||
<!-- Upload form -->
|
||||
<div class="card mb-6">
|
||||
<h2 class="text-lg font-semibold mb-4">New Batch</h2>
|
||||
|
||||
<form @submit.prevent="handleUpload" class="space-y-4">
|
||||
<!-- File input -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Scanned Document (PDF, JPEG, PNG — max 25 MB)
|
||||
</label>
|
||||
<input
|
||||
type="file"
|
||||
ref="fileInput"
|
||||
@change="onFileChange"
|
||||
accept=".pdf,.jpg,.jpeg,.png"
|
||||
class="block w-full text-sm text-gray-500
|
||||
file:mr-4 file:py-2 file:px-4
|
||||
file:rounded-md file:border-0
|
||||
file:text-sm file:font-semibold
|
||||
file:bg-primary-50 file:text-primary-700
|
||||
hover:file:bg-primary-100"
|
||||
/>
|
||||
<p v-if="selectedFile" class="text-sm text-gray-500 mt-2">
|
||||
{{ selectedFile.name }} ({{ (selectedFile.size / 1024 / 1024).toFixed(2) }} MB)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Batch type -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Batch Type</label>
|
||||
<select v-model="batchType" class="form-input" required>
|
||||
<option value="">Select batch type...</option>
|
||||
<option value="PATIENT_REGISTRATION">Patient Registration</option>
|
||||
<option value="ENCOUNTER_SUMMARY">Encounter Summary</option>
|
||||
<option value="VITALS_SHEET">Vitals Sheet</option>
|
||||
<option value="LAB_RESULTS">Lab Results</option>
|
||||
<option value="MEDICATION_LIST">Medication List</option>
|
||||
<option value="ALLERGY_UPDATE">Allergy Update</option>
|
||||
<option value="MIXED">Mixed</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Track -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Track</label>
|
||||
<select v-model="track" class="form-input">
|
||||
<option value="BACKFILL">Backfill (Track A)</option>
|
||||
<option value="LIVE_CAPTURE">Live Capture (Track B)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Patient search -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Patient (optional — can link later)
|
||||
</label>
|
||||
<PatientSearch v-model="patientId" />
|
||||
</div>
|
||||
|
||||
<div v-if="uploadError" class="text-clinical-danger text-sm">
|
||||
{{ uploadError }}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn-primary"
|
||||
:disabled="!selectedFile || !batchType || batchStore.loading"
|
||||
>
|
||||
{{ batchStore.loading ? 'Uploading...' : 'Upload and Create Batch' }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Recent uploads -->
|
||||
<div class="card">
|
||||
<h2 class="text-lg font-semibold mb-4">Recent Uploads</h2>
|
||||
<div v-if="assignError" class="text-clinical-danger text-sm mb-4">
|
||||
{{ assignError }}
|
||||
</div>
|
||||
<BatchList
|
||||
:batches="batchStore.batches"
|
||||
:loading="batchStore.loading"
|
||||
show-assign
|
||||
@assign="openAssignDialog"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AssignClerkDialog
|
||||
:show="assignDialogOpen"
|
||||
:batch-id="assignBatchId"
|
||||
@close="closeAssignDialog"
|
||||
@assigned="handleAssign"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useBatchStore } from '../stores/batches'
|
||||
import AppHeader from '../components/AppHeader.vue'
|
||||
import PatientSearch from '../components/PatientSearch.vue'
|
||||
import BatchList from '../components/BatchList.vue'
|
||||
import AssignClerkDialog from '../components/AssignClerkDialog.vue'
|
||||
|
||||
const batchStore = useBatchStore()
|
||||
|
||||
const selectedFile = ref<File | null>(null)
|
||||
const batchType = ref('')
|
||||
const track = ref('BACKFILL')
|
||||
const patientId = ref<string | undefined>(undefined)
|
||||
const uploadError = ref('')
|
||||
const assignError = ref('')
|
||||
const assignDialogOpen = ref(false)
|
||||
const assignBatchId = ref<string | null>(null)
|
||||
|
||||
function onFileChange(event: Event) {
|
||||
const input = event.target as HTMLInputElement
|
||||
selectedFile.value = input.files?.[0] ?? null
|
||||
}
|
||||
|
||||
async function handleUpload() {
|
||||
if (!selectedFile.value || !batchType.value) return
|
||||
|
||||
uploadError.value = ''
|
||||
try {
|
||||
const batch = await batchStore.uploadBatch(
|
||||
selectedFile.value,
|
||||
batchType.value,
|
||||
track.value,
|
||||
patientId.value
|
||||
)
|
||||
if (batch) {
|
||||
selectedFile.value = null
|
||||
batchType.value = ''
|
||||
track.value = 'BACKFILL'
|
||||
patientId.value = undefined
|
||||
await loadRecent()
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
uploadError.value = e instanceof Error ? e.message : 'Upload failed'
|
||||
}
|
||||
}
|
||||
|
||||
function openAssignDialog(batchId: string) {
|
||||
assignError.value = ''
|
||||
assignBatchId.value = batchId
|
||||
assignDialogOpen.value = true
|
||||
}
|
||||
|
||||
function closeAssignDialog() {
|
||||
assignDialogOpen.value = false
|
||||
assignBatchId.value = null
|
||||
}
|
||||
|
||||
async function handleAssign(batchId: string, clerkUserId: string) {
|
||||
assignError.value = ''
|
||||
try {
|
||||
await batchStore.assignBatch(batchId, clerkUserId)
|
||||
closeAssignDialog()
|
||||
await loadRecent()
|
||||
} catch (e: unknown) {
|
||||
assignError.value = e instanceof Error ? e.message : 'Assignment failed'
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRecent() {
|
||||
await batchStore.listBatches({ status: 'UPLOADED', page: 1, pageSize: 20 })
|
||||
}
|
||||
|
||||
onMounted(loadRecent)
|
||||
</script>
|
||||
Reference in New Issue
Block a user