Merge branch 'dev' into fe-prescription-56

This commit is contained in:
Andrian Roshandy
2025-10-25 15:36:29 +07:00
16 changed files with 720 additions and 13 deletions
@@ -0,0 +1,107 @@
<script lang="ts" setup>
//
import { LucideCheck } from 'lucide-vue-next';
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
// Components
import type z from 'zod'
import ComboBox from '~/components/pub/my-ui/combobox/combobox.vue'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { CheckInFormData } from '~/schemas/encounter.schema'
import type { Encounter } from '~/models/encounter'
interface Props {
schema: z.ZodSchema<any>
values: any
doctors: { value: string; label: string }[]
employees: { value: string; label: string }[]
encounter: Encounter
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const emit = defineEmits<{
submit: [values: CheckInFormData]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: {
responsible_doctor_id: 0,
adm_employee_id: 0,
registeredAt: props.values.values?.registeredAt || '',
} as Partial<CheckInFormData>,
})
const [responsible_doctor_id, responsible_doctor_idAttrs] = defineField('responsible_doctor_id')
const [adm_employee_id, adm_employee_idAttrs] = defineField('discharge_method_code')
const [registeredAt, registeredAtAttrs] = defineField('registeredAt')
function submitForm() {
const formData: CheckInFormData = {
responsible_doctor_id: responsible_doctor_id.value,
adm_employee_id: adm_employee_id.value,
// registeredAt: registeredAt.value || '',
}
emit('submit', formData)
}
</script>
<template>
<DE.Block :cell-flex="false">
<DE.Cell>
<DE.Label>Dokter</DE.Label>
<DE.Field>
<ComboBox
id="doctor"
v-model="responsible_doctor_id"
v-bind="responsible_doctor_idAttrs"
:items="doctors"
:disabled="isLoading || isReadonly"
placeholder="Pilih Dokter DPJP"
search-placeholder="Pilih DPJP"
empty-message="DPJP tidak ditemukan"
/>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label>PJ Berkas</DE.Label>
<DE.Field>
<ComboBox
id="doctor"
v-model="adm_employee_id"
v-bind="adm_employee_idAttrs"
:items="employees"
:disabled="isLoading || isReadonly"
placeholder="Pilih Dokter DPJP"
search-placeholder="Pilih petugas"
empty-message="Petugas tidak ditemukan"
/>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label>Waktu Masuk</DE.Label>
<DE.Field>
<Input
id="name"
v-model="registeredAt"
v-bind="registeredAtAttrs"
:disabled="isLoading || isReadonly"
/>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center">
<Button @click="submitForm">
<LucideCheck />
Simpan
</Button>
</div>
</template>
<style>
</style>
@@ -0,0 +1,56 @@
<script lang="ts" setup>
// Components
import { LucidePen } from 'lucide-vue-next';
import * as DE from '~/components/pub/my-ui/doc-entry'
import Input from '~/components/pub/ui/input/Input.vue';
import type { Encounter } from '~/models/encounter'
interface Props {
encounter: Encounter
}
const props = defineProps<Props>()
const doctor = ref('-belum dipilih-')
const adm = ref('-belum dipilih-')
const emit = defineEmits<{
edit: []
}>()
watch(props.encounter, () => {
doctor.value = props.encounter.responsible_doctor?.employee?.person?.name ?? props.encounter.appointment_doctor?.employee?.person?.name ?? '-belum dipilih-'
adm.value = props.encounter.adm_employee?.person?.name ?? '-belum dipilih-'
})
</script>
<template>
<DE.Block :cell-flex="false">
<DE.Cell>
<DE.Label class="font-semibold">Dokter</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ doctor }}</div>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label class="font-semibold">PJ Berkas</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ adm }}</div>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label class="font-semibold">Waktu Masuk</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ encounter?.registeredAt || '-' }}</div>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center">
<Button @click="() => emit('edit')">
<LucidePen />
Edit
</Button>
</div>
</template>
<style>
</style>
@@ -0,0 +1,187 @@
<script lang="ts" setup>
//
import { LucideCheck } from 'lucide-vue-next';
import { useForm, useFieldArray } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
//
import type z from 'zod'
import * as CB from '~/components/pub/my-ui/combobox'
import { dischargeMethodCodes } from '~/lib/constants';
import type {
CheckOutFormData,
CheckOutDeathFormData,
CheckOutInternalReferenceFormData
} from '~/schemas/encounter.schema'
import * as Table from '~/components/pub/ui/table'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { InternalReference, CreateDto as InternalReferenceCreateDto } from '~/models/internal-reference';
interface Props {
schema: z.ZodSchema<any>
values: any
units: any[]
doctors: any[]
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const dischargeMethodItems = CB.recStrToItem(dischargeMethodCodes)
const emit = defineEmits<{
submit: [values: CheckOutFormData]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: {
discharge_method_code: '',
discharge_date: '',
internalReferences: [{ unit_id: 0, doctor_id: 0 }],
deathCauses: [""],
} as Partial<CheckOutFormData>,
})
const [ discharge_method_code, discharge_method_codeAttrs ] = defineField('discharge_method_code')
const [ discharge_date, discharge_dateAttrs ] = defineField('discharge_date')
const { fields, push, remove } = useFieldArray<InternalReferenceCreateDto>('internalReferences');
function submitForm(values: any) {
if (['consul-poly', 'consul-executive'].includes(discharge_method_code.value)) {
const formData: CheckOutInternalReferenceFormData = {
discharge_method_code: discharge_method_code.value,
discharge_date: discharge_date.value,
internalReferences: [{ unit_id: 0, doctor_id: 0 }],
}
emit('submit', formData)
} else if (discharge_method_code.value === 'death') {
const formData: CheckOutDeathFormData = {
discharge_method_code: discharge_method_code.value,
discharge_date: discharge_date.value,
death_cause: [""],
}
emit('submit', formData)
} else {
const formData: CheckOutFormData = {
discharge_method_code: discharge_method_code.value,
discharge_date: discharge_date.value,
}
emit('submit', formData)
}
}
const resetForm = () => {
discharge_method_code.value = ''
discharge_date.value = ''
}
</script>
<template>
<DE.Block :cellFlex="false">
<DE.Cell>
<DE.Label>Alasan Keluar</DE.Label>
<DE.Field>
<CB.Combobox
id="dischargeMethodItems"
v-model="discharge_method_code"
v-bind="discharge_method_codeAttrs"
:items="dischargeMethodItems"
:disabled="isLoading || isReadonly"
placeholder="Pilih Cara Keluar"
search-placeholder="Cari Cara Keluar"
empty-message="Cara Keluar tidak ditemukan"
/>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label>Waktu Keluar</DE.Label>
<DE.Cell>
<Input
id="discharge_date"
v-model="discharge_date"
v-bind="discharge_dateAttrs"
:disabled="isLoading || isReadonly"
/>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="'death' == discharge_method_code">
<DE.Label>Sebab Meninggal</DE.Label>
<DE.Cell>
<div class="mb-3">
<Input />
</div>
<div>
<Button
v-if="!isReadonly"
type="button"
:disabled="isLoading || !meta.valid"
@click="submitForm"
>
Tambah Sebab meninggal
</Button>
</div>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="['consul-poly', 'consul-executive'].includes(discharge_method_code)">
<DE.Label>Tujuan</DE.Label>
<DE.Field>
<Table.Table class="border mb-3">
<Table.TableHeader class="bg-neutral-100 dark:bg-slate-800">
<Table.TableCell class="text-center">Poly</Table.TableCell>
<Table.TableCell class="text-center">DPJP</Table.TableCell>
<Table.TableCell class="text-center !w-10"></Table.TableCell>
</Table.TableHeader>
<Table.TableBody>
<Table.TableRow v-for="(item, index) in fields" :key="index">
<Table.TableCell class="!p-0.5">
<CB.Combobox
id="dischargeMethodItems"
:v-model.number="item.value.unit_id"
:items="units"
:disabled="isLoading || isReadonly"
placeholder="Pilih Poly"
search-placeholder="Cari Poly"
empty-message="Poly tidak ditemukan"
/>
</Table.TableCell>
<Table.TableCell class="!p-0.5">
<CB.Combobox
id="dischargeMethodItems"
:v-model.number="item.value.doctor_id"
:items="units"
:disabled="isLoading || isReadonly"
placeholder="Pilih Dokter"
search-placeholder="Pilih Dokter"
empty-message="Dokter tidak ditemukan"
/>
</Table.TableCell>
<Table.TableCell>
<Button variant="destructive" size="xs" @click="remove(index)" class="w-6 h-6 rounded-full">
X
</Button>
</Table.TableCell>
</Table.TableRow>
</Table.TableBody>
</Table.Table>
<div>
<Button @click="push({ encounter_id: 0, unit_id: 0, doctor_id: 0 })">Tambah</Button>
</div>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center">
<Button @click="submitForm">>
<LucideCheck />
Simpan
</Button>
</div>
</template>
<style>
</style>
@@ -0,0 +1,90 @@
<script lang="ts" setup>
//
import { LucidePen, LucideCheck } from 'lucide-vue-next';
//
import * as CB from '~/components/pub/my-ui/combobox'
import { dischargeMethodCodes } from '~/lib/constants';
import * as Table from '~/components/pub/ui/table'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Encounter } from '~/models/encounter';
interface Props {
encounter: Encounter
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const dischargeMethodItems = CB.recStrToItem(dischargeMethodCodes)
const emit = defineEmits<{
edit: [],
finish: []
}>()
</script>
<template>
<DE.Block :cellFlex="false">
<DE.Cell>
<DE.Label class="font-semibold">Alasan Keluar</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ encounter.discharge_method_code || '-belum ditentukan-' }}</div>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label class="font-semibold">Waktu Keluar</DE.Label>
<DE.Cell>
<div class="py-2 border-b border-b-slate-300">{{ encounter.discharge_date || '-belum ditentukan-' }}</div>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="'death' == encounter.discharge_method_code">
<DE.Label class="font-semibold">Sebab Meninggal</DE.Label>
<DE.Cell>
<div class="mb-3">
<div class="py-2 border-b border-b-slate-300">{{ encounter.discharge_date }}</div>
</div>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="['consul-poly', 'consul-executive'].includes(encounter.discharge_method_code || '')">
<DE.Label class="font-semibold">Tujuan</DE.Label>
<DE.Field>
<Table.Table class="border mb-3">
<Table.TableHeader class="bg-neutral-100 dark:bg-slate-800">
<Table.TableCell class="text-center">Poly</Table.TableCell>
<Table.TableCell class="text-center">DPJP</Table.TableCell>
<Table.TableCell class="text-center !w-10"></Table.TableCell>
</Table.TableHeader>
<Table.TableBody>
<Table.TableRow v-for="(item, index) in encounter.internalReferences" :key="index">
<Table.TableCell class="!p-0.5">
</Table.TableCell>
<Table.TableCell class="!p-0.5">
</Table.TableCell>
<Table.TableCell>
</Table.TableCell>
</Table.TableRow>
</Table.TableBody>
</Table.Table>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center [&>*]:mx-1">
<Button @click="() => emit('edit')">
<LucidePen />
Edit
</Button>
<Button @click="() => emit('finish')">
<LucideCheck />
Selesai
</Button>
</div>
</template>
<style>
</style>
-6
View File
@@ -1,6 +0,0 @@
<script setup lang="ts">
</script>
<template>
<div class="p-10 text-center">Hello World!!!</div>
</template>
+5 -3
View File
@@ -5,14 +5,16 @@ import { useRoute, useRouter } from 'vue-router'
import { getDetail } from '~/services/encounter.service'
// Components
//
import type { TabItem } from '~/components/pub/my-ui/comp-tab/type'
import CompTab from '~/components/pub/my-ui/comp-tab/comp-tab.vue'
import AssesmentFunctionList from '~/components/content/soapi/entry.vue'
// PLASE ORDER BY TAB POSITION
import Status from '~/components/content/encounter/status.vue'
import AssesmentFunctionList from '~/components/content/assesment-function/list.vue'
import EarlyMedicalAssesmentList from '~/components/content/soapi/entry.vue'
import EarlyMedicalRehabList from '~/components/content/soapi/entry.vue'
import Prescription from '~/components/content/prescription/main.vue'
import Status from '~/components/app/encounter/status.vue'
import Consultation from '~/components/content/consultation/list.vue'
const route = useRoute()
+125
View File
@@ -0,0 +1,125 @@
<script setup lang="ts">
//
import { getValueLabelList as getDoctorValueLabelList } from '~/services/doctor.service'
import { getValueLabelList as getEmployeeValueLabelList } from '~/services/employee.service'
import { getValueLabelList as getUnitValueLabelList } from '~/services/unit.service'
import type { CheckInFormData, CheckOutFormData } from '~/schemas/encounter.schema'
import { CheckInSchema, CheckOutSchema } from '~/schemas/encounter.schema'
//
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import CheckInView from '~/components/app/encounter/check-in-view.vue'
import CheckInEntry from '~/components/app/encounter/check-in-entry.vue'
import CheckOutView from '~/components/app/encounter/check-out-view.vue'
import CheckOutEntry from '~/components/app/encounter/check-out-entry.vue'
import type { Encounter } from '~/models/encounter'
import { checkIn } from '~/services/encounter.service'
//
const props = defineProps<{
encounter: Encounter
}>()
// doctors
const doctors = await getDoctorValueLabelList({'includes': 'employee,employee-person'})
const employees = await getEmployeeValueLabelList({'includes': 'person', 'position-code': 'reg'})
const units = await getUnitValueLabelList()
// check in
const checkInValues = ref<any>({
discharge_method_code: '',
responsible_doctor_id: 0,
// registeredAt: '',
})
const checkInIsLoading = ref(false)
const checkInIsReadonly = ref(false)
const checkInDialogOpen = ref(false)
// check out
const checkOutValues = ref<any>({
dischargeMethod_code: '',
unit_id: 0,
responsibleDoctor_id: 0,
})
const checkOutIsLoading = ref(false)
const checkOutIsReadonly = ref(false)
const checkOutDialogOpen = ref(false)
function editCheckIn() {
checkInDialogOpen.value = true
}
function submitCheckIn(values: CheckInFormData) {
checkIn(props.encounter.id, values)
}
function editCheckOut() {
checkOutDialogOpen.value = true
}
function submitCheckOut(values: CheckOutFormData) {
console.log(values)
}
</script>
<template>
<div class="lg:grid grid-cols-2">
<div class="border-r lg:pe-4 xl:pe-5 mb-10">
<div class="mb-4 xl:mb-5 text-base text-center font-semibold">Informasi Masuk</div>
<CheckInView
:encounter="encounter"
:is-loading="checkInIsLoading"
:is-readonly="checkInIsReadonly"
@edit="editCheckIn"
/>
</div>
<div class="lg:ps-4 xl:ps-5">
<Separator class="lg:hidden my-4 xl:my-5" />
<div class="mb-4 xl:mb-5 text-base text-center font-semibold">Informasi Keluar</div>
<CheckOutView
:encounter="encounter"
:is-loading="checkOutIsLoading"
:is-readonly="checkOutIsReadonly"
@edit="editCheckOut"
/>
</div>
</div>
<Dialog
v-model:open="checkInDialogOpen"
title="Ubah Informasi Masuk"
size="md"
prevent-outside
>
<CheckInEntry
:schema="CheckInSchema"
:values="checkInValues"
:encounter="encounter"
:doctors="doctors"
:employees="employees"
:is-loading="checkInIsLoading"
:is-readonly="checkInIsReadonly"
@submit="submitCheckIn"
@cancel="checkInDialogOpen = false"
/>
</Dialog>
<Dialog
v-model:open="checkOutDialogOpen"
title="Ubah Informasi Keluar"
size="lg"
prevent-outside
>
<CheckOutEntry
:schema="CheckOutSchema"
:values="checkOutValues"
:encounter="encounter"
:units="units"
:doctors="doctors"
:is-loading="checkInIsLoading"
:is-readonly="checkInIsReadonly"
@submit="submitCheckOut"
@cancel="checkInDialogOpen = false"
/>
</Dialog>
</template>