diff --git a/pages/Anjungan/AntrianLoket/[id].vue b/pages/Anjungan/AntrianLoket/[id].vue
index 60a21fe..8e2bb3f 100644
--- a/pages/Anjungan/AntrianLoket/[id].vue
+++ b/pages/Anjungan/AntrianLoket/[id].vue
@@ -66,6 +66,7 @@
>
{{ ticket.noAntrian.split(' |')[0] }}
{{ ticket.klinik || 'Klinik' }}
+ {{ ticket.loket }}
mdi-timer
{{ getTimerText(ticket) }}
@@ -373,10 +374,20 @@ const displayedClinics = computed(() => {
// Flatten multiple call groups untuk mendapatkan semua multiple calls
const allMultipleCalls = multipleCallGroups.flat()
- // Current queue adalah yang sedang dilayani (prioritas: di-loket, lalu waiting yang paling lama)
- const currentQueue = sortedQueues.find(q => q.status === 'di-loket') ||
- sortedQueues.find(q => q.status === 'waiting') ||
- null
+ // Current queue adalah yang sedang dilayani untuk klinik ini
+ // Hanya ambil yang status 'di-loket' (yang sedang dilayani)
+ let currentQueue = null
+
+ // Cari yang sedang di-loket (sedang dilayani)
+ const diLoketQueues = sortedQueues.filter(q => q.status === 'di-loket')
+ if (diLoketQueues.length > 0) {
+ // Ambil yang paling lama (berdasarkan waktu check-in atau createdAt)
+ currentQueue = diLoketQueues.sort((a, b) => {
+ const checkInA = getCheckInTime(a)
+ const checkInB = getCheckInTime(b)
+ return checkInA - checkInB // Yang lebih lama lebih dulu
+ })[0]
+ }
// Multiple calls untuk display - semua yang dipanggil bersamaan (termasuk yang sudah di-loket)
const multipleCalls = allMultipleCalls.filter(q =>
@@ -508,9 +519,22 @@ const getTimerText = (queue) => {
const _ = currentTime.value // Force reactivity
- const callTime = queue.lastCalledAt ? new Date(queue.lastCalledAt) : (queue.createdAt ? new Date(queue.createdAt) : null)
+ // Gunakan lastCalledAt atau pendingCallAt untuk waktu panggilan
+ const callTime = queue.lastCalledAt ? new Date(queue.lastCalledAt) :
+ (queue.pendingCallAt ? new Date(queue.pendingCallAt) :
+ (queue.createdAt ? new Date(queue.createdAt) : null))
if (!callTime) return ''
+ // Jika status pending-call, hitung waktu sampai dipanggil (5 detik)
+ if (queue.status === 'pending-call') {
+ const elapsed = Date.now() - callTime.getTime()
+ const remaining = Math.max(0, 5000 - elapsed)
+ if (remaining <= 0) return ''
+ const seconds = Math.ceil(remaining / 1000)
+ return `Dipanggil dalam ${seconds}s`
+ }
+
+ // Jika sudah dipanggil, hitung waktu text-to-speech (15 detik)
const ttsDuration = 15000 // 15 detik
const elapsed = Date.now() - callTime.getTime()
const remaining = Math.max(0, ttsDuration - elapsed)
@@ -521,14 +545,72 @@ const getTimerText = (queue) => {
return `${seconds}s`
}
-// Next 5 tickets to be called (tercepat)
+// Next 5 tickets to be called (untuk urutan text-to-speech ketika ada multiple loket memanggil bersamaan)
const nextTicketsToCall = computed(() => {
- // Ambil semua antrian dengan status 'menunggu' (belum dipanggil) atau 'waiting' (sudah dipanggil tapi belum check-in)
- // Prioritaskan yang belum dipanggil (menunggu) dulu
+ // Ambil semua antrian dari SEMUA loket yang memiliki status 'waiting' atau 'pending-call' (sudah dipanggil atau akan dipanggil)
+ // Ini untuk menampilkan urutan text-to-speech ketika ada 2+ loket memanggil bersamaan
+ const allWaitingFromAllLokets = queueStore.allPatients.filter(p =>
+ (p.status === 'waiting' || p.status === 'pending-call') && (p.lastCalledAt || p.pendingCallAt)
+ )
+
+ // Group berdasarkan waktu panggilan (dalam 5 detik = multiple calls)
+ const callGroups = []
+ const processed = new Set()
+
+ allWaitingFromAllLokets.forEach((queue) => {
+ if (processed.has(queue.no)) return
+
+ // Gunakan lastCalledAt atau pendingCallAt untuk waktu panggilan
+ const callTime = new Date(queue.lastCalledAt || queue.pendingCallAt)
+ const group = [queue]
+ processed.add(queue.no)
+
+ // Cari semua tiket yang dipanggil dalam 5 detik
+ allWaitingFromAllLokets.forEach((otherQueue) => {
+ if (!processed.has(otherQueue.no) && otherQueue.no !== queue.no) {
+ const otherCallTime = new Date(otherQueue.lastCalledAt || otherQueue.pendingCallAt)
+ const timeDiff = Math.abs(callTime - otherCallTime)
+ if (timeDiff <= 5000) {
+ group.push(otherQueue)
+ processed.add(otherQueue.no)
+ }
+ }
+ })
+
+ if (group.length > 1) {
+ // Sort dalam group berdasarkan waktu panggilan (siapa yang dipanggil duluan)
+ group.sort((a, b) => {
+ const timeA = new Date(a.lastCalledAt || a.pendingCallAt)
+ const timeB = new Date(b.lastCalledAt || b.pendingCallAt)
+ return timeA - timeB
+ })
+ callGroups.push(...group)
+ }
+ })
+
+ // Jika ada multiple calls, ambil 5 teratas dari yang dipanggil bersamaan
+ // Urutkan berdasarkan waktu panggilan untuk text-to-speech
+ if (callGroups.length > 0) {
+ // Ambil 5 teratas dari multiple calls, urutkan berdasarkan waktu panggilan
+ const sortedMultipleCalls = callGroups
+ .sort((a, b) => {
+ const timeA = new Date(a.lastCalledAt || a.pendingCallAt)
+ const timeB = new Date(b.lastCalledAt || b.pendingCallAt)
+ return timeA - timeB
+ })
+ .slice(0, 5)
+
+ return sortedMultipleCalls.map(queue => ({
+ ...queue,
+ isMultipleCall: true,
+ klinik: queue.klinik || 'Klinik',
+ loket: queue.loket || 'Loket'
+ }))
+ }
+
+ // Jika tidak ada multiple calls, tampilkan 5 tiket tercepat yang akan dipanggil
const menungguQueues = loketPatients.value.filter(p => p.status === 'menunggu')
const waitingQueues = loketPatients.value.filter(p => p.status === 'waiting')
-
- // Gabungkan: menunggu dulu, baru waiting
const allWaitingQueues = [...menungguQueues, ...waitingQueues]
// Filter berdasarkan pelayanan loket jika ada
@@ -555,81 +637,37 @@ const nextTicketsToCall = computed(() => {
}
}
- // Sort berdasarkan prioritas:
- // 1. Status (menunggu lebih dulu dari waiting)
- // 2. Waktu generate tiket (createdAt) - yang lebih lama lebih dulu
- // 3. Urutan tiket (no) - yang lebih kecil lebih dulu
- // 4. Waktu check-in - yang lebih lama lebih dulu
+ // Sort berdasarkan prioritas
const sortedQueues = filteredQueues.sort((a, b) => {
- // Prioritas 1: Status (menunggu > waiting)
const statusPriority = { 'menunggu': 1, 'waiting': 2 }
const statusDiff = (statusPriority[a.status] || 99) - (statusPriority[b.status] || 99)
if (statusDiff !== 0) return statusDiff
- // Prioritas 2: Waktu generate tiket
const createdAtA = new Date(a.createdAt || 0)
const createdAtB = new Date(b.createdAt || 0)
if (createdAtA.getTime() !== createdAtB.getTime()) {
return createdAtA - createdAtB
}
- // Prioritas 3: Urutan tiket
if (a.no !== b.no) {
return a.no - b.no
}
- // Prioritas 4: Waktu check-in
const checkInA = getCheckInTime(a)
const checkInB = getCheckInTime(b)
return checkInA - checkInB
})
- // Deteksi multiple calls (antrian yang dipanggil bersamaan dalam 5 detik)
- const multipleCallGroups = []
- const processed = new Set()
-
- sortedQueues.forEach((queue, index) => {
- if (processed.has(queue.no)) return
-
- const callTime = queue.lastCalledAt ? new Date(queue.lastCalledAt) : null
- if (callTime) {
- const group = [queue]
- processed.add(queue.no)
-
- sortedQueues.forEach((otherQueue, otherIndex) => {
- if (otherIndex !== index && !processed.has(otherQueue.no)) {
- const otherCallTime = otherQueue.lastCalledAt ? new Date(otherQueue.lastCalledAt) : null
- if (otherCallTime) {
- const timeDiff = Math.abs(callTime - otherCallTime)
- if (timeDiff <= 5000) {
- group.push(otherQueue)
- processed.add(otherQueue.no)
- }
- }
- }
- })
-
- if (group.length > 1) {
- multipleCallGroups.push(group)
- }
- }
- })
-
- // Flatten multiple call groups
- const allMultipleCalls = multipleCallGroups.flat()
-
- // Exclude current called queue dari next tickets
+ // Exclude current called queue
const currentCalledNo = currentCalledQueue.value?.no
const filteredForNext = sortedQueues.filter(q => q.no !== currentCalledNo)
- // Ambil 5 teratas dan mark yang multiple call
- const next5 = filteredForNext.slice(0, 5).map(queue => ({
+ // Ambil 5 teratas
+ return filteredForNext.slice(0, 5).map(queue => ({
...queue,
- isMultipleCall: allMultipleCalls.some(mc => mc.no === queue.no),
+ isMultipleCall: false,
klinik: queue.klinik || 'Klinik'
}))
-
- return next5
})
// Get grid style based on queue count - semakin sedikit antrian, semakin besar ukurannya
@@ -941,6 +979,13 @@ onUnmounted(() => {
font-size: 14px;
font-weight: 600;
color: var(--color-neutral-600);
+ margin-bottom: 4px;
+}
+
+.next-ticket-loket {
+ font-size: 12px;
+ font-weight: 500;
+ color: var(--color-primary-600);
margin-bottom: 6px;
}
diff --git a/stores/queueStore.js b/stores/queueStore.js
index 2b14dd6..791a08b 100644
--- a/stores/queueStore.js
+++ b/stores/queueStore.js
@@ -365,22 +365,55 @@ export const useQueueStore = defineStore('queue', () => {
return { success: false, message: "Tidak ada pasien yang menunggu untuk dipanggil" };
}
- // Update status dari 'menunggu' menjadi 'waiting' (sudah dipanggil, bisa check-in)
- // Track waktu panggilan untuk multiple calls detection
- const callTimestamp = new Date().toISOString();
- const index = allPatients.value.findIndex(p => p.no === nextPatient.no);
- if (index !== -1) {
- allPatients.value[index] = {
- ...allPatients.value[index],
- status: "waiting",
- lastCalledAt: callTimestamp // Track waktu panggilan untuk multiple calls
+ // Untuk adminType 'loket', tambahkan delay 5 detik sebelum status berubah
+ // Ini untuk simulasi multiple loket memanggil bersamaan
+ if (adminType === 'loket') {
+ // Set status pending call dengan timestamp
+ const pendingCallTimestamp = new Date().toISOString();
+ const index = allPatients.value.findIndex(p => p.no === nextPatient.no);
+ if (index !== -1) {
+ allPatients.value[index] = {
+ ...allPatients.value[index],
+ status: "pending-call", // Status sementara sebelum delay
+ pendingCallAt: pendingCallTimestamp
+ };
+ }
+
+ // Set timeout 5 detik untuk mengubah status menjadi 'waiting'
+ setTimeout(() => {
+ const patientIndex = allPatients.value.findIndex(p => p.no === nextPatient.no);
+ if (patientIndex !== -1 && allPatients.value[patientIndex].status === 'pending-call') {
+ const callTimestamp = new Date().toISOString();
+ allPatients.value[patientIndex] = {
+ ...allPatients.value[patientIndex],
+ status: "waiting",
+ lastCalledAt: callTimestamp, // Track waktu panggilan untuk multiple calls
+ pendingCallAt: undefined // Clear pending call timestamp
+ };
+ }
+ }, 5000); // Delay 5 detik
+
+ return {
+ success: true,
+ message: `Memanggil pasien ${nextPatient.noAntrian.split(" |")[0]} (akan dipanggil dalam 5 detik)`,
+ };
+ } else {
+ // Untuk adminType selain loket, langsung update status
+ const callTimestamp = new Date().toISOString();
+ const index = allPatients.value.findIndex(p => p.no === nextPatient.no);
+ if (index !== -1) {
+ allPatients.value[index] = {
+ ...allPatients.value[index],
+ status: "waiting",
+ lastCalledAt: callTimestamp // Track waktu panggilan untuk multiple calls
+ };
+ }
+
+ return {
+ success: true,
+ message: `Memanggil pasien ${nextPatient.noAntrian.split(" |")[0]}`,
};
}
-
- return {
- success: true,
- message: `Memanggil pasien ${nextPatient.noAntrian.split(" |")[0]}`,
- };
};
const callMultiplePatients = (count, adminType = 'loket') => {
@@ -426,24 +459,61 @@ export const useQueueStore = defineStore('queue', () => {
const patientsToCall = combinedList.slice(0, maxCallable);
- // Update status dari 'menunggu' menjadi 'waiting' (sudah dipanggil, bisa check-in)
- // Track waktu panggilan untuk multiple calls detection - semua dipanggil dengan timestamp yang sama
- const callTimestamp = new Date().toISOString();
- patientsToCall.forEach((patient) => {
- const index = allPatients.value.findIndex(p => p.no === patient.no);
- if (index !== -1) {
- allPatients.value[index] = {
- ...allPatients.value[index],
- status: "waiting",
- lastCalledAt: callTimestamp // Track waktu panggilan untuk multiple calls
- };
- }
- });
+ // Untuk adminType 'loket', tambahkan delay 5 detik sebelum status berubah
+ // Ini untuk simulasi multiple loket memanggil bersamaan
+ if (adminType === 'loket') {
+ // Set status pending call dengan timestamp
+ const pendingCallTimestamp = new Date().toISOString();
+ patientsToCall.forEach((patient) => {
+ const index = allPatients.value.findIndex(p => p.no === patient.no);
+ if (index !== -1) {
+ allPatients.value[index] = {
+ ...allPatients.value[index],
+ status: "pending-call", // Status sementara sebelum delay
+ pendingCallAt: pendingCallTimestamp
+ };
+ }
+ });
+
+ // Set timeout 5 detik untuk mengubah status menjadi 'waiting'
+ setTimeout(() => {
+ const callTimestamp = new Date().toISOString();
+ patientsToCall.forEach((patient) => {
+ const patientIndex = allPatients.value.findIndex(p => p.no === patient.no);
+ if (patientIndex !== -1 && allPatients.value[patientIndex].status === 'pending-call') {
+ allPatients.value[patientIndex] = {
+ ...allPatients.value[patientIndex],
+ status: "waiting",
+ lastCalledAt: callTimestamp, // Track waktu panggilan untuk multiple calls
+ pendingCallAt: undefined // Clear pending call timestamp
+ };
+ }
+ });
+ }, 5000); // Delay 5 detik
+
+ return {
+ success: true,
+ message: `Memanggil ${patientsToCall.length} pasien ke loket (akan dipanggil dalam 5 detik)`,
+ };
+ } else {
+ // Untuk adminType selain loket, langsung update status
+ const callTimestamp = new Date().toISOString();
+ patientsToCall.forEach((patient) => {
+ const index = allPatients.value.findIndex(p => p.no === patient.no);
+ if (index !== -1) {
+ allPatients.value[index] = {
+ ...allPatients.value[index],
+ status: "waiting",
+ lastCalledAt: callTimestamp // Track waktu panggilan untuk multiple calls
+ };
+ }
+ });
- return {
- success: true,
- message: `Memanggil ${patientsToCall.length} pasien ke loket`,
- };
+ return {
+ success: true,
+ message: `Memanggil ${patientsToCall.length} pasien ke loket`,
+ };
+ }
};
const processPatient = (patient, action, adminType = 'loket') => {