diff --git a/components/features/queue/QueueActionsCard.vue b/components/features/queue/QueueActionsCard.vue index fa993ea..441a0a7 100644 --- a/components/features/queue/QueueActionsCard.vue +++ b/components/features/queue/QueueActionsCard.vue @@ -12,6 +12,10 @@ Tersedia {{ availableQuota }} +
+ Menunggu + {{ menungguCount }} +
Terpakai: {{ usedQuota }} + Bisa dipanggil: {{ callableQuota }}
@@ -53,6 +58,10 @@ const props = defineProps({ type: Number, default: 0 }, + menungguCount: { + type: Number, + default: 0 + }, hasNext: { type: Boolean, default: false @@ -61,6 +70,11 @@ const props = defineProps({ const availableQuota = computed(() => props.totalQuota - props.usedQuota); const quotaPercentage = computed(() => (props.usedQuota / props.totalQuota) * 100); +// Kuota yang bisa dipanggil = Tersedia - Menunggu +const callableQuota = computed(() => { + const callable = availableQuota.value - props.menungguCount; + return Math.max(0, callable); +}); defineEmits(['call']); @@ -70,6 +84,18 @@ defineEmits(['call']); border-radius: 12px; border: 1px solid var(--color-neutral-500); background: var(--color-neutral-100); + width: 100% !important; + max-width: 100% !important; + overflow: hidden !important; + box-sizing: border-box !important; + display: block !important; +} + +.queue-actions-card :deep(.v-card-text) { + padding: 16px !important; + box-sizing: border-box !important; + width: 100% !important; + max-width: 100% !important; } .section-label { @@ -81,20 +107,37 @@ defineEmits(['call']); } .quota-info { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 12px; + display: grid !important; + grid-template-columns: 1fr 1fr 1fr !important; + gap: 6px !important; + margin-bottom: 12px !important; + width: 100% !important; + box-sizing: border-box !important; + grid-auto-flow: row !important; + grid-auto-rows: auto !important; } .quota-item { - background: var(--color-neutral-300); - border-radius: 8px; - padding: 12px; - text-align: center; + background: var(--color-neutral-300) !important; + border-radius: 8px !important; + padding: 10px 6px !important; + text-align: center !important; + min-width: 0 !important; + max-width: 100% !important; + width: 100% !important; + box-sizing: border-box !important; + overflow: hidden !important; + word-wrap: break-word !important; + display: flex !important; + flex-direction: column !important; + justify-content: center !important; + align-items: center !important; + flex: 1 1 0% !important; } .quota-item.full-width { - grid-column: 1 / -1; + grid-column: 1 / 4 !important; + margin-top: 8px !important; } .quota-label { @@ -107,15 +150,29 @@ defineEmits(['call']); .quota-value { display: block; - font-size: 24px; + font-size: 22px; font-weight: 800; color: var(--color-neutral-900); + line-height: 1.2; + white-space: nowrap; } .quota-available { color: var(--color-success-600); } +.quota-waiting { + color: var(--color-warning-600); +} + +.quota-callable { + display: block; + font-size: 11px; + color: var(--color-primary-600); + margin-top: 4px; + font-weight: 600; +} + .quota-used { display: block; font-size: 11px; @@ -139,5 +196,18 @@ defineEmits(['call']); .call-buttons { grid-template-columns: repeat(2, 1fr); } + + .quota-info { + grid-template-columns: repeat(3, 1fr); + gap: 8px; + } + + .quota-item { + padding: 8px; + } + + .quota-value { + font-size: 20px; + } } \ No newline at end of file diff --git a/components/features/queue/TabelPatientData.vue b/components/features/queue/TabelPatientData.vue index 78356b0..dcb35ea 100644 --- a/components/features/queue/TabelPatientData.vue +++ b/components/features/queue/TabelPatientData.vue @@ -18,16 +18,6 @@ {{ status.label }} ({{ status.count }}) - - - @@ -200,6 +190,10 @@ const props = defineProps({ type: Number, default: 0 }, + waitingCount: { + type: Number, + default: 0 + }, showDiproses: { type: Boolean, default: true @@ -295,6 +289,16 @@ const statusOptions = computed(() => { }); } + // Tambahkan kategori "Menunggu" (pasien yang sudah dipanggil tapi belum check-in) + if (props.waitingCount > 0) { + baseOptions.push({ + value: 'waiting', + label: 'Menunggu', + count: props.waitingCount, + icon: 'mdi-clock-outline' + }); + } + baseOptions.push( { value: 'terlambat', label: props.statusLabels.terlambat, count: props.terlambatCount }, { value: 'pending', label: props.statusLabels.pending, count: props.pendingCount } @@ -459,11 +463,6 @@ const handleAction = (patient, action) => { border-color: var(--color-secondary-600); } -.search-field { - max-width: 300px; - min-width: 250px; -} - .advanced-filters { display: flex; gap: 12px; @@ -543,11 +542,6 @@ const handleAction = (patient, action) => { min-width: 100%; } - .search-field { - max-width: 100%; - min-width: 100%; - } - .advanced-filters { flex-direction: column; } diff --git a/composables/useQueue.js b/composables/useQueue.js index 97614c3..0be6bbe 100644 --- a/composables/useQueue.js +++ b/composables/useQueue.js @@ -55,9 +55,13 @@ export const useQueue = (adminType = "loket") => { }); const waitingPatients = computed(() => stagePatients.value.waiting || []); + + // Pasien yang belum dipanggil (status "menunggu") + const menungguPatients = computed(() => stagePatients.value.menunggu || []); const nextPatient = computed(() => { - return waitingPatients.value[0] || null; + // Prioritaskan pasien menunggu, baru waiting + return menungguPatients.value[0] || waitingPatients.value[0] || null; }); // Total pasien hanya untuk stage admin ini @@ -185,6 +189,7 @@ export const useQueue = (adminType = "loket") => { terlambatPatients, pendingPatients, waitingPatients, + menungguPatients, nextPatient, totalPasien, quotaUsed, diff --git a/pages/AdminKlinik.vue b/pages/AdminKlinik.vue index 3096e03..302b1cb 100644 --- a/pages/AdminKlinik.vue +++ b/pages/AdminKlinik.vue @@ -42,12 +42,12 @@ - mdi-hospital-building - Buat Antrean Klinik + mdi-door + Buat Antrean Ruang @@ -74,7 +74,7 @@
- +
@@ -137,6 +137,90 @@ @select="changeKlinik" /> + + + + + Pilih Klinik Ruang + + mdi-close + + + + + + +
+

Informasi Pasien

+ + +
+ Barcode + {{ currentProcessingPatient.barcode }} +
+
+ +
+ No. Antrian + {{ currentProcessingPatient.noAntrian }} +
+
+
+
+ + + + + + +
+ + {{ klinikRuang.kodeKlinik }} + + {{ klinikRuang.namaKlinik }} +
+ +
+ + +
+
+ mdi-door +
+ Ruang {{ ruang.nomorRuang }} - {{ ruang.namaRuang }} + Screen: {{ ruang.nomorScreen }} +
+ mdi-chevron-right +
+
+
+
+
+
+
+
+ import { ref, computed } from "vue"; import { useQueue } from "@/composables/useQueue"; +import { useQueueStore } from "@/stores/queueStore"; +import { useMasterStore } from "@/stores/masterStore"; import PageHeader from "@/components/common/PageHeader.vue"; import CurrentPatientCard from "@/components/features/queue/CurrentPatientCard.vue"; import QueueActionsCard from "@/components/features/queue/QueueActionsCard.vue"; @@ -156,6 +242,9 @@ import PatientDataTable from "@/components/features/queue/TabelPatientData.vue"; import SelectionDialog from "@/components/common/SelectionDialog.vue"; import AppSnackbar from "@/components/common/AppSnackbar.vue"; +const masterStore = useMasterStore(); +const queueStore = useQueueStore(); + const { snackbar, snackbarText, @@ -198,6 +287,24 @@ const selectedStatus = ref("all"); const searchQuery = ref(""); const selectedFastTrack = ref(null); +// Dialog Klinik Ruang +const showKlinikRuangDialog = ref(false); +const klinikRuangSearch = ref(""); + +// Get klinik ruang data from masterStore +const klinikRuangList = computed(() => { + return masterStore.ruangData || []; +}); + +const filteredKlinikRuang = computed(() => { + if (!klinikRuangSearch.value) return klinikRuangList.value; + return klinikRuangList.value.filter( + (k) => + k.namaKlinik.toLowerCase().includes(klinikRuangSearch.value.toLowerCase()) || + k.kodeKlinik.toLowerCase().includes(klinikRuangSearch.value.toLowerCase()) + ); +}); + // Fast Track options from all patients const fastTrackOptions = computed(() => { const normalizedValues = allPatients.value @@ -276,6 +383,32 @@ const handleCallPatient = () => { // Placeholder for text-to-speech integration } }; + +const openKlinikRuangDialog = () => { + showKlinikRuangDialog.value = true; +}; + +const closeKlinikRuangDialog = () => { + showKlinikRuangDialog.value = false; + klinikRuangSearch.value = ""; +}; + +const buatAntreanKlinikRuang = (klinikRuang, ruang) => { + if (!currentProcessingPatient.value) return; + + const result = queueStore.createAntreanKlinikRuang( + klinikRuang, + ruang, + currentProcessingPatient.value, + "klinik" + ); + + snackbarText.value = result.message; + snackbarColor.value = result.success ? "success" : "error"; + snackbar.value = true; + + closeKlinikRuangDialog(); +};