252 lines
9.4 KiB
Vue
252 lines
9.4 KiB
Vue
<template>
|
|
<v-container fluid class="pa-0">
|
|
|
|
<div class="pa-4 pb-0">
|
|
<v-toolbar flat color="#B3E5FC" class="floating-toolbar custom-padding"> <v-toolbar-title class="text-h6 font-weight-bold text-blue-darken-3">
|
|
<v-icon left class="mr-2" color="blue-darken-3">mdi-heart-pulse</v-icon>
|
|
Monitoring Pasien
|
|
</v-toolbar-title>
|
|
<v-spacer></v-spacer>
|
|
<v-chip color="blue-darken-1" text-color="white" class="font-weight-medium admin-chip"> <v-icon start>mdi-account-tie</v-icon>
|
|
Admin
|
|
</v-chip>
|
|
</v-toolbar>
|
|
</div>
|
|
|
|
<v-container class="mt-6">
|
|
|
|
<v-card flat class="mb-4">
|
|
<v-tabs
|
|
v-model="activeTab"
|
|
color="primary"
|
|
align-tabs="start"
|
|
show-arrows
|
|
class="mb-4"
|
|
>
|
|
<v-tab value="all" @click="filterByStatus('all')">
|
|
Semua Pasien <v-badge color="grey-lighten-1" :content="totalCount" inline class="ml-2"></v-badge>
|
|
</v-tab>
|
|
<v-tab value="Menunggu Poli" @click="filterByStatus('Menunggu Poli')">
|
|
Menunggu Poli <v-badge color="orange-lighten-1" :content="waitingCount" inline class="ml-2"></v-badge>
|
|
</v-tab>
|
|
<v-tab value="Diperiksa Dokter" @click="filterByStatus('Diperiksa Dokter')">
|
|
Diperiksa Dokter <v-badge color="green-lighten-1" :content="examiningCount" inline class="ml-2"></v-badge>
|
|
</v-tab>
|
|
<v-tab value="Selesai Pelayanan" @click="filterByStatus('Selesai Pelayanan')">
|
|
Selesai Pelayanan <v-badge color="blue-lighten-1" :content="doneCount" inline class="ml-2"></v-badge>
|
|
</v-tab>
|
|
</v-tabs>
|
|
</v-card>
|
|
|
|
<v-card class="mb-6 pa-4 elevation-1">
|
|
<v-row class="align-center">
|
|
<v-col cols="12" sm="3">
|
|
<v-text-field label="No. RM" v-model="filters.rm_number" variant="outlined" density="compact" hide-details></v-text-field>
|
|
</v-col>
|
|
<!-- <v-col cols="12" sm="3">
|
|
<v-text-field label="No. Kode QR" v-model="filters.qr_code" variant="outlined" density="compact" hide-details></v-text-field>
|
|
</v-col> -->
|
|
<v-col cols="12" sm="2">
|
|
<v-text-field label="No. Antrean" v-model="filters.queue_number" variant="outlined" density="compact" hide-details type="number"></v-text-field>
|
|
</v-col>
|
|
<v-col cols="12" sm="2">
|
|
<v-select label="Layanan" v-model="filters.service" :items="['Klinik Jiwa', 'Radiologi', 'Fisioterapi', 'Klinik Umum']" variant="outlined" density="compact" hide-details clearable></v-select>
|
|
</v-col>
|
|
<v-col cols="12" sm="2">
|
|
<v-btn color="blue-lighten-1" block height="40" @click="applyFilters">Cari</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card>
|
|
|
|
<v-data-table
|
|
:headers="headers"
|
|
:items="paginatedPatients"
|
|
:items-per-page="10"
|
|
class="elevation-1"
|
|
:search="currentSearchTerm"
|
|
>
|
|
<template v-slot:item.status="{ item }">
|
|
<v-chip :color="getStatusColor(item.status)" size="small" class="font-weight-medium" label>
|
|
{{ item.status }}
|
|
</v-chip>
|
|
</template>
|
|
<template v-slot:item.aksi="{ item }">
|
|
<v-btn size="small" color="orange-darken-1" dark @click="viewPatient(item.id)">
|
|
Lihat
|
|
</v-btn>
|
|
</template>
|
|
<template v-slot:no-data>
|
|
<v-alert :value="true" color="info" icon="mdi-information-outline">
|
|
Tidak ada data pasien untuk ditampilkan.
|
|
</v-alert>
|
|
</template>
|
|
</v-data-table>
|
|
</v-container>
|
|
</v-container>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
|
|
definePageMeta({
|
|
middleware:['auth']
|
|
})
|
|
|
|
const router = useRouter();
|
|
|
|
// === STATE MANAGEMENT ===
|
|
const activeTab = ref('all');
|
|
|
|
// Filter Model: Menampung input dari form pencarian
|
|
const filters = ref({
|
|
rm_number: '',
|
|
// qr_code: '',
|
|
queue_number: '', // Filter baru
|
|
service: null, // Filter Layanan
|
|
});
|
|
|
|
// State untuk menyimpan hasil filtering
|
|
const filteredPatientList = ref([]);
|
|
const currentSearchTerm = ref('');
|
|
|
|
// Table Headers
|
|
const headers = [
|
|
{ title: 'No. RM', key: 'rm_number' },
|
|
{ title: 'Nama', key: 'name' },
|
|
{ title: 'No. Antrean', key: 'queue_number' },
|
|
{ title: 'Layanan', key: 'service' },
|
|
{ title: 'Status', key: 'status' },
|
|
{ title: 'Aksi', key: 'aksi', sortable: false },
|
|
];
|
|
|
|
// Mock Patient Data
|
|
const ALL_PATIENTS_DATA = [
|
|
{ id: '11111111', rm_number: '11111111', name: 'Ragil Bayu N.', queue_number: 1, service: 'Klinik Jiwa', status: 'Diperiksa Dokter', qr_code: 'QR123' },
|
|
{ id: '22222222', rm_number: '22222222', name: 'Siti Aisyah', queue_number: 5, service: 'Radiologi', status: 'Menunggu Poli', qr_code: 'QR456' },
|
|
{ id: '33333333', rm_number: '33333333', name: 'Ahmad Dani', queue_number: 2, service: 'Klinik Jiwa', status: 'Selesai Pelayanan', qr_code: 'QR789' },
|
|
{ id: '44444444', rm_number: '44444444', name: 'Dewi Sartika', queue_number: 6, service: 'Fisioterapi', status: 'Menunggu Poli', qr_code: 'QR101' },
|
|
{ id: '55555555', rm_number: '55555555', name: 'Joko Susilo', queue_number: 3, service: 'Klinik Umum', status: 'Diperiksa Dokter', qr_code: 'QR112' },
|
|
{ id: '66666666', rm_number: '66666666', name: 'Budi Santoso', queue_number: 4, service: 'Radiologi', status: 'Menunggu Poli', qr_code: 'QR131' },
|
|
];
|
|
|
|
// Initialize the filtered list with all data
|
|
filteredPatientList.value = [...ALL_PATIENTS_DATA];
|
|
|
|
// === FILTERING LOGIC ===
|
|
|
|
// 1. Logic Pencarian Gabungan saat tombol "Cari" ditekan
|
|
const applyFilters = () => {
|
|
// 1. Filter berdasarkan teks di semua kolom yang relevan (RM, QR, Antrean, Nama)
|
|
let textSearch = '';
|
|
|
|
// Gabungkan semua nilai filter menjadi satu string pencarian
|
|
if (filters.value.rm_number) textSearch += filters.value.rm_number.toLowerCase() + ' ';
|
|
if (filters.value.qr_code) textSearch += filters.value.qr_code.toLowerCase() + ' ';
|
|
if (filters.value.queue_number) textSearch += filters.value.queue_number.toString().toLowerCase() + ' ';
|
|
|
|
// Vuetify v-data-table memiliki fitur pencarian bawaan (di properti `search`).
|
|
// Kita gunakan fitur ini untuk pencarian berbasis teks (No. RM, QR, Antrean).
|
|
currentSearchTerm.value = textSearch.trim();
|
|
|
|
// 2. Filter berdasarkan Layanan (Service), yang tidak didukung oleh `v-data-table` search
|
|
let results = ALL_PATIENTS_DATA;
|
|
|
|
if (filters.value.service) {
|
|
results = results.filter(p => p.service === filters.value.service);
|
|
}
|
|
|
|
// Terapkan hasil filter Layanan
|
|
filteredPatientList.value = results;
|
|
|
|
// Reset status tab (penting agar hasil pencarian tampil terlepas dari tab yang aktif)
|
|
activeTab.value = 'all';
|
|
};
|
|
|
|
// 2. Logic Filter Status (saat tab ditekan)
|
|
const filterByStatus = (status) => {
|
|
// Pastikan status tab diperbarui
|
|
activeTab.value = status;
|
|
|
|
// Clear the text search term when switching tabs
|
|
currentSearchTerm.value = '';
|
|
|
|
if (status === 'all') {
|
|
filteredPatientList.value = ALL_PATIENTS_DATA;
|
|
} else {
|
|
filteredPatientList.value = ALL_PATIENTS_DATA.filter(p => p.status === status);
|
|
}
|
|
|
|
// Clear form filters (opsional, tetapi membuat UX lebih jelas)
|
|
filters.value.rm_number = '';
|
|
filters.value.qr_code = '';
|
|
filters.value.queue_number = '';
|
|
filters.value.service = null;
|
|
};
|
|
|
|
// === COMPUTED PROPERTIES ===
|
|
|
|
// Hitungan untuk Badge Tab
|
|
const listForCounts = computed(() => {
|
|
// Gunakan ALL_PATIENTS_DATA untuk hitungan badge agar hitungan selalu stabil
|
|
return ALL_PATIENTS_DATA;
|
|
});
|
|
|
|
const totalCount = computed(() => listForCounts.value.length);
|
|
const waitingCount = computed(() => listForCounts.value.filter(p => p.status === 'Menunggu Poli').length);
|
|
const examiningCount = computed(() => listForCounts.value.filter(p => p.status === 'Diperiksa Dokter').length);
|
|
const doneCount = computed(() => listForCounts.value.filter(p => p.status === 'Selesai Pelayanan').length);
|
|
|
|
// Data yang ditampilkan di tabel adalah data yang sudah difilter
|
|
const paginatedPatients = computed(() => {
|
|
// Jika ada filter status yang aktif (setelah menekan tab)
|
|
if (activeTab.value !== 'all') {
|
|
return filteredPatientList.value.filter(p => p.status === activeTab.value);
|
|
}
|
|
|
|
// Jika tidak ada filter status yang aktif, tampilkan hasil dari applyFilters()
|
|
return filteredPatientList.value;
|
|
});
|
|
|
|
// === UTILITIES & NAVIGATION ===
|
|
|
|
// Utility function to set color based on status
|
|
const getStatusColor = (status) => {
|
|
if (status === 'Diperiksa Dokter') return 'green-darken-1';
|
|
if (status === 'Menunggu Poli') return 'orange-darken-1';
|
|
if (status === 'Selesai Pelayanan') return 'blue-darken-2';
|
|
return 'grey';
|
|
};
|
|
|
|
// Function to navigate to the detailed patient information page
|
|
const viewPatient = (patientId) => {
|
|
router.push(`/MonitoringPasien/Pasien/${patientId}`);
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.floating-toolbar {
|
|
border-radius: 9999px;
|
|
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.15);
|
|
height: 64px !important;
|
|
}
|
|
|
|
/* Jarak PENTING: Tambahkan padding horizontal ke toolbar */
|
|
.floating-toolbar.custom-padding {
|
|
padding: 0 16px; /* Memberi jarak 16px di kiri dan kanan */
|
|
}
|
|
|
|
/* Penyesuaian agar teks dan ikon kontras dengan soft blue background */
|
|
.v-toolbar-title {
|
|
margin-left: 0 !important; /* Reset default margin */
|
|
}
|
|
|
|
/* Pastikan chip Admin tidak mepet */
|
|
.admin-chip {
|
|
margin-right: 0 !important; /* Reset default margin */
|
|
}
|
|
|
|
.v-tab.v-tab--selected {
|
|
font-weight: 600;
|
|
}
|
|
</style> |