235 lines
6.4 KiB
Vue
235 lines
6.4 KiB
Vue
<script setup lang="ts">
|
|
import type { DataTableHeaders } from '~/plugins/vuetify'
|
|
|
|
definePageMeta({
|
|
icon: 'mdi-table',
|
|
title: 'Data Praktisi',
|
|
drawerIndex: 3,
|
|
})
|
|
|
|
const search = ref('')
|
|
const dialogDelete = useTemplateRef('dialogDelete')
|
|
function showDialogDelete(name: string) {
|
|
dialogDelete.value
|
|
?.open('Are you sure you want to delete this dessert?')
|
|
.then(async (confirmed: boolean) => {
|
|
if (confirmed) {
|
|
try {
|
|
const index = desserts.value!.findIndex((v) => v.name === name)
|
|
desserts.value!.splice(index, 1)
|
|
Notify.success('Deleted')
|
|
} catch (e) {
|
|
Notify.error(e)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
const practitioners = ref([])
|
|
const loading = ref(true)
|
|
const error = ref(null)
|
|
const snackbar = ref({
|
|
show: false,
|
|
text: '',
|
|
color: 'info',
|
|
})
|
|
|
|
// Mengambil data dari API
|
|
const fetchPractitioners = async () => {
|
|
try {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
const response = await fetch('/api/practitioner')
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Error: ${response.status} - ${response.statusText}`)
|
|
}
|
|
|
|
const data = await response.json()
|
|
console.log(data)
|
|
practitioners.value = data
|
|
} catch (err) {
|
|
console.error('Failed to fetch practitioners:', err)
|
|
error.value = `Gagal memuat data praktisi: ${err.message}`
|
|
showSnackbar('Gagal memuat data praktisi', 'error')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// Mendapatkan nomor telepon praktisi
|
|
const getPractitionerPhone = (practitioner) => {
|
|
if (!practitioner.telecom || practitioner.telecom.length === 0) {
|
|
return 'Tidak ada data'
|
|
}
|
|
|
|
const phone = practitioner.telecom.find((t) => t.system === 'phone')
|
|
return phone ? phone.value : 'Tidak ada data'
|
|
}
|
|
|
|
// Mendapatkan email praktisi
|
|
const getPractitionerEmail = (practitioner) => {
|
|
if (!practitioner.telecom || practitioner.telecom.length === 0) {
|
|
return 'Tidak ada data'
|
|
}
|
|
|
|
const email = practitioner.telecom.find((t) => t.system === 'email')
|
|
return email ? email.value : 'Tidak ada data'
|
|
}
|
|
|
|
// Menampilkan detail praktisi
|
|
const showDetail = (practitioner) => {
|
|
console.log('Menampilkan detail praktisi:', getPractitionerName(practitioner))
|
|
showSnackbar(`Detail ${getPractitionerName(practitioner)}`, 'primary')
|
|
// Di sini bisa ditambahkan navigasi ke halaman detail atau membuka dialog
|
|
}
|
|
|
|
// Menampilkan snackbar
|
|
const showSnackbar = (text, color = 'success') => {
|
|
snackbar.value = {
|
|
show: true,
|
|
text,
|
|
color,
|
|
}
|
|
}
|
|
|
|
// Panggil API saat komponen dimuat
|
|
onMounted(() => {
|
|
fetchPractitioners()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<v-container fluid>
|
|
<v-row>
|
|
<v-col>
|
|
<v-card>
|
|
<client-only>
|
|
<teleport to="#app-bar">
|
|
<v-text-field
|
|
v-model="search"
|
|
prepend-inner-icon="mdi-magnify"
|
|
label="Search"
|
|
single-line
|
|
hide-details
|
|
density="compact"
|
|
class="mr-2"
|
|
rounded="xl"
|
|
flat
|
|
variant="solo"
|
|
style="width: 250px"
|
|
/>
|
|
</teleport>
|
|
</client-only>
|
|
<v-container>
|
|
<h1 class="text-h4 mb-4">Daftar Praktisi</h1>
|
|
|
|
<v-alert v-if="error" type="error" class="mb-4">
|
|
{{ error }}
|
|
</v-alert>
|
|
|
|
<v-progress-circular
|
|
v-if="loading"
|
|
indeterminate
|
|
color="primary"
|
|
size="64"
|
|
class="my-8 mx-auto d-block"
|
|
></v-progress-circular>
|
|
|
|
<v-row v-else>
|
|
<v-col
|
|
v-for="(practitioner, index) in practitioners"
|
|
:key="index"
|
|
cols="12"
|
|
sm="6"
|
|
md="4"
|
|
>
|
|
<v-card
|
|
class="mx-auto mb-4"
|
|
max-width="400"
|
|
elevation="3"
|
|
shaped
|
|
>
|
|
<v-card-title class="text-h5">
|
|
{{ joinName(practitioner.name, ['official', 'usual']) }}
|
|
</v-card-title>
|
|
|
|
<v-card-text>
|
|
<v-row align="center" class="mx-0">
|
|
<DivAvatar
|
|
size="80"
|
|
class="mr-3"
|
|
:gender="practitioner.gender"
|
|
/>
|
|
|
|
<div>
|
|
<DivIconText
|
|
icon="mdi-map-marker"
|
|
:text="practitioner.birthPlace || 'Tidak ada data'"
|
|
/>
|
|
<DivIconText
|
|
icon="mdi-calendar"
|
|
:text="formatDate(practitioner.birthDate, 'full')"
|
|
/>
|
|
<DivIconText
|
|
icon="mdi-phone"
|
|
:text="
|
|
getContactPoints(
|
|
practitioner.telecom,
|
|
'phone',
|
|
).join(',')
|
|
"
|
|
/>
|
|
<DivIconText
|
|
icon="mdi-email"
|
|
:text="
|
|
getContactPoints(
|
|
practitioner.telecom,
|
|
'email',
|
|
).join(',')
|
|
"
|
|
/>
|
|
</div>
|
|
</v-row>
|
|
</v-card-text>
|
|
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
color="primary"
|
|
text
|
|
@click="showDetail(practitioner)"
|
|
>
|
|
Detail
|
|
<v-icon small class="ml-1">mdi-arrow-right</v-icon>
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<v-snackbar
|
|
v-model="snackbar.show"
|
|
:color="snackbar.color"
|
|
:timeout="3000"
|
|
>
|
|
{{ snackbar.text }}
|
|
</v-snackbar>
|
|
</v-container>
|
|
<DialogConfirm ref="dialogDelete" />
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
</v-container>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.v-card {
|
|
transition: transform 0.3s;
|
|
}
|
|
.v-card:hover {
|
|
transform: translateY(-5px);
|
|
}
|
|
</style>
|