mbois gak rek?

This commit is contained in:
Khafid Prayoga
2025-12-08 12:19:14 +07:00
parent b5846adb7e
commit 52a6935d07
47 changed files with 3304 additions and 297 deletions
@@ -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>
@@ -1,61 +0,0 @@
<script setup lang="ts">
import type { ExposedForm } from '~/types/form'
import { type AssessmentEducationFormData, AssessmentEducationSchema, encode } from '~/schemas/assessment-education'
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
// Handlers
import {
isReadonly,
isProcessing,
onResetState,
handleActionSave,
handleCancelForm,
} from '~/handlers/assessment-education.handler'
// Helpers
import { toast } from '~/components/pub/ui/toast'
const form = ref<ExposedForm<AssessmentEducationFormData> | null>(null)
async function saveData(values: AssessmentEducationFormData) {
const transform = encode(1, values)
handleActionSave(
transform,
() => {
window.location.reload()
},
() => {},
toast,
)
}
async function cancelData() {}
async function handleActionClick(eventType: string) {
if (eventType === 'submit') {
const formValidate = await form.value?.validate()
console.log(formValidate)
if (!formValidate?.valid) return
saveData(formValidate?.values)
}
if (eventType === 'cancel') {
cancelData()
}
}
</script>
<template>
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Tambah Asesmen Edukasi</div>
<AppAssessmentEducationEntryForm
ref="form"
:is-readonly="isReadonly"
:is-loading="isProcessing"
:schema="AssessmentEducationSchema"
/>
<div class="my-2 flex justify-end py-2">
<Action @click="handleActionClick" />
</div>
</template>
@@ -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,74 @@
<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 AppAssessmentEducationEntry from '~/components/app/assessment-education/entry.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>
<AppAssessmentEducationEntry
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"
/>
<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 AppAssessmentEducationList from '~/components/app/assessment-education/list.vue'
import AppAssessmentEducationListHistory from '~/components/app/assessment-education/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/assessment-education/list.cfg'
// types
import { ActionEvents } from '~/components/pub/my-ui/data/types'
import type { Encounter } from '~/models/encounter'
// Samples
import { sampleRows, type AssessmentEducationData } from '~/components/app/assessment-education/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">Asesmen Kebutuhan Edukasi</h1>
<p class="mt-1 text-sm text-gray-500">Manajemen asesmen kebutuhan edukasi pasien rawat jalan</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 Asesmen"
label="Filter"
icon="i-lucide-filter"
@click="
() => {
isDialogOpen = true
}
"
/>
</div>
<div class="ml-auto flex items-center gap-2">
<ButtonAction
preset="custom"
title="Riwayat"
icon="i-lucide-history"
label="Riwayat"
@click="
() => {
isDialogOpen = true
}
"
/>
<ButtonAction
preset="add"
title="Tambah Asesmen"
icon="i-lucide-plus"
label="Asesmen"
@click="
() => {
goToEntry()
}
"
/>
</div>
</div>
<div class="overflow-x-auto p-4">
<AppAssessmentEducationList
: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 Asesmen Kebutuhan Edukasi"
size="2xl"
prevent-outside
@update:open="
(value: any) => {
isDialogOpen = value
}
"
>
<AppAssessmentEducationListHistory
: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>