This commit is contained in:
hasyim_kai
2025-11-25 09:17:11 +07:00
parent a2323e0827
commit 3e5c03148b
30 changed files with 2063 additions and 1 deletions
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, BillingCodeTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => BillingCodeTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, BirthDescriptionTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => BirthDescriptionTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, BirthPlaceDescriptionTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => BirthPlaceDescriptionTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,117 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import { differenceInDays, differenceInMonths, differenceInYears, parseISO } from 'date-fns'
import { Input } from '~/components/pub/ui/input'
import { cn } from '~/lib/utils'
import * as DE from '~/components/pub/my-ui/doc-entry'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
isWithTime?: boolean
}>()
const {
fieldName = 'birthDate',
label = 'Tanggal Lahir',
placeholder = 'Pilih tanggal lahir',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
// Reactive variables for age calculation
const patientAge = ref<string>('Masukkan tanggal lahir')
// Function to calculate age with years, months, and days
function calculateAge(birthDate: string | Date | undefined): string {
if (!birthDate) {
return 'Masukkan tanggal lahir'
}
try {
let dateObj: Date
if (typeof birthDate === 'string') {
dateObj = parseISO(birthDate)
} else {
dateObj = birthDate
}
const today = new Date()
// Calculate years, months, and days
const totalYears = differenceInYears(today, dateObj)
// Calculate remaining months after years
const yearsPassed = new Date(dateObj)
yearsPassed.setFullYear(yearsPassed.getFullYear() + totalYears)
const remainingMonths = differenceInMonths(today, yearsPassed)
// Calculate remaining days after years and months
const monthsPassed = new Date(yearsPassed)
monthsPassed.setMonth(monthsPassed.getMonth() + remainingMonths)
const remainingDays = differenceInDays(today, monthsPassed)
// Format the result
const parts = []
if (totalYears > 0) parts.push(`${totalYears} Tahun`)
if (remainingMonths > 0) parts.push(`${remainingMonths} Bulan`)
if (remainingDays > 0) parts.push(`${remainingDays} Hari`)
return parts.length > 0 ? parts.join(' ') : '0 Hari'
} catch {
return 'Masukkan tanggal lahir'
}
}
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Input
id="birthDate"
:type="props.isWithTime ? 'datetime-local' : 'date'"
min="1900-01-01"
v-bind="componentField"
:placeholder="placeholder"
@update:model-value="
(value: string | number) => {
const dateStr = typeof value === 'number' ? String(value) : value
patientAge = calculateAge(dateStr)
}
"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, DissectionTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => DissectionTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,87 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const doctors = ref<Array<Item>>([])
async function fetchDpjp() {
doctors.value = await getDoctorLabelList({
serviceType: 1,
serviceDate: new Date().toISOString().substring(0, 10),
includes: 'employee-person',
}, true)
}
onMounted(() => {
fetchDpjp()
})
// function handleDpjpChange(selected: string) {
// selectedDpjpId.value = selected ?? null
// }
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="doctors"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,87 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const doctors = ref<Array<Item>>([])
async function fetchDpjp() {
doctors.value = await getDoctorLabelList({
serviceType: 1,
serviceDate: new Date().toISOString().substring(0, 10),
includes: 'employee-person',
}, true)
}
onMounted(() => {
fetchDpjp()
})
// function handleDpjpChange(selected: string) {
// selectedDpjpId.value = selected ?? null
// }
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="doctors"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, SpecimenTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => SpecimenTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, SurgeryOrderTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => SurgeryOrderTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, SurgerySystemTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => SurgerySystemTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import { occupationCodes, SurgeryTypeOptList } from '~/lib/constants'
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Item } from '~/components/pub/my-ui/combobox'
const props = defineProps<{
fieldName?: string
label?: string
placeholder?: string
errors?: FormErrors
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const {
fieldName = 'job',
label = 'Pekerjaan',
placeholder = 'Pilih pekerjaan',
errors,
class: containerClass,
fieldGroupClass,
labelClass,
} = props
const itemList = computed<Array<Item>>(() => SurgeryTypeOptList)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
: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>
<Combobox
class="focus:ring-0 focus:ring-offset-0"
:id="fieldName"
v-bind="componentField"
:items="itemList"
:placeholder="placeholder"
search-placeholder="Cari..."
empty-message="Data tidak ditemukan"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,54 @@
<script setup lang="ts">
import DetailRow from '~/components/pub/my-ui/form/view/detail-row.vue'
import { cn, } from '~/lib/utils'
import type { ControlLetter } from '~/models/control-letter'
// #region Props & Emits
const props = defineProps<{
instance: ControlLetter | null
}>()
const emit = defineEmits<{
(e: 'click', type: string): void
}>()
// #endregion
// #region State & Computed
// #endregion
// Computed addresses from nested data
// #endregion
// #region Lifecycle Hooks
// #endregion
// #region Functions
// #endregion region
// #region Utilities & event handlers
function onClick(type: string) {
emit('click', type)
}
// #endregion
// #region Watchers
// #endregion
</script>
<template>
<div :class="cn('min-h-[50vh] space-y-2',)">
<DetailRow label="Tgl Rencana Kontrol">{{ props.instance?.date ? new Date(props.instance?.date).toLocaleDateString('id-ID') : '-' }}</DetailRow>
<DetailRow label="Unit">{{ props.instance?.unit.name || '-' }}</DetailRow>
<DetailRow label="Spesialis">{{ props.instance?.specialist.name || '-' }}</DetailRow>
<DetailRow label="Sub Spesialis">{{ props.instance?.subspecialist.name || '-' }}</DetailRow>
<DetailRow label="DPJP">{{ props.instance?.doctor.employee.person.name || '-' }}</DetailRow>
<DetailRow label="Status SEP">{{ 'SEP INTERNAL' }}</DetailRow>
</div>
<div class="border-t-1 my-2 flex justify-end border-t-slate-300 py-2">
<PubMyUiNavFooterBaEd @click="onClick" />
</div>
</template>
<style scoped></style>
+183
View File
@@ -0,0 +1,183 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import { toTypedSchema } from '@vee-validate/zod'
import { Form } from '~/components/pub/ui/form'
import SelectDate from './_common/select-date.vue'
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
import * as DE from '~/components/pub/my-ui/doc-entry'
import SelectDpjpBedah from './_common/select-dpjp-bedah.vue'
import SelectDpjpAnastesi from './_common/select-dpjp-anastesi.vue'
import SelectSurgeryType from './_common/select-surgery-type.vue'
import SelectBillingCode from './_common/select-billing-code.vue'
import SelectSurgerySystemType from './_common/select-surgery-system-type.vue'
import SelectDissectionType from './_common/select-dissection-type.vue'
import SelectSurgeryOrder from './_common/select-surgery-order.vue'
import SelectBirthDesc from './_common/select-birth-desc.vue'
import SelectBirthPlaceDesc from './_common/select-birth-place-desc.vue'
import SelectSpecimenType from './_common/select-specimen-type.vue'
import TextAreaInput from '~/components/pub/my-ui/form/text-area-input.vue'
const props = defineProps<{
schema: any
initialValues?: any
errors?: FormErrors
}>()
const formSchema = toTypedSchema(props.schema)
const formRef = ref()
const isOperativeActionDialogOpen = inject<Ref<boolean>>('isOperativeActionDialogOpen')!
defineExpose({
validate: () => formRef.value?.validate(),
resetForm: () => formRef.value?.resetForm(),
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
values: computed(() => formRef.value?.values),
})
const handleToggleOperativeActionDialog = () => {
isOperativeActionDialogOpen.value = !isOperativeActionDialogOpen.value
}
</script>
<template>
<Form
ref="formRef"
v-slot="{ values }"
as=""
keep-values
:validation-schema="formSchema"
:validate-on-mount="false"
validation-mode="onSubmit"
:initial-values="initialValues ? initialValues : {}"
>
<DE.Block :col-count="4" :cell-flex="false">
<SelectDpjpBedah :errors="errors"
field-name="a1"
label="DPJP Bedah" placeholder="Pilih DPJP Bedah"
is-required
/>
<InputBase :errors="errors"
field-name="a2"
label="Operator" placeholder="Isi Operator"
/>
<InputBase :errors="errors"
field-name="a3"
label="Asisten Operator" placeholder="Isi Asisten Operator"
/>
<InputBase :errors="errors"
field-name="a4"
label="Instrumentir" placeholder="Isi Instrumentir"
/>
<SelectDpjpAnastesi :errors="errors"
field-name="a5"
label="DPJP Anastesi" placeholder="Pilih DPJP Anastesi"
is-required
/>
<InputBase :errors="errors"
field-name="a6"
label="Perawatan Anastesi" placeholder="Isi Perawatan Anastesi"
/>
<SelectDate
field-name="a7"
label="Tanggal Pembedahan"
:errors="errors"
is-required
/>
</DE.Block>
<DE.Block :col-count="4" :cell-flex="false">
<DE.Cell :col-span="2">
<TextAreaInput :errors="errors"
field-name="a8"
label="Diagnosa Pra Bedah" placeholder="Isi Diagnosa Pra Bedah"
is-required
/>
</DE.Cell>
<InputBase :errors="errors"
field-name="a9"
label="Perawat Pra Bedah" placeholder="Isi Perawat Pra Bedah"
/>
</DE.Block>
<!-- PICKER -->
<!-- -->
<DE.Block :col-count="3" :cell-flex="false">
<SelectSurgeryType :errors="errors"
field-name="a10"
label="Tipe Operasi" placeholder="Pilih Tipe Operasi"
is-required
/>
<SelectBillingCode :errors="errors"
field-name="a11"
label="Kode Billing" placeholder="Pilih Kode Billing"
is-required
/>
<SelectSurgerySystemType :errors="errors"
field-name="a12"
label="Sistem Operasi" placeholder="Pilih Sistem Operasi"
is-required
/>
<SelectDate :errors="errors"
field-name="a7"
label="Operasi Mulai"
is-required
is-with-time
/>
<SelectDate :errors="errors"
field-name="a7"
label="Operasi Selesai"
is-required
is-with-time
/>
<div>
<p class="mt-1.5 mb-2">Lama Operasi</p>
<h1 class="text-lg"><b>{{ 1 }}</b> Jam <b>{{ 1 }}</b> Menit</h1>
</div>
<SelectDate :errors="errors"
field-name="a7"
label="Pembiusan Mulai"
is-required
is-with-time
/>
<SelectDate :errors="errors"
field-name="a7"
label="Pembiusan Selesai"
is-required
is-with-time
/>
<div>
<p class="mt-1.5 mb-2">Lama Pembiusan</p>
<h1 class="text-lg"><b>{{ 1 }}</b> Jam <b>{{ 1 }}</b> Menit</h1>
</div>
<SelectDissectionType :errors="errors"
field-name="a13"
label="Tipe Pembedahan" placeholder="Pilih Tipe Pembedahan"
is-required
/>
<SelectSurgeryOrder :errors="errors"
field-name="a14"
label="Urutan Operasi" placeholder="Pilih Urutan Operasi"
is-required
/>
<SelectBirthDesc :errors="errors"
field-name="a15"
label="Ket. Lahir" placeholder="Pilih Ket. Lahir"
is-required
/>
<SelectBirthPlaceDesc :errors="errors"
field-name="a16"
label="Ket. Tempat Lahir" placeholder="Pilih Ket. Tempat Lahir"
is-required
/>
<SelectSpecimenType :errors="errors"
field-name="a17"
label="Specimen/Jaringan Dikirim Ke" placeholder="Pilih Specimen/Jaringan Dikirim Ke"
is-required
/>
</DE.Block>
</Form>
</template>
@@ -0,0 +1,57 @@
import type { Config } from '~/components/pub/my-ui/data-table'
import type { Patient } from '~/models/patient'
import { defineAsyncComponent } from 'vue'
import { educationCodes, genderCodes } from '~/lib/constants'
import { calculateAge } from '~/lib/utils'
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
export const config: Config = {
cols: [{}, {}, {}, {}, {}, {}, {}, {},],
headers: [
[
{ label: 'Tgl Laporan' },
{ label: 'DPJP Bedah' },
{ label: 'DPJP Anastesi' },
{ label: 'Tgl Pembedahan' },
{ label: 'Jenis Operasi' },
{ label: 'Kode Billing' },
{ label: 'Sistem Operasi' },
{ label: 'Action' },
],
],
keys: ['date', 'doctor.employee.person.name', 'doctor.employee.person.name', 'date', 'name', 'name', 'name', 'action'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
],
parses: {
date: (rec: unknown): unknown => {
const date = (rec as any).date
if (typeof date == 'object' && date) {
return (date as Date).toLocaleDateString('id-ID')
} else if (typeof date == 'string') {
return (date as string).substring(0, 10)
}
return date
},
},
components: {
action(rec, idx) {
return {
idx,
rec: rec as object,
component: action,
}
},
},
htmls: {
},
}
@@ -0,0 +1,31 @@
<script setup lang="ts">
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
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,33 @@
<script setup lang="ts">
import { config } from './operative-action-list.cfg'
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
interface Props {
data: any[]
paginationMeta: PaginationMeta
}
defineProps<Props>()
const modelValue = defineModel<any[]>('modelValue', { default: [] })
const emit = defineEmits<{
pageChange: [page: number]
}>()
function handlePageChange(page: number) {
emit('pageChange', page)
}
</script>
<template>
<div class="space-y-4">
<PubMyUiDataTable
select-mode="multiple"
v-model="modelValue"
v-bind="config"
:rows="data"
:skeleton-size="paginationMeta?.pageSize"
/>
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
</template>
@@ -0,0 +1,56 @@
import type { Config } from '~/components/pub/my-ui/data-table'
import type { Patient } from '~/models/patient'
import { defineAsyncComponent } from 'vue'
import { educationCodes, genderCodes } from '~/lib/constants'
import { calculateAge } from '~/lib/utils'
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
export const config: Config = {
cols: [{}, {}, {}, {}, {}, {}, {}, ],
headers: [
[
{ label: 'Tgl Laporan' },
{ label: 'DPJP Bedah' },
{ label: 'DPJP Anastesi' },
{ label: 'Tgl Pembedahan' },
{ label: 'Jenis Operasi' },
{ label: 'Kode Billing' },
{ label: 'Sistem Operasi' },
],
],
keys: ['date', 'doctor.employee.person.name', 'doctor.employee.person.name', 'date', 'name', 'name', 'name'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
],
parses: {
date: (rec: unknown): unknown => {
const date = (rec as any).date
if (typeof date == 'object' && date) {
return (date as Date).toLocaleDateString('id-ID')
} else if (typeof date == 'string') {
return (date as string).substring(0, 10)
}
return date
},
},
components: {
// action(rec, idx) {
// return {
// idx,
// rec: rec as object,
// component: action,
// }
// },
},
htmls: {
},
}
@@ -26,6 +26,7 @@ import DocUploadList from '~/components/content/document-upload/list.vue'
import GeneralConsentList from '~/components/content/general-consent/entry.vue'
import ResumeList from '~/components/content/resume/list.vue'
import ControlLetterList from '~/components/content/control-letter/list.vue'
import SurgeryReportList from '~/components/content/surgery-report/list.vue'
const route = useRoute()
const router = useRouter()
@@ -90,6 +91,7 @@ const tabs: TabItem[] = [
{ value: 'resume', label: 'Resume', component: ResumeList, props: { encounter: data } },
{ value: 'control', label: 'Surat Kontrol', component: ControlLetterList, props: { encounter: data } },
{ value: 'screening', label: 'Skrinning MPP' },
{ value: 'surgery-report', label: 'Laporan Operasi', component: SurgeryReportList, props: { encounter: data } },
{
value: 'supporting-document',
label: 'Upload Dokumen Pendukung',
@@ -0,0 +1,77 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { withBase } from '~/models/_base'
import type { HeaderPrep } from '~/components/pub/my-ui/data/types'
import type { Patient } from '~/models/patient'
import type { Person } from '~/models/person'
import { getDetail } from '~/services/surgery-report.service'
// Components
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import type { SurgeryReport } from '~/models/surgery-report'
// #region Props & Emits
const props = defineProps<{
}>()
// #endregion
// #region State & Computed
const route = useRoute()
const router = useRouter()
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
const surgeryReportId = typeof route.params.surgery_report_id == 'string' ? parseInt(route.params.surgery_report_id) : 0
const surgeryReport = ref<SurgeryReport | null>(null)
const headerPrep: HeaderPrep = {
title: 'Detail Laporan Operasi',
icon: 'i-lucide-newspaper',
}
// #endregion
// #region Lifecycle Hooks
onMounted(async () => {
const result = await getDetail(controlLetterId, {
includes: "unit,specialist,subspecialist,doctor-employee-person",
})
if (result.success) {
controlLetter.value = result.body?.data
}
})
// #endregion
// #region Functions
function goBack() {
router.go(-1)
}
// #endregion region
// #region Utilities & event handlers
function handleAction(type: string) {
switch (type) {
// case 'edit':
// navigateTo({
// name: 'rehab-encounter-id-surgery-report-control_letter_id-edit',
// params: { id: encounterId, "control_letter_id": controlLetterId },
// })
// break
case 'back':
goBack()
break
}
}
// #endregion
// #region Watchers
// #endregion
</script>
<template>
<Header :prep="headerPrep" :ref-search-nav="headerPrep.refSearchNav" />
<AppSurgeryReportDetail :instance="controlLetter" @click="handleAction" />
</template>
@@ -0,0 +1,194 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
import type { Patient, genPatientProps } from '~/models/patient'
import type { ExposedForm } from '~/types/form'
import type { PatientBase } from '~/models/patient'
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import { genPatient } from '~/models/patient'
import { PatientSchema } from '~/schemas/patient.schema'
import { PersonAddressRelativeSchema } from '~/schemas/person-address-relative.schema'
import { PersonAddressSchema } from '~/schemas/person-address.schema'
import { PersonContactListSchema } from '~/schemas/person-contact.schema'
import { PersonFamiliesSchema } from '~/schemas/person-family.schema'
import { ResponsiblePersonSchema } from '~/schemas/person-relative.schema'
import { uploadAttachment } from '~/services/patient.service'
import { getDetail, update } from '~/services/surgery-report.service'
import type { SurgeryReport } from '~/models/surgery-report'
import { toast } from '~/components/pub/ui/toast'
import { withBase } from '~/models/_base'
import Confirmation from '~/components/pub/my-ui/confirmation/confirmation.vue'
import { SurgeryReportSchema } from '~/schemas/surgery-report.schema'
import { formatDateYyyyMmDd } from '~/lib/date'
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import { getList, remove } from '~/services/surgery-report.service'
import { handleActionEdit } from '~/handlers/surgery-report.handler'
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
// #region Props & Emits
const props = defineProps<{
callbackUrl?: string
}>()
// form related state
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
fetchFn: (params) => getList({ ...params, includes: 'specialist,subspecialist,doctor-employee-person', }),
entityName: 'surgery-report',
})
// #endregion
// #region State & Computed
const route = useRoute()
const router = useRouter()
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
const surgeryReportId = typeof route.params.surgery_report_id == 'string' ? parseInt(route.params.surgery_report_id) : 0
const inputForm = ref<ExposedForm<any> | null>(null)
const surgeryReport = ref({})
const isConfirmationOpen = ref(false)
const selectedOperativeAction = ref<any>(null)
const isOperativeActionDialogOpen = ref(false)
provide("isOperativeActionDialogOpen", isOperativeActionDialogOpen);
const headerPrep: HeaderPrep = {
title: "Pilih Tindakan",
icon: 'i-lucide-history',
}
const refSearchNav: RefSearchNav = {
onClick: () => {
// open filter modal
},
onInput: (val: string) => {
searchInput.value = val
},
onClear: () => {
searchInput.value = ''
},
}
// #endregion
// #region Lifecycle Hooks
onMounted(async () => {
const result = await getDetail(surgeryReportId)
if (result.success) {
const responseData = {...result.body.data, date: formatDateYyyyMmDd(result.body.data.date)}
surgeryReport.value = responseData
inputForm.value?.setValues(responseData)
}
})
// #endregion
// #region Functions
function goBack() {
router.go(-1)
}
async function handleConfirmAdd() {
const response = await handleActionEdit(
surgeryReportId,
await composeFormData(),
() => { },
() => { },
toast,
)
goBack()
}
async function composeFormData(): Promise<SurgeryReport> {
const [input,] = await Promise.all([
inputForm.value?.validate(),
])
const results = [input]
const allValid = results.every((r) => r?.valid)
// exit, if form errors happend during validation
if (!allValid) return Promise.reject('Form validation failed')
const formData = input?.values
formData.encounter_id = encounterId
return new Promise((resolve) => resolve(formData))
}
// #endregion region
// #region Utilities & event handlers
async function handleActionClick(eventType: string) {
if (eventType === 'submit') {
isConfirmationOpen.value = true
}
if (eventType === 'back') {
if (props.callbackUrl) {
await navigateTo(props.callbackUrl)
return
}
goBack()
}
}
function handleCancelAdd() {
isConfirmationOpen.value = false
}
function actionDialogHandler(type: string) {
if (type === 'submit') {
// icdPreview.value.procedures = selectedOperativeAction.value || []
}
isOperativeActionDialogOpen.value = false
}
// #endregion
// #region Watchers
// #endregion
</script>
<template>
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Update Laporan Operasi</div>
<AppSurgeryReportEntry
ref="inputForm"
:schema="SurgeryReportSchema"
/>
<div class="my-2 flex justify-end py-2">
<Action :enable-draft="false" @click="handleActionClick" />
</div>
<Dialog
v-model:open="isOperativeActionDialogOpen"
title=""
size="xl"
>
<Header
:prep="headerPrep"
:ref-search-nav="refSearchNav"
v-model:search="searchInput"
@search="handleSearch" />
<AppSurgeryReportPicker
v-model:model-value="selectedOperativeAction"
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<div class="my-2 flex justify-end py-2">
<ActionDialog @click="actionDialogHandler" />
</div>
</Dialog>
<Confirmation
v-model:open="isConfirmationOpen"
title="Simpan Data"
message="Apakah Anda yakin ingin menyimpan data ini?"
confirm-text="Simpan"
@confirm="handleConfirmAdd"
@cancel="handleCancelAdd"
/>
</template>
<style scoped>
/* component style */
</style>
@@ -0,0 +1,195 @@
<script setup lang="ts">
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
// #region Imports
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
import { ActionEvents } from '~/components/pub/my-ui/data/types'
import Filter from '~/components/pub/my-ui/nav-header/filter.vue'
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import { usePaginatedList } from '~/composables/usePaginatedList'
import { getList, remove } from '~/services/surgery-report.service'
import { toast } from '~/components/pub/ui/toast'
import type { Encounter } from '~/models/encounter'
import WarningAlert from '~/components/pub/my-ui/alert/warning-alert.vue'
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
// #endregion
// #region State
const props = defineProps<{
encounter?: Encounter
}>()
const route = useRoute()
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
fetchFn: (params) => getList({ ...params, includes: 'specialist,subspecialist,doctor-employee-person', }),
entityName: 'surgery-report',
})
const isFilterDialogOpen = ref(false)
const isRecordConfirmationOpen = ref(false)
const summaryLoading = ref(false)
const isRequirementsMet = ref(true)
const recId = ref<number>(0)
const recAction = ref<string>('')
const recItem = ref<any>(null)
const headerPrep: HeaderPrep = {
title: "Laporan Operasi",
icon: 'i-lucide-history',
addNav: {
label: "Laporan Operasi",
onClick: () => navigateTo({
name: 'rehab-encounter-id-surgery-report-add',
params: { id: encounterId },
}),
},
}
const refSearchNav: RefSearchNav = {
onClick: () => {
isFilterDialogOpen.value = true
},
onInput: (val: string) => {
searchInput.value = val
},
onClear: () => {
searchInput.value = ''
},
}
// #endregion
// #region Lifecycle Hooks
onMounted(() => {
getListData()
})
// #endregion
// #region Functions
async function getListData() {
try {
summaryLoading.value = true
await new Promise((resolve) => setTimeout(resolve, 500))
} catch (error) {
console.error('Error fetching Data:', error)
} finally {
summaryLoading.value = false
}
}
// Handle confirmation result
async function handleConfirmDelete(record: any, action: string) {
if (action === 'delete' && record?.id) {
try {
const result = await remove(record.id)
if (result.success) {
toast({ title: 'Berhasil', description: 'Data berhasil dihapus', variant: 'default' })
await fetchData()
} else {
toast({ title: 'Gagal', description: `Data gagal dihapus`, variant: 'destructive' })
}
} catch (error) {
toast({ title: 'Gagal', description: `Something went wrong`, variant: 'destructive' })
}
}
}
function handleCancelConfirmation() {
// Reset record state when cancelled
recId.value = 0
recAction.value = ''
recItem.value = null
}
// #endregion
// #region Provide
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
// #endregion
// #region Watchers
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
navigateTo({
name: 'rehab-encounter-id-surgery-report-control_letter_id',
params: { id: encounterId, "control_letter_id": recId.value },
})
break
case ActionEvents.showEdit:
// TODO: Handle edit action
// isFormEntryDialogOpen.value = true
navigateTo({
name: 'rehab-encounter-id-surgery-report-control_letter_id-edit',
params: { id: encounterId, "control_letter_id": recId.value },
})
break
case ActionEvents.showConfirmDelete:
// Trigger confirmation modal open
isRecordConfirmationOpen.value = true
break
}
})
// #endregion
</script>
<template>
<WarningAlert v-if="!isRequirementsMet"
class="mb-5"
text="Syarat pembuatan Laporan Operasi belum terpenuhi"
:description="[
'Lanjutan Penatalaksanaan Pasien harus pulang/KRS.',
'Status Resume Medis harus tervalidasi.'
]" />
<div v-else>
<Header v-model:search="searchInput"
:prep="headerPrep"
:ref-search-nav="refSearchNav"
@search="handleSearch" />
<Filter
:prep="headerPrep"
:ref-search-nav="refSearchNav"
:enable-export="false"
/>
<AppSurgeryReportList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Dialog v-model:open="isFilterDialogOpen" title="Filter" size="lg">
<!-- <AppEncounterFilter
:installation="{
msg: { placeholder: 'Pilih' },
items: [],
}"
:schema="{}"
/> -->
aaaaaaa
</Dialog>
<RecordConfirmation v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem"
@confirm="handleConfirmDelete" @cancel="handleCancelConfirmation">
<template #default="{ record }">
<div class="text-sm">
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.firstName">
<strong>Nama:</strong>
{{ record.firstName }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.cellphone }}
</p>
</div>
</template>
</RecordConfirmation>
</div>
</template>
@@ -101,7 +101,7 @@ function onFilterClick() {
</Button>
<DropdownMenu v-show="props.enableExport">
<DropdownMenuTrigger as-child>
<DropdownMenuTrigger v-show="props.enableExport" as-child>
<Button variant="outline" class="ml-auto border-orange-500 text-orange-600 hover:bg-orange-50">
<Icon name="i-lucide-download" class="h-4 w-4" />
Ekspor
+24
View File
@@ -0,0 +1,24 @@
// Handlers
import { genCrudHandler } from '~/handlers/_handler'
// Services
import { create, update, remove } from '~/services/surgery-report.service'
export const {
recId,
recAction,
recItem,
isReadonly,
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
handleCancelForm,
} = genCrudHandler({
create,
update,
remove,
})
+62
View File
@@ -425,3 +425,65 @@ export const supportingDocOpt = [
{ label: 'Data Penunjang', value: 'encounter-support' },
{ label: 'Lain - Lain', value: 'encounter-other' },
]
export type SurgeryType = "kecil" | "sedang" | "besar" | "khusus"
export const SurgeryTypeOptList: { label: string; value: SurgeryType }[] = [
{ label: 'Kecil', value: 'kecil' },
{ label: 'Sedang', value: 'sedang' },
{ label: 'Besar', value: 'besar' },
{ label: 'Khusus', value: 'khusus' },
]
export type BillingCodeType = "general" | "regional" | "local"
export const BillingCodeTypeOptList: { label: string; value: BillingCodeType }[] = [
{ label: 'General', value: 'general' },
{ label: 'Regional', value: 'regional' },
{ label: 'Local', value: 'local' },
]
export type SurgerySystemType = "cito" | "urgent" | "efektif" | "khusus"
export const SurgerySystemTypeOptList: { label: string; value: SurgerySystemType }[] = [
{ label: 'Cito', value: 'cito' },
{ label: 'Urgent', value: 'urgent' },
{ label: 'Efektif', value: 'efektif' },
{ label: 'Khusus', value: 'khusus' },
]
export type DissectionType = "bersih" | "bersih terkontaminasi" | "terkontaminasi kotor" | "kotor"
export const DissectionTypeOptList: { label: string; value: DissectionType }[] = [
{ label: 'Bersih', value: 'bersih' },
{ label: 'Bersih terkontaminasi', value: 'bersih terkontaminasi' },
{ label: 'Terkontaminasi kotor', value: 'terkontaminasi kotor' },
{ label: 'Kotor', value: 'kotor' },
]
export type SurgeryOrderType = "satu" | "ulangan"
export const SurgeryOrderTypeOptList: { label: string; value: SurgeryOrderType }[] = [
{ label: 'Satu', value: 'satu' },
{ label: 'Ulangan', value: 'ulangan' },
]
export type BirthDescriptionType = "lahir hidup" | "lahir mati"
export const BirthDescriptionTypeOptList: { label: string; value: BirthDescriptionType }[] = [
{ label: 'Lahir Hidup', value: 'lahir hidup' },
{ label: 'Lahir Mati', value: 'lahir mati' },
]
export type BirthPlaceDescriptionType = "rssa" | "bidan luar" | "dokter luar" | "dukun bayi" | "puskesmas" | "paramedis luar"
export const BirthPlaceDescriptionTypeOptList: { label: string; value: BirthPlaceDescriptionType }[] = [
{ label: 'RSSA', value: 'rssa' },
{ label: 'Bidan luar', value: 'bidan luar' },
{ label: 'Dokter luar', value: 'dokter luar' },
{ label: 'Dukun bayi', value: 'dukun bayi' },
{ label: 'Puskesmas', value: 'puskesmas' },
{ label: 'Paramedis luar', value: 'paramedis luar' },
]
export type SpecimenType = "pa" | "mikrobiologi" | "laborat" | "tidak perlu"
export const SpecimenTypeOptList: { label: string; value: SpecimenType }[] = [
{ label: 'PA', value: 'pa' },
{ label: 'Mikrobiologi', value: 'mikrobiologi' },
{ label: 'Laborat', value: 'laborat' },
{ label: 'Tidak perlu', value: 'tidak perlu' },
]
+37
View File
@@ -0,0 +1,37 @@
import { type Base, genBase } from "./_base"
import { genDoctor, type Doctor } from "./doctor"
import { genEncounter, type Encounter } from "./encounter"
import { genSpecialist, type Specialist } from "./specialist"
import { genSubspecialist, type Subspecialist } from "./subspecialist"
import { genUnit, type Unit } from "./unit"
export interface SurgeryReport extends Base {
encounter_id: number
encounter: Encounter
unit_id: number
unit: Unit
specialist_id: number
specialist: Specialist
subspecialist_id: number
subspecialist: Subspecialist
doctor_id: number
doctor: Doctor
date: ''
}
export function genSurgeryReport(): SurgeryReport {
return {
...genBase(),
encounter_id: 0,
encounter: genEncounter(),
unit_id: 0,
unit: genUnit(),
specialist_id: 0,
specialist: genSpecialist(),
subspecialist_id: 0,
subspecialist: genSubspecialist(),
doctor_id: 0,
doctor: genDoctor(),
date: ''
}
}
@@ -0,0 +1,41 @@
<script setup lang="ts">
import type { PagePermission } from '~/models/role'
import Error from '~/components/pub/my-ui/error/error.vue'
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
definePageMeta({
middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Update Surat Kontrol',
contentFrame: 'cf-full-width',
})
const route = useRoute()
useHead({
title: () => route.meta.title as string,
})
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
const { checkRole, hasReadAccess } = useRBAC()
// Check if user has access to this page
const hasAccess = checkRole(roleAccess)
// if (!hasAccess) {
// navigateTo('/403')
// }
// Define permission-based computed properties
// const canRead = hasReadAccess(roleAccess)
const canRead = true
</script>
<template>
<div>
<div v-if="canRead">
<ContentSurgeryReportEntry />
</div>
<Error v-else :status-code="403" />
</div>
</template>
@@ -0,0 +1,41 @@
<script setup lang="ts">
import type { PagePermission } from '~/models/role'
import Error from '~/components/pub/my-ui/error/error.vue'
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
definePageMeta({
middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Detail Surat Kontrol',
contentFrame: 'cf-container-md',
})
const route = useRoute()
useHead({
title: () => route.meta.title as string,
})
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
const { checkRole, hasReadAccess } = useRBAC()
// Check if user has access to this page
const hasAccess = checkRole(roleAccess)
// if (!hasAccess) {
// navigateTo('/403')
// }
// Define permission-based computed properties
// const canRead = hasReadAccess(roleAccess)
const canRead = true
</script>
<template>
<div>
<div v-if="canRead">
<ContentSurgeryReportDetail :patient-id="Number(route.params.id)" />
</div>
<Error v-else :status-code="403" />
</div>
</template>
@@ -0,0 +1,41 @@
<script setup lang="ts">
import type { PagePermission } from '~/models/role'
import Error from '~/components/pub/my-ui/error/error.vue'
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
definePageMeta({
middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Tambah Surat Kontrol',
contentFrame: 'cf-full-width',
})
const route = useRoute()
useHead({
title: () => route.meta.title as string,
})
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
const { checkRole, getPagePermissions } = useRBAC()
// Check if user has access to this page
const hasAccess = checkRole(roleAccess)
// if (!hasAccess) {
// navigateTo('/403')
// }
// Define permission-based computed properties
const pagePermission = getPagePermissions(roleAccess)
const callbackUrl = route.query['return-path'] as string | undefined
</script>
<template>
<div>
<div v-if="pagePermission.canRead">
<ContentSurgeryReportEntry :callback-url="callbackUrl" />
</div>
<Error v-else :status-code="403" />
</div>
</template>
+47
View File
@@ -0,0 +1,47 @@
import { z } from 'zod'
const SurgeryReportSchema = z.object({
sepStatus: z.string({
required_error: 'Mohon isi status SEP',
}).default('SEP Internal'),
unit_code: z.string({
required_error: 'Mohon isi Unit',
}),
specialist_code: z.string({
required_error: 'Mohon isi Spesialis',
}),
subspecialist_code: z.string({
required_error: 'Mohon isi Sub Spesialis',
}),
doctor_code: z.string({
required_error: 'Mohon isi DPJP',
}),
encounter_code: z.string().optional(),
date: z.string({
required_error: 'Mohon lengkapi Tanggal Kontrol',
})
.refine(
(date) => {
// Jika kosong, return false untuk required validation
if (!date || date.trim() === '') return false
// Jika ada isi, validasi format tanggal
try {
const dateObj = new Date(date)
// Cek apakah tanggal valid dan tahun >= 1900
return !isNaN(dateObj.getTime()) && dateObj.getFullYear() >= 1900
} catch {
return false
}
},
{
message: 'Mohon lengkapi Tanggal Kontrol dengan format yang valid',
},
)
.transform((dateStr) => new Date(dateStr).toISOString()),
})
type SurgeryReportFormData = z.infer<typeof SurgeryReportSchema>
export { SurgeryReportSchema }
export type { SurgeryReportFormData }
+28
View File
@@ -0,0 +1,28 @@
// Base
import * as base from './_crud-base'
// Constants
import { encounterClassCodes } from '~/lib/constants'
const path = '/api/v1/surgery-report'
const name = 'surgery-report'
export function create(data: any) {
return base.create(path, data, name)
}
export function getList(params: any = null) {
return base.getList(path, params, name)
}
export function getDetail(id: number | string, params?: any) {
return base.getDetail(path, id, name, params)
}
export function update(id: number | string, data: any) {
return base.update(path, id, data, name)
}
export function remove(id: number | string) {
return base.remove(path, id, name)
}