push loket perbaikan sedang dilayani

This commit is contained in:
Fanrouver
2026-01-12 15:35:28 +07:00
parent 42fe62cb41
commit 48e4aabd4b
2 changed files with 205 additions and 90 deletions
+104 -59
View File
@@ -66,6 +66,7 @@
>
<div class="next-ticket-number">{{ ticket.noAntrian.split(' |')[0] }}</div>
<div class="next-ticket-klinik">{{ ticket.klinik || 'Klinik' }}</div>
<div v-if="ticket.loket" class="next-ticket-loket">{{ ticket.loket }}</div>
<div v-if="ticket.isMultipleCall && getTimerText(ticket)" class="next-ticket-timer">
<v-icon size="14" color="warning">mdi-timer</v-icon>
<span>{{ getTimerText(ticket) }}</span>
@@ -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;
}
+101 -31
View File
@@ -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') => {