Merge branch 'dev' into feat/surat-kontrol-135

This commit is contained in:
Muhammad Hasyim Chaidir Ali
2025-11-07 09:11:10 +07:00
committed by GitHub
58 changed files with 1777 additions and 509 deletions
@@ -0,0 +1,107 @@
<script lang="ts" setup>
//
import { LucideCheck } from 'lucide-vue-next';
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
// Components
import type z from 'zod'
import ComboBox from '~/components/pub/my-ui/combobox/combobox.vue'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { CheckInFormData } from '~/schemas/encounter.schema'
import type { Encounter } from '~/models/encounter'
interface Props {
schema: z.ZodSchema<any>
values: any
doctors: { value: string; label: string }[]
employees: { value: string; label: string }[]
encounter: Encounter
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const emit = defineEmits<{
submit: [values: CheckInFormData]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: {
responsible_doctor_id: 0,
adm_employee_id: 0,
registeredAt: props.values.values?.registeredAt || '',
} as Partial<CheckInFormData>,
})
const [responsible_doctor_id, responsible_doctor_idAttrs] = defineField('responsible_doctor_id')
const [adm_employee_id, adm_employee_idAttrs] = defineField('discharge_method_code')
const [registeredAt, registeredAtAttrs] = defineField('registeredAt')
function submitForm() {
const formData: CheckInFormData = {
responsible_doctor_id: responsible_doctor_id.value,
adm_employee_id: adm_employee_id.value,
// registeredAt: registeredAt.value || '',
}
emit('submit', formData)
}
</script>
<template>
<DE.Block :cell-flex="false">
<DE.Cell>
<DE.Label>Dokter</DE.Label>
<DE.Field>
<ComboBox
id="doctor"
v-model="responsible_doctor_id"
v-bind="responsible_doctor_idAttrs"
:items="doctors"
:disabled="isLoading || isReadonly"
placeholder="Pilih Dokter DPJP"
search-placeholder="Pilih DPJP"
empty-message="DPJP tidak ditemukan"
/>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label>PJ Berkas</DE.Label>
<DE.Field>
<ComboBox
id="doctor"
v-model="adm_employee_id"
v-bind="adm_employee_idAttrs"
:items="employees"
:disabled="isLoading || isReadonly"
placeholder="Pilih Dokter DPJP"
search-placeholder="Pilih petugas"
empty-message="Petugas tidak ditemukan"
/>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label>Waktu Masuk</DE.Label>
<DE.Field>
<Input
id="name"
v-model="registeredAt"
v-bind="registeredAtAttrs"
:disabled="isLoading || isReadonly"
/>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center">
<Button @click="submitForm">
<LucideCheck />
Simpan
</Button>
</div>
</template>
<style>
</style>
@@ -0,0 +1,56 @@
<script lang="ts" setup>
// Components
import { LucidePen } from 'lucide-vue-next';
import * as DE from '~/components/pub/my-ui/doc-entry'
import Input from '~/components/pub/ui/input/Input.vue';
import type { Encounter } from '~/models/encounter'
interface Props {
encounter: Encounter
}
const props = defineProps<Props>()
const doctor = ref('-belum dipilih-')
const adm = ref('-belum dipilih-')
const emit = defineEmits<{
edit: []
}>()
watch(props.encounter, () => {
doctor.value = props.encounter.responsible_doctor?.employee?.person?.name ?? props.encounter.appointment_doctor?.employee?.person?.name ?? '-belum dipilih-'
adm.value = props.encounter.adm_employee?.person?.name ?? '-belum dipilih-'
})
</script>
<template>
<DE.Block :cell-flex="false">
<DE.Cell>
<DE.Label class="font-semibold">Dokter</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ doctor }}</div>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label class="font-semibold">PJ Berkas</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ adm }}</div>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label class="font-semibold">Waktu Masuk</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ encounter?.registeredAt || '-' }}</div>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center">
<Button @click="() => emit('edit')">
<LucidePen />
Edit
</Button>
</div>
</template>
<style>
</style>
@@ -0,0 +1,187 @@
<script lang="ts" setup>
//
import { LucideCheck } from 'lucide-vue-next';
import { useForm, useFieldArray } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
//
import type z from 'zod'
import * as CB from '~/components/pub/my-ui/combobox'
import { dischargeMethodCodes } from '~/lib/constants';
import type {
CheckOutFormData,
CheckOutDeathFormData,
CheckOutInternalReferenceFormData
} from '~/schemas/encounter.schema'
import * as Table from '~/components/pub/ui/table'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { InternalReference, CreateDto as InternalReferenceCreateDto } from '~/models/internal-reference';
interface Props {
schema: z.ZodSchema<any>
values: any
units: any[]
doctors: any[]
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const dischargeMethodItems = CB.recStrToItem(dischargeMethodCodes)
const emit = defineEmits<{
submit: [values: CheckOutFormData]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: {
discharge_method_code: '',
discharge_date: '',
internalReferences: [{ unit_id: 0, doctor_id: 0 }],
deathCauses: [""],
} as Partial<CheckOutFormData>,
})
const [ discharge_method_code, discharge_method_codeAttrs ] = defineField('discharge_method_code')
const [ discharge_date, discharge_dateAttrs ] = defineField('discharge_date')
const { fields, push, remove } = useFieldArray<InternalReferenceCreateDto>('internalReferences');
function submitForm(values: any) {
if (['consul-poly', 'consul-executive'].includes(discharge_method_code.value)) {
const formData: CheckOutInternalReferenceFormData = {
discharge_method_code: discharge_method_code.value,
discharge_date: discharge_date.value,
internalReferences: [{ unit_id: 0, doctor_id: 0 }],
}
emit('submit', formData)
} else if (discharge_method_code.value === 'death') {
const formData: CheckOutDeathFormData = {
discharge_method_code: discharge_method_code.value,
discharge_date: discharge_date.value,
death_cause: [""],
}
emit('submit', formData)
} else {
const formData: CheckOutFormData = {
discharge_method_code: discharge_method_code.value,
discharge_date: discharge_date.value,
}
emit('submit', formData)
}
}
const resetForm = () => {
discharge_method_code.value = ''
discharge_date.value = ''
}
</script>
<template>
<DE.Block :cellFlex="false">
<DE.Cell>
<DE.Label>Alasan Keluar</DE.Label>
<DE.Field>
<CB.Combobox
id="dischargeMethodItems"
v-model="discharge_method_code"
v-bind="discharge_method_codeAttrs"
:items="dischargeMethodItems"
:disabled="isLoading || isReadonly"
placeholder="Pilih Cara Keluar"
search-placeholder="Cari Cara Keluar"
empty-message="Cara Keluar tidak ditemukan"
/>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label>Waktu Keluar</DE.Label>
<DE.Cell>
<Input
id="discharge_date"
v-model="discharge_date"
v-bind="discharge_dateAttrs"
:disabled="isLoading || isReadonly"
/>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="'death' == discharge_method_code">
<DE.Label>Sebab Meninggal</DE.Label>
<DE.Cell>
<div class="mb-3">
<Input />
</div>
<div>
<Button
v-if="!isReadonly"
type="button"
:disabled="isLoading || !meta.valid"
@click="submitForm"
>
Tambah Sebab meninggal
</Button>
</div>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="['consul-poly', 'consul-executive'].includes(discharge_method_code)">
<DE.Label>Tujuan</DE.Label>
<DE.Field>
<Table.Table class="border mb-3">
<Table.TableHeader class="bg-neutral-100 dark:bg-slate-800">
<Table.TableCell class="text-center">Poly</Table.TableCell>
<Table.TableCell class="text-center">DPJP</Table.TableCell>
<Table.TableCell class="text-center !w-10"></Table.TableCell>
</Table.TableHeader>
<Table.TableBody>
<Table.TableRow v-for="(item, index) in fields" :key="index">
<Table.TableCell class="!p-0.5">
<CB.Combobox
id="dischargeMethodItems"
:v-model.number="item.value.unit_id"
:items="units"
:disabled="isLoading || isReadonly"
placeholder="Pilih Poly"
search-placeholder="Cari Poly"
empty-message="Poly tidak ditemukan"
/>
</Table.TableCell>
<Table.TableCell class="!p-0.5">
<CB.Combobox
id="dischargeMethodItems"
:v-model.number="item.value.doctor_id"
:items="units"
:disabled="isLoading || isReadonly"
placeholder="Pilih Dokter"
search-placeholder="Pilih Dokter"
empty-message="Dokter tidak ditemukan"
/>
</Table.TableCell>
<Table.TableCell>
<Button variant="destructive" size="xs" @click="remove(index)" class="w-6 h-6 rounded-full">
X
</Button>
</Table.TableCell>
</Table.TableRow>
</Table.TableBody>
</Table.Table>
<div>
<Button @click="push({ encounter_id: 0, unit_id: 0, doctor_id: 0 })">Tambah</Button>
</div>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center">
<Button @click="submitForm">>
<LucideCheck />
Simpan
</Button>
</div>
</template>
<style>
</style>
@@ -0,0 +1,90 @@
<script lang="ts" setup>
//
import { LucidePen, LucideCheck } from 'lucide-vue-next';
//
import * as CB from '~/components/pub/my-ui/combobox'
import { dischargeMethodCodes } from '~/lib/constants';
import * as Table from '~/components/pub/ui/table'
import * as DE from '~/components/pub/my-ui/doc-entry'
import type { Encounter } from '~/models/encounter';
interface Props {
encounter: Encounter
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const dischargeMethodItems = CB.recStrToItem(dischargeMethodCodes)
const emit = defineEmits<{
edit: [],
finish: []
}>()
</script>
<template>
<DE.Block :cellFlex="false">
<DE.Cell>
<DE.Label class="font-semibold">Alasan Keluar</DE.Label>
<DE.Field>
<div class="py-2 border-b border-b-slate-300">{{ encounter.discharge_method_code || '-belum ditentukan-' }}</div>
</DE.Field>
</DE.Cell>
<DE.Cell>
<DE.Label class="font-semibold">Waktu Keluar</DE.Label>
<DE.Cell>
<div class="py-2 border-b border-b-slate-300">{{ encounter.discharge_date || '-belum ditentukan-' }}</div>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="'death' == encounter.discharge_method_code">
<DE.Label class="font-semibold">Sebab Meninggal</DE.Label>
<DE.Cell>
<div class="mb-3">
<div class="py-2 border-b border-b-slate-300">{{ encounter.discharge_date }}</div>
</div>
</DE.Cell>
</DE.Cell>
<DE.Cell v-if="['consul-poly', 'consul-executive'].includes(encounter.discharge_method_code || '')">
<DE.Label class="font-semibold">Tujuan</DE.Label>
<DE.Field>
<Table.Table class="border mb-3">
<Table.TableHeader class="bg-neutral-100 dark:bg-slate-800">
<Table.TableCell class="text-center">Poly</Table.TableCell>
<Table.TableCell class="text-center">DPJP</Table.TableCell>
<Table.TableCell class="text-center !w-10"></Table.TableCell>
</Table.TableHeader>
<Table.TableBody>
<Table.TableRow v-for="(item, index) in encounter.internalReferences" :key="index">
<Table.TableCell class="!p-0.5">
</Table.TableCell>
<Table.TableCell class="!p-0.5">
</Table.TableCell>
<Table.TableCell>
</Table.TableCell>
</Table.TableRow>
</Table.TableBody>
</Table.Table>
</DE.Field>
</DE.Cell>
</DE.Block>
<div class="text-center [&>*]:mx-1">
<Button @click="() => emit('edit')">
<LucidePen />
Edit
</Button>
<Button @click="() => emit('finish')">
<LucideCheck />
Selesai
</Button>
</div>
</template>
<style>
</style>
-6
View File
@@ -1,6 +0,0 @@
<script setup lang="ts">
</script>
<template>
<div class="p-10 text-center">Hello World!!!</div>
</template>
+4 -80
View File
@@ -7,94 +7,18 @@ const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dr
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
export const config: Config = {
cols: [
{},
{},
{},
{ width: 100 },
{ width: 120 },
{},
{},
{},
{ width: 100 },
{ width: 100 },
{},
{ width: 50 },
],
cols: [{}, {}, {}, {}],
headers: [
[
{ label: 'Nama' },
{ label: 'Rekam Medis' },
{ label: 'KTP' },
{ label: 'Tgl Lahir' },
{ label: 'Umur' },
{ label: 'JK' },
{ label: 'Pendidikan' },
{ label: 'Status' },
{ label: '' },
],
],
headers: [[{ label: 'Kode' }, { label: 'Nama (FHIR)' }, { label: 'Nama (ID)' }, { label: '' }]],
keys: [
'name',
'medicalRecord_number',
'identity_number',
'birth_date',
'patient_age',
'gender',
'education',
'status',
'action',
],
keys: ['code', 'name', 'indName', 'action'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
],
parses: {
name: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return `${recX.firstName} ${recX.middleName || ''} ${recX.lastName || ''}`
},
identity_number: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
return '(TANPA NIK)'
}
return recX.identity_number
},
birth_date: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (typeof recX.birth_date == 'object' && recX.birth_date) {
return (recX.birth_date as Date).toLocaleDateString()
} else if (typeof recX.birth_date == 'string') {
return (recX.birth_date as string).substring(0, 10)
}
return recX.birth_date
},
patient_age: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return recX.birth_date?.split('T')[0]
},
gender: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (typeof recX?.gender_code !== 'number' && recX?.gender_code !== '') {
return 'Tidak Diketahui'
}
return recX.gender_code
},
education: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (typeof recX.education_code == 'number' && recX.education_code >= 0) {
return recX.education_code
} else if (typeof recX.education_code) {
return recX.education_code
}
return '-'
},
},
parses: {},
components: {
action(rec, idx) {
@@ -2,7 +2,7 @@
import { config } from './list-cfg'
defineProps<{ data: any[] }>()
const modelValue = defineModel<any | null>()
const modelValue = defineModel<any[]>('modelValue', { default: [] })
</script>
<template>
+13 -9
View File
@@ -1,8 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import { Trash2 } from 'lucide-vue-next'
// import { Button } from '@/components/ui/button'
// import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
interface Diagnosa {
id: number
@@ -10,10 +7,10 @@ interface Diagnosa {
icd: string
}
const list = ref<Diagnosa[]>([{ id: 1, diagnosa: 'Acute appendicitis', icd: 'K35' }])
const modelValue = defineModel<Diagnosa[]>({ default: [] })
function removeItem(id: number) {
list.value = list.value.filter((item) => item.id !== id)
modelValue.value = modelValue.value.filter((item) => item.id !== id)
}
</script>
@@ -30,12 +27,19 @@ function removeItem(id: number) {
</TableHeader>
<TableBody>
<TableRow v-for="(item, i) in list" :key="item.id">
<TableRow
v-for="(item, i) in modelValue"
:key="item.id"
>
<TableCell class="text-center font-medium">{{ i + 1 }}</TableCell>
<TableCell>{{ item.diagnosa }}</TableCell>
<TableCell>{{ item.icd }}</TableCell>
<TableCell>{{ item.code }}</TableCell>
<TableCell>{{ item.name }}</TableCell>
<TableCell class="text-center">
<Button variant="ghost" size="icon" @click="removeItem(item.id)">
<Button
variant="ghost"
size="icon"
@click="removeItem(item.id)"
>
<Trash2 class="h-4 w-4 text-gray-500 hover:text-red-500" />
</Button>
</TableCell>
+5 -5
View File
@@ -63,13 +63,13 @@ const validate = async () => {
}
defineExpose({ validate })
const icdPreview = inject('icdPreview')
const isExcluded = (key: string) => props.excludeFields?.includes(key)
</script>
<template>
<form id="entry-form">
{{ errors }}
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<Block>
<Cell>
@@ -285,7 +285,7 @@ const isExcluded = (key: string) => props.excludeFields?.includes(key)
<Button
class="rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('modal', 'diagnosa')"
@click="emit('modal', 'diagnosa')"
>
+ Pilih Diagnosa
</Button>
@@ -298,7 +298,7 @@ const isExcluded = (key: string) => props.excludeFields?.includes(key)
<Button
class="rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('modal', 'prosedur')"
@click="emit('modal', 'prosedur')"
>
+ Pilih Prosedur
</Button>
@@ -307,8 +307,8 @@ const isExcluded = (key: string) => props.excludeFields?.includes(key)
</Block>
<div class="mb-8 grid grid-cols-2 gap-4">
<AppIcdPreview />
<AppIcdPreview />
<AppIcdPreview v-model="icdPreview.diagnoses" />
<AppIcdPreview v-model="icdPreview.procedures" />
</div>
<Block :colCount="3">
+10 -1
View File
@@ -85,6 +85,7 @@ const validate = async () => {
}
defineExpose({ validate })
const icdPreview = inject('icdPreview')
const isExcluded = (key: string) => props.excludeFields?.includes(key)
</script>
@@ -452,10 +453,18 @@ const isExcluded = (key: string) => props.excludeFields?.includes(key)
<div class="my-2">
<h1 class="font-semibold">Diagnosa Fungsional (ICD-X)</h1>
<Button
class="rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emit('click', 'fungsional')"
>
+ Pilih Prosedur
</Button>
</div>
<div class="mb-8 grid grid-cols-2 gap-4">
<AppIcdPreview />
<AppIcdPreview v-model="icdPreview.diagnoses" />
</div>
<div class="my-2">
+1
View File
@@ -43,5 +43,6 @@ defineExpose({ validate })
@click="$emit('click', $event)"
@submit="$emit('submit', $event)"
@cancel="$emit('cancel', $event)"
@modal="$emit('modal', $event)"
/>
</template>
+17 -15
View File
@@ -86,6 +86,8 @@ const validate = async () => {
defineExpose({ validate })
const icdPreview = inject('icdPreview')
const isExcluded = (key: string) => props.excludeFields?.includes(key)
const disorders = ref<string[]>([])
const therapies = ref<string[]>([])
@@ -558,33 +560,33 @@ const therapyOptions = ['Terapi Latihan', 'Modalitas Fisik', 'Protesa/Ortosa', '
<Button
class="my-2 rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('click', 'prosedur')"
@click="emit('click', 'diagnosa')"
>
+ Pilih Prosedur
</Button>
<AppIcdPreview />
<AppIcdPreview v-model="icdPreview.diagnoses" />
</div>
<div>
<span class="text-md">Diagnosa Fungsional (ICD-X)</span>
<Button
class="my-2 rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emit('click', 'fungsional')"
>
+ Pilih Prosedur
</Button>
<AppIcdPreview v-model="icdPreview.fungsional" />
</div>
<div>
<span class="text-md">Diagnosa Medis (ICD-X)</span>
<Button
class="my-2 rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('click', 'prosedur')"
@click="emit('click', 'prosedur')"
>
+ Pilih Prosedur
</Button>
<AppIcdPreview />
</div>
<div>
<span class="text-md">Diagnosa Medis (ICD-X)</span>
<Button
class="my-2 rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('click', 'prosedur')"
>
+ Pilih Prosedur
</Button>
<AppIcdPreview />
<AppIcdPreview v-model="icdPreview.procedures" />
</div>
</div>
+32 -68
View File
@@ -6,46 +6,21 @@ type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
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 }],
headers: [
[
{ label: 'Nama' },
{ label: 'Rekam Medis' },
{ label: 'KTP' },
{ label: 'Tgl Lahir' },
{ label: 'Umur' },
{ label: 'JK' },
{ label: 'Pendidikan' },
{ label: 'Tanggal' },
{ label: 'DPJP' },
{ label: 'Keluhan & Riwayat' },
{ label: 'Pemeriksaan' },
{ label: 'Diagnosa' },
{ label: 'Status' },
{ label: '' },
{ label: 'Aksi' },
],
],
keys: [
'name',
'medicalRecord_number',
'identity_number',
'birth_date',
'patient_age',
'gender',
'education',
'status',
'action',
],
keys: ['time', 'employee_id', 'main_complaint', 'encounter_id', 'diagnose', 'status', 'action'],
delKeyNames: [
{ key: 'code', label: 'Kode' },
@@ -53,45 +28,34 @@ export const config: Config = {
],
parses: {
name: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return `${recX.firstName} ${recX.middleName || ''} ${recX.lastName || ''}`
time(rec: any) {
return rec.time ? new Date(rec.time).toLocaleDateString() : ''
},
identity_number: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (recX.identity_number?.substring(0, 5) === 'BLANK') {
return '(TANPA NIK)'
main_complaint(rec: any) {
const { value } = rec ?? {}
if (typeof value !== 'string') return '-'
try {
const parsed = JSON.parse(value)
console.log('parsed', parsed)
return parsed?.['prim-compl'] || '-'
} catch {
return '-'
}
return recX.identity_number
},
birth_date: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (typeof recX.birth_date === 'object' && recX.birth_date) {
return (recX.birth_date as Date).toLocaleDateString()
} else if (typeof recX.birth_date === 'string') {
return recX.birth_date.substring(0, 10)
diagnose(rec: any) {
const { value } = rec ?? {}
if (typeof value !== 'string') return '-'
try {
const parsed = JSON.parse(value)
const diagnose = parsed?.diagnose || []
return diagnose.map((d: any) => d.name).join(', ')
} catch {
return '-'
}
return recX.birth_date
},
patient_age: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return recX.birth_date?.split('T')[0]
},
gender: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (typeof recX?.gender_code !== 'number' && recX?.gender_code !== '') {
return 'Tidak Diketahui'
}
return recX.gender_code
},
education: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
if (typeof recX.education_code === 'number' && recX.education_code >= 0) {
return recX.education_code
} else if (typeof recX.education_code !== 'undefined') {
return recX.education_code
}
return '-'
},
},