feat: Add entry forms and schemas for item, item price, and encounter management.

This commit is contained in:
riefive
2025-12-04 14:36:04 +07:00
parent 952a3490da
commit ff22ce6848
7 changed files with 332 additions and 102 deletions
+1
View File
@@ -1,4 +1,5 @@
NUXT_MAIN_API_ORIGIN= NUXT_MAIN_API_ORIGIN=
NUXT_BPJS_API_ORIGIN= NUXT_BPJS_API_ORIGIN=
NUXT_API_VCLAIM_SWAGGER= # https://vclaim-api.multy.chat
NUXT_SYNC_API_ORIGIN= NUXT_SYNC_API_ORIGIN=
NUXT_API_ORIGIN= NUXT_API_ORIGIN=
+3 -3
View File
@@ -348,7 +348,7 @@ defineExpose({
placeholder="Pilih Dokter" placeholder="Pilih Dokter"
search-placeholder="Cari Dokter" search-placeholder="Cari Dokter"
empty-message="Dokter tidak ditemukan" empty-message="Dokter tidak ditemukan"
@update:model-value="(value) => emit('onSelectDoctor', value)" @update:model-value="(value: any) => emit('onSelectDoctor', value)"
/> />
</DE.Field> </DE.Field>
</DE.Cell> </DE.Cell>
@@ -576,7 +576,7 @@ defineExpose({
</span> </span>
<p v-if="sepFileReview"> <p v-if="sepFileReview">
<a <a
class="mt-1 text-sm text-blue-500 capitalize" class="mt-1 text-sm capitalize text-blue-500"
href="#" href="#"
@click="openFile(sepFileReview.filePath)" @click="openFile(sepFileReview.filePath)"
> >
@@ -601,7 +601,7 @@ defineExpose({
</span> </span>
<p v-if="sippFileReview"> <p v-if="sippFileReview">
<a <a
class="mt-1 text-sm text-blue-500 capitalize" class="mt-1 text-sm capitalize text-blue-500"
href="#" href="#"
@click="openFile(sippFileReview.filePath)" @click="openFile(sippFileReview.filePath)"
> >
+125 -39
View File
@@ -1,50 +1,136 @@
<script setup lang="ts"> <script setup lang="ts">
import Block from '~/components/pub/my-ui/form/block.vue' // Components
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue' import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Field from '~/components/pub/my-ui/form/field.vue' import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Label from '~/components/pub/my-ui/form/label.vue' import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
const props = defineProps<{ modelValue: any }>() // Types
const emit = defineEmits(['update:modelValue', 'event']) import type { ItemPriceFormData } from '~/schemas/item-price.schema'
const data = computed({ // Helpers
get: () => props.modelValue, import type z from 'zod'
set: (val) => emit('update:modelValue', val), import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
interface Props {
schema: z.ZodSchema<any>
values: any
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const isLoading = props.isLoading !== undefined ? props.isLoading : false
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
const emit = defineEmits<{
submit: [values: ItemPriceFormData, resetForm: () => void]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: {
item_code: '',
price: 0,
insuranceCompany_code: '',
} as Partial<ItemPriceFormData>,
}) })
const items = [ const [item_code, item_codeAttrs] = defineField('item_code')
{ value: '1', label: 'item 1' }, const [price, priceAttrs] = defineField('price')
{ value: '2', label: 'item 2' }, const [insuranceCompany_code, insuranceCompany_codeAttrs] = defineField('insuranceCompany_code')
{ value: '3', label: 'item 3' },
{ value: '4', label: 'item 4' }, if (props.values) {
] if (props.values.item_code !== undefined) item_code.value = props.values.item_code
if (props.values.price !== undefined) price.value = props.values.price
if (props.values.insuranceCompany_code !== undefined) insuranceCompany_code.value = props.values.insuranceCompany_code
}
const resetForm = () => {
item_code.value = ''
price.value = 0
insuranceCompany_code.value = ''
}
function onSubmitForm() {
const formData: ItemPriceFormData = {
item_code: item_code.value || '',
price: Number(price.value) || 0,
insuranceCompany_code: insuranceCompany_code.value || '',
}
emit('submit', formData, resetForm)
}
function onCancelForm() {
emit('cancel', resetForm)
}
</script> </script>
<template> <template>
<form id="entry-form"> <form
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl"> id="form-item-price"
<div class="flex flex-col justify-between"> @submit.prevent
<Block> >
<FieldGroup> <Block
<Label>Items</Label> labelSize="thin"
<Field> class="!mb-2.5 !pt-0 xl:!mb-3"
<Select :items="items" /> :colCount="1"
</Field> >
</FieldGroup> <Cell>
<FieldGroup> <Label height="compact">Item</Label>
<Label>Perusahaan Insuransi</Label> <Field :errMessage="errors.item_code">
<Field> <Input
<Select :items="items" /> id="item_code"
</Field> v-model="item_code"
</FieldGroup> v-bind="item_codeAttrs"
<FieldGroup> :disabled="isLoading || isReadonly"
<Label>Harga</Label> />
<Field> </Field>
<Input v-model="data.price" /> </Cell>
</Field> <Cell>
</FieldGroup> <Label height="compact">Harga</Label>
</Block> <Field :errMessage="errors.price">
</div> <Input
id="price"
type="number"
v-model="price"
v-bind="priceAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Perusahaan Asuransi</Label>
<Field :errMessage="errors.insuranceCompany_code">
<Input
id="insuranceCompany_code"
v-model="insuranceCompany_code"
v-bind="insuranceCompany_codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
class="w-[120px]"
:disabled="isLoading || !meta.valid"
@click="onSubmitForm"
>
Simpan
</Button>
</div> </div>
</form> </form>
</template> </template>
+173 -57
View File
@@ -1,68 +1,184 @@
<script setup lang="ts"> <script setup lang="ts">
import Block from '~/components/pub/my-ui/form/block.vue' // Components
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue' import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Field from '~/components/pub/my-ui/form/field.vue' import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Label from '~/components/pub/my-ui/form/label.vue' import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
const props = defineProps<{ modelValue: any }>() // Types
const emit = defineEmits(['update:modelValue', 'event']) import type { ItemFormData } from '~/schemas/item.schema'
const data = computed({ // Helpers
get: () => props.modelValue, import type z from 'zod'
set: (val) => emit('update:modelValue', val), import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
interface Props {
schema: z.ZodSchema<any>
values: any
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const isLoading = props.isLoading !== undefined ? props.isLoading : false
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
const emit = defineEmits<{
submit: [values: ItemFormData, resetForm: () => void]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: {
code: '',
name: '',
itemGroup_code: '',
uom_code: '',
infra_code: '',
stock: 0,
} as Partial<ItemFormData>,
}) })
const items = [ const [code, codeAttrs] = defineField('code')
{ value: '1', label: 'item 1' }, const [name, nameAttrs] = defineField('name')
{ value: '2', label: 'item 2' }, const [itemGroup_code, itemGroup_codeAttrs] = defineField('itemGroup_code')
{ value: '3', label: 'item 3' }, const [uom_code, uom_codeAttrs] = defineField('uom_code')
{ value: '4', label: 'item 4' }, const [infra_code, infra_codeAttrs] = defineField('infra_code')
] const [stock, stockAttrs] = defineField('stock')
if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.itemGroup_code !== undefined) itemGroup_code.value = props.values.itemGroup_code
if (props.values.uom_code !== undefined) uom_code.value = props.values.uom_code
if (props.values.infra_code !== undefined) infra_code.value = props.values.infra_code
if (props.values.stock !== undefined) stock.value = props.values.stock
}
const resetForm = () => {
code.value = ''
name.value = ''
itemGroup_code.value = ''
uom_code.value = ''
infra_code.value = ''
stock.value = 0
}
function onSubmitForm() {
const formData: ItemFormData = {
code: code.value || '',
name: name.value || '',
itemGroup_code: itemGroup_code.value || '',
uom_code: uom_code.value || '',
infra_code: infra_code.value || '',
stock: Number(stock.value) || 0,
}
emit('submit', formData, resetForm)
}
function onCancelForm() {
emit('cancel', resetForm)
}
</script> </script>
<template> <template>
<form id="entry-form"> <form
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl"> id="form-item"
<div class="flex flex-col justify-between"> @submit.prevent
<Block> >
<FieldGroup> <Block
<Label>Nama</Label> labelSize="thin"
<Field> class="!mb-2.5 !pt-0 xl:!mb-3"
<Input v-model="data.name" /> :colCount="1"
</Field> >
</FieldGroup> <Cell>
<FieldGroup> <Label height="compact">Kode</Label>
<Label>Kode</Label> <Field :errMessage="errors.code">
<Field> <Input
<Input v-model="data.code" /> id="code"
</Field> v-model="code"
</FieldGroup> v-bind="codeAttrs"
<FieldGroup> :disabled="isLoading || isReadonly"
<Label>Item Group</Label> />
<Field> </Field>
<Select :items="items" /> </Cell>
</Field> <Cell>
</FieldGroup> <Label height="compact">Nama</Label>
<FieldGroup> <Field :errMessage="errors.name">
<Label>UOM</Label> <Input
<Field> id="name"
<Select :items="items" /> v-model="name"
</Field> v-bind="nameAttrs"
</FieldGroup> :disabled="isLoading || isReadonly"
<FieldGroup> />
<Label>Infra</Label> </Field>
<Field> </Cell>
<Select :items="items" /> <Cell>
</Field> <Label height="compact">Item Group</Label>
</FieldGroup> <Field :errMessage="errors.itemGroup_code">
<FieldGroup> <Input
<Label>Harga</Label> id="itemGroup_code"
<Field> v-model="itemGroup_code"
<Input v-model="data.price" /> v-bind="itemGroup_codeAttrs"
</Field> :disabled="isLoading || isReadonly"
</FieldGroup> />
</Block> </Field>
</div> </Cell>
<Cell>
<Label height="compact">UOM</Label>
<Field :errMessage="errors.uom_code">
<Input
id="uom_code"
v-model="uom_code"
v-bind="uom_codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Infra</Label>
<Field :errMessage="errors.infra_code">
<Input
id="infra_code"
v-model="infra_code"
v-bind="infra_codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Stok</Label>
<Field :errMessage="errors.stock">
<Input
id="stock"
type="number"
v-model="stock"
v-bind="stockAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
class="w-[120px]"
:disabled="isLoading || !meta.valid"
@click="onSubmitForm"
>
Simpan
</Button>
</div> </div>
</form> </form>
</template> </template>
+3 -3
View File
@@ -107,8 +107,8 @@ const protocolRows = [
{ value: 'prescription', label: 'Order Obat', component: Prescription, props: { encounter_id: data.value.id } }, { value: 'prescription', label: 'Order Obat', component: Prescription, props: { encounter_id: data.value.id } },
{ value: 'device-order', label: 'Order Alkes', component: DeviceOrder, props: { encounter_id: data.value.id } }, { value: 'device-order', label: 'Order Alkes', component: DeviceOrder, props: { encounter_id: data.value.id } },
{ value: 'device', label: 'Order Alkes' }, { value: 'device', label: 'Order Alkes' },
{ value: 'mcu-radiology', label: 'Order Radiologi', component: Radiology, props: { encounter_id: data.id } }, { value: 'mcu-radiology', label: 'Order Radiologi', component: Radiology, props: { encounter_id: data.value.id } },
{ value: 'mcu-lab-cp', label: 'Order Lab PK', component: CpLabOrder, props: { encounter_id: data.id } }, { value: 'mcu-lab-cp', label: 'Order Lab PK', component: CpLabOrder, props: { encounter_id: data.value.id } },
{ value: 'mcu-lab-micro', label: 'Order Lab Mikro' }, { value: 'mcu-lab-micro', label: 'Order Lab Mikro' },
{ value: 'mcu-lab-pa', label: 'Order Lab PA' }, { value: 'mcu-lab-pa', label: 'Order Lab PA' },
{ value: 'medical-action', label: 'Order Ruang Tindakan' }, { value: 'medical-action', label: 'Order Ruang Tindakan' },
@@ -214,4 +214,4 @@ async function getData() {
@change-menu="activeMenu = $event" @change-menu="activeMenu = $event"
/> />
</div> </div>
</template> </template>
+12
View File
@@ -0,0 +1,12 @@
import { z } from 'zod'
const ItemPriceSchema = z.object({
item_code: z.string({ required_error: 'Item harus diisi' }).min(1, 'Item harus diisi'),
price: z.number({ required_error: 'Harga harus diisi' }).min(0, 'Harga tidak boleh kurang dari 0'),
insuranceCompany_code: z.string({ required_error: 'Perusahaan Asuransi harus diisi' }).min(1, 'Perusahaan Asuransi harus diisi'),
})
type ItemPriceFormData = z.infer<typeof ItemPriceSchema>
export { ItemPriceSchema }
export type { ItemPriceFormData }
+15
View File
@@ -0,0 +1,15 @@
import { z } from 'zod'
const ItemSchema = z.object({
code: z.string({ required_error: 'Kode harus diisi' }).min(1, 'Kode minimum 1 karakter'),
name: z.string({ required_error: 'Nama harus diisi' }).min(1, 'Nama minimum 1 karakter'),
itemGroup_code: z.string({ required_error: 'Item Group harus diisi' }).min(1, 'Item Group harus diisi'),
uom_code: z.string({ required_error: 'UOM harus diisi' }).min(1, 'UOM harus diisi'),
infra_code: z.string({ required_error: 'Infra harus diisi' }).min(1, 'Infra harus diisi'),
stock: z.number({ required_error: 'Stok harus diisi' }).min(0, 'Stok tidak boleh kurang dari 0'),
})
type ItemFormData = z.infer<typeof ItemSchema>
export { ItemSchema }
export type { ItemFormData }