Merge branch 'dev' into feat/kfr-kemoterapi-174
This commit is contained in:
@@ -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,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
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoading?: boolean
|
||||
@@ -92,8 +93,9 @@ watch(subSpecialistId, async (newValue) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Watch SEP number changes to notify parent
|
||||
watch(sepNumber, (newValue) => {
|
||||
// Debounced SEP number watcher: emit change only after user stops typing
|
||||
const debouncedSepNumber = refDebounced(sepNumber, 500)
|
||||
watch(debouncedSepNumber, (newValue) => {
|
||||
emit('event', 'sep-number-changed', newValue)
|
||||
})
|
||||
|
||||
@@ -452,7 +454,11 @@ defineExpose({
|
||||
name="i-lucide-loader-2"
|
||||
class="h-4 w-4 animate-spin"
|
||||
/>
|
||||
<span v-else>+</span>
|
||||
<Icon
|
||||
v-else
|
||||
name="i-lucide-plus"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
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
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-pdud.vue'))
|
||||
const action = defineAsyncComponent(() => import('./dropdown-action.vue'))
|
||||
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||
import { config } from './list.cfg'
|
||||
|
||||
const props = defineProps<{
|
||||
data: any[]
|
||||
data: any[],
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PubMyUiDataTable
|
||||
<DataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:rows="props.data"
|
||||
/>
|
||||
</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">
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { genderCodes } from '~/const/key-val/person';
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
|
||||
const props = defineProps<{
|
||||
data: Encounter
|
||||
}>()
|
||||
|
||||
let address = ''
|
||||
let address = ref('')
|
||||
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) {
|
||||
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) {
|
||||
dpjp = props.data.appointment_doctor.employee.person.name
|
||||
dpjp.value = props.data.appointment_doctor.employee.person.name
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||
<div class="w-full">
|
||||
<!-- Data Pasien -->
|
||||
<h2 class="mb-2 font-semibold md:text-base 2xl:text-lg">
|
||||
{{ data.patient.person.name }} - {{ data.patient.number }}
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-3">
|
||||
<div>
|
||||
<DE.Block
|
||||
mode="preview"
|
||||
labelSize="large"
|
||||
>
|
||||
<DE.Block mode="preview">
|
||||
<DE.Cell>
|
||||
<DE.Label class="font-semibold">No. RM</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ data.patient.person.birthDate?.substring(0, 10) }}
|
||||
</DE.Field>
|
||||
</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>
|
||||
{{ data.patient.person.gender_code }}
|
||||
{{ genderCodes[data.patient.person.gender_code as keyof typeof genderCodes] }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label class="font-semibold">Alamat</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
<div v-html="address"></div>
|
||||
</DE.Field>
|
||||
@@ -54,35 +54,39 @@ if (props.data.responsible_doctor) {
|
||||
</DE.Block>
|
||||
</div>
|
||||
<div>
|
||||
<DE.Block
|
||||
mode="preview"
|
||||
labelSize="large"
|
||||
>
|
||||
<DE.Block mode="preview">
|
||||
<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>
|
||||
{{ data.visitDate.substring(0, 10) }}
|
||||
</DE.Field>
|
||||
</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>
|
||||
{{ data.unit?.name }}
|
||||
</DE.Field>
|
||||
</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>
|
||||
{{ dpjp }}
|
||||
{{ data.unit?.name }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</div>
|
||||
<div>
|
||||
<DE.Block
|
||||
mode="preview"
|
||||
labelSize="large"
|
||||
>
|
||||
<DE.Block mode="preview">
|
||||
<DE.Cell>
|
||||
<DE.Label position="dynamic" class="font-semibold">DPJP</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ dpjp }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label
|
||||
position="dynamic"
|
||||
@@ -90,6 +94,7 @@ if (props.data.responsible_doctor) {
|
||||
>
|
||||
Billing
|
||||
</DE.Label>
|
||||
<DE.Colon class="pt-1" />
|
||||
<DE.Field class="text-base 2xl:text-lg">
|
||||
Rp. 000.000
|
||||
<!-- {{ 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',
|
||||
process: 'fresh',
|
||||
done: 'positive',
|
||||
canceled: 'destructive',
|
||||
cancel: 'destructive',
|
||||
rejected: 'destructive',
|
||||
skiped: 'negative',
|
||||
}
|
||||
|
||||
@@ -143,12 +143,16 @@ watch(props, (value) => {
|
||||
nationalId.value = objects?.nationalIdentity || '-'
|
||||
medicalRecordNumber.value = objects?.medicalRecordNumber || '-'
|
||||
patientName.value = objects?.patientName || '-'
|
||||
phoneNumber.value = objects?.phoneNumber || '-'
|
||||
if (objects?.sepType === 'internal') {
|
||||
admissionType.value = '4'
|
||||
}
|
||||
if (objects?.sepType === 'external') {
|
||||
admissionType.value = '1'
|
||||
}
|
||||
if (objects?.diagnoseLabel) {
|
||||
initialDiagnosis.value = objects?.diagnoseLabel
|
||||
}
|
||||
isDateReload.value = true
|
||||
setTimeout(() => {
|
||||
if (objects?.letterDate) {
|
||||
@@ -176,6 +180,9 @@ onMounted(() => {
|
||||
if (!isService.value) {
|
||||
serviceType.value = '2'
|
||||
}
|
||||
if (!admissionType.value) {
|
||||
admissionType.value = '1'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ const props = defineProps<{
|
||||
|
||||
<template>
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="props.data"
|
||||
v-bind="config"
|
||||
v-on="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { defineAsyncComponent } from 'vue'
|
||||
|
||||
type SmallDetailDto = any
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, { width: 100 }, { width: 120 }, {}, {}, {}, { width: 100 }, { width: 100 }, {}, { width: 50 }],
|
||||
@@ -20,7 +20,7 @@ export const config: Config = {
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['time', 'employee_id', 'main_complaint', 'encounter_id', 'diagnose', 'status', 'action'],
|
||||
keys: ['time', 'employee_id', 'main_complaint', 'encounter', 'diagnose', 'status', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
@@ -44,6 +44,10 @@ export const config: Config = {
|
||||
return '-'
|
||||
}
|
||||
},
|
||||
encounter(rec: any) {
|
||||
const data = rec?.encounter ?? {}
|
||||
return data?.class_code || '-'
|
||||
},
|
||||
diagnose(rec: any) {
|
||||
const { value } = rec ?? {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user