Merge branch 'dev' into feat/micro-lab-order-50
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns='http://www.w3.org/2000/svg' width='250' height='30' viewBox='0 0 1000 120'><rect fill='#000000' width='1000' height='120'/><g fill='none' stroke='#222' stroke-width='10' stroke-opacity='1'><path d='M-500 75c0 0 125-30 250-30S0 75 0 75s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 45c0 0 125-30 250-30S0 45 0 45s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 105c0 0 125-30 250-30S0 105 0 105s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 15c0 0 125-30 250-30S0 15 0 15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500-15c0 0 125-30 250-30S0-15 0-15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/><path d='M-500 135c0 0 125-30 250-30S0 135 0 135s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/></g></svg>
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
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'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { genBase } from '~/models/_base'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: any
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
excludeFields?: string[]
|
||||||
|
isReadonly?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', val: any): void
|
||||||
|
(e: 'submit', val: any): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Setup form
|
||||||
|
const {
|
||||||
|
validate: _validate,
|
||||||
|
defineField,
|
||||||
|
handleSubmit,
|
||||||
|
errors,
|
||||||
|
values,
|
||||||
|
} = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: props.modelValue,
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(values, (val) => emit('update:modelValue', val), { deep: true })
|
||||||
|
|
||||||
|
const [subjective, subjectiveAttrs] = defineField('subjective')
|
||||||
|
const [objective, objectiveAttrs] = defineField('objective')
|
||||||
|
const [assesment, assesmentAttrs] = defineField('assesment')
|
||||||
|
const [plan, planAttrs] = defineField('plan')
|
||||||
|
const [review, reviewAttrs] = defineField('review')
|
||||||
|
|
||||||
|
const validate = async () => {
|
||||||
|
const result = await _validate()
|
||||||
|
console.log('Component validate() result:', result)
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
data: result.values,
|
||||||
|
errors: result.errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ validate })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="my-2">
|
||||||
|
<h1 class="font-semibold">Data Petugas</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-2 rounded-md border border-slate-300 p-4">
|
||||||
|
<Block :colCount="2">
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>PPA</Label>
|
||||||
|
<Field>
|
||||||
|
<Input disabled />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Nama PPA</Label>
|
||||||
|
<Field>
|
||||||
|
<Input disabled />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-2">
|
||||||
|
<h1 class="font-semibold">Data S.O.A.P</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-2 rounded-md border border-slate-300 p-4">
|
||||||
|
<Block :colCount="2">
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Subjektif</Label>
|
||||||
|
<Field>
|
||||||
|
<Textarea
|
||||||
|
v-model="subjective"
|
||||||
|
v-bind="subjectiveAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Objektif</Label>
|
||||||
|
<Field>
|
||||||
|
<Textarea
|
||||||
|
v-model="objective"
|
||||||
|
v-bind="objectiveAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<Block :colCount="2">
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Assesmen</Label>
|
||||||
|
<Field>
|
||||||
|
<Textarea
|
||||||
|
v-model="assesment"
|
||||||
|
v-bind="assesmentAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Plan</Label>
|
||||||
|
<Field>
|
||||||
|
<Textarea
|
||||||
|
v-model="plan"
|
||||||
|
v-bind="planAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<Block>
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Review</Label>
|
||||||
|
<Field>
|
||||||
|
<Textarea
|
||||||
|
v-model="review"
|
||||||
|
v-bind="reviewAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
<Separator class="mt-8" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<!-- FIXME: pindahkan ke content/division/list.vue -->
|
||||||
|
<PaginationView
|
||||||
|
:pagination-meta="paginationMeta"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Pubs
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
import * as CB from '~/components/pub/my-ui/combobox'
|
||||||
|
import Nav from '~/components/pub/my-ui/nav-footer/cl-sa.vue'
|
||||||
|
|
||||||
|
// This scope
|
||||||
|
import type { DeviceOrderItem } from '~/models/device-order-item';
|
||||||
|
import type { Device } from '~/models/device';
|
||||||
|
|
||||||
|
// Props
|
||||||
|
const props = defineProps<{
|
||||||
|
data: DeviceOrderItem
|
||||||
|
devices: Device[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Refs
|
||||||
|
const { devices } = toRefs(props)
|
||||||
|
const deviceItems = ref<CB.Item[]>([])
|
||||||
|
|
||||||
|
// Nav actions
|
||||||
|
type ClickType = 'close' | 'save'
|
||||||
|
|
||||||
|
// Emits
|
||||||
|
const emit = defineEmits<{
|
||||||
|
close: [],
|
||||||
|
save: [data: DeviceOrderItem],
|
||||||
|
'update:searchText': [value: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Reactivities
|
||||||
|
watch(devices, (data) => {
|
||||||
|
deviceItems.value = CB.objectsToItems(data, 'code', 'name')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
function searchDeviceText(value: string) {
|
||||||
|
emit('update:searchText', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function navClick(type: ClickType) {
|
||||||
|
if (type === 'close') {
|
||||||
|
emit('close')
|
||||||
|
} else if (type === 'save') {
|
||||||
|
emit('save', props.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Block :colCount="4" :cellFlex="false">
|
||||||
|
<DE.Cell :colSpan="4">
|
||||||
|
<DE.Label>Nama</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
<CB.Combobox
|
||||||
|
v-model="data.device_code"
|
||||||
|
:items="deviceItems"
|
||||||
|
@update:searchText="searchDeviceText"
|
||||||
|
/>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Jumlah</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
<Input v-model="data.quantity" type="number" />
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
|
<Separator class="my-5" />
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Nav @click="navClick" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,36 +1,35 @@
|
|||||||
import { defineAsyncComponent } from 'vue'
|
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'))
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
|
||||||
|
|
||||||
export const config: Config = {
|
export const config: Config = {
|
||||||
cols: [{}, {}, { width: 50 }],
|
cols: [{}, { width: 200 }, { width: 100 }],
|
||||||
headers: [[{ label: 'Nama' }, { label: 'Jumlah' }, { label: '' }]],
|
headers: [[{ label: 'Nama' }, { label: 'Jumlah' }, { label: '' }]],
|
||||||
keys: ['name', 'count', 'action'],
|
keys: ['device.name', 'quantity', 'action'],
|
||||||
delKeyNames: [
|
delKeyNames: [
|
||||||
{ key: 'name', label: 'Nama' },
|
{ key: 'name', label: 'Nama' },
|
||||||
{ key: 'count', label: 'Jumlah' },
|
{ key: 'count', label: 'Jumlah' },
|
||||||
],
|
],
|
||||||
skeletonSize: 10
|
skeletonSize: 10,
|
||||||
// funcParsed: {
|
// funcParsed: {
|
||||||
// parent: (rec: unknown): unknown => {
|
// parent: (rec: unknown): unknown => {
|
||||||
// const recX = rec as SmallDetailDto
|
// const recX = rec as SmallDetailDto
|
||||||
// return recX.parent?.name || '-'
|
// return recX.parent?.name || '-'
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// funcComponent: {
|
components: {
|
||||||
// action(rec: object, idx: any) {
|
action(rec, idx) {
|
||||||
// const res: RecComponent = {
|
const res: RecComponent = {
|
||||||
// idx,
|
idx,
|
||||||
// rec: rec as object,
|
rec: rec as object,
|
||||||
// component: action,
|
component: action,
|
||||||
// props: {
|
props: {
|
||||||
// size: 'sm',
|
size: 'sm',
|
||||||
// },
|
},
|
||||||
// }
|
}
|
||||||
// return res
|
return res
|
||||||
// },
|
},
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||||
import { config } from './list-entry.config'
|
import { config } from './list-entry.config'
|
||||||
|
import type { DeviceOrderItem } from '~/models/device-order-item';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: DeviceOrderItem[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
add: []
|
||||||
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DataTable v-bind="config" :rows="[]" class="border mb-3 2xl:mb-4" />
|
<DataTable v-bind="config" :rows="data" class="border mb-3 2xl:mb-4" />
|
||||||
<div>
|
<div>
|
||||||
<Button>
|
<Button @click="emit('add')">
|
||||||
Tambah
|
<Icon name="i-lucide-plus" />
|
||||||
|
Tambah Item
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { DeviceOrder } from '~/models/device-order'
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: DeviceOrder | null | undefined
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<DE.Block mode="preview" label-size="small" class="!mb-0">
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Tgl. Order</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
|
<DE.Field>
|
||||||
|
{{ data?.createdAt?.substring(0, 10) }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>DPJP</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
|
<DE.Field>
|
||||||
|
{{ data?.doctor?.employee?.person?.name }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
|
</template>
|
||||||
@@ -1,6 +1,25 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
import type { DeviceOrder } from '~/models/device-order';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: DeviceOrder
|
||||||
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
Test
|
<DE.Block :col-count="2" mode="preview">
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Tanggal</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ data?.createdAt?.substring(0, 10) }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>DPJP</DE.Label>
|
||||||
|
<DE.Field>
|
||||||
|
{{ data?.doctor?.employee?.person?.name }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -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 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 = {
|
export const config: Config = {
|
||||||
cols: [{ width: 120 }, { }, { }, { width: 50 }],
|
cols: [{ width: 120 }, { }, { }, { }, { width: 50 }],
|
||||||
headers: [[{ label: 'Tanggal' }, { label: 'DPJP' }, { label: 'Alat Kesehatan' }, { label: '' }]],
|
headers: [[
|
||||||
keys: ['createdAt', 'encounter.doctor.person.name', 'items', 'action'],
|
{ label: 'Tanggal' },
|
||||||
|
{ label: 'DPJP' },
|
||||||
|
{ label: 'Alat Kesehatan' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: '' }]],
|
||||||
|
keys: ['createdAt', 'doctor.employee.person.name', 'items', 'status_code', 'action'],
|
||||||
delKeyNames: [
|
delKeyNames: [
|
||||||
{ key: 'code', label: 'Kode' },
|
{ key: 'code', label: 'Kode' },
|
||||||
{ key: 'name', label: 'Nama' },
|
{ key: 'name', label: 'Nama' },
|
||||||
@@ -16,27 +21,45 @@ export const config: Config = {
|
|||||||
htmls: {
|
htmls: {
|
||||||
items: (rec: unknown): unknown => {
|
items: (rec: unknown): unknown => {
|
||||||
const recX = rec as DeviceOrder
|
const recX = rec as DeviceOrder
|
||||||
return recX.items?.length || 0
|
if (recX.items?.length > 0) {
|
||||||
|
let output = '<table><tbody>'
|
||||||
|
recX.items.forEach((item: DeviceOrderItem) => {
|
||||||
|
output += '' +
|
||||||
|
'<tr>'+
|
||||||
|
`<td class="pe-10">${item.device?.name}</td>` +
|
||||||
|
'<td class="w-4">:</td>' +
|
||||||
|
`<td class="w-10">${item.quantity}</td>` +
|
||||||
|
'</tr>'
|
||||||
|
})
|
||||||
|
output += '</tbody></table>'
|
||||||
|
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
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ import type { PaginationMeta } from '~/components/pub/my-ui/pagination/paginatio
|
|||||||
// Configs
|
// Configs
|
||||||
import { config } from './list.config'
|
import { config } from './list.config'
|
||||||
|
|
||||||
interface Props {
|
defineProps<{
|
||||||
data: any[]
|
data: any[]
|
||||||
paginationMeta: PaginationMeta
|
paginationMeta: PaginationMeta
|
||||||
}
|
}>()
|
||||||
|
|
||||||
defineProps<Props>()
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
pageChange: [page: number]
|
pageChange: [page: number]
|
||||||
@@ -28,7 +26,7 @@ function handlePageChange(page: number) {
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<PubMyUiDataTable
|
<PubMyUiDataTable
|
||||||
v-bind="config"
|
v-bind="config"
|
||||||
:rows="[]"
|
:rows="data"
|
||||||
/>
|
/>
|
||||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
|
|||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [division, divisionAttrs] = defineField('division_id')
|
const [division, divisionAttrs] = defineField('division_code')
|
||||||
const [employee, employeeAttrs] = defineField('employee_id')
|
const [employee, employeeAttrs] = defineField('employee_id')
|
||||||
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ const headStatusStr = computed<string>({
|
|||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
if (props.values.division_id !== undefined)
|
if (props.values.division_code !== undefined)
|
||||||
division.value = props.values.division_id ? Number(props.values.division_id) : null
|
division.value = props.values.division_code ? String(props.values.division_code) : null
|
||||||
if (props.values.employee_id !== undefined)
|
if (props.values.employee_id !== undefined)
|
||||||
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
||||||
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
||||||
@@ -72,7 +72,7 @@ if (props.values) {
|
|||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
code.value = ''
|
code.value = ''
|
||||||
name.value = ''
|
name.value = ''
|
||||||
division.value = null
|
division.value = ''
|
||||||
employee.value = null
|
employee.value = null
|
||||||
headStatus.value = false
|
headStatus.value = false
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ function onSubmitForm() {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
division_id: division.value || null,
|
division_code: division.value || '',
|
||||||
employee_id: employee.value || null,
|
employee_id: employee.value || null,
|
||||||
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ function onCancelForm() {
|
|||||||
</Cell>
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Label height="compact">Posisi Divisi</Label>
|
<Label height="compact">Posisi Divisi</Label>
|
||||||
<Field :errMessage="errors.division_id">
|
<Field :errMessage="errors.division_code">
|
||||||
<Combobox
|
<Combobox
|
||||||
id="division"
|
id="division"
|
||||||
v-model="division"
|
v-model="division"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { genDivisionPosition } from '~/models/division-position'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
schema: z.ZodSchema<any>
|
schema: z.ZodSchema<any>
|
||||||
divisionId: number
|
divisionId: string
|
||||||
employees: any[]
|
employees: any[]
|
||||||
values: any
|
values: any
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
|
|||||||
@@ -36,25 +36,25 @@ const { defineField, errors, meta } = useForm({
|
|||||||
initialValues: {
|
initialValues: {
|
||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
parent_id: null,
|
parent_code: null,
|
||||||
} as Partial<DivisionFormData>,
|
} as Partial<DivisionFormData>,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [parent, parentAttrs] = defineField('parent_id')
|
const [parent, parentAttrs] = defineField('parent_code')
|
||||||
|
|
||||||
// Fill fields from props.values if provided
|
// Fill fields from props.values if provided
|
||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
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 = () => {
|
const resetForm = () => {
|
||||||
code.value = ''
|
code.value = ''
|
||||||
name.value = ''
|
name.value = ''
|
||||||
parent.value = null
|
parent.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Form submission handler
|
// Form submission handler
|
||||||
@@ -63,7 +63,7 @@ function onSubmitForm() {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
parent_id: parent.value || null,
|
parent_code: parent.value || '',
|
||||||
}
|
}
|
||||||
emit('submit', formData, resetForm)
|
emit('submit', formData, resetForm)
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ function onCancelForm() {
|
|||||||
</Cell>
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Label height="compact">Divisi Induk</Label>
|
<Label height="compact">Divisi Induk</Label>
|
||||||
<Field :errMessage="errors.parent_id">
|
<Field :errMessage="errors.parent_code">
|
||||||
<TreeSelect
|
<TreeSelect
|
||||||
id="parent"
|
id="parent"
|
||||||
v-model="parent"
|
v-model="parent"
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
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
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="supportingDocOpt"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
import FileField from '~/components/pub/my-ui/form/file-field.vue'
|
||||||
|
import SelectDocType from './_common/select-doc-type.vue'
|
||||||
|
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
schema: any
|
||||||
|
initialValues?: any
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
validate: () => formRef.value?.validate(),
|
||||||
|
resetForm: () => formRef.value?.resetForm(),
|
||||||
|
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||||
|
values: computed(() => formRef.value?.values),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
ref="formRef"
|
||||||
|
v-slot="{ values }"
|
||||||
|
as=""
|
||||||
|
keep-values
|
||||||
|
:validation-schema="formSchema"
|
||||||
|
:validate-on-mount="false"
|
||||||
|
validation-mode="onSubmit"
|
||||||
|
:initial-values="initialValues ? initialValues : {}"
|
||||||
|
>
|
||||||
|
<DE.Block :col-count="2" :cell-flex="false">
|
||||||
|
<InputBase
|
||||||
|
field-name="officer"
|
||||||
|
label="Petugas Upload"
|
||||||
|
placeholder="Masukkan Petugas Upload"
|
||||||
|
:is-disabled="true"
|
||||||
|
/>
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<Separator class="w-full my-4"/>
|
||||||
|
<DE.Block :col-count="3" :cell-flex="false">
|
||||||
|
<InputBase
|
||||||
|
field-name="name"
|
||||||
|
label="Nama Dokumen"
|
||||||
|
placeholder="Maukkan Nama Dokumen"
|
||||||
|
/>
|
||||||
|
<SelectDocType
|
||||||
|
field-name="type_code"
|
||||||
|
label="Tipe Dokumen"
|
||||||
|
placeholder="Pilih Jenis Dokumen"
|
||||||
|
:errors="errors"
|
||||||
|
is-required
|
||||||
|
/>
|
||||||
|
<FileField
|
||||||
|
field-name="content"
|
||||||
|
label="Upload Dokumen"
|
||||||
|
placeholder="Unggah dokumen"
|
||||||
|
:errors="errors"
|
||||||
|
:accept="['pdf', 'jpg', 'png']"
|
||||||
|
:max-size-mb="1"
|
||||||
|
/>
|
||||||
|
</DE.Block>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -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: {
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { LinkItem, ListItemDto } from '~/components/pub/my-ui/data/types'
|
||||||
|
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: ListItemDto
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const recId = inject<Ref<number>>('rec_id')!
|
||||||
|
const recAction = inject<Ref<string>>('rec_action')!
|
||||||
|
const recItem = inject<Ref<any>>('rec_item')!
|
||||||
|
const activeServicePosition = inject<Ref<string>>('activeServicePosition')! // previously: activePosition
|
||||||
|
|
||||||
|
const activeKey = ref<string | null>(null)
|
||||||
|
const linkItemsFiltered = ref<LinkItem[]>([])
|
||||||
|
const baseLinkItems: LinkItem[] = [
|
||||||
|
{
|
||||||
|
label: 'Detail',
|
||||||
|
value: 'detail',
|
||||||
|
onClick: () => {
|
||||||
|
proceedItem(ActionEvents.showDetail)
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-eye',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const medicalLinkItems: LinkItem[] = [
|
||||||
|
{
|
||||||
|
label: 'Process',
|
||||||
|
value: 'process',
|
||||||
|
onClick: () => {
|
||||||
|
proceedItem(ActionEvents.showProcess)
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-shuffle',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const regLinkItems: LinkItem[] = [
|
||||||
|
{
|
||||||
|
label: 'Print',
|
||||||
|
value: 'print',
|
||||||
|
onClick: () => {
|
||||||
|
proceedItem(ActionEvents.showPrint)
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-printer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Batalkan',
|
||||||
|
value: 'cancel',
|
||||||
|
onClick: () => {
|
||||||
|
proceedItem(ActionEvents.showCancel)
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-circle-x',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hapus',
|
||||||
|
value: 'remove',
|
||||||
|
onClick: () => {
|
||||||
|
proceedItem(ActionEvents.showConfirmDelete)
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-trash',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const voidLinkItems: LinkItem[] = [
|
||||||
|
{
|
||||||
|
label: 'Nothing',
|
||||||
|
value: 'nothing',
|
||||||
|
icon: 'i-lucide-file',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
linkItemsFiltered.value = [...baseLinkItems]
|
||||||
|
|
||||||
|
getLinks()
|
||||||
|
|
||||||
|
watch(activeServicePosition, () => {
|
||||||
|
getLinks()
|
||||||
|
})
|
||||||
|
|
||||||
|
function proceedItem(action: string) {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recItem.value = props.rec
|
||||||
|
recAction.value = action
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLinks() {
|
||||||
|
switch (activeServicePosition.value) {
|
||||||
|
case 'medical':
|
||||||
|
linkItemsFiltered.value = [...baseLinkItems, ...medicalLinkItems]
|
||||||
|
break
|
||||||
|
case 'registration':
|
||||||
|
linkItemsFiltered.value = [...baseLinkItems, ...regLinkItems]
|
||||||
|
case 'unit|resp':
|
||||||
|
linkItemsFiltered.value = [...baseLinkItems]
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
linkItemsFiltered.value = voidLinkItems
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<SidebarMenuButton
|
||||||
|
size="lg"
|
||||||
|
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white dark:data-[state=open]:bg-slate-800"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="i-lucide-chevrons-up-down"
|
||||||
|
class="ml-auto size-4"
|
||||||
|
/>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
class="w-[--radix-dropdown-menu-trigger-width] min-w-40 rounded-lg border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
||||||
|
align="end"
|
||||||
|
>
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-for="item in linkItemsFiltered"
|
||||||
|
:key="item.label"
|
||||||
|
class="hover:bg-gray-100 dark:hover:bg-slate-700"
|
||||||
|
@click="item.onClick"
|
||||||
|
@mouseenter="activeKey = item.label"
|
||||||
|
@mouseleave="activeKey = null"
|
||||||
|
>
|
||||||
|
<Icon :name="item.icon ?? ''" />
|
||||||
|
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -20,6 +20,7 @@ import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
|
|||||||
// Helpers
|
// Helpers
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import { useForm } from 'vee-validate'
|
import { useForm } from 'vee-validate'
|
||||||
|
import { refDebounced } from '@vueuse/core'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
@@ -92,8 +93,9 @@ watch(subSpecialistId, async (newValue) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Watch SEP number changes to notify parent
|
// Debounced SEP number watcher: emit change only after user stops typing
|
||||||
watch(sepNumber, (newValue) => {
|
const debouncedSepNumber = refDebounced(sepNumber, 500)
|
||||||
|
watch(debouncedSepNumber, (newValue) => {
|
||||||
emit('event', 'sep-number-changed', newValue)
|
emit('event', 'sep-number-changed', newValue)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -452,7 +454,11 @@ defineExpose({
|
|||||||
name="i-lucide-loader-2"
|
name="i-lucide-loader-2"
|
||||||
class="h-4 w-4 animate-spin"
|
class="h-4 w-4 animate-spin"
|
||||||
/>
|
/>
|
||||||
<span v-else>+</span>
|
<Icon
|
||||||
|
v-else
|
||||||
|
name="i-lucide-plus"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
v-else
|
v-else
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
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<DateRange>
|
||||||
|
|
||||||
|
// function onFilterClick() {
|
||||||
|
// console.log('Search:', searchQuery.value)
|
||||||
|
// console.log('Date Range:', dateRange.value)
|
||||||
|
// props.refSearchNav?.onClick()
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="relative w-64">
|
||||||
|
<Search class="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-gray-400" />
|
||||||
|
<Input v-model="searchQuery" type="text" placeholder="Cari Nama /No.RM" class="pl-9" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
:class="cn('w-[200px] justify-start text-left font-normal', !value && 'text-muted-foreground')"
|
||||||
|
>
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="value.start">
|
||||||
|
<template v-if="value.end">
|
||||||
|
{{ df.format(value.start.toDate(getLocalTimeZone())) }} -
|
||||||
|
{{ df.format(value.end.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ df.format(value.start.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else> Pick a date </template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<RangeCalendar
|
||||||
|
v-model="value"
|
||||||
|
initial-focus
|
||||||
|
:number-of-months="2"
|
||||||
|
@update:start-value="(startDate) => (value.start = startDate)"
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<Button variant="outline" class="border-orange-500 text-orange-600 hover:bg-orange-50" @click="onFilterClick">
|
||||||
|
<FilterIcon class="mr-2 size-4" />
|
||||||
|
Filter
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<DropdownMenu v-show="props.enableExport">
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<Button variant="outline" class="ml-auto border-orange-500 text-orange-600 hover:bg-orange-50">
|
||||||
|
<Icon name="i-lucide-download" class="h-4 w-4" />
|
||||||
|
Ekspor
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent>
|
||||||
|
<DropdownMenuItem @click="onExportPdf">
|
||||||
|
Ekspor PDF
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem @click="onExportCsv">
|
||||||
|
Ekspor CSV
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem @click="onExportExcel">
|
||||||
|
Ekspor Excel
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import { Button } from '~/components/pub/ui/button'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// Track active menu item from query param
|
||||||
|
const activeMenu = computed(() => route.query.menu as string || '')
|
||||||
|
|
||||||
|
interface ButtonItems {
|
||||||
|
label: string
|
||||||
|
icon: string
|
||||||
|
value: string
|
||||||
|
type: 'icon' | 'image'
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemsOne: ButtonItems[] = [
|
||||||
|
{
|
||||||
|
label: 'Data Pendaftaran',
|
||||||
|
icon: 'i-lucide-file',
|
||||||
|
value: 'register',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Status Pembayaran',
|
||||||
|
icon: 'i-lucide-banknote-arrow-down',
|
||||||
|
value: 'status',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Riwayat Pasien',
|
||||||
|
icon: 'i-lucide-history',
|
||||||
|
value: 'history',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Penunjang',
|
||||||
|
icon: 'i-lucide-library-big',
|
||||||
|
value: 'support',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Resep',
|
||||||
|
icon: 'i-lucide-pill',
|
||||||
|
value: 'receipt',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'DPJP',
|
||||||
|
icon: 'i-lucide-stethoscope',
|
||||||
|
value: 'doctor',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'I-Care BPJS',
|
||||||
|
icon: '/bpjs.png',
|
||||||
|
value: 'bpjs',
|
||||||
|
type: 'image',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'File SEP',
|
||||||
|
icon: 'i-lucide-file',
|
||||||
|
value: 'sep',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const itemsTwo: ButtonItems[] = [
|
||||||
|
{
|
||||||
|
label: 'Tarif Tindakan',
|
||||||
|
icon: 'i-lucide-banknote-arrow-down',
|
||||||
|
value: 'price-list',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tarif Tindakan Paket',
|
||||||
|
icon: 'i-lucide-banknote-arrow-down',
|
||||||
|
value: 'price-list-package',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function handleClick(value: string) {
|
||||||
|
router.replace({ path: route.path, query: { menu: value } })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="my-4">
|
||||||
|
<h2 class="mb-4 text-base font-semibold md:text-lg 2xl:text-xl">History Pasien:</h2>
|
||||||
|
<div class="flex flex-wrap gap-2 mb-2">
|
||||||
|
<Button
|
||||||
|
v-for="item in itemsOne"
|
||||||
|
:key="item.value"
|
||||||
|
:class="[
|
||||||
|
'flex items-center gap-2 rounded-lg border px-3 py-1 text-sm font-medium transition-colors',
|
||||||
|
activeMenu === item.value
|
||||||
|
? 'border-orange-300 bg-orange-400 text-white'
|
||||||
|
: 'border-orange-300 bg-white text-orange-500 hover:bg-orange-400 hover:text-white'
|
||||||
|
]"
|
||||||
|
@click="handleClick(item.value)"
|
||||||
|
>
|
||||||
|
<Icon v-if="item.type === 'icon'"
|
||||||
|
:name="item.icon"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
|
<img v-else-if="item.type === 'image'" :src="item.icon" class="h-[24px] w-[24px] object-contain" />
|
||||||
|
{{ item.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<h2 class="mb-4 text-base font-semibold md:text-lg 2xl:text-xl">Billing Pasien:</h2>
|
||||||
|
<div class="flex flex-wrap gap-2 mb-2">
|
||||||
|
<Button
|
||||||
|
v-for="item in itemsTwo"
|
||||||
|
:key="item.value"
|
||||||
|
:class="[
|
||||||
|
'flex items-center gap-2 rounded-lg border px-3 py-1 text-sm font-medium transition-colors',
|
||||||
|
activeMenu === item.value
|
||||||
|
? 'border-orange-300 bg-orange-400 text-white'
|
||||||
|
: 'border-orange-300 bg-white text-orange-500 hover:bg-orange-400 hover:text-white'
|
||||||
|
]"
|
||||||
|
@click="handleClick(item.value)"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="item.icon"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
|
{{ item.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -6,7 +6,7 @@ import { getAge } from '~/lib/date'
|
|||||||
|
|
||||||
type SmallDetailDto = Encounter
|
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'))
|
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||||
|
|
||||||
export const config: Config = {
|
export const config: Config = {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||||
import { config } from './list.cfg'
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: any[]
|
data: any[],
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PubMyUiDataTable
|
<DataTable
|
||||||
v-bind="config"
|
v-bind="config"
|
||||||
:rows="data"
|
:rows="props.data"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Types
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '~/components/pub/ui/accordion'
|
||||||
|
import EncounterQuickInfoFull from '~/components/app/encounter/quick-info-full.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: Encounter
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||||
|
<!-- Data Pasien -->
|
||||||
|
<Accordion type="single" defaultValue="item-patient" collapsible>
|
||||||
|
<AccordionItem value="item-patient" class="border-none">
|
||||||
|
<AccordionTrigger class="focus:outline-none focus:ring-0">
|
||||||
|
<h2 class="mb-4 text-base font-semibold md:text-lg 2xl:text-xl">Data Pasien:</h2>
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
<EncounterQuickInfoFull :data="props.data" :is-grid="true" />
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Types
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import EncounterQuickInfoFull from '~/components/app/encounter/quick-info-full.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: Encounter
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||||
|
<h2 class="mb-4 text-base font-semibold md:text-lg 2xl:text-xl">Data Pasien:</h2>
|
||||||
|
<EncounterQuickInfoFull :data="props.data" :is-grid="false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Helpers
|
||||||
|
import { format, parseISO } from 'date-fns'
|
||||||
|
import { id as localeID } from 'date-fns/locale'
|
||||||
|
import { getAge } from '~/lib/date'
|
||||||
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
import { paymentTypes } from '~/lib/constants.vclaim'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: Encounter
|
||||||
|
isGrid?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isGrid = props.isGrid !== undefined ? props.isGrid : true
|
||||||
|
|
||||||
|
// Address
|
||||||
|
const address = computed(() => {
|
||||||
|
if (props.data.patient.person.addresses && props.data.patient.person.addresses.length > 0) {
|
||||||
|
return props.data.patient.person.addresses.map((a) => a.address).join(', ')
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
})
|
||||||
|
|
||||||
|
// DPJP
|
||||||
|
const dpjp = computed(() => {
|
||||||
|
if (props.data.responsible_doctor) {
|
||||||
|
const dp = props.data.responsible_doctor.employee.person
|
||||||
|
return `${dp.frontTitle || ''} ${dp.name || ''} ${dp.endTitle || ''}`.trim()
|
||||||
|
} else if (props.data.appointment_doctor) {
|
||||||
|
const dp = props.data.appointment_doctor.employee.person
|
||||||
|
return `${dp.frontTitle || ''} ${dp.name || ''} ${dp.endTitle || ''}`.trim()
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Tgl. Lahir dengan Umur
|
||||||
|
const birthDateFormatted = computed(() => {
|
||||||
|
if (!props.data.patient.person.birthDate) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const ageData = getAge(props.data.patient.person.birthDate)
|
||||||
|
const ageYears = ageData.extFormat.split(' ')[0] || '0'
|
||||||
|
return `${ageData.idFormat} (${ageYears} Tahun)`
|
||||||
|
} catch {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Tgl. Masuk RS dengan waktu
|
||||||
|
const registeredDateFormatted = computed(() => {
|
||||||
|
const dateStr = props.data.registeredAt || props.data.visitDate
|
||||||
|
if (!dateStr) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const date = parseISO(dateStr)
|
||||||
|
return format(date, 'dd MMMM yyyy HH:mm', { locale: localeID })
|
||||||
|
} catch {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Jns. Kelamin
|
||||||
|
const genderLabel = computed(() => {
|
||||||
|
const code = props.data.patient.person.gender_code
|
||||||
|
if (!code) return '-'
|
||||||
|
// Map common gender codes
|
||||||
|
if (code === 'M' || code === 'male' || code.toLowerCase() === 'l') {
|
||||||
|
return 'Laki-laki'
|
||||||
|
} else if (code === 'F' || code === 'female' || code.toLowerCase() === 'p') {
|
||||||
|
return 'Perempuan'
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
})
|
||||||
|
|
||||||
|
// Jns. Pembayaran
|
||||||
|
const paymentTypeLabel = computed(() => {
|
||||||
|
const code = props.data.paymentMethod_code
|
||||||
|
if (!code) return '-'
|
||||||
|
|
||||||
|
// Map payment method codes
|
||||||
|
if (code === 'insurance') {
|
||||||
|
return 'JKN'
|
||||||
|
} else if (code === 'jkn') {
|
||||||
|
return 'JKN'
|
||||||
|
} else if (code === 'jkmm') {
|
||||||
|
return 'JKMM'
|
||||||
|
} else if (code === 'spm') {
|
||||||
|
return 'SPM'
|
||||||
|
} else if (code === 'pks') {
|
||||||
|
return 'PKS'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get from paymentTypes constant
|
||||||
|
if (paymentTypes[code]) {
|
||||||
|
return paymentTypes[code].split(' ')[0] // Get first part (e.g., "JKN" from "JKN (Jaminan...)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
})
|
||||||
|
|
||||||
|
// No. Billing - try to get from trx_number or other billing fields
|
||||||
|
const billingNumber = computed(() => {
|
||||||
|
// Check if encounter has payment data with trx_number
|
||||||
|
if ((props.data as any).trx_number) {
|
||||||
|
return (props.data as any).trx_number
|
||||||
|
}
|
||||||
|
// Check if encounter has payment relation
|
||||||
|
if ((props.data as any).payment?.trx_number) {
|
||||||
|
return (props.data as any).payment.trx_number
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Nama Ruang - from unit name or room relation
|
||||||
|
const roomName = computed(() => {
|
||||||
|
if (props.data.unit?.name) {
|
||||||
|
return props.data.unit.name
|
||||||
|
}
|
||||||
|
// Check if there's a room relation
|
||||||
|
if ((props.data as any).room?.name) {
|
||||||
|
return (props.data as any).room.name
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
})
|
||||||
|
|
||||||
|
// No Bed - from bed relation or field
|
||||||
|
const bedNumber = computed(() => {
|
||||||
|
// Check if encounter has bed data
|
||||||
|
if ((props.data as any).bed?.number) {
|
||||||
|
return (props.data as any).bed.number
|
||||||
|
}
|
||||||
|
if ((props.data as any).bed_number) {
|
||||||
|
return (props.data as any).bed_number
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2 class="mb-4 font-semibold md:text-base 2xl:text-lg">Data Pasien:</h2>
|
||||||
|
<!-- 4 Column Grid Layout -->
|
||||||
|
<div :class="cn('grid grid-cols-4 gap-4', isGrid && 'sm:grid-cols-4')">
|
||||||
|
<!-- No. RM -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. RM</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ data.patient.number || '-' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tgl. Lahir -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Tgl. Lahir</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ birthDateFormatted }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Jns. Pembayaran -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Cara Bayar</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ paymentTypeLabel }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- No Bed -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No Bed</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ bedNumber }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nama Pasien -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. Bed</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ data.patient.person.name || '-' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tgl. Masuk RS -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Tgl. Masuk RS</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ registeredDateFormatted }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- No. Billing -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. Billing</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ billingNumber }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DPJP -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">DPJP</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ dpjp }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Alamat -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Alamat</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ address }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Jns. Kelamin -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Jns. Kelamin</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ genderLabel }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nama Ruang -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Nama Ruang</label>
|
||||||
|
<label class="w-2 flex-none">:</label>
|
||||||
|
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
|
||||||
|
{{ roomName }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,52 +1,52 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
import { genderCodes } from '~/const/key-val/person';
|
||||||
import type { Encounter } from '~/models/encounter'
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: Encounter
|
data: Encounter
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
let address = ''
|
let address = ref('')
|
||||||
if (props.data.patient.person.addresses) {
|
if (props.data.patient.person.addresses) {
|
||||||
address = props.data.patient.person.addresses.map((a) => a.address).join(', ')
|
address.value = props.data.patient.person.addresses.map((a) => a.address).join(', ')
|
||||||
}
|
}
|
||||||
|
|
||||||
let dpjp = ''
|
let dpjp = ref('')
|
||||||
if (props.data.responsible_doctor) {
|
if (props.data.responsible_doctor) {
|
||||||
const dp = props.data.responsible_doctor.employee.person
|
const dp = props.data.responsible_doctor.employee.person
|
||||||
dpjp = `${dp.frontTitle} ${dp.name} ${dp.endTitle}`
|
dpjp.value = `${dp.frontTitle} ${dp.name} ${dp.endTitle}`
|
||||||
} else if (props.data.appointment_doctor) {
|
} else if (props.data.appointment_doctor) {
|
||||||
dpjp = props.data.appointment_doctor.employee.person.name
|
dpjp.value = props.data.appointment_doctor.employee.person.name
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
<div class="w-full">
|
||||||
<!-- Data Pasien -->
|
<!-- Data Pasien -->
|
||||||
<h2 class="mb-2 font-semibold md:text-base 2xl:text-lg">
|
<h2 class="mb-2 font-semibold md:text-base 2xl:text-lg">
|
||||||
{{ data.patient.person.name }} - {{ data.patient.number }}
|
{{ data.patient.person.name }} - {{ data.patient.number }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="grid grid-cols-3">
|
<div class="grid grid-cols-3">
|
||||||
<div>
|
<div>
|
||||||
<DE.Block
|
<DE.Block mode="preview">
|
||||||
mode="preview"
|
|
||||||
labelSize="large"
|
|
||||||
>
|
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">No. RM</DE.Label>
|
<DE.Label class="font-semibold">No. RM</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.patient.person.birthDate?.substring(0, 10) }}
|
{{ data.patient.person.birthDate?.substring(0, 10) }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Jenis Kelamin</DE.Label>
|
<DE.Label class="font-semibold">Jns. Kelamin</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.patient.person.gender_code }}
|
{{ genderCodes[data.patient.person.gender_code as keyof typeof genderCodes] }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Alamat</DE.Label>
|
<DE.Label class="font-semibold">Alamat</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
<div v-html="address"></div>
|
<div v-html="address"></div>
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
@@ -54,35 +54,39 @@ if (props.data.responsible_doctor) {
|
|||||||
</DE.Block>
|
</DE.Block>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<DE.Block
|
<DE.Block mode="preview">
|
||||||
mode="preview"
|
|
||||||
labelSize="large"
|
|
||||||
>
|
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Tgl. Kunjungan</DE.Label>
|
<DE.Label position="dynamic" class="font-semibold">Tgl. Masuk</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.visitDate.substring(0, 10) }}
|
{{ data.visitDate.substring(0, 10) }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Klinik</DE.Label>
|
<DE.Label position="dynamic" class="font-semibold">Poliklinik</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.unit?.name }}
|
{{ data.unit?.name }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">DPJP</DE.Label>
|
<DE.Label position="dynamic" class="font-semibold">Klinik</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ dpjp }}
|
{{ data.unit?.name }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<DE.Block
|
<DE.Block mode="preview">
|
||||||
mode="preview"
|
<DE.Cell>
|
||||||
labelSize="large"
|
<DE.Label position="dynamic" class="font-semibold">DPJP</DE.Label>
|
||||||
>
|
<DE.Colon />
|
||||||
|
<DE.Field>
|
||||||
|
{{ dpjp }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label
|
<DE.Label
|
||||||
position="dynamic"
|
position="dynamic"
|
||||||
@@ -90,6 +94,7 @@ if (props.data.responsible_doctor) {
|
|||||||
>
|
>
|
||||||
Billing
|
Billing
|
||||||
</DE.Label>
|
</DE.Label>
|
||||||
|
<DE.Colon class="pt-1" />
|
||||||
<DE.Field class="text-base 2xl:text-lg">
|
<DE.Field class="text-base 2xl:text-lg">
|
||||||
Rp. 000.000
|
Rp. 000.000
|
||||||
<!-- {{ data }} -->
|
<!-- {{ data }} -->
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import { Button } from '~/components/pub/ui/button'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// Track active menu item from query param
|
||||||
|
const activeMenu = computed(() => route.query.menu as string || '')
|
||||||
|
|
||||||
|
interface ButtonItems {
|
||||||
|
label: string
|
||||||
|
icon: string
|
||||||
|
value: string
|
||||||
|
type: 'icon' | 'image'
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemsOne: ButtonItems[] = [
|
||||||
|
{
|
||||||
|
label: 'Data Pendaftaran',
|
||||||
|
icon: 'i-lucide-file',
|
||||||
|
value: 'register',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Status Pembayaran',
|
||||||
|
icon: 'i-lucide-banknote-arrow-down',
|
||||||
|
value: 'status',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Riwayat Pasien',
|
||||||
|
icon: 'i-lucide-history',
|
||||||
|
value: 'history',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Penunjang',
|
||||||
|
icon: 'i-lucide-library-big',
|
||||||
|
value: 'support',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Resep',
|
||||||
|
icon: 'i-lucide-pill',
|
||||||
|
value: 'receipt',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'DPJP',
|
||||||
|
icon: 'i-lucide-stethoscope',
|
||||||
|
value: 'doctor',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'I-Care BPJS',
|
||||||
|
icon: '/bpjs.png',
|
||||||
|
value: 'bpjs',
|
||||||
|
type: 'image',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'File SEP',
|
||||||
|
icon: 'i-lucide-file',
|
||||||
|
value: 'sep',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const itemsTwo: ButtonItems[] = [
|
||||||
|
{
|
||||||
|
label: 'Tarif Tindakan',
|
||||||
|
icon: 'i-lucide-banknote-arrow-down',
|
||||||
|
value: 'price-list',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tarif Tindakan Paket',
|
||||||
|
icon: 'i-lucide-banknote-arrow-down',
|
||||||
|
value: 'price-list-package',
|
||||||
|
type: 'icon',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function handleClick(value: string) {
|
||||||
|
router.replace({ path: route.path, query: { menu: value } })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2 class="mb-4 font-semibold md:text-base 2xl:text-lg">Menu Cepat:</h2>
|
||||||
|
<div class="flex flex-wrap gap-2 mb-2">
|
||||||
|
<Button
|
||||||
|
v-for="item in itemsOne"
|
||||||
|
:key="item.value"
|
||||||
|
:class="[
|
||||||
|
'flex items-center gap-2 rounded-lg border px-3 py-1 text-sm font-medium transition-colors',
|
||||||
|
activeMenu === item.value
|
||||||
|
? 'border-orange-300 bg-orange-400 text-white'
|
||||||
|
: 'border-orange-300 bg-white text-orange-500 hover:bg-orange-400 hover:text-white'
|
||||||
|
]"
|
||||||
|
@click="handleClick(item.value)"
|
||||||
|
>
|
||||||
|
<Icon v-if="item.type === 'icon'"
|
||||||
|
:name="item.icon"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
|
<img v-else-if="item.type === 'image'" :src="item.icon" class="h-[24px] w-[24px] object-contain" />
|
||||||
|
{{ item.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap gap-2 mb-2">
|
||||||
|
<Button
|
||||||
|
v-for="item in itemsTwo"
|
||||||
|
:key="item.value"
|
||||||
|
:class="[
|
||||||
|
'flex items-center gap-2 rounded-lg border px-3 py-1 text-sm font-medium transition-colors',
|
||||||
|
activeMenu === item.value
|
||||||
|
? 'border-orange-300 bg-orange-400 text-white'
|
||||||
|
: 'border-orange-300 bg-white text-orange-500 hover:bg-orange-400 hover:text-white'
|
||||||
|
]"
|
||||||
|
@click="handleClick(item.value)"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="item.icon"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
|
{{ item.label }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -12,7 +12,7 @@ const statusCodeColors: Record<string, Variants> = {
|
|||||||
review: 'fresh',
|
review: 'fresh',
|
||||||
process: 'fresh',
|
process: 'fresh',
|
||||||
done: 'positive',
|
done: 'positive',
|
||||||
canceled: 'destructive',
|
cancel: 'destructive',
|
||||||
rejected: 'destructive',
|
rejected: 'destructive',
|
||||||
skiped: 'negative',
|
skiped: 'negative',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
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'
|
||||||
|
// Helpers
|
||||||
|
import type z from 'zod'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
// import { ref, watch, inject } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: any
|
||||||
|
schema: z.ZodSchema<any>
|
||||||
|
excludeFields?: string[]
|
||||||
|
isReadonly?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', val: any): void
|
||||||
|
(e: 'submit', val: any): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Setup form
|
||||||
|
const {
|
||||||
|
validate: _validate,
|
||||||
|
defineField,
|
||||||
|
handleSubmit,
|
||||||
|
errors,
|
||||||
|
values,
|
||||||
|
} = useForm({
|
||||||
|
validationSchema: toTypedSchema(props.schema),
|
||||||
|
initialValues: props.modelValue,
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(values, (val) => emit('update:modelValue', val), { deep: true })
|
||||||
|
|
||||||
|
// Define form fields
|
||||||
|
const [relatives, relativesAttrs] = defineField('relatives')
|
||||||
|
const [responsibleName, responsibleNameAttrs] = defineField('responsibleName')
|
||||||
|
const [responsiblePhone, responsiblePhoneAttrs] = defineField('responsiblePhone')
|
||||||
|
const [informant, informantAttrs] = defineField('informant')
|
||||||
|
const [witness1, witness1Attrs] = defineField('witness1')
|
||||||
|
const [witness2, witness2Attrs] = defineField('witness2')
|
||||||
|
const [tanggal, tanggalAttrs] = defineField('tanggal')
|
||||||
|
|
||||||
|
// Relatives list handling
|
||||||
|
const addRelative = () => {
|
||||||
|
relatives.value = [...(relatives.value || []), { name: '', phone: '' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeRelative = (index: number) => {
|
||||||
|
relatives.value = relatives.value.filter((_: any, i: number) => i !== index)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = async () => {
|
||||||
|
const result = await _validate()
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
data: result.values,
|
||||||
|
errors: result.errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ validate })
|
||||||
|
|
||||||
|
const icdPreview = inject('icdPreview')
|
||||||
|
|
||||||
|
const isExcluded = (key: string) => props.excludeFields?.includes(key)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<Block>
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Tanggal</Label>
|
||||||
|
<Field>
|
||||||
|
<Input
|
||||||
|
v-model="tanggal"
|
||||||
|
v-bind="tanggalAttrs"
|
||||||
|
:disabled="props.isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
|
||||||
|
<Separator class="mt-8" />
|
||||||
|
|
||||||
|
<div class="my-2 flex items-center justify-between">
|
||||||
|
<h1 class="font-semibold">Anggota Keluarga</h1>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
@click="addRelative"
|
||||||
|
>
|
||||||
|
+ Tambah
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="(item, idx) in relatives"
|
||||||
|
:key="idx"
|
||||||
|
class="my-2 rounded-md border border-slate-300 p-4"
|
||||||
|
>
|
||||||
|
<Block :colCount="2">
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Nama Anggota Keluarga</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="relatives[idx].name" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>No. Hp Anggota Keluarga</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="relatives[idx].phone" />
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="mt-3 text-sm text-red-500"
|
||||||
|
@click="removeRelative(idx)"
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator class="mt-8" />
|
||||||
|
|
||||||
|
<!-- Responsible Section -->
|
||||||
|
<div class="my-2">
|
||||||
|
<h1 class="font-semibold">Penanggung Jawab</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-2 rounded-md border border-slate-300 p-4">
|
||||||
|
<Block :colCount="2">
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Nama Penanggung Jawab</Label>
|
||||||
|
<Field>
|
||||||
|
<Input
|
||||||
|
v-model="responsibleName"
|
||||||
|
v-bind="responsibleNameAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>No. Hp Penanggung Jawab</Label>
|
||||||
|
<Field>
|
||||||
|
<Input
|
||||||
|
v-model="responsiblePhone"
|
||||||
|
v-bind="responsiblePhoneAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator class="mt-8" />
|
||||||
|
|
||||||
|
<!-- Informant -->
|
||||||
|
<div class="my-2">
|
||||||
|
<h1 class="font-semibold">Pemberi Informasi</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-2 rounded-md border border-slate-300 p-4">
|
||||||
|
<Block>
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Informant</Label>
|
||||||
|
<Field>
|
||||||
|
<Input
|
||||||
|
v-model="informant"
|
||||||
|
v-bind="informantAttrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator class="mt-8" />
|
||||||
|
|
||||||
|
<!-- Witnesses -->
|
||||||
|
<div class="my-2">
|
||||||
|
<h1 class="font-semibold">Saksi</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-2 rounded-md border border-slate-300 p-4">
|
||||||
|
<Block :colCount="2">
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Saksi 1</Label>
|
||||||
|
<Field>
|
||||||
|
<Input
|
||||||
|
v-model="witness1"
|
||||||
|
v-bind="witness1Attrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
|
||||||
|
<Cell>
|
||||||
|
<Label dynamic>Saksi 2</Label>
|
||||||
|
<Field>
|
||||||
|
<Input
|
||||||
|
v-model="witness2"
|
||||||
|
v-bind="witness2Attrs"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<!-- FIXME: pindahkan ke content/division/list.vue -->
|
||||||
|
<PaginationView
|
||||||
|
:pagination-meta="paginationMeta"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
|
|||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [installation, installationAttrs] = defineField('installation_id')
|
const [installation, installationAttrs] = defineField('installation_code')
|
||||||
const [employee, employeeAttrs] = defineField('employee_id')
|
const [employee, employeeAttrs] = defineField('employee_id')
|
||||||
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ const headStatusStr = computed<string>({
|
|||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
if (props.values.installation_id !== undefined)
|
if (props.values.installation_code !== undefined)
|
||||||
installation.value = props.values.installation_id ? Number(props.values.installation_id) : null
|
installation.value = props.values.installation_code ? String(props.values.installation_code) : null
|
||||||
if (props.values.employee_id !== undefined)
|
if (props.values.employee_id !== undefined)
|
||||||
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
||||||
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
||||||
@@ -83,7 +83,7 @@ function onSubmitForm() {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
installation_id: installation.value || null,
|
installation_code: String(installation.value) || '',
|
||||||
employee_id: employee.value || null,
|
employee_id: employee.value || null,
|
||||||
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ function onCancelForm() {
|
|||||||
</Cell>
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Label height="compact">Instalasi</Label>
|
<Label height="compact">Instalasi</Label>
|
||||||
<Field :errMessage="errors.installation_id">
|
<Field :errMessage="errors.installation_code">
|
||||||
<Combobox
|
<Combobox
|
||||||
id="installation"
|
id="installation"
|
||||||
v-model="installation"
|
v-model="installation"
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// 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<any>
|
||||||
|
values?: any
|
||||||
|
isLoading?: boolean
|
||||||
|
isReadonly?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||||
|
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: 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)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form
|
||||||
|
id="form-medicine-group"
|
||||||
|
@submit.prevent
|
||||||
|
>
|
||||||
|
<Block
|
||||||
|
labelSize="thin"
|
||||||
|
class="!mb-2.5 !pt-0 xl:!mb-3"
|
||||||
|
:colCount="1"
|
||||||
|
>
|
||||||
|
<Cell>
|
||||||
|
<Label height="">Kode</Label>
|
||||||
|
<Field :errMessage="errors.code">
|
||||||
|
<Input
|
||||||
|
id="code"
|
||||||
|
v-model="code"
|
||||||
|
v-bind="codeAttrs"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Nama</Label>
|
||||||
|
<Field :errMessage="errors.name">
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
v-model="name"
|
||||||
|
v-bind="nameAttrs"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
|
</Block>
|
||||||
|
<div class="my-2 flex justify-end gap-2 py-2">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
class="w-[120px]"
|
||||||
|
@click="onCancelForm"
|
||||||
|
>
|
||||||
|
Kembali
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
v-if="!isReadonly"
|
||||||
|
type="button"
|
||||||
|
class="w-[120px]"
|
||||||
|
:disabled="isLoading || !meta.valid"
|
||||||
|
@click="onSubmitForm"
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -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: {},
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
import { config } from './list-cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -18,6 +18,7 @@ interface Props {
|
|||||||
isReadonly?: boolean
|
isReadonly?: boolean
|
||||||
medicineGroups?: { value: string; label: string }[]
|
medicineGroups?: { value: string; label: string }[]
|
||||||
medicineMethods?: { value: string; label: string }[]
|
medicineMethods?: { value: string; label: string }[]
|
||||||
|
medicineForms?: { value: string; label: string }[]
|
||||||
uoms?: { value: string; label: string }[]
|
uoms?: { value: string; label: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ const { defineField, errors, meta } = useForm({
|
|||||||
name: '',
|
name: '',
|
||||||
medicineGroup_code: '',
|
medicineGroup_code: '',
|
||||||
medicineMethod_code: '',
|
medicineMethod_code: '',
|
||||||
|
medicineForm_code: '',
|
||||||
uom_code: '',
|
uom_code: '',
|
||||||
stock: 0,
|
stock: 0,
|
||||||
},
|
},
|
||||||
@@ -45,6 +47,7 @@ const [code, codeAttrs] = defineField('code')
|
|||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [medicineGroup_code, medicineGroupAttrs] = defineField('medicineGroup_code')
|
const [medicineGroup_code, medicineGroupAttrs] = defineField('medicineGroup_code')
|
||||||
const [medicineMethod_code, medicineMethodAttrs] = defineField('medicineMethod_code')
|
const [medicineMethod_code, medicineMethodAttrs] = defineField('medicineMethod_code')
|
||||||
|
const [medicineForm_code, medicineFormAttrs] = defineField('medicineForm_code')
|
||||||
const [uom_code, uomAttrs] = defineField('uom_code')
|
const [uom_code, uomAttrs] = defineField('uom_code')
|
||||||
const [stock, stockAttrs] = defineField('stock')
|
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.name !== undefined) name.value = props.values.name
|
||||||
if (props.values.medicineGroup_code !== undefined) medicineGroup_code.value = props.values.medicineGroup_code
|
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.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.uom_code !== undefined) uom_code.value = props.values.uom_code
|
||||||
if (props.values.stock !== undefined) stock.value = props.values.stock
|
if (props.values.stock !== undefined) stock.value = props.values.stock
|
||||||
}
|
}
|
||||||
@@ -62,6 +66,7 @@ const resetForm = () => {
|
|||||||
name.value = ''
|
name.value = ''
|
||||||
medicineGroup_code.value = ''
|
medicineGroup_code.value = ''
|
||||||
medicineMethod_code.value = ''
|
medicineMethod_code.value = ''
|
||||||
|
medicineForm_code.value = '',
|
||||||
uom_code.value = ''
|
uom_code.value = ''
|
||||||
stock.value = 0
|
stock.value = 0
|
||||||
}
|
}
|
||||||
@@ -72,6 +77,7 @@ function onSubmitForm() {
|
|||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
medicineGroup_code: medicineGroup_code.value || '',
|
medicineGroup_code: medicineGroup_code.value || '',
|
||||||
medicineMethod_code: medicineMethod_code.value || '',
|
medicineMethod_code: medicineMethod_code.value || '',
|
||||||
|
medicineForm_code: medicineForm_code.value || '',
|
||||||
uom_code: uom_code.value || '',
|
uom_code: uom_code.value || '',
|
||||||
stock: stock.value || 0,
|
stock: stock.value || 0,
|
||||||
}
|
}
|
||||||
@@ -138,6 +144,20 @@ function onCancelForm() {
|
|||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
</Cell>
|
</Cell>
|
||||||
|
<Cell>
|
||||||
|
<Label height="compact">Sediaan Obat</Label>
|
||||||
|
<Field :errMessage="errors.medicineForm_code">
|
||||||
|
<Select
|
||||||
|
id="medicineForm_code"
|
||||||
|
v-model="medicineForm_code"
|
||||||
|
icon-name="i-lucide-chevron-down"
|
||||||
|
placeholder="Pilih metode pemberian"
|
||||||
|
v-bind="medicineFormAttrs"
|
||||||
|
:items="props.medicineForms || []"
|
||||||
|
:disabled="isLoading || isReadonly"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Label>Satuan</Label>
|
<Label>Satuan</Label>
|
||||||
<Field :errMessage="errors.uom_code">
|
<Field :errMessage="errors.uom_code">
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ export const config: Config = {
|
|||||||
{ label: 'Golongan' },
|
{ label: 'Golongan' },
|
||||||
{ label: 'Metode Pemberian' },
|
{ label: 'Metode Pemberian' },
|
||||||
{ label: 'Satuan' },
|
{ label: 'Satuan' },
|
||||||
|
{ label: 'Sediaan' },
|
||||||
{ label: 'Stok' },
|
{ label: 'Stok' },
|
||||||
{ label: 'Aksi' },
|
{ label: 'Aksi' },
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
keys: ['code', 'name', 'group', 'method', 'unit', 'stock', 'action'],
|
keys: ['code', 'name', 'group', 'method', 'unit', 'form', 'stock', 'action'],
|
||||||
|
|
||||||
delKeyNames: [
|
delKeyNames: [
|
||||||
{ key: 'code', label: 'Kode' },
|
{ key: 'code', label: 'Kode' },
|
||||||
@@ -37,6 +38,9 @@ export const config: Config = {
|
|||||||
unit: (rec: unknown): unknown => {
|
unit: (rec: unknown): unknown => {
|
||||||
return (rec as SmallDetailDto).uom?.name || '-'
|
return (rec as SmallDetailDto).uom?.name || '-'
|
||||||
},
|
},
|
||||||
|
form: (rec: unknown): unknown => {
|
||||||
|
return (rec as SmallDetailDto).medicineForm?.name || '-'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@@ -11,16 +11,17 @@ export const config: Config = {
|
|||||||
headers: [
|
headers: [
|
||||||
[
|
[
|
||||||
{ label: 'Nama' },
|
{ label: 'Nama' },
|
||||||
|
{ label: 'Cara Buat' },
|
||||||
{ label: 'Bentuk' },
|
{ label: 'Bentuk' },
|
||||||
{ label: 'Freq' },
|
{ label: 'Freq' },
|
||||||
{ label: 'Dosis' },
|
{ label: 'Dosis' },
|
||||||
{ label: 'Interval' },
|
// { label: 'Interval' },
|
||||||
{ label: 'Total' },
|
{ label: 'Total' },
|
||||||
{ label: '' },
|
{ label: '' },
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
keys: ['name', 'uom_code', 'frequency', 'multiplier', 'interval', 'total', 'action'],
|
keys: ['medicine.name', 'isMix', 'medicine.medicineForm.name', 'frequency', 'dose', 'quantity', 'action'], //
|
||||||
|
|
||||||
delKeyNames: [
|
delKeyNames: [
|
||||||
{ key: 'code', label: 'Kode' },
|
{ key: 'code', label: 'Kode' },
|
||||||
@@ -28,17 +29,8 @@ export const config: Config = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
parses: {
|
parses: {
|
||||||
cateogry: (rec: unknown): unknown => {
|
isMix: (rec: unknown): unknown => {
|
||||||
return (rec as SmallDetailDto).medicineCategory?.name || '-'
|
return (rec as SmallDetailDto).isMix ? 'Racikan' : 'Non Racikan'
|
||||||
},
|
|
||||||
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 || '-'
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ export const config: Config = {
|
|||||||
{ label: 'Bentuk' },
|
{ label: 'Bentuk' },
|
||||||
{ label: 'Freq' },
|
{ label: 'Freq' },
|
||||||
{ label: 'Dosis' },
|
{ label: 'Dosis' },
|
||||||
{ label: 'Interval' },
|
|
||||||
{ label: 'Total' },
|
{ label: 'Total' },
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
keys: ['name', 'uom_code', 'frequency', 'multiplier', 'interval', 'total'],
|
keys: ['medicine.name', 'medicine.medicineForm.name', 'frequency', 'dose', 'total'],
|
||||||
|
|
||||||
delKeyNames: [
|
delKeyNames: [
|
||||||
{ key: 'code', label: 'Kode' },
|
{ key: 'code', label: 'Kode' },
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||||
|
|
||||||
import type { PrescriptionItem } from '~/models/prescription-item';
|
import type { PrescriptionItem } from '~/models/prescription-item';
|
||||||
import { config } from './list.cfg'
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
@@ -13,7 +15,7 @@ const emit = defineEmits<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PubMyUiDataTable class="border mb-2 2xl:mb-3"
|
<DataTable class="border mb-2"
|
||||||
v-bind="config"
|
v-bind="config"
|
||||||
:rows="data"
|
:rows="data"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,29 +1,36 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { LucidePlus } from 'lucide-vue-next';
|
|
||||||
|
|
||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
import Separator from '~/components/pub/ui/separator/Separator.vue';
|
import Separator from '~/components/pub/ui/separator/Separator.vue';
|
||||||
import * as Table from '~/components/pub/ui/table'
|
import * as Table from '~/components/pub/ui/table'
|
||||||
import Nav from '~/components/pub/my-ui/nav-footer/cl-sa.vue'
|
import Nav from '~/components/pub/my-ui/nav-footer/cl-sa.vue'
|
||||||
|
import * as CB from '~/components/pub/my-ui/combobox'
|
||||||
|
|
||||||
import { genBase } from '~/models/_base';
|
import { genBase } from '~/models/_base';
|
||||||
import { genMedicine } from '~/models/medicine';
|
import { type Medicine, genMedicine } from '~/models/medicine';
|
||||||
import type { MedicinemixItem } from '~/models/medicinemix-item';
|
import type { MedicinemixItem } from '~/models/medicinemix-item';
|
||||||
import type { PrescriptionItem } from '~/models/prescription-item';
|
import type { PrescriptionItem } from '~/models/prescription-item';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: PrescriptionItem
|
data: PrescriptionItem
|
||||||
items: MedicinemixItem[]
|
items: MedicinemixItem[]
|
||||||
|
medicines: Medicine[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { medicines } = toRefs(props)
|
||||||
|
const medicineItems = ref<CB.Item[]>([])
|
||||||
|
|
||||||
type ClickType = 'close' | 'save'
|
type ClickType = 'close' | 'save'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
close: [],
|
close: [],
|
||||||
save: [data: PrescriptionItem, items: MedicinemixItem[]],
|
save: [data: PrescriptionItem, items: MedicinemixItem[]],
|
||||||
|
'update:searchText': [value: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
watch(medicines, (data) => {
|
||||||
|
medicineItems.value = CB.objectsToItems(data, 'code', 'name')
|
||||||
|
})
|
||||||
|
|
||||||
function navClick(type: ClickType) {
|
function navClick(type: ClickType) {
|
||||||
if (type === 'close') {
|
if (type === 'close') {
|
||||||
emit('close')
|
emit('close')
|
||||||
@@ -42,33 +49,43 @@ function addItem() {
|
|||||||
uom_code: '',
|
uom_code: '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function searchMedicineText(value: string) {
|
||||||
|
emit('update:searchText', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteItem(idx: number) {
|
||||||
|
props.items.splice(idx, 1)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DE.Block :colCount="5" :cellFlex="false">
|
<DE.Block :colCount="4" :cellFlex="false">
|
||||||
<DE.Cell :colSpan="5">
|
<DE.Cell :colSpan="4">
|
||||||
<DE.Label>Nama</DE.Label>
|
<DE.Label>Nama</DE.Label>
|
||||||
<DE.Field><Input :value="data.medicineMix?.name" /></DE.Field>
|
<DE.Field>
|
||||||
|
<Input :value="data.medicineMix?.name" />
|
||||||
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label>Frequensi</DE.Label>
|
<DE.Label>Frequensi</DE.Label>
|
||||||
<DE.Field><Input v-model="data.frequency" /></DE.Field>
|
<DE.Field><Input v-model="data.frequency" /></DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label>Dosis</DE.Label>
|
<DE.Label>Dosis</DE.Label>
|
||||||
<DE.Field><Input v-model="data.dose" /></DE.Field>
|
<DE.Field><Input v-model="data.dose" /></DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label>Sediaan</DE.Label>
|
<DE.Label>Sediaan</DE.Label>
|
||||||
<DE.Field><Input :value="data.medicineMix?.uom_code" /></DE.Field>
|
<DE.Field><Input :value="data.medicineMix?.uom_code" /></DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label>Total</DE.Label>
|
<DE.Label>Total</DE.Label>
|
||||||
<DE.Field><Input /></DE.Field>
|
<DE.Field><Input v-model="data.quantity" /></DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell :colSpan="5">
|
<DE.Cell :colSpan="4">
|
||||||
<DE.Label>Cara Pakai</DE.Label>
|
<DE.Label>Cara Pakai</DE.Label>
|
||||||
<DE.Field><Input /></DE.Field>
|
<DE.Field><Input v-model="data.usage" /></DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
<div class="text-sm 2xl:text-base font-semibold !mb-3">Daftar Obat</div>
|
<div class="text-sm 2xl:text-base font-semibold !mb-3">Daftar Obat</div>
|
||||||
@@ -77,20 +94,29 @@ function addItem() {
|
|||||||
<Table.TableRow>
|
<Table.TableRow>
|
||||||
<Table.TableHead>Nama</Table.TableHead>
|
<Table.TableHead>Nama</Table.TableHead>
|
||||||
<Table.TableHead class="w-24">Dosis</Table.TableHead>
|
<Table.TableHead class="w-24">Dosis</Table.TableHead>
|
||||||
<Table.TableHead class="w-24">Satuan</Table.TableHead>
|
<!-- <Table.TableHead class="w-24">Satuan</Table.TableHead> -->
|
||||||
<Table.TableHead class="w-20">..</Table.TableHead>
|
<Table.TableHead class="w-10"></Table.TableHead>
|
||||||
</Table.TableRow>
|
</Table.TableRow>
|
||||||
</Table.TableHeader>
|
</Table.TableHeader>
|
||||||
<Table.TableBody class="[&_td]:p-0.6">
|
<Table.TableBody class="[&_td]:p-0.6">
|
||||||
<Table.TableRow v-if="items.length > 0" v-for="item in items">
|
<Table.TableRow v-if="items.length > 0" v-for="item, idx in items">
|
||||||
<Table.TableCell>
|
<Table.TableCell>
|
||||||
<Input v-model="item.medicine.name" />
|
<CB.Combobox
|
||||||
|
v-model="data.medicine_code"
|
||||||
|
:items="medicineItems"
|
||||||
|
@update:searchText="searchMedicineText"
|
||||||
|
/>
|
||||||
</Table.TableCell>
|
</Table.TableCell>
|
||||||
<Table.TableCell>
|
<Table.TableCell>
|
||||||
<Input v-model="item.dose" />
|
<Input v-model="item.dose" />
|
||||||
</Table.TableCell>
|
</Table.TableCell>
|
||||||
<Table.TableCell>
|
<!-- <Table.TableCell>
|
||||||
<Input />
|
<Input />
|
||||||
|
</Table.TableCell> -->
|
||||||
|
<Table.TableCell class="text-center">
|
||||||
|
<Button class="!w-7 !h-7 !p-0 rounded-full" @click="deleteItem(idx)">
|
||||||
|
<Icon name="i-lucide-trash" />
|
||||||
|
</Button>
|
||||||
</Table.TableCell>
|
</Table.TableCell>
|
||||||
</Table.TableRow>
|
</Table.TableRow>
|
||||||
<Table.TableRow v-else>
|
<Table.TableRow v-else>
|
||||||
@@ -102,7 +128,7 @@ function addItem() {
|
|||||||
</Table.Table>
|
</Table.Table>
|
||||||
<div>
|
<div>
|
||||||
<Button @click="addItem">
|
<Button @click="addItem">
|
||||||
<LucidePlus />
|
<Icon name="i-lucide-plus" class="me-2" />
|
||||||
Tambah
|
Tambah
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,40 +2,45 @@
|
|||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
||||||
import Nav from '~/components/pub/my-ui/nav-footer/cl-sa.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'
|
import type { PrescriptionItem } from '~/models/prescription-item'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: PrescriptionItem
|
data: PrescriptionItem
|
||||||
|
medicines: Medicine[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { medicines } = toRefs(props)
|
||||||
|
const medicineItems = ref<CB.Item[]>([])
|
||||||
|
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 ClickType = 'close' | 'save'
|
||||||
type Item = {
|
type Item = {
|
||||||
value: string
|
value: string
|
||||||
label: string
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const bigTimeUnitCodeItems: Item[] = []
|
|
||||||
|
|
||||||
if(!props.data.intervalUnit_code) {
|
if(!props.data.intervalUnit_code) {
|
||||||
props.data.intervalUnit_code = 'day'
|
props.data.intervalUnit_code = 'day'
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(bigTimeUnitCodes).forEach((key) => {
|
|
||||||
bigTimeUnitCodeItems.push({
|
|
||||||
value: key,
|
|
||||||
label: bigTimeUnitCodes[key] || '',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
close: [],
|
close: [],
|
||||||
save: [data: PrescriptionItem],
|
save: [data: PrescriptionItem],
|
||||||
|
'update:searchText': [value: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
watch(medicines, (data) => {
|
||||||
|
medicineItems.value = CB.objectsToItems(data, 'code', 'name')
|
||||||
|
})
|
||||||
|
|
||||||
function navClick(type: ClickType) {
|
function navClick(type: ClickType) {
|
||||||
if (type === 'close') {
|
if (type === 'close') {
|
||||||
emit('close')
|
emit('close')
|
||||||
@@ -43,13 +48,23 @@ function navClick(type: ClickType) {
|
|||||||
emit('save', props.data)
|
emit('save', props.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function searchMedicineText(value: string) {
|
||||||
|
emit('update:searchText', value)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DE.Block :colCount="5" :cellFlex="false">
|
<DE.Block :colCount="4" :cellFlex="false">
|
||||||
<DE.Cell :colSpan="5">
|
<DE.Cell :colSpan="4">
|
||||||
<DE.Label>Nama</DE.Label>
|
<DE.Label>Nama</DE.Label>
|
||||||
<DE.Field><Input :value="data.medicineMix?.name" /></DE.Field>
|
<DE.Field>
|
||||||
|
<CB.Combobox
|
||||||
|
v-model="data.medicine_code"
|
||||||
|
:items="medicineItems"
|
||||||
|
@update:searchText="searchMedicineText"
|
||||||
|
/>
|
||||||
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label>Frequensi</DE.Label>
|
<DE.Label>Frequensi</DE.Label>
|
||||||
@@ -59,11 +74,7 @@ function navClick(type: ClickType) {
|
|||||||
<DE.Label>Dosis</DE.Label>
|
<DE.Label>Dosis</DE.Label>
|
||||||
<DE.Field><Input type="number" v-model.number="data.dose" /></DE.Field>
|
<DE.Field><Input type="number" v-model.number="data.dose" /></DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<!-- <DE.Cell>
|
||||||
<DE.Label>Sediaan</DE.Label>
|
|
||||||
<DE.Field><Input :value="data.medicineMix?.uom_code" readonly /></DE.Field>
|
|
||||||
</DE.Cell>
|
|
||||||
<DE.Cell>
|
|
||||||
<DE.Label>Interval</DE.Label>
|
<DE.Label>Interval</DE.Label>
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
<Select
|
<Select
|
||||||
@@ -71,16 +82,20 @@ function navClick(type: ClickType) {
|
|||||||
:items="bigTimeUnitCodeItems"
|
:items="bigTimeUnitCodeItems"
|
||||||
/>
|
/>
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell> -->
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label>Total</DE.Label>
|
<DE.Label>Total</DE.Label>
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
<Input v-model="data.quantity" />
|
<Input type="number" v-model="data.quantity" />
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell :colSpan="5">
|
<DE.Cell>
|
||||||
|
<DE.Label>Sediaan</DE.Label>
|
||||||
|
<DE.Field><Input :value="medicineForm" readonly /></DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell :colSpan="4">
|
||||||
<DE.Label>Cara Pakai</DE.Label>
|
<DE.Label>Cara Pakai</DE.Label>
|
||||||
<DE.Field><Input /></DE.Field>
|
<DE.Field><Input v-model="data.usage" /></DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
<Separator class="my-5" />
|
<Separator class="my-5" />
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const props = defineProps<{
|
|||||||
Order {{ data.issuedAt?.substring(0, 10) || data.createdAt?.substring(0, 10) }} - {{ data.status_code }}
|
Order {{ data.issuedAt?.substring(0, 10) || data.createdAt?.substring(0, 10) }} - {{ data.status_code }}
|
||||||
</div>
|
</div>
|
||||||
<div class="max-w-[1000px]">
|
<div class="max-w-[1000px]">
|
||||||
<DE.Block mode="preview" :col-count=5 class="!mb-3">
|
<DE.Block mode="preview" label-size="sm" :col-count=5 class="!mb-3">
|
||||||
<DE.Cell :col-span="2">
|
<DE.Cell :col-span="2">
|
||||||
<DE.Label class="font-semibold">DPJP</DE.Label>
|
<DE.Label class="font-semibold">DPJP</DE.Label>
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Prescription } from '~/models/prescription';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: Prescription
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="md:grid md:grid-cols-2 font-semibold">
|
<div class="md:grid md:grid-cols-2">
|
||||||
<div class="md:pe-10">
|
<div class="md:pe-10">
|
||||||
<PubMyUiDocEntryBlock>
|
<PubMyUiDocEntryBlock>
|
||||||
<PubMyUiDocEntryCell>
|
<PubMyUiDocEntryCell>
|
||||||
<PubMyUiDocEntryLabel>Tgl Order</PubMyUiDocEntryLabel>
|
<PubMyUiDocEntryLabel>Tgl Order</PubMyUiDocEntryLabel>
|
||||||
<PubMyUiDocEntryField>
|
<PubMyUiDocEntryField>
|
||||||
<Input />
|
<Input :value="data.issuedAt || data.createdAt?.substring(0, 10)" readonly />
|
||||||
</PubMyUiDocEntryField>
|
</PubMyUiDocEntryField>
|
||||||
</PubMyUiDocEntryCell>
|
</PubMyUiDocEntryCell>
|
||||||
<PubMyUiDocEntryCell>
|
<PubMyUiDocEntryCell>
|
||||||
<PubMyUiDocEntryLabel>Status</PubMyUiDocEntryLabel>
|
<PubMyUiDocEntryLabel>Status</PubMyUiDocEntryLabel>
|
||||||
<PubMyUiDocEntryField>
|
<PubMyUiDocEntryField>
|
||||||
<Input />
|
<Input v-model="data.status_code" readonly />
|
||||||
</PubMyUiDocEntryField>
|
</PubMyUiDocEntryField>
|
||||||
</PubMyUiDocEntryCell>
|
</PubMyUiDocEntryCell>
|
||||||
</PubMyUiDocEntryBlock>
|
</PubMyUiDocEntryBlock>
|
||||||
@@ -21,16 +29,22 @@
|
|||||||
<PubMyUiDocEntryCell>
|
<PubMyUiDocEntryCell>
|
||||||
<PubMyUiDocEntryLabel position="dynamic">DPJP</PubMyUiDocEntryLabel>
|
<PubMyUiDocEntryLabel position="dynamic">DPJP</PubMyUiDocEntryLabel>
|
||||||
<PubMyUiDocEntryField>
|
<PubMyUiDocEntryField>
|
||||||
<Input />
|
<Input v-model="data.doctor.employee.person.name" readonly />
|
||||||
</PubMyUiDocEntryField>
|
</PubMyUiDocEntryField>
|
||||||
</PubMyUiDocEntryCell>
|
</PubMyUiDocEntryCell>
|
||||||
<PubMyUiDocEntryCell>
|
<PubMyUiDocEntryCell>
|
||||||
<PubMyUiDocEntryLabel position="dynamic">PPDS</PubMyUiDocEntryLabel>
|
<PubMyUiDocEntryLabel position="dynamic">PPDS</PubMyUiDocEntryLabel>
|
||||||
<PubMyUiDocEntryField>
|
<PubMyUiDocEntryField>
|
||||||
<Input />
|
<Input value="......" readonly />
|
||||||
</PubMyUiDocEntryField>
|
</PubMyUiDocEntryField>
|
||||||
</PubMyUiDocEntryCell>
|
</PubMyUiDocEntryCell>
|
||||||
</PubMyUiDocEntryBlock>
|
</PubMyUiDocEntryBlock>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- <div class="">
|
||||||
|
<Button>
|
||||||
|
<Icon name="i-lucide-check" />
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</div> -->
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as Table from '~/components/pub/ui/table'
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import Nav from '~/components/pub/my-ui/nav-footer/ca-ed-su.vue'
|
||||||
|
|
||||||
|
import type { Prescription } from '~/models/prescription';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: Prescription[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
cancel: [data: any]
|
||||||
|
edit: [data: any],
|
||||||
|
submit: [data: any]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function navClick(type: 'cancel' | 'edit' | 'submit', data: Prescription): void {
|
||||||
|
if (type === 'cancel') {
|
||||||
|
emit('cancel', data)
|
||||||
|
} else if (type === 'edit') {
|
||||||
|
emit('edit', data)
|
||||||
|
} else if (type === 'submit') {
|
||||||
|
emit('submit', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="data.length == 0" class="p-10 text-center">
|
||||||
|
<div class="mb-4 xl:mb-5">Belum Ada Data</div>
|
||||||
|
<div>
|
||||||
|
<Button>
|
||||||
|
<Icon name="i-lucide-plus" class="me-2 align-middle" />
|
||||||
|
Tambah Order
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table.Table>
|
||||||
|
<Table.TableHeader>
|
||||||
|
<Table.TableRow>
|
||||||
|
<Table.TableHead class="w-28">Tgl Order</Table.TableHead>
|
||||||
|
<Table.TableHead>DPJP</Table.TableHead>
|
||||||
|
<Table.TableHead>PPDS</Table.TableHead>
|
||||||
|
<Table.TableHead>Jenis Obat</Table.TableHead>
|
||||||
|
<Table.TableHead class="text-center">Status</Table.TableHead>
|
||||||
|
<Table.TableHead class="w-20"></Table.TableHead>
|
||||||
|
</Table.TableRow>
|
||||||
|
</Table.TableHeader>
|
||||||
|
<Table.TableBody>
|
||||||
|
<Table.TableRow v-for="item, idx in data">
|
||||||
|
<Table.TableCell>
|
||||||
|
{{ item.issuedAt?.substring(0, 10) || item.createdAt?.substring(0, 10) }}
|
||||||
|
</Table.TableCell>
|
||||||
|
<Table.TableCell>
|
||||||
|
{{ item.doctor?.employee?.person?.name || '-' }}
|
||||||
|
</Table.TableCell>
|
||||||
|
<Table.TableCell>
|
||||||
|
</Table.TableCell>
|
||||||
|
<Table.TableCell>
|
||||||
|
<div>
|
||||||
|
Racikan: {{ item.items.filter(function(element){ return element.isMix}).length }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Non Racikan: {{ item.items.filter(function(element){ return !element.isMix}).length }}
|
||||||
|
</div>
|
||||||
|
</Table.TableCell>
|
||||||
|
<Table.TableCell class="text-center">
|
||||||
|
{{ item.status_code }}
|
||||||
|
</Table.TableCell>
|
||||||
|
<Table.TableCell class="text-end">
|
||||||
|
<Nav
|
||||||
|
v-if="item.status_code == 'new'"
|
||||||
|
:small-mode="true"
|
||||||
|
:default-class="'flex gap-1'"
|
||||||
|
@click="(type) => { navClick(type, item) }"
|
||||||
|
/>
|
||||||
|
</Table.TableCell>
|
||||||
|
</Table.TableRow>
|
||||||
|
</Table.TableBody>
|
||||||
|
</Table.Table>
|
||||||
|
<template v-for="item, idx in data">
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
+1
-2
@@ -5,7 +5,6 @@ import Nav from '~/components/pub/my-ui/nav-footer/ca-ed-su.vue'
|
|||||||
|
|
||||||
import type { Prescription } from '~/models/prescription';
|
import type { Prescription } from '~/models/prescription';
|
||||||
import PrescriptionItem from '~/components/app/prescription-item/list.vue';
|
import PrescriptionItem from '~/components/app/prescription-item/list.vue';
|
||||||
import { add } from 'date-fns';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: Prescription[]
|
data: Prescription[]
|
||||||
@@ -66,7 +65,7 @@ function navClick(type: 'cancel' | 'edit' | 'submit', data: Prescription): void
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
<PrescriptionItem :data="item.items || []" @click="console.log('click')" class="mb-10" />
|
<PrescriptionItem :data="item.items || []" class="mb-10" />
|
||||||
<!-- <div v-if="idx < data.length - 1" class="my-8 -mx-4 border-t border-t-slate-300" /> -->
|
<!-- <div v-if="idx < data.length - 1" class="my-8 -mx-4 border-t border-t-slate-300" /> -->
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import { Block, Cell } from '~/components/pub/my-ui/doc-entry/index'
|
import * as DE from '~/components/pub/my-ui/doc-entry';
|
||||||
// import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
|
||||||
// import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -17,40 +15,40 @@
|
|||||||
<Separator class="my-5" />
|
<Separator class="my-5" />
|
||||||
<div class="md:grid md:grid-cols-2 font-semibold">
|
<div class="md:grid md:grid-cols-2 font-semibold">
|
||||||
<div>
|
<div>
|
||||||
<PubCustomUiDocEntryBlock mode="preview">
|
<DE.Block mode="preview">
|
||||||
<PubCustomUiDocEntryCell>
|
<DE.Cell>
|
||||||
<PubCustomUiDocEntryLabel>Order #1</PubCustomUiDocEntryLabel>
|
<DE.Label>Order #1</DE.Label>
|
||||||
<PubCustomUiDocEntryColon />
|
<DE.Colon />
|
||||||
<PubCustomUiDocEntryField>
|
<DE.Field>
|
||||||
2025-01-01
|
2025-01-01
|
||||||
</PubCustomUiDocEntryField>
|
</DE.Field>
|
||||||
</PubCustomUiDocEntryCell>
|
</DE.Cell>
|
||||||
<PubCustomUiDocEntryCell>
|
<DE.Cell>
|
||||||
<PubCustomUiDocEntryLabel>Status</PubCustomUiDocEntryLabel>
|
<DE.Label>Status</DE.Label>
|
||||||
<PubCustomUiDocEntryColon />
|
<DE.Colon />
|
||||||
<PubCustomUiDocEntryField>
|
<DE.Field>
|
||||||
Status
|
Status
|
||||||
</PubCustomUiDocEntryField>
|
</DE.Field>
|
||||||
</PubCustomUiDocEntryCell>
|
</DE.Cell>
|
||||||
</PubCustomUiDocEntryBlock>
|
</DE.Block>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<PubCustomUiDocEntryBlock mode="preview">
|
<DE.Block mode="preview">
|
||||||
<PubCustomUiDocEntryCell>
|
<DE.Cell>
|
||||||
<PubCustomUiDocEntryLabel>DPJP</PubCustomUiDocEntryLabel>
|
<DE.Label>DPJP</DE.Label>
|
||||||
<PubCustomUiDocEntryColon />
|
<DE.Colon />
|
||||||
<PubCustomUiDocEntryField>
|
<DE.Field>
|
||||||
Nama Dokter
|
Nama Dokter
|
||||||
</PubCustomUiDocEntryField>
|
</DE.Field>
|
||||||
</PubCustomUiDocEntryCell>
|
</DE.Cell>
|
||||||
<PubCustomUiDocEntryCell>
|
<DE.Cell>
|
||||||
<PubCustomUiDocEntryLabel>PPDS</PubCustomUiDocEntryLabel>
|
<DE.Label>PPDS</DE.Label>
|
||||||
<PubCustomUiDocEntryColon />
|
<DE.Colon />
|
||||||
<PubCustomUiDocEntryField>
|
<DE.Field>
|
||||||
Nama PPDS
|
Nama PPDS
|
||||||
</PubCustomUiDocEntryField>
|
</DE.Field>
|
||||||
</PubCustomUiDocEntryCell>
|
</DE.Cell>
|
||||||
</PubCustomUiDocEntryBlock>
|
</DE.Block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
url: string
|
||||||
|
btnTxt?: string
|
||||||
|
rec: ListItemDto
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePrint() {
|
||||||
|
navigateTo(props.url || 'https://google.com', {external: true,open: { target: "_blank" },});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button
|
||||||
|
class="gap-3 items-center border-orange-400 text-orange-400"
|
||||||
|
variant="outline" @click="handlePrint">
|
||||||
|
<Icon name="i-lucide-printer" class="h-4 w-4" />
|
||||||
|
{{ props.btnTxt || 'Lampiran' }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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,
|
||||||
|
placeholder,
|
||||||
|
errors,
|
||||||
|
class: containerClass,
|
||||||
|
fieldGroupClass,
|
||||||
|
labelClass,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const arrangementTypeOpts = [
|
||||||
|
{ label: 'KRS', value: "krs" },
|
||||||
|
{ label: 'MRS', value: "mrs" },
|
||||||
|
{ label: 'Rujuk Internal', value: "rujukInternal" },
|
||||||
|
{ label: 'Rujuk External', value: "rujukExternal" },
|
||||||
|
{ label: 'Meninggal', value: "meninggal" },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
v-show="label"
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField :name="fieldName" v-slot="{ componentField, value }">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
v-bind="componentField"
|
||||||
|
:model-value="value"
|
||||||
|
:items="arrangementTypeOpts"
|
||||||
|
:defaultValue='arrangementTypeOpts[0]?.value'
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { differenceInDays, differenceInMonths, differenceInYears, parseISO } from 'date-fns'
|
||||||
|
import { Input } from '~/components/pub/ui/input'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
fieldName?: string
|
||||||
|
label?: string
|
||||||
|
placeholder?: string
|
||||||
|
errors?: FormErrors
|
||||||
|
class?: string
|
||||||
|
selectClass?: string
|
||||||
|
fieldGroupClass?: string
|
||||||
|
labelClass?: string
|
||||||
|
isRequired?: boolean
|
||||||
|
isDisabled?: boolean
|
||||||
|
isWithTime?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const {
|
||||||
|
fieldName = 'birthDate',
|
||||||
|
label = 'Tanggal Lahir',
|
||||||
|
placeholder = 'Pilih tanggal lahir',
|
||||||
|
errors,
|
||||||
|
class: containerClass,
|
||||||
|
fieldGroupClass,
|
||||||
|
labelClass,
|
||||||
|
isWithTime = false,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
// Reactive variables for age calculation
|
||||||
|
const patientAge = ref<string>('Masukkan tanggal lahir')
|
||||||
|
|
||||||
|
// Function to calculate age with years, months, and days
|
||||||
|
function calculateAge(birthDate: string | Date | undefined): string {
|
||||||
|
if (!birthDate) {
|
||||||
|
return 'Masukkan tanggal lahir'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let dateObj: Date
|
||||||
|
|
||||||
|
if (typeof birthDate === 'string') {
|
||||||
|
dateObj = parseISO(birthDate)
|
||||||
|
} else {
|
||||||
|
dateObj = birthDate
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = new Date()
|
||||||
|
|
||||||
|
// Calculate years, months, and days
|
||||||
|
const totalYears = differenceInYears(today, dateObj)
|
||||||
|
|
||||||
|
// Calculate remaining months after years
|
||||||
|
const yearsPassed = new Date(dateObj)
|
||||||
|
yearsPassed.setFullYear(yearsPassed.getFullYear() + totalYears)
|
||||||
|
const remainingMonths = differenceInMonths(today, yearsPassed)
|
||||||
|
|
||||||
|
// Calculate remaining days after years and months
|
||||||
|
const monthsPassed = new Date(yearsPassed)
|
||||||
|
monthsPassed.setMonth(monthsPassed.getMonth() + remainingMonths)
|
||||||
|
const remainingDays = differenceInDays(today, monthsPassed)
|
||||||
|
|
||||||
|
// Format the result
|
||||||
|
const parts = []
|
||||||
|
if (totalYears > 0) parts.push(`${totalYears} Tahun`)
|
||||||
|
if (remainingMonths > 0) parts.push(`${remainingMonths} Bulan`)
|
||||||
|
if (remainingDays > 0) parts.push(`${remainingDays} Hari`)
|
||||||
|
|
||||||
|
return parts.length > 0 ? parts.join(' ') : '0 Hari'
|
||||||
|
} catch {
|
||||||
|
return 'Masukkan tanggal lahir'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired && !isDisabled"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="birthDate"
|
||||||
|
:type="isWithTime ? 'datetime-local' : 'date'"
|
||||||
|
min="1900-01-01"
|
||||||
|
:max="new Date().toISOString().split('T')[0]"
|
||||||
|
v-bind="componentField"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
@update:model-value="
|
||||||
|
(value: string | number) => {
|
||||||
|
const dateStr = typeof value === 'number' ? String(value) : value
|
||||||
|
patientAge = calculateAge(dateStr)
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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,
|
||||||
|
placeholder,
|
||||||
|
errors,
|
||||||
|
class: containerClass,
|
||||||
|
fieldGroupClass,
|
||||||
|
labelClass,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
v-show="label"
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
import { occupationCodes } from '~/lib/constants'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
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,
|
||||||
|
placeholder,
|
||||||
|
errors,
|
||||||
|
class: containerClass,
|
||||||
|
fieldGroupClass,
|
||||||
|
labelClass,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
// Generate job options from constants, sama seperti pola genderCodes
|
||||||
|
const jobOptions = mapToComboboxOptList(occupationCodes)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
||||||
|
<DE.Label
|
||||||
|
v-show="label"
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
:class="cn('select-field-wrapper')"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
class="focus:ring-0 focus:ring-offset-0"
|
||||||
|
:id="fieldName"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="jobOptions"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
search-placeholder="Cari..."
|
||||||
|
empty-message="Data tidak ditemukan"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge, type Variants } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const activeStatusCodes: Record<string, string> = {
|
||||||
|
verified: 'Terverifikasi',
|
||||||
|
validated: 'Tervalidasi',
|
||||||
|
unverified: 'Belum Verifikasi',
|
||||||
|
unvalidated: 'Batal Validasi',
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
let code: keyof typeof activeStatusCodes = `unverified`
|
||||||
|
switch (props.rec.status_code) {
|
||||||
|
case 1:
|
||||||
|
code = 'verified'
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
code = 'validated'
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
code = 'unverified'
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
code = 'unvalidated'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
code = 'unverified'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return activeStatusCodes[code]
|
||||||
|
})
|
||||||
|
|
||||||
|
const badgeVariant = computed(() => {
|
||||||
|
let variant: Variants = `outline`
|
||||||
|
switch (props.rec.status_code) {
|
||||||
|
case 1:
|
||||||
|
variant = 'secondary'
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
variant = 'default'
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
variant = 'outline'
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
variant = 'destructive'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
variant = 'outline'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return variant
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Badge :variant="badgeVariant" class="rounded-2xl text-[0.6rem]" >
|
||||||
|
{{ statusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,482 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
import { FieldArray } from 'vee-validate'
|
||||||
|
import SelectDate from './_common/select-date.vue'
|
||||||
|
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
import TextAreaInput from '~/components/pub/my-ui/form/text-area-input.vue'
|
||||||
|
import SelectArrangement from './_common/select-arrangement.vue'
|
||||||
|
import type { ResumeArrangementType } from '~/schemas/resume.schema'
|
||||||
|
import SelectFaskes from './_common/select-faskes.vue'
|
||||||
|
import SelectDeathCause from './_common/select-death-cause.vue'
|
||||||
|
import SelectIcd10 from './_common/select-icd-10.vue'
|
||||||
|
import SelectIcd9 from './_common/select-icd-9.vue'
|
||||||
|
import SelectConciousLevel from './_common/select-concious-level.vue'
|
||||||
|
import SelectPainScale from './_common/select-pain-scale.vue'
|
||||||
|
import SelectNationalProgramService from './_common/select-national-program-service.vue'
|
||||||
|
import SelectNationalProgramServiceStatus from './_common/select-national-program-service-status.vue'
|
||||||
|
import SelectHospitalLeaveCondition from './_common/select-hospital-leave-condition.vue'
|
||||||
|
import SelectHospitalLeaveMethod from './_common/select-hospital-leave-method.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
schema: any
|
||||||
|
initialValues?: any
|
||||||
|
resumeArrangementType: ResumeArrangementType
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isActionHistoryOpen = inject(`isActionHistoryOpen`) as Ref<boolean>
|
||||||
|
const isConsultationHistoryOpen = inject(`isConsultationHistoryOpen`) as Ref<boolean>
|
||||||
|
const isSupportingHistoryOpen = inject(`isSupportingHistoryOpen`) as Ref<boolean>
|
||||||
|
const isFarmacyHistoryOpen = inject(`isFarmacyHistoryOpen`) as Ref<boolean>
|
||||||
|
const isNationalProgramServiceHistoryOpen = inject(`isNationalProgramServiceHistoryOpen`) as Ref<boolean>
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
validate: () => formRef.value?.validate(),
|
||||||
|
resetForm: () => formRef.value?.resetForm(),
|
||||||
|
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||||
|
values: computed(() => formRef.value?.values),
|
||||||
|
})
|
||||||
|
|
||||||
|
const DEFAULT_SECONDARY_DIAGNOSIS_VALUE = {
|
||||||
|
diagnosis: '',
|
||||||
|
icd10: '',
|
||||||
|
diagnosisBasis: '',
|
||||||
|
};
|
||||||
|
const DEFAULT_SECONDARY_ACTION_VALUE = {
|
||||||
|
action: '',
|
||||||
|
icd9: '',
|
||||||
|
actionBasis: '',
|
||||||
|
};
|
||||||
|
const DEFAULT_CONSULTATION_VALUE = {
|
||||||
|
consultation: '',
|
||||||
|
consultationReply: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialFormValues = {
|
||||||
|
secondaryDiagnosis: [DEFAULT_SECONDARY_DIAGNOSIS_VALUE],
|
||||||
|
secondaryOperativeNonOperativeAct: [DEFAULT_SECONDARY_ACTION_VALUE],
|
||||||
|
consultation: [DEFAULT_CONSULTATION_VALUE],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form ref="formRef"
|
||||||
|
v-slot="{ values }"
|
||||||
|
as=""
|
||||||
|
keep-values
|
||||||
|
:validation-schema="formSchema"
|
||||||
|
:validate-on-mount="false"
|
||||||
|
validation-mode="onSubmit"
|
||||||
|
:initial-values="initialValues ? initialValues : initialFormValues">
|
||||||
|
|
||||||
|
<!-- Pasien -->
|
||||||
|
<h1 class="mb-3 text-base font-medium">Pemeriksaan Pasien</h1>
|
||||||
|
<DE.Block :col-count="3" :cell-flex="false">
|
||||||
|
<SelectDate field-name="inDate" label="Tanggal Masuk" :errors="errors" :is-disabled="true" />
|
||||||
|
<SelectDate field-name="outDate" label="Tanggal Keluar" :errors="errors" :is-disabled="true" />
|
||||||
|
<InputBase
|
||||||
|
field-name="doctor_id"
|
||||||
|
:is-disabled="true"
|
||||||
|
label="DPJP" placeholder="DPJP"/>
|
||||||
|
</DE.Block>
|
||||||
|
<DE.Block class="" :cell-flex="false">
|
||||||
|
<InputBase
|
||||||
|
class="w-2/3"
|
||||||
|
field-name="first_diagnosis"
|
||||||
|
:is-disabled="true"
|
||||||
|
label="Diagnosis Masuk/Rujukan" placeholder="Diagnosis Masuk/Rujukan"/>
|
||||||
|
</DE.Block>
|
||||||
|
<DE.Block :col-count="3" :cell-flex="false">
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Indikasi Rawat Jalan" placeholder="Indikasi Rawat Jalan" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Keluhan Utama (Singkat & Menunjang)" placeholder="Keluhan Utama (Singkat & Menunjang)" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Pemeriksaan Fisik (Singkat & Menunjang)" placeholder="Pemeriksaan Fisik (Singkat & Menunjang)" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Riwayat Penyakit" placeholder="Riwayat Penyakit" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Diagnosa Medis" placeholder="Diagnosa Medis" :errors="errors" />
|
||||||
|
</DE.Block>
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- DIAGNOSIS -->
|
||||||
|
<h1 class="mb-3 text-base font-medium">Diagnosa</h1>
|
||||||
|
<h1 class="mb-3 text-base font-medium">Diagnosa Utama</h1>
|
||||||
|
<DE.Block :col-count="2" class="" :cell-flex="false">
|
||||||
|
<InputBase
|
||||||
|
field-name="diagnosis"
|
||||||
|
label="Diagnosa" placeholder="Masukkan Diagnosa"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<SelectIcd10
|
||||||
|
field-name="primaryDiagnosis"
|
||||||
|
label="ICD 10"
|
||||||
|
placeholder="ICD 10"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Dasar Diagnosa" placeholder="Masukkan Dasar Diagnosa utama Pasien" :errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<h1 class="mb-3 text-base font-medium">Diagnosa Sekunder</h1>
|
||||||
|
<div class="w-full rounded-md border bg-gray-50/50 p-4 shadow-sm dark:bg-neutral-950">
|
||||||
|
|
||||||
|
<FieldArray v-slot="{ fields, push, remove }" name="secondaryDiagnosis">
|
||||||
|
<div v-for="(field, idx) in fields" :key="idx" class="flex items-center gap-3 mb-3">
|
||||||
|
<div class="w-full">
|
||||||
|
<h1 class="font-medium">Diagnosa {{ idx + 1 }}</h1>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<InputBase
|
||||||
|
:field-name="`secondaryDiagnosis[${idx}].diagnosis`"
|
||||||
|
label="Diagnosa" placeholder="Masukkan Diagnosa"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<SelectIcd10
|
||||||
|
:field-name="`secondaryDiagnosis[${idx}].icd10`"
|
||||||
|
label="ICD 10"
|
||||||
|
placeholder="ICD 10"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
</div>
|
||||||
|
<TextAreaInput :field-name="`secondaryDiagnosis[${idx}].diagnosisBasis`"
|
||||||
|
label="Dasar Diagnosa" placeholder="Masukkan Dasar Diagnosa Pasien" :errors="errors" />
|
||||||
|
</div>
|
||||||
|
<Button v-if="idx !== 0" type="button" variant="destructive" size="sm" @click="remove(idx)">
|
||||||
|
<Icon name="i-lucide-trash-2" class="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button type="button" variant="outline"
|
||||||
|
class="w-full rounded-md border border-primary bg-white px-4 py-2 text-primary hover:border-primary hover:bg-primary hover:text-white sm:w-auto sm:text-sm"
|
||||||
|
@click="push(DEFAULT_SECONDARY_DIAGNOSIS_VALUE)">
|
||||||
|
<Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle transition-colors" />
|
||||||
|
Tambah Diagnosis
|
||||||
|
</Button>
|
||||||
|
</FieldArray>
|
||||||
|
</div>
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</DE.Block>
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Tindakan OPERATIF/NON OPERATIF -->
|
||||||
|
<div class="mb-3 flex gap-3 items-center">
|
||||||
|
<h1 class="text-base font-medium">Tindakan Operatif/Non Operatif</h1>
|
||||||
|
<Button variant="ghost"
|
||||||
|
class="gap-1 items-center text-orange-400"
|
||||||
|
@click="isActionHistoryOpen = true">
|
||||||
|
<Icon name="i-lucide-history" class="h-4 w-4" /> Riwayat Tindakan</Button>
|
||||||
|
</div>
|
||||||
|
<h1 class="mb-3 font-normal">Tindakan Operatif/Non Operatif Utama</h1>
|
||||||
|
<DE.Block :col-count="2" class="" :cell-flex="false">
|
||||||
|
<InputBase
|
||||||
|
field-name="diagnosis"
|
||||||
|
label="Tindakan" placeholder="Masukkan Tindakan"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<SelectIcd9
|
||||||
|
field-name="primaryDiagnosis"
|
||||||
|
label="ICD 9"
|
||||||
|
placeholder="ICD 9"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Dasar Tindakan" placeholder="Masukkan Dasar Tindakan utama Pasien" :errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<h1 class="mb-3 text-base font-medium">Tindakan Lain</h1>
|
||||||
|
<div class="w-full rounded-md border bg-gray-50/50 p-4 shadow-sm dark:bg-neutral-950">
|
||||||
|
|
||||||
|
<FieldArray v-slot="{ fields, push, remove }" name="secondaryOperativeNonOperativeAct">
|
||||||
|
<div v-for="(field, idx) in fields" :key="idx" class="flex items-center gap-3 mb-3">
|
||||||
|
<div class="w-full">
|
||||||
|
<h1 class="font-medium">Tindakan {{ idx + 1 }}</h1>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<InputBase
|
||||||
|
:field-name="`secondaryOperativeNonOperativeAct[${idx}].action`"
|
||||||
|
label="Tindakan" placeholder="Masukkan Tindakan"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<SelectIcd10
|
||||||
|
:field-name="`secondaryOperativeNonOperativeAct[${idx}].icd9`"
|
||||||
|
label="ICD 10"
|
||||||
|
placeholder="ICD 10"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
</div>
|
||||||
|
<TextAreaInput :field-name="`secondaryOperativeNonOperativeAct[${idx}].actionBasis`"
|
||||||
|
label="Dasar Tindakan" placeholder="Masukkan Dasar Tindakan Pasien" :errors="errors" />
|
||||||
|
</div>
|
||||||
|
<Button v-if="idx !== 0" type="button" variant="destructive" size="sm" @click="remove(idx)">
|
||||||
|
<Icon name="i-lucide-trash-2" class="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button type="button" variant="outline"
|
||||||
|
class="w-full rounded-md border border-primary bg-white px-4 py-2 text-primary hover:border-primary hover:bg-primary hover:text-white sm:w-auto sm:text-sm"
|
||||||
|
@click="push(DEFAULT_SECONDARY_ACTION_VALUE)">
|
||||||
|
<Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle transition-colors" />
|
||||||
|
Tambah Tindakan
|
||||||
|
</Button>
|
||||||
|
</FieldArray>
|
||||||
|
</div>
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
</DE.Block>
|
||||||
|
<TextAreaInput :field-name="`medicalAction`"
|
||||||
|
label="Tindakan Medis" placeholder="Masukkan Tindakan Medis" :errors="errors" />
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- KONSULTASI -->
|
||||||
|
<div class="mb-3 flex gap-3 items-center">
|
||||||
|
<h1 class="text-base font-medium">Konsultasi</h1>
|
||||||
|
<Button variant="ghost"
|
||||||
|
class="gap-1 items-center text-orange-400"
|
||||||
|
@click="isConsultationHistoryOpen = true">
|
||||||
|
<Icon name="i-lucide-history" class="h-4 w-4" /> Riwayat Konsultasi</Button>
|
||||||
|
</div>
|
||||||
|
<div class="w-full rounded-md border bg-gray-50/50 p-4 shadow-sm dark:bg-neutral-950">
|
||||||
|
<FieldArray v-slot="{ fields, push, remove }" name="consultation">
|
||||||
|
<div v-for="(field, idx) in fields" :key="idx" class="flex items-center gap-3 mb-3">
|
||||||
|
<div class="w-full">
|
||||||
|
<h1 class="font-medium mb-1">Konsultasi {{ idx + 1 }}</h1>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<TextAreaInput :field-name="`consultation[${idx}].consultation`"
|
||||||
|
label="Konsultasi" placeholder="Masukkan Konsultasi" :errors="errors" />
|
||||||
|
<TextAreaInput :field-name="`consultation[${idx}].consultationReply`"
|
||||||
|
label="Jawaban Konsultasi" placeholder="Masukkan Jawaban Konsultasi" :errors="errors" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button v-if="idx !== 0" type="button" variant="destructive" size="sm" @click="remove(idx)">
|
||||||
|
<Icon name="i-lucide-trash-2" class="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button type="button" variant="outline"
|
||||||
|
class="w-full rounded-md border border-primary bg-white px-4 py-2 text-primary hover:border-primary hover:bg-primary hover:text-white sm:w-auto sm:text-sm"
|
||||||
|
@click="push(DEFAULT_CONSULTATION_VALUE)">
|
||||||
|
<Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle transition-colors" />
|
||||||
|
Tambah Konsultasi
|
||||||
|
</Button>
|
||||||
|
</FieldArray>
|
||||||
|
</div>
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- DATA PENUNJANG -->
|
||||||
|
<section>
|
||||||
|
<div class="mb-3 flex gap-3 items-center">
|
||||||
|
<h1 class="text-base font-medium">Data Penunjang</h1>
|
||||||
|
<Button variant="ghost"
|
||||||
|
class="gap-1 items-center text-orange-400"
|
||||||
|
@click="isSupportingHistoryOpen = true">
|
||||||
|
<Icon name="i-lucide-history" class="h-4 w-4" /> Riwayat Data Penunjang</Button>
|
||||||
|
</div>
|
||||||
|
<DE.Block class="" :cell-flex="false">
|
||||||
|
<TextAreaInput field-name="supplementCheckup" label="Pemeriksaan Penunjang" placeholder="Masukkan Pemeriksaan Penunjang" :errors="errors" />
|
||||||
|
</DE.Block>
|
||||||
|
</section>
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- DATA Farmasi -->
|
||||||
|
<section>
|
||||||
|
<div class="mb-3 flex gap-3 items-center">
|
||||||
|
<h1 class="text-base font-medium">Data Farmasi</h1>
|
||||||
|
<Button variant="ghost"
|
||||||
|
class="gap-1 items-center text-orange-400"
|
||||||
|
@click="isFarmacyHistoryOpen = true">
|
||||||
|
<Icon name="i-lucide-history" class="h-4 w-4" /> Riwayat Data Farmasi</Button>
|
||||||
|
</div>
|
||||||
|
<DE.Block class="items-end" :col-count="2" :cell-flex="false">
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Kelainan Khusus Alergi" placeholder="Masukkan Kelainan Khusus Alergi" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Kelainan Lain" placeholder="Masukkan Kelainan Lain" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Terapi yang Diberikan (Farmakologi dan Non Farmakologi) Selama Dirawat" placeholder="Masukkan Terapi yang Diberikan (Farmakologi dan Non Farmakologi) Selama Dirawat" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Terapi yang Diberikan (Farmakologi dan Non Farmakologi) Waktu Pulang" placeholder="Masukkan Terapi yang Diberikan (Farmakologi dan Non Farmakologi) Waktu Pulang" :errors="errors" />
|
||||||
|
<TextAreaInput field-name="supplementCheckup"
|
||||||
|
label="Instruksi Tindak Lanjut/Anjuran dan Edukasi (Follow Up/Kontrol)" placeholder="Masukkan Instruksi Tindak Lanjut/Anjuran dan Edukasi (Follow Up/Kontrol)" :errors="errors" />
|
||||||
|
</DE.Block>
|
||||||
|
</section>
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Keadaan Waktu Keluar -->
|
||||||
|
<section>
|
||||||
|
<h1 class="mb-3 font-medium">Keadaan Waktu Keluar</h1>
|
||||||
|
<DE.Block :col-count="4" :cell-flex="false">
|
||||||
|
<InputBase
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Tekanan Darah Sistol" placeholder="Masukkan Tekanan Darah Sistol"
|
||||||
|
right-label="mmHg" bottom-label="Contoh: 90"
|
||||||
|
:errors="errors" is-required numeric-only />
|
||||||
|
<InputBase
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Tekanan Darah Diastol" placeholder="Masukkan Tekanan Darah Diastol"
|
||||||
|
right-label="mmHg" bottom-label="Contoh: 60"
|
||||||
|
:errors="errors" is-required numeric-only />
|
||||||
|
<InputBase
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Pernafasan" placeholder="Masukkan Pernafasan"
|
||||||
|
right-label="kali / menit" bottom-label="Contoh: 20"
|
||||||
|
:errors="errors" is-required numeric-only />
|
||||||
|
<InputBase
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Denyut Jantung" placeholder="Masukkan Denyut Jantung"
|
||||||
|
right-label="kali / menit" bottom-label="Contoh: 20"
|
||||||
|
:errors="errors" is-required numeric-only />
|
||||||
|
<InputBase
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Suhu Tubuh" placeholder="Masukkan Suhu Tubuh"
|
||||||
|
right-label="'C" bottom-label="Contoh: 37"
|
||||||
|
:errors="errors" is-required numeric-only />
|
||||||
|
<SelectConciousLevel
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Tingkat Kesadaran"
|
||||||
|
placeholder="Tingkat Kesadaran"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<SelectPainScale
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Skala Nyeri"
|
||||||
|
placeholder="Skala Nyeri"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
</DE.Block>
|
||||||
|
</section>
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Program Nasional -->
|
||||||
|
<section>
|
||||||
|
<div class="mb-3 flex gap-3 items-center">
|
||||||
|
<h1 class="text-base font-medium">Program Nasional</h1>
|
||||||
|
<Button variant="ghost"
|
||||||
|
class="gap-1 items-center text-orange-400"
|
||||||
|
@click="isNationalProgramServiceHistoryOpen = true">
|
||||||
|
<Icon name="i-lucide-history" class="h-4 w-4" /> Riwayat Program Nasional</Button>
|
||||||
|
</div>
|
||||||
|
<DE.Block :col-count="3" :cell-flex="false">
|
||||||
|
<SelectNationalProgramService
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Layanan Program Nasional"
|
||||||
|
placeholder="Layanan Program Nasional"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<SelectNationalProgramServiceStatus
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Status Layanan Program Nasional"
|
||||||
|
placeholder="Status Layanan Program Nasional"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
</DE.Block>
|
||||||
|
</section>
|
||||||
|
<Separator class="my-4" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Penatalaksanaan -->
|
||||||
|
<section>
|
||||||
|
<h1 class="mb-3 font-medium">Penatalaksanaan</h1>
|
||||||
|
<DE.Block :col-count="3" :cell-flex="false">
|
||||||
|
<SelectArrangement
|
||||||
|
field-name="arrangement"
|
||||||
|
label="Lanjutan Penatalaksanaan"
|
||||||
|
placeholder="Pilih Arrangement"
|
||||||
|
:errors="errors" />
|
||||||
|
<SelectHospitalLeaveCondition
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Kondisi Meninggalkan RS"
|
||||||
|
placeholder="Kondisi Meninggalkan RS"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
<SelectHospitalLeaveMethod
|
||||||
|
:field-name="`aaaaaaaaaaa`"
|
||||||
|
label="Cara Keluar RS"
|
||||||
|
placeholder="Cara Keluar RS"
|
||||||
|
:errors="errors" is-required />
|
||||||
|
</DE.Block>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<DE.Cell :col-span="3"
|
||||||
|
v-show="resumeArrangementType === `mrs`">
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="inpatientIndication"
|
||||||
|
label="Indikasi Rawat Jalan"
|
||||||
|
placeholder="Indikasi Rawat Jalan"
|
||||||
|
:errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
<DE.Block :col-count="3" :cell-flex="false">
|
||||||
|
<SelectFaskes
|
||||||
|
v-show="resumeArrangementType === `rujukExternal` "
|
||||||
|
field-name="faskes"
|
||||||
|
label="Faskes"
|
||||||
|
placeholder="Pilih Faskes"
|
||||||
|
:errors="errors"
|
||||||
|
/>
|
||||||
|
<InputBase
|
||||||
|
v-show="resumeArrangementType === `rujukExternal` "
|
||||||
|
field-name="clinic"
|
||||||
|
label="Klinik"
|
||||||
|
placeholder="Masukkan Klinik"
|
||||||
|
:errors="errors"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SelectDate
|
||||||
|
v-show="resumeArrangementType === `meninggal`"
|
||||||
|
field-name="deathDate"
|
||||||
|
label="Jam Tanggal Meninggal"
|
||||||
|
:errors="errors"
|
||||||
|
:is-with-time="true" />
|
||||||
|
|
||||||
|
<DE.Cell class="mt-2" :col-span="3" v-show="resumeArrangementType === `meninggal`">
|
||||||
|
<div class="w-2/3 rounded-md border bg-gray-50/50 p-4 shadow-sm dark:bg-neutral-950">
|
||||||
|
<h1 class="mb-3 font-medium">Sebab Meninggal</h1>
|
||||||
|
|
||||||
|
<FieldArray v-slot="{ fields, push, remove }" name="deathCause">
|
||||||
|
<div v-for="(field, idx) in fields" :key="idx" class="flex items-center gap-3 mb-3">
|
||||||
|
<SelectDeathCause
|
||||||
|
:field-name="`deathCause[${idx}]`"
|
||||||
|
:errors="errors"
|
||||||
|
/>
|
||||||
|
<Button v-if="idx !== 0" type="button" variant="destructive" size="sm" @click="remove(idx)">
|
||||||
|
<Icon name="i-lucide-trash-2" class="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button v-if="fields.length < 3" type="button" variant="outline"
|
||||||
|
class="w-full rounded-md border border-primary bg-white px-4 py-2 text-primary hover:border-primary hover:bg-primary hover:text-white sm:w-auto sm:text-sm"
|
||||||
|
@click="push(``)">
|
||||||
|
<Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle transition-colors" />
|
||||||
|
Tambah Sebab Meninggal
|
||||||
|
</Button>
|
||||||
|
</FieldArray>
|
||||||
|
</div>
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
<DE.Cell
|
||||||
|
v-show="resumeArrangementType === `meninggal`"
|
||||||
|
:col-span="3">
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="deathCauseDescription"
|
||||||
|
label="Keterangan Sebab Meninggal"
|
||||||
|
placeholder="Keterangan Sebab Meninggal"
|
||||||
|
:errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
</DE.Block>
|
||||||
|
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './action-list.cfg'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import { CalendarIcon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
dateValue: DateRange
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isModalOpen = inject(`isActionHistoryOpen`) as Ref<boolean>
|
||||||
|
|
||||||
|
const df = new DateFormatter('en-US', { dateStyle: 'medium',})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
'update:dateValue': [value: DateRange]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-model:open="isModalOpen" title="" size="2xl">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button variant="outline" :class="cn('mb-1 w-[280px] justify-start text-left font-normal',
|
||||||
|
!props.dateValue && 'text-muted-foreground')">
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="props.dateValue.start">
|
||||||
|
<template v-if="props.dateValue.end">
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }} -
|
||||||
|
{{ df.format(props.dateValue.end.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else> Pick a date </template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<RangeCalendar v-model="props.dateValue" initial-focus :number-of-months="2"
|
||||||
|
@update:model-value="(date) => emit('update:dateValue', date)" />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="props.paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="props.paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -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 '-'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './consultation-list.cfg'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import { CalendarIcon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
dateValue: DateRange
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isModalOpen = inject(`isConsultationHistoryOpen`) as Ref<boolean>
|
||||||
|
|
||||||
|
const df = new DateFormatter('en-US', { dateStyle: 'medium',})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
'update:dateValue': [value: DateRange]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-model:open="isModalOpen" title="" size="2xl">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button variant="outline" :class="cn('mb-1 w-[280px] justify-start text-left font-normal',
|
||||||
|
!props.dateValue && 'text-muted-foreground')">
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="props.dateValue.start">
|
||||||
|
<template v-if="props.dateValue.end">
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }} -
|
||||||
|
{{ df.format(props.dateValue.end.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else> Pick a date </template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<RangeCalendar v-model="props.dateValue" initial-focus :number-of-months="2"
|
||||||
|
@update:model-value="(date) => emit('update:dateValue', date)" />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="props.paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="props.paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -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: {
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './farmacy-list.cfg'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import { CalendarIcon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
dateValue: DateRange
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isModalOpen = inject(`isFarmacyHistoryOpen`) as Ref<boolean>
|
||||||
|
|
||||||
|
const df = new DateFormatter('en-US', { dateStyle: 'medium',})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
'update:dateValue': [value: DateRange]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-model:open="isModalOpen" title="" size="2xl">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button variant="outline" :class="cn('mb-1 w-[280px] justify-start text-left font-normal',
|
||||||
|
!props.dateValue && 'text-muted-foreground')">
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="props.dateValue.start">
|
||||||
|
<template v-if="props.dateValue.end">
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }} -
|
||||||
|
{{ df.format(props.dateValue.end.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else> Pick a date </template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<RangeCalendar v-model="props.dateValue" initial-focus :number-of-months="2"
|
||||||
|
@update:model-value="(date) => emit('update:dateValue', date)" />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="props.paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="props.paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -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: {
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './national-program-list.cfg'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import { CalendarIcon } from 'lucide-vue-next'
|
||||||
|
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||||
|
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
searchValue: string
|
||||||
|
statusValue: string
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isModalOpen = inject(`isNationalProgramServiceHistoryOpen`) as Ref<boolean>
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
'update:searchValue': [value: string]
|
||||||
|
'update:statusValue': [value: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusOptions: Item[] = [
|
||||||
|
{ value: 'all', label: 'All Statuses' },
|
||||||
|
{ value: 'pending', label: 'Pending' },
|
||||||
|
{ value: 'active', label: 'Active' },
|
||||||
|
{ value: 'completed', label: 'Completed' },
|
||||||
|
{ value: 'archived', label: 'Archived' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-model:open="isModalOpen" title="" size="2xl">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<DE.Block :col-count="4" class="" :cell-flex="false">
|
||||||
|
<Input
|
||||||
|
v-model="props.searchValue"
|
||||||
|
class=""
|
||||||
|
placeholder="Cari .."/>
|
||||||
|
<Select
|
||||||
|
:items="statusOptions"
|
||||||
|
:model-value="props.statusValue"
|
||||||
|
@update:model-value="(data) => emit('update:statusValue', data)"
|
||||||
|
/>
|
||||||
|
</DE.Block>
|
||||||
|
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="props.paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="props.paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -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: {
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './supporting-list.cfg'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import { CalendarIcon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
dateValue: DateRange
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const isModalOpen = inject(`isSupportingHistoryOpen`) as Ref<boolean>
|
||||||
|
|
||||||
|
const df = new DateFormatter('en-US', { dateStyle: 'medium',})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
'update:dateValue': [value: DateRange]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-model:open="isModalOpen" title="" size="2xl">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button variant="outline" :class="cn('mb-1 w-[280px] justify-start text-left font-normal',
|
||||||
|
!props.dateValue && 'text-muted-foreground')">
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="props.dateValue.start">
|
||||||
|
<template v-if="props.dateValue.end">
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }} -
|
||||||
|
{{ df.format(props.dateValue.end.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else> Pick a date </template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<RangeCalendar v-model="props.dateValue" initial-focus :number-of-months="2"
|
||||||
|
@update:model-value="(date) => emit('update:dateValue', date)" />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="props.paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="props.paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -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 '-'
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -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 '-'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||||
|
import type { InstallationFormData } from '~/schemas/installation.schema'
|
||||||
|
import TextCaptcha from '~/components/pub/my-ui/form/text-captcha.vue'
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
schema: any
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InstallationFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
const captchaRef = ref<InstanceType<typeof TextCaptcha> | null>(null)
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
function onSubmitForm(values: any, { resetForm }: { resetForm: () => void }) {
|
||||||
|
const formData: InstallationFormData = {
|
||||||
|
name: values.name || '',
|
||||||
|
code: values.code || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form cancel handler
|
||||||
|
function onCancelForm({ resetForm }: { resetForm: () => void }) {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = ref([
|
||||||
|
{ label: 'Rujukan Internal', value: 'ri' },
|
||||||
|
{ label: 'SEP Rujukan', value: 'sr' },
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
v-slot="{ handleSubmit, resetForm }"
|
||||||
|
as=""
|
||||||
|
keep-values
|
||||||
|
:validation-schema="formSchema"
|
||||||
|
>
|
||||||
|
<form id="entry-form" @submit="handleSubmit($event, (values) => onSubmitForm(values, { resetForm }))">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
|
||||||
|
<InputBase
|
||||||
|
field-name="name"
|
||||||
|
label="Nama"
|
||||||
|
placeholder="Masukkan Nama"
|
||||||
|
:errors="errors"/>
|
||||||
|
<InputBase
|
||||||
|
field-name="email"
|
||||||
|
label="Email"
|
||||||
|
placeholder="Masukkan Email"
|
||||||
|
:errors="errors"/>
|
||||||
|
|
||||||
|
<div class="mt-2">
|
||||||
|
<Label class="" for="password">Password</Label>
|
||||||
|
<Field class="" id="password" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="password">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
v-bind="componentField"
|
||||||
|
type="password"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TextCaptcha
|
||||||
|
ref="captchaRef"
|
||||||
|
:length="5"
|
||||||
|
:useSpacing="true"
|
||||||
|
:noiseChars="true"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -143,12 +143,16 @@ watch(props, (value) => {
|
|||||||
nationalId.value = objects?.nationalIdentity || '-'
|
nationalId.value = objects?.nationalIdentity || '-'
|
||||||
medicalRecordNumber.value = objects?.medicalRecordNumber || '-'
|
medicalRecordNumber.value = objects?.medicalRecordNumber || '-'
|
||||||
patientName.value = objects?.patientName || '-'
|
patientName.value = objects?.patientName || '-'
|
||||||
|
phoneNumber.value = objects?.phoneNumber || '-'
|
||||||
if (objects?.sepType === 'internal') {
|
if (objects?.sepType === 'internal') {
|
||||||
admissionType.value = '4'
|
admissionType.value = '4'
|
||||||
}
|
}
|
||||||
if (objects?.sepType === 'external') {
|
if (objects?.sepType === 'external') {
|
||||||
admissionType.value = '1'
|
admissionType.value = '1'
|
||||||
}
|
}
|
||||||
|
if (objects?.diagnoseLabel) {
|
||||||
|
initialDiagnosis.value = objects?.diagnoseLabel
|
||||||
|
}
|
||||||
isDateReload.value = true
|
isDateReload.value = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (objects?.letterDate) {
|
if (objects?.letterDate) {
|
||||||
@@ -176,6 +180,9 @@ onMounted(() => {
|
|||||||
if (!isService.value) {
|
if (!isService.value) {
|
||||||
serviceType.value = '2'
|
serviceType.value = '2'
|
||||||
}
|
}
|
||||||
|
if (!admissionType.value) {
|
||||||
|
admissionType.value = '1'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ const props = defineProps<{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PubMyUiDataTable
|
<PubMyUiDataTable
|
||||||
v-bind="config"
|
|
||||||
:rows="props.data"
|
:rows="props.data"
|
||||||
|
v-bind="config"
|
||||||
|
v-on="$attrs"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { defineAsyncComponent } from 'vue'
|
|||||||
|
|
||||||
type SmallDetailDto = any
|
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 = {
|
export const config: Config = {
|
||||||
cols: [{}, {}, {}, { width: 100 }, { width: 120 }, {}, {}, {}, { width: 100 }, { width: 100 }, {}, { width: 50 }],
|
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: [
|
delKeyNames: [
|
||||||
{ key: 'code', label: 'Kode' },
|
{ key: 'code', label: 'Kode' },
|
||||||
@@ -44,6 +44,10 @@ export const config: Config = {
|
|||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
encounter(rec: any) {
|
||||||
|
const data = rec?.encounter ?? {}
|
||||||
|
return data?.class_code || '-'
|
||||||
|
},
|
||||||
diagnose(rec: any) {
|
diagnose(rec: any) {
|
||||||
const { value } = rec ?? {}
|
const { value } = rec ?? {}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { genSpecialistPosition } from '~/models/specialist-position'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
schema: z.ZodSchema<any>
|
schema: z.ZodSchema<any>
|
||||||
specialistId: number
|
specialistId: string
|
||||||
employees: any[]
|
employees: any[]
|
||||||
values: any
|
values: any
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
|
|||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [specialist, specialistAttrs] = defineField('specialist_id')
|
const [specialist, specialistAttrs] = defineField('specialist_code')
|
||||||
const [employee, employeeAttrs] = defineField('employee_id')
|
const [employee, employeeAttrs] = defineField('employee_id')
|
||||||
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ const headStatusStr = computed<string>({
|
|||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
if (props.values.specialist_id !== undefined)
|
if (props.values.specialist_code !== undefined)
|
||||||
specialist.value = props.values.specialist_id ? Number(props.values.specialist_id) : null
|
specialist.value = props.values.specialist_code ? String(props.values.specialist_code) : null
|
||||||
if (props.values.employee_id !== undefined)
|
if (props.values.employee_id !== undefined)
|
||||||
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
||||||
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
||||||
@@ -72,7 +72,7 @@ if (props.values) {
|
|||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
code.value = ''
|
code.value = ''
|
||||||
name.value = ''
|
name.value = ''
|
||||||
specialist.value = null
|
specialist.value = ''
|
||||||
employee.value = null
|
employee.value = null
|
||||||
headStatus.value = false
|
headStatus.value = false
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ function onSubmitForm() {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
specialist_id: specialist.value || null,
|
specialist_code: specialist.value || '',
|
||||||
employee_id: employee.value || null,
|
employee_id: employee.value || null,
|
||||||
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ function onCancelForm() {
|
|||||||
</Cell>
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Label height="compact">Spesialis</Label>
|
<Label height="compact">Spesialis</Label>
|
||||||
<Field :errMessage="errors.specialist_id">
|
<Field :errMessage="errors.specialist_code">
|
||||||
<Combobox
|
<Combobox
|
||||||
id="specialist"
|
id="specialist"
|
||||||
v-model="specialist"
|
v-model="specialist"
|
||||||
|
|||||||
@@ -36,19 +36,19 @@ const { defineField, errors, meta } = useForm({
|
|||||||
initialValues: {
|
initialValues: {
|
||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
unit_id: 0,
|
unit_code: '',
|
||||||
} as Partial<SpecialistFormData>,
|
} as Partial<SpecialistFormData>,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [unit, unitAttrs] = defineField('unit_id')
|
const [unit, unitAttrs] = defineField('unit_code')
|
||||||
|
|
||||||
// Fill fields from props.values if provided
|
// Fill fields from props.values if provided
|
||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
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 = () => {
|
const resetForm = () => {
|
||||||
@@ -63,7 +63,7 @@ function onSubmitForm(values: any) {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
unit_id: unit.value ? Number(unit.value) : null,
|
unit_code: unit.value ? String(unit.value) : null,
|
||||||
}
|
}
|
||||||
emit('submit', formData, resetForm)
|
emit('submit', formData, resetForm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { genSubSpecialistPosition } from '~/models/subspecialist-position'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
schema: z.ZodSchema<any>
|
schema: z.ZodSchema<any>
|
||||||
subspecialistId: number
|
subspecialistId: string
|
||||||
employees: any[]
|
employees: any[]
|
||||||
values: any
|
values: any
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
|
|||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [subSpecialist, subSpecialistAttrs] = defineField('subspecialist_id')
|
const [subSpecialist, subSpecialistAttrs] = defineField('subspecialist_code')
|
||||||
const [employee, employeeAttrs] = defineField('employee_id')
|
const [employee, employeeAttrs] = defineField('employee_id')
|
||||||
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ const headStatusStr = computed<string>({
|
|||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
if (props.values.subspecialist_id !== undefined)
|
if (props.values.subspecialist_code !== undefined)
|
||||||
subSpecialist.value = props.values.subspecialist_id ? Number(props.values.subspecialist_id) : null
|
subSpecialist.value = props.values.subspecialist_code ? String(props.values.subspecialist_code) : null
|
||||||
if (props.values.employee_id !== undefined)
|
if (props.values.employee_id !== undefined)
|
||||||
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
||||||
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
||||||
@@ -83,7 +83,7 @@ function onSubmitForm() {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
subspecialist_id: subSpecialist.value || null,
|
subspecialist_code: subSpecialist.value || null,
|
||||||
employee_id: employee.value || null,
|
employee_id: employee.value || null,
|
||||||
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ function onCancelForm() {
|
|||||||
</Cell>
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Label height="compact">Sub Spesialis</Label>
|
<Label height="compact">Sub Spesialis</Label>
|
||||||
<Field :errMessage="errors.subspecialist_id">
|
<Field :errMessage="errors.subspecialist_code">
|
||||||
<Combobox
|
<Combobox
|
||||||
id="specialist"
|
id="specialist"
|
||||||
v-model="subSpecialist"
|
v-model="subSpecialist"
|
||||||
|
|||||||
@@ -36,20 +36,20 @@ const { defineField, errors, meta } = useForm({
|
|||||||
initialValues: {
|
initialValues: {
|
||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
specialist_id: 0,
|
specialist_code: '',
|
||||||
} as Partial<SubspecialistFormData>,
|
} as Partial<SubspecialistFormData>,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [specialist, specialistAttrs] = defineField('specialist_id')
|
const [specialist, specialistAttrs] = defineField('specialist_code')
|
||||||
|
|
||||||
// Fill fields from props.values if provided
|
// Fill fields from props.values if provided
|
||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
if (props.values.specialist_id !== undefined)
|
if (props.values.specialist_code !== undefined)
|
||||||
specialist.value = props.values.specialist_id ? String(props.values.specialist_id) : null
|
specialist.value = props.values.specialist_code ? String(props.values.specialist_code) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
@@ -64,7 +64,7 @@ function onSubmitForm(values: any) {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
specialist_id: specialist.value ? Number(specialist.value) : null,
|
specialist_code: specialist.value ? String(specialist.value) : "",
|
||||||
}
|
}
|
||||||
emit('submit', formData, resetForm)
|
emit('submit', formData, resetForm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,192 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
// 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 Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import type { UnitPositionFormData } from '~/schemas/unit-position.schema'
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
import type z from 'zod'
|
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
|
||||||
import { useForm } from 'vee-validate'
|
|
||||||
import { genBase } from '~/models/_base'
|
|
||||||
import { genUnitPosition } from '~/models/unit-position'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
schema: z.ZodSchema<any>
|
|
||||||
unitId: number
|
|
||||||
employees: any[]
|
|
||||||
values: any
|
|
||||||
isLoading?: boolean
|
|
||||||
isReadonly?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
|
|
||||||
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
|
||||||
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
|
||||||
const emit = defineEmits<{
|
|
||||||
submit: [values: UnitPositionFormData, resetForm: () => void]
|
|
||||||
cancel: [resetForm: () => void]
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const { defineField, errors, meta } = useForm({
|
|
||||||
validationSchema: toTypedSchema(props.schema),
|
|
||||||
initialValues: genUnitPosition() as Partial<UnitPositionFormData>,
|
|
||||||
})
|
|
||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
|
||||||
const [name, nameAttrs] = defineField('name')
|
|
||||||
const [employee, employeeAttrs] = defineField('employee_id')
|
|
||||||
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
|
||||||
|
|
||||||
// RadioGroup uses string values; expose a string computed that maps to the boolean field
|
|
||||||
const headStatusStr = computed<string>({
|
|
||||||
get() {
|
|
||||||
if (headStatus.value === true) return 'true'
|
|
||||||
if (headStatus.value === false) return 'false'
|
|
||||||
return ''
|
|
||||||
},
|
|
||||||
set(v: string) {
|
|
||||||
if (v === 'true') headStatus.value = true
|
|
||||||
else if (v === 'false') headStatus.value = false
|
|
||||||
else headStatus.value = undefined
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// 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.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
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetForm = () => {
|
|
||||||
code.value = ''
|
|
||||||
name.value = ''
|
|
||||||
employee.value = null
|
|
||||||
headStatus.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Form submission handler
|
|
||||||
function onSubmitForm() {
|
|
||||||
const formData: UnitPositionFormData = {
|
|
||||||
...genBase(),
|
|
||||||
name: name.value || '',
|
|
||||||
code: code.value || '',
|
|
||||||
|
|
||||||
// readonly based on detail unit
|
|
||||||
unit_id: props.unitId,
|
|
||||||
|
|
||||||
employee_id: employee.value || null,
|
|
||||||
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
|
||||||
}
|
|
||||||
emit('submit', formData, resetForm)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Form cancel handler
|
|
||||||
function onCancelForm() {
|
|
||||||
emit('cancel', resetForm)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<form
|
|
||||||
id="form-unit-position"
|
|
||||||
@submit.prevent
|
|
||||||
>
|
|
||||||
<Block
|
|
||||||
labelSize="thin"
|
|
||||||
class="!mb-2.5 !pt-0 xl:!mb-3"
|
|
||||||
:colCount="1"
|
|
||||||
>
|
|
||||||
<Cell>
|
|
||||||
<Label height="compact">Kode Jabatan</Label>
|
|
||||||
<Field :errMessage="errors.code">
|
|
||||||
<Input
|
|
||||||
id="code"
|
|
||||||
v-model="code"
|
|
||||||
v-bind="codeAttrs"
|
|
||||||
:disabled="isLoading || isReadonly"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</Cell>
|
|
||||||
<Cell>
|
|
||||||
<Label height="compact">Nama Jabatan</Label>
|
|
||||||
<Field :errMessage="errors.name">
|
|
||||||
<Input
|
|
||||||
id="name"
|
|
||||||
v-model="name"
|
|
||||||
v-bind="nameAttrs"
|
|
||||||
:disabled="isLoading || isReadonly"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</Cell>
|
|
||||||
<Cell>
|
|
||||||
<Label height="compact">Pengisi Jabatan</Label>
|
|
||||||
<Field :errMessage="errors.employee_id">
|
|
||||||
<Combobox
|
|
||||||
id="employee"
|
|
||||||
v-model="employee"
|
|
||||||
v-bind="employeeAttrs"
|
|
||||||
:items="employees"
|
|
||||||
:is-disabled="isLoading || isReadonly"
|
|
||||||
placeholder="Pilih Karyawan"
|
|
||||||
search-placeholder="Cari Karyawan"
|
|
||||||
empty-message="Item tidak ditemukan"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</Cell>
|
|
||||||
<Cell>
|
|
||||||
<Label height="compact">Status Kepala</Label>
|
|
||||||
<Field :errMessage="errors.headStatus">
|
|
||||||
<RadioGroup
|
|
||||||
v-model="headStatusStr"
|
|
||||||
v-bind="headStatusAttrs"
|
|
||||||
class="flex gap-4"
|
|
||||||
>
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem
|
|
||||||
id="head-yes"
|
|
||||||
value="true"
|
|
||||||
/>
|
|
||||||
<Label for="head-yes">Ya</Label>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem
|
|
||||||
id="head-no"
|
|
||||||
value="false"
|
|
||||||
/>
|
|
||||||
<Label for="head-no">Tidak</Label>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
</Field>
|
|
||||||
</Cell>
|
|
||||||
</Block>
|
|
||||||
<div class="my-2 flex justify-end gap-2 py-2">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="secondary"
|
|
||||||
class="w-[120px]"
|
|
||||||
@click="onCancelForm"
|
|
||||||
>
|
|
||||||
Kembali
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
v-if="!isReadonly"
|
|
||||||
type="button"
|
|
||||||
class="w-[120px]"
|
|
||||||
:disabled="isLoading || !meta.valid"
|
|
||||||
@click="onSubmitForm"
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
@@ -40,7 +40,7 @@ const { defineField, errors, meta } = useForm({
|
|||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [unit, unitAttrs] = defineField('unit_id')
|
const [unit, unitAttrs] = defineField('unit_code')
|
||||||
const [employee, employeeAttrs] = defineField('employee_id')
|
const [employee, employeeAttrs] = defineField('employee_id')
|
||||||
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
const [headStatus, headStatusAttrs] = defineField('headStatus')
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ const headStatusStr = computed<string>({
|
|||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
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)
|
if (props.values.employee_id !== undefined)
|
||||||
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
|
||||||
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
|
||||||
@@ -82,7 +82,7 @@ function onSubmitForm() {
|
|||||||
...genBase(),
|
...genBase(),
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
unit_id: unit.value || null,
|
unit_code: unit.value || '',
|
||||||
employee_id: employee.value || null,
|
employee_id: employee.value || null,
|
||||||
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ function onCancelForm() {
|
|||||||
</Cell>
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Label height="compact">Unit</Label>
|
<Label height="compact">Unit</Label>
|
||||||
<Field :errMessage="errors.unit_id">
|
<Field :errMessage="errors.unit_code">
|
||||||
<Combobox
|
<Combobox
|
||||||
id="unit"
|
id="unit"
|
||||||
v-model="unit"
|
v-model="unit"
|
||||||
|
|||||||
@@ -35,20 +35,20 @@ const { defineField, errors, meta } = useForm({
|
|||||||
initialValues: {
|
initialValues: {
|
||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
installation_id: 0,
|
installation_code: '',
|
||||||
} as Partial<UnitFormData>,
|
} as Partial<UnitFormData>,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [code, codeAttrs] = defineField('code')
|
const [code, codeAttrs] = defineField('code')
|
||||||
const [name, nameAttrs] = defineField('name')
|
const [name, nameAttrs] = defineField('name')
|
||||||
const [installation, installationAttrs] = defineField('installation_id')
|
const [installation, installationAttrs] = defineField('installation_code')
|
||||||
|
|
||||||
// Fill fields from props.values if provided
|
// Fill fields from props.values if provided
|
||||||
if (props.values) {
|
if (props.values) {
|
||||||
if (props.values.code !== undefined) code.value = props.values.code
|
if (props.values.code !== undefined) code.value = props.values.code
|
||||||
if (props.values.name !== undefined) name.value = props.values.name
|
if (props.values.name !== undefined) name.value = props.values.name
|
||||||
if (props.values.installation_id !== undefined)
|
if (props.values.installation_code !== undefined)
|
||||||
installation.value = props.values.installation_id ? Number(props.values.installation_id) : null
|
installation.value = props.values.installation_code ? String(props.values.installation_code) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
@@ -62,7 +62,7 @@ function onSubmitForm() {
|
|||||||
const formData: UnitFormData = {
|
const formData: UnitFormData = {
|
||||||
name: name.value || '',
|
name: name.value || '',
|
||||||
code: code.value || '',
|
code: code.value || '',
|
||||||
installation_id: installation.value ? Number(installation.value) : null,
|
installation_code: installation.value ? String(installation.value) : "",
|
||||||
}
|
}
|
||||||
emit('submit', formData, resetForm)
|
emit('submit', formData, resetForm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ import EncounterHome from '~/components/content/encounter/home.vue'
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<EncounterHome classes="chemotherapy" />
|
<EncounterHome display="menu" class-code="ambulatory" sub-class-code="chemo" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useQueryMode } from '@/composables/useQueryMode'
|
||||||
|
|
||||||
|
import List from './list.vue'
|
||||||
|
import Form from './form.vue'
|
||||||
|
|
||||||
|
// Models
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
// Props
|
||||||
|
interface Props {
|
||||||
|
encounter: Encounter
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { mode, goToEntry, backToList } = useQueryCRUDMode('mode')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<List
|
||||||
|
v-if="mode === 'list'"
|
||||||
|
:encounter="props.encounter"
|
||||||
|
@add="goToEntry"
|
||||||
|
@edit="goToEntry"
|
||||||
|
/>
|
||||||
|
<Form
|
||||||
|
v-else
|
||||||
|
@back="backToList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { z } from 'zod'
|
||||||
|
import Entry from '~/components/app/cprj/entry.vue'
|
||||||
|
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||||
|
import ActionDialog from '~/components/pub/my-ui/nav-footer/ba-su.vue'
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import { CprjSoapiSchema } from '~/schemas/soapi.schema'
|
||||||
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
|
import { handleActionSave, handleActionEdit } from '~/handlers/soapi-early.handler'
|
||||||
|
const { backToList } = useQueryMode('mode')
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const fungsional = ref([])
|
||||||
|
const schema = CprjSoapiSchema
|
||||||
|
const payload = ref({
|
||||||
|
encounter_id: 0,
|
||||||
|
time: '',
|
||||||
|
typeCode: 'dev-record',
|
||||||
|
value: '',
|
||||||
|
})
|
||||||
|
const model = ref({
|
||||||
|
ppa: '',
|
||||||
|
ppa_name: '',
|
||||||
|
subjective: '',
|
||||||
|
objective: '',
|
||||||
|
assesment: '',
|
||||||
|
plan: '',
|
||||||
|
review: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const isLoading = reactive<DataTableLoader>({
|
||||||
|
isTableLoading: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {})
|
||||||
|
|
||||||
|
const cprjRef = ref()
|
||||||
|
async function actionHandler(type: string) {
|
||||||
|
if (type === 'back') {
|
||||||
|
backToList()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const result = await cprjRef.value?.validate()
|
||||||
|
console.log('result', result)
|
||||||
|
if (result?.valid) {
|
||||||
|
console.log('data', result.data)
|
||||||
|
handleActionSave(
|
||||||
|
{
|
||||||
|
...payload.value,
|
||||||
|
value: JSON.stringify(result.data),
|
||||||
|
encounter_id: +route.params.id,
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
toast,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
console.log('Ada error di form', result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provide('table_data_loader', isLoading)
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Entry
|
||||||
|
ref="cprjRef"
|
||||||
|
v-model="model"
|
||||||
|
:schema="schema"
|
||||||
|
type="function"
|
||||||
|
/>
|
||||||
|
<div class="my-2 flex justify-end py-2">
|
||||||
|
<Action @click="actionHandler" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Components
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||||
|
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||||
|
import List from '~/components/app/soapi/list.vue'
|
||||||
|
import Entry from '~/components/app/cprj/entry.vue'
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import { usePaginatedList } from '~/composables/usePaginatedList'
|
||||||
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
import {
|
||||||
|
recId,
|
||||||
|
recAction,
|
||||||
|
recItem,
|
||||||
|
isReadonly,
|
||||||
|
isProcessing,
|
||||||
|
isFormEntryDialogOpen,
|
||||||
|
isRecordConfirmationOpen,
|
||||||
|
onResetState,
|
||||||
|
handleActionSave,
|
||||||
|
handleActionEdit,
|
||||||
|
handleActionRemove,
|
||||||
|
handleCancelForm,
|
||||||
|
} from '~/handlers/consultation.handler'
|
||||||
|
|
||||||
|
// Services
|
||||||
|
import { getList, getDetail } from '~/services/soapi-early.service'
|
||||||
|
|
||||||
|
// Models
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
// Props
|
||||||
|
interface Props {
|
||||||
|
encounter: Encounter
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emits = defineEmits(['add', 'edit'])
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { recordId } = useQueryCRUDRecordId()
|
||||||
|
const { goToEntry, backToList } = useQueryCRUDMode('mode')
|
||||||
|
|
||||||
|
let units = ref<{ value: string; label: string }[]>([])
|
||||||
|
const encounterId = ref<number>(props?.encounter?.id || 0)
|
||||||
|
const title = ref('')
|
||||||
|
const id = route.params.id
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isLoading,
|
||||||
|
paginationMeta,
|
||||||
|
searchInput,
|
||||||
|
handlePageChange,
|
||||||
|
handleSearch,
|
||||||
|
fetchData: getMyList,
|
||||||
|
} = usePaginatedList({
|
||||||
|
fetchFn: async ({ page, search }) => {
|
||||||
|
const result = await getList({
|
||||||
|
'encounter-id': id,
|
||||||
|
typeCode: 'dev-record',
|
||||||
|
includes: 'encounter',
|
||||||
|
search,
|
||||||
|
page,
|
||||||
|
})
|
||||||
|
if (result.success) {
|
||||||
|
data.value = result.body.data
|
||||||
|
}
|
||||||
|
return { success: result.success || false, body: result.body || {} }
|
||||||
|
},
|
||||||
|
entityName: 'cprj',
|
||||||
|
})
|
||||||
|
|
||||||
|
const headerPrep: HeaderPrep = {
|
||||||
|
title: 'CPRJ',
|
||||||
|
icon: 'i-lucide-box',
|
||||||
|
refSearchNav: {
|
||||||
|
placeholder: 'Cari (min. 3 karakter)...',
|
||||||
|
minLength: 3,
|
||||||
|
debounceMs: 500,
|
||||||
|
showValidationFeedback: true,
|
||||||
|
onInput: (value: string) => {
|
||||||
|
searchInput.value = value
|
||||||
|
},
|
||||||
|
onClick: () => {},
|
||||||
|
onClear: () => {},
|
||||||
|
},
|
||||||
|
addNav: {
|
||||||
|
label: 'Tambah',
|
||||||
|
icon: 'i-lucide-plus',
|
||||||
|
onClick: () => {
|
||||||
|
goToEntry()
|
||||||
|
emits('add')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = new Date()
|
||||||
|
|
||||||
|
provide('rec_id', recId)
|
||||||
|
provide('rec_action', recAction)
|
||||||
|
provide('rec_item', recItem)
|
||||||
|
provide('table_data_loader', isLoading)
|
||||||
|
|
||||||
|
const getMyDetail = async (id: number | string) => {
|
||||||
|
const result = await getDetail(id)
|
||||||
|
if (result.success) {
|
||||||
|
const currentValue = result.body?.data || {}
|
||||||
|
recItem.value = currentValue
|
||||||
|
isFormEntryDialogOpen.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for row actions when recId or recAction changes
|
||||||
|
watch([recId, recAction], () => {
|
||||||
|
switch (recAction.value) {
|
||||||
|
case ActionEvents.showDetail:
|
||||||
|
getMyDetail(recId.value)
|
||||||
|
title.value = 'Detail Konsultasi'
|
||||||
|
isReadonly.value = true
|
||||||
|
break
|
||||||
|
case ActionEvents.showEdit:
|
||||||
|
emits('edit')
|
||||||
|
recordId.value = recId.value
|
||||||
|
console.log('recordId', recId.value)
|
||||||
|
break
|
||||||
|
case ActionEvents.showConfirmDelete:
|
||||||
|
isRecordConfirmationOpen.value = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getMyList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Header
|
||||||
|
v-model="searchInput"
|
||||||
|
:prep="headerPrep"
|
||||||
|
:ref-search-nav="headerPrep.refSearchNav"
|
||||||
|
@search="handleSearch"
|
||||||
|
class="mb-4 xl:mb-5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<List
|
||||||
|
:data="data"
|
||||||
|
:pagination-meta="paginationMeta"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Record Confirmation Modal -->
|
||||||
|
<RecordConfirmation
|
||||||
|
v-model:open="isRecordConfirmationOpen"
|
||||||
|
action="delete"
|
||||||
|
:record="recItem"
|
||||||
|
@confirm="() => handleActionRemove(recId, getMyList, toast)"
|
||||||
|
@cancel=""
|
||||||
|
>
|
||||||
|
<template #default="{ record }">
|
||||||
|
<div class="text-sm">
|
||||||
|
<p>
|
||||||
|
<strong>ID:</strong>
|
||||||
|
{{ record?.id }}
|
||||||
|
</p>
|
||||||
|
<p v-if="record?.name">
|
||||||
|
<strong>Nama:</strong>
|
||||||
|
{{ record.name }}
|
||||||
|
</p>
|
||||||
|
<p v-if="record?.code">
|
||||||
|
<strong>Kode:</strong>
|
||||||
|
{{ record.code }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</RecordConfirmation>
|
||||||
|
</template>
|
||||||
@@ -1,24 +1,177 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Nav from '~/components/pub/my-ui/nav-footer/ba-su.vue'
|
// Composables
|
||||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
|
||||||
|
|
||||||
import { useQueryCRUDMode } from '~/composables/useQueryCRUD'
|
import { useQueryCRUDMode } from '~/composables/useQueryCRUD'
|
||||||
|
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||||
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
|
|
||||||
|
// Pub components
|
||||||
|
import Nav from '~/components/pub/my-ui/nav-footer/ba-de-su.vue'
|
||||||
|
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
// Refs / src
|
||||||
|
import { type Device } from '~/models/device'
|
||||||
|
import { getList as getDeviceList } from '~/services/device.service'
|
||||||
|
|
||||||
|
// Device order things
|
||||||
|
import { getDetail, remove, submit } from '~/services/device-order.service'
|
||||||
|
import EntryForm from '~/components/app/device-order/entry-form.vue'
|
||||||
|
|
||||||
|
// Items
|
||||||
|
import {
|
||||||
|
getList as getItemList,
|
||||||
|
create as createItem,
|
||||||
|
update as updateItem,
|
||||||
|
} from '~/services/device-order-item.service'
|
||||||
|
import {
|
||||||
|
recId,
|
||||||
|
recAction,
|
||||||
|
recItem,
|
||||||
|
handleActionRemove,
|
||||||
|
} from '~/handlers/device-order-item.handler'
|
||||||
|
import { genDeviceOrderItem, type DeviceOrderItem } from '~/models/device-order-item'
|
||||||
import ItemListEntry from '~/components/app/device-order-item/list-entry.vue'
|
import ItemListEntry from '~/components/app/device-order-item/list-entry.vue'
|
||||||
import type { HeaderPrep } from '~/components/pub/my-ui/data/types'
|
import ItemEntry from '~/components/app/device-order-item/entry-form.vue'
|
||||||
|
|
||||||
const { backToList } = useQueryCRUDMode()
|
|
||||||
|
|
||||||
|
// Header
|
||||||
const headerPrep: HeaderPrep = {
|
const headerPrep: HeaderPrep = {
|
||||||
title: 'Tambah Order Alkes',
|
title: 'Tambah Order Alkes',
|
||||||
icon: 'i-lucide-box',
|
icon: 'i-lucide-box',
|
||||||
}
|
}
|
||||||
|
|
||||||
function navClick(type: 'cancel' | 'submit') {
|
// Device order things
|
||||||
if (type === 'cancel') {
|
const { getQueryParam } = useQueryParam()
|
||||||
backToList()
|
const rawId = getQueryParam('id')
|
||||||
}
|
const id = typeof rawId === 'string' ? parseInt(rawId) : 0
|
||||||
|
const dataRes = await getDetail(id, { includes: 'doctor,doctor-employee,doctor-employee-person' })
|
||||||
|
const data = ref(dataRes.body?.data || null)
|
||||||
|
const devices = ref<Device[]>([])
|
||||||
|
|
||||||
|
const isSubmitConfirmationOpen = ref(false)
|
||||||
|
const isDeleteConfirmationOpen = ref(false)
|
||||||
|
|
||||||
|
// Items
|
||||||
|
const {
|
||||||
|
data: items,
|
||||||
|
isLoading,
|
||||||
|
fetchData: getDeviceOrderItems,
|
||||||
|
} = usePaginatedList({
|
||||||
|
fetchFn: async (params: any) => {
|
||||||
|
const result = await getItemList({
|
||||||
|
'device-order-id': id,
|
||||||
|
includes: 'device',
|
||||||
|
search: params.search,
|
||||||
|
sort: 'createdAt:asc',
|
||||||
|
'page-number': params['page-number'] || 0,
|
||||||
|
'page-size': params['page-size'] || 10,
|
||||||
|
})
|
||||||
|
return { success: result.success || false, body: result.body || {} }
|
||||||
|
},
|
||||||
|
entityName: 'division',
|
||||||
|
})
|
||||||
|
const selectedItem = ref<DeviceOrderItem>(genDeviceOrderItem())
|
||||||
|
const isItemDetailDialogOpen = ref(false)
|
||||||
|
const isItemEntryDialogOpen = ref(false)
|
||||||
|
const isItemDelConfirmDialogOpen = ref(false)
|
||||||
|
|
||||||
|
selectedItem.value.deviceOrder_id = id
|
||||||
|
|
||||||
|
// Last navs
|
||||||
|
const { backToList } = useQueryCRUDMode()
|
||||||
|
|
||||||
|
// Reactivities
|
||||||
|
provide('rec_id', recId)
|
||||||
|
provide('rec_action', recAction)
|
||||||
|
provide('rec_item', recItem)
|
||||||
|
provide('table_data_loader', isLoading)
|
||||||
|
|
||||||
|
watch([recId, recAction], () => {
|
||||||
|
let item: DeviceOrderItem | null
|
||||||
|
switch (recAction.value) {
|
||||||
|
case ActionEvents.showDetail:
|
||||||
|
item = pickItem()
|
||||||
|
if (item) {
|
||||||
|
isItemDetailDialogOpen.value = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case ActionEvents.showEdit:
|
||||||
|
item = pickItem()
|
||||||
|
if (item) {
|
||||||
|
isItemEntryDialogOpen.value = true
|
||||||
|
getDevices('', item.device_code)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case ActionEvents.showConfirmDelete:
|
||||||
|
isItemDelConfirmDialogOpen.value = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getDeviceOrderItems()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
function navClick(type: 'back' | 'delete' | 'draft' | 'submit') {
|
||||||
|
if (type === 'back') {
|
||||||
|
backToList()
|
||||||
|
} else if (type === 'delete') {
|
||||||
|
isDeleteConfirmationOpen.value = true
|
||||||
|
} else if (type === 'submit') {
|
||||||
|
isSubmitConfirmationOpen.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeOrder() {
|
||||||
|
const res = await remove(id)
|
||||||
|
if (res.success) {
|
||||||
|
backToList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitOrder() {
|
||||||
|
const res = await submit(id)
|
||||||
|
if (res.success) {
|
||||||
|
backToList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addItem() {
|
||||||
|
isItemEntryDialogOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDevices(value: string, code?: string) {
|
||||||
|
const res = await getDeviceList({ 'search': value, 'code': code })
|
||||||
|
if (res.success) {
|
||||||
|
devices.value = res.body.data
|
||||||
|
} else {
|
||||||
|
devices.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickItem(): DeviceOrderItem | null {
|
||||||
|
const item = items.value.find(item => item.id === recId.value)
|
||||||
|
selectedItem.value = item
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveItem() {
|
||||||
|
let res: any;
|
||||||
|
if(!selectedItem.value.id) {
|
||||||
|
res = await createItem(selectedItem.value)
|
||||||
|
} else {
|
||||||
|
res = await updateItem(selectedItem.value.id, selectedItem.value)
|
||||||
|
}
|
||||||
|
if (res.success) {
|
||||||
|
toast({ title: 'Berhasil', description: 'Resep telah di ajukan', variant: 'default' })
|
||||||
|
getDeviceOrderItems()
|
||||||
|
isItemEntryDialogOpen.value = false
|
||||||
|
selectedItem.value = genDeviceOrderItem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -28,10 +181,74 @@ const headerPrep: HeaderPrep = {
|
|||||||
class="mb-4 xl:mb-5"
|
class="mb-4 xl:mb-5"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ItemListEntry />
|
<EntryForm :data="data" @add="addItem" />
|
||||||
|
|
||||||
|
<ItemListEntry :data="items" @add="addItem" />
|
||||||
|
|
||||||
<Separator class="my-5" />
|
<Separator class="my-5" />
|
||||||
|
|
||||||
<div class="w-full flex justify-center">
|
<div class="w-full flex justify-center">
|
||||||
<Nav @click="navClick" />
|
<Nav @click="navClick" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Confirm delete -->
|
||||||
|
<RecordConfirmation
|
||||||
|
v-model:open="isDeleteConfirmationOpen"
|
||||||
|
action="delete"
|
||||||
|
:record="data"
|
||||||
|
@confirm="removeOrder()"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Confirm submit -->
|
||||||
|
<RecordConfirmation
|
||||||
|
v-model:open="isSubmitConfirmationOpen"
|
||||||
|
customTitle="Ajukan Order"
|
||||||
|
customMessage="Akan dilakukan pengajuan order alat kesehatan"
|
||||||
|
customConfirmText="Ajukan"
|
||||||
|
:record="data"
|
||||||
|
@confirm="submitOrder"
|
||||||
|
@cancel=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Item entry -->
|
||||||
|
<Dialog
|
||||||
|
v-model:open="isItemEntryDialogOpen"
|
||||||
|
title="Item Order"
|
||||||
|
size="lg"
|
||||||
|
prevent-outside
|
||||||
|
>
|
||||||
|
<ItemEntry
|
||||||
|
:data="selectedItem"
|
||||||
|
:devices="devices"
|
||||||
|
@close="isItemEntryDialogOpen = false"
|
||||||
|
@save="saveItem"
|
||||||
|
@update:searchText="getDevices"
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<RecordConfirmation
|
||||||
|
v-model:open="isItemDelConfirmDialogOpen"
|
||||||
|
action="delete"
|
||||||
|
size="md"
|
||||||
|
:record="recItem"
|
||||||
|
@confirm="() => handleActionRemove(recId, getDeviceOrderItems, toast)"
|
||||||
|
@cancel=""
|
||||||
|
>
|
||||||
|
<DE.Block mode="preview" label-size="small">
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Nama</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
|
<DE.Field>
|
||||||
|
{{ recItem.device.name }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell>
|
||||||
|
<DE.Label>Dosis</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
|
<DE.Field>
|
||||||
|
{{ recItem.quantity }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</DE.Block>
|
||||||
|
</RecordConfirmation>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,69 +1,39 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// Helpers
|
||||||
|
import { usePaginatedList } from '~/composables/usePaginatedList'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
|
||||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||||
import List from '~/components/app/device-order/list.vue'
|
import List from '~/components/app/device-order/list.vue'
|
||||||
|
|
||||||
// Helpers
|
|
||||||
import { usePaginatedList } from '~/composables/usePaginatedList'
|
|
||||||
import { toast } from '~/components/pub/ui/toast'
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
// import { useQueryMode } from '~/composables/useQueryMode'
|
|
||||||
import { useQueryCRUDMode } from '~/composables/useQueryCRUD'
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
|
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||||
import { type DeviceOrderFormData, DeviceOrderSchema } from '~/schemas/device-order.schema'
|
|
||||||
import type { DeviceOrder } from "~/models/device-order";
|
|
||||||
|
|
||||||
// Handlers
|
// Device order things
|
||||||
import {
|
import {
|
||||||
recId,
|
recId,
|
||||||
recAction,
|
recAction,
|
||||||
recItem,
|
recItem,
|
||||||
isReadonly,
|
isReadonly,
|
||||||
isProcessing,
|
|
||||||
isFormEntryDialogOpen,
|
|
||||||
isRecordConfirmationOpen,
|
|
||||||
onResetState,
|
|
||||||
handleActionSave,
|
handleActionSave,
|
||||||
handleActionEdit,
|
|
||||||
handleActionRemove,
|
handleActionRemove,
|
||||||
handleCancelForm,
|
|
||||||
} from '~/handlers/device-order.handler'
|
} from '~/handlers/device-order.handler'
|
||||||
|
import { getList, submit } from '~/services/device-order.service'
|
||||||
|
import type { ToastFn } from '~/handlers/_handler'
|
||||||
|
import { type DeviceOrder } from "~/models/device-order";
|
||||||
|
import ConfirmationInfo from '~/components/app/device-order/confirmation-info.vue'
|
||||||
|
|
||||||
// Services
|
// Props
|
||||||
import { getList } from '~/services/device-order.service'
|
const props = defineProps<{
|
||||||
|
encounter_id: number
|
||||||
|
}>()
|
||||||
|
|
||||||
const route = useRoute()
|
const encounter_id = props.encounter_id
|
||||||
const title = ref('')
|
|
||||||
|
|
||||||
// const { mode, openForm, backToList } = useQueryMode()
|
|
||||||
const { mode, goToEntry, backToList } = useQueryCRUDMode()
|
|
||||||
const { recordId } = useQueryCRUDRecordId()
|
|
||||||
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
isLoading,
|
|
||||||
paginationMeta,
|
|
||||||
searchInput,
|
|
||||||
handlePageChange,
|
|
||||||
handleSearch,
|
|
||||||
fetchData: getMyList,
|
|
||||||
} = usePaginatedList({
|
|
||||||
fetchFn: async (params: any) => {
|
|
||||||
const result = await getList({
|
|
||||||
search: params.search,
|
|
||||||
sort: 'createdAt:asc',
|
|
||||||
'page-number': params['page-number'] || 0,
|
|
||||||
'page-size': params['page-size'] || 10,
|
|
||||||
includes: 'parent,childrens',
|
|
||||||
})
|
|
||||||
return { success: result.success || false, body: result.body || {} }
|
|
||||||
},
|
|
||||||
entityName: 'device-order',
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// Header
|
||||||
|
const voidFn = () => {}
|
||||||
const headerPrep: HeaderPrep = {
|
const headerPrep: HeaderPrep = {
|
||||||
title: 'Order Alkes',
|
title: 'Order Alkes',
|
||||||
icon: 'i-lucide-box',
|
icon: 'i-lucide-box',
|
||||||
@@ -85,21 +55,88 @@ const headerPrep: HeaderPrep = {
|
|||||||
recItem.value = null
|
recItem.value = null
|
||||||
recId.value = 0
|
recId.value = 0
|
||||||
isReadonly.value = false
|
isReadonly.value = false
|
||||||
// await handleActionSave(recItem, getMyList, () => {}, () => {})
|
const saveResp = await handleActionSave({ encounter_id }, voidFn, voidFn, voidFn)
|
||||||
goToEntry()
|
if (saveResp.success) {
|
||||||
|
setQueryParams({
|
||||||
|
'mode': 'entry',
|
||||||
|
'id': saveResp.body?.data?.id.toString()
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isLoading,
|
||||||
|
paginationMeta,
|
||||||
|
searchInput,
|
||||||
|
handlePageChange,
|
||||||
|
handleSearch,
|
||||||
|
fetchData: getMyList,
|
||||||
|
} = usePaginatedList({
|
||||||
|
fetchFn: async (params: any) => {
|
||||||
|
const result = await getList({
|
||||||
|
'encounter-id': encounter_id,
|
||||||
|
search: params.search,
|
||||||
|
includes: 'doctor,doctor-employee,doctor-employee-person,items,items-device',
|
||||||
|
page: params.page,
|
||||||
|
'page-number': params['page-number'] || 0,
|
||||||
|
'page-size': params['page-size'] || 10,
|
||||||
|
})
|
||||||
|
return { success: result.success || false, body: result.body || {} }
|
||||||
|
},
|
||||||
|
entityName: 'device-order',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Selected item
|
||||||
|
const selectedItem = ref<DeviceOrder | null>()
|
||||||
|
const isSubmitConfirmationOpen = ref(false)
|
||||||
|
const isDeleteConfirmationOpen = ref(false)
|
||||||
|
const { setQueryParams } = useQueryParam()
|
||||||
|
|
||||||
provide('rec_id', recId)
|
provide('rec_id', recId)
|
||||||
provide('rec_action', recAction)
|
provide('rec_action', recAction)
|
||||||
provide('rec_item', recItem)
|
provide('rec_item', recItem)
|
||||||
provide('table_data_loader', isLoading)
|
provide('table_data_loader', isLoading)
|
||||||
|
|
||||||
// Watch for row actions when recId or recAction changes
|
watch([recId, recAction], () => {
|
||||||
onMounted(async () => {
|
let item: DeviceOrder | null
|
||||||
await getMyList()
|
switch (recAction.value) {
|
||||||
|
case ActionEvents.showDetail:
|
||||||
|
setQueryParams({
|
||||||
|
'mode': 'entry',
|
||||||
|
'id': recId.value.toString()
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case ActionEvents.showConfirmSubmit:
|
||||||
|
selectedItem.value = pickItem()
|
||||||
|
isSubmitConfirmationOpen.value = true
|
||||||
|
break
|
||||||
|
case ActionEvents.showConfirmDelete:
|
||||||
|
selectedItem.value = pickItem()
|
||||||
|
isDeleteConfirmationOpen.value = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
recAction.value = '';
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function handleActionSubmit(id: number, refresh: () => void, toast: ToastFn) {
|
||||||
|
const result = await submit(id)
|
||||||
|
if (result.success) {
|
||||||
|
toast({ title: 'Berhasil', description: 'Resep telah di ajukan', variant: 'default' })
|
||||||
|
setTimeout(refresh, 300)
|
||||||
|
} else {
|
||||||
|
toast({ title: 'Gagal', description: 'Gagal menjalankan perintah', variant: 'destructive' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickItem(): DeviceOrder | null {
|
||||||
|
const item = data.value.find(item => item.id === recId.value)
|
||||||
|
selectedItem.value = item
|
||||||
|
return item
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -108,38 +145,39 @@ onMounted(async () => {
|
|||||||
:prep="headerPrep"
|
:prep="headerPrep"
|
||||||
:ref-search-nav="headerPrep.refSearchNav"
|
:ref-search-nav="headerPrep.refSearchNav"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
class="mb-4 xl:mb-5"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<List
|
<List
|
||||||
|
v-if="!isLoading.dataListLoading"
|
||||||
:data="data"
|
:data="data"
|
||||||
:pagination-meta="paginationMeta"
|
:pagination-meta="paginationMeta"
|
||||||
@page-change="handlePageChange"
|
@page-change="handlePageChange"
|
||||||
/>
|
/>
|
||||||
|
<!--
|
||||||
|
@cancel="cancel"
|
||||||
|
@edit="edit"
|
||||||
|
@submit="submit"
|
||||||
|
-->
|
||||||
|
|
||||||
<!-- Record Confirmation Modal -->
|
<!-- Submit Record Confirmation Modal -->
|
||||||
<RecordConfirmation
|
<RecordConfirmation
|
||||||
v-model:open="isRecordConfirmationOpen"
|
v-model:open="isSubmitConfirmationOpen"
|
||||||
|
customTitle="Ajukan Order"
|
||||||
|
customMessage="Akan dilakukan pengajuan order alat kesehatan"
|
||||||
|
customConfirmText="Ajukan"
|
||||||
|
:record="recItem"
|
||||||
|
@confirm="() => handleActionSubmit(recId, getMyList, toast)"
|
||||||
|
>
|
||||||
|
<ConfirmationInfo :data="selectedItem" />
|
||||||
|
</RecordConfirmation>
|
||||||
|
|
||||||
|
<!-- Del Record Confirmation Modal -->
|
||||||
|
<RecordConfirmation
|
||||||
|
v-model:open="isDeleteConfirmationOpen"
|
||||||
action="delete"
|
action="delete"
|
||||||
:record="recItem"
|
:record="recItem"
|
||||||
@confirm="() => handleActionRemove(recId, getMyList, toast)"
|
@confirm="() => handleActionRemove(recId, getMyList, toast)"
|
||||||
@cancel=""
|
|
||||||
>
|
>
|
||||||
<template #default="{ record }">
|
<ConfirmationInfo :data="selectedItem" />
|
||||||
<div class="text-sm">
|
|
||||||
<p>
|
|
||||||
<strong>ID:</strong>
|
|
||||||
{{ record?.id }}
|
|
||||||
</p>
|
|
||||||
<p v-if="record?.name">
|
|
||||||
<strong>Nama:</strong>
|
|
||||||
{{ record.name }}
|
|
||||||
</p>
|
|
||||||
<p v-if="record?.code">
|
|
||||||
<strong>Kode:</strong>
|
|
||||||
{{ record.code }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</RecordConfirmation>
|
</RecordConfirmation>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,10 +3,14 @@
|
|||||||
import List from './list.vue'
|
import List from './list.vue'
|
||||||
import Entry from './entry.vue'
|
import Entry from './entry.vue'
|
||||||
|
|
||||||
const { mode } = useQueryMode()
|
defineProps<{
|
||||||
|
encounter_id: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { mode } = useQueryCRUDMode()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<List v-if="mode === 'list'" />
|
<List v-if="mode === 'list'" :encounter_id="encounter_id" />
|
||||||
<Entry v-else />
|
<Entry v-else :encounter_id="encounter_id" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user