6ad99d45f2
* fix(style): formatting inconsistencies across codebase - Remove trailing semicolons from TypeScript imports - Fix Vue template indentation and line breaks - Standardize component attribute formatting - Remove unnecessary empty lines - Reorder import statements for consistency * chore: update import path and add editorconfig Update SidebarNavLink import path to match new component structure and add standard editorconfig for consistent code formatting
122 lines
2.8 KiB
Vue
122 lines
2.8 KiB
Vue
<script setup lang="ts">
|
|
import type { DataTableLoader } from '~/components/pub/base/data-table/type'
|
|
import type { Summary } from '~/components/pub/base/summary-card/type'
|
|
import type { HeaderPrep, RefSearchNav } from '~/components/pub/custom-ui/data/types'
|
|
import { Calendar, Hospital, UserCheck, UsersRound } from 'lucide-vue-next'
|
|
import SummaryCard from '~/components/pub/base/summary-card/summary-card.vue'
|
|
import Header from '~/components/pub/custom-ui/nav-header/prep.vue'
|
|
|
|
const data = ref([])
|
|
|
|
const refSearchNav: RefSearchNav = {
|
|
onClick: () => {
|
|
// open filter modal
|
|
},
|
|
onInput: (_val: string) => {
|
|
// filter patient list
|
|
},
|
|
onClear: () => {
|
|
// clear url param
|
|
},
|
|
}
|
|
|
|
// Loading state management
|
|
const isLoading = reactive<DataTableLoader>({
|
|
summary: false,
|
|
isTableLoading: false,
|
|
})
|
|
|
|
const recId = ref<number>(0)
|
|
const recAction = ref<string>('')
|
|
const recItem = ref<any>(null)
|
|
|
|
const hreaderPrep: HeaderPrep = {
|
|
title: 'Pasien',
|
|
icon: 'i-lucide-users',
|
|
addNav: {
|
|
label: 'Tambah',
|
|
onClick: () => navigateTo('/patient/add'),
|
|
},
|
|
}
|
|
|
|
// Initial/default data structure
|
|
const summaryData: 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',
|
|
},
|
|
]
|
|
|
|
// API call function
|
|
async function getPatientSummary() {
|
|
try {
|
|
isLoading.summary = true
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
} catch (error) {
|
|
console.error('Error fetching patient summary:', error)
|
|
// Keep default/existing data on error
|
|
} finally {
|
|
isLoading.summary = false
|
|
}
|
|
}
|
|
|
|
async function getPatientList() {
|
|
isLoading.isTableLoading = true
|
|
const resp = await xfetch('/api/v1/patient')
|
|
if (resp.success) {
|
|
data.value = (resp.body as Record<string, any>).data
|
|
}
|
|
isLoading.isTableLoading = false
|
|
}
|
|
|
|
onMounted(() => {
|
|
getPatientSummary()
|
|
getPatientList()
|
|
})
|
|
|
|
provide('rec_id', recId)
|
|
provide('rec_action', recAction)
|
|
provide('rec_item', recItem)
|
|
provide('table_data_loader', isLoading)
|
|
</script>
|
|
|
|
<template>
|
|
<Header :prep="{ ...hreaderPrep }" :ref-search-nav="refSearchNav" />
|
|
<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="isLoading.summary">
|
|
<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>
|
|
<AppPatientList :data="data" />
|
|
</div>
|
|
</template>
|