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_BPJS_API_ORIGIN=
NUXT_API_VCLAIM_SWAGGER= # https://vclaim-api.multy.chat
NUXT_SYNC_API_ORIGIN=
NUXT_API_ORIGIN=
+3 -3
View File
@@ -348,7 +348,7 @@ defineExpose({
placeholder="Pilih Dokter"
search-placeholder="Cari Dokter"
empty-message="Dokter tidak ditemukan"
@update:model-value="(value) => emit('onSelectDoctor', value)"
@update:model-value="(value: any) => emit('onSelectDoctor', value)"
/>
</DE.Field>
</DE.Cell>
@@ -576,7 +576,7 @@ defineExpose({
</span>
<p v-if="sepFileReview">
<a
class="mt-1 text-sm text-blue-500 capitalize"
class="mt-1 text-sm capitalize text-blue-500"
href="#"
@click="openFile(sepFileReview.filePath)"
>
@@ -601,7 +601,7 @@ defineExpose({
</span>
<p v-if="sippFileReview">
<a
class="mt-1 text-sm text-blue-500 capitalize"
class="mt-1 text-sm capitalize text-blue-500"
href="#"
@click="openFile(sippFileReview.filePath)"
>
+125 -39
View File
@@ -1,50 +1,136 @@
<script setup lang="ts">
import Block from '~/components/pub/my-ui/form/block.vue'
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
import Field from '~/components/pub/my-ui/form/field.vue'
import Label from '~/components/pub/my-ui/form/label.vue'
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.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 }>()
const emit = defineEmits(['update:modelValue', 'event'])
// Types
import type { ItemPriceFormData } from '~/schemas/item-price.schema'
const data = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
// Helpers
import type z from 'zod'
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 = [
{ value: '1', label: 'item 1' },
{ value: '2', label: 'item 2' },
{ value: '3', label: 'item 3' },
{ value: '4', label: 'item 4' },
]
const [item_code, item_codeAttrs] = defineField('item_code')
const [price, priceAttrs] = defineField('price')
const [insuranceCompany_code, insuranceCompany_codeAttrs] = defineField('insuranceCompany_code')
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>
<template>
<form id="entry-form">
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<div class="flex flex-col justify-between">
<Block>
<FieldGroup>
<Label>Items</Label>
<Field>
<Select :items="items" />
</Field>
</FieldGroup>
<FieldGroup>
<Label>Perusahaan Insuransi</Label>
<Field>
<Select :items="items" />
</Field>
</FieldGroup>
<FieldGroup>
<Label>Harga</Label>
<Field>
<Input v-model="data.price" />
</Field>
</FieldGroup>
</Block>
</div>
<form
id="form-item-price"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Item</Label>
<Field :errMessage="errors.item_code">
<Input
id="item_code"
v-model="item_code"
v-bind="item_codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Harga</Label>
<Field :errMessage="errors.price">
<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>
</form>
</template>
+173 -57
View File
@@ -1,68 +1,184 @@
<script setup lang="ts">
import Block from '~/components/pub/my-ui/form/block.vue'
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
import Field from '~/components/pub/my-ui/form/field.vue'
import Label from '~/components/pub/my-ui/form/label.vue'
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.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 }>()
const emit = defineEmits(['update:modelValue', 'event'])
// Types
import type { ItemFormData } from '~/schemas/item.schema'
const data = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
// Helpers
import type z from 'zod'
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 = [
{ value: '1', label: 'item 1' },
{ value: '2', label: 'item 2' },
{ value: '3', label: 'item 3' },
{ value: '4', label: 'item 4' },
]
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [itemGroup_code, itemGroup_codeAttrs] = defineField('itemGroup_code')
const [uom_code, uom_codeAttrs] = defineField('uom_code')
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>
<template>
<form id="entry-form">
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<div class="flex flex-col justify-between">
<Block>
<FieldGroup>
<Label>Nama</Label>
<Field>
<Input v-model="data.name" />
</Field>
</FieldGroup>
<FieldGroup>
<Label>Kode</Label>
<Field>
<Input v-model="data.code" />
</Field>
</FieldGroup>
<FieldGroup>
<Label>Item Group</Label>
<Field>
<Select :items="items" />
</Field>
</FieldGroup>
<FieldGroup>
<Label>UOM</Label>
<Field>
<Select :items="items" />
</Field>
</FieldGroup>
<FieldGroup>
<Label>Infra</Label>
<Field>
<Select :items="items" />
</Field>
</FieldGroup>
<FieldGroup>
<Label>Harga</Label>
<Field>
<Input v-model="data.price" />
</Field>
</FieldGroup>
</Block>
</div>
<form
id="form-item"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Item Group</Label>
<Field :errMessage="errors.itemGroup_code">
<Input
id="itemGroup_code"
v-model="itemGroup_code"
v-bind="itemGroup_codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</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>
</form>
</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: 'device-order', label: 'Order Alkes', component: DeviceOrder, props: { encounter_id: data.value.id } },
{ value: 'device', label: 'Order Alkes' },
{ value: 'mcu-radiology', label: 'Order Radiologi', component: Radiology, props: { encounter_id: data.id } },
{ value: 'mcu-lab-cp', label: 'Order Lab PK', component: CpLabOrder, 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.value.id } },
{ value: 'mcu-lab-micro', label: 'Order Lab Mikro' },
{ value: 'mcu-lab-pa', label: 'Order Lab PA' },
{ value: 'medical-action', label: 'Order Ruang Tindakan' },
@@ -214,4 +214,4 @@ async function getData() {
@change-menu="activeMenu = $event"
/>
</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 }