mv treatment-report to action-report based on encounter menu id list
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import List from './list.vue'
|
||||
import Form from './form.vue'
|
||||
import View from './view.vue'
|
||||
|
||||
// Models
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
encounter: Encounter
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { mode, goToEntry, goToView } = useQueryCRUDMode('mode')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<List
|
||||
v-if="mode === 'list'"
|
||||
:encounter="props.encounter"
|
||||
@add="goToEntry"
|
||||
@edit="goToEntry({ fromView: false })"
|
||||
@view="goToView"
|
||||
/>
|
||||
<View
|
||||
v-else-if="mode === 'view'"
|
||||
:encounter="props.encounter"
|
||||
/>
|
||||
<Form v-else />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import mockData from './sample'
|
||||
|
||||
// type
|
||||
import { genDoctor, type Doctor } from '~/models/doctor'
|
||||
import type { ActionReportFormData } from '~/schemas/action-report.schema'
|
||||
|
||||
// components
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import AppActionReportEntry from '~/components/app/action-report/entry-form.vue'
|
||||
import ArrangementProcedurePicker from '~/components/app/therapy-protocol/picker-dialog/arrangement-procedure/procedure-picker.vue'
|
||||
|
||||
// states
|
||||
const route = useRoute()
|
||||
const { mode, goBack } = useQueryCRUDMode('mode')
|
||||
const { recordId } = useQueryCRUDRecordId('record-id')
|
||||
const reportData = ref<ActionReportFormData>({} as unknown as ActionReportFormData)
|
||||
const doctors = ref<Doctor[]>([])
|
||||
const isLoading = ref<boolean>(false)
|
||||
|
||||
// TODO: dummy data
|
||||
;(() => {
|
||||
doctors.value = [genDoctor()]
|
||||
})()
|
||||
|
||||
const entryMode = ref<'add' | 'edit' | 'view'>('add')
|
||||
const isDataReady = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
if (mode.value === 'entry' && recordId.value) {
|
||||
entryMode.value = 'edit'
|
||||
await loadEntryForEdit(+recordId.value)
|
||||
} else {
|
||||
// Untuk mode 'add', langsung set ready
|
||||
isDataReady.value = true
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: map data
|
||||
async function loadEntryForEdit(id: number | string) {
|
||||
isLoading.value = true
|
||||
const result = mockData
|
||||
reportData.value = result as ActionReportFormData
|
||||
isLoading.value = false
|
||||
isDataReady.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppActionReportEntry
|
||||
v-if="isDataReady"
|
||||
:isLoading="isLoading"
|
||||
:mode="entryMode"
|
||||
@submit="(val) => console.log(val)"
|
||||
@back="goBack"
|
||||
@error="
|
||||
(err: Error) => {
|
||||
toast({
|
||||
title: 'Terjadi Kesalahan',
|
||||
description: err.message,
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
"
|
||||
:doctors="doctors"
|
||||
:initialValues="reportData"
|
||||
>
|
||||
<template #procedures>
|
||||
<ArrangementProcedurePicker
|
||||
field-name="procedures"
|
||||
title="Tindakan Operatif/Non-Operatif Lain"
|
||||
sub-title="Pilih Prosedur"
|
||||
/>
|
||||
</template>
|
||||
</AppActionReportEntry>
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center justify-center p-8"
|
||||
>
|
||||
<p class="text-muted-foreground">Memuat data...</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,276 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import AppActionReportList from '~/components/app/action-report/list.vue'
|
||||
import AppActionReportListHistory from '~/components/app/action-report/list-history.vue'
|
||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||
import { ButtonAction } from '~/components/pub/my-ui/form'
|
||||
|
||||
// config
|
||||
import { config } from '~/components/app/action-report/list.cfg'
|
||||
|
||||
// types
|
||||
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
|
||||
// Samples
|
||||
import { sampleRows, type ActionReportData } from '~/components/app/action-report/sample'
|
||||
import sampleReport from './sample'
|
||||
|
||||
// helpers
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
encounter: Encounter
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emits = defineEmits<{
|
||||
(e: 'add'): void
|
||||
(e: 'edit', id: number | string): void
|
||||
(e: 'view', id: number | string): void
|
||||
}>()
|
||||
|
||||
// states
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { goToEntry, backToList } = useQueryCRUDMode('mode')
|
||||
const title = ref('')
|
||||
const search = ref('')
|
||||
const dateFrom = ref('')
|
||||
const dateTo = ref('')
|
||||
const isDialogOpen = ref<boolean>(false)
|
||||
const isLoading = ref<boolean>(false)
|
||||
|
||||
// #region mock
|
||||
// Handlers
|
||||
import {
|
||||
recId,
|
||||
recAction,
|
||||
recItem,
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} from '~/handlers/consultation.handler'
|
||||
// #endregion
|
||||
|
||||
// filter + pencarian sederhana (client-side)
|
||||
const filtered = computed(() => {
|
||||
const q = search.value.trim().toLowerCase()
|
||||
return sampleRows.filter((r: ActionReportData) => {
|
||||
if (q) {
|
||||
return r.nama.toLowerCase().includes(q) || r.noRm.toLowerCase().includes(q) || r.dokter.toLowerCase().includes(q)
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
const goEdit = (id: number | string) => {
|
||||
router.replace({
|
||||
path: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
mode: 'entry',
|
||||
'record-id': id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const goView = (id: number | string) => {
|
||||
router.replace({
|
||||
path: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
mode: 'view',
|
||||
'record-id': id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
provide('rec_id', recId)
|
||||
provide('rec_action', recAction)
|
||||
provide('rec_item', recItem)
|
||||
provide('table_data_loader', isLoading)
|
||||
|
||||
async function onGetDetail(id: number | string) {
|
||||
isLoading.value = true
|
||||
const res = sampleReport
|
||||
recItem.value = res
|
||||
console.log(res)
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
// #region watcher
|
||||
watch([recId, recAction], (newVal) => {
|
||||
const [id, action] = newVal
|
||||
|
||||
// Guard: jangan proses jika id = 0 atau action kosong
|
||||
if (!id || !action) return
|
||||
|
||||
switch (action) {
|
||||
case ActionEvents.showDetail:
|
||||
// onGetDetail(recId.value)
|
||||
goView(id)
|
||||
title.value = 'Detail Konsultasi'
|
||||
break
|
||||
case ActionEvents.showEdit:
|
||||
goEdit(id)
|
||||
title.value = 'Edit Konsultasi'
|
||||
break
|
||||
case ActionEvents.showConfirmDelete:
|
||||
isRecordConfirmationOpen.value = true
|
||||
break
|
||||
}
|
||||
|
||||
// Reset KEDUANYA menggunakan nextTick agar tidak trigger watcher lagi
|
||||
nextTick(() => {
|
||||
recId.value = 0
|
||||
recAction.value = ''
|
||||
})
|
||||
})
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mx-auto max-w-full">
|
||||
<div class="border-b p-6">
|
||||
<h1 class="text-2xl font-semibold">Laporan Tindakan</h1>
|
||||
<p class="mt-1 text-sm text-gray-500">Infomasi laporan tindakan pasien</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3 border-b p-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
v-model="search"
|
||||
placeholder="Cari Nama / No.RM"
|
||||
class="w-64 rounded border px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
v-model="dateFrom"
|
||||
type="date"
|
||||
class="rounded border px-3 py-2"
|
||||
/>
|
||||
<span class="text-sm text-gray-500">-</span>
|
||||
<input
|
||||
v-model="dateTo"
|
||||
type="date"
|
||||
class="rounded border px-3 py-2"
|
||||
/>
|
||||
<ButtonAction
|
||||
preset="custom"
|
||||
title="Filter List Laporan Tindakan"
|
||||
label="Filter"
|
||||
icon="i-lucide-filter"
|
||||
@click="
|
||||
() => {
|
||||
isDialogOpen = true
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<ButtonAction
|
||||
preset="custom"
|
||||
title="Riwayat Laporan Tindakan"
|
||||
icon="i-lucide-history"
|
||||
label="Riwayat Laporan Tindakan"
|
||||
@click="
|
||||
() => {
|
||||
isDialogOpen = true
|
||||
}
|
||||
"
|
||||
/>
|
||||
<ButtonAction
|
||||
preset="add"
|
||||
title="Tambah Data Laporan Tindakan"
|
||||
icon="i-lucide-plus"
|
||||
label="Tambah Data"
|
||||
@click="
|
||||
() => {
|
||||
goToEntry()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto p-4">
|
||||
<AppActionReportList
|
||||
:data="filtered"
|
||||
:pagination-meta="{
|
||||
recordCount: 2,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
totalPage: 1,
|
||||
hasPrev: false,
|
||||
hasNext: false,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
v-model:open="isDialogOpen"
|
||||
title="Arsip Riwayat Laporan Tindakan"
|
||||
size="2xl"
|
||||
prevent-outside
|
||||
@update:open="
|
||||
(value: any) => {
|
||||
isDialogOpen = value
|
||||
}
|
||||
"
|
||||
>
|
||||
<AppActionReportListHistory
|
||||
:data="filtered"
|
||||
:pagination-meta="{
|
||||
recordCount: 2,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
totalPage: 1,
|
||||
hasPrev: false,
|
||||
hasNext: false,
|
||||
}"
|
||||
/>
|
||||
</Dialog>
|
||||
|
||||
<RecordConfirmation
|
||||
v-model:open="isRecordConfirmationOpen"
|
||||
action="delete"
|
||||
:record="recItem"
|
||||
@confirm="
|
||||
() =>
|
||||
handleActionRemove(
|
||||
recItem.id,
|
||||
() => {
|
||||
router.go(0)
|
||||
},
|
||||
toast,
|
||||
)
|
||||
"
|
||||
@cancel=""
|
||||
>
|
||||
<template #default="{ record }">
|
||||
{{ console.log(JSON.stringify(record)) }}
|
||||
<div class="space-y-1 text-sm">
|
||||
<p
|
||||
v-for="field in config.delKeyNames"
|
||||
:key="field.key"
|
||||
:v-if="record?.[field.key]"
|
||||
>
|
||||
<span class="font-semibold">{{ field.label }}:</span>
|
||||
{{ record[field.key] }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
</template>
|
||||
@@ -0,0 +1,68 @@
|
||||
export default {
|
||||
operatorTeam: {
|
||||
dpjpId: -1,
|
||||
operatorName: 'Julian Alvarez',
|
||||
assistantOperatorName: 'Arda Guller',
|
||||
instrumentNurseName: 'Kenan Yildiz',
|
||||
surgeryDate: '2025-11-13T14:29:00',
|
||||
actionDiagnosis: 'Sprei gratisnya mana',
|
||||
},
|
||||
procedures: [
|
||||
{
|
||||
id: -1,
|
||||
name: 'Ndase mumet',
|
||||
code: 'CX1',
|
||||
},
|
||||
],
|
||||
operationExecution: {
|
||||
surgeryType: 'khusus',
|
||||
billingCode: 'local',
|
||||
operationSystem: 'cito',
|
||||
surgeryCleanType: 'kotor',
|
||||
surgeryNumber: 'retry',
|
||||
birthPlaceNote: 'out3',
|
||||
personWeight: 100,
|
||||
operationDescription: 'asdsadsa1',
|
||||
birthRemark: 'lahir_hidup',
|
||||
|
||||
operationStartAt: '2025-11-13T14:29:00',
|
||||
operationEndAt: '2025-11-13T17:29:00',
|
||||
|
||||
anesthesiaStartAt: '2025-11-13T11:29:00',
|
||||
anesthesiaEndAt: '2025-11-13T18:29:00',
|
||||
},
|
||||
bloodInput: {
|
||||
type: 'tc',
|
||||
amount: {
|
||||
prc: null,
|
||||
wb: null,
|
||||
ffp: null,
|
||||
tc: 3243324,
|
||||
},
|
||||
},
|
||||
implant: {
|
||||
brand: 'Samsung',
|
||||
name: 'S.Komedi',
|
||||
companionName: 'When ya',
|
||||
},
|
||||
specimen: {
|
||||
destination: 'pa',
|
||||
},
|
||||
tissueNotes: [
|
||||
{
|
||||
note: 'Anjai',
|
||||
},
|
||||
{
|
||||
note: 'Ciee Kaget',
|
||||
},
|
||||
{
|
||||
note: 'Baper',
|
||||
},
|
||||
{
|
||||
note: 'Saltink weeh',
|
||||
},
|
||||
{
|
||||
note: 'Kaburrr',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import mockData from './sample'
|
||||
|
||||
// types
|
||||
import { type ActionReportFormData } from '~/schemas/action-report.schema'
|
||||
import { type Encounter } from '~/models/encounter'
|
||||
|
||||
// Components
|
||||
import AppActionReportPreview from '~/components/app/action-report/preview.vue'
|
||||
import type { HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
// #region Props & Emits
|
||||
const router = useRouter()
|
||||
const { backToList, goToEntry } = useQueryCRUDMode('mode')
|
||||
const { recordId } = useQueryCRUDRecordId('record-id')
|
||||
|
||||
function onEditFromView() {
|
||||
goToEntry({ fromView: true })
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
encounter: Encounter
|
||||
}>()
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
const reportData = ref<ActionReportFormData | null>(null)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: 'Detail Laporan Tindakan',
|
||||
icon: 'i-lucide-stethoscope',
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
onMounted(async () => {
|
||||
reportData.value = mockData as unknown as ActionReportFormData
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
function onEdit() {
|
||||
router.push({
|
||||
name: 'action-report-id-edit',
|
||||
params: { id: 100 },
|
||||
})
|
||||
}
|
||||
function onBack() {}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppActionReportPreview
|
||||
v-if="reportData"
|
||||
:data="reportData"
|
||||
@back="backToList"
|
||||
@edit="onEditFromView"
|
||||
/>
|
||||
</template>
|
||||
Reference in New Issue
Block a user