new components
This commit is contained in:
189
components/TabelData.vue
Normal file
189
components/TabelData.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<v-card-text>
|
||||
<v-toolbar flat color="transparent" dense>
|
||||
<v-toolbar-title class="text-subtitle-1 font-weight-bold" :class="{ 'red--text': title.includes('TERLAMBAT') }">
|
||||
{{ title }}
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<div class="d-flex align-center">
|
||||
<span>Show</span>
|
||||
<v-select
|
||||
:items="[10, 25, 50, 100]"
|
||||
v-model="itemsPerPage"
|
||||
label="entries"
|
||||
dense
|
||||
single-line
|
||||
hide-details
|
||||
class="shrink mx-2"
|
||||
style="width: 80px"
|
||||
></v-select>
|
||||
<span>entries</span>
|
||||
</div>
|
||||
<div v-if="showSearch" class="d-flex align-center ml-4">
|
||||
<span class="mr-2">Search:</span>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
label="Search"
|
||||
hide-details
|
||||
density="compact"
|
||||
style="min-width: 200px"
|
||||
></v-text-field>
|
||||
</div>
|
||||
</v-toolbar>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="paginatedItems"
|
||||
:search="search"
|
||||
no-data-text="No data available in table"
|
||||
hide-default-footer
|
||||
class="elevation-1"
|
||||
>
|
||||
<template v-slot:item.aksi="{ item }">
|
||||
<slot name="actions" :item="item">
|
||||
</slot>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<div class="d-flex justify-space-between align-center pt-4">
|
||||
<div class="text-body-2 text-grey-darken-1">
|
||||
Showing {{ currentPageStart }} to {{ currentPageEnd }} of {{ totalEntries }} entries
|
||||
</div>
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
:disabled="currentPage === 1"
|
||||
@click="previousPage"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-body-2"
|
||||
>
|
||||
Previous
|
||||
</v-btn>
|
||||
<template v-for="page in visiblePages" :key="page">
|
||||
<v-btn
|
||||
v-if="page !== '...'"
|
||||
:color="page === currentPage ? 'primary' : ''"
|
||||
:variant="page === currentPage ? 'flat' : 'text'"
|
||||
@click="goToPage(page)"
|
||||
size="small"
|
||||
class="mx-1"
|
||||
min-width="40"
|
||||
>
|
||||
{{ page }}
|
||||
</v-btn>
|
||||
<span v-else class="mx-1">...</span>
|
||||
</template>
|
||||
<v-btn
|
||||
:disabled="currentPage === totalPages"
|
||||
@click="nextPage"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-body-2"
|
||||
>
|
||||
Next
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { defineProps } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
headers: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
showSearch: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
});
|
||||
|
||||
const search = ref("");
|
||||
const itemsPerPage = ref(10);
|
||||
const currentPage = ref(1);
|
||||
|
||||
const totalEntries = computed(() => props.items.length);
|
||||
const totalPages = computed(() => Math.ceil(totalEntries.value / itemsPerPage.value));
|
||||
|
||||
const paginatedItems = computed(() => {
|
||||
const start = (currentPage.value - 1) * itemsPerPage.value;
|
||||
const end = start + itemsPerPage.value;
|
||||
return props.items.slice(start, end);
|
||||
});
|
||||
|
||||
const currentPageStart = computed(() => (currentPage.value - 1) * itemsPerPage.value + 1);
|
||||
const currentPageEnd = computed(() => Math.min(currentPage.value * itemsPerPage.value, totalEntries.value));
|
||||
|
||||
const visiblePages = computed(() => {
|
||||
const pages = [];
|
||||
const total = totalPages.value;
|
||||
const current = currentPage.value;
|
||||
|
||||
if (total <= 7) {
|
||||
for (let i = 1; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
if (current <= 4) {
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
pages.push("...");
|
||||
pages.push(total);
|
||||
} else if (current >= total - 3) {
|
||||
pages.push(1);
|
||||
pages.push("...");
|
||||
for (let i = total - 4; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
pages.push(1);
|
||||
pages.push("...");
|
||||
for (let i = current - 1; i <= current + 1; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
pages.push("...");
|
||||
pages.push(total);
|
||||
}
|
||||
}
|
||||
return pages;
|
||||
});
|
||||
|
||||
const previousPage = () => {
|
||||
if (currentPage.value > 1) {
|
||||
currentPage.value--;
|
||||
}
|
||||
};
|
||||
|
||||
const nextPage = () => {
|
||||
if (currentPage.value < totalPages.value) {
|
||||
currentPage.value++;
|
||||
}
|
||||
};
|
||||
|
||||
const goToPage = (page) => {
|
||||
currentPage.value = page;
|
||||
};
|
||||
|
||||
// Reset halaman ke 1 saat jumlah itemsPerPage berubah
|
||||
watch(itemsPerPage, () => {
|
||||
currentPage.value = 1;
|
||||
});
|
||||
|
||||
// Reset halaman ke 1 saat data items berubah (misalnya, setelah filter atau fetch data baru)
|
||||
watch(() => props.items, () => {
|
||||
currentPage.value = 1;
|
||||
});
|
||||
</script>
|
||||
@@ -152,7 +152,7 @@ const currentActiveMenu = computed(() => {
|
||||
|
||||
<style scoped>
|
||||
.v-list-item--active {
|
||||
background-color: var(--v-theme-primary);
|
||||
color: #fff !important;
|
||||
background-color: #ffffff;
|
||||
color: #000000 !important;
|
||||
}
|
||||
</style>
|
||||
318
pages/Loket-Admin.vue
Normal file
318
pages/Loket-Admin.vue
Normal file
@@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar>
|
||||
<v-container fluid class="pa-4">
|
||||
<v-row align="center" no-gutters class="fill-height">
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center">
|
||||
<div
|
||||
class="bg-grey-darken-4 px-3 py-1 mr-2"
|
||||
style="border-radius: 3px"
|
||||
>
|
||||
<span class="text-body-1 font-weight-bold text-white"
|
||||
>Total 2</span
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="bg-grey-darken-2 px-3 py-1"
|
||||
style="border-radius: 3px"
|
||||
>
|
||||
<span class="text-body-1 text-white">Max 150 Pasien</span>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center text-body-2">
|
||||
<v-icon size="small" class="mr-2">mdi-view-dashboard</v-icon>
|
||||
<span class="mr-6">Dashboard</span>
|
||||
<span>Loket 12 | Rabu, 13 Agustus 2025 - Pelayanan</span>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-app-bar>
|
||||
|
||||
<div class="bg-cyan" style="height: 3px" />
|
||||
|
||||
<v-main>
|
||||
<v-container fluid class="pa-6">
|
||||
<v-row class="mb-6">
|
||||
<v-col>
|
||||
<div class="text-h6 text-grey-darken-2 font-weight-medium">
|
||||
Loket 12 | Rabu, 13 Agustus 2025 - Pelayanan :
|
||||
</div>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
color="success"
|
||||
dark
|
||||
size="large"
|
||||
class="mr-4 px-8"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">1</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="info"
|
||||
dark
|
||||
size="large"
|
||||
class="mr-4 px-4"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">5</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="warning"
|
||||
dark
|
||||
size="large"
|
||||
class="mr-4 px-4"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">10</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="error"
|
||||
dark
|
||||
size="large"
|
||||
class="px-4"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">20</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row justify="center" class="pa-12">
|
||||
<v-col cols="12" md="10" lg="8">
|
||||
<v-card
|
||||
color="success"
|
||||
dark
|
||||
flat
|
||||
class="text-center"
|
||||
style="min-height: 160px; border-radius: 8px"
|
||||
>
|
||||
<v-card-text class="pa-8">
|
||||
<div
|
||||
class="text-h2 font-weight-bold mb-2"
|
||||
style="letter-spacing: 4px"
|
||||
>
|
||||
NEXT
|
||||
</div>
|
||||
<div class="text-h6 mb-4 font-weight-normal">
|
||||
Pasien - UM1004
|
||||
</div>
|
||||
<div
|
||||
class="text-body-1 font-weight-normal"
|
||||
style="opacity: 0.9"
|
||||
>
|
||||
Klik untuk memanggil pasien selanjutnya
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-card class="mb-4">
|
||||
<TabelData
|
||||
:headers="mainHeaders"
|
||||
:items="mainPatients"
|
||||
title="DATA PASIEN"
|
||||
>
|
||||
<template #actions="{ item }">
|
||||
<div class="d-flex ga-1">
|
||||
<v-btn size="small" color="success" variant="flat">Panggil</v-btn>
|
||||
<v-btn size="small" color="info" variant="flat">Cancel</v-btn>
|
||||
<v-btn size="small" color="primary" variant="flat">Selesai</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.jamPanggil="{ item }">
|
||||
<span :class="getRowClass(item)">{{ item.jamPanggil }}</span>
|
||||
</template>
|
||||
</TabelData>
|
||||
</v-card>
|
||||
|
||||
<v-card color="cyan" dark class="mb-4">
|
||||
<v-card-text class="text-center">
|
||||
<div class="text-h6">Total Quota Terpakai 5</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card class="mb-4">
|
||||
<TabelData
|
||||
:headers="lateHeaders"
|
||||
:items="latePatients"
|
||||
title="INFO PASIEN LAPOR TERLAMBAT"
|
||||
:show-search="false"
|
||||
>
|
||||
</TabelData>
|
||||
</v-card>
|
||||
|
||||
<v-card class="mb-4">
|
||||
<TabelData
|
||||
:headers="clinicHeaders"
|
||||
:items="clinicPatients"
|
||||
title="INFO PASIEN MASUK KLINIK"
|
||||
>
|
||||
</TabelData>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import TabelData from "../components/TabelData.vue";
|
||||
|
||||
// Define interfaces
|
||||
interface Patient {
|
||||
no: number;
|
||||
jamPanggil: string;
|
||||
barcode: string;
|
||||
noAntrian: string;
|
||||
shift: string;
|
||||
klinik: string;
|
||||
fastTrack: string;
|
||||
pembayaran: string;
|
||||
panggil: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
interface LatePatient {
|
||||
no: number;
|
||||
barcode: string;
|
||||
noAntrian: string;
|
||||
shift: string;
|
||||
klinik: string;
|
||||
aksi: string;
|
||||
}
|
||||
|
||||
interface ClinicPatient {
|
||||
no: number;
|
||||
barcode: string;
|
||||
noAntrian: string;
|
||||
noRM: string;
|
||||
shift: string;
|
||||
klinik: string;
|
||||
fastTrack: string;
|
||||
pembayaran: string;
|
||||
aksi: string;
|
||||
}
|
||||
|
||||
// Data and Headers
|
||||
const mainHeaders = ref([
|
||||
{ title: "No", value: "no", sortable: false },
|
||||
{ title: "Jam Panggil", value: "jamPanggil", sortable: true },
|
||||
{ title: "Barcode", value: "barcode", sortable: true },
|
||||
{ title: "No Antrian", value: "noAntrian", sortable: true },
|
||||
{ title: "Shift", value: "shift", sortable: true },
|
||||
{ title: "Klinik", value: "klinik", sortable: true },
|
||||
{ title: "Fast Track", value: "fastTrack", sortable: true },
|
||||
{ title: "Pembayaran", value: "pembayaran", sortable: true },
|
||||
{ title: "Panggil", value: "panggil", sortable: true },
|
||||
{ title: "Aksi", value: "aksi", sortable: false },
|
||||
]);
|
||||
|
||||
const mainPatients = ref<Patient[]>([
|
||||
{
|
||||
no: 1,
|
||||
jamPanggil: "12:49",
|
||||
barcode: "250811100163",
|
||||
noAntrian: "UM1001 | Online - 250811100163",
|
||||
shift: "Shift 1",
|
||||
klinik: "KANDUNGAN",
|
||||
fastTrack: "UMUM",
|
||||
pembayaran: "UMUM",
|
||||
panggil: "Panggil",
|
||||
status: "current",
|
||||
},
|
||||
{
|
||||
no: 2,
|
||||
jamPanggil: "10:52",
|
||||
barcode: "250811100155",
|
||||
noAntrian: "UM1002 | Online - 250811100155",
|
||||
shift: "Shift 1",
|
||||
klinik: "IPD",
|
||||
fastTrack: "UMUM",
|
||||
pembayaran: "UMUM",
|
||||
panggil: "Cancel",
|
||||
status: "normal",
|
||||
},
|
||||
...Array.from({ length: 20 }, (_, i) => ({
|
||||
no: i + 3,
|
||||
jamPanggil: `${String(Math.floor(Math.random() * 12) + 1).padStart(2, "0")}:${String(Math.floor(Math.random() * 60)).padStart(2, "0")}`,
|
||||
barcode: `25081110${String(i + 200).padStart(4, "0")}`,
|
||||
noAntrian: `UM100${i + 3} | Online - 25081110${String(i + 200).padStart(4, "0")}`,
|
||||
shift: "Shift 1",
|
||||
klinik: ["KANDUNGAN", "IPD", "THT", "SARAF"][Math.floor(Math.random() * 4)],
|
||||
fastTrack: "UMUM",
|
||||
pembayaran: "UMUM",
|
||||
panggil: "Panggil",
|
||||
status: "normal",
|
||||
})),
|
||||
]);
|
||||
|
||||
const lateHeaders = ref([
|
||||
{ title: "No", value: "no", sortable: false },
|
||||
{ title: "Barcode", value: "barcode", sortable: true },
|
||||
{ title: "No Antrian", value: "noAntrian", sortable: true },
|
||||
{ title: "Shift", value: "shift", sortable: true },
|
||||
{ title: "Klinik", value: "klinik", sortable: true },
|
||||
{ title: "Aksi", value: "aksi", sortable: false },
|
||||
]);
|
||||
const latePatients = ref<LatePatient[]>([]);
|
||||
|
||||
const clinicHeaders = ref([
|
||||
{ title: "#", value: "no", sortable: false },
|
||||
{ title: "Barcode", value: "barcode", sortable: true },
|
||||
{ title: "No Antrian", value: "noAntrian", sortable: true },
|
||||
{ title: "No RM", value: "noRM", sortable: true },
|
||||
{ title: "Shift", value: "shift", sortable: true },
|
||||
{ title: "Klinik", value: "klinik", sortable: true },
|
||||
{ title: "Fast Track", value: "fastTrack", sortable: true },
|
||||
{ title: "Pembayaran", value: "pembayaran", sortable: true },
|
||||
{ title: "Aksi", value: "aksi", sortable: false },
|
||||
]);
|
||||
const clinicPatients = ref<ClinicPatient[]>([]);
|
||||
|
||||
// Method yang tetap di halaman induk (seperti getRowClass)
|
||||
const getRowClass = (item: Patient): string => {
|
||||
if (item.status === "current") {
|
||||
return "text-green font-weight-bold";
|
||||
}
|
||||
return "";
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-list-item--active {
|
||||
background-color: rgba(25, 118, 210, 0.12);
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.text-green {
|
||||
color: #4caf50 !important;
|
||||
}
|
||||
|
||||
.bg-cyan {
|
||||
background-color: #00bcd4 !important;
|
||||
}
|
||||
|
||||
/* Row highlighting */
|
||||
:deep(.v-data-table tbody tr:nth-child(1)) {
|
||||
background-color: #fff3cd !important;
|
||||
}
|
||||
|
||||
.v-btn {
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.v-btn--size-small {
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,783 +0,0 @@
|
||||
<!-- eslint-disable vue/valid-v-slot -->
|
||||
<!-- eslint-disable vue/no-unused-vars -->
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar>
|
||||
<v-container fluid class="pa-4">
|
||||
<v-row align="center" no-gutters class="fill-height">
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center">
|
||||
<!-- Total 2 with dark background -->
|
||||
<div
|
||||
class="bg-grey-darken-4 px-3 py-1 mr-2"
|
||||
style="border-radius: 3px"
|
||||
>
|
||||
<span class="text-body-1 font-weight-bold text-white"
|
||||
>Total 2</span
|
||||
>
|
||||
</div>
|
||||
<!-- Max 150 Pasien with lighter background -->
|
||||
<div
|
||||
class="bg-grey-darken-2 px-3 py-1"
|
||||
style="border-radius: 3px"
|
||||
>
|
||||
<span class="text-body-1 text-white">Max 150 Pasien</span>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center text-body-2">
|
||||
<v-icon size="small" class="mr-2">mdi-view-dashboard</v-icon>
|
||||
<span class="mr-6">Dashboard</span>
|
||||
<span>Loket 12 | Rabu, 13 Agustus 2025 - Pelayanan</span>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-app-bar>
|
||||
|
||||
<!-- Cyan Divider -->
|
||||
<div class="bg-cyan" style="height: 3px" />
|
||||
|
||||
<!-- Main Content -->
|
||||
<v-main>
|
||||
<v-container fluid class="pa-6">
|
||||
<!-- Date and Service Info -->
|
||||
<v-row class="mb-6">
|
||||
<v-col>
|
||||
<div class="text-h6 text-grey-darken-2 font-weight-medium">
|
||||
Loket 12 | Rabu, 13 Agustus 2025 - Pelayanan :
|
||||
</div>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<!-- Queue Number Buttons on the right -->
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
color="success"
|
||||
dark
|
||||
size="large"
|
||||
class="mr-4 px-8"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">1</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="info"
|
||||
dark
|
||||
size="large"
|
||||
class="mr-4 px-4"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">5</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="warning"
|
||||
dark
|
||||
size="large"
|
||||
class="mr-4 px-4"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">10</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="error"
|
||||
dark
|
||||
size="large"
|
||||
class="px-4"
|
||||
style="min-width: 120px; height: 40px"
|
||||
>
|
||||
<span class="text-h6 font-weight-bold">20</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Next Patient Card -->
|
||||
<v-row justify="center" class="pa-12">
|
||||
<v-col cols="12" md="10" lg="8">
|
||||
<v-card
|
||||
color="success"
|
||||
dark
|
||||
flat
|
||||
class="text-center"
|
||||
style="min-height: 160px; border-radius: 8px"
|
||||
>
|
||||
<v-card-text class="pa-8">
|
||||
<div
|
||||
class="text-h2 font-weight-bold mb-2"
|
||||
style="letter-spacing: 4px"
|
||||
>
|
||||
NEXT
|
||||
</div>
|
||||
<div class="text-h6 mb-4 font-weight-normal">
|
||||
Pasien - UM1004
|
||||
</div>
|
||||
<div
|
||||
class="text-body-1 font-weight-normal"
|
||||
style="opacity: 0.9"
|
||||
>
|
||||
Klik untuk memanggil pasien selanjutnya
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Main Data Table -->
|
||||
<v-card class="mb-4">
|
||||
<v-card-title class="text-subtitle-1 font-weight-bold">
|
||||
DATA PASIEN
|
||||
</v-card-title>
|
||||
|
||||
<div class="d-flex justify-space-between align-center pa-4">
|
||||
<div class="d-flex align-center">
|
||||
<span>Show</span>
|
||||
<v-select
|
||||
v-model="itemsPerPage"
|
||||
:items="[10, 25, 50]"
|
||||
label="Entries"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="mx-2"
|
||||
style="width: 80px"
|
||||
/>
|
||||
<span>entries</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center">
|
||||
<span class="mr-2">Search:</span>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
label="Search"
|
||||
hide-details
|
||||
density="compact"
|
||||
style="min-width: 200px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-data-table
|
||||
:headers="mainHeaders"
|
||||
:items="paginatedMainPatients"
|
||||
:search="search"
|
||||
hide-default-footer
|
||||
class="elevation-1"
|
||||
>
|
||||
<template #item.aksi="{ item }">
|
||||
<div class="d-flex ga-1">
|
||||
<v-btn size="small" color="success" variant="flat"
|
||||
>Panggil</v-btn
|
||||
>
|
||||
<v-btn size="small" color="info" variant="flat">Cancel</v-btn>
|
||||
<v-btn size="small" color="primary" variant="flat"
|
||||
>Selesai</v-btn
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.jamPanggil="{ item }">
|
||||
<span :class="getRowClass(item)">{{ item.jamPanggil }}</span>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<!-- Custom Pagination for Main Table -->
|
||||
<v-row align="center" class="pa-4">
|
||||
<v-col cols="auto">
|
||||
<span class="text-body-2 text-grey-darken-1">
|
||||
Showing {{ getStartEntry(mainCurrentPage, itemsPerPage) }} to
|
||||
{{
|
||||
getEndEntry(
|
||||
mainCurrentPage,
|
||||
itemsPerPage,
|
||||
filteredMainPatients.length
|
||||
)
|
||||
}}
|
||||
of {{ filteredMainPatients.length }} entries
|
||||
</span>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
:disabled="mainCurrentPage === 1"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-grey-darken-1 mr-2"
|
||||
@click="goToMainPage(mainCurrentPage - 1)"
|
||||
>
|
||||
Previous
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-for="page in getVisiblePages(
|
||||
mainCurrentPage,
|
||||
getMainTotalPages
|
||||
)"
|
||||
:key="page"
|
||||
:variant="page === mainCurrentPage ? 'flat' : 'text'"
|
||||
:color="
|
||||
page === mainCurrentPage ? 'primary' : 'grey-lighten-1'
|
||||
"
|
||||
size="small"
|
||||
class="mx-1"
|
||||
min-width="35"
|
||||
@click="goToMainPage(page)"
|
||||
>
|
||||
{{ page }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
:disabled="mainCurrentPage === getMainTotalPages"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-grey-darken-1 ml-2"
|
||||
@click="goToMainPage(mainCurrentPage + 1)"
|
||||
>
|
||||
Next
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
|
||||
<!-- Total Quota Used -->
|
||||
<v-card color="cyan" dark class="mb-4">
|
||||
<v-card-text class="text-center">
|
||||
<div class="text-h6">Total Quota Terpakai 5</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- Late Patients Table -->
|
||||
<v-card class="mb-4">
|
||||
<v-card-title
|
||||
class="text-subtitle-1 font-weight-bold bg-red-lighten-3"
|
||||
>
|
||||
INFO PASIEN LAPOR TERLAMBAT
|
||||
</v-card-title>
|
||||
|
||||
<div class="d-flex justify-space-between align-center pa-4">
|
||||
<div class="d-flex align-center">
|
||||
<span>Show</span>
|
||||
<v-select
|
||||
v-model="lateItemsPerPage"
|
||||
:items="[10, 25, 50]"
|
||||
label="Entries"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="mx-2"
|
||||
style="width: 80px"
|
||||
/>
|
||||
<span>entries</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center">
|
||||
<span class="mr-2">Search:</span>
|
||||
<v-text-field
|
||||
v-model="lateSearch"
|
||||
label="Search"
|
||||
hide-details
|
||||
density="compact"
|
||||
style="min-width: 200px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-data-table
|
||||
:headers="lateHeaders"
|
||||
:items="paginatedLatePatients"
|
||||
:search="lateSearch"
|
||||
hide-default-footer
|
||||
class="elevation-1"
|
||||
>
|
||||
<template #no-data>
|
||||
<div class="text-center pa-4">No data available in table</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<!-- Custom Pagination for Late Patients -->
|
||||
<v-row
|
||||
v-if="filteredLatePatients.length > 0"
|
||||
align="center"
|
||||
class="pa-4"
|
||||
>
|
||||
<v-col cols="auto">
|
||||
<span class="text-body-2 text-grey-darken-1">
|
||||
Showing
|
||||
{{ getStartEntry(lateCurrentPage, lateItemsPerPage) }} to
|
||||
{{
|
||||
getEndEntry(
|
||||
lateCurrentPage,
|
||||
lateItemsPerPage,
|
||||
filteredLatePatients.length
|
||||
)
|
||||
}}
|
||||
of {{ filteredLatePatients.length }} entries
|
||||
</span>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
:disabled="lateCurrentPage === 1"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-grey-darken-1 mr-2"
|
||||
@click="goToLatePage(lateCurrentPage - 1)"
|
||||
>
|
||||
Previous
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-for="page in getVisiblePages(
|
||||
lateCurrentPage,
|
||||
getLateTotalPages
|
||||
)"
|
||||
:key="page"
|
||||
:variant="page === lateCurrentPage ? 'flat' : 'text'"
|
||||
:color="
|
||||
page === lateCurrentPage ? 'primary' : 'grey-lighten-1'
|
||||
"
|
||||
size="small"
|
||||
class="mx-1"
|
||||
min-width="35"
|
||||
@click="goToLatePage(page)"
|
||||
>
|
||||
{{ page }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
:disabled="lateCurrentPage === getLateTotalPages"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-grey-darken-1 ml-2"
|
||||
@click="goToLatePage(lateCurrentPage + 1)"
|
||||
>
|
||||
Next
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
|
||||
<!-- Clinic Entry Patients Table -->
|
||||
<v-card class="mb-4">
|
||||
<v-card-title
|
||||
class="text-subtitle-1 font-weight-bold bg-red-lighten-3"
|
||||
>
|
||||
INFO PASIEN MASUK KLINIK
|
||||
</v-card-title>
|
||||
|
||||
<div class="d-flex justify-space-between align-center pa-4">
|
||||
<div class="d-flex align-center">
|
||||
<span>Show</span>
|
||||
<v-select
|
||||
v-model="clinicItemsPerPage"
|
||||
:items="[10, 25, 50]"
|
||||
label="Entries"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="mx-2"
|
||||
style="width: 80px"
|
||||
/>
|
||||
<span>entries</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center">
|
||||
<span class="mr-2">Search:</span>
|
||||
<v-text-field
|
||||
v-model="clinicSearch"
|
||||
label="Search"
|
||||
hide-details
|
||||
density="compact"
|
||||
style="min-width: 200px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-data-table
|
||||
:headers="clinicHeaders"
|
||||
:items="paginatedClinicPatients"
|
||||
:search="clinicSearch"
|
||||
hide-default-footer
|
||||
class="elevation-1"
|
||||
>
|
||||
<template #no-data>
|
||||
<div class="text-center pa-4">No data available in table</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<!-- Custom Pagination for Clinic Patients -->
|
||||
<v-row
|
||||
v-if="filteredClinicPatients.length > 0"
|
||||
align="center"
|
||||
class="pa-4"
|
||||
>
|
||||
<v-col cols="auto">
|
||||
<span class="text-body-2 text-grey-darken-1">
|
||||
Showing
|
||||
{{ getStartEntry(clinicCurrentPage, clinicItemsPerPage) }} to
|
||||
{{
|
||||
getEndEntry(
|
||||
clinicCurrentPage,
|
||||
clinicItemsPerPage,
|
||||
filteredClinicPatients.length
|
||||
)
|
||||
}}
|
||||
of {{ filteredClinicPatients.length }} entries
|
||||
</span>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<v-col cols="auto">
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
:disabled="clinicCurrentPage === 1"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-grey-darken-1 mr-2"
|
||||
@click="goToClinicPage(clinicCurrentPage - 1)"
|
||||
>
|
||||
Previous
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-for="page in getVisiblePages(
|
||||
clinicCurrentPage,
|
||||
getClinicTotalPages
|
||||
)"
|
||||
:key="page"
|
||||
:variant="page === clinicCurrentPage ? 'flat' : 'text'"
|
||||
:color="
|
||||
page === clinicCurrentPage ? 'primary' : 'grey-lighten-1'
|
||||
"
|
||||
size="small"
|
||||
class="mx-1"
|
||||
min-width="35"
|
||||
@click="goToClinicPage(page)"
|
||||
>
|
||||
{{ page }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
:disabled="clinicCurrentPage === getClinicTotalPages"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-grey-darken-1 ml-2"
|
||||
@click="goToClinicPage(clinicCurrentPage + 1)"
|
||||
>
|
||||
Next
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
// Define interfaces for type safety
|
||||
interface Patient {
|
||||
no: number;
|
||||
jamPanggil: string;
|
||||
barcode: string;
|
||||
noAntrian: string;
|
||||
shift: string;
|
||||
klinik: string;
|
||||
fastTrack: string;
|
||||
pembayaran: string;
|
||||
panggil: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
interface LatePatient {
|
||||
no: number;
|
||||
barcode: string;
|
||||
noAntrian: string;
|
||||
shift: string;
|
||||
klinik: string;
|
||||
aksi: string;
|
||||
}
|
||||
|
||||
interface ClinicPatient {
|
||||
no: number;
|
||||
barcode: string;
|
||||
noAntrian: string;
|
||||
noRM: string;
|
||||
shift: string;
|
||||
klinik: string;
|
||||
fastTrack: string;
|
||||
pembayaran: string;
|
||||
aksi: string;
|
||||
}
|
||||
|
||||
// Reactive data
|
||||
const search = ref<string>("");
|
||||
const lateSearch = ref<string>("");
|
||||
const clinicSearch = ref<string>("");
|
||||
const itemsPerPage = ref<number>(10);
|
||||
const lateItemsPerPage = ref<number>(10);
|
||||
const clinicItemsPerPage = ref<number>(10);
|
||||
|
||||
// Pagination current pages
|
||||
const mainCurrentPage = ref<number>(1);
|
||||
const lateCurrentPage = ref<number>(1);
|
||||
const clinicCurrentPage = ref<number>(1);
|
||||
|
||||
// Table headers
|
||||
const mainHeaders = ref([
|
||||
{ title: "No", value: "no", sortable: false },
|
||||
{ title: "Jam Panggil", value: "jamPanggil", sortable: true },
|
||||
{ title: "Barcode", value: "barcode", sortable: true },
|
||||
{ title: "No Antrian", value: "noAntrian", sortable: true },
|
||||
{ title: "Shift", value: "shift", sortable: true },
|
||||
{ title: "Klinik", value: "klinik", sortable: true },
|
||||
{ title: "Fast Track", value: "fastTrack", sortable: true },
|
||||
{ title: "Pembayaran", value: "pembayaran", sortable: true },
|
||||
{ title: "Panggil", value: "panggil", sortable: true },
|
||||
{ title: "Aksi", value: "aksi", sortable: false },
|
||||
]);
|
||||
|
||||
const lateHeaders = ref([
|
||||
{ title: "No", value: "no", sortable: false },
|
||||
{ title: "Barcode", value: "barcode", sortable: true },
|
||||
{ title: "No Antrian", value: "noAntrian", sortable: true },
|
||||
{ title: "Shift", value: "shift", sortable: true},
|
||||
{ title: "Klinik", value: "klinik", sortable: true },
|
||||
{ title: "Aksi", value: "aksi", sortable: false },
|
||||
]);
|
||||
|
||||
const clinicHeaders = ref([
|
||||
{ title: "#", value: "no", sortable: false },
|
||||
{ title: "Barcode", value: "barcode", sortable: true },
|
||||
{ title: "No Antrian", value: "noAntrian", sortable: true },
|
||||
{ title: "No RM", value: "noRM", sortable: true },
|
||||
{ title: "Shift", value: "shift", sortable: true },
|
||||
{ title: "Klinik", value: "klinik", sortable: true },
|
||||
{ title: "Fast Track", value: "fastTrack", sortable: true },
|
||||
{ title: "Pembayaran", value: "pembayaran", sortable: true },
|
||||
{ title: "Aksi", value: "aksi", sortable: false },
|
||||
]);
|
||||
|
||||
// Sample data - Extended for pagination demo
|
||||
const mainPatients = ref<Patient[]>([
|
||||
{
|
||||
no: 1,
|
||||
jamPanggil: "12:49",
|
||||
barcode: "250811100163",
|
||||
noAntrian: "UM1001 | Online - 250811100163",
|
||||
shift: "Shift 1",
|
||||
klinik: "KANDUNGAN",
|
||||
fastTrack: "UMUM",
|
||||
pembayaran: "UMUM",
|
||||
panggil: "Panggil",
|
||||
status: "current",
|
||||
},
|
||||
{
|
||||
no: 2,
|
||||
jamPanggil: "10:52",
|
||||
barcode: "250811100155",
|
||||
noAntrian: "UM1002 | Online - 250811100155",
|
||||
shift: "Shift 1",
|
||||
klinik: "IPD",
|
||||
fastTrack: "UMUM",
|
||||
pembayaran: "UMUM",
|
||||
panggil: "Cancel",
|
||||
status: "normal",
|
||||
},
|
||||
// Add more sample data for pagination demo
|
||||
...Array.from({ length: 20 }, (_, i) => ({
|
||||
no: i + 3,
|
||||
jamPanggil: `${String(Math.floor(Math.random() * 12) + 1).padStart(2, "0")}:${String(Math.floor(Math.random() * 60)).padStart(2, "0")}`,
|
||||
barcode: `25081110${String(i + 200).padStart(4, "0")}`,
|
||||
noAntrian: `UM100${i + 3} | Online - 25081110${String(i + 200).padStart(4, "0")}`,
|
||||
shift: "Shift 1",
|
||||
klinik: ["KANDUNGAN", "IPD", "THT", "SARAF"][Math.floor(Math.random() * 4)],
|
||||
fastTrack: "UMUM",
|
||||
pembayaran: "UMUM",
|
||||
panggil: "Panggil",
|
||||
status: "normal",
|
||||
})),
|
||||
]);
|
||||
|
||||
const latePatients = ref<LatePatient[]>([]);
|
||||
const clinicPatients = ref<ClinicPatient[]>([]);
|
||||
|
||||
// Computed properties for filtering
|
||||
const filteredMainPatients = computed(() => {
|
||||
if (!search.value) return mainPatients.value;
|
||||
return mainPatients.value.filter((item) =>
|
||||
Object.values(item).some((val) =>
|
||||
String(val).toLowerCase().includes(search.value.toLowerCase())
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const filteredLatePatients = computed(() => {
|
||||
if (!lateSearch.value) return latePatients.value;
|
||||
return latePatients.value.filter((item) =>
|
||||
Object.values(item).some((val) =>
|
||||
String(val).toLowerCase().includes(lateSearch.value.toLowerCase())
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const filteredClinicPatients = computed(() => {
|
||||
if (!clinicSearch.value) return clinicPatients.value;
|
||||
return clinicPatients.value.filter((item) =>
|
||||
Object.values(item).some((val) =>
|
||||
String(val).toLowerCase().includes(clinicSearch.value.toLowerCase())
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Computed properties for pagination
|
||||
const getMainTotalPages = computed(() => {
|
||||
return Math.ceil(filteredMainPatients.value.length / itemsPerPage.value);
|
||||
});
|
||||
|
||||
const getLateTotalPages = computed(() => {
|
||||
return Math.ceil(filteredLatePatients.value.length / lateItemsPerPage.value);
|
||||
});
|
||||
|
||||
const getClinicTotalPages = computed(() => {
|
||||
return Math.ceil(
|
||||
filteredClinicPatients.value.length / clinicItemsPerPage.value
|
||||
);
|
||||
});
|
||||
|
||||
const paginatedMainPatients = computed(() => {
|
||||
const start = (mainCurrentPage.value - 1) * itemsPerPage.value;
|
||||
const end = start + itemsPerPage.value;
|
||||
return filteredMainPatients.value.slice(start, end);
|
||||
});
|
||||
|
||||
const paginatedLatePatients = computed(() => {
|
||||
const start = (lateCurrentPage.value - 1) * lateItemsPerPage.value;
|
||||
const end = start + lateItemsPerPage.value;
|
||||
return filteredLatePatients.value.slice(start, end);
|
||||
});
|
||||
|
||||
const paginatedClinicPatients = computed(() => {
|
||||
const start = (clinicCurrentPage.value - 1) * clinicItemsPerPage.value;
|
||||
const end = start + clinicItemsPerPage.value;
|
||||
return filteredClinicPatients.value.slice(start, end);
|
||||
});
|
||||
|
||||
// Methods
|
||||
const getRowClass = (item: Patient): string => {
|
||||
if (item.status === "current") {
|
||||
return "text-green font-weight-bold";
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
const getStartEntry = (currentPage: number, itemsPerPage: number): number => {
|
||||
return (currentPage - 1) * itemsPerPage + 1;
|
||||
};
|
||||
|
||||
const getEndEntry = (currentPage: number, itemsPerPage: number, totalItems: number): number => {
|
||||
const end = currentPage * itemsPerPage;
|
||||
return Math.min(end, totalItems);
|
||||
};
|
||||
|
||||
const getVisiblePages = (currentPage: number, totalPages: number): (number | string)[] => {
|
||||
const pages: (number | string)[] = [];
|
||||
const total = totalPages;
|
||||
const current = currentPage;
|
||||
|
||||
if (total <= 0) return pages;
|
||||
|
||||
// Always show first page
|
||||
pages.push(1);
|
||||
|
||||
// Show pages around current page
|
||||
const start = Math.max(2, current - 1);
|
||||
const end = Math.min(total - 1, current + 1);
|
||||
|
||||
// Add ellipsis if needed
|
||||
if (start > 2) pages.push("...");
|
||||
|
||||
// Add middle pages
|
||||
for (let i = start; i <= end; i++) {
|
||||
if (i !== 1 && i !== total) {
|
||||
pages.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Add ellipsis if needed
|
||||
if (end < total - 1) pages.push("...");
|
||||
|
||||
// Always show last page if more than 1 page
|
||||
if (total > 1) pages.push(total);
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
const goToMainPage = (page: number): void => {
|
||||
if (
|
||||
page >= 1 &&
|
||||
page <= getMainTotalPages.value &&
|
||||
typeof page === "number"
|
||||
) {
|
||||
mainCurrentPage.value = page;
|
||||
}
|
||||
};
|
||||
|
||||
const goToLatePage = (page: number): void => {
|
||||
if (
|
||||
page >= 1 &&
|
||||
page <= getLateTotalPages.value &&
|
||||
typeof page === "number"
|
||||
) {
|
||||
lateCurrentPage.value = page;
|
||||
}
|
||||
};
|
||||
|
||||
const goToClinicPage = (page: number): void => {
|
||||
if (
|
||||
page >= 1 &&
|
||||
page <= getClinicTotalPages.value &&
|
||||
typeof page === "number"
|
||||
) {
|
||||
clinicCurrentPage.value = page;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-list-item--active {
|
||||
background-color: rgba(25, 118, 210, 0.12);
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.text-green {
|
||||
color: #4caf50 !important;
|
||||
}
|
||||
|
||||
.bg-cyan {
|
||||
background-color: #00bcd4 !important;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
:deep(.v-data-table) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Row highlighting */
|
||||
:deep(.v-data-table tbody tr:nth-child(1)) {
|
||||
background-color: #fff3cd !important;
|
||||
}
|
||||
|
||||
.v-btn {
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.v-btn--size-small {
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
</style>
|
||||
355
pages/index.vue
355
pages/index.vue
@@ -1,5 +1,3 @@
|
||||
<!-- eslint-disable vue/valid-v-slot -->
|
||||
<!-- eslint-disable vue/no-unused-vars -->
|
||||
<template>
|
||||
<v-container fluid class="bg-grey-lighten-4 pa-4">
|
||||
<p class="mb-4">Admin Anjungan</p>
|
||||
@@ -29,119 +27,20 @@
|
||||
|
||||
<v-divider :thickness="5" color="deep-orange-darken-4"></v-divider>
|
||||
|
||||
<v-card-text>
|
||||
<v-toolbar flat color="transparent" dense>
|
||||
<v-toolbar-title class="text-subtitle-1 font-weight-bold red--text">
|
||||
DATA PENGUNJUNG TERLAMBAT
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<div class="d-flex align-center">
|
||||
<span>Show</span>
|
||||
<v-select
|
||||
:items="[10, 25, 50, 100]"
|
||||
label="entries"
|
||||
dense
|
||||
single-line
|
||||
hide-details
|
||||
class="shrink mx-2"
|
||||
style="width: 80px"
|
||||
></v-select>
|
||||
<span>entries</span>
|
||||
</div>
|
||||
</v-toolbar>
|
||||
<v-data-table
|
||||
<TabelData
|
||||
:headers="lateHeaders"
|
||||
:items="lateVisitors"
|
||||
:search="searchLate"
|
||||
no-data-text="No data available in table"
|
||||
hide-default-footer
|
||||
class="elevation-1"
|
||||
>
|
||||
</v-data-table>
|
||||
<!-- Custom Pagination for Late Visitors -->
|
||||
<div class="d-flex justify-space-between align-center pt-4">
|
||||
<div class="text-body-2 text-grey-darken-1">
|
||||
Showing {{ currentPageStart }} to {{ currentPageEnd }} of {{ totalLateEntries }} entries
|
||||
</div>
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
:disabled="currentPage === 1"
|
||||
@click="previousPage"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-body-2"
|
||||
>
|
||||
Previous
|
||||
</v-btn>
|
||||
<template v-for="page in visiblePages" :key="page">
|
||||
<v-btn
|
||||
v-if="page !== '...'"
|
||||
:color="page === currentPage ? 'primary' : ''"
|
||||
:variant="page === currentPage ? 'flat' : 'text'"
|
||||
@click="goToPage(page)"
|
||||
size="small"
|
||||
class="mx-1"
|
||||
min-width="40"
|
||||
>
|
||||
{{ page }}
|
||||
</v-btn>
|
||||
<span v-else class="mx-1">...</span>
|
||||
</template>
|
||||
<v-btn
|
||||
:disabled="currentPage === totalPages"
|
||||
@click="nextPage"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-body-2"
|
||||
>
|
||||
Next
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
title="DATA PENGUNJUNG TERLAMBAT"
|
||||
/>
|
||||
|
||||
<v-divider :thickness="5" color="deep-orange-darken-4"></v-divider>
|
||||
|
||||
<v-card-text>
|
||||
<v-toolbar flat color="transparent" dense>
|
||||
<v-toolbar-title class="text-subtitle-1 font-weight-bold">
|
||||
DATA PENGUNJUNG
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<div class="d-flex justify-space-between align-center px-4">
|
||||
<div class="d-flex align-center">
|
||||
<span>Show</span>
|
||||
<v-select
|
||||
:items="[10, 25, 50]"
|
||||
v-model="itemsPerPage"
|
||||
label="Entries"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="mx-2"
|
||||
style="width: 80px"
|
||||
></v-select>
|
||||
<span>entries</span>
|
||||
</div>
|
||||
<div class="d-flex align-center">
|
||||
<span class="mr-2">Search:</span>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
label="Search"
|
||||
hide-details
|
||||
density="compact"
|
||||
style="min-width: 200px"
|
||||
></v-text-field>
|
||||
</div>
|
||||
</div>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="paginatedMainPatients"
|
||||
:search="search"
|
||||
no-data-text="No data available in table"
|
||||
class="elevation-1"
|
||||
hide-default-footer
|
||||
<TabelData
|
||||
:headers="mainHeaders"
|
||||
:items="mainPatients"
|
||||
title="DATA PENGUNJUNG"
|
||||
>
|
||||
<template v-slot:item.aksi="{ item }">
|
||||
<template v-slot:actions="{ item }">
|
||||
<div class="d-flex ga-1">
|
||||
<v-btn small color="success" class="d-flex flex-row" variant="flat">Tiket</v-btn>
|
||||
<v-btn small color="success" class="d-flex flex-row" variant="flat"
|
||||
@@ -150,82 +49,20 @@
|
||||
<v-btn small color="info" class="d-flex flex-row" variant="flat">ByPass</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<!-- Custom Pagination for Main Patients -->
|
||||
<div class="d-flex justify-space-between align-center pt-4">
|
||||
<div class="text-body-2 text-grey-darken-1">
|
||||
Showing {{ mainCurrentPageStart }} to {{ mainCurrentPageEnd }} of {{ totalMainEntries }} entries
|
||||
</div>
|
||||
<div class="d-flex align-center">
|
||||
<v-btn
|
||||
:disabled="mainCurrentPage === 1"
|
||||
@click="mainPreviousPage"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-body-2"
|
||||
>
|
||||
Previous
|
||||
</v-btn>
|
||||
<template v-for="page in mainVisiblePages" :key="page">
|
||||
<v-btn
|
||||
v-if="page !== '...'"
|
||||
:color="page === mainCurrentPage ? 'primary' : ''"
|
||||
:variant="page === mainCurrentPage ? 'flat' : 'text'"
|
||||
@click="mainGoToPage(page)"
|
||||
size="small"
|
||||
class="mx-1"
|
||||
min-width="40"
|
||||
>
|
||||
{{ page }}
|
||||
</v-btn>
|
||||
<span v-else class="mx-1">...</span>
|
||||
</template>
|
||||
<v-btn
|
||||
:disabled="mainCurrentPage === mainTotalPages"
|
||||
@click="mainNextPage"
|
||||
variant="text"
|
||||
size="small"
|
||||
class="text-body-2"
|
||||
>
|
||||
Next
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</TabelData>
|
||||
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import { ref } from "vue";
|
||||
import TabelData from "../components/TabelData.vue"; // Pastikan path-nya benar
|
||||
|
||||
// Struktur data yang memisahkan menu dengan dan tanpa submenu
|
||||
const items = ref([
|
||||
{ title: "Dashboard", icon: "mdi-view-dashboard", to: "/dashboard" },
|
||||
{
|
||||
title: "Setting",
|
||||
icon: "mdi-cog",
|
||||
children: [
|
||||
{ title: "Hak Akses", to: "/setting/hak-akses" },
|
||||
{ title: "User Login", to: "/setting/user-login" },
|
||||
{ title: "Master Loket", to: "/setting/master-loket" },
|
||||
{ title: "Master Klinik", to: "/setting/master-klinik" },
|
||||
{ title: "Master Klinik Ruang", to: "/setting/master-klinik-ruang" },
|
||||
{ title: "Screen", to: "/setting/screen" },
|
||||
],
|
||||
},
|
||||
{ title: "Loket Admin", icon: "mdi-account-supervisor" },
|
||||
{ title: "Ranap Admin", icon: "mdi-bed" },
|
||||
{ title: "Klinik Admin", icon: "mdi-hospital-box" },
|
||||
{ title: "Klinik Ruang Admin", icon: "mdi-hospital-marker" },
|
||||
{ title: "Anjungan", icon: "mdi-account-box-multiple", to: "/anjungan" },
|
||||
{ title: "Fast Track", icon: "mdi-clock-fast" },
|
||||
{ title: "Data Pasien", icon: "mdi-account-multiple" },
|
||||
{ title: "Screen", icon: "mdi-monitor" },
|
||||
{ title: "List Pasien", icon: "mdi-format-list-bulleted" },
|
||||
]);
|
||||
// Ini adalah data yang akan menjadi "single source of truth"
|
||||
// untuk tabel Anda. Data ini dikirim sebagai props ke komponen anak.
|
||||
|
||||
const headers = ref([
|
||||
const mainHeaders = ref([
|
||||
{ title: "No", value: "no", sortable: false },
|
||||
{ title: "Tgl Daftar", value: "tglDaftar", sortable: true },
|
||||
{ title: "RM", value: "rm", sortable: true },
|
||||
@@ -294,136 +131,38 @@ const mainPatients = ref([
|
||||
},
|
||||
]);
|
||||
|
||||
const search = ref("");
|
||||
const itemsPerPage = ref(10);
|
||||
const lateHeaders = ref([
|
||||
{ title: "No", value: "no", sortable: false },
|
||||
// Tambahkan headers spesifik untuk tabel ini jika berbeda
|
||||
]);
|
||||
|
||||
// Pagination for Late Visitors
|
||||
const searchLate = ref('');
|
||||
const lateHeaders = ref([]);
|
||||
const lateVisitors = ref([]);
|
||||
const currentPage = ref(1);
|
||||
const totalLateEntries = computed(() => lateVisitors.value.length);
|
||||
const totalPages = computed(() => Math.ceil(totalLateEntries.value / itemsPerPage.value));
|
||||
const currentPageStart = computed(() => (currentPage.value - 1) * itemsPerPage.value + 1);
|
||||
const currentPageEnd = computed(() => Math.min(currentPage.value * itemsPerPage.value, totalLateEntries.value));
|
||||
const lateVisitors = ref([
|
||||
// Tambahkan data spesifik untuk tabel ini jika ada
|
||||
]);
|
||||
|
||||
// Pagination for Main Patients
|
||||
const mainCurrentPage = ref(1);
|
||||
const totalMainEntries = computed(() => mainPatients.value.length);
|
||||
const mainTotalPages = computed(() => Math.ceil(totalMainEntries.value / itemsPerPage.value));
|
||||
const mainCurrentPageStart = computed(() => (mainCurrentPage.value - 1) * itemsPerPage.value + 1);
|
||||
const mainCurrentPageEnd = computed(() => Math.min(mainCurrentPage.value * itemsPerPage.value, totalMainEntries.value));
|
||||
|
||||
// Paginated data for main patients
|
||||
const paginatedMainPatients = computed(() => {
|
||||
const start = (mainCurrentPage.value - 1) * itemsPerPage.value;
|
||||
const end = start + itemsPerPage.value;
|
||||
return mainPatients.value.slice(start, end);
|
||||
});
|
||||
|
||||
// Visible pages calculation for Late Visitors
|
||||
const visiblePages = computed(() => {
|
||||
const pages = [];
|
||||
const total = totalPages.value;
|
||||
const current = currentPage.value;
|
||||
|
||||
if (total <= 7) {
|
||||
for (let i = 1; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
if (current <= 4) {
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
pages.push('...');
|
||||
pages.push(total);
|
||||
} else if (current >= total - 3) {
|
||||
pages.push(1);
|
||||
pages.push('...');
|
||||
for (let i = total - 4; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
pages.push(1);
|
||||
pages.push('...');
|
||||
for (let i = current - 1; i <= current + 1; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
pages.push('...');
|
||||
pages.push(total);
|
||||
}
|
||||
}
|
||||
return pages;
|
||||
});
|
||||
|
||||
// Visible pages calculation for Main Patients
|
||||
const mainVisiblePages = computed(() => {
|
||||
const pages = [];
|
||||
const total = mainTotalPages.value;
|
||||
const current = mainCurrentPage.value;
|
||||
|
||||
if (total <= 7) {
|
||||
for (let i = 1; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
if (current <= 4) {
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
pages.push('...');
|
||||
pages.push(total);
|
||||
} else if (current >= total - 3) {
|
||||
pages.push(1);
|
||||
pages.push('...');
|
||||
for (let i = total - 4; i <= total; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
pages.push(1);
|
||||
pages.push('...');
|
||||
for (let i = current - 1; i <= current + 1; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
pages.push('...');
|
||||
pages.push(total);
|
||||
}
|
||||
}
|
||||
return pages;
|
||||
});
|
||||
|
||||
// Pagination methods for Late Visitors
|
||||
const previousPage = () => {
|
||||
if (currentPage.value > 1) {
|
||||
currentPage.value--;
|
||||
}
|
||||
};
|
||||
|
||||
const nextPage = () => {
|
||||
if (currentPage.value < totalPages.value) {
|
||||
currentPage.value++;
|
||||
}
|
||||
};
|
||||
|
||||
const goToPage = (page: number) => {
|
||||
currentPage.value = page;
|
||||
};
|
||||
|
||||
// Pagination methods for Main Patients
|
||||
const mainPreviousPage = () => {
|
||||
if (mainCurrentPage.value > 1) {
|
||||
mainCurrentPage.value--;
|
||||
}
|
||||
};
|
||||
|
||||
const mainNextPage = () => {
|
||||
if (mainCurrentPage.value < mainTotalPages.value) {
|
||||
mainCurrentPage.value++;
|
||||
}
|
||||
};
|
||||
|
||||
const mainGoToPage = (page: number) => {
|
||||
mainCurrentPage.value = page;
|
||||
};
|
||||
// ... Sisa kode lainnya yang tidak terkait dengan tabel ...
|
||||
const items = ref([
|
||||
{ title: "Dashboard", icon: "mdi-view-dashboard", to: "/dashboard" },
|
||||
{
|
||||
title: "Setting",
|
||||
icon: "mdi-cog",
|
||||
children: [
|
||||
{ title: "Hak Akses", to: "/setting/hak-akses" },
|
||||
{ title: "User Login", to: "/setting/user-login" },
|
||||
{ title: "Master Loket", to: "/setting/master-loket" },
|
||||
{ title: "Master Klinik", to: "/setting/master-klinik" },
|
||||
{ title: "Master Klinik Ruang", to: "/setting/master-klinik-ruang" },
|
||||
{ title: "Screen", to: "/setting/screen" },
|
||||
],
|
||||
},
|
||||
{ title: "Loket Admin", icon: "mdi-account-supervisor" },
|
||||
{ title: "Ranap Admin", icon: "mdi-bed" },
|
||||
{ title: "Klinik Admin", icon: "mdi-hospital-box" },
|
||||
{ title: "Klinik Ruang Admin", icon: "mdi-hospital-marker" },
|
||||
{ title: "Anjungan", icon: "mdi-account-box-multiple", to: "/anjungan" },
|
||||
{ title: "Fast Track", icon: "mdi-clock-fast" },
|
||||
{ title: "Data Pasien", icon: "mdi-account-multiple" },
|
||||
{ title: "Screen", icon: "mdi-monitor" },
|
||||
{ title: "List Pasien", icon: "mdi-format-list-bulleted" },
|
||||
]);
|
||||
</script>
|
||||
Reference in New Issue
Block a user