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_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=
|
||||||
|
|||||||
@@ -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)"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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' },
|
||||||
|
|||||||
@@ -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