diff --git a/app/assets/svg/wavey-fingerprint.svg b/app/assets/svg/wavey-fingerprint.svg
new file mode 100644
index 00000000..b281297f
--- /dev/null
+++ b/app/assets/svg/wavey-fingerprint.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/components/app/cprj/entry.vue b/app/components/app/cprj/entry.vue
new file mode 100644
index 00000000..d03bbf77
--- /dev/null
+++ b/app/components/app/cprj/entry.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
diff --git a/app/components/app/cprj/list.cfg.ts b/app/components/app/cprj/list.cfg.ts
new file mode 100644
index 00000000..a394e81d
--- /dev/null
+++ b/app/components/app/cprj/list.cfg.ts
@@ -0,0 +1,56 @@
+import type { Config, RecComponent, RecStrFuncComponent, RecStrFuncUnknown } from '~/components/pub/my-ui/data-table'
+import { defineAsyncComponent } from 'vue'
+import type { GeneralConsent } from '~/models/general-consent'
+
+type SmallDetailDto = any
+
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
+export const config: Config = {
+ cols: [{ width: 100 }, {}, {}, {}, { width: 50 }],
+ headers: [
+ [
+ { label: 'Tanggal' },
+ { label: 'PPA' },
+ { label: 'Hasil' },
+ { label: 'Review & Verifikasi' },
+ { label: 'Status' },
+ { label: 'Aksi' },
+ ],
+ ],
+ keys: ['date', 'ppa', 'result', 'review', 'status', 'action'],
+ delKeyNames: [
+ { key: 'data', label: 'Tanggal' },
+ { key: 'dstDoctor.name', label: 'Dokter' },
+ ],
+ parses: {
+ action(rec, idx) {
+ const res: RecComponent = {
+ idx,
+ rec: rec as object,
+ component: action,
+ props: {
+ size: 'sm',
+ },
+ }
+ return res
+ },
+ date(rec) {
+ const recX = rec as GeneralConsent
+ return recX.date?.substring(0, 10) || '-'
+ },
+ },
+ components: {
+ action(rec, idx) {
+ const res: RecComponent = {
+ idx,
+ rec: rec as object,
+ component: action,
+ props: {
+ size: 'sm',
+ },
+ }
+ return res
+ },
+ } as RecStrFuncComponent,
+ htmls: {} as RecStrFuncUnknown,
+}
diff --git a/app/components/app/cprj/list.vue b/app/components/app/cprj/list.vue
new file mode 100644
index 00000000..46f595f5
--- /dev/null
+++ b/app/components/app/cprj/list.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
diff --git a/app/components/app/device-order-item/entry-form.vue b/app/components/app/device-order-item/entry-form.vue
new file mode 100644
index 00000000..3ae2d200
--- /dev/null
+++ b/app/components/app/device-order-item/entry-form.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+ Nama
+
+
+
+
+
+ Jumlah
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/device-order-item/list-entry.config.ts b/app/components/app/device-order-item/list-entry.config.ts
index f2f3ef86..d47c4368 100644
--- a/app/components/app/device-order-item/list-entry.config.ts
+++ b/app/components/app/device-order-item/list-entry.config.ts
@@ -1,36 +1,35 @@
import { defineAsyncComponent } from 'vue'
-import type { Config } from '~/components/pub/my-ui/data-table'
-
+import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
export const config: Config = {
- cols: [{}, {}, { width: 50 }],
+ cols: [{}, { width: 200 }, { width: 100 }],
headers: [[{ label: 'Nama' }, { label: 'Jumlah' }, { label: '' }]],
- keys: ['name', 'count', 'action'],
+ keys: ['device.name', 'quantity', 'action'],
delKeyNames: [
{ key: 'name', label: 'Nama' },
{ key: 'count', label: 'Jumlah' },
],
- skeletonSize: 10
+ skeletonSize: 10,
// funcParsed: {
// parent: (rec: unknown): unknown => {
// const recX = rec as SmallDetailDto
// return recX.parent?.name || '-'
// },
// },
- // funcComponent: {
- // action(rec: object, idx: any) {
- // const res: RecComponent = {
- // idx,
- // rec: rec as object,
- // component: action,
- // props: {
- // size: 'sm',
- // },
- // }
- // return res
- // },
- // }
+ components: {
+ action(rec, idx) {
+ const res: RecComponent = {
+ idx,
+ rec: rec as object,
+ component: action,
+ props: {
+ size: 'sm',
+ },
+ }
+ return res
+ },
+ }
}
diff --git a/app/components/app/device-order-item/list-entry.vue b/app/components/app/device-order-item/list-entry.vue
index 26f6691d..b6031228 100644
--- a/app/components/app/device-order-item/list-entry.vue
+++ b/app/components/app/device-order-item/list-entry.vue
@@ -1,13 +1,23 @@
-
+
-
diff --git a/app/components/app/device-order/confirmation-info.vue b/app/components/app/device-order/confirmation-info.vue
new file mode 100644
index 00000000..6711091f
--- /dev/null
+++ b/app/components/app/device-order/confirmation-info.vue
@@ -0,0 +1,26 @@
+
+
+
+
+ Tgl. Order
+
+
+ {{ data?.createdAt?.substring(0, 10) }}
+
+
+
+ DPJP
+
+
+ {{ data?.doctor?.employee?.person?.name }}
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/device-order/entry-form.vue b/app/components/app/device-order/entry-form.vue
index bea2b6eb..4e66c441 100644
--- a/app/components/app/device-order/entry-form.vue
+++ b/app/components/app/device-order/entry-form.vue
@@ -1,6 +1,25 @@
- Test
+
+
+ Tanggal
+
+ {{ data?.createdAt?.substring(0, 10) }}
+
+
+
+ DPJP
+
+ {{ data?.doctor?.employee?.person?.name }}
+
+
+
diff --git a/app/components/app/device-order/list.config.ts b/app/components/app/device-order/list.config.ts
index 7580c576..04a6c9fc 100644
--- a/app/components/app/device-order/list.config.ts
+++ b/app/components/app/device-order/list.config.ts
@@ -1,13 +1,18 @@
-import type { Config } from '~/components/pub/my-ui/data-table'
+import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
import type { DeviceOrder } from '~/models/device-order'
-import { defineAsyncComponent } from 'vue'
+import type { DeviceOrderItem } from '~/models/device-order-item'
-const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dsd.vue'))
export const config: Config = {
- cols: [{ width: 120 }, { }, { }, { width: 50 }],
- headers: [[{ label: 'Tanggal' }, { label: 'DPJP' }, { label: 'Alat Kesehatan' }, { label: '' }]],
- keys: ['createdAt', 'encounter.doctor.person.name', 'items', 'action'],
+ cols: [{ width: 120 }, { }, { }, { }, { width: 50 }],
+ headers: [[
+ { label: 'Tanggal' },
+ { label: 'DPJP' },
+ { label: 'Alat Kesehatan' },
+ { label: 'Status' },
+ { label: '' }]],
+ keys: ['createdAt', 'doctor.employee.person.name', 'items', 'status_code', 'action'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
@@ -16,27 +21,45 @@ export const config: Config = {
htmls: {
items: (rec: unknown): unknown => {
const recX = rec as DeviceOrder
- return recX.items?.length || 0
+ if (recX.items?.length > 0) {
+ let output = '
'
+ recX.items.forEach((item: DeviceOrderItem) => {
+ output += '' +
+ ''+
+ `| ${item.device?.name} | ` +
+ ': | ' +
+ `${item.quantity} | ` +
+ '
'
+ })
+ output += '
'
+ return output
+ } else {
+ return '-'
+ }
+ },
+ },
+ parses: {
+ createdAt: (rec: unknown): unknown => {
+ const recX = rec as DeviceOrder
+ return recX.createdAt ? new Date(recX.createdAt).toLocaleDateString() : '-'
+ },
+ // parent: (rec: unknown): unknown => {
+ // const recX = rec as SmallDetailDto
+ // return recX.parent?.name || '-'
+ // },
+ },
+ components: {
+ action(rec, idx) {
+ const res: RecComponent = {
+ idx,
+ rec: rec as object,
+ component: action,
+ props: {
+ size: 'sm',
+ },
+ }
+ return res
},
}
- // funcParsed: {
- // parent: (rec: unknown): unknown => {
- // const recX = rec as SmallDetailDto
- // return recX.parent?.name || '-'
- // },
- // },
- // funcComponent: {
- // action(rec: object, idx: any) {
- // const res: RecComponent = {
- // idx,
- // rec: rec as object,
- // component: action,
- // props: {
- // size: 'sm',
- // },
- // }
- // return res
- // },
- // }
}
diff --git a/app/components/app/device-order/list.vue b/app/components/app/device-order/list.vue
index 37b24ea3..83f57dd6 100644
--- a/app/components/app/device-order/list.vue
+++ b/app/components/app/device-order/list.vue
@@ -8,12 +8,10 @@ import type { PaginationMeta } from '~/components/pub/my-ui/pagination/paginatio
// Configs
import { config } from './list.config'
-interface Props {
+defineProps<{
data: any[]
paginationMeta: PaginationMeta
-}
-
-defineProps()
+}>()
const emit = defineEmits<{
pageChange: [page: number]
@@ -28,7 +26,7 @@ function handlePageChange(page: number) {
diff --git a/app/components/app/division-position/entry-form.vue b/app/components/app/division-position/entry-form.vue
index 2525b49b..b25c9d0c 100644
--- a/app/components/app/division-position/entry-form.vue
+++ b/app/components/app/division-position/entry-form.vue
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [division, divisionAttrs] = defineField('division_id')
+const [division, divisionAttrs] = defineField('division_code')
const [employee, employeeAttrs] = defineField('employee_id')
const [headStatus, headStatusAttrs] = defineField('headStatus')
@@ -62,8 +62,8 @@ const headStatusStr = computed({
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.division_id !== undefined)
- division.value = props.values.division_id ? Number(props.values.division_id) : null
+ if (props.values.division_code !== undefined)
+ division.value = props.values.division_code ? String(props.values.division_code) : null
if (props.values.employee_id !== undefined)
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
@@ -72,7 +72,7 @@ if (props.values) {
const resetForm = () => {
code.value = ''
name.value = ''
- division.value = null
+ division.value = ''
employee.value = null
headStatus.value = false
}
@@ -83,7 +83,7 @@ function onSubmitForm() {
...genBase(),
name: name.value || '',
code: code.value || '',
- division_id: division.value || null,
+ division_code: division.value || '',
employee_id: employee.value || null,
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
}
@@ -130,7 +130,7 @@ function onCancelForm() {
-
+
- divisionId: number
+ divisionId: string
employees: any[]
values: any
isLoading?: boolean
diff --git a/app/components/app/division/entry-form.vue b/app/components/app/division/entry-form.vue
index 7702fdc6..19ca8196 100644
--- a/app/components/app/division/entry-form.vue
+++ b/app/components/app/division/entry-form.vue
@@ -36,25 +36,25 @@ const { defineField, errors, meta } = useForm({
initialValues: {
code: '',
name: '',
- parent_id: null,
+ parent_code: null,
} as Partial,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [parent, parentAttrs] = defineField('parent_id')
+const [parent, parentAttrs] = defineField('parent_code')
// Fill fields from props.values if provided
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.parent_id !== undefined) parent.value = String(props.values.parent_id)
+ if (props.values.parent_code !== undefined) parent.value = String(props.values.parent_code)
}
const resetForm = () => {
code.value = ''
name.value = ''
- parent.value = null
+ parent.value = ''
}
// Form submission handler
@@ -63,7 +63,7 @@ function onSubmitForm() {
...genBase(),
name: name.value || '',
code: code.value || '',
- parent_id: parent.value || null,
+ parent_code: parent.value || '',
}
emit('submit', formData, resetForm)
}
@@ -108,7 +108,7 @@ function onCancelForm() {
|
-
+
+import type { FormErrors } from '~/types/error'
+import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
+import { cn, mapToComboboxOptList } from '~/lib/utils'
+import { docTypeCode, supportingDocOpt, type docTypeCodeKey } from '~/lib/constants'
+import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
+import { getValueLabelList as getUnitLabelList } from '~/services/unit.service'
+
+import * as DE from '~/components/pub/my-ui/doc-entry'
+import type { Item } from '~/components/pub/my-ui/combobox'
+
+const props = defineProps<{
+ fieldName?: string
+ label?: string
+ placeholder?: string
+ errors?: FormErrors
+ class?: string
+ selectClass?: string
+ fieldGroupClass?: string
+ labelClass?: string
+ isRequired?: boolean
+}>()
+
+const {
+ fieldName = 'job',
+ label = 'Pekerjaan',
+ placeholder = 'Pilih pekerjaan',
+ errors,
+ class: containerClass,
+ fieldGroupClass,
+ labelClass,
+} = props
+
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/document-upload/entry-form.vue b/app/components/app/document-upload/entry-form.vue
new file mode 100644
index 00000000..f97a5161
--- /dev/null
+++ b/app/components/app/document-upload/entry-form.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
diff --git a/app/components/app/document-upload/list.cfg.ts b/app/components/app/document-upload/list.cfg.ts
new file mode 100644
index 00000000..979c916d
--- /dev/null
+++ b/app/components/app/document-upload/list.cfg.ts
@@ -0,0 +1,43 @@
+import type { Config } from '~/components/pub/my-ui/data-table'
+import { defineAsyncComponent } from 'vue'
+import { docTypeCode, docTypeLabel, type docTypeCodeKey } from '~/lib/constants'
+
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dd.vue'))
+
+export const config: Config = {
+ cols: [{}, {}, {}, {width: 50},],
+
+ headers: [
+ [
+ { label: 'Nama Dokumen' },
+ { label: 'Tipe Dokumen' },
+ { label: 'Petugas Upload' },
+ { label: 'Action' },
+ ],
+ ],
+
+ keys: ['fileName', 'type_code', 'employee.name', 'action'],
+
+ delKeyNames: [
+
+ ],
+
+ parses: {
+ type_code: (v: unknown) => {
+ return docTypeLabel[v?.type_code as docTypeCodeKey]
+ },
+ },
+
+ components: {
+ action(rec, idx) {
+ return {
+ idx,
+ rec: rec as object,
+ component: action,
+ }
+ },
+ },
+
+ htmls: {
+ },
+}
diff --git a/app/components/app/document-upload/list.vue b/app/components/app/document-upload/list.vue
new file mode 100644
index 00000000..8274e752
--- /dev/null
+++ b/app/components/app/document-upload/list.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/app/components/app/encounter/dropdown-action.vue b/app/components/app/encounter/dropdown-action.vue
new file mode 100644
index 00000000..0cd85204
--- /dev/null
+++ b/app/components/app/encounter/dropdown-action.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/encounter/entry-form.vue b/app/components/app/encounter/entry-form.vue
index 9c24a0c6..22acbbb8 100644
--- a/app/components/app/encounter/entry-form.vue
+++ b/app/components/app/encounter/entry-form.vue
@@ -20,6 +20,7 @@ import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
// Helpers
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
+import { refDebounced } from '@vueuse/core'
const props = defineProps<{
isLoading?: boolean
@@ -92,8 +93,9 @@ watch(subSpecialistId, async (newValue) => {
}
})
-// Watch SEP number changes to notify parent
-watch(sepNumber, (newValue) => {
+// Debounced SEP number watcher: emit change only after user stops typing
+const debouncedSepNumber = refDebounced(sepNumber, 500)
+watch(debouncedSepNumber, (newValue) => {
emit('event', 'sep-number-changed', newValue)
})
@@ -452,7 +454,11 @@ defineExpose({
name="i-lucide-loader-2"
class="h-4 w-4 animate-spin"
/>
- +
+
+import { Calendar as CalendarIcon, Filter as FilterIcon, Search } from 'lucide-vue-next'
+import { ref } from 'vue'
+import type { Ref } from 'vue'
+import type { DateRange } from 'radix-vue'
+import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
+import { cn } from '~/lib/utils'
+import type { RefExportNav, RefSearchNav } from '~/components/pub/my-ui/data/types'
+
+const props = defineProps<{
+ refSearchNav?: RefSearchNav
+ enableExport?: boolean
+ refExportNav?: RefExportNav
+ onFilterClick?: () => void
+ onExportPdf?: () => void
+ onExportExcel?: () => void
+ onExportCsv?: () => void
+}>()
+
+// function emitSearchNavClick() {
+// props.refSearchNav?.onClick()
+// }
+//
+// function onInput(event: Event) {
+// props.refSearchNav?.onInput((event.target as HTMLInputElement).value)
+// }
+//
+// function btnClick() {
+// props.prep?.addNav?.onClick?.()
+// }
+
+const searchQuery = ref('')
+const dateRange = ref<{ from: Date | null; to: Date | null }>({
+ from: new Date(),
+ to: new Date(),
+})
+
+const df = new DateFormatter('en-US', {
+ dateStyle: 'medium',
+})
+
+// Get current date
+const today = new Date()
+const todayCalendar = new CalendarDate(today.getFullYear(), today.getMonth() + 1, today.getDate())
+
+// Get date 1 month ago
+const oneMonthAgo = new Date(today)
+oneMonthAgo.setMonth(today.getMonth() - 1)
+const oneMonthAgoCalendar = new CalendarDate(oneMonthAgo.getFullYear(), oneMonthAgo.getMonth() + 1, oneMonthAgo.getDate())
+
+const value = ref({
+ start: oneMonthAgoCalendar,
+ end: todayCalendar,
+}) as Ref
+
+// function onFilterClick() {
+// console.log('Search:', searchQuery.value)
+// console.log('Date Range:', dateRange.value)
+// props.refSearchNav?.onClick()
+// }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ df.format(value.start.toDate(getLocalTimeZone())) }} -
+ {{ df.format(value.end.toDate(getLocalTimeZone())) }}
+
+
+
+ {{ df.format(value.start.toDate(getLocalTimeZone())) }}
+
+
+ Pick a date
+
+
+
+ (value.start = startDate)"
+ />
+
+
+
+
+
+ Filter
+
+
+
+
+
+
+ Ekspor
+
+
+
+
+ Ekspor PDF
+
+
+ Ekspor CSV
+
+
+ Ekspor Excel
+
+
+
+
diff --git a/app/components/app/encounter/history-button-menu.vue b/app/components/app/encounter/history-button-menu.vue
new file mode 100644
index 00000000..4274a98f
--- /dev/null
+++ b/app/components/app/encounter/history-button-menu.vue
@@ -0,0 +1,133 @@
+
+
+
+
+ History Pasien:
+
+
+
+
+ {{ item.label }}
+
+
+ Billing Pasien:
+
+
+
+ {{ item.label }}
+
+
+
+
diff --git a/app/components/app/encounter/list.cfg.ts b/app/components/app/encounter/list.cfg.ts
index 9ebbc34f..cc0b951c 100644
--- a/app/components/app/encounter/list.cfg.ts
+++ b/app/components/app/encounter/list.cfg.ts
@@ -6,7 +6,7 @@ import { getAge } from '~/lib/date'
type SmallDetailDto = Encounter
-const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-pdud.vue'))
+const action = defineAsyncComponent(() => import('./dropdown-action.vue'))
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
export const config: Config = {
diff --git a/app/components/app/encounter/list.vue b/app/components/app/encounter/list.vue
index 1d58d6a7..801e5b4c 100644
--- a/app/components/app/encounter/list.vue
+++ b/app/components/app/encounter/list.vue
@@ -1,14 +1,15 @@
-
diff --git a/app/components/app/encounter/patient-info-collapsible.vue b/app/components/app/encounter/patient-info-collapsible.vue
new file mode 100644
index 00000000..924a8c2e
--- /dev/null
+++ b/app/components/app/encounter/patient-info-collapsible.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+ Data Pasien:
+
+
+
+
+
+
+
+
diff --git a/app/components/app/encounter/patient-info.vue b/app/components/app/encounter/patient-info.vue
new file mode 100644
index 00000000..ea41199e
--- /dev/null
+++ b/app/components/app/encounter/patient-info.vue
@@ -0,0 +1,18 @@
+
+
+
+
+ Data Pasien:
+
+
+
diff --git a/app/components/app/encounter/quick-info-full.vue b/app/components/app/encounter/quick-info-full.vue
new file mode 100644
index 00000000..4cbc5bfb
--- /dev/null
+++ b/app/components/app/encounter/quick-info-full.vue
@@ -0,0 +1,247 @@
+
+
+
+ Data Pasien:
+
+
+
+
+
+
+
+ {{ data.patient.number || '-' }}
+
+
+
+
+
+
+
+
+ {{ birthDateFormatted }}
+
+
+
+
+
+
+
+
+ {{ paymentTypeLabel }}
+
+
+
+
+
+
+
+
+ {{ bedNumber }}
+
+
+
+
+
+
+
+
+ {{ data.patient.person.name || '-' }}
+
+
+
+
+
+
+
+
+ {{ registeredDateFormatted }}
+
+
+
+
+
+
+
+
+ {{ billingNumber }}
+
+
+
+
+
+
+
+
+ {{ dpjp }}
+
+
+
+
+
+
+
+
+ {{ address }}
+
+
+
+
+
+
+
+
+ {{ genderLabel }}
+
+
+
+
+
+
+
+
+ {{ roomName }}
+
+
+
+
diff --git a/app/components/app/encounter/quick-info.vue b/app/components/app/encounter/quick-info.vue
index 4b223917..17e1053b 100644
--- a/app/components/app/encounter/quick-info.vue
+++ b/app/components/app/encounter/quick-info.vue
@@ -1,52 +1,52 @@
-
+
{{ data.patient.person.name }} - {{ data.patient.number }}
-
-
+
No. RM
+
{{ data.patient.person.birthDate?.substring(0, 10) }}
- Jenis Kelamin
+ Jns. Kelamin
+
- {{ data.patient.person.gender_code }}
+ {{ genderCodes[data.patient.person.gender_code as keyof typeof genderCodes] }}
Alamat
+
@@ -54,35 +54,39 @@ if (props.data.responsible_doctor) {
-
+
- Tgl. Kunjungan
+ Tgl. Masuk
+
{{ data.visitDate.substring(0, 10) }}
- Klinik
+ Poliklinik
+
{{ data.unit?.name }}
- DPJP
+ Klinik
+
- {{ dpjp }}
+ {{ data.unit?.name }}
-
+
+
+ DPJP
+
+
+ {{ dpjp }}
+
+
Billing
+
Rp. 000.000
diff --git a/app/components/app/encounter/quick-shortcut.vue b/app/components/app/encounter/quick-shortcut.vue
new file mode 100644
index 00000000..751b2feb
--- /dev/null
+++ b/app/components/app/encounter/quick-shortcut.vue
@@ -0,0 +1,130 @@
+
+
+
+ Menu Cepat:
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+ {{ item.label }}
+
+
+
diff --git a/app/components/app/encounter/status-badge.vue b/app/components/app/encounter/status-badge.vue
index c8d4cd06..2ddd6549 100644
--- a/app/components/app/encounter/status-badge.vue
+++ b/app/components/app/encounter/status-badge.vue
@@ -12,7 +12,7 @@ const statusCodeColors: Record = {
review: 'fresh',
process: 'fresh',
done: 'positive',
- canceled: 'destructive',
+ cancel: 'destructive',
rejected: 'destructive',
skiped: 'negative',
}
diff --git a/app/components/app/general-consent/entry.vue b/app/components/app/general-consent/entry.vue
new file mode 100644
index 00000000..75e30cec
--- /dev/null
+++ b/app/components/app/general-consent/entry.vue
@@ -0,0 +1,214 @@
+
+
+
+
+
diff --git a/app/components/app/general-consent/list.cfg.ts b/app/components/app/general-consent/list.cfg.ts
new file mode 100644
index 00000000..c2f57c54
--- /dev/null
+++ b/app/components/app/general-consent/list.cfg.ts
@@ -0,0 +1,82 @@
+import type { Config, RecComponent, RecStrFuncComponent, RecStrFuncUnknown } from '~/components/pub/my-ui/data-table'
+import { defineAsyncComponent } from 'vue'
+import type { GeneralConsent } from '~/models/general-consent'
+
+type SmallDetailDto = any
+
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
+export const config: Config = {
+ cols: [{ width: 100 }, {}, {}, {}, { width: 50 }],
+ headers: [
+ [
+ { label: 'Tanggal' },
+ { label: 'Anggota Keluarga' },
+ { label: 'Penanggung Jawab' },
+ { label: 'Pemberi Informasi' },
+ { label: 'Saksi 1' },
+ { label: 'Saksi 2' },
+ { label: '' },
+ ],
+ ],
+ keys: ['date', 'relatives', 'responsible', 'informant', 'witness1', 'witness2', 'action'],
+ delKeyNames: [
+ { key: 'data', label: 'Tanggal' },
+ { key: 'dstDoctor.name', label: 'Dokter' },
+ ],
+ parses: {
+ date(rec) {
+ const recX = rec as GeneralConsent
+ return recX?.createdAt?.substring(0, 10) || '-'
+ },
+ relatives(rec) {
+ const recX = rec as GeneralConsent
+ const parsed = JSON.parse(recX?.value || '{}')
+ return parsed?.relatives?.join(', ') || '-'
+ },
+ responsible(rec) {
+ const recX = rec as GeneralConsent
+ const parsed = JSON.parse(recX?.value || '{}')
+ return parsed?.responsible || '-'
+ },
+ informant(rec) {
+ const recX = rec as GeneralConsent
+ const parsed = JSON.parse(recX?.value || '{}')
+ return parsed?.informant || '-'
+ },
+ witness1(rec) {
+ const recX = rec as GeneralConsent
+ const parsed = JSON.parse(recX?.value || '{}')
+ return parsed?.witness1 || '-'
+ },
+ witness2(rec) {
+ const recX = rec as GeneralConsent
+ const parsed = JSON.parse(recX?.value || '{}')
+ return parsed?.witness2 || '-'
+ },
+ action(rec, idx) {
+ const res: RecComponent = {
+ idx,
+ rec: rec as object,
+ component: action,
+ props: {
+ size: 'sm',
+ },
+ }
+ return res
+ },
+ },
+ components: {
+ action(rec, idx) {
+ const res: RecComponent = {
+ idx,
+ rec: rec as object,
+ component: action,
+ props: {
+ size: 'sm',
+ },
+ }
+ return res
+ },
+ } as RecStrFuncComponent,
+ htmls: {} as RecStrFuncUnknown,
+}
diff --git a/app/components/app/general-consent/list.vue b/app/components/app/general-consent/list.vue
new file mode 100644
index 00000000..46f595f5
--- /dev/null
+++ b/app/components/app/general-consent/list.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
diff --git a/app/components/app/installation-position/entry-form.vue b/app/components/app/installation-position/entry-form.vue
index 4f7885a1..1bf65323 100644
--- a/app/components/app/installation-position/entry-form.vue
+++ b/app/components/app/installation-position/entry-form.vue
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [installation, installationAttrs] = defineField('installation_id')
+const [installation, installationAttrs] = defineField('installation_code')
const [employee, employeeAttrs] = defineField('employee_id')
const [headStatus, headStatusAttrs] = defineField('headStatus')
@@ -62,8 +62,8 @@ const headStatusStr = computed({
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.installation_id !== undefined)
- installation.value = props.values.installation_id ? Number(props.values.installation_id) : null
+ if (props.values.installation_code !== undefined)
+ installation.value = props.values.installation_code ? String(props.values.installation_code) : null
if (props.values.employee_id !== undefined)
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
@@ -83,7 +83,7 @@ function onSubmitForm() {
...genBase(),
name: name.value || '',
code: code.value || '',
- installation_id: installation.value || null,
+ installation_code: String(installation.value) || '',
employee_id: employee.value || null,
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
}
@@ -130,7 +130,7 @@ function onCancelForm() {
-
+
+// 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'
+
+// Types
+import type { BaseFormData } from '~/schemas/base.schema'
+
+// Helpers
+import type z from 'zod'
+import { useForm } from 'vee-validate'
+import { toTypedSchema } from '@vee-validate/zod'
+
+interface Props {
+ schema?: z.ZodSchema
+ values?: any
+ isLoading?: boolean
+ isReadonly?: boolean
+}
+
+const props = defineProps()
+const isLoading = props.isLoading !== undefined ? props.isLoading : false
+const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
+const emit = defineEmits<{
+ submit: [values: BaseFormData, resetForm: () => void]
+ cancel: [resetForm: () => void]
+}>()
+
+const { defineField, errors, meta } = useForm({
+ validationSchema: props.schema ? toTypedSchema(props.schema) : undefined,
+ initialValues: {
+ code: '',
+ name: '',
+ },
+})
+
+const [code, codeAttrs] = defineField('code')
+const [name, nameAttrs] = defineField('name')
+
+if (props.values) {
+ if (props.values.code !== undefined) code.value = props.values.code
+ if (props.values.name !== undefined) name.value = props.values.name
+}
+
+const resetForm = () => {
+ code.value = ''
+ name.value = ''
+}
+
+function onSubmitForm() {
+ const formData = {
+ name: name.value || '',
+ code: code.value || '',
+ }
+ emit('submit', formData, resetForm)
+}
+
+function onCancelForm() {
+ emit('cancel', resetForm)
+}
+
+
+
+
+
diff --git a/app/components/app/medicine-form/list-cfg.ts b/app/components/app/medicine-form/list-cfg.ts
new file mode 100644
index 00000000..5b66812a
--- /dev/null
+++ b/app/components/app/medicine-form/list-cfg.ts
@@ -0,0 +1,38 @@
+import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
+import { defineAsyncComponent } from 'vue'
+
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
+
+export const config: Config = {
+ cols: [{}, {}, { width: 50 }],
+
+ headers: [
+ [
+ { label: 'Kode' },
+ { label: 'Nama' },
+ { label: 'Aksi' },
+ ],
+ ],
+
+ keys: ['code', 'name', 'action'],
+
+ delKeyNames: [
+ { key: 'code', label: 'Kode' },
+ { key: 'name', label: 'Nama' },
+ ],
+
+ parses: {},
+
+ components: {
+ action(rec, idx) {
+ const res: RecComponent = {
+ idx,
+ rec: rec as object,
+ component: action,
+ }
+ return res
+ },
+ },
+
+ htmls: {},
+}
diff --git a/app/components/app/medicine-form/list.vue b/app/components/app/medicine-form/list.vue
new file mode 100644
index 00000000..e4544c2f
--- /dev/null
+++ b/app/components/app/medicine-form/list.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
diff --git a/app/components/app/medicine/entry-form.vue b/app/components/app/medicine/entry-form.vue
index 42989fcb..af4df34f 100644
--- a/app/components/app/medicine/entry-form.vue
+++ b/app/components/app/medicine/entry-form.vue
@@ -18,6 +18,7 @@ interface Props {
isReadonly?: boolean
medicineGroups?: { value: string; label: string }[]
medicineMethods?: { value: string; label: string }[]
+ medicineForms?: { value: string; label: string }[]
uoms?: { value: string; label: string }[]
}
@@ -36,6 +37,7 @@ const { defineField, errors, meta } = useForm({
name: '',
medicineGroup_code: '',
medicineMethod_code: '',
+ medicineForm_code: '',
uom_code: '',
stock: 0,
},
@@ -45,6 +47,7 @@ const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [medicineGroup_code, medicineGroupAttrs] = defineField('medicineGroup_code')
const [medicineMethod_code, medicineMethodAttrs] = defineField('medicineMethod_code')
+const [medicineForm_code, medicineFormAttrs] = defineField('medicineForm_code')
const [uom_code, uomAttrs] = defineField('uom_code')
const [stock, stockAttrs] = defineField('stock')
@@ -53,6 +56,7 @@ if (props.values) {
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.medicineGroup_code !== undefined) medicineGroup_code.value = props.values.medicineGroup_code
if (props.values.medicineMethod_code !== undefined) medicineMethod_code.value = props.values.medicineMethod_code
+ if (props.values.medicineForm_code !== undefined) medicineForm_code.value = props.values.medicineForm_code
if (props.values.uom_code !== undefined) uom_code.value = props.values.uom_code
if (props.values.stock !== undefined) stock.value = props.values.stock
}
@@ -62,6 +66,7 @@ const resetForm = () => {
name.value = ''
medicineGroup_code.value = ''
medicineMethod_code.value = ''
+ medicineForm_code.value = '',
uom_code.value = ''
stock.value = 0
}
@@ -72,6 +77,7 @@ function onSubmitForm() {
name: name.value || '',
medicineGroup_code: medicineGroup_code.value || '',
medicineMethod_code: medicineMethod_code.value || '',
+ medicineForm_code: medicineForm_code.value || '',
uom_code: uom_code.value || '',
stock: stock.value || 0,
}
@@ -138,6 +144,20 @@ function onCancelForm() {
/>
|
+
+
+
+
+
+ |
diff --git a/app/components/app/medicine/list-cfg.ts b/app/components/app/medicine/list-cfg.ts
index 059022c8..5d1740f8 100644
--- a/app/components/app/medicine/list-cfg.ts
+++ b/app/components/app/medicine/list-cfg.ts
@@ -15,12 +15,13 @@ export const config: Config = {
{ label: 'Golongan' },
{ label: 'Metode Pemberian' },
{ label: 'Satuan' },
+ { label: 'Sediaan' },
{ label: 'Stok' },
{ label: 'Aksi' },
],
],
- keys: ['code', 'name', 'group', 'method', 'unit', 'stock', 'action'],
+ keys: ['code', 'name', 'group', 'method', 'unit', 'form', 'stock', 'action'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
@@ -37,6 +38,9 @@ export const config: Config = {
unit: (rec: unknown): unknown => {
return (rec as SmallDetailDto).uom?.name || '-'
},
+ form: (rec: unknown): unknown => {
+ return (rec as SmallDetailDto).medicineForm?.name || '-'
+ },
},
components: {
diff --git a/app/components/app/prescription-item/list-entry.cfg.ts b/app/components/app/prescription-item/list-entry.cfg.ts
index 070dd65f..cfc6bdbd 100644
--- a/app/components/app/prescription-item/list-entry.cfg.ts
+++ b/app/components/app/prescription-item/list-entry.cfg.ts
@@ -11,16 +11,17 @@ export const config: Config = {
headers: [
[
{ label: 'Nama' },
+ { label: 'Cara Buat' },
{ label: 'Bentuk' },
{ label: 'Freq' },
{ label: 'Dosis' },
- { label: 'Interval' },
+ // { label: 'Interval' },
{ label: 'Total' },
{ label: '' },
],
],
- keys: ['name', 'uom_code', 'frequency', 'multiplier', 'interval', 'total', 'action'],
+ keys: ['medicine.name', 'isMix', 'medicine.medicineForm.name', 'frequency', 'dose', 'quantity', 'action'], //
delKeyNames: [
{ key: 'code', label: 'Kode' },
@@ -28,17 +29,8 @@ export const config: Config = {
],
parses: {
- cateogry: (rec: unknown): unknown => {
- return (rec as SmallDetailDto).medicineCategory?.name || '-'
- },
- group: (rec: unknown): unknown => {
- return (rec as SmallDetailDto).medicineGroup?.name || '-'
- },
- method: (rec: unknown): unknown => {
- return (rec as SmallDetailDto).medicineMethod?.name || '-'
- },
- unit: (rec: unknown): unknown => {
- return (rec as SmallDetailDto).medicineUnit?.name || '-'
+ isMix: (rec: unknown): unknown => {
+ return (rec as SmallDetailDto).isMix ? 'Racikan' : 'Non Racikan'
},
},
diff --git a/app/components/app/prescription-item/list.cfg.ts b/app/components/app/prescription-item/list.cfg.ts
index fd980bb1..5a3447ef 100644
--- a/app/components/app/prescription-item/list.cfg.ts
+++ b/app/components/app/prescription-item/list.cfg.ts
@@ -12,12 +12,11 @@ export const config: Config = {
{ label: 'Bentuk' },
{ label: 'Freq' },
{ label: 'Dosis' },
- { label: 'Interval' },
{ label: 'Total' },
],
],
- keys: ['name', 'uom_code', 'frequency', 'multiplier', 'interval', 'total'],
+ keys: ['medicine.name', 'medicine.medicineForm.name', 'frequency', 'dose', 'total'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
diff --git a/app/components/app/prescription-item/list.vue b/app/components/app/prescription-item/list.vue
index ed64b09e..d8c4c107 100644
--- a/app/components/app/prescription-item/list.vue
+++ b/app/components/app/prescription-item/list.vue
@@ -1,4 +1,6 @@
-
diff --git a/app/components/app/prescription-item/mix-entry.vue b/app/components/app/prescription-item/mix-entry.vue
index d9b4881e..05960307 100644
--- a/app/components/app/prescription-item/mix-entry.vue
+++ b/app/components/app/prescription-item/mix-entry.vue
@@ -1,29 +1,36 @@
-
-
- Nama
-
+
+
+ Nama
+
+
+
- Frequensi
-
+ Frequensi
+
- Dosis
-
+ Dosis
+
- Sediaan
-
+ Sediaan
+
- Total
-
+ Total
+
-
- Cara Pakai
-
+
+ Cara Pakai
+
Daftar Obat
@@ -77,20 +94,29 @@ function addItem() {
Nama
Dosis
- Satuan
- ..
+
+
-
+
-
+
-
+
+
+
+
+
@@ -102,7 +128,7 @@ function addItem() {
-
+
Tambah
diff --git a/app/components/app/prescription-item/non-mix-entry.vue b/app/components/app/prescription-item/non-mix-entry.vue
index 25970646..78ee9ada 100644
--- a/app/components/app/prescription-item/non-mix-entry.vue
+++ b/app/components/app/prescription-item/non-mix-entry.vue
@@ -2,40 +2,45 @@
import * as DE from '~/components/pub/my-ui/doc-entry'
import Separator from '~/components/pub/ui/separator/Separator.vue'
import Nav from '~/components/pub/my-ui/nav-footer/cl-sa.vue'
+import * as CB from '~/components/pub/my-ui/combobox'
-import { bigTimeUnitCodes } from '~/lib/constants'
+// import { bigTimeUnitCodes } from '~/lib/constants'
+import { type Medicine } from '~/models/medicine';
import type { PrescriptionItem } from '~/models/prescription-item'
const props = defineProps<{
data: PrescriptionItem
+ medicines: Medicine[]
}>()
+const { medicines } = toRefs(props)
+const medicineItems = ref([])
+const medicineForm = computed(() => {
+ const medicine = props.medicines.find(m => m.code === props.data.medicine_code)
+ return medicine ? medicine.medicineForm?.name : '--tidak diketahui--'
+})
+
type ClickType = 'close' | 'save'
type Item = {
value: string
label: string
}
-const bigTimeUnitCodeItems: Item[] = []
-
if(!props.data.intervalUnit_code) {
props.data.intervalUnit_code = 'day'
}
-Object.keys(bigTimeUnitCodes).forEach((key) => {
- bigTimeUnitCodeItems.push({
- value: key,
- label: bigTimeUnitCodes[key] || '',
- })
-})
-
-
const emit = defineEmits<{
close: [],
save: [data: PrescriptionItem],
+ 'update:searchText': [value: string]
}>()
+watch(medicines, (data) => {
+ medicineItems.value = CB.objectsToItems(data, 'code', 'name')
+})
+
function navClick(type: ClickType) {
if (type === 'close') {
emit('close')
@@ -43,13 +48,23 @@ function navClick(type: ClickType) {
emit('save', props.data)
}
}
+
+function searchMedicineText(value: string) {
+ emit('update:searchText', value)
+}
-
-
- Nama
-
+
+
+ Nama
+
+
+
Frequensi
@@ -59,11 +74,7 @@ function navClick(type: ClickType) {
Dosis
-
- Sediaan
-
-
-
+
Total
-
+
-
+
+ Sediaan
+
+
+
Cara Pakai
-
+
diff --git a/app/components/app/prescription/detail.vue b/app/components/app/prescription/detail.vue
index 694eb9a1..21962532 100644
--- a/app/components/app/prescription/detail.vue
+++ b/app/components/app/prescription/detail.vue
@@ -13,7 +13,7 @@ const props = defineProps<{
Order {{ data.issuedAt?.substring(0, 10) || data.createdAt?.substring(0, 10) }} - {{ data.status_code }}
|
-
+
DPJP
diff --git a/app/components/app/prescription/entry.vue b/app/components/app/prescription/entry.vue
index ef8756e8..28041eea 100644
--- a/app/components/app/prescription/entry.vue
+++ b/app/components/app/prescription/entry.vue
@@ -1,17 +1,25 @@
+
+
-
+
+
diff --git a/app/components/app/prescription/flat-list.vue b/app/components/app/prescription/flat-list.vue
new file mode 100644
index 00000000..fe0bab9c
--- /dev/null
+++ b/app/components/app/prescription/flat-list.vue
@@ -0,0 +1,86 @@
+
+
+
+
+ Belum Ada Data
+
+
+
+ Tambah Order
+
+
+
+
+
+
+ Tgl Order
+ DPJP
+ PPDS
+ Jenis Obat
+ Status
+
+
+
+
+
+
+ {{ item.issuedAt?.substring(0, 10) || item.createdAt?.substring(0, 10) }}
+
+
+ {{ item.doctor?.employee?.person?.name || '-' }}
+
+
+
+
+
+ Racikan: {{ item.items.filter(function(element){ return element.isMix}).length }}
+
+
+ Non Racikan: {{ item.items.filter(function(element){ return !element.isMix}).length }}
+
+
+
+ {{ item.status_code }}
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/prescription/list.vue b/app/components/app/prescription/grouped-list.vue
similarity index 95%
rename from app/components/app/prescription/list.vue
rename to app/components/app/prescription/grouped-list.vue
index ae5126ca..07d6fcaa 100644
--- a/app/components/app/prescription/list.vue
+++ b/app/components/app/prescription/grouped-list.vue
@@ -5,7 +5,6 @@ import Nav from '~/components/pub/my-ui/nav-footer/ca-ed-su.vue'
import type { Prescription } from '~/models/prescription';
import PrescriptionItem from '~/components/app/prescription-item/list.vue';
-import { add } from 'date-fns';
interface Props {
data: Prescription[]
@@ -66,7 +65,7 @@ function navClick(type: 'cancel' | 'edit' | 'submit', data: Prescription): void
/>
-
+
diff --git a/app/components/app/prescription/list-with-sub.vue b/app/components/app/prescription/list-with-sub.vue
index 14bc1785..4d1a2cdf 100644
--- a/app/components/app/prescription/list-with-sub.vue
+++ b/app/components/app/prescription/list-with-sub.vue
@@ -1,7 +1,5 @@
@@ -17,40 +15,40 @@
-
-
- Order #1
-
-
+
+
+ Order #1
+
+
2025-01-01
-
-
-
- Status
-
-
+
+
+
+ Status
+
+
Status
-
-
-
+
+
+
-
-
- DPJP
-
-
+
+
+ DPJP
+
+
Nama Dokter
-
-
-
- PPDS
-
-
+
+
+
+ PPDS
+
+
Nama PPDS
-
-
-
+
+
+
diff --git a/app/components/app/resume/_common/print-btn.vue b/app/components/app/resume/_common/print-btn.vue
new file mode 100644
index 00000000..5688d007
--- /dev/null
+++ b/app/components/app/resume/_common/print-btn.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+ {{ props.btnTxt || 'Lampiran' }}
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/_common/select-arrangement.vue b/app/components/app/resume/_common/select-arrangement.vue
new file mode 100644
index 00000000..7e236ff0
--- /dev/null
+++ b/app/components/app/resume/_common/select-arrangement.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-concious-level.vue b/app/components/app/resume/_common/select-concious-level.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-concious-level.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-date.vue b/app/components/app/resume/_common/select-date.vue
new file mode 100644
index 00000000..74245e7e
--- /dev/null
+++ b/app/components/app/resume/_common/select-date.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+ {
+ const dateStr = typeof value === 'number' ? String(value) : value
+ patientAge = calculateAge(dateStr)
+ }
+ "
+ />
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-death-cause.vue b/app/components/app/resume/_common/select-death-cause.vue
new file mode 100644
index 00000000..a155b139
--- /dev/null
+++ b/app/components/app/resume/_common/select-death-cause.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-faskes.vue b/app/components/app/resume/_common/select-faskes.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-faskes.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-following-arrangement.vue b/app/components/app/resume/_common/select-following-arrangement.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-following-arrangement.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-hospital-leave-condition.vue b/app/components/app/resume/_common/select-hospital-leave-condition.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-hospital-leave-condition.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-hospital-leave-method.vue b/app/components/app/resume/_common/select-hospital-leave-method.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-hospital-leave-method.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-icd-10.vue b/app/components/app/resume/_common/select-icd-10.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-icd-10.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-icd-9.vue b/app/components/app/resume/_common/select-icd-9.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-icd-9.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-national-program-service-status.vue b/app/components/app/resume/_common/select-national-program-service-status.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-national-program-service-status.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-national-program-service.vue b/app/components/app/resume/_common/select-national-program-service.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-national-program-service.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-pain-scale.vue b/app/components/app/resume/_common/select-pain-scale.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-pain-scale.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-primary-diagnosis.vue b/app/components/app/resume/_common/select-primary-diagnosis.vue
new file mode 100644
index 00000000..0852195b
--- /dev/null
+++ b/app/components/app/resume/_common/select-primary-diagnosis.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/select-secondary-diagnosis.vue b/app/components/app/resume/_common/select-secondary-diagnosis.vue
new file mode 100644
index 00000000..a155b139
--- /dev/null
+++ b/app/components/app/resume/_common/select-secondary-diagnosis.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/app/resume/_common/verify-badge.vue b/app/components/app/resume/_common/verify-badge.vue
new file mode 100644
index 00000000..8a999895
--- /dev/null
+++ b/app/components/app/resume/_common/verify-badge.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+ {{ statusText }}
+
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/add.vue b/app/components/app/resume/add.vue
new file mode 100644
index 00000000..d5fa3370
--- /dev/null
+++ b/app/components/app/resume/add.vue
@@ -0,0 +1,482 @@
+
+
+
+
+
diff --git a/app/components/app/resume/history-list/action-history-dialog.vue b/app/components/app/resume/history-list/action-history-dialog.vue
new file mode 100644
index 00000000..5602016e
--- /dev/null
+++ b/app/components/app/resume/history-list/action-history-dialog.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/history-list/action-list.cfg.ts b/app/components/app/resume/history-list/action-list.cfg.ts
new file mode 100644
index 00000000..2dad3e6a
--- /dev/null
+++ b/app/components/app/resume/history-list/action-list.cfg.ts
@@ -0,0 +1,94 @@
+import type { Config } from '~/components/pub/my-ui/data-table'
+import type { Patient } from '~/models/patient'
+import { defineAsyncComponent } from 'vue'
+import { educationCodes, genderCodes } from '~/lib/constants'
+import { calculateAge } from '~/lib/utils'
+
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dvvp.vue'))
+
+export const config: Config = {
+ cols: [{width: 140}, {}, {}, {width: 140}, {width: 10},],
+
+ headers: [
+ [
+ { label: 'Tanggal/Jam' },
+ { label: 'Dokter' },
+ { label: 'Tempat Layanan' },
+ { label: 'Jenis' },
+ { label: 'Jenis Pemeriksaan' },
+ { label: 'Tanggal/Jam' },
+ ],
+ ],
+
+ keys: ['birth_date', 'person.name', 'person.name', 'person.name', 'person.name', 'birth_date',],
+
+ delKeyNames: [
+ { key: 'code', label: 'Kode' },
+ { key: 'name', label: 'Nama' },
+ ],
+
+ parses: {
+ patientId: (rec: unknown): unknown => {
+ const patient = rec as Patient
+ return patient.number
+ },
+ identity_number: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (person.nationality == 'WNA') {
+ return person.passportNumber
+ }
+
+ return person.residentIdentityNumber || '-'
+ },
+ birth_date: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (typeof person.birthDate == 'object' && person.birthDate) {
+ return (person.birthDate as Date).toLocaleDateString('id-ID')
+ } else if (typeof person.birthDate == 'string') {
+ return (person.birthDate as string).substring(0, 10)
+ }
+ return person.birthDate
+ },
+ patient_age: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+ return calculateAge(person.birthDate)
+ },
+ gender: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (typeof person.gender_code == 'number' && person.gender_code >= 0) {
+ return person.gender_code
+ } else if (typeof person.gender_code === 'string' && person.gender_code) {
+ return genderCodes[person.gender_code] || '-'
+ }
+ return '-'
+ },
+ education: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+ if (typeof person.education_code == 'number' && person.education_code >= 0) {
+ return person.education_code
+ } else if (typeof person.education_code === 'string' && person.education_code) {
+ return educationCodes[person.education_code] || '-'
+ }
+ return '-'
+ },
+ },
+
+ components: {
+ action(rec, idx) {
+ return {
+ idx,
+ rec: rec as object,
+ component: action,
+ }
+ },
+ },
+
+ htmls: {
+ patient_address(_rec) {
+ return '-'
+ },
+ },
+}
diff --git a/app/components/app/resume/history-list/consultation-history-dialog.vue b/app/components/app/resume/history-list/consultation-history-dialog.vue
new file mode 100644
index 00000000..ad1e777e
--- /dev/null
+++ b/app/components/app/resume/history-list/consultation-history-dialog.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/history-list/consultation-list.cfg.ts b/app/components/app/resume/history-list/consultation-list.cfg.ts
new file mode 100644
index 00000000..196c208c
--- /dev/null
+++ b/app/components/app/resume/history-list/consultation-list.cfg.ts
@@ -0,0 +1,51 @@
+import type { Config } from '~/components/pub/my-ui/data-table'
+import type { Patient } from '~/models/patient'
+import { defineAsyncComponent } from 'vue'
+
+const lampiranBtn = defineAsyncComponent(() => import('../_common/print-btn.vue'))
+
+export const config: Config = {
+ cols: [{}, {}, {}, {}, {},],
+
+ headers: [
+ [
+ { label: 'Tanggal/Jam' },
+ { label: 'Dokter' },
+ { label: 'Tempat Layanan' },
+ { label: 'KSM' },
+ { label: 'Tanggal/Jam' },
+ { label: 'Tujuan' },
+ { label: 'Dokter' },
+ { label: 'Berkas' },
+ ],
+ ],
+
+ keys: ['birth_date', 'person.name', 'person.name', 'person.name', 'person.name', 'birth_date','person.name', 'action', ],
+
+ parses: {
+ birth_date: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (typeof person.birthDate == 'object' && person.birthDate) {
+ return (person.birthDate as Date).toLocaleDateString('id-ID')
+ } else if (typeof person.birthDate == 'string') {
+ return (person.birthDate as string).substring(0, 10)
+ }
+ return person.birthDate
+ },
+ },
+
+ components: {
+ action(rec, idx) {
+ return {
+ idx,
+ rec: rec as object,
+ component: lampiranBtn,
+ }
+ },
+ },
+
+ htmls: {
+
+ },
+}
diff --git a/app/components/app/resume/history-list/farmacy-history-dialog.vue b/app/components/app/resume/history-list/farmacy-history-dialog.vue
new file mode 100644
index 00000000..53bf3875
--- /dev/null
+++ b/app/components/app/resume/history-list/farmacy-history-dialog.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/history-list/farmacy-list.cfg.ts b/app/components/app/resume/history-list/farmacy-list.cfg.ts
new file mode 100644
index 00000000..75ea1e80
--- /dev/null
+++ b/app/components/app/resume/history-list/farmacy-list.cfg.ts
@@ -0,0 +1,39 @@
+import type { Config } from '~/components/pub/my-ui/data-table'
+import type { Patient } from '~/models/patient'
+
+export const config: Config = {
+ cols: [{}, {}, {}, {}, {},],
+
+ headers: [
+ [
+ { label: 'Tanggal Order' },
+ { label: 'No Resep' },
+ { label: 'Tempat Layanan' },
+ { label: 'Nama Obat' },
+ { label: 'Tanggal Disetujui' },
+ ],
+ ],
+
+ keys: ['birth_date', 'person.name', 'person.name', 'person.name', 'birth_date',],
+
+ parses: {
+ birth_date: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (typeof person.birthDate == 'object' && person.birthDate) {
+ return (person.birthDate as Date).toLocaleDateString('id-ID')
+ } else if (typeof person.birthDate == 'string') {
+ return (person.birthDate as string).substring(0, 10)
+ }
+ return person.birthDate
+ },
+ },
+
+ components: {
+
+ },
+
+ htmls: {
+
+ },
+}
diff --git a/app/components/app/resume/history-list/national-program-history-dialog.vue b/app/components/app/resume/history-list/national-program-history-dialog.vue
new file mode 100644
index 00000000..cc702227
--- /dev/null
+++ b/app/components/app/resume/history-list/national-program-history-dialog.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/history-list/national-program-list.cfg.ts b/app/components/app/resume/history-list/national-program-list.cfg.ts
new file mode 100644
index 00000000..fe6d3ea0
--- /dev/null
+++ b/app/components/app/resume/history-list/national-program-list.cfg.ts
@@ -0,0 +1,30 @@
+import type { Config } from '~/components/pub/my-ui/data-table'
+import type { Patient } from '~/models/patient'
+
+export const config: Config = {
+ cols: [{}, {}, {}, {}, {},],
+
+ headers: [
+ [
+ { label: 'Nomor' },
+ { label: 'Layanan Program Nasional' },
+ { label: 'Status' },
+ ],
+ ],
+
+ keys: ['person.name', 'person.name', 'person.name',],
+
+ parses: {
+ // birth_date: (rec: unknown): unknown => {
+
+ // },
+ },
+
+ components: {
+
+ },
+
+ htmls: {
+
+ },
+}
diff --git a/app/components/app/resume/history-list/supporting-history-dialog.vue b/app/components/app/resume/history-list/supporting-history-dialog.vue
new file mode 100644
index 00000000..32fb4508
--- /dev/null
+++ b/app/components/app/resume/history-list/supporting-history-dialog.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/history-list/supporting-list.cfg.ts b/app/components/app/resume/history-list/supporting-list.cfg.ts
new file mode 100644
index 00000000..f005042d
--- /dev/null
+++ b/app/components/app/resume/history-list/supporting-list.cfg.ts
@@ -0,0 +1,39 @@
+import type { Config } from '~/components/pub/my-ui/data-table'
+import type { Patient } from '~/models/patient'
+
+export const config: Config = {
+ cols: [{}, {}, {}, {}, {},],
+
+ headers: [
+ [
+ { label: 'Tanggal Order' },
+ { label: 'No Lab' },
+ { label: 'Nama Pemeriksaan' },
+ { label: 'Tanggal Pemeriksaan' },
+ ],
+ ],
+
+ keys: ['birth_date', 'person.name', 'person.name', 'birth_date',],
+
+ parses: {
+ birth_date: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (typeof person.birthDate == 'object' && person.birthDate) {
+ return (person.birthDate as Date).toLocaleDateString('id-ID')
+ } else if (typeof person.birthDate == 'string') {
+ return (person.birthDate as string).substring(0, 10)
+ }
+ return person.birthDate
+ },
+ },
+
+ components: {
+ },
+
+ htmls: {
+ // patient_address(_rec) {
+ // return '-'
+ // },
+ },
+}
diff --git a/app/components/app/resume/list.cfg.ts b/app/components/app/resume/list.cfg.ts
new file mode 100644
index 00000000..b756b1ee
--- /dev/null
+++ b/app/components/app/resume/list.cfg.ts
@@ -0,0 +1,101 @@
+import type { Config } from '~/components/pub/my-ui/data-table'
+import type { Patient } from '~/models/patient'
+import { defineAsyncComponent } from 'vue'
+import { educationCodes, genderCodes } from '~/lib/constants'
+import { calculateAge } from '~/lib/utils'
+
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dvvp.vue'))
+const statusBadge = defineAsyncComponent(() => import('./_common/verify-badge.vue'))
+
+export const config: Config = {
+ cols: [{width: 140}, {}, {}, {width: 140}, {width: 10},],
+
+ headers: [
+ [
+ { label: 'Tgl Simpan' },
+ { label: 'DPJP' },
+ { label: 'KSM' },
+ { label: 'Status' },
+ { label: 'Action' },
+ ],
+ ],
+
+ keys: ['birth_date', 'number', 'person.name', 'status', 'action'],
+
+ delKeyNames: [
+ { key: 'code', label: 'Kode' },
+ { key: 'name', label: 'Nama' },
+ ],
+
+ parses: {
+ patientId: (rec: unknown): unknown => {
+ const patient = rec as Patient
+ return patient.number
+ },
+ identity_number: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (person.nationality == 'WNA') {
+ return person.passportNumber
+ }
+
+ return person.residentIdentityNumber || '-'
+ },
+ birth_date: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (typeof person.birthDate == 'object' && person.birthDate) {
+ return (person.birthDate as Date).toLocaleDateString('id-ID')
+ } else if (typeof person.birthDate == 'string') {
+ return (person.birthDate as string).substring(0, 10)
+ }
+ return person.birthDate
+ },
+ patient_age: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+ return calculateAge(person.birthDate)
+ },
+ gender: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+
+ if (typeof person.gender_code == 'number' && person.gender_code >= 0) {
+ return person.gender_code
+ } else if (typeof person.gender_code === 'string' && person.gender_code) {
+ return genderCodes[person.gender_code] || '-'
+ }
+ return '-'
+ },
+ education: (rec: unknown): unknown => {
+ const { person } = rec as Patient
+ if (typeof person.education_code == 'number' && person.education_code >= 0) {
+ return person.education_code
+ } else if (typeof person.education_code === 'string' && person.education_code) {
+ return educationCodes[person.education_code] || '-'
+ }
+ return '-'
+ },
+ },
+
+ components: {
+ action(rec, idx) {
+ return {
+ idx,
+ rec: rec as object,
+ component: action,
+ }
+ },
+ status(rec, idx) {
+ return {
+ idx,
+ rec: rec as object,
+ component: statusBadge,
+ }
+ },
+ },
+
+ htmls: {
+ patient_address(_rec) {
+ return '-'
+ },
+ },
+}
diff --git a/app/components/app/resume/list.vue b/app/components/app/resume/list.vue
new file mode 100644
index 00000000..0d62cc6c
--- /dev/null
+++ b/app/components/app/resume/list.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/resume/verify-dialog.vue b/app/components/app/resume/verify-dialog.vue
new file mode 100644
index 00000000..ca4fb554
--- /dev/null
+++ b/app/components/app/resume/verify-dialog.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/app/sep/entry-form.vue b/app/components/app/sep/entry-form.vue
index 35956ad7..0aa33178 100644
--- a/app/components/app/sep/entry-form.vue
+++ b/app/components/app/sep/entry-form.vue
@@ -143,12 +143,16 @@ watch(props, (value) => {
nationalId.value = objects?.nationalIdentity || '-'
medicalRecordNumber.value = objects?.medicalRecordNumber || '-'
patientName.value = objects?.patientName || '-'
+ phoneNumber.value = objects?.phoneNumber || '-'
if (objects?.sepType === 'internal') {
admissionType.value = '4'
}
if (objects?.sepType === 'external') {
admissionType.value = '1'
}
+ if (objects?.diagnoseLabel) {
+ initialDiagnosis.value = objects?.diagnoseLabel
+ }
isDateReload.value = true
setTimeout(() => {
if (objects?.letterDate) {
@@ -176,6 +180,9 @@ onMounted(() => {
if (!isService.value) {
serviceType.value = '2'
}
+ if (!admissionType.value) {
+ admissionType.value = '1'
+ }
})
diff --git a/app/components/app/sep/list.vue b/app/components/app/sep/list.vue
index 2afaa2cc..78a0b740 100644
--- a/app/components/app/sep/list.vue
+++ b/app/components/app/sep/list.vue
@@ -11,7 +11,8 @@ const props = defineProps<{
diff --git a/app/components/app/soapi/list-cfg.ts b/app/components/app/soapi/list-cfg.ts
index 648297c4..3db1b24a 100644
--- a/app/components/app/soapi/list-cfg.ts
+++ b/app/components/app/soapi/list-cfg.ts
@@ -3,7 +3,7 @@ import { defineAsyncComponent } from 'vue'
type SmallDetailDto = any
-const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
+const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
export const config: Config = {
cols: [{}, {}, {}, { width: 100 }, { width: 120 }, {}, {}, {}, { width: 100 }, { width: 100 }, {}, { width: 50 }],
@@ -20,7 +20,7 @@ export const config: Config = {
],
],
- keys: ['time', 'employee_id', 'main_complaint', 'encounter_id', 'diagnose', 'status', 'action'],
+ keys: ['time', 'employee_id', 'main_complaint', 'encounter', 'diagnose', 'status', 'action'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
@@ -44,6 +44,10 @@ export const config: Config = {
return '-'
}
},
+ encounter(rec: any) {
+ const data = rec?.encounter ?? {}
+ return data?.class_code || '-'
+ },
diagnose(rec: any) {
const { value } = rec ?? {}
diff --git a/app/components/app/specialist-position/entry-detail.vue b/app/components/app/specialist-position/entry-detail.vue
index f21ff65f..099927f6 100644
--- a/app/components/app/specialist-position/entry-detail.vue
+++ b/app/components/app/specialist-position/entry-detail.vue
@@ -18,7 +18,7 @@ import { genSpecialistPosition } from '~/models/specialist-position'
interface Props {
schema: z.ZodSchema
- specialistId: number
+ specialistId: string
employees: any[]
values: any
isLoading?: boolean
diff --git a/app/components/app/specialist-position/entry-form.vue b/app/components/app/specialist-position/entry-form.vue
index be031219..e1ef6009 100644
--- a/app/components/app/specialist-position/entry-form.vue
+++ b/app/components/app/specialist-position/entry-form.vue
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [specialist, specialistAttrs] = defineField('specialist_id')
+const [specialist, specialistAttrs] = defineField('specialist_code')
const [employee, employeeAttrs] = defineField('employee_id')
const [headStatus, headStatusAttrs] = defineField('headStatus')
@@ -62,8 +62,8 @@ const headStatusStr = computed({
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.specialist_id !== undefined)
- specialist.value = props.values.specialist_id ? Number(props.values.specialist_id) : null
+ if (props.values.specialist_code !== undefined)
+ specialist.value = props.values.specialist_code ? String(props.values.specialist_code) : null
if (props.values.employee_id !== undefined)
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
@@ -72,7 +72,7 @@ if (props.values) {
const resetForm = () => {
code.value = ''
name.value = ''
- specialist.value = null
+ specialist.value = ''
employee.value = null
headStatus.value = false
}
@@ -83,7 +83,7 @@ function onSubmitForm() {
...genBase(),
name: name.value || '',
code: code.value || '',
- specialist_id: specialist.value || null,
+ specialist_code: specialist.value || '',
employee_id: employee.value || null,
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
}
@@ -130,7 +130,7 @@ function onCancelForm() {
-
+
,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [unit, unitAttrs] = defineField('unit_id')
+const [unit, unitAttrs] = defineField('unit_code')
// Fill fields from props.values if provided
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.unit_id !== undefined) unit.value = props.values.unit_id ? String(props.values.unit_id) : null
+ if (props.values.unit_code !== undefined) unit.value = props.values.unit_code ? String(props.values.unit_code) : null
}
const resetForm = () => {
@@ -63,7 +63,7 @@ function onSubmitForm(values: any) {
...genBase(),
name: name.value || '',
code: code.value || '',
- unit_id: unit.value ? Number(unit.value) : null,
+ unit_code: unit.value ? String(unit.value) : null,
}
emit('submit', formData, resetForm)
}
diff --git a/app/components/app/subspecialist-position/entry-detail.vue b/app/components/app/subspecialist-position/entry-detail.vue
index fd3f91b7..2a3e5e1c 100644
--- a/app/components/app/subspecialist-position/entry-detail.vue
+++ b/app/components/app/subspecialist-position/entry-detail.vue
@@ -18,7 +18,7 @@ import { genSubSpecialistPosition } from '~/models/subspecialist-position'
interface Props {
schema: z.ZodSchema
- subspecialistId: number
+ subspecialistId: string
employees: any[]
values: any
isLoading?: boolean
diff --git a/app/components/app/subspecialist-position/entry-form.vue b/app/components/app/subspecialist-position/entry-form.vue
index c897b2f5..4522d82c 100644
--- a/app/components/app/subspecialist-position/entry-form.vue
+++ b/app/components/app/subspecialist-position/entry-form.vue
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [subSpecialist, subSpecialistAttrs] = defineField('subspecialist_id')
+const [subSpecialist, subSpecialistAttrs] = defineField('subspecialist_code')
const [employee, employeeAttrs] = defineField('employee_id')
const [headStatus, headStatusAttrs] = defineField('headStatus')
@@ -62,8 +62,8 @@ const headStatusStr = computed({
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.subspecialist_id !== undefined)
- subSpecialist.value = props.values.subspecialist_id ? Number(props.values.subspecialist_id) : null
+ if (props.values.subspecialist_code !== undefined)
+ subSpecialist.value = props.values.subspecialist_code ? String(props.values.subspecialist_code) : null
if (props.values.employee_id !== undefined)
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
@@ -83,7 +83,7 @@ function onSubmitForm() {
...genBase(),
name: name.value || '',
code: code.value || '',
- subspecialist_id: subSpecialist.value || null,
+ subspecialist_code: subSpecialist.value || null,
employee_id: employee.value || null,
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
}
@@ -130,7 +130,7 @@ function onCancelForm() {
|
-
+
,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [specialist, specialistAttrs] = defineField('specialist_id')
+const [specialist, specialistAttrs] = defineField('specialist_code')
// Fill fields from props.values if provided
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.specialist_id !== undefined)
- specialist.value = props.values.specialist_id ? String(props.values.specialist_id) : null
+ if (props.values.specialist_code !== undefined)
+ specialist.value = props.values.specialist_code ? String(props.values.specialist_code) : null
}
const resetForm = () => {
@@ -64,7 +64,7 @@ function onSubmitForm(values: any) {
...genBase(),
name: name.value || '',
code: code.value || '',
- specialist_id: specialist.value ? Number(specialist.value) : null,
+ specialist_code: specialist.value ? String(specialist.value) : "",
}
emit('submit', formData, resetForm)
}
diff --git a/app/components/app/unit-position/entry-detail.vue b/app/components/app/unit-position/entry-detail.vue
deleted file mode 100644
index fb84e41a..00000000
--- a/app/components/app/unit-position/entry-detail.vue
+++ /dev/null
@@ -1,192 +0,0 @@
-
-
-
-
-
diff --git a/app/components/app/unit-position/entry-form.vue b/app/components/app/unit-position/entry-form.vue
index b6ab8609..ea2efc09 100644
--- a/app/components/app/unit-position/entry-form.vue
+++ b/app/components/app/unit-position/entry-form.vue
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [unit, unitAttrs] = defineField('unit_id')
+const [unit, unitAttrs] = defineField('unit_code')
const [employee, employeeAttrs] = defineField('employee_id')
const [headStatus, headStatusAttrs] = defineField('headStatus')
@@ -62,7 +62,7 @@ const headStatusStr = computed({
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.unit_id !== undefined) unit.value = props.values.unit_id ? Number(props.values.unit_id) : null
+ if (props.values.unit_code !== undefined) unit.value = props.values.unit_code ? String(props.values.unit_code) : null
if (props.values.employee_id !== undefined)
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
@@ -82,7 +82,7 @@ function onSubmitForm() {
...genBase(),
name: name.value || '',
code: code.value || '',
- unit_id: unit.value || null,
+ unit_code: unit.value || '',
employee_id: employee.value || null,
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
}
@@ -129,7 +129,7 @@ function onCancelForm() {
|
-
+
,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
-const [installation, installationAttrs] = defineField('installation_id')
+const [installation, installationAttrs] = defineField('installation_code')
// Fill fields from props.values if provided
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.installation_id !== undefined)
- installation.value = props.values.installation_id ? Number(props.values.installation_id) : null
+ if (props.values.installation_code !== undefined)
+ installation.value = props.values.installation_code ? String(props.values.installation_code) : null
}
const resetForm = () => {
@@ -62,7 +62,7 @@ function onSubmitForm() {
const formData: UnitFormData = {
name: name.value || '',
code: code.value || '',
- installation_id: installation.value ? Number(installation.value) : null,
+ installation_code: installation.value ? String(installation.value) : "",
}
emit('submit', formData, resetForm)
}
diff --git a/app/components/content/chemotherapy/process.vue b/app/components/content/chemotherapy/process.vue
index 7f355b4b..2a61c468 100644
--- a/app/components/content/chemotherapy/process.vue
+++ b/app/components/content/chemotherapy/process.vue
@@ -3,5 +3,5 @@ import EncounterHome from '~/components/content/encounter/home.vue'
-
+
diff --git a/app/components/content/cprj/entry.vue b/app/components/content/cprj/entry.vue
new file mode 100644
index 00000000..5769e967
--- /dev/null
+++ b/app/components/content/cprj/entry.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
diff --git a/app/components/content/cprj/form.vue b/app/components/content/cprj/form.vue
new file mode 100644
index 00000000..ef2a51d7
--- /dev/null
+++ b/app/components/content/cprj/form.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
diff --git a/app/components/content/cprj/list.vue b/app/components/content/cprj/list.vue
new file mode 100644
index 00000000..109bea6b
--- /dev/null
+++ b/app/components/content/cprj/list.vue
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+ handleActionRemove(recId, getMyList, toast)"
+ @cancel=""
+ >
+
+
+
+ ID:
+ {{ record?.id }}
+
+
+ Nama:
+ {{ record.name }}
+
+
+ Kode:
+ {{ record.code }}
+
+
+
+
+
diff --git a/app/components/content/device-order/entry.vue b/app/components/content/device-order/entry.vue
index 6d76d685..5d87d960 100644
--- a/app/components/content/device-order/entry.vue
+++ b/app/components/content/device-order/entry.vue
@@ -1,24 +1,177 @@
@@ -28,10 +181,74 @@ const headerPrep: HeaderPrep = {
class="mb-4 xl:mb-5"
/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ handleActionRemove(recId, getDeviceOrderItems, toast)"
+ @cancel=""
+ >
+
+
+ Nama
+
+
+ {{ recItem.device.name }}
+
+
+
+ Dosis
+
+
+ {{ recItem.quantity }}
+
+
+
+
diff --git a/app/components/content/device-order/list.vue b/app/components/content/device-order/list.vue
index e62a2fbd..1ffa324d 100644
--- a/app/components/content/device-order/list.vue
+++ b/app/components/content/device-order/list.vue
@@ -1,69 +1,39 @@
@@ -108,38 +145,39 @@ onMounted(async () => {
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
- class="mb-4 xl:mb-5"
/>
+
-
+
handleActionSubmit(recId, getMyList, toast)"
+ >
+
+
+
+
+ handleActionRemove(recId, getMyList, toast)"
- @cancel=""
>
-
-
-
- ID:
- {{ record?.id }}
-
-
- Nama:
- {{ record.name }}
-
-
- Kode:
- {{ record.code }}
-
-
-
+
diff --git a/app/components/content/device-order/main.vue b/app/components/content/device-order/main.vue
index ae5a9ca8..ff8722de 100644
--- a/app/components/content/device-order/main.vue
+++ b/app/components/content/device-order/main.vue
@@ -3,10 +3,14 @@
import List from './list.vue'
import Entry from './entry.vue'
-const { mode } = useQueryMode()
+defineProps<{
+ encounter_id: number
+}>()
+
+const { mode } = useQueryCRUDMode()
-
-
+
+
diff --git a/app/components/content/division-position/list.vue b/app/components/content/division-position/list.vue
index 4fa8605d..10bbc4e2 100644
--- a/app/components/content/division-position/list.vue
+++ b/app/components/content/division-position/list.vue
@@ -105,12 +105,12 @@ const getCurrentDivisionDetail = async (id: number | string) => {
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
- getCurrentDivisionDetail(recId.value)
+ getCurrentDivisionDetail(recItem.value.code)
title.value = 'Detail Divisi Position'
isReadonly.value = true
break
case ActionEvents.showEdit:
- getCurrentDivisionDetail(recId.value)
+ getCurrentDivisionDetail(recItem.value.code)
title.value = 'Edit Divisi Position'
isReadonly.value = false
break
@@ -122,7 +122,7 @@ watch([recId, recAction], () => {
onMounted(async () => {
try {
- divisions.value = await getDivisionLabelList({ sort: 'createdAt:asc', 'page-size': 100 })
+ divisions.value = await getDivisionLabelList({ sort: 'createdAt:asc', 'page-size': 100 }, true)
employees.value = await getEmployeeLabelList({ sort: 'createdAt:asc', 'page-size': 100, includes: 'person' })
await getDivisionList()
} catch (err) {
@@ -172,9 +172,8 @@ onMounted(async () => {
:is-readonly="isReadonly"
@submit="
(values: DivisionPositionFormData | Record, resetForm: () => void) => {
- console.log(values)
- if (recId > 0) {
- handleActionEdit(recId, values, getDivisionList, resetForm, toast)
+ if (recItem?.code.length > 0) {
+ handleActionEdit(recItem.code, values, getDivisionList, resetForm, toast)
return
}
handleActionSave(values, getDivisionList, resetForm, toast)
@@ -189,7 +188,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getDivisionList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getDivisionList, toast)"
@cancel=""
>
diff --git a/app/components/content/division/detail.vue b/app/components/content/division/detail.vue
index 8b7d2e92..e059702b 100644
--- a/app/components/content/division/detail.vue
+++ b/app/components/content/division/detail.vue
@@ -47,7 +47,7 @@ const title = ref('')
// #region Props & Emits
const props = defineProps<{
- divisionId: number
+ divisionId: string
}>()
const division = ref({} as Division)
// #endregion
@@ -152,7 +152,7 @@ watch([recId, recAction], () => {
console.log(recId, recAction)
switch (recAction.value) {
case ActionEvents.showEdit:
- getDetailDivisionPosition(recId.value)
+ getDetailDivisionPosition(recItem.value.code)
title.value = 'Edit Jabatan'
isReadonly.value = false
isFormEntryDialogOpen.value = true
@@ -201,8 +201,8 @@ watch([recId, recAction], () => {
@submit="
(values: DivisionPositionFormData | Record, resetForm: () => void) => {
console.log(values)
- if (recId > 0) {
- handleActionEdit(recId, values, getDivisionPositionList, onResetState, toast)
+ if (recItem?.code.length > 0) {
+ handleActionEdit(recItem.code, values, getDivisionPositionList, onResetState, toast)
return
}
handleActionSave(values, getDivisionPositionList, onResetState, toast)
@@ -215,7 +215,7 @@ watch([recId, recAction], () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getDivisionPositionList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getDivisionPositionList, toast)"
@cancel=""
>
diff --git a/app/components/content/division/list.vue b/app/components/content/division/list.vue
index 0456891b..ecfd0cb1 100644
--- a/app/components/content/division/list.vue
+++ b/app/components/content/division/list.vue
@@ -105,26 +105,23 @@ const getCurrentDivisionDetail = async (id: number | string) => {
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
- if (Number(recId.value) > 0) {
- const id = Number(recId.value)
+ if (recItem.value.code.length > 0) {
+ navigateTo({
+ name: 'org-src-division-id',
+ params: {
+ id: recItem.value.code,
+ },
+ })
recAction.value = ''
recItem.value = null
recId.value = 0
isFormEntryDialogOpen.value = false
isReadonly.value = false
-
- navigateTo({
- name: 'org-src-division-id',
- params: {
- id,
- },
- })
}
-
break
case ActionEvents.showEdit:
- getCurrentDivisionDetail(recId.value)
+ getCurrentDivisionDetail(recItem.value.code)
title.value = 'Edit Divisi'
isReadonly.value = false
break
@@ -146,7 +143,7 @@ watch(
if (result.success) {
const currentData = result.body.data || []
const normalizedData = currentData.filter((division: Division) => !division.parent_id)
- divisionsTrees.value = getValueTreeItems(normalizedData)
+ divisionsTrees.value = getValueTreeItems(normalizedData, true)
}
},
)
@@ -190,9 +187,9 @@ onMounted(async () => {
:is-readonly="isReadonly"
@submit="
(values: DivisionFormData | Record, resetForm: () => void) => {
- console.log(values)
- if (recId > 0) {
- handleActionEdit(recId, values, getDivisionList, resetForm, toast)
+ // console.log(values)
+ if (recItem?.code.length > 0) {
+ handleActionEdit(recItem.code, values, getDivisionList, resetForm, toast)
return
}
handleActionSave(values, getDivisionList, resetForm, toast)
@@ -207,7 +204,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getDivisionList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getDivisionList, toast)"
@cancel=""
>
diff --git a/app/components/content/document-upload/add.vue b/app/components/content/document-upload/add.vue
new file mode 100644
index 00000000..9d099189
--- /dev/null
+++ b/app/components/content/document-upload/add.vue
@@ -0,0 +1,128 @@
+
+
+
+
+ Upload Dokumen
+
+
+
+
+
+
+
+
+
diff --git a/app/components/content/document-upload/edit.vue b/app/components/content/document-upload/edit.vue
new file mode 100644
index 00000000..c4033fb2
--- /dev/null
+++ b/app/components/content/document-upload/edit.vue
@@ -0,0 +1,134 @@
+
+
+
+
+ Upload Dokumen
+
+
+
+
+
+
+
+
+
diff --git a/app/components/content/document-upload/list.vue b/app/components/content/document-upload/list.vue
new file mode 100644
index 00000000..7f7631d9
--- /dev/null
+++ b/app/components/content/document-upload/list.vue
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+ ID:
+ {{ record?.id }}
+
+
+ Nama:
+ {{ record?.name }}
+
+
+
+
+
+
+
diff --git a/app/components/content/encounter/detail.vue b/app/components/content/encounter/detail.vue
new file mode 100644
index 00000000..5f2255c5
--- /dev/null
+++ b/app/components/content/encounter/detail.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/app/components/content/encounter/entry.vue b/app/components/content/encounter/entry.vue
index 78297754..228d165e 100644
--- a/app/components/content/encounter/entry.vue
+++ b/app/components/content/encounter/entry.vue
@@ -1,42 +1,14 @@
-
-
-
-
diff --git a/app/components/content/encounter/list.vue b/app/components/content/encounter/list.vue
index 655a553d..31c11326 100644
--- a/app/components/content/encounter/list.vue
+++ b/app/components/content/encounter/list.vue
@@ -1,30 +1,40 @@
-
-
+
+ isFilterFormDialogOpen = true"
+ @onExportPdf="() => {}"
+ @onExportExcel="() => {}"
+ @nExportCsv="() => {}"
+ />
+
-
-
-
+
+
-
+
+
+
+
+ Nama:
+ {{ record.patient.person.name }}
+
+
+ No RM:
+ {{ record.medical_record_number }}
+
+
+
+
+
+
+
@@ -258,4 +309,11 @@ onMounted(() => {
+
diff --git a/app/components/content/encounter/process-bu.vue b/app/components/content/encounter/process-bu.vue
new file mode 100644
index 00000000..02fc7495
--- /dev/null
+++ b/app/components/content/encounter/process-bu.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
diff --git a/app/components/content/encounter/process.vue b/app/components/content/encounter/process.vue
index 5caa55fb..b9e8355f 100644
--- a/app/components/content/encounter/process.vue
+++ b/app/components/content/encounter/process.vue
@@ -1,95 +1,142 @@
-
diff --git a/app/components/content/equipment/list.vue b/app/components/content/equipment/list.vue
index 19e5d913..0db6034c 100644
--- a/app/components/content/equipment/list.vue
+++ b/app/components/content/equipment/list.vue
@@ -110,6 +110,7 @@ watch([recId, recAction], () => {
getCurrentMaterialDetail(recId.value)
title.value = 'Edit Perlengkapan'
isReadonly.value = false
+ isFormEntryDialogOpen.value = true
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
@@ -158,7 +159,7 @@ onMounted(async () => {
@submit="
(values: MaterialFormData, resetForm: any) => {
if (recId > 0) {
- handleActionEdit(recId, values, getEquipmentList, resetForm, toast)
+ handleActionEdit(recItem.code, values, getEquipmentList, resetForm, toast)
return
}
handleActionSave(values, getEquipmentList, resetForm, toast)
@@ -173,7 +174,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getEquipmentList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getEquipmentList, toast)"
@cancel=""
>
diff --git a/app/components/content/general-consent/entry.vue b/app/components/content/general-consent/entry.vue
new file mode 100644
index 00000000..5769e967
--- /dev/null
+++ b/app/components/content/general-consent/entry.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
diff --git a/app/components/content/general-consent/form.vue b/app/components/content/general-consent/form.vue
new file mode 100644
index 00000000..738adca1
--- /dev/null
+++ b/app/components/content/general-consent/form.vue
@@ -0,0 +1,185 @@
+
+
+
+
+
+
diff --git a/app/components/content/general-consent/list.vue b/app/components/content/general-consent/list.vue
new file mode 100644
index 00000000..4cf269ee
--- /dev/null
+++ b/app/components/content/general-consent/list.vue
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+ handleActionRemove(recId, getMyList, toast)"
+ @cancel=""
+ >
+
+
+
+ ID:
+ {{ record?.id }}
+
+
+ Nama:
+ {{ record.name }}
+
+
+ Kode:
+ {{ record.code }}
+
+
+
+
+
diff --git a/app/components/content/installation-position/list.vue b/app/components/content/installation-position/list.vue
index ea60ee04..734406e8 100644
--- a/app/components/content/installation-position/list.vue
+++ b/app/components/content/installation-position/list.vue
@@ -108,7 +108,7 @@ watch([recId, recAction], () => {
isReadonly.value = true
break
case ActionEvents.showEdit:
- getCurrentInstallationDetail(recId.value)
+ getCurrentInstallationDetail(recItem.value.code)
title.value = 'Edit Instalasi - Posisi'
isReadonly.value = false
break
@@ -120,7 +120,7 @@ watch([recId, recAction], () => {
onMounted(async () => {
try {
- installations.value = await getInstallationLabelList({ sort: 'createdAt:asc', 'page-size': 100 })
+ installations.value = await getInstallationLabelList({ sort: 'createdAt:asc', 'page-size': 100 }, true)
employees.value = await getEmployeeLabelList({ sort: 'createdAt:asc', 'page-size': 100, includes: 'person' })
} catch (err) {
console.log(err)
@@ -169,8 +169,8 @@ onMounted(async () => {
:is-readonly="isReadonly"
@submit="
(values: InstallationPositionFormData | Record, resetForm: () => void) => {
- if (recId > 0) {
- handleActionEdit(recId, values, getInstallationPositionList, resetForm, toast)
+ if (recItem?.code.length > 0) {
+ handleActionEdit(recItem.code, values, getInstallationPositionList, resetForm, toast)
return
}
handleActionSave(values, getInstallationPositionList, resetForm, toast)
@@ -185,7 +185,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getInstallationPositionList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getInstallationPositionList, toast)"
@cancel=""
>
diff --git a/app/components/content/installation/detail.vue b/app/components/content/installation/detail.vue
index 0f12ad8c..bd0d2988 100644
--- a/app/components/content/installation/detail.vue
+++ b/app/components/content/installation/detail.vue
@@ -47,7 +47,7 @@ const title = ref('')
// #region Props & Emits
const props = defineProps<{
- installationId: number
+ installationId: string
}>()
const installation = ref({} as Installation)
// #endregion
@@ -153,7 +153,7 @@ watch([recId, recAction], () => {
console.log(recId, recAction)
switch (recAction.value) {
case ActionEvents.showEdit:
- getDetailInstallationPosition(recId.value)
+ getDetailInstallationPosition(recItem.value.code)
title.value = 'Edit Jabatan'
isReadonly.value = false
isFormEntryDialogOpen.value = true
@@ -202,8 +202,8 @@ watch([recId, recAction], () => {
@submit="
(values: InstallationPositionFormData | Record, resetForm: () => void) => {
console.log(values)
- if (recId > 0) {
- handleActionEdit(recId, values, getInstallationPositionList, onResetState, toast)
+ if (recItem?.code.length > 0) {
+ handleActionEdit(recItem.code, values, getInstallationPositionList, onResetState, toast)
return
}
handleActionSave(values, getInstallationPositionList, onResetState, toast)
@@ -216,7 +216,7 @@ watch([recId, recAction], () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getInstallationPositionList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getInstallationPositionList, toast)"
@cancel=""
>
diff --git a/app/components/content/installation/list.vue b/app/components/content/installation/list.vue
index 553d7ebb..6f12c90b 100644
--- a/app/components/content/installation/list.vue
+++ b/app/components/content/installation/list.vue
@@ -102,26 +102,23 @@ const getCurrentInstallationDetail = async (id: number | string) => {
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
- if (Number(recId.value) > 0) {
- const id = Number(recId.value)
+ if (recItem.value.code.length > 0) {
+ navigateTo({
+ name: 'org-src-installation-id',
+ params: {
+ id: recItem.value.code,
+ },
+ })
recAction.value = ''
recItem.value = null
recId.value = 0
isFormEntryDialogOpen.value = false
isReadonly.value = false
-
- navigateTo({
- name: 'org-src-installation-id',
- params: {
- id,
- },
- })
}
-
break
case ActionEvents.showEdit:
- getCurrentInstallationDetail(recId.value)
+ getCurrentInstallationDetail(recItem.value.code)
title.value = 'Edit Instalasi'
isReadonly.value = false
break
@@ -171,8 +168,8 @@ onMounted(async () => {
:is-readonly="isReadonly"
@submit="
(values: InstallationFormData | Record, resetForm: () => void) => {
- if (recId > 0) {
- handleActionEdit(recId, values, getInstallationList, resetForm, toast)
+ if (recItem?.code.length > 0) {
+ handleActionEdit(recItem.code, values, getInstallationList, resetForm, toast)
return
}
handleActionSave(values, getInstallationList, resetForm, toast)
@@ -187,7 +184,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getInstallationList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getInstallationList, toast)"
@cancel=""
>
diff --git a/app/components/content/medicine-form/list.vue b/app/components/content/medicine-form/list.vue
new file mode 100644
index 00000000..1ca9eefe
--- /dev/null
+++ b/app/components/content/medicine-form/list.vue
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
+
+
+
+ handleActionRemove(recItem.code, getMedicineFormList, toast)"
+ @cancel=""
+ >
+
+
+
+ ID:
+ {{ record?.id }}
+
+
+ Nama:
+ {{ record.name }}
+
+
+ Kode:
+ {{ record.code }}
+
+
+
+
+
diff --git a/app/components/content/medicine-group/list.vue b/app/components/content/medicine-group/list.vue
index 80faf1ab..6c695d72 100644
--- a/app/components/content/medicine-group/list.vue
+++ b/app/components/content/medicine-group/list.vue
@@ -108,6 +108,7 @@ watch([recId, recAction], () => {
getCurrentMedicineGroupDetail(recId.value)
title.value = 'Edit Kelompok Obat'
isReadonly.value = false
+ isFormEntryDialogOpen.value = true
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
@@ -154,7 +155,7 @@ onMounted(async () => {
@submit="
(values: BaseFormData | Record, resetForm: () => void) => {
if (recId > 0) {
- handleActionEdit(recId, values, getMedicineGroupList, resetForm, toast)
+ handleActionEdit(recItem.code, values, getMedicineGroupList, resetForm, toast)
return
}
handleActionSave(values, getMedicineGroupList, resetForm, toast)
@@ -169,7 +170,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getMedicineGroupList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getMedicineGroupList, toast)"
@cancel=""
>
diff --git a/app/components/content/medicine-method/list.vue b/app/components/content/medicine-method/list.vue
index 9e0e5d01..2c8fb8c6 100644
--- a/app/components/content/medicine-method/list.vue
+++ b/app/components/content/medicine-method/list.vue
@@ -108,6 +108,7 @@ watch([recId, recAction], () => {
getCurrentMedicineMethodDetail(recId.value)
title.value = 'Edit Metode Obat'
isReadonly.value = false
+ isFormEntryDialogOpen.value = true
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
@@ -154,7 +155,7 @@ onMounted(async () => {
@submit="
(values: BaseFormData | Record, resetForm: () => void) => {
if (recId > 0) {
- handleActionEdit(recId, values, getMedicineMethodList, resetForm, toast)
+ handleActionEdit(recItem.code, values, getMedicineMethodList, resetForm, toast)
return
}
handleActionSave(values, getMedicineMethodList, resetForm, toast)
@@ -169,7 +170,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getMedicineMethodList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getMedicineMethodList, toast)"
@cancel=""
>
diff --git a/app/components/content/medicine/list.vue b/app/components/content/medicine/list.vue
index 27470776..bfbf8750 100644
--- a/app/components/content/medicine/list.vue
+++ b/app/components/content/medicine/list.vue
@@ -37,10 +37,12 @@ import {
import { getList, getDetail } from '~/services/medicine.service'
import { getValueLabelList as getMedicineGroupList } from '~/services/medicine-group.service'
import { getValueLabelList as getMedicineMethodList } from '~/services/medicine-method.service'
+import { getValueLabelList as getMedicineFormList } from '~/services/medicine-form.service'
import { getValueLabelList as getUomList } from '~/services/uom.service'
const medicineGroups = ref<{ value: string; label: string }[]>([])
const medicineMethods = ref<{ value: string; label: string }[]>([])
+const medicineForms = ref<{ value: string; label: string }[]>([])
const uoms = ref<{ value: string; label: string }[]>([])
const title = ref('')
@@ -59,7 +61,7 @@ const {
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
- includes: 'medicineGroup,medicineMethod,uom',
+ includes: 'medicineGroup,medicineMethod,medicineForm,uom',
})
return { success: result.success || false, body: result.body || {} }
},
@@ -116,6 +118,7 @@ watch([recId, recAction], () => {
case ActionEvents.showEdit:
getCurrentMedicineDetail(recId.value)
title.value = 'Edit Obat'
+ isFormEntryDialogOpen.value = true
isReadonly.value = false
break
case ActionEvents.showConfirmDelete:
@@ -127,6 +130,7 @@ watch([recId, recAction], () => {
onMounted(async () => {
medicineGroups.value = await getMedicineGroupList({ sort: 'createdAt:asc', 'page-size': 100 })
medicineMethods.value = await getMedicineMethodList({ sort: 'createdAt:asc', 'page-size': 100 })
+ medicineForms.value = await getMedicineFormList({ sort: 'createdAt:asc', 'page-size': 100 })
uoms.value = await getUomList({ sort: 'createdAt:asc', 'page-size': 100 })
await getMedicineList()
})
@@ -163,13 +167,14 @@ onMounted(async () => {
:values="recItem"
:medicineGroups="medicineGroups"
:medicineMethods="medicineMethods"
+ :medicineForms="medicineForms"
:uoms="uoms"
:is-loading="isProcessing"
:is-readonly="isReadonly"
@submit="
(values: MedicineFormData | Record, resetForm: () => void) => {
if (recId > 0) {
- handleActionEdit(recId, values, getMedicineList, resetForm, toast)
+ handleActionEdit(recItem.code, values, getMedicineList, resetForm, toast)
return
}
handleActionSave(values, getMedicineList, resetForm, toast)
@@ -184,7 +189,7 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
- @confirm="() => handleActionRemove(recId, getMedicineList, toast)"
+ @confirm="() => handleActionRemove(recItem.code, getMedicineList, toast)"
@cancel=""
>
diff --git a/app/components/content/prescription/entry.vue b/app/components/content/prescription/entry.vue
index 4b3fa663..46768432 100644
--- a/app/components/content/prescription/entry.vue
+++ b/app/components/content/prescription/entry.vue
@@ -1,86 +1,215 @@
@@ -91,42 +220,147 @@ function saveNonMix(data: PrescriptionItem) {
class="mb-4 xl:mb-5"
/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ handleActionRemove(recId, getPrescriptionItems, toast)"
+ @cancel=""
+ >
+
+
+
+ ID:
+ {{ record?.id }}
+
+
+ Nama:
+ {{ record.name }}
+
+
+ Kode:
+ {{ record.code }}
+
+
+
+
diff --git a/app/components/content/prescription/list.vue b/app/components/content/prescription/list.vue
index 844cb04f..afd012f6 100644
--- a/app/components/content/prescription/list.vue
+++ b/app/components/content/prescription/list.vue
@@ -1,8 +1,10 @@
-
+
+
+
+
+
+
handleActionRemove(recId, getMyList, toast)"
@cancel=""
>
+
+ Tanggal
+ :
+ {{ recItem.createdAt.substring(0, 10) }}
+
+
+ DPJP
+ :
+ {{ recItem.doctor?.employee?.person?.name }}
+
+
+
+ handleActionSubmit(recId, getMyList, toast)"
+ @cancel=""
+ >
+
+ Tanggal
+ :
+ {{ recItem.createdAt.substring(0, 10) }}
+
+
+ DPJP
+ :
+ {{ recItem.doctor?.employee?.person?.name }}
+
diff --git a/app/components/content/resume/add.vue b/app/components/content/resume/add.vue
new file mode 100644
index 00000000..6fda4bf8
--- /dev/null
+++ b/app/components/content/resume/add.vue
@@ -0,0 +1,200 @@
+
+
+
+ Tambah Resume
+
+
+
+
+
+
+
+
+ aaaaaaaaaaaaaaa
+
+
+
+
+
+
+
+
diff --git a/app/components/content/resume/list.vue b/app/components/content/resume/list.vue
new file mode 100644
index 00000000..4b5b5001
--- /dev/null
+++ b/app/components/content/resume/list.vue
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/components/content/sep/entry.vue b/app/components/content/sep/entry.vue
index 3a916c4d..6e91f464 100644
--- a/app/components/content/sep/entry.vue
+++ b/app/components/content/sep/entry.vue
@@ -1,483 +1,59 @@
@@ -169,100 +85,77 @@ provide('table_data_loader', isLoading)
\ No newline at end of file
diff --git a/app/components/pub/my-ui/nav-header/prep.vue b/app/components/pub/my-ui/nav-header/prep.vue
index 7aa5e4f2..18eb2624 100644
--- a/app/components/pub/my-ui/nav-header/prep.vue
+++ b/app/components/pub/my-ui/nav-header/prep.vue
@@ -37,6 +37,10 @@ function btnClick() {
|
+
+
+
+
-
+
{{ prep.addNav.label }}
diff --git a/app/composables/useQueryCRUD.ts b/app/composables/useQueryCRUD.ts
index a48e9a2b..f81649bd 100644
--- a/app/composables/useQueryCRUD.ts
+++ b/app/composables/useQueryCRUD.ts
@@ -19,7 +19,17 @@ export function useQueryCRUDMode(key: string = 'mode') {
})
const goToEntry = () => (mode.value = 'entry')
- const backToList = () =>(mode.value = 'list')
+ const backToList = () => {
+ router.push({
+ path: route.path,
+ query: {
+ ...route.query,
+ mode: 'list',
+ // HAPUS record-id
+ 'record-id': undefined,
+ },
+ })
+ }
return { mode, goToEntry, backToList }
}
diff --git a/app/composables/useRBAC.ts b/app/composables/useRBAC.ts
index ced57e3e..60cfd9d1 100644
--- a/app/composables/useRBAC.ts
+++ b/app/composables/useRBAC.ts
@@ -1,4 +1,12 @@
-import type { Permission, RoleAccess } from '~/models/role'
+import type { Permission, RoleAccesses } from '~/models/role'
+import { systemCode } from '~/const/common/role'
+
+export interface PageOperationPermission {
+ canRead: boolean
+ canCreate: boolean
+ canUpdate: boolean
+ canDelete: boolean
+}
/**
* Check if user has access to a page
@@ -7,19 +15,27 @@ export function useRBAC() {
// NOTE: this roles was dummy for testing only, it should taken from the user store
const authStore = useUserStore()
- const checkRole = (roleAccess: RoleAccess, _userRoles?: string[]): boolean => {
- const roles = authStore.userRole
- return roles.some((role: string) => (role in roleAccess) || role === 'system') // system by-passes this check
+ const checkRole = (roleAccesses: RoleAccesses, _userRoles?: string[]): boolean => {
+ const activeRole = authStore.getActiveRole() || ''
+ if (activeRole === systemCode) {
+ return true
+ }
+ return (activeRole in roleAccesses);
}
- const checkPermission = (roleAccess: RoleAccess, permission: Permission, _userRoles?: string[]): boolean => {
- const roles = authStore.userRole
- return roles.some((role: string) => roleAccess[role]?.includes(permission) || role === 'system') // system by-passes this check
+ const checkPermission = (roleAccesses: RoleAccesses, permission: Permission, _userRoles?: string[]): boolean => {
+ const activeRole = authStore.getActiveRole() || ''
+ if (activeRole === systemCode) {
+ return true
+ }
+ if (activeRole in roleAccesses && roleAccesses[activeRole]) {
+ return roleAccesses[activeRole].includes(permission)
+ }
+ return false
}
- const getUserPermissions = (roleAccess: RoleAccess, _userRoles?: string[]): Permission[] => {
- const roles = authStore.userRole
- // const roles = ['admisi']
+ const getUserPermissions = (roleAccess: RoleAccesses, _userRoles?: string[]): Permission[] => {
+ const roles = authStore.userRoles
const permissions = new Set()
roles.forEach((role: string) => {
@@ -31,10 +47,17 @@ export function useRBAC() {
return Array.from(permissions)
}
- const hasCreateAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'C')
- const hasReadAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'R')
- const hasUpdateAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'U')
- const hasDeleteAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'D')
+ const hasCreateAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'C')
+ const hasReadAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'R')
+ const hasUpdateAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'U')
+ const hasDeleteAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'D')
+
+ const getPagePermissions = (roleAccess: RoleAccesses): PageOperationPermission => ({
+ canRead : hasReadAccess(roleAccess),
+ canCreate: hasCreateAccess(roleAccess),
+ canUpdate: hasUpdateAccess(roleAccess),
+ canDelete: hasDeleteAccess(roleAccess),
+ })
return {
checkRole,
@@ -44,5 +67,7 @@ export function useRBAC() {
hasReadAccess,
hasUpdateAccess,
hasDeleteAccess,
+ getPagePermissions,
+
}
}
diff --git a/app/const/common/role.ts b/app/const/common/role.ts
new file mode 100644
index 00000000..a0d4565a
--- /dev/null
+++ b/app/const/common/role.ts
@@ -0,0 +1,36 @@
+export const systemCode = 'system'
+
+export const rehabInstCode = 'rehab'
+export const rehabUnitCode = 'rehab'
+
+export const headPosCode = 'head' // head position
+export const respPosCode = 'resp' // responsible position, verificator
+
+export type UnitLevel =
+ 'inst' | // installation
+ 'unit' | // unit / poly
+ 'spec' | // specialist
+ 'subspec' // subspecialist
+
+export const medicalRoles = [
+ 'emp|doc', // doctor
+ 'emp|nur', // nurse
+ 'emp|miw', // midwife
+ 'emp|thr', // therapist
+ 'emp|nut', // nutritionist
+ 'emp|pha', // pharmacy
+ 'emp|lab' // laborant
+]
+
+export const serviceRoles = [
+ 'emp|reg',
+ ...medicalRoles,
+]
+
+export function genSpecHeadCode(unit_level: UnitLevel, unit_code: string): string {
+ return `${unit_level}|${unit_code}|${headPosCode}`
+}
+
+export function genUnitRespCode(unit_level: UnitLevel, unit_code: string): string {
+ return `${unit_level}|${unit_code}|${respPosCode}`
+}
diff --git a/app/const/key-val/clinical.ts b/app/const/key-val/clinical.ts
new file mode 100644
index 00000000..d6a37b43
--- /dev/null
+++ b/app/const/key-val/clinical.ts
@@ -0,0 +1,226 @@
+export type SubjectCode = 'detail' | 'pri-complain' | 'sec-complain' | 'cur-disea-hist' | 'pas-disea-hist' | 'fam-disea-hist' | 'alg-hist' | 'alg-react' | 'med-hist' | 'blood-type'
+export type ObjectCode = 'detail' | 'consc-level' | 'consc-level-det' | 'syst-bp' | 'diast-bp' | 'pulse' | 'resp-rate' | 'hear-rt' | 'neuro-cranialis' | 'sensoris' | 'reflect-fisio' | 'reflect-pato' | 'autonom-neuron' | 'neck-rom' | 'body-rom' | 'aga-rom' | 'agb-rom' | 'neck-mmt' | 'body-mmt' | 'aga-mmt' | 'agb-mmt' | 'localis' | 'medical-trouble' | 'rehab-medic-trouble' | 'temp' | 'spo2' | 'weight' | 'height' | 'head-to-toe'
+export type AssessmentCode = 'detail' | 'early-med-diag' | 'late-med-diag' | 'sec-med-diag' | 'early-func-diag' | 'late-func-diag' | 'sec-func-diag' | 'patient-dev'
+export type PlanCode = 'detail' | 'plan'
+export type InstructionCode = 'detail' | 'medical-act' | 'supporting-exam' | 'therapy' | 'medication' | 'material' | 'rehab-program' | 'physic-modal' | 'exercise' | 'ortes-protesa' | 'education' | 'other'
+export type HeadToToeCode = 'head' | 'eye' | 'ear' | 'nose' | 'mouth-throat' | 'head-others' | 'thorax' | 'heart' | 'lung' | 'abdomen' | 'liver' | 'back' | 'ekstremitas' | 'gender' | 'rectum' | 'neuron' | 'body-others'
+export type McuUrgencyLevelCode = 'cito' | 'cito-igd' | 'ponek' | 'blood-gas' | 'priority-form' | 'routine'
+export type McuScopeCode = 'cp-lab' | 'mic-lab' | 'ap-lab' | 'rad'
+export type SoapiTypeCode = 'early-medic' | 'early-rehab' | 'function' | 'progress' | 'dev-record' | 'kfr-adm' | 'kfr-series'
+export type MedicalActionTypeCode = 'chemo' | 'hemo' | 'thalasemia' | 'echocardio' | 'spirometry'
+export type VehicleTypeCode = 'ambulance' | 'transport' | 'hearse'
+export type GeneralEduCode = 'right-obg' | 'general-consent' | 'service' | 'alt-care-src' | 'home-plan' | 'home-care' | 'orientation' | 'fall-risk-prevention' | 'alt-care' | 'act-delay' | 'others'
+export type SpecialEduCode = 'disease-diag-dev' | 'safe-med-usage' | 'side-effect' | 'diet' | 'pain-mgmt' | 'medical-eq-usage' | 'rehab-technique' | 'prevention-act'
+export type EduAssessmentCode = 'learn-ability' | 'learn-will' | 'obstacle' | 'learn-method' | 'lang' | 'lang-obstacle' | 'belief'
+export type AbilityCode = 'able' | 'not-able'
+export type WillCode = 'ready' | 'interested' | 'not-interested'
+export type MedObstacleCode = 'hearing' | 'sight' | 'physical' | 'emotional' | 'cognitif'
+export type LearnMethodCode = 'demo' | 'discuss-leaflet'
+export type LangClassCode = 'ind' | 'region' | 'foreign'
+export type TranslatorSrcCode = 'team' | 'family'
+
+export const subjectCodes: Record = {
+ detail: 'Detail',
+ 'pri-complain': 'Keluhan Utama',
+ 'sec-complain': 'Secondary Complaint',
+ 'cur-disea-hist': 'Current Disease History',
+ 'pas-disea-hist': 'Past Disease History',
+ 'fam-disea-hist': 'Family Disease History',
+ 'alg-hist': 'Allergic Hist',
+ 'alg-react': 'Allergic Reaction',
+ 'med-hist': 'Medication Hist',
+ 'blood-type': 'Golongan Darah',
+}
+
+export const objectCodes: Record = {
+ detail: 'Detail',
+ 'consc-level': 'Tingkat Kesadaran',
+ 'consc-level-det': 'Detail Tingkat Kesadaran',
+ 'syst-bp': 'Tekanan Darah Systolic',
+ 'diast-bp': 'Tekanan Darah Diastolic',
+ 'pulse': 'Nadi',
+ 'resp-rate': 'Pernafasan',
+ 'hear-rt': 'Detak Jantung',
+ 'neuro-cranialis': 'Neurologist Cranialist',
+ 'sensoris': 'Sensoris',
+ 'reflect-fisio': 'Refleks Fisiologi',
+ 'reflect-pato': 'Refleks Patologi',
+ 'autonom-neuron': 'Saraf Otonom',
+ 'neck-rom': 'ROM - Leher',
+ 'body-rom': 'ROM - Batang Tubuh',
+ 'aga-rom': 'ROM - AGA',
+ 'agb-rom': 'ROM - AGB',
+ 'neck-mmt': 'MMT - Leher',
+ 'body-mmt': 'MMT - Batang Tubuh',
+ 'aga-mmt': 'MMT - AGA',
+ 'agb-mmt': 'MMT - AGB',
+ 'localis': 'Status Lokalis',
+ 'medical-trouble': 'Masalah Medis',
+ 'rehab-medic-trouble': 'Masalah Rehab Medik',
+ 'temp': 'Suhu',
+ 'spo2': 'SpO2',
+ 'weight': 'Berat Badan',
+ 'height': 'Tinggi Badan',
+ 'head-to-toe': 'Kepala Sampai Kaki',
+}
+
+export const assessmentCodes: Record = {
+ detail: 'Detail',
+ 'early-med-diag': 'Diagnosis Medis Awal',
+ 'late-med-diag': 'Diagnosis Medis Akhir',
+ 'sec-med-diag': 'Diagnosis Medis Sekunder',
+ 'early-func-diag': 'Diagnosis Fungsi Awal',
+ 'late-func-diag': 'Diagnosis Fungsi Akhir',
+ 'sec-func-diag': 'Diagnosis Fungsi Sekunder',
+ 'patient-dev': 'Catatan Perkembangan Patient',
+}
+
+export const planCodes: Record = {
+ detail: 'Detail',
+ 'plan': 'Rencana',
+}
+
+export const instructionCodes: Record = {
+ detail: 'Detail Instruksi',
+ 'medical-act': 'Tindakan medis',
+ 'supporting-exam': 'Pemeriksaan Penunjang',
+ 'therapy': 'Terapi',
+ 'medication': 'Obat',
+ 'material': 'BMHP',
+ 'rehab-program': 'Program Rehab',
+ 'physic-modal': 'Modalitas Fisik',
+ 'exercise': 'Latihan',
+ 'ortes-protesa': 'Ortesa Protesa',
+ 'education': 'Edukasi',
+ 'other': 'Lain-lain',
+}
+
+export const headToToeCodes: Record = {
+ head: 'Kepala',
+ eye: 'Mata',
+ ear: 'Telinga',
+ nose: 'Hidung',
+ 'mouth-throat': 'Mulut - Tenggorokan',
+ 'head-others': 'Kepala - Lainnya',
+ thorax: 'Toraks',
+ heart: 'Jantung',
+ lung: 'Paru',
+ abdomen: 'Perut',
+ liver: 'Hati',
+ back: 'Punggung',
+ ekstremitas: 'Ekstermitas',
+ gender: 'Kelamin',
+ rectum: 'Dubur',
+ neuron: 'Syaraf',
+ 'body-others': 'Badan - Lainnya',
+}
+
+export const mcuUrgencyLevelCodes: Record = {
+ cito: 'CITO',
+ 'cito-igd': 'CITO IGD',
+ ponek: 'PONEK',
+ 'blood-gas': 'Analisa Gas Darah',
+ 'priority-form': 'Form Prioritas',
+ routine: 'Pemeriksaan Rutin',
+}
+
+export const mcuScopeCodes: Record = {
+ 'cp-lab': 'Laboratorium',
+ 'mic-lab': 'Microbacterial Laboratorium',
+ 'ap-lab': 'Patology Anatomy Laboratorium',
+ rad: 'Radiology',
+}
+
+export const soapiTypeCodes: Record = {
+ 'early-medic': 'Kajian Awal Medis',
+ 'early-rehab': 'Kajian Awal Rehab Medik',
+ 'function': 'Assessmen Fungsi',
+ 'progress': 'CPPT',
+ 'dev-record': 'Catatan Perkembangan',
+ 'kfr-adm': 'Soapi Untuk KFR',
+ 'kfr-series': 'Soapi Untuk KFR',
+}
+
+export const medicalActionTypeCodes: Record = {
+ chemo: 'Kemoterapi',
+ hemo: 'Hemofilia',
+ thalasemia: 'Talasemi',
+ echocardio: 'Echokardio',
+ spirometry: 'Spirometri',
+}
+
+export const vehicleTypeCodes: Record = {
+ ambulance: 'Ambulans',
+ transport: 'Transport',
+ hearse: 'Jenazah',
+}
+
+export const generalEduCodes: Record = {
+ 'right-obg': 'Hak dan kewajiban pasien dan keluarga',
+ 'general-consent': 'General Consent',
+ 'service': 'Pelayanan yang disediakan (jam pelayanan, akses pelayanan dan proses pelayanan)',
+ 'alt-care-src': 'Sumber alternatif asuhan di tempat lain/faskes lain',
+ 'home-plan': 'Rencana tindakan di rumah',
+ 'home-care': 'Kebutuhan perawatan di rumah',
+ 'orientation': 'Orientasi ruangan',
+ 'fall-risk-prevention': 'Pencegahan risiko jatuh',
+ 'alt-care': 'Alternatif pelayanan',
+ 'act-delay': 'Penundaan Tindakan',
+ 'others': 'Lain - lain',
+}
+
+export const specialEduCodes: Record = {
+ 'disease-diag-dev': 'Diagnosa penyakit dan perkembangannya',
+ 'safe-med-usage': 'Penggunaan obat yang aman,',
+ 'side-effect': 'efek samping dan reaksi obat',
+ 'diet': 'Diet/Nutrisi',
+ 'pain-mgmt': 'Managemen nyeri',
+ 'medical-eq-usage': 'Penggunaan Peralatan Medis',
+ 'rehab-technique': 'Tehnik Rehabilitasi',
+ 'prevention-act': 'Tindakan pencegahan (cuci tangan, pemasangan gelang)',
+}
+
+export const eduAssessmentCodes: Record = {
+ 'learn-ability': 'Kemampuan Belajar',
+ 'learn-will': 'Kemauan Belajar',
+ 'obstacle': 'Hambatan',
+ 'learn-method': 'Metode Pembelajaran',
+ 'lang': 'Bahasa',
+ 'lang-obstacle': 'Hambatan Bahasa',
+ 'belief': 'Keyakinan',
+}
+
+export const abilityCodes: Record = {
+ able: 'Mampu',
+ 'not-able': 'Tidak Mampu',
+}
+
+export const willCodes: Record = {
+ ready: 'Siap',
+ interested: 'Tertarik',
+ 'not-interested': 'Tidak Tertarik',
+}
+
+export const medObstacleCodes: Record = {
+ hearing: 'Pendengaran',
+ sight: 'Penglihatan',
+ physical: 'Fisik',
+ emotional: 'Emosional',
+ cognitif: 'Kognitif',
+}
+
+export const learnMethodCodes: Record = {
+ demo: 'Demonstrasi',
+ 'discuss-leaflet': 'Diskusi Leaflet',
+}
+
+export const langClassCodes: Record = {
+ ind: 'Indonesia',
+ region: 'Daerah',
+ foreign: 'Asing',
+}
+
+export const translatorSrcCodes: Record = {
+ team: 'Tim Penerjemah',
+ family: 'Keluarga',
+}
diff --git a/app/const/key-val/common.ts b/app/const/key-val/common.ts
new file mode 100644
index 00000000..81e0f1ae
--- /dev/null
+++ b/app/const/key-val/common.ts
@@ -0,0 +1,84 @@
+export type ActiveStatusCode = 'active' | 'inactive'
+export type DataStatusCode = 'new' | 'review' | 'process' | 'done' | 'canceled' | 'rejected' | 'skipped'
+export type DataApprovalCode = 'new' | 'approved' | 'rejected'
+export type DataAvailabilityCode = 'review' | 'available' | 'unavailble'
+export type DataVerifiedCode = 'new' | 'verified' | 'rejected'
+export type UserStatusCode = 'new' | 'active' | 'inactive' | 'blocked' | 'suspended'
+export type ProcessStatusCode = 'success' | 'failed'
+export type TimeUnitCode = 'sec' | 'min' | 'hour' | 'day' | 'week' | 'month' | 'year'
+export type DayCode = '0' | '1' | '2' | '3' | '4' | '5' | '6'
+export type PaymentMethodCode = 'cash' | 'debit' | 'credit' | 'insurance' | 'membership'
+
+export const activeStatusCodes: Record = {
+ active: 'Aktif',
+ inactive: 'Tidak Aktif',
+}
+
+export const dataStatusCodes: Record = {
+ new: 'Baru',
+ review: 'Review',
+ process: 'Proses',
+ done: 'Selesai',
+ canceled: 'Dibatalkan',
+ rejected: 'Ditolak',
+ skipped: 'Dilewati',
+}
+
+export const dataApprovalCodes: Record = {
+ new: 'Baru',
+ approved: 'Diterima',
+ rejected: 'Ditolak',
+}
+
+export const dataAvailabilityCodes: Record = {
+ review: 'Konfirmasi',
+ available: 'Tersedia',
+ unavailble: 'Tidak Tersedia',
+}
+
+export const dataVerifiedCodes: Record = {
+ new: 'Baru',
+ verified: 'Terverifikasi',
+ rejected: 'Ditolak',
+}
+
+export const userStatusCodes: Record = {
+ new: 'Baru',
+ active: 'Aktif',
+ inactive: 'Tidak Aktif',
+ blocked: 'Diblokir',
+ suspended: 'Dibekukan',
+}
+
+export const processStatusCodes: Record = {
+ success: 'Sukses',
+ failed: 'Gagal',
+}
+
+export const timeUnitCodes: Record = {
+ sec: 'Detik',
+ min: 'Menit',
+ hour: 'Jam',
+ day: 'Hari',
+ week: 'Minggu',
+ month: 'Bulan',
+ year: 'Tahun',
+}
+
+export const dayCodes: Record = {
+ '0': 'Minggu',
+ '1': '',
+ '2': '',
+ '3': '',
+ '4': '',
+ '5': '',
+ '6': 'Sabtu',
+}
+
+export const paymentMethodCodes: Record = {
+ cash: 'Cash',
+ debit: 'Debit',
+ credit: 'Kredit',
+ insurance: 'Asuransi',
+ membership: 'Membership',
+}
diff --git a/app/const/key-val/encounter.ts b/app/const/key-val/encounter.ts
new file mode 100644
index 00000000..edd8ea29
--- /dev/null
+++ b/app/const/key-val/encounter.ts
@@ -0,0 +1,141 @@
+export type QueueStatusCode = 'wait' | 'proc' | 'done' | 'cancel' | 'skip'
+export type EncounterClassCode = 'ambulatory' | 'emergency' | 'inpatient'
+export type DischargeMethodCode = 'home' | 'home-request' | 'consul-back' | 'consul-poly' | 'consul-executive' | 'consul-ch-day' | 'emergency' | 'emergency-covid' | 'inpatient' | 'external' | 'death' | 'death-on-arrival' | 'run-away'
+export type PolySwitchCode = 'consul-poly' | 'consul-executive'
+export type RefTypeCode = 'none' | 'gov' | 'private' | 'bpjs'
+export type TransportationCode = 'ambulance' | 'car' | 'motor-cycle' | 'other'
+export type PersonConditionCode = 'res' | 'emg' | 'urg' | 'lurg' | 'nurg' | 'doa'
+export type AmbulatoryClassCode = 'reg' | 'rehab' | 'chemo'
+export type EmergencyClassCode = 'emg' | 'eon'
+export type InpatientClassCode = 'op' | 'icu' | 'hcu' | 'vk'
+export type ChemoClassCode = 'adm' | 'act'
+export type AmbulanceFacilityCode = 'std' | 'icu'
+export type AmbulanceNeedsCode = 'assist' | 'non-assist'
+export type DocTypeCode = 'person-resident-number' | 'person-driving-license' | 'person-passport' | 'person-family-card' | 'encounter-patient' | 'encounter-suport' | 'encounter-other' | 'mcu-item-result' | 'vclaim-sep' | 'vclaim-sipp' | 'general-consent'
+export type AltPaymentMethodCode = 'jkn' | 'jkmm' | 'spm' | 'pks' | 'umum'
+export type SEPRefTypCode = 'internal' | 'external'
+export type VisitModeCode = 'adm' | 'readm' | 'series'
+
+export const queueStatusCodes: Record = {
+ wait: 'Tunggu',
+ proc: 'Proses',
+ done: 'Selesai',
+ cancel: 'Batal',
+ skip: 'Dilewati',
+}
+
+export const encounterClassCodes: Record = {
+ ambulatory: 'Rawat Jalan',
+ emergency: 'Gawat Darurat',
+ inpatient: 'Rawat Inap',
+}
+
+export const dischageMethodCodes: Record = {
+ home: 'Pulang',
+ 'home-request': 'Pulang Atas Permintaan Sendiri',
+ 'consul-back': 'Konsultasi Balik / Lanjutan',
+ 'consul-poly': 'Konsultasi Poliklinik Lain',
+ 'consul-executive': 'Konsultasi Antar Dokter Eksekutif',
+ 'consul-ch-day': 'Konsultasi Hari Lain',
+ emergency: 'Rujuk IGD',
+ 'emergency-covid': 'Rujuk IGD Covid',
+ inpatient: 'Rujuk Rawat Inap',
+ external: 'Rujuk Faskes Lain',
+ death: 'Meninggal',
+ 'death-on-arrival': 'Meninggal Saat Tiba',
+ 'run-away': 'Kabur / Lari',
+}
+
+export const polySwitchCode: Record = {
+ 'consul-poly': 'Konsultasi Poliklinik Lain',
+ 'consul-executive': 'Konsultasi Antar Dokter Eksekutif',
+}
+
+export const refTypeCode: Record = {
+ none: 'Tidak Ada',
+ gov: 'Pemerintah',
+ private: 'Swasta',
+ bpjs: 'BPJS',
+}
+
+export const transportationCode: Record = {
+ ambulance: 'Ambulans',
+ car: 'Mobil',
+ 'motor-cycle': 'Motor',
+ other: 'Lainnya',
+}
+
+export const personConditionCode: Record = {
+ res: 'Resutiasi',
+ emg: 'Darurat',
+ urg: 'Mendesak',
+ lurg: 'Kurang Mendesak',
+ nurg: 'Mendesak',
+ doa: 'Meninggal Saat Tiba',
+}
+
+export const ambulatoryClassCode: Record = {
+ reg: 'Reguler',
+ rehab: 'Rehab Medik',
+ chemo: 'Kemoterapi',
+}
+
+export const emergencyClassCode: Record = {
+ emg: 'Darurat',
+ eon: 'Ponek',
+}
+
+export const inpatientClassCode: Record = {
+ op: 'Rawat Inap',
+ icu: 'ICU',
+ hcu: 'HCU',
+ vk: 'Kamar Bersalin',
+}
+
+export const chemoClassCode: Record = {
+ adm: 'Administrasi',
+ act: 'Tindakan',
+}
+
+export const ambulanceFacilityCode: Record = {
+ std: 'Standar',
+ icu: 'ICU',
+}
+
+export const ambulanceNeedsCode: Record = {
+ assist: 'Dengan Pendampingan',
+ 'non-assist': 'Tanpa Pendampingan',
+}
+
+export const docTypeCode: Record = {
+ 'person-resident-number': '',
+ 'person-driving-license': '',
+ 'person-passport': '',
+ 'person-family-card': '',
+ 'encounter-patient': '',
+ 'encounter-suport': '',
+ 'encounter-other': '',
+ 'mcu-item-result': '',
+ 'vclaim-sep': '',
+ 'vclaim-sipp': '',
+ 'general-consent': '',
+}
+
+export const altPaymentMethodCode: Record = {
+ jkn: 'JKN',
+ jkmm: 'JKMM',
+ spm: 'SPM',
+ pks: 'PKS',
+ umum: 'Umum',
+}
+
+export const sepRefTypCode: Record = {
+ internal: 'Rujukan Internal',
+ external: 'Faskes Lain',
+}
+
+export const visitModeCode: Record = {
+ adm: 'Administrasi',
+ readm: 'Administrasi',
+ series: 'Tindakan',
+}
diff --git a/app/const/key-val/org.ts b/app/const/key-val/org.ts
new file mode 100644
index 00000000..49f16e68
--- /dev/null
+++ b/app/const/key-val/org.ts
@@ -0,0 +1,69 @@
+export type InfragroupCode = 'building' | 'floor' | 'warehouse' | 'room' | 'chamber' | 'bed' | 'counter' | 'public-screen'
+export type ItemGroupCode = 'infra' | 'medicine' | 'device' | 'material' | 'employee-fee' | 'doctor-fee'
+export type UnitTypeCode = 'reg' | 'exa' | 'pay' | 'pha' | 'lab' | 'rad'
+export type ContractStatusCode = 'system' | 'employee' | 'intern' | 'other'
+export type EmployeePositionCode = 'reg' | 'nur' | 'doc' | 'miw' | 'thr' | 'nut' | 'lab' | 'pha' | 'nom'
+export type InternPositionCode = 'specialist' | 'nurse' | 'non-medic'
+export type DoctorFeeTypeCodes = 'outpatient' | 'emergency' | 'inpatient' | 'medic-rehab'
+
+export const infragroupCodes: Record = {
+ building: 'Bangunan',
+ floor: 'Lantai',
+ warehouse: 'Gudang / Depo',
+ room: 'Ruang',
+ chamber: 'Kamar',
+ bed: 'Ranjang',
+ counter: 'Counter',
+ 'public-screen': 'Public Screen',
+}
+
+export const itemGroupCodes: Record = {
+ infra: 'Infrastruktur',
+ medicine: 'Obat',
+ device: 'Peralatan',
+ material: 'Perlengkapan',
+ 'employee-fee': 'Gaji Karyawan',
+ 'doctor-fee': 'Gaji Dokter',
+}
+
+export const unitTypeCodes: Record = {
+ reg: 'Registrasi',
+ exa: 'Pemeriksaan',
+ pay: 'Pembayaran',
+ pha: 'Farmasai',
+ lab: 'Laboratorium',
+ rad: 'Radiologi',
+}
+
+export const contractStatusCodes: Record = {
+ system: 'Sistem',
+ employee: 'Pegawai',
+ intern: 'Magang',
+ other: 'Lainnya',
+}
+
+export const employeePositionCodes: Record = {
+ reg: 'Admisi / Pendaftaran',
+ nur: 'Perawat',
+ doc: 'Dokter',
+ miw: 'Bidan',
+ thr: 'Terapis',
+ nut: 'Ahli Gisi',
+ lab: 'Laboran',
+ pha: 'Farmasi',
+ nom: 'Non Medis',
+}
+
+export const internPositionCodes: Record = {
+ specialist: 'PPDS',
+ nurse: 'Perawat',
+ 'non-medic': 'Non Medis',
+}
+
+export const doctorFeeTypeCodes: Record = {
+ outpatient: 'Rawat Jalan',
+ emergency: 'Darurat',
+ inpatient: 'Rawat Inap',
+ 'medic-rehab': 'Rehab Medik',
+}
+
diff --git a/app/const/key-val/person.ts b/app/const/key-val/person.ts
new file mode 100644
index 00000000..f4f18fdb
--- /dev/null
+++ b/app/const/key-val/person.ts
@@ -0,0 +1,80 @@
+export type GenderCode = 'male' | 'female' | 'not-stated' | 'unknown'
+export type ReligionCode = 'islam' | 'protestan' | 'katolik' | 'hindu' | 'buda' | 'konghucu'
+export type EducationCode = 'TS' | 'TK' | 'SD' | 'SMP' | 'SMA' | 'D1' | 'D2' | 'D3' | 'S1' | 'S2' | 'S3'
+export type OccupationCode = 'tidak-bekerja' | 'pns' | 'polisi' | 'tni' | 'guru' | 'wiraswasta' | 'kary-swasta' | 'lainnya'
+export type AddressLocationTypeCode = 'identity' | 'domicile'
+export type PersonContactType = 'phone' | 'm-phone' | 'email' | 'fax'
+export type RelationshipCode = 'mother' | 'father' | 'uncle' | 'aunt' | 'sibling' | 'gd-mother' | 'gd-father' | 'child' | 'nephew' | 'gd-child' | 'friend' | 'spouse' | 'self' | 'other'
+export type MaritalStatus_Code = 'S' | 'M' | 'D' | 'W'
+
+export const genderCodes: Record = {
+ male: 'Laki',
+ female: 'Perempuan',
+ 'not-stated': 'Tidak Disebutkan',
+ unknown: 'Tidak Diketahui',
+}
+
+export const religionCodes: Record = {
+ islam: 'Islam',
+ protestan: 'Protestan',
+ katolik: 'Katolik',
+ hindu: 'Hindu',
+ buda: 'Buda',
+ konghucu: 'Konghucu',
+}
+
+export const educationCodes: Record = {
+ TS: 'TS',
+ TK: 'TK',
+ SD: 'SD',
+ SMP: 'SMP',
+ SMA: 'SMA',
+ D1: 'D1',
+ D2: 'D2',
+ D3: 'D3',
+ S1: 'S1',
+ S2: 'S2',
+ S3: 'S3',
+}
+
+export const occupationCodes: Record = {
+ 'tidak-bekerja': 'Tidak Bekerja',
+ pns: 'Pegawai Negeri Sipil',
+ polisi: 'Polisi',
+ tni: 'TNI',
+ guru: 'Guru',
+ wiraswasta: 'Wiraswasta',
+ 'kary-swasta': 'Karyawan Swasta',
+ lainnya: 'Lainnya',
+}
+
+export const personContactTypes: Record = {
+ phone: 'Telepon',
+ 'm-phone': 'HP / Ponsel',
+ email: 'Email',
+ fax: 'Fax',
+}
+
+export const relationshipCodes: Record = {
+ mother: 'Ibu',
+ father: 'Ayah',
+ uncle: 'Paman',
+ aunt: 'Bibi',
+ sibling: 'Saudara',
+ 'gd-mother': 'Nenek',
+ 'gd-father': 'Kakek',
+ child: 'Anak',
+ nephew: 'Keponakan',
+ 'gd-child': 'Cucu',
+ friend: 'Teman',
+ spouse: 'Pasangan (Suami / Istri)',
+ self: 'Diri sendiri',
+ other: 'Lainnya',
+}
+
+export const maritalStatusCodes: Record = {
+ S: 'Single (Belum Kawin)',
+ M: 'Married (Kawin)',
+ D: 'Divorced (Cerai Hidup)',
+ W: 'Widowed (Cerai Mati)',
+}
diff --git a/app/const/page-permission/ambulatory.ts b/app/const/page-permission/ambulatory.ts
new file mode 100644
index 00000000..453f59c3
--- /dev/null
+++ b/app/const/page-permission/ambulatory.ts
@@ -0,0 +1,57 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/ambulatory/registration-queue': {
+ 'emp|reg': ['R', 'U', 'D'],
+ },
+ '/ambulatory/encounter-queue': {
+ 'emp|nur': ['R', 'U', 'D'],
+ },
+ '/ambulatory/encounter': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ 'emp|thr': ['R'],
+ 'emp|miw': ['R'],
+ 'emp|nut': ['R'],
+ 'emp|pha': ['R'],
+ 'emp|lab': ['R'],
+ 'emp|rad': ['R'],
+ },
+ '/ambulatory/encounter/add': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/ambulatory/encounter/[id]': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ 'emp|thr': ['R'],
+ 'emp|miw': ['R'],
+ 'emp|nut': ['R'],
+ 'emp|pha': ['R'],
+ 'emp|lab': ['R'],
+ 'emp|rad': ['R'],
+ },
+ '/ambulatory/encounter/[id]/edit': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/ambulatory/encounter/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ 'emp|nur': ['R', 'U'],
+ 'emp|thr': ['R', 'U'],
+ 'emp|miw': ['R', 'U'],
+ 'emp|nut': ['R', 'U'],
+ 'emp|pha': ['R', 'U'],
+ 'emp|lab': ['R', 'U'],
+ 'emp|rad': ['R', 'U'],
+ },
+ '/ambulatory/consulation': {
+ 'emp|doc': ['R'],
+ },
+ '/ambulatory/consulation/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ },
+}
diff --git a/app/const/page-permission/chemo.ts b/app/const/page-permission/chemo.ts
new file mode 100644
index 00000000..8796999e
--- /dev/null
+++ b/app/const/page-permission/chemo.ts
@@ -0,0 +1,32 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/chemo/verification': {
+ 'emp|reg': ['R'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ },
+ '/chemo/verification/[id]': {
+ 'emp|reg': ['R'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ },
+ '/chemo/verification/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ 'emp|nur': ['R', 'U'],
+ },
+ '/chemo/action': {
+ 'emp|reg': ['R', 'U', 'D'],
+ },
+ '/chemo/action/[id]': {
+ 'emp|reg': ['R', 'U', 'D'],
+ },
+ '/chemo/action/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ 'emp|nur': ['R', 'U'],
+ 'emp|thr': ['R', 'U'],
+ },
+}
diff --git a/app/const/page-permission/chemoteraphy.ts b/app/const/page-permission/chemoteraphy.ts
new file mode 100644
index 00000000..6a1af6d5
--- /dev/null
+++ b/app/const/page-permission/chemoteraphy.ts
@@ -0,0 +1,16 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/chemotherapy/adm': {
+ 'emp|reg': ['R'],
+ 'emp|nur': ['R', 'U', 'D'],
+ },
+ '/outpatient/series': {
+ 'emp|nur': ['R', 'U', 'D'],
+ 'emp|doc': ['R', 'U', 'D'],
+ 'emp|thr': ['R', 'U', 'D'],
+ },
+}
diff --git a/app/const/page-permission/emergency.ts b/app/const/page-permission/emergency.ts
new file mode 100644
index 00000000..cfbab478
--- /dev/null
+++ b/app/const/page-permission/emergency.ts
@@ -0,0 +1,67 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/emergency/triage': {
+ 'emp|doc': ['C', 'R', 'U', 'D'],
+ 'emp|nur': ['C', 'R', 'U', 'D'],
+ },
+ '/emergency/triage/add': {
+ 'emp|doc': ['C', 'R', 'U', 'D'],
+ 'emp|nur': ['C', 'R', 'U', 'D'],
+ },
+ '/emergency/triage/[id]': {
+ 'emp|doc': ['C', 'R', 'U', 'D'],
+ 'emp|nur': ['C', 'R', 'U', 'D'],
+ },
+ '/emergency/triage/[id]/edit': {
+ 'emp|doc': ['C', 'R', 'U', 'D'],
+ 'emp|nur': ['C', 'R', 'U', 'D'],
+ },
+ '/emergency/encounter': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ 'emp|thr': ['R'],
+ 'emp|miw': ['R'],
+ 'emp|nut': ['R'],
+ 'emp|pha': ['R'],
+ 'emp|lab': ['R'],
+ 'emp|rad': ['R'],
+ },
+ '/emergency/encounter/add': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/emergency/encounter/[id]': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ 'emp|thr': ['R'],
+ 'emp|miw': ['R'],
+ 'emp|nut': ['R'],
+ 'emp|pha': ['R'],
+ 'emp|lab': ['R'],
+ 'emp|rad': ['R'],
+ },
+ '/emergency/encounter/[id]/edit': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/emergency/encounter/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ 'emp|nur': ['R', 'U'],
+ 'emp|thr': ['R', 'U'],
+ 'emp|miw': ['R', 'U'],
+ 'emp|nut': ['R', 'U'],
+ 'emp|pha': ['R', 'U'],
+ 'emp|lab': ['R', 'U'],
+ 'emp|rad': ['R', 'U'],
+ },
+ '/emergency/consulation': {
+ 'emp|doc': ['R'],
+ },
+ '/emergency/consulation/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ },
+}
diff --git a/app/const/page-permission/human-src.ts b/app/const/page-permission/human-src.ts
new file mode 100644
index 00000000..1698f3e5
--- /dev/null
+++ b/app/const/page-permission/human-src.ts
@@ -0,0 +1,25 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/human-src/employee': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
+ },
+ '/human-src/employee/add': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
+ },
+ '/human-src/employee/[id]/edit': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
+ },
+ '/human-src/intern': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
+ },
+ '/human-src/intern/add': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
+ },
+ '/human-src/intern/[id]/edit': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
+ },
+}
diff --git a/app/const/page-permission/inpatient.ts b/app/const/page-permission/inpatient.ts
new file mode 100644
index 00000000..b116c5fa
--- /dev/null
+++ b/app/const/page-permission/inpatient.ts
@@ -0,0 +1,57 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/inpatient/request': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/inpatient/request/[id]/detail': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/inpatient/encounter': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ 'emp|thr': ['R'],
+ 'emp|miw': ['R'],
+ 'emp|nut': ['R'],
+ 'emp|pha': ['R'],
+ 'emp|lab': ['R'],
+ 'emp|rad': ['R'],
+ },
+ '/inpatient/encounter/add': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/inpatient/encounter/[id]': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ 'emp|doc': ['R'],
+ 'emp|nur': ['R'],
+ 'emp|thr': ['R'],
+ 'emp|miw': ['R'],
+ 'emp|nut': ['R'],
+ 'emp|pha': ['R'],
+ 'emp|lab': ['R'],
+ 'emp|rad': ['R'],
+ },
+ '/inpatient/encounter/[id]/edit': {
+ 'emp|reg': ['C', 'R', 'U', 'D'],
+ },
+ '/inpatient/encounter/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ 'emp|nur': ['R', 'U'],
+ 'emp|thr': ['R', 'U'],
+ 'emp|miw': ['R', 'U'],
+ 'emp|nut': ['R', 'U'],
+ 'emp|pha': ['R', 'U'],
+ 'emp|lab': ['R', 'U'],
+ 'emp|rad': ['R', 'U'],
+ },
+ '/inpatient/consulation': {
+ 'emp|doc': ['R'],
+ },
+ '/inpatient/consulation/[id]/process': {
+ 'emp|doc': ['R', 'U'],
+ },
+}
diff --git a/app/const/page-permission/mcu-src.ts b/app/const/page-permission/mcu-src.ts
new file mode 100644
index 00000000..85ad5f71
--- /dev/null
+++ b/app/const/page-permission/mcu-src.ts
@@ -0,0 +1,19 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/muc-src/checkup': {
+ 'emp|lab': ['C', 'R', 'U', 'D'],
+ },
+ '/muc-src/checkup-category': {
+ 'emp|lab': ['C', 'R', 'U', 'D'],
+ },
+ '/mcu-src/antibiotic-src': {
+ 'emp|lab': ['C', 'R', 'U', 'D'],
+ },
+ '/mcu-src/antibiotic-src-category': {
+ 'emp|lab': ['C', 'R', 'U', 'D'],
+ }
+}
diff --git a/app/const/page-permission/org-src.ts b/app/const/page-permission/org-src.ts
new file mode 100644
index 00000000..9bd9eb68
--- /dev/null
+++ b/app/const/page-permission/org-src.ts
@@ -0,0 +1,31 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/infra-src/bed': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+ '/infra-src/chamber': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+ '/infra-src/room': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+ '/infra-src/warehouse': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+ '/infra-src/building': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+ '/infra-src/floor': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+ '/infra-src/counter': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+ '/infra-src/public-screen': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ },
+}
diff --git a/app/const/page-permission/tools-equipment.ts b/app/const/page-permission/tools-equipment.ts
new file mode 100644
index 00000000..dd112ae8
--- /dev/null
+++ b/app/const/page-permission/tools-equipment.ts
@@ -0,0 +1,27 @@
+import type { Permission } from "~/models/role";
+
+// Should we define the keys first?
+// export type Keys = 'key1' | 'key2' | 'key3' | etc
+
+export const permissions: Record> = {
+ '/tools-equipment-src/medicine': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ 'emp|pha': ['R'],
+ },
+ '/tools-equipment-src/medicine-method': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ 'emp|pha': ['R'],
+ },
+ '/tools-equipment-src/medicine-type': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ 'emp|pha': ['R'],
+ },
+ '/tools-equipment-src/medicine-form': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ 'emp|pha': ['R'],
+ },
+ '/tools-equipment-src/device-src': {
+ 'div|fin': ['C', 'R', 'U', 'D'],
+ 'emp|pha': ['R'],
+ },
+}
diff --git a/app/handlers/device-order.handler.ts b/app/handlers/device-order.handler.ts
index b1df996b..ab6f8fb5 100644
--- a/app/handlers/device-order.handler.ts
+++ b/app/handlers/device-order.handler.ts
@@ -1,8 +1,9 @@
// Handlers
import { genCrudHandler } from '~/handlers/_handler'
+import type { DeviceOrder } from '~/models/device-order'
// Services
-import { create, update, remove } from '~/services/device-order-item.service'
+import { create, update, remove } from '~/services/device-order.service'
export const {
recId,
@@ -17,7 +18,7 @@ export const {
handleActionEdit,
handleActionRemove,
handleCancelForm,
-} = genCrudHandler({
+} = genCrudHandler({
create,
update,
remove,
diff --git a/app/handlers/encounter-entry.handler.ts b/app/handlers/encounter-entry.handler.ts
new file mode 100644
index 00000000..71594ebf
--- /dev/null
+++ b/app/handlers/encounter-entry.handler.ts
@@ -0,0 +1,596 @@
+import { ref, reactive, computed } from 'vue'
+import { useRoute } from 'vue-router'
+
+// Components
+import { toast } from '~/components/pub/ui/toast'
+
+// Models
+import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
+import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
+import { paymentTypes, sepRefTypeCodes, participantGroups } from '~/lib/constants.vclaim'
+
+// Stores
+import { useUserStore } from '~/stores/user'
+
+// Services
+import {
+ getList as getSpecialistList,
+ getValueTreeItems as getSpecialistTreeItems,
+} from '~/services/specialist.service'
+import { getValueLabelList as getDoctorValueLabelList } from '~/services/doctor.service'
+import {
+ create as createEncounter,
+ getDetail as getEncounterDetail,
+ update as updateEncounter,
+} from '~/services/encounter.service'
+import { getList as getSepList } from '~/services/vclaim-sep.service'
+
+// Handlers
+import {
+ patients,
+ selectedPatient,
+ selectedPatientObject,
+ paginationMeta,
+ getPatientsList,
+ getPatientCurrent,
+ getPatientByIdentifierSearch,
+} from '~/handlers/patient.handler'
+
+export function useEncounterEntry(props: {
+ id: number
+ classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
+ subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
+}) {
+ const route = useRoute()
+ const userStore = useUserStore()
+ const openPatient = ref(false)
+ const isLoading = reactive({
+ isTableLoading: false,
+ })
+ const paymentsList = ref>([])
+ const sepsList = ref>([])
+ const participantGroupsList = ref>([])
+ const specialistsTree = ref([])
+ const specialistsData = ref([])
+ const doctorsList = ref>([])
+ const recSelectId = ref(null)
+ const isSaving = ref(false)
+ const isLoadingDetail = ref(false)
+ const encounterData = ref(null)
+ const formObjects = ref({})
+ const isSepValid = ref(false)
+ const isCheckingSep = ref(false)
+ const sepNumber = ref('')
+ const vclaimReference = ref(null)
+
+ const isEditMode = computed(() => props.id > 0)
+
+ const isSaveDisabled = computed(() => {
+ return !selectedPatient.value || !selectedPatientObject.value || isSaving.value || isLoadingDetail.value
+ })
+
+ function getListPath(): string {
+ if (props.classCode === 'ambulatory' && props.subClassCode === 'rehab') {
+ return '/rehab/encounter'
+ }
+ if (props.classCode === 'ambulatory' && props.subClassCode === 'reg') {
+ return '/outpatient/encounter'
+ }
+ if (props.classCode === 'emergency') {
+ return '/emergency/encounter'
+ }
+ if (props.classCode === 'inpatient') {
+ return '/inpatient/encounter'
+ }
+ return '/encounter'
+ }
+
+ function toKebabCase(str: string): string {
+ return str.replace(/([A-Z])/g, '-$1').toLowerCase()
+ }
+
+ function toNavigateSep(values: any) {
+ const queryParams = new URLSearchParams()
+ if (values['subSpecialistCode']) {
+ const isSub = getIsSubspecialist(values['subSpecialistCode'], specialistsTree.value)
+ if (!isSub) {
+ values['specialistCode'] = values['subSpecialistCode']
+ delete values['subSpecialistCode']
+ }
+ }
+
+ Object.keys(values).forEach((field) => {
+ if (values[field]) {
+ queryParams.append(toKebabCase(field), values[field])
+ }
+ })
+
+ navigateTo('/integration/bpjs-vclaim/sep/add' + `?${queryParams.toString()}`)
+ }
+
+ function getIsSubspecialist(value: string, items: TreeItem[]): boolean {
+ for (const item of items) {
+ if (item.value === value) {
+ return false
+ }
+ if (item.children) {
+ for (const child of item.children) {
+ if (child.value === value) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+ }
+
+ function getSpecialistCodeFromId(id: number | null | undefined): string | null {
+ if (!id) return null
+
+ if (encounterData.value?.specialist?.id === id) {
+ return encounterData.value.specialist.code || null
+ }
+
+ for (const specialist of specialistsData.value) {
+ if (specialist.id === id) {
+ return specialist.code || null
+ }
+ if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
+ for (const subspecialist of specialist.subspecialists) {
+ if (subspecialist.id === id) {
+ return subspecialist.code || null
+ }
+ }
+ }
+ }
+
+ return null
+ }
+
+ function getSubspecialistCodeFromId(id: number | null | undefined): string | null {
+ if (!id) return null
+
+ if (encounterData.value?.subspecialist?.id === id) {
+ return encounterData.value.subspecialist.code || null
+ }
+
+ for (const specialist of specialistsData.value) {
+ if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
+ for (const subspecialist of specialist.subspecialists) {
+ if (subspecialist.id === id) {
+ return subspecialist.code || null
+ }
+ }
+ }
+ }
+
+ return null
+ }
+
+ function getSpecialistIdsFromCode(code: string): { specialist_id: number | null; subspecialist_id: number | null } {
+ if (!code) {
+ return { specialist_id: null, subspecialist_id: null }
+ }
+
+ const isSub = getIsSubspecialist(code, specialistsTree.value)
+
+ if (isSub) {
+ for (const specialist of specialistsData.value) {
+ if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
+ for (const subspecialist of specialist.subspecialists) {
+ if (subspecialist.code === code) {
+ return {
+ specialist_id: specialist.id ? Number(specialist.id) : null,
+ subspecialist_id: subspecialist.id ? Number(subspecialist.id) : null,
+ }
+ }
+ }
+ }
+ }
+ } else {
+ for (const specialist of specialistsData.value) {
+ if (specialist.code === code) {
+ return {
+ specialist_id: specialist.id ? Number(specialist.id) : null,
+ subspecialist_id: null,
+ }
+ }
+ }
+ }
+
+ return { specialist_id: null, subspecialist_id: null }
+ }
+
+ async function getValidateSepNumber(sepNumberValue: string) {
+ vclaimReference.value = null
+ if (!sepNumberValue || sepNumberValue.trim() === '') {
+ isSepValid.value = false
+ isCheckingSep.value = false
+ return
+ }
+
+ try {
+ isSepValid.value = false
+ isCheckingSep.value = true
+ const result = await getSepList({ number: sepNumberValue.trim() })
+ if (result.success && result.body?.response !== null) {
+ const response = result.body?.response || {}
+ if (Object.keys(response).length > 0) {
+ formObjects.value.patientName = response.peserta?.nama || '-'
+ formObjects.value.medicalRecordNumber = response.peserta?.noMr || '-'
+ formObjects.value.cardNumber = response.peserta?.noKartu || '-'
+ formObjects.value.registerDate = response.tglSep || null
+ vclaimReference.value = {
+ noSep: response.noSep || sepNumberValue.trim(),
+ tglRujukan: response.tglSep ? new Date(response.tglSep).toISOString() : null,
+ ppkDirujuk: response.noRujukan || 'rssa',
+ jnsPelayanan: response.jnsPelayanan === 'Rawat Jalan' ? '2' : response.jnsPelayanan === 'Rawat Inap' ? '1' : null,
+ catatan: response.catatan || '',
+ diagRujukan: response.diagnosa || '',
+ tipeRujukan: response.tujuanKunj?.kode ?? '0',
+ poliRujukan: response.poli || '',
+ user: '',
+ }
+ }
+ isSepValid.value = result.body?.metaData?.code === '200'
+ }
+ } catch (error) {
+ console.error('Error checking SEP:', error)
+ isSepValid.value = false
+ } finally {
+ isCheckingSep.value = false
+ }
+ }
+
+ async function handleFetchSpecialists() {
+ try {
+ const specialistsResult = await getSpecialistList({ 'page-size': 100, includes: 'subspecialists' })
+ if (specialistsResult.success) {
+ const specialists = specialistsResult.body?.data || []
+ specialistsData.value = specialists
+ specialistsTree.value = getSpecialistTreeItems(specialists)
+ }
+ } catch (error) {
+ console.error('Error fetching specialist-subspecialist tree:', error)
+ }
+ }
+
+ async function handleFetchDoctors(subSpecialistId: string | null = null) {
+ try {
+ const filterParams: any = { 'page-size': 100, includes: 'employee-Person' }
+
+ if (!subSpecialistId) {
+ const doctors = await getDoctorValueLabelList(filterParams, true)
+ doctorsList.value = doctors
+ return
+ }
+
+ const isSub = getIsSubspecialist(subSpecialistId, specialistsTree.value)
+
+ if (isSub) {
+ filterParams['subspecialist-id'] = subSpecialistId
+ } else {
+ filterParams['specialist-id'] = subSpecialistId
+ }
+
+ const doctors = await getDoctorValueLabelList(filterParams, true)
+ doctorsList.value = doctors
+ } catch (error) {
+ console.error('Error fetching doctors:', error)
+ doctorsList.value = []
+ }
+ }
+
+ async function handleInit() {
+ selectedPatientObject.value = null
+ paymentsList.value = Object.keys(paymentTypes).map((item) => ({
+ value: item.toString(),
+ label: paymentTypes[item],
+ })) as any
+ sepsList.value = Object.keys(sepRefTypeCodes).map((item) => ({
+ value: item.toString(),
+ label: sepRefTypeCodes[item],
+ })) as any
+ participantGroupsList.value = Object.keys(participantGroups).map((item) => ({
+ value: item.toString(),
+ label: participantGroups[item],
+ })) as any
+ await handleFetchDoctors()
+ await handleFetchSpecialists()
+ if (route.query) {
+ formObjects.value = { ...formObjects.value }
+ const queries = route.query as any
+ if (queries['sep-number']) {
+ formObjects.value.sepNumber = queries['sep-number']
+ formObjects.value.paymentType = 'jkn'
+ }
+ }
+ }
+
+ async function loadEncounterDetail() {
+ if (!isEditMode.value || props.id <= 0) {
+ return
+ }
+
+ try {
+ isLoadingDetail.value = true
+ const result = await getEncounterDetail(props.id, {
+ includes: 'patient,patient-person,specialist,subspecialist',
+ })
+ if (result.success && result.body?.data) {
+ encounterData.value = result.body.data
+ await mapEncounterToForm(encounterData.value)
+ isLoadingDetail.value = false
+ } else {
+ toast({
+ title: 'Gagal',
+ description: 'Gagal memuat data kunjungan',
+ variant: 'destructive',
+ })
+ await navigateTo(getListPath())
+ }
+ } catch (error: any) {
+ console.error('Error loading encounter detail:', error)
+ toast({
+ title: 'Gagal',
+ description: error?.message || 'Gagal memuat data kunjungan',
+ variant: 'destructive',
+ })
+ await navigateTo(getListPath())
+ } finally {
+ isLoadingDetail.value = false
+ }
+ }
+
+ async function mapEncounterToForm(encounter: any) {
+ if (!encounter) return
+
+ if (encounter.patient) {
+ selectedPatient.value = String(encounter.patient.id)
+ selectedPatientObject.value = encounter.patient
+ if (!encounter.patient.person) {
+ await getPatientCurrent(selectedPatient.value)
+ }
+ }
+
+ const formData: any = {}
+ if (encounter.patient?.person) {
+ formData.patientName = encounter.patient.person.name || ''
+ formData.nationalIdentity = encounter.patient.person.residentIdentityNumber || ''
+ formData.medicalRecordNumber = encounter.patient.number || ''
+ } else if (selectedPatientObject.value?.person) {
+ formData.patientName = selectedPatientObject.value.person.name || ''
+ formData.nationalIdentity = selectedPatientObject.value.person.residentIdentityNumber || ''
+ formData.medicalRecordNumber = selectedPatientObject.value.number || ''
+ }
+
+ const doctorId = encounter.appointment_doctor_id || encounter.responsible_doctor_id
+ if (doctorId) {
+ formData.doctorId = String(doctorId)
+ }
+
+ if (encounter.subspecialist_id) {
+ const subspecialistCode = getSubspecialistCodeFromId(encounter.subspecialist_id)
+ if (subspecialistCode) {
+ formData.subSpecialistId = subspecialistCode
+ }
+ } else if (encounter.specialist_id) {
+ const specialistCode = getSpecialistCodeFromId(encounter.specialist_id)
+ if (specialistCode) {
+ formData.subSpecialistId = specialistCode
+ }
+ }
+
+ if (!formData.subSpecialistId) {
+ if (encounter.subspecialist?.code) {
+ formData.subSpecialistId = encounter.subspecialist.code
+ } else if (encounter.specialist?.code) {
+ formData.subSpecialistId = encounter.specialist.code
+ }
+ }
+
+ if (encounter.registeredAt) {
+ const date = new Date(encounter.registeredAt)
+ formData.registerDate = date.toISOString().split('T')[0]
+ } else if (encounter.visitDate) {
+ const date = new Date(encounter.visitDate)
+ formData.registerDate = date.toISOString().split('T')[0]
+ }
+
+ if (encounter.paymentMethod_code) {
+ if (encounter.paymentMethod_code === 'insurance') {
+ formData.paymentType = 'jkn'
+ } else {
+ const validPaymentTypes = ['jkn', 'jkmm', 'spm', 'pks']
+ if (validPaymentTypes.includes(encounter.paymentMethod_code)) {
+ formData.paymentType = encounter.paymentMethod_code
+ } else {
+ formData.paymentType = 'spm'
+ }
+ }
+ } else {
+ formData.paymentType = 'spm'
+ }
+
+ formData.cardNumber = encounter.member_number || ''
+ formData.sepNumber = encounter.ref_number || ''
+ formObjects.value = formData
+
+ if (formData.sepNumber) {
+ sepNumber.value = formData.sepNumber
+ }
+ if (formData.subSpecialistId) {
+ await handleFetchDoctors(formData.subSpecialistId)
+ }
+ }
+
+ async function handleSaveEncounter(formValues: any) {
+ if (!selectedPatient.value || !selectedPatientObject.value) {
+ toast({
+ title: 'Gagal',
+ description: 'Pasien harus dipilih terlebih dahulu',
+ variant: 'destructive',
+ })
+ return
+ }
+
+ try {
+ isSaving.value = true
+
+ const employeeId = userStore.user?.employee_id || userStore.user?.employee?.id || 0
+
+ const formatDate = (dateString: string): string => {
+ if (!dateString) return ''
+ const date = new Date(dateString)
+ return date.toISOString()
+ }
+
+ const { specialist_id, subspecialist_id } = getSpecialistIdsFromCode(formValues.subSpecialistId || '')
+
+ const patientId = formValues.patient_id || selectedPatientObject.value?.id || Number(selectedPatient.value)
+
+ const registeredAtValue = formValues.registeredAt || formValues.registerDate || ''
+ const visitDateValue = formValues.visitDate || formValues.registeredAt || formValues.registerDate || ''
+ const memberNumber = formValues.member_number ?? formValues.cardNumber ?? formValues.memberNumber ?? null
+ const refNumber = formValues.ref_number ?? formValues.sepNumber ?? formValues.refNumber ?? null
+
+ let paymentMethodCode = formValues.paymentMethod_code ?? null
+ if (!paymentMethodCode) {
+ if (formValues.paymentType === 'jkn' || formValues.paymentType === 'jkmm') {
+ paymentMethodCode = 'insurance'
+ } else if (formValues.paymentType === 'spm') {
+ paymentMethodCode = 'cash'
+ } else if (formValues.paymentType === 'pks') {
+ paymentMethodCode = 'membership'
+ } else {
+ paymentMethodCode = 'cash'
+ }
+ }
+
+ const payload: any = {
+ patient_id: patientId,
+ appointment_doctor_code: formValues.doctorId || null,
+ class_code: props.classCode || '',
+ subClass_code: props.subClassCode || '',
+ infra_id: formValues.infra_id ?? null,
+ unit_code: userStore?.user?.unit_code ?? null,
+ refSource_name: formValues.refSource_name ?? 'RSSA',
+ refTypeCode: formValues.paymentType === 'jkn' ? 'bpjs' : '',
+ vclaimReference: vclaimReference.value ?? null,
+ paymentType: formValues.paymentType,
+ registeredAt: formatDate(registeredAtValue),
+ visitDate: formatDate(visitDateValue),
+ }
+
+ if (props.classCode !== 'inpatient') {
+ delete payload.infra_id
+ }
+ if (employeeId && employeeId > 0) {
+ payload.adm_employee_id = employeeId
+ }
+ if (specialist_id) {
+ payload.specialist_id = specialist_id
+ }
+ if (subspecialist_id) {
+ payload.subspecialist_id = subspecialist_id
+ }
+ if (paymentMethodCode) {
+ payload.paymentMethod_code = paymentMethodCode
+ }
+
+ if (paymentMethodCode === 'insurance') {
+ payload.insuranceCompany_id = formValues.insuranceCompany_id ?? null
+ if (memberNumber) payload.member_number = memberNumber
+ if (refNumber) payload.ref_number = refNumber
+ if (formValues.refTypeCode) payload.refTypeCode = formValues.refTypeCode
+ if (formValues.vclaimReference) payload.vclaimReference = formValues.vclaimReference
+ } else {
+ if (paymentMethodCode === 'membership' && memberNumber) {
+ payload.member_number = memberNumber
+ }
+ if (refNumber) {
+ payload.ref_number = refNumber
+ }
+ }
+
+ if (props.classCode === 'ambulatory') {
+ payload.visitMode_code = 'adm'
+ payload.allocatedVisitCount = 0
+ }
+
+ let result
+ if (isEditMode.value) {
+ result = await updateEncounter(props.id, payload)
+ } else {
+ result = await createEncounter(payload)
+ }
+
+ if (result.success) {
+ toast({
+ title: 'Berhasil',
+ description: isEditMode.value ? 'Kunjungan berhasil diperbarui' : 'Kunjungan berhasil dibuat',
+ variant: 'default',
+ })
+ await navigateTo(getListPath())
+ } else {
+ const errorMessage =
+ result.body?.message || (isEditMode.value ? 'Gagal memperbarui kunjungan' : 'Gagal membuat kunjungan')
+ toast({
+ title: 'Gagal',
+ description: errorMessage,
+ variant: 'destructive',
+ })
+ }
+ } catch (error: any) {
+ console.error('Error saving encounter:', error)
+ toast({
+ title: 'Gagal',
+ description: error?.message || (isEditMode.value ? 'Gagal memperbarui kunjungan' : 'Gagal membuat kunjungan'),
+ variant: 'destructive',
+ })
+ } finally {
+ isSaving.value = false
+ }
+ }
+
+ return {
+ patients,
+ paymentsList,
+ sepsList,
+ sepNumber,
+ participantGroupsList,
+ specialistsTree,
+ doctorsList,
+ recSelectId,
+ isSaving,
+ isLoadingDetail,
+ encounterData,
+ formObjects,
+ openPatient,
+ isSepValid,
+ isCheckingSep,
+ isEditMode,
+ isSaveDisabled,
+ isLoading,
+ selectedPatient,
+ selectedPatientObject,
+ paginationMeta,
+ loadEncounterDetail,
+ mapEncounterToForm,
+ toKebabCase,
+ toNavigateSep,
+ getListPath,
+ getSpecialistCodeFromId,
+ getSubspecialistCodeFromId,
+ getIsSubspecialist,
+ getSpecialistIdsFromCode,
+ getPatientsList,
+ getPatientCurrent,
+ getPatientByIdentifierSearch,
+ getValidateSepNumber,
+ handleFetchSpecialists,
+ handleFetchDoctors,
+ handleInit,
+ handleSaveEncounter,
+ }
+}
diff --git a/app/handlers/encounter-init.handler.ts b/app/handlers/encounter-init.handler.ts
new file mode 100644
index 00000000..886be1a1
--- /dev/null
+++ b/app/handlers/encounter-init.handler.ts
@@ -0,0 +1,491 @@
+import { isValidDate } from '~/lib/date'
+import { medicalRoles } from '~/const/common/role'
+
+export interface EncounterItem {
+ id: string
+ title: string
+ classCode?: string[]
+ unit?: string
+ afterId?: string
+ component?: any
+ props?: Record
+}
+
+export interface EncounterProps {
+ classCode: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
+ subClassCode: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
+}
+
+export interface EncounterListData {
+ encounter?: any
+ status?: any
+ medicalAssessment?: any
+ medicalAssessmentRehab: any
+ functionAssessment?: any
+ protocolTheraphy?: any
+ protocolChemotherapy?: any
+ medicineProtocolChemotherapy?: any
+ consultation?: any
+ letterOfControl?: any
+}
+
+const StatusAsync = defineAsyncComponent(() => import('~/components/content/encounter/status.vue'))
+const AssesmentFunctionListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
+const EarlyMedicalAssesmentListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
+const EarlyMedicalRehabListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
+const ChemoProtocolListAsync = defineAsyncComponent(() => import('~/components/app/chemotherapy/list.protocol.vue'))
+const ChemoMedicineProtocolListAsync = defineAsyncComponent(
+ () => import('~/components/app/chemotherapy/list.medicine.vue'),
+)
+const DeviceOrderAsync = defineAsyncComponent(() => import('~/components/content/device-order/main.vue'))
+const PrescriptionAsync = defineAsyncComponent(() => import('~/components/content/prescription/main.vue'))
+const CpLabOrderAsync = defineAsyncComponent(() => import('~/components/content/cp-lab-order/main.vue'))
+const CprjAsync = defineAsyncComponent(() => import('~/components/content/cprj/entry.vue'))
+const RadiologyAsync = defineAsyncComponent(() => import('~/components/content/radiology-order/main.vue'))
+const ConsultationAsync = defineAsyncComponent(() => import('~/components/content/consultation/list.vue'))
+const DocUploadListAsync = defineAsyncComponent(() => import('~/components/content/document-upload/list.vue'))
+const GeneralConsentListAsync = defineAsyncComponent(() => import('~/components/content/general-consent/entry.vue'))
+const ResumeListAsync = defineAsyncComponent(() => import('~/components/content/resume/list.vue'))
+const ControlLetterListAsync = defineAsyncComponent(() => import('~/components/content/control-letter/list.vue'))
+
+const defaultKeys: Record = {
+ status: {
+ id: 'status',
+ title: 'Status Masuk/Keluar',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ earlyMedicalAssessment: {
+ id: 'early-medical-assessment',
+ title: 'Pengkajian Awal Medis',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ rehabMedicalAssessment: {
+ id: 'rehab-medical-assessment',
+ title: 'Pengkajian Awal Medis Rehabilitasi Medis',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'rehab',
+ afterId: 'early-medical-assessment',
+ },
+ functionAssessment: {
+ id: 'function-assessment',
+ title: 'Asesmen Fungsi',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'rehab',
+ afterId: 'rehab-medical-assessment',
+ },
+ therapyProtocol: {
+ id: 'therapy-protocol',
+ classCode: ['ambulatory'],
+ title: 'Protokol Terapi',
+ unit: 'rehab',
+ afterId: 'function-assessment',
+ },
+ chemotherapyProtocol: {
+ id: 'chemotherapy-protocol',
+ title: 'Protokol Kemoterapi',
+ classCode: ['ambulatory'],
+ unit: 'chemo',
+ afterId: 'early-medical-assessment',
+ },
+ chemotherapyMedicine: {
+ id: 'chemotherapy-medicine',
+ title: 'Protokol Obat Kemoterapi',
+ classCode: ['ambulatory'],
+ unit: 'chemo',
+ afterId: 'chemotherapy-protocol',
+ },
+ educationAssessment: {
+ id: 'education-assessment',
+ title: 'Asesmen Kebutuhan Edukasi',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ consent: {
+ id: 'consent',
+ title: 'General Consent',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ patientNote: {
+ id: 'patient-note',
+ title: 'CPRJ',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ prescription: {
+ id: 'prescription',
+ title: 'Order Obat',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ device: {
+ id: 'device-order',
+ title: 'Order Alkes',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ mcuRadiology: {
+ id: 'mcu-radiology',
+ title: 'Order Radiologi',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ mcuLabPc: {
+ id: 'mcu-lab-pc',
+ title: 'Order Lab PK',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ mcuLabMicro: {
+ id: 'mcu-lab-micro',
+ title: 'Order Lab Mikro',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ mcuLabPa: {
+ id: 'mcu-lab-pa',
+ title: 'Order Lab PA',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ medicalAction: {
+ id: 'medical-action',
+ title: 'Order Ruang Tindakan',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ mcuResult: {
+ id: 'mcu-result',
+ title: 'Hasil Penunjang',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ consultation: {
+ id: 'consultation',
+ title: 'Konsultasi',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ resume: {
+ id: 'resume',
+ title: 'Resume',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ control: {
+ id: 'control',
+ title: 'Surat Kontrol',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ screening: {
+ id: 'screening',
+ title: 'Skrinning MPP',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+ supportingDocument: {
+ id: 'supporting-document',
+ title: 'Upload Dokumen Pendukung',
+ classCode: ['ambulatory'],
+ unit: 'rehab',
+ },
+ priceList: {
+ id: 'price-list',
+ title: 'Tarif Tindakan',
+ classCode: ['ambulatory', 'emergency', 'inpatient'],
+ unit: 'all',
+ },
+}
+
+export function getItemsByClassCode(classCode: string, items: EncounterItem[]) {
+ return items.filter((item) => item.classCode?.includes(classCode))
+}
+
+export function getItemsByUnit(unit: string, items: EncounterItem[]) {
+ return items.filter((item) => item.unit === unit)
+}
+
+export function getItemsByIds(ids: string[], items: EncounterItem[]) {
+ return items.filter((item) => ids.includes(item.id))
+}
+
+export function getIndexById(id: string, items: EncounterItem[]) {
+ return items.findIndex((item) => item.id === id)
+}
+
+export const getItemsAll = (classCode: string, unit: string, items: EncounterItem[]) => {
+ const prevItems = [...items]
+ let updateItems = getItemsByClassCode(classCode, prevItems)
+ updateItems = getItemsByUnit(unit, updateItems)
+ return updateItems
+}
+
+export function insertItemByAfterId(id: string, items: EncounterItem[], newItem: EncounterItem) {
+ const index = getIndexById(id, items)
+ if (index > -1) {
+ items.splice(index + 1, 0, newItem)
+ }
+}
+
+export function injectComponents(id: string | number, data: EncounterListData, meta: EncounterListData) {
+ const currentKeys = { ...defaultKeys }
+ if (currentKeys?.status) {
+ currentKeys.status['component'] = StatusAsync
+ currentKeys.status['props'] = { encounter: data?.encounter }
+ }
+ if (currentKeys?.earlyMedicalAssessment) {
+ currentKeys.earlyMedicalAssessment['component'] = EarlyMedicalAssesmentListAsync
+ currentKeys.earlyMedicalAssessment['props'] = {
+ encounter: data?.encounter,
+ type: 'early-medic',
+ label: currentKeys.earlyMedicalAssessment['title'],
+ }
+ }
+ if (currentKeys?.rehabMedicalAssessment) {
+ currentKeys.rehabMedicalAssessment['component'] = EarlyMedicalRehabListAsync
+ currentKeys.rehabMedicalAssessment['props'] = {
+ encounter: data?.encounter,
+ type: 'early-rehab',
+ label: currentKeys.rehabMedicalAssessment['title'],
+ }
+ }
+ if (currentKeys?.functionAssessment) {
+ currentKeys.functionAssessment['component'] = AssesmentFunctionListAsync
+ currentKeys.functionAssessment['props'] = {
+ encounter: data?.encounter,
+ type: 'function',
+ label: currentKeys.functionAssessment['title'],
+ }
+ }
+ if (currentKeys?.therapyProtocol) {
+ // TODO: add component for therapyProtocol
+ currentKeys.therapyProtocol['component'] = null
+ currentKeys.therapyProtocol['props'] = {
+ data: data?.encounter,
+ paginationMeta: meta?.protocolTheraphy,
+ }
+ }
+ if (currentKeys?.chemotherapyProtocol) {
+ currentKeys.chemotherapyProtocol['component'] = ChemoProtocolListAsync
+ currentKeys.chemotherapyProtocol['props'] = {
+ data: data?.encounter,
+ paginationMeta: meta?.protocolChemotherapy,
+ }
+ }
+ if (currentKeys?.chemotherapyMedicine) {
+ currentKeys.chemotherapyMedicine['component'] = ChemoMedicineProtocolListAsync
+ currentKeys.chemotherapyMedicine['props'] = {
+ data: data?.encounter,
+ paginationMeta: meta?.medicineProtocolChemotherapy,
+ }
+ }
+ if (currentKeys?.educationAssessment) {
+ // TODO: add component for education assessment
+ currentKeys.educationAssessment['component'] = null
+ currentKeys.educationAssessment['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.consent) {
+ currentKeys.consent['component'] = GeneralConsentListAsync
+ currentKeys.consent['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.patientNote) {
+ currentKeys.patientNote['component'] = CprjAsync
+ currentKeys.patientNote['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.prescription) {
+ currentKeys.prescription['component'] = PrescriptionAsync
+ currentKeys.prescription['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.device) {
+ currentKeys.device['component'] = DeviceOrderAsync
+ currentKeys.device['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.mcuRadiology) {
+ currentKeys.mcuRadiology['component'] = RadiologyAsync
+ currentKeys.mcuRadiology['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.mcuLabPc) {
+ currentKeys.mcuLabPc['component'] = CpLabOrderAsync
+ currentKeys.mcuLabPc['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.mcuLabMicro) {
+ // TODO: add component for mcuLabMicro
+ currentKeys.mcuLabMicro['component'] = null
+ currentKeys.mcuLabMicro['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.mcuLabPa) {
+ // TODO: add component for mcuLabPa
+ currentKeys.mcuLabPa['component'] = null
+ currentKeys.mcuLabPa['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.medicalAction) {
+ // TODO: add component for medicalAction
+ currentKeys.medicalAction['component'] = null
+ currentKeys.medicalAction['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.mcuResult) {
+ // TODO: add component for mcuResult
+ currentKeys.mcuResult['component'] = null
+ currentKeys.mcuResult['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.consultation) {
+ currentKeys.consultation['component'] = ConsultationAsync
+ currentKeys.consultation['props'] = { encounter: data?.encounter }
+ }
+ if (currentKeys?.resume) {
+ currentKeys.resume['component'] = ResumeListAsync
+ currentKeys.resume['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.control) {
+ currentKeys.control['component'] = ControlLetterListAsync
+ currentKeys.control['props'] = { encounter: data?.encounter }
+ }
+ if (currentKeys?.screening) {
+ // TODO: add component for screening
+ currentKeys.screening['component'] = null
+ currentKeys.screening['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.supportingDocument) {
+ currentKeys.supportingDocument['component'] = DocUploadListAsync
+ currentKeys.supportingDocument['props'] = { encounter_id: id }
+ }
+ if (currentKeys?.priceList) {
+ // TODO: add component for priceList
+ currentKeys.priceList['component'] = null
+ currentKeys.priceList['props'] = { encounter_id: id }
+ }
+
+ return currentKeys
+}
+
+export function mergeArrayAt(arraysOne: T[], arraysTwo: T[] | T, deleteCount = 0): T[] {
+ const prevItems = arraysOne.slice()
+ if (!prevItems) return prevItems
+ const nextItems = Array.isArray(arraysTwo) ? arraysTwo : [arraysTwo]
+ if (nextItems.length === 0) return prevItems
+ // determine insertion position using the first item's `id` if available
+ const firstId = (nextItems[0] as any)?.afterId || (prevItems[0] as any)?.id
+ let pos = prevItems.length
+ if (typeof firstId === 'string') {
+ const index = prevItems.findIndex((item: any) => item.id === firstId)
+ pos = index < 0 ? Math.max(prevItems.length + index, 0) : Math.min(index, prevItems.length)
+ }
+ prevItems.splice(pos, deleteCount, ...nextItems)
+ return prevItems
+}
+
+// Function to map API response to Encounter structure
+export function mapResponseToEncounter(result: any): any {
+ if (!result) return null
+
+ // Check if patient and patient.person exist (minimal validation)
+ if (!result.patient || !result.patient.person) {
+ return null
+ }
+
+ const mapped: any = {
+ id: result.id || 0,
+ patient_id: result.patient_id || result.patient?.id || 0,
+ patient: {
+ id: result.patient?.id || 0,
+ number: result.patient?.number || '',
+ person: {
+ id: result.patient?.person?.id || 0,
+ name: result.patient?.person?.name || '',
+ birthDate: result.patient?.person?.birthDate || null,
+ gender_code: result.patient?.person?.gender_code || '',
+ residentIdentityNumber: result.patient?.person?.residentIdentityNumber || null,
+ frontTitle: result.patient?.person?.frontTitle || '',
+ endTitle: result.patient?.person?.endTitle || '',
+ addresses: result.patient?.person?.addresses || [],
+ },
+ },
+ registeredAt: result.registeredAt || result.patient?.registeredAt || null,
+ class_code: result.class_code || '',
+ unit_id: result.unit_id || 0,
+ unit: result.unit || null,
+ specialist_id: result.specialist_id || null,
+ subspecialist_id: result.subspecialist_id || null,
+ visitDate: isValidDate(result.visitDate)
+ ? result.visitDate
+ : result.registeredAt || result.patient?.registeredAt || null,
+ adm_employee_id: result.adm_employee_id || 0,
+ appointment_doctor_id: result.appointment_doctor_id || null,
+ responsible_doctor_id: result.responsible_doctor_id || null,
+ appointment_doctor: result.appointment_doctor || null,
+ responsible_doctor: result.responsible_doctor || null,
+ refSource_name: result.refSource_name || null,
+ appointment_id: result.appointment_id || null,
+ earlyEducation: result.earlyEducation || null,
+ medicalDischargeEducation: result.medicalDischargeEducation || '',
+ admDischargeEducation: result.admDischargeEducation || null,
+ discharge_method_code: result.discharge_method_code || null,
+ discharge_reason: result.dischargeReason || result.discharge_reason || null,
+ discharge_date: result.discharge_date || null,
+ status_code: result.status_code || '',
+ // Payment related fields
+ paymentMethod_code:
+ result.paymentMethod_code && result.paymentMethod_code.trim() !== '' ? result.paymentMethod_code : null,
+ trx_number: result.trx_number || null,
+ member_number: result.member_number || null,
+ ref_number: result.ref_number || null,
+ }
+
+ return mapped
+}
+
+export function getMenuItems(
+ id: string | number,
+ props: any,
+ user: any,
+ data: EncounterListData,
+ meta: any,
+) {
+ console.log(props)
+ // const normalClassCode = props.classCode === 'ambulatory' ? 'outpatient' : props.classCode
+ const normalClassCode = props.classCode === 'ambulatory' ? 'ambulatory' : props.classCode
+ const currentKeys = injectComponents(id, data, meta)
+ const defaultItems: EncounterItem[] = Object.values(currentKeys)
+ const listItemsForOutpatientRehab = mergeArrayAt(
+ getItemsAll('ambulatory', 'all', defaultItems),
+ getItemsAll('ambulatory', 'rehab', defaultItems),
+ )
+ const listItemsForOutpatientChemo = mergeArrayAt(
+ getItemsAll('ambulatory', 'all', defaultItems),
+ getItemsAll('ambulatory', 'chemo', defaultItems),
+ )
+ const listItems: Record>> = {
+ 'installation|ambulatory': {
+ 'unit|rehab': {
+ items: listItemsForOutpatientRehab,
+ roles: medicalRoles,
+ },
+ 'unit|chemo': {
+ items: listItemsForOutpatientChemo,
+ roles: medicalRoles,
+ },
+ all: getItemsAll('ambulatory', 'all', defaultItems),
+ },
+ 'installation|emergency': {
+ all: getItemsAll('emergency', 'all', defaultItems),
+ },
+ 'installation|inpatient': {
+ all: getItemsAll('inpatient', 'all', defaultItems),
+ },
+ }
+ const currentListItems = listItems[`installation|${normalClassCode}`]
+ if (!currentListItems) return []
+ const unitCode = user?.unit_code ? `unit|${user.unit_code}` : 'all'
+ const currentUnitItems: any = currentListItems[`${unitCode}`]
+ if (!currentUnitItems) return []
+ let menus = []
+ if (currentUnitItems.roles && currentUnitItems.roles?.includes(user.activeRole)) {
+ menus = [...currentUnitItems.items]
+ } else {
+ menus = unitCode !== 'all' && currentUnitItems?.items ? [...currentUnitItems.items] : [...currentUnitItems]
+ }
+ return menus
+}
diff --git a/app/handlers/encounter-process.handler.ts b/app/handlers/encounter-process.handler.ts
new file mode 100644
index 00000000..67850da4
--- /dev/null
+++ b/app/handlers/encounter-process.handler.ts
@@ -0,0 +1,32 @@
+// Services
+import { getDetail } from '~/services/encounter.service'
+
+// Handlers
+import { mapResponseToEncounter } from '~/handlers/encounter-init.handler'
+
+export async function getEncounterData(id: string | number) {
+ let data = null
+ try {
+ const dataRes = await getDetail(id, {
+ includes:
+ 'patient,patient-person,patient-person-addresses,unit,Appointment_Doctor,Appointment_Doctor-employee,Appointment_Doctor-employee-person,Responsible_Doctor,Responsible_Doctor-employee,Responsible_Doctor-employee-person',
+ })
+ const dataResBody = dataRes.body ?? null
+ const result = dataResBody?.data ?? null
+
+ if (result) {
+ const mappedData = mapResponseToEncounter(result)
+ if (mappedData) {
+ data = mappedData
+ } else {
+ data = null
+ }
+ } else {
+ data = null
+ }
+ } catch (error) {
+ console.error('Error fetching encounter data:', error)
+ data = null
+ }
+ return data
+}
\ No newline at end of file
diff --git a/app/handlers/general-consent.handler.ts b/app/handlers/general-consent.handler.ts
new file mode 100644
index 00000000..2f949f3f
--- /dev/null
+++ b/app/handlers/general-consent.handler.ts
@@ -0,0 +1,24 @@
+// Handlers
+import { genCrudHandler } from '~/handlers/_handler'
+
+// Services
+import { create, update, remove } from '~/services/general-consent.service'
+
+export const {
+ recId,
+ recAction,
+ recItem,
+ isReadonly,
+ isProcessing,
+ isFormEntryDialogOpen,
+ isRecordConfirmationOpen,
+ onResetState,
+ handleActionSave,
+ handleActionEdit,
+ handleActionRemove,
+ handleCancelForm,
+} = genCrudHandler({
+ create,
+ update,
+ remove,
+})
diff --git a/app/handlers/integration-sep-entry.handler.ts b/app/handlers/integration-sep-entry.handler.ts
new file mode 100644
index 00000000..ad267bef
--- /dev/null
+++ b/app/handlers/integration-sep-entry.handler.ts
@@ -0,0 +1,691 @@
+import { ref } from 'vue'
+import { useRoute } from 'vue-router'
+
+// Components
+import { toast } from '~/components/pub/ui/toast'
+
+// Types
+import type { SepHistoryData } from '~/components/app/sep/list-cfg.history'
+import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
+
+// Constants
+import {
+ serviceTypes,
+ serviceAssessments,
+ registerMethods,
+ trafficAccidents,
+ supportCodes,
+ procedureTypes,
+ purposeOfVisits,
+ classLevels,
+ classLevelUpgrades,
+ classPaySources,
+} from '~/lib/constants.vclaim'
+
+// Services
+import {
+ getList as getSpecialistList,
+ getValueTreeItems as getSpecialistTreeItems,
+} from '~/services/specialist.service'
+import { getValueLabelList as getProvinceList } from '~/services/vclaim-region-province.service'
+import { getValueLabelList as getCityList } from '~/services/vclaim-region-city.service'
+import { getValueLabelList as getDistrictList } from '~/services/vclaim-region-district.service'
+import { getValueLabelList as getDoctorLabelList } from '~/services/vclaim-doctor.service'
+import { getValueLabelList as getHealthFacilityLabelList } from '~/services/vclaim-healthcare.service'
+import { getValueLabelList as getDiagnoseLabelList } from '~/services/vclaim-diagnose.service'
+import { getList as getMemberList } from '~/services/vclaim-member.service'
+import { getList as getHospitalLetterList } from '~/services/vclaim-reference-hospital-letter.service'
+import { getList as getControlLetterList } from '~/services/vclaim-control-letter.service'
+import { getList as getMonitoringHistoryList } from '~/services/vclaim-monitoring-history.service'
+import { create as createSep, makeSepData } from '~/services/vclaim-sep.service'
+
+// Handlers
+import {
+ patients,
+ selectedPatient,
+ selectedPatientObject,
+ paginationMeta,
+ getPatientsList,
+ getPatientCurrent,
+ getPatientByIdentifierSearch,
+} from '~/handlers/patient.handler'
+
+export function useIntegrationSepEntry() {
+ const userStore = useUserStore()
+ const route = useRoute()
+
+ const openPatient = ref(false)
+ const openLetter = ref(false)
+ const openHistory = ref(false)
+ const selectedLetter = ref('')
+ const selectedObjects = ref({})
+ const selectedServiceType = ref('')
+ const selectedAdmissionType = ref('')
+ const histories = ref>([])
+ const letters = ref>([])
+ const doctors = ref>([])
+ const diagnoses = ref>([])
+ const facilitiesFrom = ref>([])
+ const facilitiesTo = ref>([])
+ const supportCodesList = ref>([])
+ const serviceTypesList = ref>([])
+ const registerMethodsList = ref>([])
+ const accidentsList = ref>([])
+ const purposeOfVisitsList = ref>([])
+ const proceduresList = ref>([])
+ const assessmentsList = ref>([])
+ const provincesList = ref>([])
+ const citiesList = ref>([])
+ const districtsList = ref>([])
+ const classLevelsList = ref>([])
+ const classLevelUpgradesList = ref>([])
+ const classPaySourcesList = ref>([])
+ const isServiceHidden = ref(false)
+ const isSaveLoading = ref(false)
+ const isLetterReadonly = ref(false)
+ const isLoadingPatient = ref(false)
+ const specialistsTree = ref([])
+ const resourceType = ref('')
+ const resourcePath = ref('')
+
+ /**
+ * Map letter data to form fields for save-sep
+ * Maps data from letters.value[0].information to selectedObjects and form values
+ */
+ function mapLetterDataToForm(formValues: any): any {
+ if (selectedAdmissionType.value === '3' || letters.value.length === 0) {
+ return formValues
+ }
+
+ const letterData = letters.value[0]
+ const info = letterData.information || {}
+
+ // Map data to selectedObjects for form population
+ if (info.cardNumber) {
+ selectedObjects.value['cardNumber'] = info.cardNumber
+ }
+ if (info.medicalRecordNumber) {
+ selectedObjects.value['medicalRecordNumber'] = info.medicalRecordNumber
+ }
+ if (info.patientPhone) {
+ selectedObjects.value['phoneNumber'] = info.patientPhone
+ }
+ if (info.classLevel) {
+ selectedObjects.value['classLevel'] = info.classLevel
+ }
+
+ // Map data to formValues for makeSepData
+ const mappedValues = { ...formValues }
+
+ // response.rujukan.peserta.noKartu → cardNumber (noKartu)
+ if (info.cardNumber) {
+ mappedValues.cardNumber = info.cardNumber
+ }
+
+ // response.rujukan.tglKunjungan → referralLetterDate (rujukan.tglRujukan)
+ if (letterData.plannedDate) {
+ mappedValues.referralLetterDate = letterData.plannedDate
+ }
+
+ // response.rujukan.noKunjungan → referralLetterNumber (rujukan.noRujukan)
+ if (letterData.letterNumber) {
+ mappedValues.referralLetterNumber = letterData.letterNumber
+ }
+
+ // response.rujukan.provPerujuk.kode → fromClinic (rujukan.ppkRujukan)
+ if (info.destination) {
+ mappedValues.referralTo = info.destination
+ }
+
+ // response.rujukan.poliRujukan.kode → polyCode
+ if (info.poly) {
+ mappedValues.polyCode = info.poly
+ }
+
+ // response.asalFaskes → asalRujukan (1 = Faskes 1, 2 = Faskes RS)
+ // Map facility to referralFrom (asalRujukan)
+ if (info.facility) {
+ mappedValues.referralFrom = info.facility
+ }
+
+ // response.rujukan.diagnosa.kode → initialDiagnosis (diagAwal)
+ if (info.diagnoses) {
+ mappedValues.initialDiagnosis = info.diagnoses
+ }
+
+ // response.rujukan.poliRujukan.kode → destinationClinic (poli.tujuan)
+ if (info.poly) {
+ mappedValues.destinationClinic = info.poly
+ }
+
+ // response.rujukan.peserta.hakKelas.kode → classLevel (klsRawat.klsRawatHak)
+ if (info.classLevel) {
+ mappedValues.classLevel = info.classLevel
+ }
+
+ // response.rujukan.peserta.mr.noMR → medicalRecordNumber (noMR)
+ if (info.medicalRecordNumber) {
+ mappedValues.medicalRecordNumber = info.medicalRecordNumber
+ }
+
+ // response.rujukan.peserta.mr.noTelepon → phoneNumber (noTelp)
+ if (info.patientPhone) {
+ mappedValues.phoneNumber = info.patientPhone
+ }
+
+ return mappedValues
+ }
+
+ async function getMonitoringHistoryMappers() {
+ histories.value = []
+ const dateFirst = new Date()
+ const dateLast = new Date()
+ dateLast.setMonth(dateFirst.getMonth() - 3)
+ const cardNumber =
+ selectedPatientObject.value?.person?.residentIdentityNumber || selectedPatientObject.value?.number || ''
+ const result = await getMonitoringHistoryList({
+ cardNumber: cardNumber,
+ startDate: dateFirst.toISOString().substring(0, 10),
+ endDate: dateLast.toISOString().substring(0, 10),
+ })
+ if (result && result.success && result.body) {
+ const historiesRaw = result.body?.response?.histori || []
+ if (!historiesRaw) return
+ historiesRaw.forEach((result: any) => {
+ histories.value.push({
+ sepNumber: result.noSep,
+ sepDate: result.tglSep,
+ referralNumber: result.noRujukan,
+ diagnosis:
+ result.diagnosa && typeof result.diagnosa === 'string' && result.diagnosa.length > 20
+ ? result.diagnosa.toString().substring(0, 17) + '...'
+ : '-',
+ serviceType: !result.jnsPelayanan ? '-' : result.jnsPelayanan === '1' ? 'Rawat Jalan' : 'Rawat Inap',
+ careClass: result.kelasRawat,
+ })
+ })
+ }
+ }
+
+ async function getLetterMappers(admissionType: string, search: string) {
+ letters.value = []
+ let result = null
+ if (admissionType !== '3') {
+ result = await getHospitalLetterList({
+ letterNumber: search,
+ })
+ } else {
+ result = await getControlLetterList({
+ letterNumber: search,
+ mode: 'by-control',
+ })
+ if (result && result.success && result.body) {
+ const lettersRaw = result.body?.response || null
+ if (!lettersRaw) {
+ result = await getControlLetterList({
+ letterNumber: search,
+ mode: 'by-card',
+ })
+ }
+ }
+ if (result && result.success && result.body) {
+ const lettersRaw = result.body?.response || null
+ if (!lettersRaw) {
+ result = await getControlLetterList({
+ letterNumber: search,
+ mode: 'by-sep',
+ })
+ }
+ }
+ }
+ if (result && result.success && result.body) {
+ const lettersRaw = result.body?.response || null
+ if (!lettersRaw) return
+ if (admissionType === '3') {
+ letters.value = [
+ {
+ letterNumber: lettersRaw.noSuratKontrol || '',
+ plannedDate: lettersRaw.tglRencanaKontrol || '',
+ sepNumber: lettersRaw.sep.noSep || '',
+ patientName: lettersRaw.sep.peserta.nama || '',
+ bpjsCardNo: lettersRaw.sep.peserta.noKartu,
+ clinic: lettersRaw.sep.poli || '',
+ doctor: lettersRaw.sep.namaDokter || '',
+ },
+ ]
+ } else {
+ letters.value = [
+ {
+ letterNumber: lettersRaw?.rujukan?.noKunjungan || '',
+ plannedDate: lettersRaw?.rujukan?.tglKunjungan || '',
+ sepNumber: lettersRaw?.rujukan?.informasi?.eSEP || '-',
+ patientName: lettersRaw?.rujukan?.peserta.nama || '',
+ bpjsCardNo: lettersRaw?.rujukan?.peserta.noKartu || '',
+ clinic: lettersRaw?.rujukan?.poliRujukan.nama || '',
+ doctor: '',
+ information: {
+ facility: lettersRaw?.asalFaskes || '',
+ diagnose: lettersRaw?.rujukan?.diagnosa?.kode || '',
+ serviceType: lettersRaw?.rujukan?.pelayanan?.kode || '',
+ classLevel: lettersRaw?.rujukan?.peserta?.hakKelas?.kode || '',
+ poly: lettersRaw?.rujukan?.poliRujukan?.kode || '',
+ cardNumber: lettersRaw?.rujukan?.peserta?.noKartu || '',
+ identity: lettersRaw?.rujukan?.peserta?.nik || '',
+ patientName: lettersRaw?.rujukan?.peserta?.nama || '',
+ patientPhone: lettersRaw?.rujukan?.peserta?.mr?.noTelepon || '',
+ medicalRecordNumber: lettersRaw?.rujukan?.peserta?.mr?.noMR || '',
+ destination: lettersRaw?.rujukan?.provPerujuk?.kode || '',
+ },
+ },
+ ]
+ }
+ }
+ }
+
+ async function getPatientInternalMappers(id: string) {
+ try {
+ await getPatientCurrent(id)
+ if (selectedPatientObject.value) {
+ const patient = selectedPatientObject.value
+ selectedObjects.value['cardNumber'] = '-'
+ selectedObjects.value['nationalIdentity'] = patient?.person?.residentIdentityNumber || '-'
+ selectedObjects.value['medicalRecordNumber'] = patient?.number || '-'
+ selectedObjects.value['patientName'] = patient?.person?.name || '-'
+ selectedObjects.value['phoneNumber'] = patient?.person?.contacts?.[0]?.value || '-'
+ }
+ } catch (err) {
+ console.error('Failed to load patient from query params:', err)
+ }
+ }
+
+ async function getPatientExternalMappers(id: string, type: string) {
+ try {
+ isLoadingPatient.value = true
+ const result = await getMemberList({
+ mode: type,
+ number: id,
+ date: new Date().toISOString().substring(0, 10),
+ })
+ if (result && result.success && result.body) {
+ const memberRaws = result.body?.response || null
+ selectedObjects.value['cardNumber'] = memberRaws?.peserta?.noKartu || ''
+ selectedObjects.value['nationalIdentity'] = memberRaws?.peserta?.nik || ''
+ selectedObjects.value['medicalRecordNumber'] = memberRaws?.peserta?.mr?.noMR || ''
+ selectedObjects.value['patientName'] = memberRaws?.peserta?.nama || ''
+ selectedObjects.value['phoneNumber'] = memberRaws?.peserta?.mr?.noTelepon || ''
+ selectedObjects.value['classLevel'] = memberRaws?.peserta?.hakKelas?.kode || ''
+ selectedObjects.value['status'] = memberRaws?.statusPeserta?.kode || ''
+ }
+ isLoadingPatient.value = false
+ } catch (err) {
+ console.error('Failed to load patient from query params:', err)
+ isLoadingPatient.value = false
+ }
+ }
+
+ function handleSaveLetter() {
+ // Find the selected letter and get its plannedDate
+ const selectedLetterData = letters.value.find((letter) => letter.letterNumber === selectedLetter.value)
+ if (selectedLetterData && selectedLetterData.plannedDate) {
+ selectedObjects.value['letterDate'] = selectedLetterData.plannedDate
+ }
+ }
+
+ async function handleSavePatient() {
+ selectedPatientObject.value = null
+ await getPatientInternalMappers(selectedPatient.value)
+ }
+
+ async function handleEvent(menu: string, value: any) {
+ if (menu === 'admission-type') {
+ selectedAdmissionType.value = value
+ return
+ }
+ if (menu === 'service-type') {
+ selectedServiceType.value = value
+ doctors.value = await getDoctorLabelList({
+ serviceType: selectedServiceType.value || '2',
+ serviceDate: new Date().toISOString().substring(0, 10),
+ specialistCode: 0,
+ })
+ }
+ if (menu === 'search-patient') {
+ getPatientsList({ 'page-size': 10, includes: 'person' }).then(() => {
+ openPatient.value = true
+ })
+ return
+ }
+ if (menu === 'search-patient-by-identifier') {
+ if (isLoadingPatient.value) return
+ const text = value.text
+ const type = value.type
+ const prevCardNumber = selectedObjects.value['cardNumber'] || ''
+ const prevNationalIdentity = selectedObjects.value['nationalIdentity'] || ''
+ if (type === 'indentity' && text !== prevNationalIdentity) {
+ await getPatientByIdentifierSearch(text)
+ await getPatientExternalMappers(text, 'by-identity')
+ }
+ if (type === 'cardNumber' && text !== prevCardNumber) {
+ await getPatientExternalMappers(text, 'by-card')
+ }
+ return
+ }
+ if (menu === 'search-letter') {
+ isLetterReadonly.value = false
+ getLetterMappers(value.admissionType, value.search).then(async () => {
+ if (letters.value.length > 0) {
+ const copyObjects = { ...selectedObjects.value }
+ const letter = letters.value[0]
+ selectedObjects.value = {}
+ selectedLetter.value = letter.letterNumber
+ isLetterReadonly.value = true
+ if (letter.information || letter.clinic) {
+ const poly = value.admissionType === '3' ? letter.clinic : letter.information?.poly
+ if (poly) {
+ const resultControl = await getControlLetterList({
+ mode: 'by-schedule',
+ controlDate: letter.plannedDate,
+ controlType: selectedServiceType.value,
+ polyCode: poly,
+ })
+ if (resultControl && resultControl.success && resultControl.body) {
+ const resultData = resultControl.body?.response?.list || []
+ const resultUnique = [...new Map(resultData.map((item: any) => [item.kodeDokter, item])).values()]
+ const controlLetters = resultUnique.map((item: any) => ({
+ value: item.kodeDokter ? String(item.kodeDokter) : '',
+ label: `${item.kodeDokter} - ${item.namaDokter} - ${item.jadwalPraktek} (${item.kapasitas})`,
+ }))
+ doctors.value = controlLetters
+ }
+ }
+ }
+ setTimeout(async () => {
+ selectedObjects.value = copyObjects
+ selectedObjects.value['letterDate'] = letter.plannedDate
+ selectedObjects.value['cardNumber'] = letter.information?.cardNumber || ''
+ selectedObjects.value['nationalIdentity'] = letter.information?.identity || ''
+ selectedObjects.value['medicalRecordNumber'] = letter.information?.medicalRecordNumber || ''
+ selectedObjects.value['patientName'] = letter.information?.patientName || ''
+ selectedObjects.value['phoneNumber'] = letter.information?.patientPhone || ''
+ selectedObjects.value['facility'] = letter.information?.facility || ''
+ selectedObjects.value['diagnose'] = letter.information?.diagnose || ''
+ selectedObjects.value['serviceType'] = letter.information?.serviceType || ''
+ selectedObjects.value['classLevel'] = letter.information?.classLevel || ''
+ selectedObjects.value['poly'] = letter.information?.poly || ''
+ selectedObjects.value['destination'] = letter.information?.destination || ''
+ if (!!selectedObjects.value['diagnose']) {
+ const diagnoseRes: any = await getDiagnoseLabelList({ diagnosa: selectedObjects.value['diagnose'] })
+ diagnoses.value = diagnoseRes
+ if (diagnoseRes && diagnoseRes.length > 0) {
+ selectedObjects.value['diagnoseLabel'] = diagnoseRes[0].value
+ }
+ }
+ }, 250)
+ }
+ })
+ return
+ }
+ if (menu === 'open-letter') {
+ openLetter.value = true
+ return
+ }
+ if (menu === 'history-sep') {
+ getMonitoringHistoryMappers().then(() => {
+ openHistory.value = true
+ })
+ return
+ }
+ if (menu === 'sep-number-changed') {
+ // Update sepNumber when it changes in form (only if different to prevent loop)
+ }
+ if (menu === 'back') {
+ navigateTo('/integration/bpjs-vclaim/sep')
+ }
+ if (menu === 'save-sep') {
+ isSaveLoading.value = true
+
+ // Map letter data to form if admissionType !== '3' and letters.value has data
+ let mappedValues = value
+ if (selectedAdmissionType.value !== '3') {
+ if (letters.value.length > 0) {
+ // Map data from letters.value to form values
+ mappedValues = mapLetterDataToForm(value)
+ } else {
+ // Fallback: use getPatientExternalMappers if letters.value is empty
+ // Get card number from form values or selectedObjects
+ const cardNumberToSearch = value.cardNumber || selectedObjects.value['cardNumber'] || ''
+ if (cardNumberToSearch && cardNumberToSearch !== '-') {
+ await getPatientExternalMappers(cardNumberToSearch, 'by-card')
+ // Update mappedValues with data from getPatientExternalMappers
+ if (selectedObjects.value['cardNumber']) {
+ mappedValues.cardNumber = selectedObjects.value['cardNumber']
+ }
+ if (selectedObjects.value['medicalRecordNumber']) {
+ mappedValues.medicalRecordNumber = selectedObjects.value['medicalRecordNumber']
+ }
+ if (selectedObjects.value['phoneNumber']) {
+ mappedValues.phoneNumber = selectedObjects.value['phoneNumber']
+ }
+ if (selectedObjects.value['classLevel']) {
+ mappedValues.classLevel = selectedObjects.value['classLevel']
+ }
+ }
+ }
+ }
+
+ if (!value.destinationClinic) {
+ mappedValues.destinationClinic = selectedObjects.value['destination'] || ''
+ }
+ if (!value.clinicExcecutive) {
+ mappedValues.clinicExcecutive = 'no'
+ }
+
+ mappedValues.userName = userStore.user?.user_name || ''
+
+ createSep(makeSepData(mappedValues))
+ .then((res) => {
+ const body = res?.body
+ const code = body?.metaData?.code
+ const message = body?.metaData?.message
+ if (code && code !== '200') {
+ toast({ title: 'Gagal', description: message || 'Gagal membuat SEP', variant: 'destructive' })
+ return
+ }
+ toast({ title: 'Berhasil', description: 'SEP berhasil dibuat', variant: 'default' })
+ if (!!resourcePath.value) {
+ navigateTo({ path: resourcePath.value, query: { 'sep-number': body?.response?.sep?.noSep || '-' } })
+ return
+ }
+ navigateTo('/integration/bpjs-vclaim/sep')
+ })
+ .catch((err) => {
+ console.error('Failed to save SEP:', err)
+ toast({ title: 'Gagal', description: err?.message || 'Gagal membuat SEP', variant: 'destructive' })
+ })
+ .finally(() => {
+ isSaveLoading.value = false
+ })
+ }
+ }
+
+ async function handleFetch(params: any) {
+ const menu = params.menu || ''
+ const value = params.value || ''
+ if (menu === 'diagnosis') {
+ diagnoses.value = await getDiagnoseLabelList({ diagnosa: value })
+ }
+ if (menu === 'clinic-from') {
+ facilitiesFrom.value = await getHealthFacilityLabelList({
+ healthcare: value,
+ healthcareType: selectedServiceType.value || 2,
+ })
+ }
+ if (menu === 'clinic-to') {
+ facilitiesTo.value = await getHealthFacilityLabelList({
+ healthcare: value,
+ healthcareType: selectedServiceType.value || 2,
+ })
+ }
+ if (menu === 'province') {
+ citiesList.value = await getCityList({ province: value })
+ districtsList.value = []
+ }
+ if (menu === 'city') {
+ districtsList.value = await getDistrictList({ city: value })
+ }
+ }
+
+ async function handleFetchSpecialists() {
+ try {
+ const specialistsResult = await getSpecialistList({ 'page-size': 100, includes: 'subspecialists' })
+ if (specialistsResult.success) {
+ const specialists = specialistsResult.body?.data || []
+ specialistsTree.value = getSpecialistTreeItems(specialists)
+ }
+ } catch (error) {
+ console.error('Error fetching specialist-subspecialist tree:', error)
+ }
+ }
+
+ async function handleInit() {
+ selectedServiceType.value = '2'
+ const facilities = await getHealthFacilityLabelList({
+ healthcare: 'Puskesmas',
+ healthcareType: selectedLetter.value || 1,
+ })
+ diagnoses.value = await getDiagnoseLabelList({ diagnosa: 'paru' })
+ facilitiesFrom.value = facilities
+ facilitiesTo.value = facilities
+ doctors.value = await getDoctorLabelList({
+ serviceType: selectedServiceType.value || '2',
+ serviceDate: new Date().toISOString().substring(0, 10),
+ specialistCode: 0,
+ })
+ provincesList.value = await getProvinceList()
+ serviceTypesList.value = Object.keys(serviceTypes).map((item) => ({
+ value: item.toString(),
+ label: serviceTypes[item],
+ })) as any
+ registerMethodsList.value = Object.keys(registerMethods)
+ .filter((item) => ![''].includes(item))
+ .map((item) => ({
+ value: item.toString(),
+ label: registerMethods[item],
+ })) as any
+ accidentsList.value = Object.keys(trafficAccidents).map((item) => ({
+ value: item.toString(),
+ label: trafficAccidents[item],
+ })) as any
+ purposeOfVisitsList.value = Object.keys(purposeOfVisits).map((item) => ({
+ value: item.toString(),
+ label: purposeOfVisits[item],
+ })) as any
+ proceduresList.value = Object.keys(procedureTypes).map((item) => ({
+ value: item.toString(),
+ label: procedureTypes[item],
+ })) as any
+ assessmentsList.value = Object.keys(serviceAssessments).map((item) => ({
+ value: item.toString(),
+ label: `${item.toString()} - ${serviceAssessments[item]}`,
+ })) as any
+ supportCodesList.value = Object.keys(supportCodes).map((item) => ({
+ value: item.toString(),
+ label: `${item.toString()} - ${supportCodes[item]}`,
+ })) as any
+ classLevelsList.value = Object.keys(classLevels).map((item) => ({
+ value: item.toString(),
+ label: classLevels[item],
+ })) as any
+ classLevelUpgradesList.value = Object.keys(classLevelUpgrades).map((item) => ({
+ value: item.toString(),
+ label: classLevelUpgrades[item],
+ })) as any
+ classPaySourcesList.value = Object.keys(classPaySources).map((item) => ({
+ value: item.toString(),
+ label: classPaySources[item],
+ })) as any
+ await handleFetchSpecialists()
+ if (route.query) {
+ const queries = route.query as any
+ isServiceHidden.value = queries['is-service'] === 'true'
+ selectedObjects.value = {}
+ if (queries['resource']) resourceType.value = queries['resource']
+ if (queries['source-path']) resourcePath.value = queries['source-path']
+ if (queries['doctor-code']) selectedObjects.value['doctorCode'] = queries['doctor-code']
+ if (queries['specialist-code']) selectedObjects.value['subSpecialistCode'] = queries['specialist-code']
+ if (queries['sub-specialist-code']) selectedObjects.value['subSpecialistCode'] = queries['sub-specialist-code']
+ if (queries['card-number']) selectedObjects.value['cardNumber'] = queries['card-number']
+ if (queries['register-date']) selectedObjects.value['registerDate'] = queries['register-date']
+ if (queries['sep-type']) selectedObjects.value['sepType'] = queries['sep-type']
+ if (queries['sep-number']) selectedObjects.value['sepNumber'] = queries['sep-number']
+ if (queries['register-date']) selectedObjects.value['registerDate'] = queries['register-date']
+ if (queries['payment-type']) selectedObjects.value['paymentType'] = queries['payment-type']
+ if (queries['patient-id']) {
+ await getPatientInternalMappers(queries['patient-id'])
+ }
+ if (queries['card-number']) {
+ const resultMember = await getMemberList({
+ mode: 'by-card',
+ number: queries['card-number'],
+ date: new Date().toISOString().substring(0, 10),
+ })
+ console.log(resultMember)
+ }
+ delete selectedObjects.value['is-service']
+ }
+ }
+
+ return {
+ openPatient,
+ openLetter,
+ openHistory,
+ selectedLetter,
+ selectedObjects,
+ selectedServiceType,
+ selectedAdmissionType,
+ histories,
+ letters,
+ doctors,
+ diagnoses,
+ facilitiesFrom,
+ facilitiesTo,
+ supportCodesList,
+ serviceTypesList,
+ registerMethodsList,
+ accidentsList,
+ purposeOfVisitsList,
+ proceduresList,
+ assessmentsList,
+ provincesList,
+ citiesList,
+ districtsList,
+ classLevelsList,
+ classLevelUpgradesList,
+ classPaySourcesList,
+ isServiceHidden,
+ isSaveLoading,
+ isLetterReadonly,
+ isLoadingPatient,
+ specialistsTree,
+ resourceType,
+ resourcePath,
+ patients,
+ selectedPatient,
+ paginationMeta,
+ getMonitoringHistoryMappers,
+ getLetterMappers,
+ getPatientInternalMappers,
+ getPatientExternalMappers,
+ getPatientsList,
+ getPatientByIdentifierSearch,
+ handleSaveLetter,
+ mapLetterDataToForm,
+ handleSavePatient,
+ handleEvent,
+ handleFetch,
+ handleFetchSpecialists,
+ handleInit,
+ }
+}
+
+export default useIntegrationSepEntry
diff --git a/app/handlers/integration-sep-list.handler.ts b/app/handlers/integration-sep-list.handler.ts
new file mode 100644
index 00000000..94cdccce
--- /dev/null
+++ b/app/handlers/integration-sep-list.handler.ts
@@ -0,0 +1,284 @@
+import { ref, reactive } from 'vue'
+// Components
+import { toast } from '~/components/pub/ui/toast'
+// Types
+import type { Ref as VueRef } from 'vue'
+import type { DateRange } from 'radix-vue'
+import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
+import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
+import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
+import type { VclaimSepData } from '~/models/vclaim'
+// Libraries
+import { CalendarDate, getLocalTimeZone } from '@internationalized/date'
+import { getFormatDateId } from '~/lib/date'
+import { downloadCsv, downloadXls } from '~/lib/download'
+import { serviceTypes } from '~/lib/constants.vclaim'
+import { getList as geMonitoringVisitList } from '~/services/vclaim-monitoring-visit.service'
+import { remove as removeSepData, makeSepDataForRemove } from '~/services/vclaim-sep.service'
+
+const headerKeys = [
+ 'letterDate',
+ 'letterNumber',
+ 'serviceType',
+ 'flow',
+ 'medicalRecordNumber',
+ 'patientName',
+ 'cardNumber',
+ 'controlLetterNumber',
+ 'controlLetterDate',
+ 'clinicDestination',
+ 'attendingDoctor',
+ 'diagnosis',
+ 'careClass',
+]
+
+const headerLabels = [
+ 'Tanggal SEP',
+ 'No. SEP',
+ 'Jenis Pelayanan',
+ 'Alur',
+ 'No. Rekam Medis',
+ 'Nama Pasien',
+ 'No. Kartu BPJS',
+ 'No. Surat Kontrol',
+ 'Tgl Surat Kontrol',
+ 'Poli Tujuan',
+ 'Dokter Penanggung Jawab',
+ 'Diagnosa',
+ 'Kelas Perawatan',
+]
+
+export function useIntegrationSepList() {
+ const userStore = useUserStore()
+ const today = new Date()
+ const initCalDate = (d: Date) => new CalendarDate(d.getFullYear(), d.getMonth() + 1, d.getDate())
+
+ const recId = ref(0)
+ const recAction = ref('')
+ const recItem = ref(null)
+
+ const data = ref([])
+ const dateSelection = ref({ start: initCalDate(today), end: initCalDate(today) }) as VueRef
+ const dateRange = ref(`${getFormatDateId(today)} - ${getFormatDateId(today)}`)
+ const serviceType = ref('2')
+ const serviceTypesList = ref([])
+ const search = ref('')
+ const open = ref(false)
+
+ const sepData = ref({
+ sepNumber: '',
+ cardNumber: '',
+ patientName: '',
+ })
+
+ const refSearchNav: RefSearchNav = {
+ onClick: () => {},
+ onInput: (_val: string) => {},
+ onClear: () => {},
+ }
+
+ const headerPrep: HeaderPrep = {
+ title: 'Daftar SEP Prosedur',
+ icon: 'i-lucide-panel-bottom',
+ addNav: {
+ label: 'Tambah',
+ onClick: () => {
+ navigateTo('/integration/bpjs-vclaim/sep/add')
+ },
+ },
+ }
+
+ const paginationMeta = reactive({
+ recordCount: 0,
+ page: 1,
+ pageSize: 10,
+ totalPage: 5,
+ hasNext: false,
+ hasPrev: false,
+ })
+
+ const isLoading = reactive({
+ isTableLoading: false,
+ })
+
+ const getDateFilter = () => {
+ let dateFilter = ''
+ const isTimeLocal = true
+ const dateFirst =
+ dateSelection.value && dateSelection.value.start
+ ? dateSelection.value.start.toDate(getLocalTimeZone())
+ : new Date()
+ if (isTimeLocal && dateSelection.value && dateSelection.value.end) {
+ const { year, month, day } = dateSelection.value.end
+ dateFilter = `${year}-${month}-${day}`
+ } else {
+ dateFilter = dateFirst.toISOString().substring(0, 10)
+ }
+ return dateFilter
+ }
+
+ const getMonitoringVisitMappers = async () => {
+ isLoading.dataListLoading = true
+ data.value = []
+ const dateFilter = getDateFilter()
+ const result = await geMonitoringVisitList({
+ date: dateFilter || '',
+ serviceType: serviceType.value,
+ })
+
+ if (result && result.success && result.body) {
+ const visitsRaw = result.body?.response?.sep || []
+ if (!visitsRaw) {
+ isLoading.dataListLoading = false
+ return
+ }
+ visitsRaw.forEach((result: any) => {
+ let st = result.jnsPelayanan || '-'
+ if (st === 'R.Inap') st = 'Rawat Inap'
+ else if (st === '1' || st === 'R.Jalan') st = 'Rawat Jalan'
+
+ data.value.push({
+ letterDate: result.tglSep || '-',
+ letterNumber: result.noSep || '-',
+ serviceType: st,
+ flow: '-',
+ medicalRecordNumber: '-',
+ patientName: result.nama || '-',
+ cardNumber: result.noKartu || '-',
+ controlLetterNumber: result.noRujukan || '-',
+ controlLetterDate: result.tglPlgSep || '-',
+ clinicDestination: result.poli || '-',
+ attendingDoctor: '-',
+ diagnosis: result.diagnosa || '-',
+ careClass: result.kelasRawat || '-',
+ })
+ })
+ }
+
+ isLoading.dataListLoading = false
+ }
+
+ const getSepList = async () => {
+ await getMonitoringVisitMappers()
+ }
+
+ const setServiceTypes = () => {
+ serviceTypesList.value = Object.keys(serviceTypes).map((item) => ({
+ value: item.toString(),
+ label: serviceTypes[item],
+ })) as any
+ }
+
+ const setDateRange = () => {
+ const startCal = dateSelection.value.start
+ const endCal = dateSelection.value.end
+ const s = startCal ? startCal.toDate(getLocalTimeZone()) : today
+ const e = endCal ? endCal.toDate(getLocalTimeZone()) : today
+ dateRange.value = `${getFormatDateId(s)} - ${getFormatDateId(e)}`
+ }
+
+ const handleExportCsv = () => {
+ if (!data.value || data.value.length === 0) {
+ toast({ title: 'Kosong', description: 'Tidak ada data untuk diekspor', variant: 'destructive' })
+ return
+ }
+
+ const yyyy = today.getFullYear()
+ const mm = String(today.getMonth() + 1).padStart(2, '0')
+ const dd = String(today.getDate()).padStart(2, '0')
+ const dateStr = `${yyyy}-${mm}-${dd}`
+ const filename = `file-sep-${dateStr}.csv`
+ downloadCsv(headerKeys, headerLabels, data.value, filename, ',', true)
+ }
+
+ const handleExportExcel = async () => {
+ if (!data.value || data.value.length === 0) {
+ toast({ title: 'Kosong', description: 'Tidak ada data untuk diekspor', variant: 'destructive' })
+ return
+ }
+
+ const yyyy = today.getFullYear()
+ const mm = String(today.getMonth() + 1).padStart(2, '0')
+ const dd = String(today.getDate()).padStart(2, '0')
+ const dateStr = `${yyyy}-${mm}-${dd}`
+ const filename = `file-sep-${dateStr}.xlsx`
+ try {
+ await downloadXls(headerKeys, headerLabels, data.value, filename, 'SEP Data')
+ } catch (err: any) {
+ console.error('exportExcel error', err)
+ toast({ title: 'Gagal', description: err?.message || 'Gagal mengekspor data ke Excel', variant: 'destructive' })
+ }
+ }
+
+ const handleRowSelected = (row: any) => {
+ if (!row) return
+ sepData.value.sepNumber = row.letterNumber || ''
+ sepData.value.cardNumber = row.cardNumber || ''
+ sepData.value.patientName = row.patientName || ''
+ recItem.value = row
+ recId.value = (row && (row.id || row.recId)) || 0
+ }
+
+ const handlePageChange = (page: number) => {
+ console.log('pageChange', page)
+ }
+
+ const handleRemove = async () => {
+ try {
+ const result = await removeSepData(
+ makeSepDataForRemove({ ...sepData.value, userName: userStore.user?.user_name }),
+ )
+ const backendMessage = result?.body?.message || result?.message || null
+ const backendStatus = result?.body?.status || result?.status || null
+
+ if (
+ backendMessage === 'success' ||
+ (backendStatus === 'error' && backendMessage === 'Decrypt failed: illegal base64 data at input byte 16')
+ ) {
+ await getSepList()
+ toast({ title: 'Berhasil', description: backendMessage || 'Data berhasil dihapus', variant: 'default' })
+ } else {
+ toast({ title: 'Gagal', description: backendMessage || 'Gagal menghapus data', variant: 'destructive' })
+ }
+ } catch (err: any) {
+ console.error('handleRemove error', err)
+ toast({
+ title: 'Gagal',
+ description: err?.message || 'Terjadi kesalahan saat menghapus data',
+ variant: 'destructive',
+ })
+ } finally {
+ recId.value = 0
+ recAction.value = ''
+ open.value = false
+ }
+ }
+
+ return {
+ recId,
+ recAction,
+ recItem,
+ data,
+ dateSelection,
+ dateRange,
+ serviceType,
+ serviceTypesList,
+ search,
+ open,
+ sepData,
+ headerPrep,
+ refSearchNav,
+ paginationMeta,
+ isLoading,
+ getSepList,
+ setServiceTypes,
+ setDateRange,
+ handleExportCsv,
+ handleExportExcel,
+ handleRowSelected,
+ handlePageChange,
+ handleRemove,
+ }
+}
+
+export default useIntegrationSepList
diff --git a/app/handlers/medicine-form.handler.ts b/app/handlers/medicine-form.handler.ts
new file mode 100644
index 00000000..6fcadb4c
--- /dev/null
+++ b/app/handlers/medicine-form.handler.ts
@@ -0,0 +1,21 @@
+import { createCrudHandler } from '~/handlers/_handler'
+import { create, update, remove } from '~/services/medicine-form.service'
+
+export const {
+ recId,
+ recAction,
+ recItem,
+ isReadonly,
+ isProcessing,
+ isFormEntryDialogOpen,
+ isRecordConfirmationOpen,
+ onResetState,
+ handleActionSave,
+ handleActionEdit,
+ handleActionRemove,
+ handleCancelForm,
+} = createCrudHandler({
+ post: create,
+ patch: update,
+ remove: remove,
+})
diff --git a/app/handlers/prescription.handler.ts b/app/handlers/prescription.handler.ts
index 62e1861e..10bea176 100644
--- a/app/handlers/prescription.handler.ts
+++ b/app/handlers/prescription.handler.ts
@@ -1,4 +1,4 @@
-import { createCrudHandler, genCrudHandler } from '~/handlers/_handler'
+import { genCrudHandler } from '~/handlers/_handler'
import { create, update, remove } from '~/services/prescription.service'
export const {
diff --git a/app/handlers/supporting-document.handler.ts b/app/handlers/supporting-document.handler.ts
new file mode 100644
index 00000000..70b29612
--- /dev/null
+++ b/app/handlers/supporting-document.handler.ts
@@ -0,0 +1,24 @@
+// Handlers
+import { genCrudHandler } from '~/handlers/_handler'
+
+// Services
+import { create, update, remove } from '~/services/supporting-document.service'
+
+export const {
+ recId,
+ recAction,
+ recItem,
+ isReadonly,
+ isProcessing,
+ isFormEntryDialogOpen,
+ isRecordConfirmationOpen,
+ onResetState,
+ handleActionSave,
+ handleActionEdit,
+ handleActionRemove,
+ handleCancelForm,
+} = genCrudHandler({
+ create,
+ update,
+ remove,
+})
diff --git a/app/layouts/default.vue b/app/layouts/default.vue
index dd52a9c8..a79ecb98 100644
--- a/app/layouts/default.vue
+++ b/app/layouts/default.vue
@@ -2,7 +2,12 @@
import CardContent from '~/components/pub/ui/card/CardContent.vue'
const route = useRoute()
+
+
const contentFrame = computed(() => route.meta.contentFrame)
+const contentPadding = computed(() => route.meta.contentPadding || 'p-4 2xl:p-5')
+const contentUseCard = computed(() => route.meta.contentUseCard === false ? false : true)
+console.log(route.meta.contentUseCard,contentUseCard)
const contentFrameClass = computed(() => {
switch (contentFrame.value) {
case 'cf-container-2xl':
@@ -28,13 +33,14 @@ const contentFrameClass = computed(() => {
-
+
diff --git a/app/lib/constants.ts b/app/lib/constants.ts
index 3a52b22e..9cde7f73 100644
--- a/app/lib/constants.ts
+++ b/app/lib/constants.ts
@@ -8,7 +8,7 @@ export const dataStatusCodes: Record = {
review: 'Review',
process: 'Proses',
done: 'Selesai',
- canceled: 'Dibatalkan',
+ cancel: 'Dibatalkan',
rejected: 'Ditolak',
skiped: 'Dilewati',
}
@@ -383,3 +383,45 @@ export const medicalActionTypeCode: Record = {
} as const
export type medicalActionTypeCodeKey = keyof typeof medicalActionTypeCode
+
+export const encounterDocTypeCode: Record = {
+ "person-resident-number": 'person-resident-number',
+ "person-driving-license": 'person-driving-license',
+ "person-passport": 'person-passport',
+ "person-family-card": 'person-family-card',
+ "mcu-item-result": 'mcu-item-result',
+ "vclaim-sep": 'vclaim-sep',
+ "vclaim-sipp": 'vclaim-sipp',
+} as const
+export type encounterDocTypeCodeKey = keyof typeof encounterDocTypeCode
+export const encounterDocOpt: { label: string; value: encounterDocTypeCodeKey }[] = [
+ { label: 'KTP', value: 'person-resident-number' },
+ { label: 'SIM', value: 'person-driving-license' },
+ { label: 'Passport', value: 'person-passport' },
+ { label: 'Kartu Keluarga', value: 'person-family-card' },
+ { label: 'Hasil MCU', value: 'mcu-item-result' },
+ { label: 'Klaim SEP', value: 'vclaim-sep' },
+ { label: 'Klaim SIPP', value: 'vclaim-sipp' },
+]
+
+
+export const docTypeCode = {
+ "encounter-patient": 'encounter-patient',
+ "encounter-support": 'encounter-support',
+ "encounter-other": 'encounter-other',
+ "vclaim-sep": 'vclaim-sep',
+ "vclaim-sipp": 'vclaim-sipp',
+} as const
+export const docTypeLabel = {
+ "encounter-patient": 'Data Pasien',
+ "encounter-support": 'Data Penunjang',
+ "encounter-other": 'Lain - Lain',
+ "vclaim-sep": 'SEP',
+ "vclaim-sipp": 'SIPP',
+} as const
+export type docTypeCodeKey = keyof typeof docTypeCode
+export const supportingDocOpt = [
+ { label: 'Data Pasien', value: 'encounter-patient' },
+ { label: 'Data Penunjang', value: 'encounter-support' },
+ { label: 'Lain - Lain', value: 'encounter-other' },
+]
diff --git a/app/lib/date.ts b/app/lib/date.ts
index 2c7b92cf..982c3c5b 100644
--- a/app/lib/date.ts
+++ b/app/lib/date.ts
@@ -1,46 +1,69 @@
+const monthsInId = [
+ 'Januari',
+ 'Februari',
+ 'Maret',
+ 'April',
+ 'Mei',
+ 'Juni',
+ 'Juli',
+ 'Agustus',
+ 'September',
+ 'Oktober',
+ 'November',
+ 'Desember',
+]
+
export function getAge(dateString: string, comparedDate?: string): { idFormat: string; extFormat: string } {
- const birthDate = new Date(dateString);
- const today = new Date();
+ const birthDate = new Date(dateString)
+ const today = new Date()
- if (comparedDate) {
- const comparedDateObj = new Date(comparedDate);
- today.setFullYear(comparedDateObj.getFullYear());
- today.setMonth(comparedDateObj.getMonth());
- today.setDate(comparedDateObj.getDate());
- }
+ if (comparedDate) {
+ const comparedDateObj = new Date(comparedDate)
+ today.setFullYear(comparedDateObj.getFullYear())
+ today.setMonth(comparedDateObj.getMonth())
+ today.setDate(comparedDateObj.getDate())
+ }
- // Format the date part
- const options: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'long', year: 'numeric' };
- const idFormat = birthDate.toLocaleDateString('id-ID', options);
+ // Format the date part
+ const options: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'long', year: 'numeric' }
+ const idFormat = birthDate.toLocaleDateString('id-ID', options)
- // Calculate age
- let years = today.getFullYear() - birthDate.getFullYear();
- let months = today.getMonth() - birthDate.getMonth();
- let days = today.getDate() - birthDate.getDate();
+ // Calculate age
+ let years = today.getFullYear() - birthDate.getFullYear()
+ let months = today.getMonth() - birthDate.getMonth()
+ let days = today.getDate() - birthDate.getDate()
- if (months < 0 || (months === 0 && days < 0)) {
- years--;
- months += 12;
- }
+ if (months < 0 || (months === 0 && days < 0)) {
+ years--
+ months += 12
+ }
- if (days < 0) {
- const prevMonth = new Date(today.getFullYear(), today.getMonth() - 1, 0);
- days += prevMonth.getDate();
- months--;
- }
+ if (days < 0) {
+ const prevMonth = new Date(today.getFullYear(), today.getMonth() - 1, 0)
+ days += prevMonth.getDate()
+ months--
+ }
- // Format the age part
- let extFormat = '';
- if ([years, months, days].filter(Boolean).join(' ')) {
- extFormat = `${years} Tahun ${months} Bulan ${days} Hari`;
- } else {
- extFormat = '0';
- }
+ // Format the age part
+ let extFormat = ''
+ if ([years, months, days].filter(Boolean).join(' ')) {
+ extFormat = `${years} Tahun ${months} Bulan ${days} Hari`
+ } else {
+ extFormat = '0'
+ }
- return {
- idFormat,
- extFormat
- };
+ return {
+ idFormat,
+ extFormat,
+ }
+}
+
+// Date selection: default to today - today
+export function getFormatDateId(date: Date) {
+ const dd = String(date.getDate()).padStart(2, '0')
+ const mm = monthsInId[date.getMonth()]
+ const yyyy = date.getFullYear()
+ return `${dd} ${mm} ${yyyy}`
}
export function formatDateYyyyMmDd(isoDateString: string): string {
@@ -49,4 +72,17 @@ export function formatDateYyyyMmDd(isoDateString: string): string {
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
-}
\ No newline at end of file
+}
+
+// Function to check if date is invalid (like "0001-01-01T00:00:00Z")
+export function isValidDate(dateString: string | null | undefined): boolean {
+ if (!dateString) return false
+ // Check for invalid date patterns
+ if (dateString.startsWith('0001-01-01')) return false
+ try {
+ const date = new Date(dateString)
+ return !isNaN(date.getTime())
+ } catch {
+ return false
+ }
+}
diff --git a/app/lib/download.ts b/app/lib/download.ts
new file mode 100644
index 00000000..eb996432
--- /dev/null
+++ b/app/lib/download.ts
@@ -0,0 +1,154 @@
+/**
+ * Download data as CSV file.
+ *
+ * @param headers - Array of header names. If omitted and data is array of objects, keys will be taken from first object.
+ * @param data - Array of rows. Each row can be either an object (key -> value) or an array of values.
+ * @param filename - optional file name to use for downloaded file
+ * @param delimiter - csv delimiter (default is comma)
+ * @param addBOM - add UTF-8 BOM to the file to make Excel detect UTF-8 correctly
+ * Usage examples:
+ * 1) With headers and array of objects
+ * downloadCsv(['name', 'age'], [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.csv');
+ * 2) Without headers (automatically uses object keys)
+ * downloadCsv(null, [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.csv');
+ * 3) With array-of-arrays
+ * downloadCsv(['col1', 'col2'], [['a', 'b'], ['c', 'd']], 'matrix.csv');
+ */
+export function downloadCsv(
+ headers: string[] | null,
+ headerLabels: string[],
+ data: Array | any[]>,
+ filename = 'data.csv',
+ delimiter = ',',
+ addBOM = true,
+) {
+ if (!Array.isArray(data) || data.length === 0) {
+ // still create an empty CSV containing only headers
+ const csvHeader = headers ? headers.join(delimiter) : ''
+ const csvString = addBOM ? '\uFEFF' + csvHeader : csvHeader
+ const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' })
+ const link = document.createElement('a')
+ link.href = URL.createObjectURL(blob)
+ link.setAttribute('download', filename)
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ return
+ }
+
+ // if headers not provided and rows are objects, take keys from first object
+ let _headers: string[] | null = headers
+ if (!_headers) {
+ const firstRow = data[0]
+ if (typeof firstRow === 'object' && !Array.isArray(firstRow)) {
+ _headers = Object.keys(firstRow)
+ } else if (Array.isArray(firstRow)) {
+ // if rows are arrays and no headers provided, we won't add header row
+ _headers = null
+ }
+ }
+
+ const escape = (val: unknown) => {
+ if (val === null || typeof val === 'undefined') return ''
+ const str = String(val)
+ const needsQuoting = str.includes(delimiter) || str.includes('\n') || str.includes('\r') || str.includes('"')
+ if (!needsQuoting) return str
+ return '"' + str.replace(/"/g, '""') + '"'
+ }
+
+ const rows: string[] = data.map((row) => {
+ if (Array.isArray(row)) {
+ return row.map(escape).join(delimiter)
+ }
+ // object row - map using headers if available, otherwise use object values
+ if (_headers && Array.isArray(_headers)) {
+ return _headers.map((h) => escape((row as Record)[h])).join(delimiter)
+ }
+ return Object.values(row).map(escape).join(delimiter)
+ })
+
+ const headerRow = headerLabels ? headerLabels.join(delimiter) : _headers ? _headers.join(delimiter) : null
+ const csvString = (addBOM ? '\uFEFF' : '') + [headerRow, ...rows].filter(Boolean).join('\r\n')
+
+ const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' })
+ const link = document.createElement('a')
+ link.href = URL.createObjectURL(blob)
+ link.setAttribute('download', filename)
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+}
+
+/**
+ * Download data as XLS (Excel) file using xlsx library.
+ *
+ * @param headers - Array of header names. If omitted and data is array of objects, keys will be taken from first object.
+ * @param data - Array of rows. Each row can be either an object (key -> value) or an array of values.
+ * @param filename - optional file name to use for downloaded file (default: 'data.xlsx')
+ * @param sheetName - optional sheet name in workbook (default: 'Sheet1')
+ * Usage examples:
+ * 1) With headers and array of objects
+ * await downloadXls(['name', 'age'], [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.xlsx');
+ * 2) Without headers (automatically uses object keys)
+ * await downloadXls(null, [{name: 'Alice', age: 25}, {name: 'Bob', age: 30}], 'people.xlsx');
+ * 3) With custom sheet name
+ * await downloadXls(['col1', 'col2'], [['a', 'b'], ['c', 'd']], 'matrix.xlsx', 'MyData');
+ */
+export async function downloadXls(
+ headers: string[] | null,
+ headerLabels: string[],
+ data: Array | any[]>,
+ filename = 'data.xlsx',
+ sheetName = 'Sheet1',
+) {
+ // Dynamically import xlsx to avoid server-side issues
+ const { utils, write } = await import('xlsx')
+ const { saveAs } = await import('file-saver')
+
+ if (!Array.isArray(data) || data.length === 0) {
+ // Create empty sheet with headers only
+ const ws = utils.aoa_to_sheet(headers ? [headers] : [[]])
+ const wb = utils.book_new()
+ utils.book_append_sheet(wb, ws, sheetName)
+ const wbout = write(wb, { bookType: 'xlsx', type: 'array' })
+ saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename)
+ return
+ }
+
+ // if headers not provided and rows are objects, take keys from first object
+ let _headers: string[] | null = headers
+ if (!_headers) {
+ const firstRow = data[0]
+ if (typeof firstRow === 'object' && !Array.isArray(firstRow)) {
+ _headers = Object.keys(firstRow)
+ } else if (Array.isArray(firstRow)) {
+ _headers = null
+ }
+ }
+
+ // Convert data rows to 2D array
+ const rows: any[][] = data.map((row) => {
+ if (Array.isArray(row)) {
+ return row
+ }
+ // object row - map using headers if available, otherwise use object values
+ if (_headers && Array.isArray(_headers)) {
+ return _headers.map((h) => (row as Record)[h] ?? '')
+ }
+ return Object.values(row)
+ })
+
+ // Combine headers/labels and rows for sheet
+ // If caller provided headerLabels (as display labels), prefer them.
+ const sheetHeader = headerLabels ? headerLabels : _headers ? _headers : null
+ const sheetData = sheetHeader ? [sheetHeader, ...rows] : rows
+
+ // Create worksheet and workbook
+ const ws = utils.aoa_to_sheet(sheetData)
+ const wb = utils.book_new()
+ utils.book_append_sheet(wb, ws, sheetName)
+
+ // Write and save file
+ const wbout = write(wb, { bookType: 'xlsx', type: 'array' })
+ saveAs(new Blob([wbout], { type: 'application/octet-stream' }), filename)
+}
diff --git a/app/lib/page-permission.ts b/app/lib/page-permission.ts
index ab7b5550..807131a8 100644
--- a/app/lib/page-permission.ts
+++ b/app/lib/page-permission.ts
@@ -1,7 +1,7 @@
import type { RoleAccess } from '~/models/role'
export const PAGE_PERMISSIONS = {
- '/patient': {
+ '/client/patient': {
'emp|doc': ['R'],
'emp|nur': ['R'],
'emp|reg': ['C', 'R', 'U', 'D'],
@@ -9,13 +9,11 @@ export const PAGE_PERMISSIONS = {
'emp|pay': ['R'],
'emp|mng': ['R'],
},
- '/doctor': {
- 'emp|doc': ['C', 'R', 'U', 'D'],
- 'emp|nur': ['R'],
- 'emp|reg': ['R'],
- 'emp|pha': ['R'],
- 'emp|pay': ['R'],
- 'emp|mng': ['R'],
+ '/human-src/employee': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
+ },
+ '/human-src/intern': {
+ 'div|hrd': ['C', 'R', 'U', 'D'],
},
'/satusehat': {
'emp|doc': ['R'],
diff --git a/app/lib/roles.ts b/app/lib/roles.ts
new file mode 100644
index 00000000..8e6561e3
--- /dev/null
+++ b/app/lib/roles.ts
@@ -0,0 +1,16 @@
+import { medicalRoles, respPosCode } from '~/const/common/role'
+
+export function getServicePosition(role?: string): string {
+ if(!role) {
+ return 'none'
+ }
+ if (medicalRoles.includes(role)) {
+ return 'medical'
+ } else if (role === 'emp|reg') {
+ return 'registration'
+ } else if (role.includes('|resp')) {
+ return 'verificator'
+ } else {
+ return 'none'
+ }
+}
diff --git a/app/lib/utils.ts b/app/lib/utils.ts
index 357d8700..67f3d04b 100644
--- a/app/lib/utils.ts
+++ b/app/lib/utils.ts
@@ -1,6 +1,7 @@
import type { ClassValue } from 'clsx'
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
+import { toast } from '~/components/pub/ui/toast'
export interface SelectOptionType<_T = string> {
value: string
@@ -104,3 +105,59 @@ export function calculateAge(birthDate: Date | string | null | undefined): strin
return `${years} tahun ${months} bulan`
}
}
+
+
+/**
+ * Converts a plain JavaScript object (including File objects) into a FormData instance.
+ * @param {object} data - The object to convert (e.g., form values).
+ * @returns {FormData} The new FormData object suitable for API submission.
+ */
+export function toFormData(data: Record): FormData {
+ const formData = new FormData();
+
+ for (const key in data) {
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
+ const value = data[key];
+
+ // Handle File objects, Blobs, or standard JSON values
+ if (value !== null && value !== undefined) {
+ // Check if the value is a File/Blob instance
+ if (value instanceof File || value instanceof Blob) {
+ // Append the file directly
+ formData.append(key, value);
+ } else if (typeof value === 'object') {
+ // Handle nested objects/arrays by stringifying them (optional, depends on API)
+ // Note: Most APIs expect nested data to be handled separately or passed as JSON string
+ // For simplicity, we stringify non-File objects.
+ formData.append(key, JSON.stringify(value));
+ } else {
+ // Append standard string, number, or boolean values
+ formData.append(key, value);
+ }
+ }
+ }
+ }
+
+ return formData;
+}
+
+export function printFormData(formData: FormData) {
+ console.log("--- FormData Contents ---");
+ // Use the entries() iterator to loop through key/value pairs
+ for (const [key, value] of formData.entries()) {
+ if (value instanceof File) {
+ console.log(`Key: ${key}, Value: [File: ${value.name}, Type: ${value.type}, Size: ${value.size} bytes]`);
+ } else {
+ console.log(`Key: ${key}, Value: "${value}"`);
+ }
+ }
+ console.log("-------------------------");
+}
+
+export function unauthorizedToast() {
+ toast({
+ title: 'Unauthorized',
+ description: 'You are not authorized to perform this action.',
+ variant: 'destructive',
+ })
+}
diff --git a/app/middleware/rbac.ts b/app/middleware/rbac.ts
index 87e8ca00..fa0c0685 100644
--- a/app/middleware/rbac.ts
+++ b/app/middleware/rbac.ts
@@ -20,7 +20,7 @@ export default defineNuxtRouteMiddleware((to) => {
const requiredRoles = to.meta.roles as string[]
if (requiredRoles && requiredRoles.length > 0) {
// FIXME: change this dummy roles, when api is ready
- const userRoles = authStore.userRole
+ const userRoles = authStore.userRoles
// const userRoles = ['admisi']
const hasRequiredRole = requiredRoles.some((role) => userRoles.includes(role))
diff --git a/app/models/device-order-item.ts b/app/models/device-order-item.ts
index fa16638a..31b3ee47 100644
--- a/app/models/device-order-item.ts
+++ b/app/models/device-order-item.ts
@@ -1,16 +1,19 @@
import { type Base, genBase } from "./_base"
+import { genDevice, type Device } from "./device"
export interface DeviceOrderItem extends Base {
deviceOrder_id: number
- device_id: number
- count: number
+ device_code: string
+ device: Device
+ quantity: number
}
export function genDeviceOrderItem(): DeviceOrderItem {
return {
...genBase(),
deviceOrder_id: 0,
- device_id: 0,
- count: 0,
+ device_code: '',
+ device: genDevice(),
+ quantity: 0,
}
}
diff --git a/app/models/device-order.ts b/app/models/device-order.ts
index 884340c5..cbb682ad 100644
--- a/app/models/device-order.ts
+++ b/app/models/device-order.ts
@@ -1,15 +1,21 @@
import { type Base, genBase } from "./_base"
+import type { DeviceOrderItem } from "./device-order-item"
+import { genDoctor, type Doctor } from "./doctor"
export interface DeviceOrder extends Base {
encounter_id: number
- doctor_id: number
+ doctor_code: number
+ doctor: Doctor
status_code?: string
+ items: DeviceOrderItem[]
}
export function genDeviceOrder(): DeviceOrder {
return {
...genBase(),
encounter_id: 0,
- doctor_id: 0,
+ doctor_code: 0,
+ doctor: genDoctor(),
+ items: []
}
}
diff --git a/app/models/division-position.ts b/app/models/division-position.ts
index dc263ada..ebd7bfa9 100644
--- a/app/models/division-position.ts
+++ b/app/models/division-position.ts
@@ -4,7 +4,7 @@ export interface DivisionPosition extends Base {
code: string
name: string
headStatus?: boolean
- division_id: number
+ division_id: string
employee_id?: number
employee?: Employee | null
@@ -16,7 +16,7 @@ export function genDivisionPosition(): DivisionPosition {
code: '',
name: '',
headStatus: false,
- division_id: 0,
+ division_id: '',
employee_id: 0,
}
}
diff --git a/app/models/encounter-document.ts b/app/models/encounter-document.ts
new file mode 100644
index 00000000..5a98ccd5
--- /dev/null
+++ b/app/models/encounter-document.ts
@@ -0,0 +1,29 @@
+import { type Base, genBase } from "./_base"
+import { docTypeLabel, } from '~/lib/constants'
+import { genEmployee, type Employee } from "./employee"
+import { genEncounter, type Encounter } from "./encounter"
+
+export interface EncounterDocument extends Base {
+ encounter_id: number
+ encounter?: Encounter
+ upload_employee_id: number
+ employee?: Employee
+ type_code: string
+ name: string
+ filePath: string
+ fileName: string
+}
+
+export function genEncounterDocument(): EncounterDocument {
+ return {
+ ...genBase(),
+ encounter_id: 2,
+ encounter: genEncounter(),
+ upload_employee_id: 0,
+ employee: genEmployee(),
+ type_code: docTypeLabel["encounter-patient"],
+ name: 'example',
+ filePath: 'https://bing.com',
+ fileName: 'example',
+ }
+}
diff --git a/app/models/encounter.ts b/app/models/encounter.ts
index fb2c0b04..85a0012d 100644
--- a/app/models/encounter.ts
+++ b/app/models/encounter.ts
@@ -1,6 +1,7 @@
import type { DeathCause } from "./death-cause"
import { type Doctor, genDoctor } from "./doctor"
import { genEmployee, type Employee } from "./employee"
+import type { EncounterDocument } from "./encounter-document"
import type { InternalReference } from "./internal-reference"
import { type Patient, genPatient } from "./patient"
import type { Specialist } from "./specialist"
@@ -36,7 +37,9 @@ export interface Encounter {
discharge_date?: string
internalReferences?: InternalReference[]
deathCause?: DeathCause
+ paymentMethod_code?: string
status_code: string
+ encounterDocuments: EncounterDocument[]
}
export function genEncounter(): Encounter {
@@ -54,7 +57,8 @@ export function genEncounter(): Encounter {
appointment_doctor_id: 0,
appointment_doctor: genDoctor(),
medicalDischargeEducation: '',
- status_code: ''
+ status_code: '',
+ encounterDocuments: [],
}
}
diff --git a/app/models/general-consent.ts b/app/models/general-consent.ts
new file mode 100644
index 00000000..fe3ac97a
--- /dev/null
+++ b/app/models/general-consent.ts
@@ -0,0 +1,49 @@
+export interface GeneralConsent {
+ id: number
+ encounter_id: number
+ value: string
+}
+
+export interface ValueCreateDto {
+ relatives: string[]
+ responsibleName: string
+ responsiblePhone: string
+ informant: string
+ witness1: string
+ witness2: string
+}
+
+export interface CreateDto {
+ encounter_id: number
+ value: string
+}
+
+export interface UpdateDto {
+ id: number
+ problem: string
+ unit_id: number
+}
+
+export interface DeleteDto {
+ id: number
+}
+
+export function genCreateDto(): CreateDto {
+ return {
+ encounter_id: 0,
+ problem: '',
+ unit_id: 0,
+ }
+}
+
+export function genConsultation(): GeneralConsent {
+ return {
+ id: 0,
+ encounter_id: 0,
+ unit_id: 0,
+ doctor_id: 0,
+ problem: '',
+ solution: '',
+ repliedAt: '',
+ }
+}
diff --git a/app/models/medicine-form.ts b/app/models/medicine-form.ts
new file mode 100644
index 00000000..8ca21a2b
--- /dev/null
+++ b/app/models/medicine-form.ts
@@ -0,0 +1,38 @@
+import { type Base, genBase } from "./_base"
+
+export interface MedicineForm extends Base {
+ name: string
+ code: string
+}
+
+export interface CreateDto {
+ name: string
+ code: string
+}
+
+export interface GetListDto {
+ page: number
+ size: number
+ name?: string
+ code?: string
+}
+
+export interface GetDetailDto {
+ id?: string
+}
+
+export interface UpdateDto extends CreateDto {
+ id?: number
+}
+
+export interface DeleteDto {
+ id?: string
+}
+
+export function genMedicine(): MedicineForm {
+ return {
+ ...genBase(),
+ name: 'name',
+ code: 'code',
+ }
+}
diff --git a/app/models/medicine.ts b/app/models/medicine.ts
index d3a04752..5aa01da4 100644
--- a/app/models/medicine.ts
+++ b/app/models/medicine.ts
@@ -1,10 +1,17 @@
+import type { MedicineFormData } from "~/schemas/medicine.schema"
import { type Base, genBase } from "./_base"
+import type { MedicineGroup } from "./medicine-group"
+import type { MedicineMethod } from "./medicine-method"
export interface Medicine extends Base {
code: string
name: string
- medicineGroup_code: string
- medicineMethod_code: string
+ medicineGroup_code?: string
+ medicineGroup?: MedicineGroup
+ medicineMethod_code?: string
+ medicineMethod?: MedicineMethod
+ medicineForm_code?: string
+ medicineForm?: MedicineFormData
uom_code: string
infra_id?: string | null
stock: number
diff --git a/app/models/medicinemix-item.ts b/app/models/medicinemix-item.ts
index d2e1f973..9c055294 100644
--- a/app/models/medicinemix-item.ts
+++ b/app/models/medicinemix-item.ts
@@ -1,7 +1,7 @@
import { type Base, genBase } from "./_base"
import { type Medicine, genMedicine } from "./medicine";
-interface MedicinemixItem extends Base {
+export interface MedicinemixItem extends Base {
id: number
medicineMix_id: number
medicine_id: number
@@ -35,7 +35,7 @@ export interface DeleteDto {
id: number
}
-export function MedicinemixItem(): MedicinemixItem {
+export function genMedicinemixItem(): MedicinemixItem {
return {
...genBase(),
medicineMix_id: 0,
diff --git a/app/models/prescription-item.ts b/app/models/prescription-item.ts
index 6f0d3716..3349fcbf 100644
--- a/app/models/prescription-item.ts
+++ b/app/models/prescription-item.ts
@@ -5,10 +5,10 @@ export interface PrescriptionItem {
id: number;
prescription_id: number;
isMix: boolean;
- medicine_id: number;
- medicine: Medicine;
- medicineMix_id: number;
- medicineMix: Medicinemix
+ medicine_code?: string;
+ medicine?: Medicine;
+ medicineMix_id?: number;
+ medicineMix?: Medicinemix
frequency: number;
dose: number;
interval: number;
@@ -29,35 +29,35 @@ export interface CreateDto {
quantity: number;
usage: string;
}
-
+
export interface GetListDto {
page: number
size: number
name?: string
// code?: string
}
-
+
export interface GetDetailDto {
id?: string
}
-
+
export interface UpdateDto extends CreateDto {
id?: number
}
-
+
export interface DeleteDto {
id?: string
}
-
-export function genPresciptionItem(): PrescriptionItem {
+
+export function genPrescriptionItem(): PrescriptionItem {
return {
id: 0,
prescription_id: 0,
isMix: false,
- medicine_id: 0,
- medicine: genMedicine(),
- medicineMix_id: 0,
- medicineMix: genMedicinemix(),
+ // medicine_code: '',
+ // medicine: genMedicine(),
+ // medicineMix_id: 0,
+ // medicineMix: genMedicinemix(),
frequency: 0,
dose: 0,
interval: 0,
@@ -66,4 +66,3 @@ export function genPresciptionItem(): PrescriptionItem {
usage: ''
}
}
-
\ No newline at end of file
diff --git a/app/models/role.ts b/app/models/role.ts
index 6c6097a9..4829a02d 100644
--- a/app/models/role.ts
+++ b/app/models/role.ts
@@ -14,9 +14,9 @@ export interface AuthState {
export type Permission = 'C' | 'R' | 'U' | 'D'
-export interface RoleAccess {
+export interface RoleAccesses {
[role: string]: Permission[]
}
-export type PagePath = keyof typeof PAGE_PERMISSIONS
-export type PagePermission = (typeof PAGE_PERMISSIONS)[PagePath]
+// export type PagePath = keyof typeof PAGE_PERMISSIONS
+// export type PagePermission = (typeof PAGE_PERMISSIONS)[PagePath]
diff --git a/app/models/specialist-position.ts b/app/models/specialist-position.ts
index 727e8374..040e1dd5 100644
--- a/app/models/specialist-position.ts
+++ b/app/models/specialist-position.ts
@@ -2,7 +2,7 @@ import { type Base, genBase } from './_base'
import type { Employee } from './employee'
export interface SpecialistPosition extends Base {
- specialist_id: number
+ specialist_id: string
code: string
name: string
headStatus?: boolean
@@ -13,7 +13,7 @@ export interface SpecialistPosition extends Base {
export function genSpecialistPosition(): SpecialistPosition {
return {
...genBase(),
- specialist_id: 0,
+ specialist_id: '',
code: '',
name: '',
headStatus: false,
diff --git a/app/models/specialist.ts b/app/models/specialist.ts
index d3180a73..2ce742d3 100644
--- a/app/models/specialist.ts
+++ b/app/models/specialist.ts
@@ -14,6 +14,6 @@ export function genSpecialist(): Specialist {
...genBase(),
code: '',
name: '',
- unit_id: 0,
+ unit_id: '',
}
}
diff --git a/app/models/subspecialist-position.ts b/app/models/subspecialist-position.ts
index 28ea2287..945780d2 100644
--- a/app/models/subspecialist-position.ts
+++ b/app/models/subspecialist-position.ts
@@ -3,7 +3,7 @@ import type { Employee } from './employee'
import type { Subspecialist } from './subspecialist'
export interface SubSpecialistPosition extends Base {
- subspecialist_id: number
+ subspecialist_id: string
code: string
name: string
headStatus?: boolean
@@ -16,7 +16,7 @@ export interface SubSpecialistPosition extends Base {
export function genSubSpecialistPosition(): SubSpecialistPosition {
return {
...genBase(),
- subspecialist_id: 0,
+ subspecialist_id: '',
code: '',
name: '',
headStatus: false,
diff --git a/app/models/unit-position.ts b/app/models/unit-position.ts
index 11e97806..3e1dc426 100644
--- a/app/models/unit-position.ts
+++ b/app/models/unit-position.ts
@@ -2,7 +2,7 @@ import { type Base, genBase } from './_base'
import type { Employee } from './employee'
export interface UnitPosition extends Base {
- unit_id: number
+ unit_id: string
code: string
name: string
headStatus?: boolean
@@ -14,7 +14,7 @@ export interface UnitPosition extends Base {
export function genUnitPosition(): UnitPosition {
return {
...genBase(),
- unit_id: 0,
+ unit_id: '',
code: '',
name: '',
headStatus: false,
diff --git a/app/pages/(features)/children-action/echocardiography/index.vue b/app/pages/(features)/ambulatory/consultation/index.vue
similarity index 100%
rename from app/pages/(features)/children-action/echocardiography/index.vue
rename to app/pages/(features)/ambulatory/consultation/index.vue
diff --git a/app/pages/(features)/children-action/spirometry/index.vue b/app/pages/(features)/ambulatory/encounter-queue/index.vue
similarity index 100%
rename from app/pages/(features)/children-action/spirometry/index.vue
rename to app/pages/(features)/ambulatory/encounter-queue/index.vue
diff --git a/app/pages/(features)/outpatient/encounter/[id]/edit.vue b/app/pages/(features)/ambulatory/encounter/[id]/edit.vue
similarity index 53%
rename from app/pages/(features)/outpatient/encounter/[id]/edit.vue
rename to app/pages/(features)/ambulatory/encounter/[id]/edit.vue
index 9713675f..3c402f32 100644
--- a/app/pages/(features)/outpatient/encounter/[id]/edit.vue
+++ b/app/pages/(features)/ambulatory/encounter/[id]/edit.vue
@@ -1,50 +1,57 @@
-
diff --git a/app/pages/(features)/outpatient/encounter/[id]/index.vue b/app/pages/(features)/ambulatory/encounter/[id]/index.vue
similarity index 56%
rename from app/pages/(features)/outpatient/encounter/[id]/index.vue
rename to app/pages/(features)/ambulatory/encounter/[id]/index.vue
index 1864cf2c..d9609b24 100644
--- a/app/pages/(features)/outpatient/encounter/[id]/index.vue
+++ b/app/pages/(features)/ambulatory/encounter/[id]/index.vue
@@ -1,34 +1,40 @@
diff --git a/app/pages/(features)/ambulatory/encounter/[id]/process.vue b/app/pages/(features)/ambulatory/encounter/[id]/process.vue
new file mode 100644
index 00000000..cf905f9f
--- /dev/null
+++ b/app/pages/(features)/ambulatory/encounter/[id]/process.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
diff --git a/app/pages/(features)/outpatient/encounter/add.vue b/app/pages/(features)/ambulatory/encounter/add.vue
similarity index 53%
rename from app/pages/(features)/outpatient/encounter/add.vue
rename to app/pages/(features)/ambulatory/encounter/add.vue
index 948b16c6..0831723f 100644
--- a/app/pages/(features)/outpatient/encounter/add.vue
+++ b/app/pages/(features)/ambulatory/encounter/add.vue
@@ -1,41 +1,43 @@
-
+// Pubs
+import Error from '~/components/pub/my-ui/error/error.vue'
+
+// Models & Consts
+import type { Permission } from '~/models/role'
+import { permissions } from '~/const/page-permission/ambulatory'
+
+// Apps
+import Content from '~/components/content/encounter/list.vue'
+
+// Page meta
+definePageMeta({
+ // middleware: ['rbac'],
+ // roles: ['emp|reg', 'emp|nur', 'emp|doc', 'emp|miw', 'emp|thr', 'emp|nut', 'emp|pha', 'emp|lab'],
+ title: 'Daftar Kunjungan',
+ contentFrame: 'cf-full-width',
+})
+
+// Define common things
+const route = useRoute()
+
+// Preps role checking
+const roleAccess: Record = permissions[route.path] || {}
+const { checkRole, hasCreateAccess, hasReadAccess } = useRBAC()
+
+// Check if user has access to this page
+const hasAccess = checkRole(roleAccess)
+const canRead = hasReadAccess(roleAccess)
+if (!hasAccess || !canRead) {
+ navigateTo('/403')
+}
+const canCreate = hasCreateAccess(roleAccess)
+
+// Page needs
+useHead({
+ title: () => route.meta.title as string,
+})
+
+// User info
+const { user } = useUserStore()
+const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
+
+
+
+
+
diff --git a/app/pages/(features)/children-action/thalasemia/index.vue b/app/pages/(features)/ambulatory/registration-queue/index.vue
similarity index 100%
rename from app/pages/(features)/children-action/thalasemia/index.vue
rename to app/pages/(features)/ambulatory/registration-queue/index.vue
diff --git a/app/pages/(features)/outpation-action/chemotherapy/[mode]/[id]/verification.vue b/app/pages/(features)/chemotherapy/[mode]/[id]/verification.vue
similarity index 100%
rename from app/pages/(features)/outpation-action/chemotherapy/[mode]/[id]/verification.vue
rename to app/pages/(features)/chemotherapy/[mode]/[id]/verification.vue
diff --git a/app/pages/(features)/outpation-action/chemotherapy/[mode]/index.vue b/app/pages/(features)/chemotherapy/[mode]/index.vue
similarity index 100%
rename from app/pages/(features)/outpation-action/chemotherapy/[mode]/index.vue
rename to app/pages/(features)/chemotherapy/[mode]/index.vue
diff --git a/app/pages/(features)/chemotherapy/encounter/[id]/detail.vue b/app/pages/(features)/chemotherapy/encounter/[id]/detail.vue
new file mode 100644
index 00000000..534f49b3
--- /dev/null
+++ b/app/pages/(features)/chemotherapy/encounter/[id]/detail.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
diff --git a/app/pages/(features)/chemotherapy/encounter/[id]/process.vue b/app/pages/(features)/chemotherapy/encounter/[id]/process.vue
new file mode 100644
index 00000000..030440e2
--- /dev/null
+++ b/app/pages/(features)/chemotherapy/encounter/[id]/process.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
diff --git a/app/pages/(features)/rehab/encounter/add.vue b/app/pages/(features)/chemotherapy/encounter/add.vue
similarity index 51%
rename from app/pages/(features)/rehab/encounter/add.vue
rename to app/pages/(features)/chemotherapy/encounter/add.vue
index fe7dd98c..438f0773 100644
--- a/app/pages/(features)/rehab/encounter/add.vue
+++ b/app/pages/(features)/chemotherapy/encounter/add.vue
@@ -1,44 +1,46 @@
-
diff --git a/app/pages/(features)/outpatient/encounter/index.vue b/app/pages/(features)/chemotherapy/encounter/index.vue
similarity index 62%
rename from app/pages/(features)/outpatient/encounter/index.vue
rename to app/pages/(features)/chemotherapy/encounter/index.vue
index 1e679c5d..4a8c9360 100644
--- a/app/pages/(features)/outpatient/encounter/index.vue
+++ b/app/pages/(features)/chemotherapy/encounter/index.vue
@@ -1,23 +1,19 @@
-
diff --git a/app/pages/(features)/outpation-action/chemotherapy/index.vue b/app/pages/(features)/chemotherapy/index.vue
similarity index 100%
rename from app/pages/(features)/outpation-action/chemotherapy/index.vue
rename to app/pages/(features)/chemotherapy/index.vue
diff --git a/app/pages/(features)/rehab/registration/index.vue b/app/pages/(features)/chemotherapy/list.vue
similarity index 72%
rename from app/pages/(features)/rehab/registration/index.vue
rename to app/pages/(features)/chemotherapy/list.vue
index 5b4fbfb8..34093b2d 100644
--- a/app/pages/(features)/rehab/registration/index.vue
+++ b/app/pages/(features)/chemotherapy/list.vue
@@ -1,12 +1,12 @@
-
-
-
-
-
-
-
diff --git a/app/pages/(features)/outpatient/consultation/index.vue b/app/pages/(features)/echocardiography/index.vue
similarity index 100%
rename from app/pages/(features)/outpatient/consultation/index.vue
rename to app/pages/(features)/echocardiography/index.vue
diff --git a/app/pages/(features)/emergency/encounter/[id]/detail.vue b/app/pages/(features)/emergency/encounter/[id]/detail.vue
new file mode 100644
index 00000000..77babf09
--- /dev/null
+++ b/app/pages/(features)/emergency/encounter/[id]/detail.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
diff --git a/app/pages/(features)/patient/index.vue b/app/pages/(features)/emergency/encounter/[id]/process.vue
similarity index 53%
rename from app/pages/(features)/patient/index.vue
rename to app/pages/(features)/emergency/encounter/[id]/process.vue
index 1877289e..7143d512 100644
--- a/app/pages/(features)/patient/index.vue
+++ b/app/pages/(features)/emergency/encounter/[id]/process.vue
@@ -1,23 +1,18 @@
-
-
-
-
-
+
+
-
+
+
\ No newline at end of file
diff --git a/app/pages/(features)/emergency/encounter/add.vue b/app/pages/(features)/emergency/encounter/add.vue
index aa382643..d3ac86b7 100644
--- a/app/pages/(features)/emergency/encounter/add.vue
+++ b/app/pages/(features)/emergency/encounter/add.vue
@@ -1,44 +1,46 @@
-
diff --git a/app/pages/(features)/emergency/encounter/index.vue b/app/pages/(features)/emergency/encounter/index.vue
index ed05c25e..bc83f3f2 100644
--- a/app/pages/(features)/emergency/encounter/index.vue
+++ b/app/pages/(features)/emergency/encounter/index.vue
@@ -1,23 +1,20 @@
-
diff --git a/app/pages/(features)/outpatient/polyclinic-queue/index.vue b/app/pages/(features)/hemophilia/index.vue
similarity index 100%
rename from app/pages/(features)/outpatient/polyclinic-queue/index.vue
rename to app/pages/(features)/hemophilia/index.vue
diff --git a/app/pages/(features)/human-src/employee/[id]/edit.vue b/app/pages/(features)/human-src/employee/[id]/edit.vue
new file mode 100644
index 00000000..00d68602
--- /dev/null
+++ b/app/pages/(features)/human-src/employee/[id]/edit.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/pages/(features)/human-src/employee/[id]/index.vue b/app/pages/(features)/human-src/employee/[id]/index.vue
new file mode 100644
index 00000000..00d68602
--- /dev/null
+++ b/app/pages/(features)/human-src/employee/[id]/index.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/pages/(features)/human-src/specialist-intern/add.vue b/app/pages/(features)/human-src/intern/add.vue
similarity index 100%
rename from app/pages/(features)/human-src/specialist-intern/add.vue
rename to app/pages/(features)/human-src/intern/add.vue
diff --git a/app/pages/(features)/human-src/specialist-intern/index.vue b/app/pages/(features)/human-src/intern/index.vue
similarity index 100%
rename from app/pages/(features)/human-src/specialist-intern/index.vue
rename to app/pages/(features)/human-src/intern/index.vue
diff --git a/app/pages/(features)/service-src/bed/index.vue b/app/pages/(features)/infra-src/bed/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/bed/index.vue
rename to app/pages/(features)/infra-src/bed/index.vue
diff --git a/app/pages/(features)/service-src/building/index.vue b/app/pages/(features)/infra-src/building/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/building/index.vue
rename to app/pages/(features)/infra-src/building/index.vue
diff --git a/app/pages/(features)/service-src/chamber/index.vue b/app/pages/(features)/infra-src/chamber/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/chamber/index.vue
rename to app/pages/(features)/infra-src/chamber/index.vue
diff --git a/app/pages/(features)/service-src/counter/index.vue b/app/pages/(features)/infra-src/counter/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/counter/index.vue
rename to app/pages/(features)/infra-src/counter/index.vue
diff --git a/app/pages/(features)/service-src/floor/index.vue b/app/pages/(features)/infra-src/floor/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/floor/index.vue
rename to app/pages/(features)/infra-src/floor/index.vue
diff --git a/app/pages/(features)/service-src/public-screen/index.vue b/app/pages/(features)/infra-src/public-screen/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/public-screen/index.vue
rename to app/pages/(features)/infra-src/public-screen/index.vue
diff --git a/app/pages/(features)/service-src/room/index.vue b/app/pages/(features)/infra-src/room/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/room/index.vue
rename to app/pages/(features)/infra-src/room/index.vue
diff --git a/app/pages/(features)/service-src/warehouse/index.vue b/app/pages/(features)/infra-src/warehouse/index.vue
similarity index 100%
rename from app/pages/(features)/service-src/warehouse/index.vue
rename to app/pages/(features)/infra-src/warehouse/index.vue
diff --git a/app/pages/(features)/inpatient/encounter/[id]/detail.vue b/app/pages/(features)/inpatient/encounter/[id]/detail.vue
new file mode 100644
index 00000000..45751701
--- /dev/null
+++ b/app/pages/(features)/inpatient/encounter/[id]/detail.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
diff --git a/app/pages/(features)/doctor/index.vue b/app/pages/(features)/inpatient/encounter/[id]/process.vue
similarity index 54%
rename from app/pages/(features)/doctor/index.vue
rename to app/pages/(features)/inpatient/encounter/[id]/process.vue
index 078df778..0a3fbe72 100644
--- a/app/pages/(features)/doctor/index.vue
+++ b/app/pages/(features)/inpatient/encounter/[id]/process.vue
@@ -1,23 +1,18 @@
-
-
-
-
-
+
+
+
diff --git a/app/pages/(features)/inpatient/encounter/add.vue b/app/pages/(features)/inpatient/encounter/add.vue
index bd827f79..e2812253 100644
--- a/app/pages/(features)/inpatient/encounter/add.vue
+++ b/app/pages/(features)/inpatient/encounter/add.vue
@@ -1,44 +1,46 @@
-
diff --git a/app/pages/(features)/inpatient/encounter/index.vue b/app/pages/(features)/inpatient/encounter/index.vue
index 7c9d1ac5..ad73d029 100644
--- a/app/pages/(features)/inpatient/encounter/index.vue
+++ b/app/pages/(features)/inpatient/encounter/index.vue
@@ -1,23 +1,19 @@
-
diff --git a/app/pages/(features)/integration/bpjs/control-letter/index.vue b/app/pages/(features)/integration/bpjs-vclaim/control-letter/index.vue
similarity index 100%
rename from app/pages/(features)/integration/bpjs/control-letter/index.vue
rename to app/pages/(features)/integration/bpjs-vclaim/control-letter/index.vue
diff --git a/app/pages/(features)/integration/bpjs/sep/add.vue b/app/pages/(features)/integration/bpjs-vclaim/sep/add.vue
similarity index 100%
rename from app/pages/(features)/integration/bpjs/sep/add.vue
rename to app/pages/(features)/integration/bpjs-vclaim/sep/add.vue
diff --git a/app/pages/(features)/integration/bpjs/sep/index.vue b/app/pages/(features)/integration/bpjs-vclaim/sep/index.vue
similarity index 100%
rename from app/pages/(features)/integration/bpjs/sep/index.vue
rename to app/pages/(features)/integration/bpjs-vclaim/sep/index.vue
diff --git a/app/pages/(features)/org-src/division/[id]/index.vue b/app/pages/(features)/org-src/division/[id]/index.vue
index 658652d9..238d90e0 100644
--- a/app/pages/(features)/org-src/division/[id]/index.vue
+++ b/app/pages/(features)/org-src/division/[id]/index.vue
@@ -33,7 +33,7 @@ const canRead = true
-
+
-
+
-
+
-
+
-
+
+import type { Permission } from '~/models/role'
+import { permissions } from '~/const/page-permission/outpatient'
+import Error from '~/components/pub/my-ui/error/error.vue'
+import Content from '~/components/content/encounter/detail.vue'
+
+definePageMeta({
+ middleware: ['rbac'],
+ roles: ['emp|doc', 'emp|nur', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng'],
+ title: 'Detail Kunjungan',
+ contentFrame: 'cf-full-width',
+})
+
+// Preps role checking
+const roleAccess: Record = permissions['/outpatient/encounter'] || {}
+const { checkRole, hasReadAccess } = useRBAC()
+
+// Check if user has access to this page
+const hasAccess = checkRole(roleAccess)
+if (!hasAccess) {
+ navigateTo('/403')
+}
+
+// Define permission-based computed properties
+const canRead = hasReadAccess(roleAccess)
+
+// Page needs
+const route = useRoute()
+useHead({
+ title: () => `${route.meta.title}`,
+})
+
+
+
+
+
+
+
+
diff --git a/app/pages/(features)/patient/[id]/detail.vue b/app/pages/(features)/patient/[id]/detail.vue
deleted file mode 100644
index 33a36f0f..00000000
--- a/app/pages/(features)/patient/[id]/detail.vue
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- detail pasien
-
diff --git a/app/pages/(features)/patient/[id]/edit.vue b/app/pages/(features)/patient/[id]/edit.vue
deleted file mode 100644
index 2b7e8a31..00000000
--- a/app/pages/(features)/patient/[id]/edit.vue
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- edit pasien
-
diff --git a/app/pages/(features)/patient/add.vue b/app/pages/(features)/patient/add.vue
deleted file mode 100644
index 412c2b3e..00000000
--- a/app/pages/(features)/patient/add.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/encounter-queue/index.vue b/app/pages/(features)/rehab/encounter-queue/index.vue
deleted file mode 100644
index 8616dd19..00000000
--- a/app/pages/(features)/rehab/encounter-queue/index.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Examination Queue
-
diff --git a/app/pages/(features)/rehab/encounter/[id]/control-letter/[control_letter_id]/edit.vue b/app/pages/(features)/rehab/encounter/[id]/control-letter/[control_letter_id]/edit.vue
deleted file mode 100644
index cc5d182f..00000000
--- a/app/pages/(features)/rehab/encounter/[id]/control-letter/[control_letter_id]/edit.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/encounter/[id]/control-letter/[control_letter_id]/index.vue b/app/pages/(features)/rehab/encounter/[id]/control-letter/[control_letter_id]/index.vue
deleted file mode 100644
index 612315ad..00000000
--- a/app/pages/(features)/rehab/encounter/[id]/control-letter/[control_letter_id]/index.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/encounter/[id]/control-letter/add.vue b/app/pages/(features)/rehab/encounter/[id]/control-letter/add.vue
deleted file mode 100644
index 1070a29f..00000000
--- a/app/pages/(features)/rehab/encounter/[id]/control-letter/add.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/encounter/[id]/edit.vue b/app/pages/(features)/rehab/encounter/[id]/edit.vue
deleted file mode 100644
index 02dc5428..00000000
--- a/app/pages/(features)/rehab/encounter/[id]/edit.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/encounter/[id]/process.vue b/app/pages/(features)/rehab/encounter/[id]/process.vue
index abd0efa7..9708adfd 100644
--- a/app/pages/(features)/rehab/encounter/[id]/process.vue
+++ b/app/pages/(features)/rehab/encounter/[id]/process.vue
@@ -2,6 +2,7 @@
import type { PagePermission } from '~/models/role'
import Error from '~/components/pub/my-ui/error/error.vue'
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
+import EncounterProcess from '~/components/content/encounter/process-next.vue'
definePageMeta({
middleware: ['rbac'],
@@ -18,7 +19,7 @@ useHead({
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
-const { checkRole, hasCreateAccess } = useRBAC()
+const { checkRole, hasCreateAccess, getPagePermissions } = useRBAC()
// Check if user has access to this page
const hasAccess = checkRole(roleAccess)
@@ -30,12 +31,12 @@ const hasAccess = checkRole(roleAccess)
// }
// Define permission-based computed properties
-const canCreate = true // hasCreateAccess(roleAccess)
+const pagePermission = getPagePermissions(roleAccess)
-
-
+
+
diff --git a/app/pages/(features)/rehab/encounter/index.vue b/app/pages/(features)/rehab/encounter/index.vue
deleted file mode 100644
index f50ad954..00000000
--- a/app/pages/(features)/rehab/encounter/index.vue
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/registration-queue/index.vue b/app/pages/(features)/rehab/registration-queue/index.vue
deleted file mode 100644
index d06e73ad..00000000
--- a/app/pages/(features)/rehab/registration-queue/index.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/registration-queue/sep-prosedur/add.vue b/app/pages/(features)/rehab/registration-queue/sep-prosedur/add.vue
deleted file mode 100644
index 6a45818c..00000000
--- a/app/pages/(features)/rehab/registration-queue/sep-prosedur/add.vue
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/registration/[id]/detail.vue b/app/pages/(features)/rehab/registration/[id]/detail.vue
deleted file mode 100644
index 77c30c00..00000000
--- a/app/pages/(features)/rehab/registration/[id]/detail.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/registration/[id]/edit.vue b/app/pages/(features)/rehab/registration/[id]/edit.vue
deleted file mode 100644
index c6af01a9..00000000
--- a/app/pages/(features)/rehab/registration/[id]/edit.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/registration/add.vue b/app/pages/(features)/rehab/registration/add.vue
deleted file mode 100644
index 87053270..00000000
--- a/app/pages/(features)/rehab/registration/add.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/pages/(features)/rehab/encounter/[id]/detail.vue b/app/pages/(features)/resume/add.vue
similarity index 77%
rename from app/pages/(features)/rehab/encounter/[id]/detail.vue
rename to app/pages/(features)/resume/add.vue
index e3d45895..7cc707f9 100644
--- a/app/pages/(features)/rehab/encounter/[id]/detail.vue
+++ b/app/pages/(features)/resume/add.vue
@@ -6,7 +6,7 @@ import { PAGE_PERMISSIONS } from '~/lib/page-permission'
definePageMeta({
middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
- title: 'Tambah Kunjungan',
+ title: 'Resume',
contentFrame: 'cf-full-width',
})
@@ -22,20 +22,20 @@ const { checkRole, hasCreateAccess } = useRBAC()
// Check if user has access to this page
const hasAccess = checkRole(roleAccess)
-if (!hasAccess) {
- throw createError({
- statusCode: 403,
- statusMessage: 'Access denied',
- })
-}
+// if (!hasAccess) {
+// throw createError({
+// statusCode: 403,
+// statusMessage: 'Access denied',
+// })
+// }
// Define permission-based computed properties
-const canCreate = hasCreateAccess(roleAccess)
+const canCreate = true // hasCreateAccess(roleAccess)
-
+
diff --git a/app/pages/(features)/outpatient/registration-queue/index.vue b/app/pages/(features)/spirometry/index.vue
similarity index 100%
rename from app/pages/(features)/outpatient/registration-queue/index.vue
rename to app/pages/(features)/spirometry/index.vue
diff --git a/app/pages/(features)/outpation-action/hemophilia/index.vue b/app/pages/(features)/thalasemia/index.vue
similarity index 100%
rename from app/pages/(features)/outpation-action/hemophilia/index.vue
rename to app/pages/(features)/thalasemia/index.vue
diff --git a/app/pages/(features)/outpation-action/chemotherapy/list.vue b/app/pages/(features)/tools-equipment-src/medicine-form/index.vue
similarity index 75%
rename from app/pages/(features)/outpation-action/chemotherapy/list.vue
rename to app/pages/(features)/tools-equipment-src/medicine-form/index.vue
index a141baaa..120df6ea 100644
--- a/app/pages/(features)/outpation-action/chemotherapy/list.vue
+++ b/app/pages/(features)/tools-equipment-src/medicine-form/index.vue
@@ -6,8 +6,8 @@ import { PAGE_PERMISSIONS } from '~/lib/page-permission'
definePageMeta({
middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
- title: 'Daftar Kempterapi',
- contentFrame: 'cf-full-width',
+ title: 'Daftar Dokter',
+ contentFrame: 'cf-container-lg',
})
const route = useRoute()
@@ -22,19 +22,17 @@ const { checkRole, hasReadAccess } = useRBAC()
// Check if user has access to this page
const hasAccess = checkRole(roleAccess)
-if (!hasAccess) {
- navigateTo('/403')
-}
+// if (!hasAccess) {
+// navigateTo('/403')
+// }
// Define permission-based computed properties
const canRead = true // hasReadAccess(roleAccess)
-
+
+
+
+
diff --git a/app/pages/(features)/tools-equipment-src/medicine/index.vue b/app/pages/(features)/tools-equipment-src/medicine/index.vue
index 2be85f63..25e5105e 100644
--- a/app/pages/(features)/tools-equipment-src/medicine/index.vue
+++ b/app/pages/(features)/tools-equipment-src/medicine/index.vue
@@ -5,7 +5,7 @@ import { PAGE_PERMISSIONS } from '~/lib/page-permission'
definePageMeta({
middleware: ['rbac'],
- roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
+ roles: [],
title: 'Daftar Dokter',
contentFrame: 'cf-full-width',
})
@@ -16,18 +16,18 @@ useHead({
title: () => route.meta.title as string,
})
-const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor']
+const roleAccess: PagePermission = PAGE_PERMISSIONS['/tools-equipment-src/medicine']!
const { checkRole, hasReadAccess } = useRBAC()
// Check if user has access to this page
const hasAccess = checkRole(roleAccess)
-if (!hasAccess) {
- navigateTo('/403')
-}
+// if (!hasAccess) {
+// navigateTo('/403')
+// }
// Define permission-based computed properties
-const canRead = hasReadAccess(roleAccess)
+const canRead = true // hasReadAccess(roleAccess)
diff --git a/app/schemas/_generate-file.ts b/app/schemas/_generate-file.ts
new file mode 100644
index 00000000..0e0b05fc
--- /dev/null
+++ b/app/schemas/_generate-file.ts
@@ -0,0 +1,5 @@
+export interface GenerateFile {
+ entityType_code: string
+ ref_id: number
+ type_code: string
+}
diff --git a/app/schemas/division-position.schema.ts b/app/schemas/division-position.schema.ts
index 65906f61..dca75129 100644
--- a/app/schemas/division-position.schema.ts
+++ b/app/schemas/division-position.schema.ts
@@ -5,7 +5,7 @@ const DivisionPositionSchema = 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'),
headStatus: z.boolean().optional().nullable(),
- division_id: z.union([
+ division_code: z.union([
z.string({ required_error: 'Divisi Induk harus diisi' }),
z.number({ required_error: 'Divisi Induk harus diisi' })
]).optional().nullable(),
diff --git a/app/schemas/division.schema.ts b/app/schemas/division.schema.ts
index fae5afca..f8fcab6e 100644
--- a/app/schemas/division.schema.ts
+++ b/app/schemas/division.schema.ts
@@ -4,7 +4,7 @@ import type { Division } from '~/models/division'
const DivisionSchema = 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'),
- parent_id: z.union([
+ parent_code: z.union([
z.string({ required_error: 'Divisi Induk harus diisi' }),
z.number({ required_error: 'Divisi Induk harus diisi' })
]).optional().nullable(),
diff --git a/app/schemas/document-upload.schema.ts b/app/schemas/document-upload.schema.ts
new file mode 100644
index 00000000..305ec16c
--- /dev/null
+++ b/app/schemas/document-upload.schema.ts
@@ -0,0 +1,25 @@
+import { z } from 'zod'
+
+const ACCEPTED_UPLOAD_TYPES = ['image/jpeg', 'image/png', 'application/pdf']
+const MAX_SIZE_BYTES = 1 * 1024 * 1024 // 1MB
+
+const DocumentUploadSchema = z.object({
+ entityType_code: z.string().default('encounter'),
+ ref_id: z.number(),
+ upload_employee_id: z.number().optional(),
+ // upload_employee_id: z.number(),
+ name: z.string({ required_error: 'Mohon isi', }),
+ type_code: z.string({ required_error: 'Mohon isi', }),
+ content: z.custom()
+ .refine((f) => f, { message: 'File tidak boleh kosong' })
+ .refine((f) => !f || f instanceof File, { message: 'Harus berupa file yang valid' })
+ .refine((f) => !f || ACCEPTED_UPLOAD_TYPES.includes(f.type), {
+ message: 'Format file harus JPG, PNG, atau PDF',
+ })
+ .refine((f) => !f || f.size <= MAX_SIZE_BYTES, { message: 'Maksimal 1MB' }),
+})
+
+type DocumentUploadFormData = z.infer
+
+export { DocumentUploadSchema }
+export type { DocumentUploadFormData }
diff --git a/app/schemas/general-consent.schema.ts b/app/schemas/general-consent.schema.ts
new file mode 100644
index 00000000..e06c7240
--- /dev/null
+++ b/app/schemas/general-consent.schema.ts
@@ -0,0 +1,16 @@
+import { z } from 'zod'
+import type { CreateDto } from '~/models/general-consent'
+
+const GeneralConsentSchema = z.object({
+ relatives: z.array(z.object({ name: z.string(), phone: z.string() })),
+ responsibleName: z.string().optional(),
+ responsiblePhone: z.string().optional(),
+ informant: z.string().optional(),
+ witness1: z.string().optional(),
+ witness2: z.string().optional(),
+})
+
+type GeneralConsentFormData = z.infer & CreateDto
+
+export { GeneralConsentSchema }
+export type { GeneralConsentFormData }
diff --git a/app/schemas/installation-position.schema.ts b/app/schemas/installation-position.schema.ts
index 5990fc6c..c869b361 100644
--- a/app/schemas/installation-position.schema.ts
+++ b/app/schemas/installation-position.schema.ts
@@ -5,7 +5,7 @@ const InstallationPositionSchema = 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'),
headStatus: z.boolean().optional().nullable(),
- installation_id: z.union([
+ installation_code: z.union([
z.string({ required_error: 'Instalasi Induk harus diisi' }),
z.number({ required_error: 'Instalasi Induk harus diisi' }),
]),
diff --git a/app/schemas/integration-bpjs.schema.ts b/app/schemas/integration-bpjs.schema.ts
index 47206edf..452735f7 100644
--- a/app/schemas/integration-bpjs.schema.ts
+++ b/app/schemas/integration-bpjs.schema.ts
@@ -78,7 +78,7 @@ const IntegrationBpjsSchema = z
.optional(),
destinationClinic: z
.string({ required_error: ERROR_MESSAGES.required.destinationClinic })
- .min(1, ERROR_MESSAGES.required.destinationClinic),
+ .min(1, ERROR_MESSAGES.required.destinationClinic).optional(),
attendingDoctor: z
.string({ required_error: ERROR_MESSAGES.required.attendingDoctor })
.min(1, ERROR_MESSAGES.required.attendingDoctor),
@@ -89,7 +89,7 @@ const IntegrationBpjsSchema = z
cataract: z.string({ required_error: ERROR_MESSAGES.required.cataract }).min(1, ERROR_MESSAGES.required.cataract),
clinicExcecutive: z
.string({ required_error: ERROR_MESSAGES.required.clinicExcecutive })
- .min(1, ERROR_MESSAGES.required.clinicExcecutive),
+ .min(1, ERROR_MESSAGES.required.clinicExcecutive).optional(),
subSpecialistId: z
.string({ required_error: ERROR_MESSAGES.required.subSpecialistId })
.min(1, ERROR_MESSAGES.required.subSpecialistId)
diff --git a/app/schemas/medicine.schema.ts b/app/schemas/medicine.schema.ts
index 44113777..6443be36 100644
--- a/app/schemas/medicine.schema.ts
+++ b/app/schemas/medicine.schema.ts
@@ -5,6 +5,7 @@ export const MedicineSchema = z.object({
name: z.string({ required_error: 'Nama harus diisi' }).min(1, 'Nama minimal 1 karakter'),
medicineGroup_code: z.string({ required_error: 'Kelompok obat harus diisi' }).min(1, 'Kelompok obat harus diisi'),
medicineMethod_code: z.string({ required_error: 'Metode pemberian harus diisi' }).min(1, 'Metode pemberian harus diisi'),
+ medicineForm_code: z.string({ required_error: 'Sediaan Obat harus diisi' }).min(1, 'Sediaan Obat harus diisi'),
uom_code: z.string({ required_error: 'Satuan harus diisi' }).min(1, 'Satuan harus diisi'),
infra_id: z.number().nullable().optional(),
stock: z.preprocess((val) => Number(val), z.number({ invalid_type_error: 'Stok harus berupa angka' }).min(1, 'Stok harus lebih besar dari 0')),
diff --git a/app/schemas/resume.schema.ts b/app/schemas/resume.schema.ts
new file mode 100644
index 00000000..3e466a33
--- /dev/null
+++ b/app/schemas/resume.schema.ts
@@ -0,0 +1,76 @@
+import { z } from 'zod'
+import type { CreateDto } from '~/models/consultation'
+
+export type ResumeArrangementType = "krs" | "mrs" | "rujukInternal" | "rujukExternal" | "meninggal" | "other"
+
+const SecondaryDiagnosisSchema = z.object({
+ diagnosis: z.string({ required_error: 'Diagnosis harus diisi' }),
+ icd10: z.string({ required_error: 'ICD 10 harus diisi' }),
+ diagnosisBasis: z.string({ required_error: 'Dasar Diagnosis harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+})
+
+const SecondaryActionSchema = z.object({
+ action: z.string({ required_error: 'Action harus diisi' }),
+ icd9: z.string({ required_error: 'ICD 10 harus diisi' }),
+ actionBasis: z.string({ required_error: 'Dasar Action harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+})
+
+const ConsultationSchema = z.object({
+ consultation: z.string({ required_error: 'Consultation harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+ consultationReply: z.string({ required_error: 'Jawaban Consultation harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+})
+
+const ResumeSchema = z.object({
+ inDate: z.string({ required_error: 'Tanggal harus diisi' }),
+ outDate: z.string({ required_error: 'Tanggal harus diisi' }),
+ anamnesis: z.number({ required_error: 'Anamnesis harus diisi' })
+ .min(1, 'Anamnesis minimum 1 karakter')
+ .max(2048, 'Anamnesis maksimum 2048 karakter'),
+ physicalCheckup: z.string({ required_error: 'Uraian harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+ supplementCheckup: z.string({ required_error: 'Uraian harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+
+ primaryDiagnosis: z.string({ required_error: 'Diagnosis harus diisi' }),
+ secondaryDiagnosis: z.array(SecondaryDiagnosisSchema).optional(),
+
+ primaryOperativeNonOperativeAct: z.string({ required_error: 'Diagnosis harus diisi' }),
+ secondaryOperativeNonOperativeAct: z.array(SecondaryActionSchema).optional(),
+ medikamentosa: z.string({ required_error: 'Uraian harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+
+ consultation: z.array(ConsultationSchema).optional(),
+
+ arrangement: z.custom().default("krs"),
+ inpatientIndication: z.string({ required_error: 'Uraian harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter'),
+ faskes: z.string({ required_error: 'Faskes harus diisi' }).optional(),
+ clinic: z.string({ required_error: 'Klinik harus diisi' }).optional(),
+ deathDate: z.string({ required_error: 'Tanggal harus diisi' }).optional(),
+ deathCause: z.array(z.string()).optional().default([]),
+ deathCauseDescription: z.string({ required_error: 'Uraian harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter')
+ .optional(),
+ keterangan: z.string({ required_error: 'Uraian harus diisi' })
+ .min(1, 'Uraian minimum 1 karakter')
+ .max(2048, 'Uraian maksimum 2048 karakter')
+ .optional(),
+})
+
+type ResumeFormData = z.infer & (CreateDto)
+
+export { ResumeSchema }
+export type { ResumeFormData }
diff --git a/app/schemas/soapi.schema.ts b/app/schemas/soapi.schema.ts
index 50ade93e..2f5f8667 100644
--- a/app/schemas/soapi.schema.ts
+++ b/app/schemas/soapi.schema.ts
@@ -179,6 +179,14 @@ const InstructionSchema = z.object({
other: z.string().default(''),
})
+export const CprjSoapiSchema = z.object({
+ subjective: z.string().default(''),
+ objective: z.string().default(''),
+ assesment: z.string().default(''),
+ plan: z.string().default(''),
+ review: z.string().default(''),
+})
+
export const SoapSchema = z.object({
subject: SubjectSchema,
object: ObjectSchema,
diff --git a/app/schemas/specialist-position.schema.ts b/app/schemas/specialist-position.schema.ts
index 7aacef37..3e8010bb 100644
--- a/app/schemas/specialist-position.schema.ts
+++ b/app/schemas/specialist-position.schema.ts
@@ -5,7 +5,7 @@ const SpecialistPositionSchema = 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'),
headStatus: z.boolean().optional().nullable(),
- specialist_id: z
+ specialist_code: z
.union([
z.string({ required_error: 'Spesialis harus diisi' }),
z.number({ required_error: 'Spesialis harus diisi' }),
diff --git a/app/schemas/specialist.schema.ts b/app/schemas/specialist.schema.ts
index 1c536bef..c9e39285 100644
--- a/app/schemas/specialist.schema.ts
+++ b/app/schemas/specialist.schema.ts
@@ -4,7 +4,7 @@ import type { Specialist } from '~/models/specialist'
const SpecialistSchema = 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'),
- unit_id: z
+ unit_code: z
.union([z.string({ required_error: 'Unit harus diisi' }), z.number({ required_error: 'Unit harus diisi' })])
.optional()
.nullable(),
diff --git a/app/schemas/subspecialist-position.schema.ts b/app/schemas/subspecialist-position.schema.ts
index 7f458e7b..d191ad9a 100644
--- a/app/schemas/subspecialist-position.schema.ts
+++ b/app/schemas/subspecialist-position.schema.ts
@@ -5,7 +5,7 @@ const SubSpecialistPositionSchema = 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'),
headStatus: z.boolean().optional().nullable(),
- subspecialist_id: z
+ subspecialist_code: z
.union([
z.string({ required_error: 'Spesialis harus diisi' }),
z.number({ required_error: 'Spesialis harus diisi' }),
diff --git a/app/schemas/subspecialist.schema.ts b/app/schemas/subspecialist.schema.ts
index e4b3ec77..a4a6c0b8 100644
--- a/app/schemas/subspecialist.schema.ts
+++ b/app/schemas/subspecialist.schema.ts
@@ -4,7 +4,7 @@ import type { Subspecialist } from '~/models/subspecialist'
const SubspecialistSchema = 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'),
- specialist_id: z
+ specialist_code: z
.union([
z.string({ required_error: 'Spesialis harus diisi' }),
z.number({ required_error: 'Spesialis harus diisi' }),
diff --git a/app/schemas/unit-position.schema.ts b/app/schemas/unit-position.schema.ts
index 2422b9a1..e170db10 100644
--- a/app/schemas/unit-position.schema.ts
+++ b/app/schemas/unit-position.schema.ts
@@ -5,7 +5,7 @@ const UnitPositionSchema = 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'),
headStatus: z.boolean().optional().nullable(),
- unit_id: z
+ unit_code: z
.union([
z.string({ required_error: 'Unit Induk harus diisi' }),
z.number({ required_error: 'Unit Induk harus diisi' }),
diff --git a/app/schemas/unit.schema.ts b/app/schemas/unit.schema.ts
index 83657224..75a19b39 100644
--- a/app/schemas/unit.schema.ts
+++ b/app/schemas/unit.schema.ts
@@ -4,7 +4,7 @@ import type { Unit } from '~/models/unit'
const UnitSchema = 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'),
- installation_id: z
+ installation_code: z
.union([
z.string({ required_error: 'Instalasi harus diisi' }),
z.number({ required_error: 'Instalasi harus diisi' }),
diff --git a/app/schemas/verification.schema.ts b/app/schemas/verification.schema.ts
new file mode 100644
index 00000000..db0319cf
--- /dev/null
+++ b/app/schemas/verification.schema.ts
@@ -0,0 +1,19 @@
+import { z } from 'zod'
+
+const VerificationSchema = z.object({
+ name: z.string({
+ required_error: 'Mohon lengkapi Nama Anda',
+ }),
+ email: z.string({
+ required_error: 'Mohon lengkapi email',
+ }),
+ password: z.string({
+ required_error: 'Mohon lengkapi password',
+ }),
+})
+
+
+type VerificationFormData = z.infer
+
+export { VerificationSchema, }
+export type { VerificationFormData, }
\ No newline at end of file
diff --git a/app/services/_crud-base.ts b/app/services/_crud-base.ts
index e3c57689..bf3a89c5 100644
--- a/app/services/_crud-base.ts
+++ b/app/services/_crud-base.ts
@@ -74,6 +74,19 @@ export async function update(path: string, id: number | string, data: any, name:
}
}
+export async function updateCustom(path: string, data: any, name: string = 'item') {
+ try {
+ const resp = await xfetch(`${path}`, 'PATCH', data)
+ const result: any = {}
+ result.success = resp.success
+ result.body = (resp.body as Record) || {}
+ return result
+ } catch (error) {
+ console.error(`Error putting ${name}:`, error)
+ throw new Error(`Failed to put ${name}`)
+ }
+}
+
export async function remove(path: string, id: number | string, name: string = 'item') {
try {
const resp = await xfetch(`${path}/${id}`, 'DELETE')
@@ -86,3 +99,16 @@ export async function remove(path: string, id: number | string, name: string = '
throw new Error(`Failed to delete ${name}`)
}
}
+
+export async function removeCustom(path: string, data: any, name: string = 'item') {
+ try {
+ const resp = await xfetch(`${path}`, 'DELETE', data)
+ const result: any = {}
+ result.success = resp.success
+ result.body = (resp.body as Record) || {}
+ return result
+ } catch (error) {
+ console.error(`Error deleting ${name}:`, error)
+ throw new Error(`Failed to delete ${name}`)
+ }
+}
\ No newline at end of file
diff --git a/app/services/device-order-item.service.ts b/app/services/device-order-item.service.ts
index 33b92b8c..b2eab0f4 100644
--- a/app/services/device-order-item.service.ts
+++ b/app/services/device-order-item.service.ts
@@ -5,7 +5,6 @@ const path = '/api/v1/device-order-item'
const name = 'device-order-item'
export function create(data: any) {
- console.log('service create', data)
return base.create(path, data, name)
}
diff --git a/app/services/device-order.service.ts b/app/services/device-order.service.ts
index b8d5372c..cf70420d 100644
--- a/app/services/device-order.service.ts
+++ b/app/services/device-order.service.ts
@@ -13,8 +13,8 @@ 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 getDetail(id: number | string, params?: any) {
+ return base.getDetail(path, id, name, params)
}
export function update(id: number | string, data: any) {
@@ -24,3 +24,16 @@ export function update(id: number | string, data: any) {
export function remove(id: number | string) {
return base.remove(path, id, name)
}
+
+export async function submit(id: number) {
+ try {
+ const resp = await xfetch(`${path}/${id}/submit`, 'PATCH')
+ const result: any = {}
+ result.success = resp.success
+ result.body = (resp.body as Record) || {}
+ return result
+ } catch (error) {
+ console.error(`Error putting ${name}:`, error)
+ throw new Error(`Failed to put ${name}`)
+ }
+}
\ No newline at end of file
diff --git a/app/services/division.service.ts b/app/services/division.service.ts
index e3285d34..21712599 100644
--- a/app/services/division.service.ts
+++ b/app/services/division.service.ts
@@ -28,13 +28,15 @@ 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: Division) => ({
- value: item.id ? Number(item.id) : item.code,
+ value: useCodeAsValue ? item.code
+ : item.id ? Number(item.id)
+ : item.code,
label: item.name,
}))
}
@@ -46,10 +48,12 @@ export async function getValueLabelList(params: any = null): Promise<{ value: st
* @param divisions Array of division objects from API
* @returns TreeItem[]
*/
-export function getValueTreeItems(divisions: any[]): TreeItem[] {
+export function getValueTreeItems(divisions: any[], byCode = true): TreeItem[] {
return divisions
.map((division: Division) => ({
- value: division.id ? String(division.id) : division.code,
+ value: byCode ? String(division.code)
+ : String(division.id) ? String(division.id)
+ : division.code,
label: division.name,
hasChildren: Array.isArray(division.childrens) && division.childrens.length > 0,
children:
diff --git a/app/services/encounter.service.ts b/app/services/encounter.service.ts
index 6e42fd41..3643277a 100644
--- a/app/services/encounter.service.ts
+++ b/app/services/encounter.service.ts
@@ -28,6 +28,11 @@ export function remove(id: number | string) {
return base.remove(path, id, name)
}
+export function cancel(id: number | string) {
+ let url = `${path}/${id}/cancel`
+ return base.updateCustom(url, null, name)
+}
+
export async function getValueLabelList(params: any = null): Promise<{ value: string; label: string }[]> {
let data: { value: string; label: string }[] = []
const result = await getList(params)
diff --git a/app/services/general-consent.service.ts b/app/services/general-consent.service.ts
new file mode 100644
index 00000000..2a6611fc
--- /dev/null
+++ b/app/services/general-consent.service.ts
@@ -0,0 +1,23 @@
+import * as base from './_crud-base'
+
+const path = '/api/v1/general-consent'
+
+export function create(data: any) {
+ return base.create(path, data)
+}
+
+export function getList(params: any = null) {
+ return base.getList(path, params)
+}
+
+export function getDetail(id: number | string) {
+ return base.getDetail(path, id)
+}
+
+export function update(id: number | string, data: any) {
+ return base.update(path, id, data)
+}
+
+export function remove(id: number | string) {
+ return base.remove(path, id)
+}
diff --git a/app/services/generate-file.service.ts b/app/services/generate-file.service.ts
new file mode 100644
index 00000000..5849e3d0
--- /dev/null
+++ b/app/services/generate-file.service.ts
@@ -0,0 +1,15 @@
+import * as base from './_crud-base'
+
+const path = '/api/v1/generate-file'
+
+export function create(data: any) {
+ return base.create(path, data)
+}
+
+export function getList(params: any = null) {
+ return base.getList(path, params)
+}
+
+export function getDetail(id: number | string) {
+ return base.getDetail(path, id)
+}
diff --git a/app/services/installation.service.ts b/app/services/installation.service.ts
index 1f1975f5..8cb3e266 100644
--- a/app/services/installation.service.ts
+++ b/app/services/installation.service.ts
@@ -27,13 +27,15 @@ 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: Installation) => ({
- value: item.id ? Number(item.id) : item.code,
+ value: useCodeAsValue ? item.code
+ : item.id ? Number(item.id)
+ : item.code,
label: item.name,
}))
}
diff --git a/app/services/medicine-form.service.ts b/app/services/medicine-form.service.ts
new file mode 100644
index 00000000..21874f5c
--- /dev/null
+++ b/app/services/medicine-form.service.ts
@@ -0,0 +1,41 @@
+// Base
+import * as base from './_crud-base'
+
+// Types
+import type { MedicineForm } from '~/models/medicine-form'
+
+const path = '/api/v1/medicine-form'
+const name = 'medicine-form'
+
+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): 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: MedicineForm) => ({
+ value: item.code,
+ label: item.name,
+ }))
+ }
+ return data
+}
diff --git a/app/services/prescription.service.ts b/app/services/prescription.service.ts
index 150357ab..7bdc9f51 100644
--- a/app/services/prescription.service.ts
+++ b/app/services/prescription.service.ts
@@ -1,4 +1,5 @@
import * as base from './_crud-base'
+import { xfetch } from '~/composables/useXfetch'
const path = '/api/v1/prescription'
const name = 'prescription'
@@ -22,3 +23,16 @@ export function update(id: number | string, data: any) {
export function remove(id: number | string) {
return base.remove(path, id)
}
+
+export async function submit(id: number) {
+ try {
+ const resp = await xfetch(`${path}/${id}/submit`, 'PATCH')
+ const result: any = {}
+ result.success = resp.success
+ result.body = (resp.body as Record) || {}
+ return result
+ } catch (error) {
+ console.error(`Error submitting ${name}:`, error)
+ throw new Error(`Failed to submit ${name}`)
+ }
+}
diff --git a/app/services/specialist.service.ts b/app/services/specialist.service.ts
index d4c81b5c..7786fcc2 100644
--- a/app/services/specialist.service.ts
+++ b/app/services/specialist.service.ts
@@ -36,7 +36,7 @@ export async function getValueLabelList(params: any = null, useCodeAsValue = fal
data = resultData.map((item: Specialist) => ({
value: useCodeAsValue ? item.code
: item.id ? Number(item.id)
- : item.id,
+ : item.code,
label: item.name,
parent: item.unit_id ? Number(item.unit_id) : null,
}))
diff --git a/app/services/subspecialist.service.ts b/app/services/subspecialist.service.ts
index f13c715f..9425067b 100644
--- a/app/services/subspecialist.service.ts
+++ b/app/services/subspecialist.service.ts
@@ -33,9 +33,9 @@ export async function getValueLabelList(params: any = null, useCodeAsValue = fal
if (result.success) {
const resultData = result.body?.data || []
data = resultData.map((item: Subspecialist) => ({
- value: useCodeAsValue ? item.code
+ value: useCodeAsValue ? item.code
: item.id ? Number(item.id)
- : item.id,
+ : item.code,
label: item.name,
parent: item.specialist_id ? Number(item.specialist_id) : null,
}))
diff --git a/app/services/supporting-document.service.ts b/app/services/supporting-document.service.ts
new file mode 100644
index 00000000..23f94820
--- /dev/null
+++ b/app/services/supporting-document.service.ts
@@ -0,0 +1,56 @@
+// Base
+import * as base from './_crud-base'
+
+// Constants
+import { uploadCode, type UploadCodeKey } from '~/lib/constants'
+
+const path = '/api/v1/encounter-document'
+const create_path = '/api/v1/upload-file'
+const name = 'encounter-document'
+
+export function create(data: any) {
+ return base.create(create_path, data, name)
+}
+
+export function getList(params: any = null) {
+ return base.getList(path, params, name)
+}
+
+export function getDetail(id: number | string, params?: any) {
+ return base.getDetail(path, id, name, params)
+}
+
+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 uploadAttachment(file: File, userId: number, key: UploadCodeKey) {
+ try {
+ const resolvedKey = uploadCode[key]
+ if (!resolvedKey) {
+ throw new Error(`Invalid upload code key: ${key}`)
+ }
+
+ // siapkan form-data body
+ const formData = new FormData()
+ formData.append('code', resolvedKey)
+ formData.append('content', file)
+
+ // kirim via xfetch
+ const resp = await xfetch(`${path}/${userId}/upload`, 'POST', formData)
+
+ // struktur hasil sama seperti patchPatient
+ const result: any = {}
+ result.success = resp.success
+ result.body = (resp.body as Record) || {}
+
+ return result
+ } catch (error) {
+ console.error('Error uploading attachment:', error)
+ throw new Error('Failed to upload attachment')
+ }
+}
diff --git a/app/services/unit.service.ts b/app/services/unit.service.ts
index 402504b6..948ac6b6 100644
--- a/app/services/unit.service.ts
+++ b/app/services/unit.service.ts
@@ -35,7 +35,7 @@ export async function getValueLabelList(params: any = null, useCodeAsValue = fal
data = resultData.map((item: Unit) => ({
value: useCodeAsValue ? item.code
: item.id ? Number(item.id)
- : item.id,
+ : item.code,
label: item.name,
}))
}
diff --git a/app/services/vclaim-control-letter.service.ts b/app/services/vclaim-control-letter.service.ts
index 007e91c5..2c638c2e 100644
--- a/app/services/vclaim-control-letter.service.ts
+++ b/app/services/vclaim-control-letter.service.ts
@@ -15,11 +15,11 @@ export function getList(params: any = null) {
if (params?.letterNumber && params.mode === 'by-sep') {
url += `/${params.letterNumber}`
}
- if (params?.letterNumber && params.mode === 'by-schedule') {
- url += `/jadwalDokter?jeniskontrol=${params.controlType}&kodepoli=${params.poliCode}&tanggalkontrol=${params.controlDate}`
+ if (params?.controlDate && params.mode === 'by-schedule') {
+ url += `/jadwalDokter?jeniskontrol=${params.controlType}&kodepoli=${params.polyCode}&tanggalkontrol=${params.controlDate}`
delete params.controlType
- delete params.poliCode
delete params.controlDate
+ delete params.polyCode
}
if (params) {
delete params.letterNumber
diff --git a/app/services/vclaim-monitoring-visit.service.ts b/app/services/vclaim-monitoring-visit.service.ts
index 0c5da64e..6004673e 100644
--- a/app/services/vclaim-monitoring-visit.service.ts
+++ b/app/services/vclaim-monitoring-visit.service.ts
@@ -4,68 +4,16 @@ import * as base from './_crud-base'
const path = '/api/vclaim/v1/monitoring/visit'
const name = 'monitoring-visit'
-const dummyResponse = {
- metaData: {
- code: '200',
- message: 'Sukses',
- },
- response: {
- sep: [
- {
- diagnosa: 'K65.0',
- jnsPelayanan: 'R.Inap',
- kelasRawat: '2',
- nama: 'HANIF ABDURRAHMAN',
- noKartu: '0001819122189',
- noSep: '0301R00110170000004',
- noRujukan: '0301U01108180200084',
- poli: null,
- tglPlgSep: '2017-10-03',
- tglSep: '2017-10-01',
- },
- {
- diagnosa: 'I50.0',
- jnsPelayanan: 'R.Inap',
- kelasRawat: '3',
- nama: 'ASRIZAL',
- noKartu: '0002283324674',
- noSep: '0301R00110170000005',
- noRujukan: '0301U01108180200184',
- poli: null,
- tglPlgSep: '2017-10-10',
- tglSep: '2017-10-01',
- },
- ],
- },
-}
-
export async function getList(params: any = null) {
- try {
- let url = path
- if (params?.date && params.serviceType) {
- url += `/${params.date}/${params.serviceType}`
- }
- if (params) {
- delete params.date
- delete params.serviceType
- }
- const resp = await base.getList(url, params, name)
-
- // Jika success false, return dummy response
- if (!resp.success || !resp.body?.response) {
- return {
- success: true,
- body: dummyResponse,
- }
- }
-
- return resp
- } catch (error) {
- // Jika terjadi error, return dummy response
- console.error(`Error fetching ${name}s:`, error)
- return {
- success: true,
- body: dummyResponse,
- }
+ let url = path
+ if (params?.date && params.serviceType) {
+ url += `/${params.date}/${params.serviceType}`
}
+ if (params) {
+ delete params.date
+ delete params.serviceType
+ }
+ const resp = await base.getList(url, params, name)
+
+ return resp
}
diff --git a/app/services/vclaim-sep.service.ts b/app/services/vclaim-sep.service.ts
index fdccc9c4..93224a4a 100644
--- a/app/services/vclaim-sep.service.ts
+++ b/app/services/vclaim-sep.service.ts
@@ -7,6 +7,9 @@ import type { IntegrationBpjsFormData } from '~/schemas/integration-bpjs.schema'
const path = '/api/vclaim-swagger/sep'
const name = 'sep'
+// TODO: temporary destinationClinic
+const destinationClinic = '1323R001'
+
export function create(data: any) {
return base.create(path, data, name)
}
@@ -20,8 +23,19 @@ export function getList(params: any = null) {
return base.getList(url, params, name)
}
+export function getDetail(id: number | string) {
+ return base.getDetail(path, id, name)
+}
+
+export function remove(payload: any) {
+ const url = `${path}`
+ return base.removeCustom(url, payload, name)
+}
+
export function makeSepData(
data: IntegrationBpjsFormData & {
+ userName: string
+ polyCode?: string
referralFrom?: string
referralTo?: string
referralLetterDate?: string
@@ -31,13 +45,13 @@ export function makeSepData(
const content = {
noKartu: data.cardNumber || '',
tglSep: data.sepDate,
- ppkPelayanan: data.fromClinic || '',
- jnsPelayanan: data.admissionType ? String(data.admissionType) : '1',
+ ppkPelayanan: destinationClinic || data.fromClinic || '',
+ jnsPelayanan: data.serviceType ? String(data.serviceType) : '2',
noMR: data.medicalRecordNumber || '',
catatan: data.note || '',
diagAwal: data.initialDiagnosis || '',
poli: {
- tujuan: data.destinationClinic || '',
+ tujuan: data.polyCode || '',
eksekutif: data.clinicExcecutive === 'yes' ? '1' : '0',
},
cob: {
@@ -46,19 +60,21 @@ export function makeSepData(
katarak: {
katarak: data.cataract === 'yes' ? '1' : '0',
},
- tujuanKunj: data.purposeOfVisit || '',
+ tujuanKunj: data.purposeOfVisit || '0',
flagProcedure: data.procedureType || '',
kdPenunjang: data.supportCode || '',
assesmentPel: data.serviceAssessment || '',
skdp: {
noSurat: ['3'].includes(data.admissionType) ? data.referralLetterNumber : '',
- kodeDPJP: ['3'].includes(data.admissionType)? data.attendingDoctor : '',
+ kodeDPJP: ['3'].includes(data.admissionType) ? data.attendingDoctor : '',
},
rujukan: {
- asalRujukan: ['2'].includes(data.admissionType) ? data?.referralFrom || '' : '',
- tglRujukan: ['2'].includes(data.admissionType) ? data?.referralLetterDate || '' : '',
- noRujukan: ['2'].includes(data.admissionType) ? data?.referralLetterNumber || '' : '',
- ppkRujukan: ['2'].includes(data.admissionType) ? data?.referralTo || '' : '',
+ // Handle referral data for admissionType !== '3'
+ // asalRujukan: 1 = Faskes 1, 2 = Faskes RS
+ asalRujukan: !['3'].includes(data.admissionType) ? data?.referralFrom || '' : '',
+ tglRujukan: !['3'].includes(data.admissionType) ? data?.referralLetterDate || '' : '',
+ noRujukan: !['3'].includes(data.admissionType) ? data?.referralLetterNumber || '' : '',
+ ppkRujukan: !['3'].includes(data.admissionType) ? data?.referralTo || '' : '',
},
klsRawat: {
klsRawatHak: data.classLevel || '',
@@ -68,7 +84,7 @@ export function makeSepData(
},
dpjpLayan: data.attendingDoctor || '',
noTelp: data.phoneNumber || '',
- user: data.patientName || '',
+ user: data.userName || '',
jaminan: {
lakaLantas: data.trafficAccident || '0',
noLP: data.lpNumber || '',
@@ -93,3 +109,14 @@ export function makeSepData(
},
}
}
+
+export function makeSepDataForRemove(data: any) {
+ return {
+ request: {
+ t_sep: {
+ noSep: data.sepNumber,
+ user: data.userName,
+ },
+ },
+ }
+}
diff --git a/app/stores/user.ts b/app/stores/user.ts
index 97bd4665..f7fe9fb5 100644
--- a/app/stores/user.ts
+++ b/app/stores/user.ts
@@ -1,19 +1,21 @@
export const useUserStore = defineStore(
'user',
() => {
+ // should be auth type
const user = ref(null)
// const token = useCookie('authentication')
const isAuthenticated = computed(() => !!user.value)
- const userRole = computed(() => {
+ const userRoles = computed(() => {
return user.value?.roles || []
- // return roles.map((input: string) => {
- // const parts = input.split('|')
- // return parts.length > 1 ? parts[1]: parts[0]
- // })
})
+ const userActiveRole = ref('') // watched user failed, so create a ref here
+ // computed(() => {
+ // // return user.value?.activeRole || ''
+ // })
+
const login = async (userData: any) => {
user.value = userData
}
@@ -25,10 +27,11 @@ export const useUserStore = defineStore(
const setActiveRole = (role: string) => {
if (user.value && user.value.roles.includes(role)) {
user.value.activeRole = role
+ userActiveRole.value = role
}
}
- const getActiveRole = () => {
+ const getActiveRole = (): string | undefined => {
if (user.value?.activeRole) {
return user.value.activeRole
}
@@ -36,12 +39,14 @@ export const useUserStore = defineStore(
user.value.activeRole = user.value.roles[0]
return user.value.activeRole
}
+ return undefined
}
return {
user,
isAuthenticated,
- userRole,
+ userRoles,
+ userActiveRole,
login,
logout,
setActiveRole,
diff --git a/package.json b/package.json
index d415abde..08b0d87c 100644
--- a/package.json
+++ b/package.json
@@ -23,10 +23,12 @@
"date-fns": "^4.1.0",
"embla-carousel": "^8.5.2",
"embla-carousel-vue": "^8.5.2",
+ "file-saver": "^2.0.5",
"h3": "^1.15.4",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
- "tailwindcss-animate": "^1.0.7"
+ "tailwindcss-animate": "^1.0.7",
+ "xlsx": "^0.18.5"
},
"devDependencies": {
"@antfu/eslint-config": "^4.10.1",
@@ -36,6 +38,7 @@
"@nuxtjs/color-mode": "^3.5.2",
"@nuxtjs/tailwindcss": "6.14.0",
"@pinia/nuxt": "^0.11.2",
+ "@types/file-saver": "^2.0.7",
"@unocss/eslint-plugin": "^66.0.0",
"@unocss/nuxt": "^66.0.0",
"@vee-validate/zod": "^4.15.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5ae777c3..565ff313 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -32,6 +32,9 @@ dependencies:
embla-carousel-vue:
specifier: ^8.5.2
version: 8.6.0(vue@3.5.21)
+ file-saver:
+ specifier: ^2.0.5
+ version: 2.0.5
h3:
specifier: ^1.15.4
version: 1.15.4
@@ -44,6 +47,9 @@ dependencies:
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.4.17)
+ xlsx:
+ specifier: ^0.18.5
+ version: 0.18.5
devDependencies:
'@antfu/eslint-config':
@@ -67,6 +73,9 @@ devDependencies:
'@pinia/nuxt':
specifier: ^0.11.2
version: 0.11.2(pinia@3.0.3)
+ '@types/file-saver':
+ specifier: ^2.0.7
+ version: 2.0.7
'@unocss/eslint-plugin':
specifier: ^66.0.0
version: 66.5.1(eslint@9.36.0)(typescript@5.9.2)
@@ -3182,6 +3191,10 @@ packages:
/@types/estree@1.0.8:
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+ /@types/file-saver@2.0.7:
+ resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
+ dev: true
+
/@types/geojson@7946.0.16:
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
dev: false
@@ -4588,6 +4601,11 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ /adler-32@1.3.1:
+ resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
+ engines: {node: '>=0.8'}
+ dev: false
+
/agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
@@ -4796,12 +4814,12 @@ packages:
dev: true
optional: true
- /my-ui64-js@1.5.1:
+ /base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true
- /my-uiline-browser-mapping@2.8.6:
- resolution: {integrity: sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==}
+ /baseline-browser-mapping@2.8.29:
+ resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==}
hasBin: true
dev: true
@@ -4845,7 +4863,7 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
- baseline-browser-mapping: 2.8.6
+ baseline-browser-mapping: 2.8.29
caniuse-lite: 1.0.30001743
electron-to-chromium: 1.5.222
node-releases: 2.0.21
@@ -4966,6 +4984,14 @@ packages:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
dev: true
+ /cfb@1.2.2:
+ resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
+ engines: {node: '>=0.8'}
+ dependencies:
+ adler-32: 1.3.1
+ crc-32: 1.2.2
+ dev: false
+
/chai@5.3.3:
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
engines: {node: '>=18'}
@@ -5084,6 +5110,11 @@ packages:
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
dev: true
+ /codepage@1.15.0:
+ resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
+ engines: {node: '>=0.8'}
+ dev: false
+
/color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -5244,7 +5275,6 @@ packages:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
hasBin: true
- dev: true
/crc32-stream@6.0.0:
resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==}
@@ -6754,6 +6784,10 @@ packages:
flat-cache: 4.0.1
dev: true
+ /file-saver@2.0.5:
+ resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
+ dev: false
+
/file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
dev: true
@@ -6814,6 +6848,11 @@ packages:
engines: {node: '>=0.4.x'}
dev: true
+ /frac@1.1.2:
+ resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
+ engines: {node: '>=0.8'}
+ dev: false
+
/fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
dev: true
@@ -10219,6 +10258,13 @@ packages:
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
engines: {node: '>=0.10.0'}
+ /ssf@0.11.2:
+ resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
+ engines: {node: '>=0.8'}
+ dependencies:
+ frac: 1.1.2
+ dev: false
+
/stable-hash-x@0.2.0:
resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==}
engines: {node: '>=12.0.0'}
@@ -11603,11 +11649,21 @@ packages:
stackback: 0.0.2
dev: true
+ /wmf@1.0.2:
+ resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
+ engines: {node: '>=0.8'}
+ dev: false
+
/word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
dev: true
+ /word@0.3.0:
+ resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
+ engines: {node: '>=0.8'}
+ dev: false
+
/wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -11648,6 +11704,20 @@ packages:
is-wsl: 3.1.0
dev: true
+ /xlsx@0.18.5:
+ resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
+ engines: {node: '>=0.8'}
+ hasBin: true
+ dependencies:
+ adler-32: 1.3.1
+ cfb: 1.2.2
+ codepage: 1.15.0
+ crc-32: 1.2.2
+ ssf: 0.11.2
+ wmf: 1.0.2
+ word: 0.3.0
+ dev: false
+
/xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'}
diff --git a/public/bpjs.png b/public/bpjs.png
new file mode 100644
index 00000000..781e19b0
Binary files /dev/null and b/public/bpjs.png differ
diff --git a/public/side-menu-items/emp-doc.json b/public/side-menu-items/emp-doc.json
index b7ecb430..292d8c5d 100644
--- a/public/side-menu-items/emp-doc.json
+++ b/public/side-menu-items/emp-doc.json
@@ -12,14 +12,14 @@
"icon": "i-lucide-stethoscope",
"children": [
{
- "title": "Triase",
+ "title": "Kunjungan",
"icon": "i-lucide-stethoscope",
- "link": "/outpatient/encounter"
+ "link": "/ambulatory/encounter"
},
{
"title": "Konsultasi",
"icon": "i-lucide-building-2",
- "link": "/outpatient/consultation"
+ "link": "/ambulatory/consultation"
}
]
},
@@ -44,22 +44,6 @@
}
]
},
- {
- "title": "Rehabilitasi Medik",
- "icon": "i-lucide-bike",
- "children": [
- {
- "title": "Kunjungan",
- "icon": "i-lucide-building-2",
- "link": "/rehab/encounter"
- },
- {
- "title": "Konsultasi",
- "icon": "i-lucide-building-2",
- "link": "/rehab/consultation"
- }
- ]
- },
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
diff --git a/public/side-menu-items/emp-mid.json b/public/side-menu-items/emp-mid.json
index 80eeee7c..1c6e5aef 100644
--- a/public/side-menu-items/emp-mid.json
+++ b/public/side-menu-items/emp-mid.json
@@ -10,7 +10,7 @@
{
"title": "Rawat Jalan",
"icon": "i-lucide-stethoscope",
- "link": "/outpatient/encounter"
+ "link": "/ambulatory/encounter"
},
{
"title": "IGD",
diff --git a/public/side-menu-items/emp-nur.json b/public/side-menu-items/emp-nur.json
index 1c993144..f940219e 100644
--- a/public/side-menu-items/emp-nur.json
+++ b/public/side-menu-items/emp-nur.json
@@ -13,11 +13,11 @@
"children": [
{
"title": "Antrian Poliklinik",
- "link": "/outpatient/encounter-queue"
+ "link": "/ambulatory/encounter-queue"
},
{
"title": "Kunjungan",
- "link": "/outpatient/encounter"
+ "link": "/ambulatory/encounter"
}
]
},
@@ -35,21 +35,6 @@
}
]
},
- {
- "title": "Rehabilitasi Medik",
- "icon": "i-lucide-bike",
- "link": "/rehab",
- "children": [
- {
- "title": "Antrean Poliklinik",
- "link": "/rehab/encounter-queue"
- },
- {
- "title": "Kunjungan",
- "link": "/rehab/encounter"
- }
- ]
- },
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
@@ -63,12 +48,12 @@
{
"title": "Kemoterapi",
"icon": "i-lucide-droplets",
- "link": "/outpation-action/chemotherapy"
+ "link": "/chemotherapy"
},
{
"title": "Hemofilia",
"icon": "i-lucide-droplet-off",
- "link": "/outpation-action/hemophilia"
+ "link": "/hemophilia"
}
]
},
@@ -78,17 +63,17 @@
{
"title": "Thalasemi",
"icon": "i-lucide-baby",
- "link": "/children-action/thalasemia"
+ "link": "/thalasemia"
},
{
"title": "Echocardiography",
"icon": "i-lucide-baby",
- "link": "/children-action/echocardiography"
+ "link": "/echocardiography"
},
{
"title": "Spirometri",
"icon": "i-lucide-baby",
- "link": "/children-action/spirometry"
+ "link": "/spirometry"
}
]
}
diff --git a/public/side-menu-items/emp-nut.json b/public/side-menu-items/emp-nut.json
index ef71282a..43f3e503 100644
--- a/public/side-menu-items/emp-nut.json
+++ b/public/side-menu-items/emp-nut.json
@@ -10,7 +10,7 @@
{
"title": "Rawat Jalan",
"icon": "i-lucide-stethoscope",
- "link": "/outpatient/encounter"
+ "link": "/ambulatory/encounter"
},
{
"title": "IGD",
diff --git a/public/side-menu-items/emp-reg.json b/public/side-menu-items/emp-reg.json
index 86d6d4ed..df874e65 100644
--- a/public/side-menu-items/emp-reg.json
+++ b/public/side-menu-items/emp-reg.json
@@ -13,11 +13,11 @@
"children": [
{
"title": "Antrian Pendaftaran",
- "link": "/outpatient/registration-queue"
+ "link": "/ambulatory/registration-queue"
},
{
"title": "Kunjungan",
- "link": "/outpatient/encounter"
+ "link": "/ambulatory/encounter"
}
]
},
@@ -26,21 +26,6 @@
"icon": "i-lucide-zap",
"link": "/emergency/encounter"
},
- {
- "title": "Rehabilitasi Medik",
- "icon": "i-lucide-bike",
- "link": "/rehab",
- "children": [
- {
- "title": "Antrean Pendaftaran",
- "link": "/rehab/registration-queue"
- },
- {
- "title": "Kunjungan",
- "link": "/rehab/encounter"
- }
- ]
- },
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
diff --git a/public/side-menu-items/sys.json b/public/side-menu-items/sys.json
new file mode 100644
index 00000000..996c2a6b
--- /dev/null
+++ b/public/side-menu-items/sys.json
@@ -0,0 +1,350 @@
+[
+ {
+ "heading": "Menu Utama",
+ "items": [
+ {
+ "title": "Dashboard",
+ "icon": "i-lucide-home",
+ "link": "/"
+ },
+ {
+ "title": "Rawat Jalan",
+ "icon": "i-lucide-stethoscope",
+ "children": [
+ {
+ "title": "Antrian Pendaftaran",
+ "link": "/ambulatory/registration-queue"
+ },
+ {
+ "title": "Antrian Poliklinik",
+ "link": "/ambulatory/encounter-queue"
+ },
+ {
+ "title": "Kunjungan",
+ "link": "/ambulatory/encounter"
+ },
+ {
+ "title": "Konsultasi",
+ "link": "/ambulatory/consultation"
+ }
+ ]
+ },
+ {
+ "title": "IGD",
+ "icon": "i-lucide-zap",
+ "children": [
+ {
+ "title": "Triase",
+ "link": "/emergency/triage"
+ },
+ {
+ "title": "Kunjungan",
+ "link": "/emergency/encounter"
+ },
+ {
+ "title": "Konsultasi",
+ "link": "/emergency/consultation"
+ }
+ ]
+ },
+ {
+ "title": "Rawat Inap",
+ "icon": "i-lucide-building-2",
+ "children": [
+ {
+ "title": "Permintaan",
+ "link": "/inpatient/request"
+ },
+ {
+ "title": "Kunjungan",
+ "link": "/inpatient/encounter"
+ },
+ {
+ "title": "Konsultasi",
+ "link": "/inpatient/consultation"
+ }
+ ]
+ },
+ {
+ "title": "Obat - Order",
+ "icon": "i-lucide-briefcase-medical",
+ "children": [
+ {
+ "title": "Permintaan",
+ "link": "/medication/order"
+ },
+ {
+ "title": "Standing Order",
+ "link": "/medication/standing-order"
+ }
+ ]
+ },
+ {
+ "title": "Radiologi - Order",
+ "icon": "i-lucide-radio",
+ "link": "/radiology-order"
+ },
+ {
+ "title": "Lab - Order",
+ "icon": "i-lucide-microscope",
+ "link": "/cp-lab-order"
+ },
+ {
+ "title": "Lab Mikro - Order",
+ "icon": "i-lucide-microscope",
+ "link": "/micro-lab-order"
+ },
+ {
+ "title": "Lab PA - Order",
+ "icon": "i-lucide-microscope",
+ "link": "/ap-lab-order"
+ },
+ {
+ "title": "Gizi",
+ "icon": "i-lucide-egg-fried",
+ "link": "/nutrition-order"
+ },
+ {
+ "title": "Pembayaran",
+ "icon": "i-lucide-banknote-arrow-up",
+ "link": "/payment"
+ }
+ ]
+ },
+ {
+ "heading": "Ruang Tindakan Rajal",
+ "items": [
+ {
+ "title": "Kemoterapi",
+ "icon": "i-lucide-droplets",
+ "link": "/outpation-action/cemotherapy"
+ },
+ {
+ "title": "Hemofilia",
+ "icon": "i-lucide-droplet-off",
+ "link": "/outpation-action/hemophilia"
+ }
+ ]
+ },
+ {
+ "heading": "Ruang Tindakan Anak",
+ "items": [
+ {
+ "title": "Thalasemi",
+ "icon": "i-lucide-baby",
+ "link": "/children-action/thalasemia"
+ },
+ {
+ "title": "Echocardiography",
+ "icon": "i-lucide-baby",
+ "link": "/children-action/echocardiography"
+ },
+ {
+ "title": "Spirometri",
+ "icon": "i-lucide-baby",
+ "link": "/children-action/spirometry"
+ }
+ ]
+ },
+ {
+ "heading": "Client",
+ "items": [
+ {
+ "title": "Pasien",
+ "icon": "i-lucide-users",
+ "link": "/client/patient"
+ },
+ {
+ "title": "Rekam Medis",
+ "icon": "i-lucide-file-text",
+ "link": "/client/medical-record"
+ }
+ ]
+ },
+ {
+ "heading": "Integrasi",
+ "items": [
+ {
+ "title": "BPJS",
+ "icon": "i-lucide-circuit-board",
+ "children": [
+ {
+ "title": "SEP",
+ "icon": "i-lucide-circuit-board",
+ "link": "/integration/bpjs-vclaim/sep"
+ },
+ {
+ "title": "Peserta",
+ "icon": "i-lucide-circuit-board",
+ "link": "/integration/bpjs-vclaim/member"
+ },
+ {
+ "title": "Surat Kontrol",
+ "icon": "i-lucide-circuit-board",
+ "link": "/integration/bpjs-vclaim/control-letter"
+ }
+ ]
+ },
+ {
+ "title": "SATUSEHAT",
+ "icon": "i-lucide-database",
+ "link": "/integration/satusehat"
+ }
+ ]
+ },
+ {
+ "heading": "Source",
+ "items": [
+ {
+ "title": "Peralatan dan Perlengkapan",
+ "icon": "i-lucide-layout-dashboard",
+ "children": [
+ {
+ "title": "Obat",
+ "link": "/tools-equipment-src/medicine"
+ },
+ {
+ "title": "Peralatan",
+ "link": "/tools-equipment-src/tools"
+ },
+ {
+ "title": "Perlengkapan (BMHP)",
+ "link": "/tools-equipment-src/equipment"
+ },
+ {
+ "title": "Metode Obat",
+ "link": "/tools-equipment-src/medicine-method"
+ },
+ {
+ "title": "Jenis Obat",
+ "link": "/tools-equipment-src/medicine-type"
+ },
+ {
+ "title": "Sediaan Obat",
+ "link": "/tools-equipment-src/medicine-form"
+ }
+ ]
+ },
+ {
+ "title": "Pengguna",
+ "icon": "i-lucide-user",
+ "children": [
+ {
+ "title": "Pegawai",
+ "link": "/human-src/employee"
+ },
+ {
+ "title": "PPDS",
+ "link": "/human-src/specialist-intern"
+ }
+ ]
+ },
+ {
+ "title": "Pemeriksaan Penunjang",
+ "icon": "i-lucide-layout-list",
+ "children": [
+ {
+ "title": "Checkup",
+ "link": "/mcu-src/mcu"
+ },
+ {
+ "title": "Prosedur",
+ "link": "/mcu-src/procedure"
+ },
+ {
+ "title": "Diagnosis",
+ "link": "/mcu-src/diagnose"
+ },
+ {
+ "title": "Medical Action",
+ "link": "/mcu-src/medical-action"
+ }
+ ]
+ },
+ {
+ "title": "Infrastruktur",
+ "icon": "i-lucide-layout-list",
+ "children": [
+ {
+ "title": "Kasur",
+ "link": "/infra-src/bed"
+ },
+ {
+ "title": "Kamar",
+ "link": "/infra-src/chamber"
+ },
+ {
+ "title": "Ruang",
+ "link": "/infra-src/room"
+ },
+ {
+ "title": "Depo",
+ "link": "/infra-src/warehouse"
+ },
+ {
+ "title": "Lantai",
+ "link": "/infra-src/floor"
+ },
+ {
+ "title": "Gedung",
+ "link": "/infra-src/building"
+ },
+ {
+ "title": "Counter",
+ "link": "/infra-src/counter"
+ },
+ {
+ "title": "Public Screen (Big Screen)",
+ "link": "/infra-src/public-screen"
+ }
+ ]
+ },
+ {
+ "title": "Organisasi",
+ "icon": "i-lucide-network",
+ "children": [
+ {
+ "title": "Divisi",
+ "link": "/org-src/division"
+ },
+ {
+ "title": "Instalasi",
+ "link": "/org-src/installation"
+ },
+ {
+ "title": "Unit",
+ "link": "/org-src/unit"
+ },
+ {
+ "title": "Spesialis",
+ "link": "/org-src/specialist"
+ },
+ {
+ "title": "Sub Spesialis",
+ "link": "/org-src/subspecialist"
+ }
+ ]
+ },
+ {
+ "title": "Umum",
+ "icon": "i-lucide-airplay",
+ "children": [
+ {
+ "title": "Uom",
+ "link": "/common/uom"
+ }
+ ]
+ },
+ {
+ "title": "Keuangan",
+ "icon": "i-lucide-airplay",
+ "children": [
+ {
+ "title": "Item & Pricing",
+ "link": "/common/item"
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/public/side-menu-items/system.json b/public/side-menu-items/system.json
index 5bf62f36..c46775aa 100644
--- a/public/side-menu-items/system.json
+++ b/public/side-menu-items/system.json
@@ -13,19 +13,19 @@
"children": [
{
"title": "Antrian Pendaftaran",
- "link": "/outpatient/registration-queue"
+ "link": "/ambulatory/registration-queue"
},
{
"title": "Antrian Poliklinik",
- "link": "/outpatient/encounter-queue"
+ "link": "/ambulatory/encounter-queue"
},
{
"title": "Kunjungan",
- "link": "/outpatient/encounter"
+ "link": "/ambulatory/encounter"
},
{
"title": "Konsultasi",
- "link": "/outpatient/consultation"
+ "link": "/ambulatory/consultation"
}
]
},
@@ -193,17 +193,17 @@
{
"title": "SEP",
"icon": "i-lucide-circuit-board",
- "link": "/integration/bpjs/sep"
+ "link": "/integration/bpjs-vclaim/sep"
},
{
"title": "Peserta",
"icon": "i-lucide-circuit-board",
- "link": "/integration/bpjs/member"
+ "link": "/integration/bpjs-vclaim/member"
},
{
"title": "Surat Kontrol",
"icon": "i-lucide-circuit-board",
- "link": "/integration/bpjs/control-letter"
+ "link": "/integration/bpjs-vclaim/control-letter"
}
]
},
@@ -240,6 +240,10 @@
{
"title": "Jenis Obat",
"link": "/tools-equipment-src/medicine-type"
+ },
+ {
+ "title": "Sediaan Obat",
+ "link": "/tools-equipment-src/medicine-form"
}
]
},
diff --git a/server/api/[...req].ts b/server/api/[...req].ts
index 5948eda6..c58bf7d6 100644
--- a/server/api/[...req].ts
+++ b/server/api/[...req].ts
@@ -1,7 +1,7 @@
import { defineEventHandler, getCookie, getRequestHeaders, getRequestURL, readBody } from 'h3'
export default defineEventHandler(async (event) => {
- const { method } = event.node.req
+ const { method } = event.node.req as any
const headers = getRequestHeaders(event)
const url = getRequestURL(event)
const config = useRuntimeConfig()
@@ -36,7 +36,7 @@ export default defineEventHandler(async (event) => {
}
let body: any
- if (['POST', 'PATCH'].includes(method!)) {
+ if (['POST', 'PATCH', 'PUT', 'DELETE'].includes(method)) {
if (headers['content-type']?.includes('multipart/form-data')) {
body = await readBody(event)
} else {
|