feat detail akun

This commit is contained in:
2026-06-19 11:10:51 +07:00
parent 8b9c4725de
commit 400bfcdcaf
3 changed files with 472 additions and 8 deletions
+423
View File
@@ -0,0 +1,423 @@
<script setup>
import PageHeader from '@/components/common/PageHeader.vue';
const currentDate = computed(() => {
const now = new Date();
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
const months = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'];
return `${days[now.getDay()]}, ${now.getDate()} ${months[now.getMonth()]} ${now.getFullYear()}`;
});
const snackbar = ref({
show: false,
message: '',
color: 'success'
});
// Profile data
const profileData = reactive({
name: 'Budi Santoso',
registrationNumber: 'RM-2023-8812',
nim: '3573230897851332',
phone: '+62 812-3456-7890',
verified: true
});
// Family members data
const familyMembers = reactive([
{
id: 1,
name: 'Andi Pratama',
status: 'PENDING'
},
{
id: 2,
name: 'Siti Aminah',
status: 'AKTIF'
},
{
id: 3,
name: 'Maimunah',
status: 'AKTIF'
}
]);
// Activity history
const activityHistory = reactive([
{
id: 1,
title: 'Member Added',
date: '10 Okt 2023, 14:20',
icon: 'mdi-account-plus',
color: 'primary'
},
{
id: 2,
title: 'Member Data Updated',
date: '08 Okt 2023, 09:15',
icon: 'mdi-pencil',
color: 'success'
},
{
id: 3,
title: 'Member Removed',
date: '07 Okt 2023, 16:45',
icon: 'mdi-account-remove',
color: 'error'
}
]);
// Modal state
const isModalOpen = ref(false);
const selectedMember = ref(null);
const modalData = reactive({
namaLengkap: '',
tanggalLahir: '',
nik: '',
jenisKelamin: '',
hubungan: '',
nomorTelepon: '',
alamat: ''
});
const genderOptions = ['Laki-laki', 'Perempuan'];
const relationshipOptions = ['Orang tua', 'Suami/Istri', 'Anak', 'Saudara', 'Lainnya'];
const openModal = (member) => {
selectedMember.value = member;
// Reset form
modalData.namaLengkap = member.name;
modalData.tanggalLahir = '';
modalData.nik = '';
modalData.jenisKelamin = '';
modalData.hubungan = '';
modalData.nomorTelepon = '';
modalData.alamat = '';
isModalOpen.value = true;
};
const closeModal = () => {
isModalOpen.value = false;
selectedMember.value = null;
};
const handleApprove = () => {
snackbar.value = {
show: true,
message: 'Pengajuan anggota baru disetujui.',
color: 'success'
};
closeModal();
};
const handleReject = () => {
snackbar.value = {
show: true,
message: 'Pengajuan anggota baru ditolak.',
color: 'error'
};
closeModal();
};
</script>
<template>
<div>
<PageHeader
icon="mdi-shield-check"
title="Detail Akun"
:subtitle="currentDate"
:show-add-button="false"
theme="primary"
/>
<v-container class="py-6">
<!-- Profile Card -->
<v-row class="mb-6">
<v-col cols="12">
<v-card class="pa-8" color="white" elevation="2" rounded="lg">
<v-row class="align-center" no-gutters>
<!-- Avatar -->
<v-col cols="auto" class="mr-6">
<v-card height="100" width="100" class="bg-lightPrimary d-flex align-center justify-center" elevation="0" rounded="lg">
<v-icon size="50" color="primary">mdi-account-outline</v-icon>
</v-card>
</v-col>
<!-- Nama Lengkap & NIK -->
<v-col cols="3">
<div class="text-caption text-uppercase text-grey font-weight-bold">Nama Lengkap</div>
<div class="text-h6 font-weight-bold text-primary-700 mb-4">{{ profileData.name }}</div>
<div class="text-caption text-uppercase text-grey font-weight-bold">N.I.K</div>
<div class="text-body2">{{ profileData.nim }}</div>
</v-col>
<!-- Nomor RM & Nomor Telepon -->
<v-col cols="3">
<div class="text-caption text-uppercase text-grey font-weight-bold">Nomor RM</div>
<div class="text-h6 font-weight-bold mb-4">{{ profileData.registrationNumber }}</div>
<div class="text-caption text-uppercase text-grey font-weight-bold">Nomor Telepon</div>
<div class="text-body2">{{ profileData.phone }}</div>
</v-col>
<!-- Buttons -->
<v-col cols="max" class="text-right">
<v-chip
v-if="profileData.verified"
color="success"
size="small"
class="mb-3"
>
<v-icon start size="small">mdi-check-circle</v-icon>
Terverifikasi
</v-chip>
<div>
<v-btn
color="primary"
variant="outlined"
size="small"
>
<v-icon start>mdi-pencil</v-icon>
Edit Profil
</v-btn>
</div>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
<!-- Family Members Section -->
<v-row class="mb-6">
<v-col cols="12">
<h3 class="text-h6 font-weight-bold">Anggota Keluarga Terhubung</h3>
<p class="text-body-2 text-muted mb-4">Daftar anggota keluarga yang berada dalam satu kartu keluarga</p>
<v-card class="pa-2">
<v-table class="elevation-0">
<thead>
<tr>
<th class="text-left text-uppercase">Nama Anggota</th>
<th class="text-center text-uppercase">Status</th>
<th class="text-center text-uppercase">Aksi</th>
</tr>
</thead>
<tbody>
<tr v-for="member in familyMembers" :key="member.id">
<td class="text-body-2 font-weight-bold ">{{ member.name }}</td>
<td class="text-center">
<v-chip
:color="member.status === 'PENDING' ? 'secondary' : 'success'"
size="small"
>
<v-icon start size="small">{{
member.status === 'PENDING' ? 'mdi-clock-outline' : 'mdi-check-decagram'
}}</v-icon>
{{ member.status }}
</v-chip>
</td>
<td class="text-center">
<div class="d-flex justify-center gap-2">
<v-btn
v-if="member.status === 'PENDING'"
size="small"
color="primary"
@click="openModal(member)"
>
<v-icon start>mdi-clipboard-check-outline</v-icon>
Proses
</v-btn>
<div v-else>
<v-btn
size="small"
color="error"
variant="outlined"
class="mr-2"
>
<v-icon start>mdi-delete</v-icon>
Hapus
</v-btn>
<v-btn
size="small"
color="primary"
variant="outlined"
>
<v-icon start>mdi-pencil</v-icon>
Edit
</v-btn>
</div>
</div>
</td>
</tr>
</tbody>
</v-table>
</v-card>
</v-col>
</v-row>
<!-- Activity History Section -->
<v-row>
<v-col cols="12">
<div class="d-flex align-center mb-6">
<v-icon class="mr-2" color="primary">mdi-history</v-icon>
<h3 class="text-h6 font-weight-bold">Riwayat Aktivitas</h3>
</div>
<v-card class="pa-6">
<v-timeline
density="compact"
side="end">
<v-timeline-item
v-for="activity in activityHistory"
:key="activity.id"
:dot-color="activity.color"
:icon="activity.icon"
fill-dot
>
<div class="text-subtitle-1 font-weight-bold">{{ activity.title }}</div>
<div class="text-caption text-muted">{{ activity.date }}</div>
</v-timeline-item>
</v-timeline>
<div class="text-center mt-6">
<v-btn color="primary" variant="text">
Lihat Semua Riwayat
</v-btn>
</div>
</v-card>
</v-col>
</v-row>
<!-- Verification Modal -->
<v-dialog v-model="isModalOpen" max-width="700px" persistent scrollable>
<v-card class="pa-0">
<!-- Modal Header -->
<v-card-title class="bg-primary text-white pa-6">
<div class="d-flex justify-space-between align-center w-100">
<h2 class="text-h5 font-weight-bold">Verifikasi Pengajuan Anggota Keluarga</h2>
<v-btn icon="mdi-close" variant="text" @click="closeModal" class="text-white"></v-btn>
</div>
</v-card-title>
<!-- Form Content - Scrollable -->
<v-card-text class="pa-6 bg-light">
<!-- Informasi Data Diri Section - Card -->
<v-card class="mb-6 pa-6" variant="flat">
<div class="d-flex align-center mb-4">
<h3 class="text-subtitle-1 font-weight-bold text-primary">INFORMASI DATA DIRI</h3>
</div>
<v-form>
<v-row>
<v-col cols="12">
<v-text-field
v-model="modalData.namaLengkap"
label="Nama Lengkap"
variant="outlined"
density="compact"
></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="modalData.tanggalLahir"
label="Tanggal Lahir"
type="date"
variant="outlined"
density="compact"
></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-select
v-model="modalData.jenisKelamin"
:items="genderOptions"
label="Jenis Kelamin"
variant="outlined"
density="compact"
></v-select>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="modalData.nik"
label="NIK"
variant="outlined"
density="compact"
></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-select
v-model="modalData.hubungan"
:items="relationshipOptions"
label="Hubungan"
variant="outlined"
density="compact"
></v-select>
</v-col>
</v-row>
</v-form>
</v-card>
<!-- Kontak & Alamat Section - Card -->
<v-card class="mb-6 pa-6" variant="flat">
<div class="d-flex align-center mb-4">
<h3 class="text-subtitle-1 font-weight-bold text-primary">KONTAK & ALAMAT</h3>
</div>
<v-form>
<v-row>
<v-col cols="12">
<v-text-field
v-model="modalData.nomorTelepon"
label="Nomor Telepon"
variant="outlined"
density="compact"
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="modalData.alamat"
label="Alamat"
variant="outlined"
density="compact"
></v-text-field>
</v-col>
</v-row>
</v-form>
</v-card>
</v-card-text>
<!-- Modal Actions -->
<v-divider></v-divider>
<v-card-actions class="pa-6 justify-end">
<v-btn
color="error"
variant="outlined"
@click="handleReject"
>
<v-icon start>mdi-close</v-icon>
Tolak
</v-btn>
<v-btn
color="primary"
variant="flat"
@click="handleApprove"
>
<v-icon start>mdi-check</v-icon>
Setujui
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Snackbar -->
<v-snackbar v-model="snackbar.show" :color="snackbar.color" :timeout="3000">
<span class="body-3">{{ snackbar.message }}</span>
<template #actions>
<v-btn variant="text" size="small" @click="snackbar.show = false">Tutup</v-btn>
</template>
</v-snackbar>
</v-container>
</div>
</template>
+35 -7
View File
@@ -96,11 +96,28 @@
size="small"
@click="openDaftarModal(item)"
variant="flat"
class="btn-verify"
class="btn-verify mt-2"
>
<v-icon size="16" left>mdi-qrcode-scan</v-icon>
Verifikasi
</v-btn>
<v-btn
class="my-2"
color="primary"
size="small"
variant="outlined"
@click="router.push(`/VerifikasiAkun/DetailAkun`)"
>
<v-icon size="16" left>mdi-account-cog-outline</v-icon>
Kelola Akun
</v-btn>
</template>
<template v-slot:item.pending="{ item }">
<v-chip size="small" class="chip-orange">
{{ item.pending || 2 }}
</v-chip>
</template>
</v-data-table>
</v-card-text>
@@ -137,6 +154,7 @@
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import { useDisplay } from 'vuetify';
import { storeToRefs } from 'pinia';
import { useRouter } from 'vue-router';
import QrcodeVue from 'qrcode.vue';
import PageHeader from '@/components/common/PageHeader.vue';
import QRVerificationDialog from '@/components/verification/QRVerificationDialog.vue';
@@ -149,6 +167,7 @@ definePageMeta({
const display = useDisplay();
const verificationStore = useVerificationStore();
const { patients, loading, error } = storeToRefs(verificationStore);
const router = useRouter();
// Load initial patients on mount
onMounted(() => {
@@ -160,9 +179,10 @@ const headers = ref([
{ title: 'No', value: 'no', sortable: false, width: '60px', align: 'center' },
{ title: 'Nama Pasien', value: 'nama', sortable: true, align: 'center' },
{ title: 'No. RM', value: 'rm', sortable: true, align: 'center' },
{ title: 'Alamat', value: 'alamat', sortable: false, align: 'center' },
{ title: 'Alamat', value: 'alamat', sortable: false, align: 'left' },
{ title: 'No. Telepon', value: 'telepon', sortable: false, align: 'center' },
{ title: 'Status', value: 'status', sortable: true, width: '180px', align: 'center' },
{ title: 'Pending', value: 'pending', sortable: false, width: '120px', align: 'center' },
{ title: 'Actions', value: 'actions', sortable: false, width: '200px', align: 'center' },
]);
@@ -396,10 +416,10 @@ $font-weight-semibold: 600;
color: $neutral-800;
font-size: 14px;
line-height: 20px;
text-align: center !important;
// text-align: center !important;
.v-data-table-header__content {
justify-content: center !important;
// justify-content: center !important;
width: 100%;
}
}
@@ -408,20 +428,28 @@ $font-weight-semibold: 600;
font-size: 14px;
line-height: 20px;
color: $neutral-900;
text-align: center !important;
// text-align: center !important;
}
// Ensure the cell content itself is centered if it contains flex/divs
:deep(.v-data-table__td > *) {
display: inline-flex;
justify-content: center;
align-items: center;
// justify-content: center;
// align-items: center;
}
}
// ============================================
// CHIPS
// ============================================
.chip-orange {
background-color: #FE6B22 !important;
color: $neutral-100 !important;
font-weight: $font-weight-medium;
font-size: 12px;
line-height: 16px;
}
.chip-success {
background-color: #009262 !important;
color: $neutral-100 !important;
+14 -1
View File
@@ -10,7 +10,20 @@ import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
export default defineNuxtPlugin((app) => {
const vuetify = createVuetify({
ssr: true,
blueprint: md2
blueprint: md2,
theme: {
themes: {
light: {
colors: {
primary: '#3A5FBC',
lightPrimary: '#DBE1FF',
secondary: '#E65A0D',
error: '#D82719',
success: '#008D65',
}
}
}
}
})
app.vueApp.use(vuetify)
})