From ff22ce684824fb2ef20d21f51eecf1262efad18e Mon Sep 17 00:00:00 2001 From: riefive Date: Thu, 4 Dec 2025 14:36:04 +0700 Subject: [PATCH 01/11] feat: Add entry forms and schemas for item, item price, and encounter management. --- .env.example | 1 + app/components/app/encounter/entry-form.vue | 6 +- app/components/app/item-price/entry-form.vue | 164 +++++++++---- app/components/app/item/entry-form.vue | 230 ++++++++++++++----- app/components/content/encounter/process.vue | 6 +- app/schemas/item-price.schema.ts | 12 + app/schemas/item.schema.ts | 15 ++ 7 files changed, 332 insertions(+), 102 deletions(-) create mode 100644 app/schemas/item-price.schema.ts create mode 100644 app/schemas/item.schema.ts diff --git a/.env.example b/.env.example index 884ee1fb..864bbbe6 100644 --- a/.env.example +++ b/.env.example @@ -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= diff --git a/app/components/app/encounter/entry-form.vue b/app/components/app/encounter/entry-form.vue index 9950a3dd..a331dc63 100644 --- a/app/components/app/encounter/entry-form.vue +++ b/app/components/app/encounter/entry-form.vue @@ -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)" /> @@ -576,7 +576,7 @@ defineExpose({

@@ -601,7 +601,7 @@ defineExpose({

diff --git a/app/components/app/item-price/entry-form.vue b/app/components/app/item-price/entry-form.vue index f2f55d76..b7b79cc6 100644 --- a/app/components/app/item-price/entry-form.vue +++ b/app/components/app/item-price/entry-form.vue @@ -1,50 +1,136 @@ diff --git a/app/components/app/item/entry-form.vue b/app/components/app/item/entry-form.vue index 7ddc930b..67f12f50 100644 --- a/app/components/app/item/entry-form.vue +++ b/app/components/app/item/entry-form.vue @@ -1,68 +1,184 @@ diff --git a/app/components/content/encounter/process.vue b/app/components/content/encounter/process.vue index b298c92f..c5e0a5e4 100644 --- a/app/components/content/encounter/process.vue +++ b/app/components/content/encounter/process.vue @@ -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" /> - \ No newline at end of file + diff --git a/app/schemas/item-price.schema.ts b/app/schemas/item-price.schema.ts new file mode 100644 index 00000000..a9408a9c --- /dev/null +++ b/app/schemas/item-price.schema.ts @@ -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 + +export { ItemPriceSchema } +export type { ItemPriceFormData } diff --git a/app/schemas/item.schema.ts b/app/schemas/item.schema.ts new file mode 100644 index 00000000..11b597e0 --- /dev/null +++ b/app/schemas/item.schema.ts @@ -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 + +export { ItemSchema } +export type { ItemFormData } From 413a3c2b2952780ffe8c3aadf93096e2e6aa934b Mon Sep 17 00:00:00 2001 From: riefive Date: Thu, 4 Dec 2025 14:40:59 +0700 Subject: [PATCH 02/11] feat: Add item and item price list components with their respective configurations. --- app/components/app/item-price/list-cfg.ts | 24 +++++-------- app/components/app/item-price/list.vue | 35 ++++++++++++++++--- app/components/app/item-price/picker.vue | 0 app/components/app/item-price/search.vue | 0 .../app/item-price/status-badge.vue | 29 --------------- app/components/app/item/list-cfg.ts | 19 ++++------ app/components/app/item/list.vue | 35 ++++++++++++++++--- app/components/app/item/picker.vue | 0 app/components/app/item/search.vue | 0 app/components/app/item/status-badge.vue | 29 --------------- 10 files changed, 75 insertions(+), 96 deletions(-) delete mode 100644 app/components/app/item-price/picker.vue delete mode 100644 app/components/app/item-price/search.vue delete mode 100644 app/components/app/item-price/status-badge.vue delete mode 100644 app/components/app/item/picker.vue delete mode 100644 app/components/app/item/search.vue delete mode 100644 app/components/app/item/status-badge.vue diff --git a/app/components/app/item-price/list-cfg.ts b/app/components/app/item-price/list-cfg.ts index c3db4f50..f1ebbd13 100644 --- a/app/components/app/item-price/list-cfg.ts +++ b/app/components/app/item-price/list-cfg.ts @@ -3,27 +3,23 @@ import { defineAsyncComponent } from 'vue' const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue')) -const _doctorStatus = { - 0: 'Tidak Aktif', - 1: 'Aktif', -} - export const config: Config = { - cols: [{}, {}, { width: 50 }], + cols: [{}, {}, {}, { width: 50 }], headers: [ [ - { label: 'Kode' }, - { label: 'Nama' }, + { label: 'Item' }, + { label: 'Harga' }, + { label: 'Perusahaan Asuransi' }, { label: 'Aksi' }, ], ], - keys: ['code', 'name', 'action'], + keys: ['item_code', 'price', 'insuranceCompany_code', 'action'], delKeyNames: [ - { key: 'code', label: 'Kode' }, - { key: 'name', label: 'Nama' }, + { key: 'item_code', label: 'Item' }, + { key: 'insuranceCompany_code', label: 'Perusahaan Asuransi' }, ], parses: {}, @@ -39,9 +35,5 @@ export const config: Config = { }, }, - htmls: { - patient_address(_rec) { - return '-' - }, - }, + htmls: {}, } diff --git a/app/components/app/item-price/list.vue b/app/components/app/item-price/list.vue index 96697bde..2f7908cb 100644 --- a/app/components/app/item-price/list.vue +++ b/app/components/app/item-price/list.vue @@ -1,14 +1,39 @@ diff --git a/app/components/app/item-price/picker.vue b/app/components/app/item-price/picker.vue deleted file mode 100644 index e69de29b..00000000 diff --git a/app/components/app/item-price/search.vue b/app/components/app/item-price/search.vue deleted file mode 100644 index e69de29b..00000000 diff --git a/app/components/app/item-price/status-badge.vue b/app/components/app/item-price/status-badge.vue deleted file mode 100644 index 32cdfbca..00000000 --- a/app/components/app/item-price/status-badge.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/app/components/app/item/list-cfg.ts b/app/components/app/item/list-cfg.ts index c3db4f50..d781dc79 100644 --- a/app/components/app/item/list-cfg.ts +++ b/app/components/app/item/list-cfg.ts @@ -3,23 +3,22 @@ import { defineAsyncComponent } from 'vue' const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue')) -const _doctorStatus = { - 0: 'Tidak Aktif', - 1: 'Aktif', -} - export const config: Config = { - cols: [{}, {}, { width: 50 }], + cols: [{}, {}, {}, {}, {}, {}, { width: 50 }], headers: [ [ { label: 'Kode' }, { label: 'Nama' }, + { label: 'Item Group' }, + { label: 'UOM' }, + { label: 'Infra' }, + { label: 'Stok' }, { label: 'Aksi' }, ], ], - keys: ['code', 'name', 'action'], + keys: ['code', 'name', 'itemGroup_code', 'uom_code', 'infra_code', 'stock', 'action'], delKeyNames: [ { key: 'code', label: 'Kode' }, @@ -39,9 +38,5 @@ export const config: Config = { }, }, - htmls: { - patient_address(_rec) { - return '-' - }, - }, + htmls: {}, } diff --git a/app/components/app/item/list.vue b/app/components/app/item/list.vue index 96697bde..2f7908cb 100644 --- a/app/components/app/item/list.vue +++ b/app/components/app/item/list.vue @@ -1,14 +1,39 @@ diff --git a/app/components/app/item/picker.vue b/app/components/app/item/picker.vue deleted file mode 100644 index e69de29b..00000000 diff --git a/app/components/app/item/search.vue b/app/components/app/item/search.vue deleted file mode 100644 index e69de29b..00000000 diff --git a/app/components/app/item/status-badge.vue b/app/components/app/item/status-badge.vue deleted file mode 100644 index 32cdfbca..00000000 --- a/app/components/app/item/status-badge.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - From 864979aa1519c7f8e67acea794f834b43ffecf51 Mon Sep 17 00:00:00 2001 From: riefive Date: Thu, 4 Dec 2025 14:57:36 +0700 Subject: [PATCH 03/11] feat: Implement item price CRUD service and handlers, and item CRUD handlers. --- app/handlers/item-price.handler.ts | 24 ++++++++++++++++++++++++ app/handlers/item.handler.ts | 24 ++++++++++++++++++++++++ app/services/item-price.service.ts | 25 +++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 app/handlers/item-price.handler.ts create mode 100644 app/handlers/item.handler.ts create mode 100644 app/services/item-price.service.ts diff --git a/app/handlers/item-price.handler.ts b/app/handlers/item-price.handler.ts new file mode 100644 index 00000000..bd7dbefe --- /dev/null +++ b/app/handlers/item-price.handler.ts @@ -0,0 +1,24 @@ +// Handlers +import { genCrudHandler } from '~/handlers/_handler' + +// Services +import { create, update, remove } from '~/services/item-price.service' + +export const { + recId, + recAction, + recItem, + isReadonly, + isProcessing, + isFormEntryDialogOpen, + isRecordConfirmationOpen, + onResetState, + handleActionSave, + handleActionEdit, + handleActionRemove, + handleCancelForm, +} = genCrudHandler({ + create, + update, + remove, +}) diff --git a/app/handlers/item.handler.ts b/app/handlers/item.handler.ts new file mode 100644 index 00000000..3911df83 --- /dev/null +++ b/app/handlers/item.handler.ts @@ -0,0 +1,24 @@ +// Handlers +import { genCrudHandler } from '~/handlers/_handler' + +// Services +import { create, update, remove } from '~/services/item.service' + +export const { + recId, + recAction, + recItem, + isReadonly, + isProcessing, + isFormEntryDialogOpen, + isRecordConfirmationOpen, + onResetState, + handleActionSave, + handleActionEdit, + handleActionRemove, + handleCancelForm, +} = genCrudHandler({ + create, + update, + remove, +}) diff --git a/app/services/item-price.service.ts b/app/services/item-price.service.ts new file mode 100644 index 00000000..764bded1 --- /dev/null +++ b/app/services/item-price.service.ts @@ -0,0 +1,25 @@ +// Base +import * as base from './_crud-base' + +const path = '/api/v1/item-price' +const name = 'item-price' + +export function create(data: any) { + return base.create(path, data, name) +} + +export function getList(params: any = null) { + return base.getList(path, params, name) +} + +export function getDetail(id: number | string) { + return base.getDetail(path, id, name) +} + +export function update(id: number | string, data: any) { + return base.update(path, id, data, name) +} + +export function remove(id: number | string) { + return base.remove(path, id, name) +} From 615a7c448538e1fb1c9f3de5c10594dbb7274717 Mon Sep 17 00:00:00 2001 From: riefive Date: Fri, 5 Dec 2025 13:54:53 +0700 Subject: [PATCH 04/11] feat: Implement item management with CRUD operations, forms, and listing pages. --- app/components/app/item/entry-form.vue | 27 ++- app/components/app/item/list-cfg.ts | 19 +- app/components/content/item-price/list.vue | 220 ++++++++++++++----- app/components/content/item/list.vue | 232 ++++++++++++++++----- app/pages/(features)/item-price/index.vue | 29 +++ app/pages/(features)/item/index.vue | 29 +++ app/schemas/item.schema.ts | 4 +- app/services/item-group.service.ts | 41 ++++ app/services/item.service.ts | 7 +- 9 files changed, 499 insertions(+), 109 deletions(-) create mode 100644 app/pages/(features)/item-price/index.vue create mode 100644 app/pages/(features)/item/index.vue create mode 100644 app/services/item-group.service.ts diff --git a/app/components/app/item/entry-form.vue b/app/components/app/item/entry-form.vue index 67f12f50..5e2ab11a 100644 --- a/app/components/app/item/entry-form.vue +++ b/app/components/app/item/entry-form.vue @@ -16,12 +16,15 @@ import { toTypedSchema } from '@vee-validate/zod' interface Props { schema: z.ZodSchema + itemGroups: any[] + uoms: any[] values: any isLoading?: boolean isReadonly?: boolean } const props = defineProps() +const isShowInfra = false; const isLoading = props.isLoading !== undefined ? props.isLoading : false const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false const emit = defineEmits<{ @@ -44,7 +47,7 @@ const { defineField, errors, meta } = useForm({ 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 [uom, uomAttrs] = defineField('uom_code') const [infra_code, infra_codeAttrs] = defineField('infra_code') const [stock, stockAttrs] = defineField('stock') @@ -52,7 +55,7 @@ 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.uom_code !== undefined) uom.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 } @@ -61,7 +64,7 @@ const resetForm = () => { code.value = '' name.value = '' itemGroup_code.value = '' - uom_code.value = '' + uom.value = '' infra_code.value = '' stock.value = 0 } @@ -71,7 +74,7 @@ function onSubmitForm() { code: code.value || '', name: name.value || '', itemGroup_code: itemGroup_code.value || '', - uom_code: uom_code.value || '', + uom_code: uom.value || '', infra_code: infra_code.value || '', stock: Number(stock.value) || 0, } @@ -118,10 +121,13 @@ function onCancelForm() { - @@ -129,15 +135,18 @@ function onCancelForm() { - - + { + const recX = rec as any + return recX.itemGroup_code || '-' + }, + uom_code: (rec: unknown): unknown => { + const recX = rec as any + return recX.uom?.name || '-' + }, + infra_code: (rec: unknown): unknown => { + const recX = rec as any + return recX.infra_code || '-' + }, + stock: (rec: unknown): unknown => { + const recX = rec as any + return recX.stock || '-' + }, + }, components: { action(rec, idx) { diff --git a/app/components/content/item-price/list.vue b/app/components/content/item-price/list.vue index aa277b71..1e4de55f 100644 --- a/app/components/content/item-price/list.vue +++ b/app/components/content/item-price/list.vue @@ -1,70 +1,194 @@ diff --git a/app/components/content/item/list.vue b/app/components/content/item/list.vue index 15887985..c17ef3dd 100644 --- a/app/components/content/item/list.vue +++ b/app/components/content/item/list.vue @@ -1,70 +1,208 @@ diff --git a/app/pages/(features)/item-price/index.vue b/app/pages/(features)/item-price/index.vue new file mode 100644 index 00000000..b97966e5 --- /dev/null +++ b/app/pages/(features)/item-price/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/app/pages/(features)/item/index.vue b/app/pages/(features)/item/index.vue new file mode 100644 index 00000000..33fc9b23 --- /dev/null +++ b/app/pages/(features)/item/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/app/schemas/item.schema.ts b/app/schemas/item.schema.ts index 11b597e0..9e74ad70 100644 --- a/app/schemas/item.schema.ts +++ b/app/schemas/item.schema.ts @@ -5,8 +5,8 @@ const ItemSchema = z.object({ 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'), + infra_code: z.string({ required_error: 'Infra harus diisi' }).optional(), + stock: z.number({ required_error: 'Stok harus diisi' }).min(0, 'Stok tidak boleh kurang dari 0').optional(), }) type ItemFormData = z.infer diff --git a/app/services/item-group.service.ts b/app/services/item-group.service.ts new file mode 100644 index 00000000..c7c2d82b --- /dev/null +++ b/app/services/item-group.service.ts @@ -0,0 +1,41 @@ +// Base +import * as base from './_crud-base' + +const path = '/api/v1/item-group' +const name = 'item-group' + +export function create(data: any) { + return base.create(path, data, name) +} + +export function getList(params: any = null) { + return base.getList(path, params, name) +} + +export function getDetail(id: number | string) { + return base.getDetail(path, id, name) +} + +export function update(id: number | string, data: any) { + return base.update(path, id, data, name) +} + +export function remove(id: number | string) { + return base.remove(path, id, name) +} + +export async function getValueLabelList( + params: any = null, + useCodeAsValue = false, +): Promise<{ value: string; label: string }[]> { + let data: { value: string; label: string }[] = [] + const result = await getList(params) + if (result.success) { + const resultData = result.body?.data || [] + data = resultData.map((item: any) => ({ + value: useCodeAsValue ? item.code : item.id ? Number(item.id) : item.code, + label: item.name, + })) + } + return data +} diff --git a/app/services/item.service.ts b/app/services/item.service.ts index 8b44acd8..7d135201 100644 --- a/app/services/item.service.ts +++ b/app/services/item.service.ts @@ -24,13 +24,16 @@ export function remove(id: number | string) { return base.remove(path, id, name) } -export async function getValueLabelList(params: any = null): Promise<{ value: string; label: string }[]> { +export async function getValueLabelList( + params: any = null, + useCodeAsValue = false, +): Promise<{ value: string; label: string }[]> { let data: { value: string; label: string }[] = [] const result = await getList(params) if (result.success) { const resultData = result.body?.data || [] data = resultData.map((item: any) => ({ - value: item.id ? Number(item.id) : item.code, + value: useCodeAsValue ? item.code : item.id ? Number(item.id) : item.code, label: item.name, })) } From 4715e5330a739922fe5e8ab76ac6e2cacdc9c6db Mon Sep 17 00:00:00 2001 From: riefive Date: Fri, 5 Dec 2025 14:53:31 +0700 Subject: [PATCH 05/11] feat: Implement item management module and add encounter initialization handler. --- app/components/app/item/entry-form.vue | 34 ++++++++ app/components/app/item/list-cfg.ts | 38 +++++++- app/components/content/item/list.vue | 7 +- app/handlers/encounter-init.handler.ts | 115 ++++++++++++++++--------- app/schemas/item.schema.ts | 2 + 5 files changed, 149 insertions(+), 47 deletions(-) diff --git a/app/components/app/item/entry-form.vue b/app/components/app/item/entry-form.vue index 5e2ab11a..e40f1f66 100644 --- a/app/components/app/item/entry-form.vue +++ b/app/components/app/item/entry-form.vue @@ -41,6 +41,8 @@ const { defineField, errors, meta } = useForm({ uom_code: '', infra_code: '', stock: 0, + buyingPrice: 0, + sellingPrice: 0, } as Partial, }) @@ -50,6 +52,8 @@ const [itemGroup_code, itemGroup_codeAttrs] = defineField('itemGroup_code') const [uom, uomAttrs] = defineField('uom_code') const [infra_code, infra_codeAttrs] = defineField('infra_code') const [stock, stockAttrs] = defineField('stock') +const [buyingPrice, buyingPriceAttrs] = defineField('buyingPrice') +const [sellingPrice, sellingPriceAttrs] = defineField('sellingPrice') if (props.values) { if (props.values.code !== undefined) code.value = props.values.code @@ -58,6 +62,8 @@ if (props.values) { if (props.values.uom_code !== undefined) uom.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 + if (props.values.buyingPrice !== undefined) buyingPrice.value = props.values.buyingPrice + if (props.values.sellingPrice !== undefined) sellingPrice.value = props.values.sellingPrice } const resetForm = () => { @@ -67,6 +73,8 @@ const resetForm = () => { uom.value = '' infra_code.value = '' stock.value = 0 + buyingPrice.value = 0 + sellingPrice.value = 0 } function onSubmitForm() { @@ -77,6 +85,8 @@ function onSubmitForm() { uom_code: uom.value || '', infra_code: infra_code.value || '', stock: Number(stock.value) || 0, + buyingPrice: Number(buyingPrice.value) || 0, + sellingPrice: Number(sellingPrice.value) || 0, } emit('submit', formData, resetForm) } @@ -169,6 +179,30 @@ function onCancelForm() { /> + + + + + + + + + + + +