refactor(patient): extract age calculation logic to shared utility
Move age calculation logic from multiple components to a shared utility function in person model Add age display to patient entry form and update preview component to use shared utility Remove redundant age field from select-dob component
This commit is contained in:
@@ -5,6 +5,9 @@ import { toTypedSchema } from '@vee-validate/zod'
|
||||
// types
|
||||
import { type PatientFormData, PatientSchema } from '~/schemas/patient.schema'
|
||||
|
||||
// utils
|
||||
import { calculateAge } from '~/models/person'
|
||||
|
||||
// components
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { InputBase, FileField as FileUpload } from '~/components/pub/my-ui/form'
|
||||
@@ -26,7 +29,9 @@ import {
|
||||
SelectReligion,
|
||||
} from './fields'
|
||||
|
||||
interface FormData extends PatientFormData {}
|
||||
interface FormData extends PatientFormData {
|
||||
_calculatedAge: string
|
||||
}
|
||||
|
||||
// Type untuk initial values (sebelum transform schema)
|
||||
interface PatientFormInput {
|
||||
@@ -74,6 +79,18 @@ defineExpose({
|
||||
setValues,
|
||||
values,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => values.birthDate,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
setFieldValue('_calculatedAge', calculateAge(newValue))
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -140,6 +157,13 @@ defineExpose({
|
||||
is-required
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="_calculatedAge"
|
||||
label="Usia"
|
||||
placeholder="-"
|
||||
numeric-only
|
||||
is-disabled
|
||||
/>
|
||||
<SelectEducation
|
||||
field-name="education"
|
||||
label="Pendidikan"
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
<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 { calculateAge } from '~/models/person'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
// componenets
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
@@ -23,56 +22,13 @@ const {
|
||||
fieldName = 'birthDate',
|
||||
label = 'Tanggal Lahir',
|
||||
placeholder = 'Pilih tanggal lahir',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = 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'
|
||||
}
|
||||
}
|
||||
const patientAge = ref<string>('-')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -86,7 +42,6 @@ function calculateAge(birthDate: string | Date | undefined): string {
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
@@ -116,27 +71,4 @@ function calculateAge(birthDate: string | Date | undefined): string {
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label label-for="patientAge">Usia</DE.Label>
|
||||
<DE.Field id="patientAge">
|
||||
<FormField name="patientAge">
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
:value="patientAge"
|
||||
disabled
|
||||
readonly
|
||||
placeholder="Masukkan tanggal lahir"
|
||||
:class="
|
||||
cn(
|
||||
'cursor-not-allowed bg-gray-50 focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
religionCodes,
|
||||
} from '~/lib/constants'
|
||||
import { mapToComboboxOptList } from '~/lib/utils'
|
||||
import { calculateAge } from '~/models/person'
|
||||
|
||||
// components
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '~/components/pub/ui/accordion'
|
||||
@@ -64,14 +65,7 @@ const patientAge = computed(() => {
|
||||
if (!props.patient.person.birthDate) {
|
||||
return '-'
|
||||
}
|
||||
const birthDate = new Date(props.patient.person.birthDate)
|
||||
const today = new Date()
|
||||
let age = today.getFullYear() - birthDate.getFullYear()
|
||||
const monthDiff = today.getMonth() - birthDate.getMonth()
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age--
|
||||
}
|
||||
return age
|
||||
return calculateAge(props.patient.person.birthDate)
|
||||
})
|
||||
// #endregion
|
||||
|
||||
@@ -117,7 +111,7 @@ function onNavigate(type: ClickType) {
|
||||
: '-'
|
||||
}}
|
||||
</DetailRow>
|
||||
<DetailRow label="Usia">{{ patientAge || '-' }} Tahun</DetailRow>
|
||||
<DetailRow label="Usia">{{ patientAge || '-' }}</DetailRow>
|
||||
<DetailRow label="Tanggal Daftar">
|
||||
{{
|
||||
patient.person.createdAt
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { differenceInDays, differenceInMonths, differenceInYears, parseISO } from 'date-fns'
|
||||
|
||||
import { type Base, genBase } from './_base'
|
||||
import type { PersonAddress } from './person-address'
|
||||
import type { PersonContact } from './person-contact'
|
||||
@@ -56,3 +58,44 @@ export function parseName(person: Person): string {
|
||||
|
||||
return fullName
|
||||
}
|
||||
|
||||
export 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'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user