feat: Add entry forms and schemas for item, item price, and encounter management.
This commit is contained in:
@@ -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=
|
||||
|
||||
@@ -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)"
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }
|
||||
@@ -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 }
|
||||
Reference in New Issue
Block a user