feat(patient): add edit functionality to patient form - Modify genPatientEntity to accept existing patient data for updates - Add handleActionEdit handler for edit mode - Update form to handle both create and edit modes - Rename patient ref to patientDetail for clarity refactor(patient): update marital status codes and job options mapping - Change marital status enum values to standardized codes (S, M, D, W) - Simplify job options and marital status options mapping using mapToComboboxOptList - Add error handling in patient data loading ajust styling text based on combobox wip: edit patient redirect refactor(models): update type definitions and form field handling - Add field-name prop to SelectDob component for better form handling - Update Person and Patient interfaces to use null for optional fields - Add maritalStatus_code field to Person interface - Improve type safety by replacing undefined with null for optional fields fix casting radio str to boolean and parsing date error
226 lines
5.4 KiB
Vue
226 lines
5.4 KiB
Vue
<script setup lang="ts">
|
|
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
|
import type { Summary } from '~/components/pub/my-ui/summary-card/type'
|
|
// #region Imports
|
|
import { Calendar, Hospital, UserCheck, UsersRound } from 'lucide-vue-next'
|
|
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
|
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
|
|
|
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
|
import SummaryCard from '~/components/pub/my-ui/summary-card/summary-card.vue'
|
|
import { usePaginatedList } from '~/composables/usePaginatedList'
|
|
|
|
import { getPatients, removePatient } from '~/services/patient.service'
|
|
|
|
// #endregion
|
|
|
|
// #region State
|
|
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
|
|
fetchFn: (params) => getPatients({ ...params, includes: ['person', 'person-Addresses'] }),
|
|
entityName: 'patient',
|
|
})
|
|
|
|
const refSearchNav: RefSearchNav = {
|
|
onClick: () => {
|
|
// open filter modal
|
|
},
|
|
onInput: (val: string) => {
|
|
searchInput.value = val
|
|
},
|
|
onClear: () => {
|
|
searchInput.value = ''
|
|
},
|
|
}
|
|
|
|
const isRecordConfirmationOpen = ref(false)
|
|
const summaryLoading = ref(false)
|
|
|
|
const recId = ref<number>(0)
|
|
const recAction = ref<string>('')
|
|
const recItem = ref<any>(null)
|
|
|
|
const headerPrep: HeaderPrep = {
|
|
title: 'Pasien',
|
|
icon: 'i-lucide-users',
|
|
addNav: {
|
|
label: 'Tambah',
|
|
onClick: () => navigateTo('/client/patient/add'),
|
|
},
|
|
}
|
|
|
|
// Disable dulu, ayahab kalo diminta
|
|
// const summaryData = ref<Summary[]>([
|
|
// {
|
|
// title: 'Total Pasien',
|
|
// icon: UsersRound,
|
|
// metric: 23,
|
|
// trend: 15,
|
|
// timeframe: 'daily',
|
|
// },
|
|
// {
|
|
// title: 'Pasien Aktif',
|
|
// icon: UserCheck,
|
|
// metric: 100,
|
|
// trend: 9,
|
|
// timeframe: 'daily',
|
|
// },
|
|
// {
|
|
// title: 'Kunjungan Hari Ini',
|
|
// icon: Calendar,
|
|
// metric: 52,
|
|
// trend: 1,
|
|
// timeframe: 'daily',
|
|
// },
|
|
// {
|
|
// title: 'Peserta BPJS',
|
|
// icon: Hospital,
|
|
// metric: 71,
|
|
// trend: -3,
|
|
// timeframe: 'daily',
|
|
// },
|
|
// ])
|
|
// #endregion
|
|
|
|
// #region Lifecycle Hooks
|
|
onMounted(() => {
|
|
getPatientSummary()
|
|
})
|
|
// #endregion
|
|
|
|
// #region Functions
|
|
async function getPatientSummary() {
|
|
try {
|
|
summaryLoading.value = true
|
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
} catch (error) {
|
|
console.error('Error fetching patient summary:', error)
|
|
} finally {
|
|
summaryLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Handle confirmation result
|
|
async function handleConfirmDelete(record: any, action: string) {
|
|
console.log('Confirmed action:', action, 'for record:', record)
|
|
|
|
if (action === 'delete' && record?.id) {
|
|
try {
|
|
const result = await removePatient(record.id)
|
|
if (result.success) {
|
|
console.log('Patient deleted successfully')
|
|
// Refresh the list
|
|
await fetchData()
|
|
} else {
|
|
console.error('Failed to delete patient:', result)
|
|
// Handle error - show error message to user
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting patient:', error)
|
|
// Handle error - show error message to user
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleCancelConfirmation() {
|
|
// Reset record state when cancelled
|
|
recId.value = 0
|
|
recAction.value = ''
|
|
recItem.value = null
|
|
}
|
|
// #endregion
|
|
|
|
// #region Provide
|
|
provide('rec_id', recId)
|
|
provide('rec_action', recAction)
|
|
provide('rec_item', recItem)
|
|
provide('table_data_loader', isLoading)
|
|
// #endregion
|
|
|
|
// #region Watchers
|
|
watch([recId, recAction], ([newId, newAction]) => {
|
|
switch (newAction) {
|
|
case ActionEvents.showDetail:
|
|
navigateTo({
|
|
name: 'client-patient-id',
|
|
params: { id: newId },
|
|
})
|
|
break
|
|
|
|
case ActionEvents.showEdit:
|
|
navigateTo({
|
|
name: 'client-patient-id-edit',
|
|
params: {
|
|
id: newId,
|
|
},
|
|
})
|
|
break
|
|
|
|
case ActionEvents.showConfirmDelete:
|
|
// Trigger confirmation modal open
|
|
isRecordConfirmationOpen.value = true
|
|
break
|
|
}
|
|
})
|
|
// #endregion
|
|
</script>
|
|
|
|
<template>
|
|
<Header
|
|
v-model:search="searchInput"
|
|
:prep="{ ...headerPrep }"
|
|
:ref-search-nav="refSearchNav"
|
|
/>
|
|
|
|
<!-- Disable dulu, ayahab kalo diminta beneran -->
|
|
<!-- <div class="my-4 flex flex-1 flex-col gap-4 md:gap-8">
|
|
<div class="grid gap-4 md:grid-cols-2 md:gap-8 lg:grid-cols-4">
|
|
<template v-if="summaryLoading">
|
|
<SummaryCard
|
|
v-for="n in 4"
|
|
:key="n"
|
|
is-skeleton
|
|
/>
|
|
</template>
|
|
<template v-else>
|
|
<SummaryCard
|
|
v-for="card in summaryData"
|
|
:key="card.title"
|
|
:stat="card"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
-->
|
|
|
|
<AppPatientList
|
|
:data="data"
|
|
:pagination-meta="paginationMeta"
|
|
@page-change="handlePageChange"
|
|
/>
|
|
|
|
<RecordConfirmation
|
|
v-model:open="isRecordConfirmationOpen"
|
|
action="delete"
|
|
:record="recItem"
|
|
@confirm="handleConfirmDelete"
|
|
@cancel="handleCancelConfirmation"
|
|
>
|
|
<template #default="{ record }">
|
|
<div class="text-sm">
|
|
<p>
|
|
<strong>ID:</strong>
|
|
{{ record?.id }}
|
|
</p>
|
|
<p v-if="record?.firstName">
|
|
<strong>Nama:</strong>
|
|
{{ record.firstName }}
|
|
</p>
|
|
<p v-if="record?.code">
|
|
<strong>Kode:</strong>
|
|
{{ record.cellphone }}
|
|
</p>
|
|
</div>
|
|
</template>
|
|
</RecordConfirmation>
|
|
</template>
|