impl: form entry with source of add,edit

feat(treatment-report): add history list component and dialog integration

- Create new list-history.vue component for displaying treatment report history
- Add configuration file for history list data table
- Integrate history list into main treatment report view with dialog
- Replace simple filter button with action buttons including history view

feat(treatment-report): implement crud navigation and form actions

- Add new entry component for treatment report with list and form views
- Implement back navigation using useQueryCRUDMode
- Replace form submit button with action footer component
- Update dropdown action component for detail view

refactor(treatment-report): restructure form components and update mode handling

- Rename 'add.vue' to 'form.vue' for better clarity
- Update form mode types from 'create|update|view' to 'add|edit|view'
- Implement proper reactive state handling for form modes
- Add new form component with loading states and mock data
- Enhance list component with action handlers and navigation

fix default calculated hours
This commit is contained in:
Khafid Prayoga
2025-12-01 19:50:43 +07:00
parent 1b4d3af909
commit be1b86141f
10 changed files with 442 additions and 18 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ import CpLabOrder from '~/components/content/cp-lab-order/main.vue'
import Radiology from '~/components/content/radiology-order/main.vue'
import Consultation from '~/components/content/consultation/list.vue'
import Cprj from '~/components/content/cprj/entry.vue'
import TreatmentReport from '~/components/content/treatment-report/list.vue'
import TreatmentReport from '~/components/content/treatment-report/entry.vue'
import DocUploadList from '~/components/content/document-upload/list.vue'
import GeneralConsentList from '~/components/content/general-consent/entry.vue'
import ResumeList from '~/components/content/resume/list.vue'
@@ -15,8 +15,9 @@ interface Props {
}
const props = defineProps<Props>()
const detail = ref<TreatmentReportFormData | null>(null)
const { backToList } = useQueryCRUDMode('mode')
const detail = ref<TreatmentReportFormData | null>({} as unknown as TreatmentReportFormData)
const doctors = ref<Doctor[]>([])
// TODO: dummy data
@@ -36,7 +37,9 @@ onMounted(() => {
<AppTreatmentReportEntry
v-if="detail"
:isLoading="false"
:mode="'edit'"
@submit="(val) => console.log(val)"
@back="backToList"
@error="
(err: Error) => {
toast({
@@ -47,7 +50,7 @@ onMounted(() => {
}
"
:doctors="doctors"
:initialValues="detail as unknown as TreatmentReportFormData"
:initialValues="detail"
>
<template #procedures>
<ArrangementProcedurePicker
@@ -0,0 +1,31 @@
<script setup lang="ts">
import List from './list.vue'
import Form from './form.vue'
// Models
import type { Encounter } from '~/models/encounter'
// Props
interface Props {
encounter: Encounter
}
const props = defineProps<Props>()
const { mode, goToEntry, backToList } = useQueryCRUDMode('mode')
</script>
<template>
<div>
<List
v-if="mode === 'list'"
:encounter="props.encounter"
@add="goToEntry"
@edit="goToEntry"
/>
<Form
v-else
@back="backToList"
/>
</div>
</template>
@@ -1,4 +1,6 @@
<script setup lang="ts">
import mockData from './sample'
// type
import { genDoctor, type Doctor } from '~/models/doctor'
import type { TreatmentReportFormData } from '~/schemas/treatment-report.schema'
@@ -8,18 +10,49 @@ import { toast } from '~/components/pub/ui/toast'
import AppTreatmentReportEntry from '~/components/app/treatment-report/entry-form.vue'
import ArrangementProcedurePicker from '~/components/app/therapy-protocol/picker-dialog/arrangement-procedure/procedure-picker.vue'
// states
const route = useRoute()
const { mode, backToList } = useQueryCRUDMode('mode')
const { recordId } = useQueryCRUDRecordId('record-id')
const reportData = ref<TreatmentReportFormData>({} as unknown as TreatmentReportFormData)
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 TreatmentReportFormData
isLoading.value = false
isDataReady.value = true
}
</script>
<template>
<AppTreatmentReportEntry
v-if="isDataReady"
:isLoading="isLoading"
:mode="entryMode"
@submit="(val) => console.log(val)"
@back="backToList"
@error="
(err: Error) => {
toast({
@@ -30,6 +63,7 @@ const isLoading = ref<boolean>(false)
}
"
:doctors="doctors"
:initialValues="reportData"
>
<template #procedures>
<ArrangementProcedurePicker
@@ -39,4 +73,10 @@ const isLoading = ref<boolean>(false)
/>
</template>
</AppTreatmentReportEntry>
<div
v-else
class="flex items-center justify-center p-8"
>
<p class="text-muted-foreground">Memuat data...</p>
</div>
</template>
@@ -1,15 +1,58 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
// Components
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import AppTreatmentReportList from '~/components/app/treatment-report/list.vue'
import AppTreatmentReportListHistory from '~/components/app/treatment-report/list-history.vue'
import { ButtonAction } from '~/components/pub/my-ui/form'
// types
import { ActionEvents } from '~/components/pub/my-ui/data/types'
// Samples
import { sampleRows, type TreatmentReportData } from '~/components/app/treatment-report/sample'
import sampleReport from './sample'
// Models
import type { Encounter } from '~/models/encounter'
// Props
interface Props {
encounter: Encounter
}
const props = defineProps<Props>()
const emits = defineEmits<{
(e: 'add'): void
(e: 'edit', 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(() => {
@@ -21,6 +64,50 @@ const filtered = computed(() => {
return true
})
})
const goEdit = (id: number | string) => {
router.replace({
path: route.path,
query: {
...route.query,
mode: 'entry',
'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], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
onGetDetail(recId.value)
title.value = 'Detail Konsultasi'
break
case ActionEvents.showEdit:
goEdit(recId.value)
// reset
recId.value = 0
title.value = 'Edit Konsultasi'
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
break
}
})
// #endregion
</script>
<template>
@@ -51,9 +138,43 @@ const filtered = computed(() => {
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>
<button class="ml-auto rounded bg-orange-500 px-3 py-2 text-white hover:bg-orange-600">Filter</button>
<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">
@@ -70,4 +191,28 @@ const filtered = computed(() => {
/>
</div>
</div>
<Dialog
v-model:open="isDialogOpen"
title="Arsip Riwayat Laporan Tindakan"
size="2xl"
prevent-outside
@update:open="
(value: any) => {
isDialogOpen = value
}
"
>
<AppTreatmentReportListHistory
:data="filtered"
:pagination-meta="{
recordCount: 2,
page: 1,
pageSize: 10,
totalPage: 1,
hasPrev: false,
hasNext: false,
}"
/>
</Dialog>
</template>