push loket perbaikan sedang dilayani
This commit is contained in:
@@ -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
@@ -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') => {
|
||||
|
||||
Reference in New Issue
Block a user