From 7ee6f401966fe143eb3b6bc39360ad55bab53e47 Mon Sep 17 00:00:00 2001 From: Khafid Prayoga Date: Tue, 25 Nov 2025 13:53:12 +0700 Subject: [PATCH] init: treatment report feat(treatment-report): add treatment report component with sample data Implement new treatment report feature including list view component, sample data, and configuration. The component supports pagination, filtering by date range, and search functionality. Also integrates with encounter process and home views. wip: init form and schema --- .../app/treatment-report/entry-form.vue | 7 ++ .../app/treatment-report/fields/index.ts | 1 + .../treatment-report/fields/select-dpjp.vue | 3 + .../app/treatment-report/list.cfg.ts | 88 ++++++++++++++++++ app/components/app/treatment-report/list.vue | 39 ++++++++ app/components/app/treatment-report/sample.ts | 54 +++++++++++ app/components/content/encounter/home.vue | 10 ++- app/components/content/encounter/process.vue | 8 ++ .../content/treatment-report/add.vue | 7 ++ .../content/treatment-report/list.vue | 73 +++++++++++++++ app/pages/(features)/treatment-report/add.vue | 45 ++++++++++ app/schemas/treatment-report.schema.ts | 89 +++++++++++++++++++ 12 files changed, 422 insertions(+), 2 deletions(-) create mode 100644 app/components/app/treatment-report/entry-form.vue create mode 100644 app/components/app/treatment-report/fields/index.ts create mode 100644 app/components/app/treatment-report/fields/select-dpjp.vue create mode 100644 app/components/app/treatment-report/list.cfg.ts create mode 100644 app/components/app/treatment-report/list.vue create mode 100644 app/components/app/treatment-report/sample.ts create mode 100644 app/components/content/treatment-report/add.vue create mode 100644 app/components/content/treatment-report/list.vue create mode 100644 app/pages/(features)/treatment-report/add.vue create mode 100644 app/schemas/treatment-report.schema.ts diff --git a/app/components/app/treatment-report/entry-form.vue b/app/components/app/treatment-report/entry-form.vue new file mode 100644 index 00000000..2a474b15 --- /dev/null +++ b/app/components/app/treatment-report/entry-form.vue @@ -0,0 +1,7 @@ + + + diff --git a/app/components/app/treatment-report/fields/index.ts b/app/components/app/treatment-report/fields/index.ts new file mode 100644 index 00000000..ab2e32e9 --- /dev/null +++ b/app/components/app/treatment-report/fields/index.ts @@ -0,0 +1 @@ +export { default as SelectDPJP } from './select-dpjp.vue' diff --git a/app/components/app/treatment-report/fields/select-dpjp.vue b/app/components/app/treatment-report/fields/select-dpjp.vue new file mode 100644 index 00000000..dae90589 --- /dev/null +++ b/app/components/app/treatment-report/fields/select-dpjp.vue @@ -0,0 +1,3 @@ + + + diff --git a/app/components/app/treatment-report/list.cfg.ts b/app/components/app/treatment-report/list.cfg.ts new file mode 100644 index 00000000..9b4c5df1 --- /dev/null +++ b/app/components/app/treatment-report/list.cfg.ts @@ -0,0 +1,88 @@ +import { defineAsyncComponent } from 'vue' + +import { format } from 'date-fns' +import { id } from 'date-fns/locale' + +import type { Config, RecComponent } from '~/components/pub/my-ui/data-table' +import type { TreatmentReportData } from '~/components/app/treatment-report/sample' +const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue')) + +export const config: Config = { + cols: [ + { width: 120 }, + { width: 120 }, + { width: 120 }, + { width: 120 }, + { width: 120 }, + { width: 120 }, + { width: 120 }, + { width: 50 }, + ], + + headers: [ + [ + { label: 'TANGGAL LAPORAN' }, + { label: 'DPJP' }, + { label: 'OPERATOR' }, + { label: 'TANGGAL PEMBEDAHAN' }, + { label: 'JENIS OPERASI' }, + { label: 'KODE BILLING' }, + { label: 'SISTEM OPERASI' }, + { label: 'AKSI' }, + ], + ], + + keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'billing', 'system', 'action'], + + delKeyNames: [ + { key: 'code', label: 'Kode' }, + { key: 'name', label: 'Nama' }, + ], + + parses: { + reportAt: (rec: unknown): unknown => { + const attr = (rec as TreatmentReportData).reportAt + const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id }) + + return result + }, + operationAt: (rec: unknown): unknown => { + const attr = (rec as TreatmentReportData).operationAt + const result = format(new Date(attr), 'd MMMM yyyy', { locale: id }) + + return result + }, + system: (rec: unknown): unknown => { + return 'Cito' + }, + operator: (rec: unknown): unknown => { + return 'dr. Dewi Arum Sawitri, Sp.An' + }, + billing: (rec: unknown): unknown => { + return 'General' + }, + operationType: (rec: unknown): unknown => { + return 'Besar' + }, + dpjp: (rec: unknown): unknown => { + return 'dr. Irwansyah Kurniawan Sp.Bo' + }, + parent: (rec: unknown): unknown => { + const recX = rec as any + return recX.parent?.name || '-' + }, + }, + + components: { + action(rec, idx) { + const res: RecComponent = { + idx, + rec: rec as object, + component: action, + } + return res + }, + }, + + htmls: {}, +} diff --git a/app/components/app/treatment-report/list.vue b/app/components/app/treatment-report/list.vue new file mode 100644 index 00000000..6ad7dd81 --- /dev/null +++ b/app/components/app/treatment-report/list.vue @@ -0,0 +1,39 @@ + + + diff --git a/app/components/app/treatment-report/sample.ts b/app/components/app/treatment-report/sample.ts new file mode 100644 index 00000000..9446b91c --- /dev/null +++ b/app/components/app/treatment-report/sample.ts @@ -0,0 +1,54 @@ +import { addWeeks, formatISO } from 'date-fns' + +export type TreatmentReportData = { + id: number + reportAt: string + operationAt: string + noRm: string + noBill: string + nama: string + jk: string + alamat: string + klinik: string + dokter: string + caraBayar: string + rujukan: string + ketRujukan: string + asal: string +} + +export const sampleRows: TreatmentReportData[] = [ + { + id: 1, + reportAt: formatISO(addWeeks(new Date(), -1)), + operationAt: formatISO(addWeeks(new Date(), 1)), + noRm: 'RM23311224', + noBill: '-', + nama: 'Ahmad Baidowi', + jk: 'L', + alamat: 'Jl Jaksa Agung S. No. 9', + klinik: 'Penyakit dalam', + dokter: 'Dr. Andreas Sutaji', + caraBayar: 'JKN', + rujukan: 'Faskes BPJS', + ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang', + asal: 'Rawat Jalan Reguler', + }, + { + id: 2, + reportAt: new Date().toISOString(), + operationAt: formatISO(addWeeks(new Date(), 2)), + noRm: 'RM23455667', + noBill: '-', + nama: 'Abraham Sulaiman', + jk: 'L', + alamat: 'Purwantoro, Blimbing', + klinik: 'Penyakit dalam', + dokter: 'Dr. Andreas Sutaji', + caraBayar: 'JKN', + rujukan: 'Faskes BPJS', + ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang', + asal: 'Rawat Jalan Reguler', + }, + // tambahkan lebih banyak baris contoh jika perlu +] diff --git a/app/components/content/encounter/home.vue b/app/components/content/encounter/home.vue index 1f2c2943..c9822e52 100644 --- a/app/components/content/encounter/home.vue +++ b/app/components/content/encounter/home.vue @@ -130,7 +130,6 @@ const tabsRaws: TabItem[] = [ component: MedicineProtocolList, props: { data: protocolRows, paginationMeta }, }, - { value: 'report', label: 'Laporan Tindakan', groups: ['chemotherapy'] }, { value: 'patient-note', label: 'CPRJ', groups: ['ambulatory', 'rehabilitation', 'chemotherapy'] }, { value: 'education-assessment', @@ -162,13 +161,20 @@ const tabsRaws: TabItem[] = [ { value: 'resume', label: 'Resume', groups: ['ambulatory', 'rehabilitation', 'chemotherapy'] }, { value: 'control', label: 'Surat Kontrol', groups: ['ambulatory', 'rehabilitation', 'chemotherapy'] }, { value: 'screening', label: 'Skrinning MPP', groups: ['ambulatory', 'rehabilitation', 'chemotherapy'] }, + { + value: 'report', + label: 'Laporan Tindakan', + groups: ['ambulatory', 'rehabilitation', 'chemotherapy'], + component: Consultation, + props: { encounter: data }, + }, { value: 'supporting-document', label: 'Upload Dokumen Pendukung', groups: ['ambulatory', 'rehabilitation'] }, { value: 'price-list', label: 'Tarif Tindakan', groups: ['ambulatory', 'rehabilitation', 'chemotherapy'] }, ] const tabs = computed(() => { return tabsRaws - .filter((tab: TabItem) => tab.groups ? tab.groups.some((group: string) => props.classes?.includes(group)) : true) + .filter((tab: TabItem) => (tab.groups ? tab.groups.some((group: string) => props.classes?.includes(group)) : true)) .map((tab: TabItem) => { return { ...tab, props: { ...tab.props, encounter: data } } }) diff --git a/app/components/content/encounter/process.vue b/app/components/content/encounter/process.vue index 02fc7495..43b0f55b 100644 --- a/app/components/content/encounter/process.vue +++ b/app/components/content/encounter/process.vue @@ -22,6 +22,7 @@ import CpLabOrder from '~/components/content/cp-lab-order/main.vue' import Radiology from '~/components/content/radiology-order/main.vue' import Consultation from '~/components/content/consultation/list.vue' import Cprj from '~/components/content/cprj/entry.vue' +import TreatmentReport from '~/components/content/treatment-report/list.vue' import DocUploadList from '~/components/content/document-upload/list.vue' import GeneralConsentList from '~/components/content/general-consent/entry.vue' import ResumeList from '~/components/content/resume/list.vue' @@ -90,6 +91,13 @@ const tabs: TabItem[] = [ { value: 'resume', label: 'Resume', component: ResumeList, props: { encounter: data } }, { value: 'control', label: 'Surat Kontrol', component: ControlLetterList, props: { encounter: data } }, { value: 'screening', label: 'Skrinning MPP' }, + { + value: 'report', + label: 'Laporan Tindakan', + groups: ['ambulatory', 'rehabilitation', 'chemotherapy'], + component: TreatmentReport, + props: { encounter: data }, + }, { value: 'supporting-document', label: 'Upload Dokumen Pendukung', diff --git a/app/components/content/treatment-report/add.vue b/app/components/content/treatment-report/add.vue new file mode 100644 index 00000000..c1446a5a --- /dev/null +++ b/app/components/content/treatment-report/add.vue @@ -0,0 +1,7 @@ + + + diff --git a/app/components/content/treatment-report/list.vue b/app/components/content/treatment-report/list.vue new file mode 100644 index 00000000..e6ece811 --- /dev/null +++ b/app/components/content/treatment-report/list.vue @@ -0,0 +1,73 @@ + + + diff --git a/app/pages/(features)/treatment-report/add.vue b/app/pages/(features)/treatment-report/add.vue new file mode 100644 index 00000000..b8459422 --- /dev/null +++ b/app/pages/(features)/treatment-report/add.vue @@ -0,0 +1,45 @@ + + + diff --git a/app/schemas/treatment-report.schema.ts b/app/schemas/treatment-report.schema.ts new file mode 100644 index 00000000..f9aac9a8 --- /dev/null +++ b/app/schemas/treatment-report.schema.ts @@ -0,0 +1,89 @@ +import { z } from 'zod' + +const isoDateTime = z.string().min(1, 'Tanggal / waktu wajib diisi') +const positiveInt = z.number().int().nonnegative() + +const OperatorTeamSchema = z.object({ + dpjpId: z.number().int(), + operatorId: z.number().int(), + assistantOperatorId: z.number().int().optional().nullable(), + instrumentNurseId: z.number().int().optional().nullable(), + + surgeryDate: isoDateTime, + actionDiagnosis: z.string().min(1), + + postOpNurseId: z.number().int().optional().nullable(), +}) + +const ProcedureSchema = z.object({ + id: z.number().int().optional(), + name: z.string().min(1), + code: z.string().min(1), +}) + +const OperationExecutionSchema = z.object({ + surgeryType: z.enum(['kecil', 'sedang', 'besar', 'khusus']), + billingCode: z.string().min(1), + operationSystem: z.enum(['khusus', 'cito', 'elektif']), + + operationStartAt: isoDateTime, + operationEndAt: isoDateTime, + + anesthesiaStartAt: isoDateTime, + anesthesiaEndAt: isoDateTime, + + surgeryCleanType: z.enum(['bersih', 'bersih_terkontaminasi', 'terkontaminasi', 'kotor']).optional(), + surgeryNumber: z.enum(['1', '2', '3', '4+', 'tidak_diketahui']).optional(), + + birthPlaceNote: z.string().optional(), + babyWeightGram: positiveInt.optional(), + birthCondition: z.string().optional(), + + operationDescription: z.string().min(1), + + bleedingAmountCc: positiveInt.optional(), + + birthRemark: z.enum(['lahir_hidup', 'lahir_mati', 'abortus', 'tidak_diketahui']).optional(), +}) + +const BloodComponentSchema = z.object({ + used: z.boolean().default(false), + volumeCc: positiveInt.optional(), +}) + +const BloodInputSchema = z.object({ + prc: BloodComponentSchema, + ffp: BloodComponentSchema, + wb: BloodComponentSchema, + tc: BloodComponentSchema, +}) + +const ImplantSchema = z.object({ + brand: z.string().optional(), + name: z.string().optional(), + stickerNumber: z.string().optional(), + companionName: z.string().optional(), +}) + +const SpecimenSchema = z.object({ + destination: z.string().min(1), +}) + +const TissueNoteSchema = z.object({ + note: z.string().min(1), +}) + +export const ActionReportSchema = z.object({ + operatorTeam: OperatorTeamSchema, + procedures: z.array(ProcedureSchema).min(1), + + operationExecution: OperationExecutionSchema, + + bloodInput: BloodInputSchema.optional(), + implant: ImplantSchema.optional(), + specimen: SpecimenSchema.optional(), + + tissueNotes: z.array(TissueNoteSchema).optional(), +}) + +export type ActionReportFormData = z.infer