mbois gak rek?
This commit is contained in:
@@ -0,0 +1,468 @@
|
||||
<script setup lang="ts">
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { type Duration, intervalToDuration } from 'date-fns'
|
||||
|
||||
// schema
|
||||
import { type ActionReportFormData, ActionReportSchema } from '~/schemas/action-report.schema'
|
||||
|
||||
// type
|
||||
import type { Doctor } from '~/models/doctor'
|
||||
import { type ClickType as ActionClickType } from '~/components/pub/my-ui/nav-footer/index'
|
||||
|
||||
// components
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
||||
import { ArrayMessage } from '~/components/pub/ui/form'
|
||||
import ActionForm from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
|
||||
// form field components
|
||||
import {
|
||||
FillNotes,
|
||||
RadioBloods,
|
||||
SelectBilling,
|
||||
SelectBirthPlace,
|
||||
SelectBirthType,
|
||||
SelectOperationSystem,
|
||||
SelectOperationType,
|
||||
SelectSpecimen,
|
||||
SelectSurgeryCounter,
|
||||
SelectSurgeryType,
|
||||
} from './fields'
|
||||
import { ButtonAction, Fragment, InputBase, TextAreaInput } from '~/components/pub/my-ui/form/'
|
||||
import { SelectDoctor } from '~/components/app/doctor/fields'
|
||||
// Helpers
|
||||
|
||||
// #region Props & Emits
|
||||
interface FormData extends ActionReportFormData {
|
||||
_operationDuration: string
|
||||
_anesthesiaDuration: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
mode: 'add' | 'edit' | 'view'
|
||||
initialValues?: Partial<FormData>
|
||||
|
||||
// form related
|
||||
doctors: Doctor[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<{
|
||||
(e: 'submit', payload: FormData): void
|
||||
(e: 'back'): void
|
||||
(e: 'error', errors: Error): void
|
||||
}>()
|
||||
|
||||
const tissueNotesLimit = 5
|
||||
const mode = toRef(props, 'mode')
|
||||
const isLoading = toRef(props, 'isLoading')
|
||||
|
||||
const isReadonly = computed(() => {
|
||||
if (isLoading.value === true) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (mode.value === 'view') {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
const formSchema = toTypedSchema(ActionReportSchema)
|
||||
|
||||
const { errors, handleSubmit, values, meta, resetForm, setFieldValue, setValues, validate } = useForm<FormData>({
|
||||
name: 'encounterActionReportForm',
|
||||
validationSchema: formSchema,
|
||||
initialValues: props.initialValues ? props.initialValues : {},
|
||||
validateOnMount: false,
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
resetForm,
|
||||
setValues,
|
||||
values,
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
const onFormActionClicked = (action: ActionClickType) => {
|
||||
if (action === 'back') {
|
||||
emit('back')
|
||||
return
|
||||
}
|
||||
if (action === 'submit') {
|
||||
onSubmit()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const onSubmit = handleSubmit(
|
||||
(values) => {
|
||||
console.log(JSON.stringify(values))
|
||||
emit('submit', values)
|
||||
},
|
||||
(errors) => {
|
||||
console.error(errors)
|
||||
emit('error', new Error('Silahkan lengkapi form terlebih dahulu'))
|
||||
},
|
||||
)
|
||||
// #endregion
|
||||
|
||||
// #region Watcher
|
||||
watch(
|
||||
() => [values.operationExecution.operationStartAt, values.operationExecution.operationEndAt],
|
||||
([start, end]) => {
|
||||
if (!start || !end) return
|
||||
const pStart = new Date(start)
|
||||
const pEnd = new Date(end)
|
||||
|
||||
const formatTime = (r: Duration) =>
|
||||
[r.hours && `${r.hours} jam`, r.minutes && `${r.minutes} menit`, r.seconds && `${r.seconds} detik`]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const res = intervalToDuration({
|
||||
start: pStart,
|
||||
end: pEnd,
|
||||
})
|
||||
|
||||
setFieldValue('_operationDuration', formatTime(res))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => [values.operationExecution.anesthesiaStartAt, values.operationExecution.anesthesiaEndAt],
|
||||
([start, end]) => {
|
||||
if (!start || !end) return
|
||||
const pStart = new Date(start)
|
||||
const pEnd = new Date(end)
|
||||
|
||||
const formatTime = (r: Duration) =>
|
||||
[r.hours && `${r.hours} jam`, r.minutes && `${r.minutes} menit`, r.seconds && `${r.seconds} detik`]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const res = intervalToDuration({
|
||||
start: pStart,
|
||||
end: pEnd,
|
||||
})
|
||||
|
||||
setFieldValue('_anesthesiaDuration', formatTime(res))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="onSubmit">
|
||||
<Fragment
|
||||
v-slot="{ section }"
|
||||
title="Tim Pelaksana Tindakan"
|
||||
>
|
||||
<p class="text-lg font-semibold">{{ section }}</p>
|
||||
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<SelectDoctor
|
||||
fieldName="operatorTeam.dpjpId"
|
||||
label="Dokter Pemeriksa"
|
||||
placeholder="Pilih dokter"
|
||||
:doctors="doctors"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.operatorName"
|
||||
label="Operator"
|
||||
placeholder="Masukkan operator"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.assistantOperatorName"
|
||||
label="Asisten Operator"
|
||||
placeholder="Masukkan asisten operator"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.instrumentNurseName"
|
||||
label="Instrumentir"
|
||||
placeholder="Masukkan instrumentir"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.surgeryDate"
|
||||
label="Tanggal Pembedahan"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
placeholder=""
|
||||
/>
|
||||
</DE.Block>
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<TextAreaInput
|
||||
field-name="operatorTeam.actionDiagnosis"
|
||||
label="Diagnosa Tindakan"
|
||||
placeholder="Masukkan diagnosa tindakan"
|
||||
:col-span="2"
|
||||
:rows="5"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorTeam.postSurgeryNurseId"
|
||||
label="Perawat Pasca Bedah"
|
||||
placeholder="Masukkan perawat pasca bedah"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
</Fragment>
|
||||
|
||||
<Separator class="my-4" />
|
||||
|
||||
<Fragment
|
||||
v-slot="{ section }"
|
||||
title="Tindakan Operatif/Non Operatif Lain"
|
||||
>
|
||||
<!-- <p class="text-lg font-semibold">{{ section }}</p> -->
|
||||
<DE.Block
|
||||
:col-count="2"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<DE.Cell>
|
||||
<slot name="procedures" />
|
||||
<ArrayMessage
|
||||
class="mt-1"
|
||||
v-if="meta.touched"
|
||||
name="procedures"
|
||||
/>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</Fragment>
|
||||
|
||||
<Separator class="my-4" />
|
||||
|
||||
<Fragment
|
||||
v-slot="{ section }"
|
||||
title="Data Pelaksanaan Operasi"
|
||||
>
|
||||
<p class="text-lg font-semibold">{{ section }}</p>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<SelectSurgeryType
|
||||
field-name="operationExecution.surgeryType"
|
||||
label="Jenis Operasi"
|
||||
placeholder="Pilih jenis operasi"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectBilling
|
||||
field-name="operationExecution.billingCode"
|
||||
label="Kode Billing"
|
||||
placeholder="Pilih kode billing"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectOperationSystem
|
||||
field-name="operationExecution.operationSystem"
|
||||
label="Sistem Operasi"
|
||||
placeholder="Pilih sistem operasi"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
field-name="operationExecution.operationStartAt"
|
||||
label="Operasi Mulai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.operationEndAt"
|
||||
label="Operasi Selesai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="_operationDuration"
|
||||
label="Lama Operasi"
|
||||
placeholder="-"
|
||||
is-disabled
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
field-name="operationExecution.anesthesiaStartAt"
|
||||
label="Pembiusan Mulai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.anesthesiaEndAt"
|
||||
label="Pembiusan Selesai"
|
||||
placeholder="Pilih Tanggal"
|
||||
input-type="datetime-local"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="_anesthesiaDuration"
|
||||
label="Lama Pembiusan"
|
||||
placeholder="-"
|
||||
is-disabled
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<SelectOperationType
|
||||
field-name="operationExecution.surgeryCleanType"
|
||||
label="Jenis Pembedahan"
|
||||
placeholder="Pilih jenis pembedahan"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectSurgeryCounter
|
||||
field-name="operationExecution.surgeryNumber"
|
||||
label="Operasi Ke"
|
||||
placeholder="Pilih"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectBirthType
|
||||
field-name="operationExecution.birthRemark"
|
||||
label="Keterangan Lahir"
|
||||
placeholder="Pilih"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectBirthPlace
|
||||
field-name="operationExecution.birthPlaceNote"
|
||||
label="Ket. Tempat Lahir"
|
||||
placeholder="Pilih keterangan tempat lahir"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="operationExecution.personWeight"
|
||||
label="Berat Badan"
|
||||
placeholder="Masukkan berat badan"
|
||||
numeric-only
|
||||
suffix-msg="Gram"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.birthCondition"
|
||||
label="Ket. Saat Lahir"
|
||||
placeholder="Tambah catatan"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<TextAreaInput
|
||||
field-name="operationExecution.operationDescription"
|
||||
label="Uraian Operasi"
|
||||
placeholder="Masukkan uraian"
|
||||
:rows="3"
|
||||
:col-span="2"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operationExecution.bleedingAmountCc"
|
||||
label="Jumlah Pendarahan"
|
||||
placeholder="Masukkan jumlah pendarahan"
|
||||
suffix-msg="CC"
|
||||
numeric-only
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<RadioBloods
|
||||
field-name="bloodInput"
|
||||
label="Jenis & Jumlah Darah Masuk"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
field-name="implant.brand"
|
||||
label="Merk"
|
||||
placeholder="Masukkan merk"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="implant.name"
|
||||
label="Nama Implant"
|
||||
placeholder="Masukkan nama implant"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="implant.sticker"
|
||||
label="Sticker/Nomer Register Implant"
|
||||
placeholder="Masukkan sticker/nomor register implant"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
|
||||
<InputBase
|
||||
field-name="implant.companionName"
|
||||
label="Nama Pendamping Implant"
|
||||
placeholder="Masukkan nama pendamping implant"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<SelectSpecimen
|
||||
field-name="specimen.destination"
|
||||
label="Specimen/Jaringan dikirim ke"
|
||||
placeholder="Pilih"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Block>
|
||||
<FillNotes
|
||||
title="Keterangan Jaringan"
|
||||
:limit="tissueNotesLimit"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</Fragment>
|
||||
<div class="mt-4 flex justify-end">
|
||||
<ActionForm @click="onFormActionClicked" />
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
@@ -0,0 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
// components
|
||||
import { FieldArray } from 'vee-validate'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
// form field components
|
||||
import { ButtonAction, InputBase } from '~/components/pub/my-ui/form'
|
||||
|
||||
interface Props {
|
||||
isDisabled: boolean
|
||||
limit: number
|
||||
title: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const isReadonly = computed(() => props.isDisabled)
|
||||
</script>
|
||||
<template>
|
||||
<FieldArray
|
||||
v-slot="{ fields, push, remove }"
|
||||
name="tissueNotes"
|
||||
>
|
||||
<template v-if="fields.length === 0">
|
||||
{{ push({ note: '' }) }}
|
||||
</template>
|
||||
<div class="space-y-4">
|
||||
<DE.Block
|
||||
v-for="(field, idx) in fields"
|
||||
:key="field.key"
|
||||
:col-count="3"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<InputBase
|
||||
:label="idx === 0 ? 'Keterangan Jaringan' : undefined"
|
||||
:field-name="`tissueNotes[${idx}].note`"
|
||||
placeholder="Masukkan catatan"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<DE.Cell class="flex items-start justify-start">
|
||||
<DE.Field :class="idx === 0 ? 'mt-[30px]' : 'mt-0'">
|
||||
<ButtonAction
|
||||
v-if="idx !== 0"
|
||||
:disabled="isReadonly"
|
||||
preset="delete"
|
||||
:title="`Hapus Kontak ${idx + 1}`"
|
||||
icon-only
|
||||
@click="remove(idx)"
|
||||
/>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</div>
|
||||
|
||||
<ButtonAction
|
||||
preset="add"
|
||||
label="Tambah Catatan"
|
||||
title="Tambah Catatan Keterangan Jaringan"
|
||||
:disabled="fields.length >= limit || isReadonly"
|
||||
:full-width-mobile="true"
|
||||
class="mt-4"
|
||||
@click="push({ name: '', dose: '', unit: '' })"
|
||||
/>
|
||||
</FieldArray>
|
||||
</template>
|
||||
@@ -0,0 +1,10 @@
|
||||
export { default as FillNotes } from './fill-notes.vue'
|
||||
export { default as RadioBloods } from './radio-bloods.vue'
|
||||
export { default as SelectBilling } from './select-billing.vue'
|
||||
export { default as SelectBirthPlace } from './select-birth-place.vue'
|
||||
export { default as SelectBirthType } from './select-birth-type.vue'
|
||||
export { default as SelectOperationSystem } from './select-operation-system.vue'
|
||||
export { default as SelectOperationType } from './select-operation-type.vue'
|
||||
export { default as SelectSpecimen } from './select-specimen.vue'
|
||||
export { default as SelectSurgeryCounter } from './select-surgery-counter.vue'
|
||||
export { default as SelectSurgeryType } from './select-surgery-type.vue'
|
||||
@@ -0,0 +1,101 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { RadioGroup, RadioGroupItem } from '~/components/pub/ui/radio-group'
|
||||
import { Label as RadioLabel } from '~/components/pub/ui/label'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
class?: string
|
||||
radioGroupClass?: string
|
||||
radioItemClass?: string
|
||||
labelClass?: string
|
||||
isDisabled?: boolean
|
||||
}>()
|
||||
|
||||
const { class: containerClass, radioGroupClass, radioItemClass, labelClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'PRC', value: 'prc' },
|
||||
{ label: 'WB', value: 'wb' },
|
||||
{ label: 'FFP', value: 'ffp' },
|
||||
{ label: 'TC', value: 'tc' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell
|
||||
:class="cn('radio-group-field', containerClass)"
|
||||
:col-span="2"
|
||||
>
|
||||
<DE.Label :label-for="fieldName">
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field :id="fieldName">
|
||||
<FormField
|
||||
v-slot="{ componentField: radioField }"
|
||||
:name="`${fieldName}.type`"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
:model-value="radioField.modelValue"
|
||||
@update:model-value="radioField.onChange"
|
||||
:class="cn('grid grid-cols-1 items-start gap-4 sm:grid-cols-2 ', radioGroupClass)"
|
||||
>
|
||||
<div
|
||||
v-for="(option, index) in opts"
|
||||
:key="option.value"
|
||||
:class="cn('grid grid-cols-1 items-start gap-3 sm:grid-cols-[auto,1fr]', radioItemClass)"
|
||||
>
|
||||
<div class="flex min-w-fit items-center gap-2">
|
||||
<RadioGroupItem
|
||||
:id="`type-${index}`"
|
||||
:value="option.value"
|
||||
:disabled="isDisabled"
|
||||
/>
|
||||
<RadioLabel :for="`type-${index}`">
|
||||
{{ option.label }}
|
||||
</RadioLabel>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
v-slot="{ componentField: amountField }"
|
||||
:name="`${fieldName}.amount.${option.value}`"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div class="relative w-[140px]">
|
||||
<Input
|
||||
v-bind="amountField"
|
||||
placeholder="00"
|
||||
class="pr-10"
|
||||
:disabled="radioField.modelValue !== option.value || isDisabled"
|
||||
@input="
|
||||
(e: InputEvent) => {
|
||||
const target = e.target as HTMLInputElement
|
||||
const v = target.value.replace(/\D/g, '')
|
||||
amountField.onChange(v)
|
||||
}
|
||||
"
|
||||
/>
|
||||
<span
|
||||
class="absolute inset-y-0 end-0 flex items-center justify-center px-2 text-sm text-muted-foreground"
|
||||
>
|
||||
CC
|
||||
</span>
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'General', value: 'general' },
|
||||
{ label: 'Regional', value: 'regional' },
|
||||
{ label: 'Local', value: 'local' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,71 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'RSSA', value: 'rssa' },
|
||||
{ label: 'Bidan Luar', value: 'out1' },
|
||||
{ label: 'Dokter Luar', value: 'out2' },
|
||||
{ label: 'Dukun Bayi', value: 'out3' },
|
||||
{ label: 'Puskesmas', value: 'out4' },
|
||||
{ label: 'Paramedis Luar', value: 'out5' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Lahir Hidup', value: 'lahir_hidup' },
|
||||
{ label: 'Lahir Mati', value: 'lahir_mati' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Cito', value: 'cito' },
|
||||
{ label: 'Urgent', value: 'urgent' },
|
||||
{ label: 'Efektif', value: 'efektif' },
|
||||
{ label: 'Khusus', value: 'khusus' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Bersih', value: 'bersih' },
|
||||
{ label: 'Bersih Terkontaminasi', value: 'bersih_terkontaminasi' },
|
||||
{ label: 'Terkontaminasi Kotor', value: 'terkontaminasi' },
|
||||
{ label: 'Kotor', value: 'kotor' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'PA', value: 'pa' },
|
||||
{ label: 'Mikrobiologi', value: 'microbiology' },
|
||||
{ label: 'Laborat', value: 'laboratory' },
|
||||
{ label: 'Tidak Perlu', value: 'none' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: '1 (Satu)', value: 'first' },
|
||||
{ label: 'Ulangan', value: 'retry' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
isDisabled?: boolean
|
||||
isRequired?: boolean
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
}>()
|
||||
|
||||
const { errors, class: containerClass, selectClass, fieldGroupClass } = props
|
||||
|
||||
const opts = [
|
||||
{ label: 'Kecil', value: 'kecil' },
|
||||
{ label: 'Sedang', value: 'sedang' },
|
||||
{ label: 'Besar', value: 'besar' },
|
||||
{ label: 'Khusus', value: 'khusus' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||
<DE.Label
|
||||
label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
:id="fieldName"
|
||||
:is-disabled="isDisabled"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:preserve-order="false"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
|
||||
selectClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,88 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-d.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL LAPORAN' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'OPERATOR' },
|
||||
{ label: 'TANGGAL PEMBEDAHAN' },
|
||||
{ label: 'JENIS OPERASI' },
|
||||
{ label: 'KODE BILLING' },
|
||||
{ label: 'SISTEM OPERASI' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'billing', 'system', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).operationAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return 'dr. Dewi Arum Sawitri, Sp.An'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return 'Besar'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return 'dr. Irwansyah Kurniawan Sp.Bo'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
|
||||
// Types
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
|
||||
// Configs
|
||||
import { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,89 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 120 },
|
||||
{ width: 50 },
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL LAPORAN' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'OPERATOR' },
|
||||
{ label: 'TANGGAL PEMBEDAHAN' },
|
||||
{ label: 'JENIS OPERASI' },
|
||||
{ label: 'KODE BILLING' },
|
||||
{ label: 'SISTEM OPERASI' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'billing', 'system', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).operationAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return 'dr. Dewi Arum Sawitri, Sp.An'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return 'Besar'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return 'dr. Irwansyah Kurniawan Sp.Bo'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
|
||||
// Types
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
|
||||
// Configs
|
||||
import { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,166 @@
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
// type
|
||||
import { type ProcedureSrc } from '~/models/procedure-src'
|
||||
import { type ActionReportFormData } from '~/schemas/action-report.schema'
|
||||
|
||||
// componenets
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '~/components/pub/ui/accordion'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import DetailRow from '~/components/pub/my-ui/form/view/detail-row.vue'
|
||||
import ArrangementProcedurePicker from '~/components/app/therapy-protocol/picker-dialog/arrangement-procedure/procedure-picker.vue'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = defineProps<{
|
||||
data: ActionReportFormData
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'back'): void
|
||||
(e: 'edit'): void
|
||||
}>()
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
const { operatorTeam, procedures, operationExecution, bloodInput, implant, specimen, tissueNotes = [] } = props.data
|
||||
|
||||
const procedureSampleData = procedures as unknown as ProcedureSrc[]
|
||||
// #region Lifecycle Hooks
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
|
||||
// #endregion region
|
||||
// #region Utilities & event handlers
|
||||
function onNavigate(type: string) {
|
||||
if (type == 'back') emit('back')
|
||||
if (type == 'edit') emit('edit')
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DetailRow label="Tanggal Laporan">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
|
||||
<Accordion
|
||||
type="multiple"
|
||||
class="w-full"
|
||||
collapsible
|
||||
:default-value="['section-1', 'section-2', 'section-3']"
|
||||
>
|
||||
<AccordionItem value="section-1">
|
||||
<AccordionTrigger>Tim Pelaksanaan Tindakan</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DE.Block
|
||||
:cell-flex="false"
|
||||
:col-count="2"
|
||||
>
|
||||
<DE.Cell>
|
||||
<DetailRow label="DPJP">dr. Marcell Galliard Sp.Gr</DetailRow>
|
||||
<DetailRow label="Operator">Sumitro</DetailRow>
|
||||
<DetailRow label="Asisten Operator">Alexis Lewis Carol</DetailRow>
|
||||
<DetailRow label="Instrumentir">Mikel Arteta</DetailRow>
|
||||
<DetailRow label="Tanggal Pembedahan">
|
||||
{{ format(new Date(), 'd MMMM yyyy', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Diagnosa Tindakan">{{ operatorTeam?.actionDiagnosis || '-' }}</DetailRow>
|
||||
<DetailRow label="Perawat Pasca Bedah">Cak Armuji</DetailRow>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="section-2">
|
||||
<AccordionTrigger>Tindakan Operatif / Non Operatif Lain</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DE.Block
|
||||
:cell-flex="false"
|
||||
:col-count="2"
|
||||
>
|
||||
<DE.Cell>
|
||||
<ArrangementProcedurePicker
|
||||
field-name="procedures"
|
||||
title="List Prosedur"
|
||||
sub-title="Pilih Prosedur"
|
||||
:mode="'preview'"
|
||||
:sample-items="procedureSampleData"
|
||||
/>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="section-3">
|
||||
<AccordionTrigger>Data Pelaksanaan Tindakan</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<DE.Block
|
||||
:cell-flex="false"
|
||||
:col-count="2"
|
||||
>
|
||||
<DE.Cell>
|
||||
<DetailRow label="Jenis Operasi">dr. Marcell Galliard Sp.Gr</DetailRow>
|
||||
<DetailRow label="Kode Billing">GCASH1128190</DetailRow>
|
||||
<DetailRow label="Sistem Operasi">Alexis Lewis Carol</DetailRow>
|
||||
<DetailRow label="Operasi Mulai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Operasi Selesai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Lama Operasi">5 menit</DetailRow>
|
||||
<DetailRow label="Pembiusan Mulai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Pembiusan Selesai">
|
||||
{{ format(new Date(), 'd MMMM yyyy, HH:mm', { locale: id }) }}
|
||||
</DetailRow>
|
||||
<DetailRow label="Lama Pembiusan">5 menit</DetailRow>
|
||||
|
||||
<DetailRow label="PRC">300 CC</DetailRow>
|
||||
<DetailRow label="FPP">-</DetailRow>
|
||||
<DetailRow label="WB">-</DetailRow>
|
||||
<DetailRow label="TC">-</DetailRow>
|
||||
<DetailRow label="Merk">-</DetailRow>
|
||||
<DetailRow label="Nama Implant">-</DetailRow>
|
||||
<DetailRow label="Sticker / Nomor Register Implant">-</DetailRow>
|
||||
<DetailRow label="Nama Pendamping Implant">-</DetailRow>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DetailRow label="Jenis Pembedahan">Bersih</DetailRow>
|
||||
<DetailRow label="Operasi ke">1 (Satu)</DetailRow>
|
||||
<DetailRow label="Keterangan Lahir">Lahir Hidup</DetailRow>
|
||||
<DetailRow label="Ket. Tempat Lahir">RSSA</DetailRow>
|
||||
<DetailRow label="Berat Badan">18 gram</DetailRow>
|
||||
<DetailRow label="Ket. Saat Lahir">Normal dan sehat</DetailRow>
|
||||
<DetailRow label="Uraian Operasi">-</DetailRow>
|
||||
<DetailRow label="Jumlah Pendarahan">300 CC</DetailRow>
|
||||
<DetailRow label="Specimen / Jaringan dikirim ke">PA</DetailRow>
|
||||
<DetailRow label="Keterangan Jaringan">
|
||||
<ul
|
||||
class="list-disc space-y-1 pl-5 text-sm"
|
||||
v-if="tissueNotes.length > 0"
|
||||
v-for="item in tissueNotes"
|
||||
>
|
||||
<li>{{ item.note }}</li>
|
||||
</ul>
|
||||
<span v-else>-</span>
|
||||
</DetailRow>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
<div class="my-2 flex justify-end py-2">
|
||||
<PubMyUiNavFooterBaEd @click="onNavigate" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,54 @@
|
||||
import { addWeeks, formatISO } from 'date-fns'
|
||||
|
||||
export type ActionReportData = {
|
||||
id: number
|
||||
reportAt: string
|
||||
operationAt: string
|
||||
noRm: string
|
||||
noBill: string
|
||||
nama: string
|
||||
jk: string
|
||||
alamat: string
|
||||
klinik: string
|
||||
dokter: string
|
||||
caraBayar: string
|
||||
rujukan: string
|
||||
ketRujukan: string
|
||||
asal: string
|
||||
}
|
||||
|
||||
export const sampleRows: ActionReportData[] = [
|
||||
{
|
||||
id: 1,
|
||||
reportAt: formatISO(addWeeks(new Date(), -1)),
|
||||
operationAt: formatISO(addWeeks(new Date(), 1)),
|
||||
noRm: 'RM23311224',
|
||||
noBill: '-',
|
||||
nama: 'Ahmad Baidowi',
|
||||
jk: 'L',
|
||||
alamat: 'Jl Jaksa Agung S. No. 9',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
reportAt: new Date().toISOString(),
|
||||
operationAt: formatISO(addWeeks(new Date(), 2)),
|
||||
noRm: 'RM23455667',
|
||||
noBill: '-',
|
||||
nama: 'Abraham Sulaiman',
|
||||
jk: 'L',
|
||||
alamat: 'Purwantoro, Blimbing',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
// tambahkan lebih banyak baris contoh jika perlu
|
||||
]
|
||||
+37
-31
@@ -1,13 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import { FieldArray, useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { FieldArray } from 'vee-validate'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
|
||||
// types
|
||||
// import { type AssessmentEducationFormData, AssessmentEducationSchema, encode } from '~/schemas/assessment-education'
|
||||
|
||||
// componenets
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
||||
import { BaseSelect, CheckboxGeneral, CheckboxSpecial } from './fields'
|
||||
import { ButtonAction, TextAreaInput } from '~/components/pub/my-ui/form'
|
||||
import ActionForm from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
|
||||
// constant
|
||||
import {
|
||||
@@ -19,12 +23,13 @@ import {
|
||||
translatorSrcCode,
|
||||
} from '~/lib/clinical.constants'
|
||||
|
||||
interface FormData {}
|
||||
interface Props {
|
||||
schema: any
|
||||
initialValues?: any
|
||||
noteLimit?: number
|
||||
isLoading?: boolean
|
||||
isReadonly?: boolean
|
||||
noteLimit?: number
|
||||
mode: 'add' | 'edit' | 'view'
|
||||
initialValues?: Partial<FormData>
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -33,37 +38,34 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
noteLimit: 10,
|
||||
})
|
||||
const isDisabled = computed(() => props.isLoading || props.isReadonly)
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
ref="formRef"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="
|
||||
initialValues || {
|
||||
// const formSchema = toTypedSchema(AssessmentEducationSchema)
|
||||
const formSchema = toTypedSchema(z.object({}))
|
||||
const { errors, handleSubmit, values, meta, resetForm, setFieldValue, setValues, validate } = useForm<FormData>({
|
||||
name: 'assessmentEducationForm',
|
||||
validationSchema: formSchema,
|
||||
initialValues: props.initialValues
|
||||
? props.initialValues
|
||||
: {
|
||||
plans: [
|
||||
{
|
||||
id: 1,
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
"
|
||||
>
|
||||
},
|
||||
validateOnMount: false,
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
resetForm,
|
||||
setValues,
|
||||
values,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="">
|
||||
<p class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base">Kebutuhan Edukasi</p>
|
||||
<DE.Block
|
||||
:col-count="4"
|
||||
@@ -205,5 +207,9 @@ defineExpose({
|
||||
</div>
|
||||
</FieldArray>
|
||||
</DE.Block>
|
||||
</Form>
|
||||
<!-- todo -->
|
||||
<div class="mt-4 flex justify-end">
|
||||
<ActionForm @click="() => {}" />
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
@@ -0,0 +1,75 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{ width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 50 }],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'INFORMASI UMUM' },
|
||||
{ label: 'EDUKASI KHUSUS' },
|
||||
{ label: 'RENCANA EDUKASI' },
|
||||
{ label: 'PELAKSANAAN' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
return '1 Rencana Edukasi'
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return '2 Edukasi dipilih'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return '3 Informasi Dipilih'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
|
||||
// Types
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
|
||||
// Configs
|
||||
import { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,75 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{ width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 50 }],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'INFORMASI UMUM' },
|
||||
{ label: 'EDUKASI KHUSUS' },
|
||||
{ label: 'RENCANA EDUKASI' },
|
||||
{ label: 'PELAKSANAAN' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
return '1 Rencana Edukasi'
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return '2 Edukasi dipilih'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return '3 Informasi Dipilih'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
|
||||
// Types
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
|
||||
// Configs
|
||||
import { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,54 @@
|
||||
import { addWeeks, formatISO } from 'date-fns'
|
||||
|
||||
export type ActionReportData = {
|
||||
id: number
|
||||
reportAt: string
|
||||
operationAt: string
|
||||
noRm: string
|
||||
noBill: string
|
||||
nama: string
|
||||
jk: string
|
||||
alamat: string
|
||||
klinik: string
|
||||
dokter: string
|
||||
caraBayar: string
|
||||
rujukan: string
|
||||
ketRujukan: string
|
||||
asal: string
|
||||
}
|
||||
|
||||
export const sampleRows: ActionReportData[] = [
|
||||
{
|
||||
id: 1,
|
||||
reportAt: formatISO(addWeeks(new Date(), -1)),
|
||||
operationAt: formatISO(addWeeks(new Date(), 1)),
|
||||
noRm: 'RM23311224',
|
||||
noBill: '-',
|
||||
nama: 'Ahmad Baidowi',
|
||||
jk: 'L',
|
||||
alamat: 'Jl Jaksa Agung S. No. 9',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
reportAt: new Date().toISOString(),
|
||||
operationAt: formatISO(addWeeks(new Date(), 2)),
|
||||
noRm: 'RM23455667',
|
||||
noBill: '-',
|
||||
nama: 'Abraham Sulaiman',
|
||||
jk: 'L',
|
||||
alamat: 'Purwantoro, Blimbing',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
// tambahkan lebih banyak baris contoh jika perlu
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
export { default as SelectDoctor } from './select-doctor.vue'
|
||||
@@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
// type
|
||||
import type { Doctor } from '~/models/doctor'
|
||||
import { type Person, parseName } from '~/models/person'
|
||||
|
||||
// componenets
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName: string
|
||||
label: string
|
||||
placeholder: string
|
||||
doctors?: Doctor[]
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
isDisabled?: boolean
|
||||
colSpan?: number
|
||||
}>()
|
||||
|
||||
const { class: containerClass, labelClass, colSpan = 1, doctors = [] } = props
|
||||
|
||||
const opts = computed(() => {
|
||||
return doctors.map((doc) => ({
|
||||
value: doc.id,
|
||||
label: parseName(doc.employee.person as Person),
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell
|
||||
:col-span="colSpan"
|
||||
:class="cn('select-field-group', fieldGroupClass, containerClass)"
|
||||
>
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="opts"
|
||||
:placeholder="placeholder"
|
||||
:is-disabled="isDisabled"
|
||||
search-placeholder="Cari dokter..."
|
||||
empty-message="Dokter tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</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,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>
|
||||
@@ -45,16 +45,18 @@ const MicroLabOrderAsync = defineAsyncComponent(() => import('~/components/conte
|
||||
const CprjAsync = defineAsyncComponent(() => import('~/components/content/cprj/entry.vue'))
|
||||
const RadiologyAsync = defineAsyncComponent(() => import('~/components/content/radiology-order/main.vue'))
|
||||
const ConsultationAsync = defineAsyncComponent(() => import('~/components/content/consultation/list.vue'))
|
||||
const DocUploadListAsync = defineAsyncComponent(() => import('~/components/content/document-upload/main.vue'))
|
||||
const DocUploadListAsync = defineAsyncComponent(() => import('~/components/content/document-upload/list.vue'))
|
||||
const GeneralConsentListAsync = defineAsyncComponent(() => import('~/components/content/general-consent/entry.vue'))
|
||||
const ResumeListAsync = defineAsyncComponent(() => import('~/components/content/resume/main.vue'))
|
||||
const ControlLetterListAsync = defineAsyncComponent(() => import('~/components/content/control-letter/main.vue'))
|
||||
const KfrListAsync = defineAsyncComponent(() => import('~/components/content/kfr/main.vue'))
|
||||
const PrbListAsync = defineAsyncComponent(() => import('~/components/content/prb/main.vue'))
|
||||
const SurgeryReportListAsync = defineAsyncComponent(() => import('~/components/content/surgery-report/main.vue'))
|
||||
const VaccineDataListAsync = defineAsyncComponent(() => import('~/components/content/vaccine-data/main.vue'))
|
||||
const ResumeListAsync = defineAsyncComponent(() => import('~/components/content/resume/list.vue'))
|
||||
const ControlLetterListAsync = defineAsyncComponent(() => import('~/components/content/control-letter/list.vue'))
|
||||
const KfrListAsync = defineAsyncComponent(() => import('~/components/content/kfr/list.vue'))
|
||||
const PrbListAsync = defineAsyncComponent(() => import('~/components/content/prb/list.vue'))
|
||||
const SurgeryReportListAsync = defineAsyncComponent(() => import('~/components/content/surgery-report/list.vue'))
|
||||
const VaccineDataListAsync = defineAsyncComponent(() => import('~/components/content/vaccine-data/list.vue'))
|
||||
const InitialNursingStudyAsync = defineAsyncComponent(() => import('~/components/content/initial-nursing/entry.vue'))
|
||||
const SummaryMedicAsync = defineAsyncComponent(() => import('~/components/content/summary-medic/entry.vue'))
|
||||
const AssessmentEducationEntryAsync = defineAsyncComponent(
|
||||
() => import('~/components/content/assessment-education/entry.vue'),
|
||||
)
|
||||
|
||||
const defaultKeys: Record<string, any> = {
|
||||
status: {
|
||||
@@ -63,19 +65,17 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
// NOTE : HIDDEN UNTIL IT IS READY
|
||||
// earlyNurseryAssessment: {
|
||||
// id: 'early-nursery-assessment',
|
||||
// title: 'Pengkajian Awal Keperawatan',
|
||||
// classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
// unit: 'all',
|
||||
// },
|
||||
earlyNurseryAssessment: {
|
||||
id: 'early-nursery-assessment',
|
||||
title: 'Pengkajian Awal Keperawatan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
earlyMedicalAssessment: {
|
||||
id: 'early-medical-assessment',
|
||||
title: 'Pengkajian Awal Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
afterId: 'early-medical-assessment',
|
||||
},
|
||||
earlyMedicalRehabAssessment: {
|
||||
id: 'rehab-medical-assessment',
|
||||
@@ -84,27 +84,20 @@ const defaultKeys: Record<string, any> = {
|
||||
unit: 'rehab',
|
||||
afterId: 'early-medical-assessment',
|
||||
},
|
||||
fkr: {
|
||||
id: 'fkr',
|
||||
title: 'FKR',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
functionAssessment: {
|
||||
id: 'function-assessment',
|
||||
title: 'Asesmen Fungsi',
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'rehab',
|
||||
afterId: 'rehab-medical-assessment',
|
||||
},
|
||||
therapyProtocol: {
|
||||
id: 'therapy-protocol',
|
||||
classCode: ['ambulatory'],
|
||||
title: 'Protokol Terapi',
|
||||
unit: 'rehab',
|
||||
afterId: 'function-assessment',
|
||||
},
|
||||
// NOTE: Replaced by FRK
|
||||
// functionAssessment: {
|
||||
// id: 'function-assessment',
|
||||
// title: 'Asesmen Fungsi',
|
||||
// classCode: ['ambulatory'],
|
||||
// unit: 'rehab',
|
||||
// afterId: 'rehab-medical-assessment',
|
||||
// },
|
||||
// therapyProtocol: {
|
||||
// id: 'therapy-protocol',
|
||||
// classCode: ['ambulatory'],
|
||||
// title: 'Protokol Terapi',
|
||||
// unit: 'rehab',
|
||||
// afterId: 'function-assessment',
|
||||
// },
|
||||
chemotherapyProtocol: {
|
||||
id: 'chemotherapy-protocol',
|
||||
title: 'Protokol Kemoterapi',
|
||||
@@ -227,6 +220,12 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency'],
|
||||
unit: 'all',
|
||||
},
|
||||
kfr: {
|
||||
id: 'kfr',
|
||||
title: 'KFR',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
refBack: {
|
||||
id: 'reference-back',
|
||||
title: 'PRB',
|
||||
@@ -263,12 +262,6 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
summaryMedic: {
|
||||
id: 'summary-medic',
|
||||
title: 'Profil Ringkasan Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
initialNursingStudy: {
|
||||
id: 'initial-nursing-study',
|
||||
title: 'Kajian Awal Keperawatan',
|
||||
@@ -360,9 +353,12 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
}
|
||||
}
|
||||
if (currentKeys?.educationAssessment) {
|
||||
// TODO: add component for education assessment
|
||||
currentKeys.educationAssessment['component'] = null
|
||||
currentKeys.educationAssessment['props'] = { encounter_id: id }
|
||||
currentKeys.educationAssessment['component'] = AssessmentEducationEntryAsync
|
||||
currentKeys.educationAssessment['props'] = {
|
||||
encounter: data?.encounter,
|
||||
type: 'education-assessment',
|
||||
label: currentKeys.educationAssessment['title'],
|
||||
}
|
||||
}
|
||||
if (currentKeys?.generalConsent) {
|
||||
currentKeys.generalConsent['component'] = GeneralConsentListAsync
|
||||
@@ -422,9 +418,9 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
currentKeys.refBack['component'] = PrbListAsync
|
||||
currentKeys.refBack['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
if (currentKeys?.fkr) {
|
||||
currentKeys.fkr['component'] = KfrListAsync
|
||||
currentKeys.fkr['props'] = { encounter: data?.encounter }
|
||||
if (currentKeys?.kfr) {
|
||||
currentKeys.kfr['component'] = KfrListAsync
|
||||
currentKeys.kfr['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
if (currentKeys?.screening) {
|
||||
// TODO: add component for screening
|
||||
@@ -448,10 +444,12 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
currentKeys.priceList['component'] = null
|
||||
currentKeys.priceList['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.summaryMedic) {
|
||||
currentKeys.summaryMedic['component'] = SummaryMedicAsync
|
||||
currentKeys.summaryMedic['props'] = { encounter_id: id }
|
||||
|
||||
if (currentKeys?.initialNursingStudy) {
|
||||
currentKeys.initialNursingStudy['component'] = InitialNursingStudyAsync
|
||||
currentKeys.initialNursingStudy['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
|
||||
if (currentKeys?.initialNursingStudy) {
|
||||
currentKeys.initialNursingStudy['component'] = InitialNursingStudyAsync
|
||||
currentKeys.initialNursingStudy['props'] = { encounter: data?.encounter }
|
||||
@@ -512,12 +510,9 @@ export function mapResponseToEncounter(result: any): any {
|
||||
? result.visitDate
|
||||
: result.registeredAt || result.patient?.registeredAt || null,
|
||||
adm_employee_id: result.adm_employee_id || 0,
|
||||
adm_employee: result.adm_employee || null,
|
||||
responsible_nurse_code: result.responsible_nurse_code || null,
|
||||
responsible_nurse: result.responsible_nurse || null,
|
||||
appointment_doctor_code: result.appointment_doctor_code || null,
|
||||
appointment_doctor_id: result.appointment_doctor_id || null,
|
||||
responsible_doctor_id: result.responsible_doctor_id || null,
|
||||
appointment_doctor: result.appointment_doctor || null,
|
||||
responsible_doctor_code: result.responsible_doctor_id || null,
|
||||
responsible_doctor: result.responsible_doctor || null,
|
||||
refSource_name: result.refSource_name || null,
|
||||
appointment_id: result.appointment_id || null,
|
||||
@@ -539,13 +534,7 @@ export function mapResponseToEncounter(result: any): any {
|
||||
return mapped
|
||||
}
|
||||
|
||||
export function getMenuItems(
|
||||
id: string | number,
|
||||
props: any,
|
||||
user: any,
|
||||
data: EncounterListData,
|
||||
meta: any,
|
||||
) {
|
||||
export function getMenuItems(id: string | number, props: any, user: any, data: EncounterListData, meta: any) {
|
||||
// const normalClassCode = props.classCode === 'ambulatory' ? 'outpatient' : props.classCode
|
||||
const normalClassCode = props.classCode === 'ambulatory' ? 'ambulatory' : props.classCode
|
||||
const currentKeys = injectComponents(id, data, meta)
|
||||
|
||||
+3
-2
@@ -1,4 +1,3 @@
|
||||
|
||||
export interface Base {
|
||||
id: number
|
||||
createdAt: string | null
|
||||
@@ -20,7 +19,9 @@ export interface TreeItem {
|
||||
|
||||
export function genBase(): Base {
|
||||
return {
|
||||
id: 0,
|
||||
// -1 buat mock data
|
||||
// backend harusnya non-negative/ > 0 (untuk auto increment constraint) jadi harusnya aman ya
|
||||
id: -1,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import { type Employee, genEmployee } from "./employee"
|
||||
import type { Unit } from "./unit"
|
||||
import type { Specialist } from "./specialist"
|
||||
import type { Subspecialist } from "./subspecialist"
|
||||
import { type Base, genBase } from './_base'
|
||||
import { type Employee, genEmployee } from './employee'
|
||||
import type { Unit } from './unit'
|
||||
import type { Specialist } from './specialist'
|
||||
import type { Subspecialist } from './subspecialist'
|
||||
|
||||
export interface Doctor extends Base {
|
||||
employee_id: number
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { type Base, genBase } from './_base'
|
||||
|
||||
export type JsonRaw = string
|
||||
|
||||
export interface EduAssessment extends Base {
|
||||
encounter_id: number
|
||||
generalEdus: Record<string, string>
|
||||
specialEdus: Record<string, string>
|
||||
assessments: Record<string, string>
|
||||
plan: Record<string, string>
|
||||
}
|
||||
|
||||
export function genEduAssessment(): EduAssessment {
|
||||
return {
|
||||
...genBase(),
|
||||
encounter_id: 0,
|
||||
generalEdus: {},
|
||||
specialEdus: {},
|
||||
assessments: {},
|
||||
plan: {},
|
||||
}
|
||||
}
|
||||
|
||||
export interface EduAssessmentRaw extends Base {
|
||||
encounter_id: number
|
||||
generalEdus: string
|
||||
specialEdus: string
|
||||
assessments: string
|
||||
plan: string
|
||||
}
|
||||
|
||||
export function genEduAssessmentRaw(): EduAssessmentRaw {
|
||||
return {
|
||||
...genBase(),
|
||||
encounter_id: 0,
|
||||
generalEdus: '',
|
||||
specialEdus: '',
|
||||
assessments: '',
|
||||
plan: '',
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export function genEncounter(): Encounter {
|
||||
patient: genPatient(),
|
||||
registeredAt: '',
|
||||
class_code: '',
|
||||
unit_code: '',
|
||||
unit_code: 0,
|
||||
unit: genUnit(),
|
||||
visitDate: '',
|
||||
adm_employee_id: 0,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import type { Employee } from "./employee"
|
||||
|
||||
export interface Nurse extends Base {
|
||||
employee_id: number
|
||||
employee?: Employee
|
||||
ihs_number?: string
|
||||
unit_id: number
|
||||
infra_id: number
|
||||
|
||||
+14
-5
@@ -1,7 +1,7 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import type { PersonAddress } from "./person-address"
|
||||
import type { PersonContact } from "./person-contact"
|
||||
import type { PersonRelative } from "./person-relative"
|
||||
import { type Base, genBase } from './_base'
|
||||
import type { PersonAddress } from './person-address'
|
||||
import type { PersonContact } from './person-contact'
|
||||
import type { PersonRelative } from './person-relative'
|
||||
import type { Ethnic } from './ethnic'
|
||||
import type { Language } from './language'
|
||||
import type { Regency } from './regency'
|
||||
@@ -43,6 +43,15 @@ export interface Person extends Base {
|
||||
export function genPerson(): Person {
|
||||
return {
|
||||
...genBase(),
|
||||
name: '',
|
||||
frontTitle: '[MOCK] dr. ',
|
||||
name: 'Agus Iwan Setiawan',
|
||||
endTitle: 'Sp.Bo',
|
||||
}
|
||||
}
|
||||
|
||||
export function parseName(person: Person): string {
|
||||
if (!person) return ''
|
||||
const fullName = [person.frontTitle, person.name, person.endTitle].filter(Boolean).join(' ').trim()
|
||||
|
||||
return fullName
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
const isoDateTime = z
|
||||
.string()
|
||||
.min(1, 'Tanggal / waktu wajib diisi')
|
||||
.refine((val) => {
|
||||
const date = new Date(val)
|
||||
return !isNaN(date.getTime())
|
||||
}, 'Format tanggal / waktu tidak valid')
|
||||
|
||||
const positiveInt = z.coerce.number().int().nonnegative()
|
||||
|
||||
const OperatorTeamSchema = z.object({
|
||||
dpjpId: z.coerce
|
||||
.number({
|
||||
invalid_type_error: 'Silahkan pilih dpjp terlebih dahulu',
|
||||
})
|
||||
.int(),
|
||||
operatorName: z.string({
|
||||
required_error: 'Masukkan nama operator',
|
||||
}),
|
||||
assistantOperatorName: z.string({
|
||||
required_error: 'Masukkan nama asisten operator',
|
||||
}),
|
||||
instrumentNurseName: z.string({
|
||||
required_error: 'Masukkan nama instrumentir',
|
||||
}),
|
||||
|
||||
surgeryDate: isoDateTime,
|
||||
actionDiagnosis: z.string(),
|
||||
|
||||
postSurgeryNurseId: z.number().int().optional().nullable(),
|
||||
})
|
||||
|
||||
const ProcedureSchema = z.object({
|
||||
id: z.number().int(),
|
||||
name: z.string().min(1),
|
||||
code: z.string().min(1),
|
||||
})
|
||||
|
||||
const OperationExecutionSchema = z.object({
|
||||
surgeryType: z.enum(['kecil', 'sedang', 'besar', 'khusus'], { required_error: 'Silahkan pilih jenis operasi' }),
|
||||
billingCode: z.string({
|
||||
required_error: 'Silahkan pilih kode billing',
|
||||
}),
|
||||
operationSystem: z.enum(['khusus', 'cito', 'efektif', 'urgent'], { required_error: 'Silahkan pilih sistem operasi' }),
|
||||
|
||||
operationStartAt: isoDateTime,
|
||||
operationEndAt: isoDateTime,
|
||||
|
||||
anesthesiaStartAt: isoDateTime,
|
||||
anesthesiaEndAt: isoDateTime,
|
||||
|
||||
surgeryCleanType: z.enum(['bersih', 'bersih_terkontaminasi', 'terkontaminasi', 'kotor']).optional(),
|
||||
surgeryNumber: z.enum(['first', 'retry']).optional(),
|
||||
|
||||
birthPlaceNote: z.string().optional(),
|
||||
personWeight: positiveInt.optional(),
|
||||
birthCondition: z.string().optional(),
|
||||
|
||||
operationDescription: z.string({
|
||||
required_error: 'Mohon lengkapi uraian operasi',
|
||||
}),
|
||||
|
||||
bleedingAmountCc: positiveInt.optional(),
|
||||
|
||||
birthRemark: z.enum(['lahir_hidup', 'lahir_mati']).optional(),
|
||||
})
|
||||
|
||||
const BloodInputSchema = z
|
||||
.object({
|
||||
type: z.enum(['prc', 'wb', 'ffp', 'tc']),
|
||||
amount: z.object({
|
||||
prc: z.coerce.number().optional(),
|
||||
wb: z.coerce.number().optional(),
|
||||
ffp: z.coerce.number().optional(),
|
||||
tc: z.coerce.number().optional(),
|
||||
}),
|
||||
})
|
||||
.transform((val) => ({
|
||||
type: val.type,
|
||||
amount: Object.fromEntries(
|
||||
['prc', 'wb', 'ffp', 'tc'].map((k) => [k, val.type === k ? (val.amount[k] ?? null) : null]),
|
||||
),
|
||||
}))
|
||||
|
||||
const ImplantSchema = z.object({
|
||||
brand: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
stickerNumber: z.string().optional(),
|
||||
companionName: z.string().optional(),
|
||||
})
|
||||
|
||||
const SpecimenSchema = z.object({
|
||||
destination: z.string({
|
||||
required_error: 'Silahkan pilih specimen',
|
||||
}),
|
||||
})
|
||||
|
||||
const TissueNoteSchema = z.object({
|
||||
note: z
|
||||
.string()
|
||||
.trim()
|
||||
.transform((val) => (val === '' ? undefined : val))
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export const ActionReportSchema = z.object({
|
||||
operatorTeam: OperatorTeamSchema,
|
||||
procedures: z.array(ProcedureSchema).min(1, { message: 'Silahkan pilih prosedur' }),
|
||||
|
||||
operationExecution: OperationExecutionSchema,
|
||||
|
||||
bloodInput: BloodInputSchema.optional(),
|
||||
implant: ImplantSchema.optional(),
|
||||
specimen: SpecimenSchema.optional(),
|
||||
|
||||
tissueNotes: z.array(TissueNoteSchema).optional(),
|
||||
})
|
||||
|
||||
export type ActionReportFormData = z.infer<typeof ActionReportSchema>
|
||||
@@ -1,83 +0,0 @@
|
||||
import * as z from 'zod'
|
||||
import { genEduAssessmentRaw, type EduAssessment, type EduAssessmentRaw } from '../models/edu-assessment'
|
||||
import { serializeKeyToBoolean } from '../lib/clinical.constants'
|
||||
|
||||
const AssessmentEducationSchema = z.object({
|
||||
generalEducationNeeds: z
|
||||
.array(z.string(), {
|
||||
required_error: 'Mohon pilih setidaknya item',
|
||||
})
|
||||
.min(1, 'Mohon pilih setidaknya item'),
|
||||
|
||||
specificEducationNeeds: z
|
||||
.array(z.string(), {
|
||||
required_error: 'Mohon pilih setidaknya item',
|
||||
})
|
||||
.min(1, 'Mohon pilih setidaknya item'),
|
||||
|
||||
learningAbility: z.string({
|
||||
required_error: 'Mohon pilih kemampuan belajar',
|
||||
}),
|
||||
|
||||
learningWillingness: z.string({
|
||||
required_error: 'Mohon pilih kemauan belajar',
|
||||
}),
|
||||
|
||||
barrier: z.string({
|
||||
required_error: 'Mohon pilih hambatan',
|
||||
}),
|
||||
|
||||
learningMethod: z.string({
|
||||
required_error: 'Mohon pilih metode pembelajaran',
|
||||
}),
|
||||
|
||||
language: z.string({
|
||||
required_error: 'Mohon pilih bahasa',
|
||||
}),
|
||||
|
||||
languageBarrier: z.string({
|
||||
required_error: 'Mohon pilih hambatan bahasa',
|
||||
}),
|
||||
|
||||
beliefValue: z.string({
|
||||
required_error: 'Mohon pilih keyakinan pada nilai-nilai yang dianut',
|
||||
}),
|
||||
|
||||
plans: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
value: z.string().nonempty('Mohon masukkan catatan'),
|
||||
}),
|
||||
)
|
||||
.min(1, 'Minimal 1 catatan rencana studi'),
|
||||
})
|
||||
|
||||
type AssessmentEducationFormData = z.infer<typeof AssessmentEducationSchema>
|
||||
|
||||
export { AssessmentEducationSchema }
|
||||
export type { AssessmentEducationFormData }
|
||||
|
||||
export function encode(encounterId: number, formData: AssessmentEducationFormData): EduAssessmentRaw {
|
||||
let base = genEduAssessmentRaw()
|
||||
// serialize general data
|
||||
const general = serializeKeyToBoolean('general', formData.generalEducationNeeds)
|
||||
const special = serializeKeyToBoolean('special', formData.specificEducationNeeds)
|
||||
|
||||
base.encounter_id = encounterId
|
||||
base.generalEdus = JSON.stringify(general)
|
||||
base.specialEdus = JSON.stringify(special)
|
||||
|
||||
base.assessments = JSON.stringify({
|
||||
'learn-ability': formData.learningAbility,
|
||||
'learn-will': formData.learningWillingness,
|
||||
obstacle: formData.barrier,
|
||||
'learn-method': formData.learningMethod,
|
||||
lang: formData.language,
|
||||
'lang-obstacle': formData.languageBarrier,
|
||||
belief: formData.beliefValue,
|
||||
})
|
||||
|
||||
base.plan = JSON.stringify(formData.plans)
|
||||
return base
|
||||
}
|
||||
@@ -4,9 +4,8 @@ import { InternalReferenceSchema } from './internal-reference.schema'
|
||||
// Check In
|
||||
const CheckInSchema = z.object({
|
||||
// registeredAt: z.string({ required_error: 'Tanggal masuk harus diisi' }),
|
||||
responsible_doctor_code: z.string({ required_error: 'Dokter harus diisi' }),
|
||||
// adm_employee_id: z.number({ required_error: 'PJA harus diisi' }).gt(0, 'PJA harus diisi'),
|
||||
registeredAt: z.string({ required_error: 'waktu harus diisi' }),
|
||||
responsible_doctor_id: z.number({ required_error: 'Dokter harus diisi' }).gt(0, 'Dokter harus diisi'),
|
||||
adm_employee_id: z.number({ required_error: 'PJA harus diisi' }).gt(0, 'PJA harus diisi'),
|
||||
})
|
||||
type CheckInFormData = z.infer<typeof CheckInSchema>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user