// stores/queueStore.js import { defineStore } from 'pinia'; import { ref, computed } from 'vue'; import { useClinicStore } from './clinicStore'; export const useQueueStore = defineStore('queue', () => { const clinicStore = useClinicStore(); // Seed data for easy reset during dev const seedPatients = [ { no: 1, jamPanggil: "12:49", barcode: "250811100163", noAntrian: "UM1001 | Online - 250811100163", shift: "Shift 1", klinik: "KANDUNGAN", fastTrack: "YA", // Fast Track Patient pembayaran: "BPJS", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 2, jamPanggil: "10:52", barcode: "250811100155", noAntrian: "UM1002 | Online - 250811100155", shift: "Shift 1", klinik: "IPD", fastTrack: "TIDAK", pembayaran: "UMUM", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 3, jamPanggil: "09:30", barcode: "250811100200", noAntrian: "UM1003 | Online - 250811100200", shift: "Shift 1", klinik: "SARAF", fastTrack: "YA", // Fast Track Patient pembayaran: "BPJS", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 4, jamPanggil: "14:15", barcode: "250811100210", noAntrian: "UM1004 | Online - 250811100210", shift: "Shift 1", klinik: "THT", fastTrack: "TIDAK", pembayaran: "UMUM", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 5, jamPanggil: "12:49", barcode: "250811100163", noAntrian: "UM1005 | Online - 250811100163", shift: "Shift 2", klinik: "KANDUNGAN", fastTrack: "TIDAK", pembayaran: "UMUM", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 6, jamPanggil: "10:52", barcode: "250811100155", noAntrian: "UM1006 | Online - 250811100155", shift: "Shift 1", klinik: "IPD", fastTrack: "YA", // Fast Track Patient pembayaran: "BPJS", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 7, jamPanggil: "09:30", barcode: "250811100200", noAntrian: "UM1007 | Online - 250811100200", shift: "Shift 1", klinik: "SARAF", fastTrack: "TIDAK", pembayaran: "UMUM", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 8, jamPanggil: "14:15", barcode: "250811100210", noAntrian: "UM1008 | Online - 250811100210", shift: "Shift 1", klinik: "THT", fastTrack: "TIDAK", pembayaran: "UMUM", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 9, jamPanggil: "12:49", barcode: "250811100163", noAntrian: "UM1009 | Online - 250811100163", shift: "Shift 2", klinik: "KANDUNGAN", fastTrack: "YA", // Fast Track Patient pembayaran: "BPJS", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 10, jamPanggil: "10:52", barcode: "250811100155", noAntrian: "UM1010 | Online - 250811100155", shift: "Shift 1", klinik: "IPD", fastTrack: "TIDAK", pembayaran: "UMUM", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 11, jamPanggil: "09:30", barcode: "250811100200", noAntrian: "UM1011 | Online - 250811100200", shift: "Shift 1", klinik: "SARAF", fastTrack: "TIDAK", pembayaran: "UMUM", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, { no: 12, jamPanggil: "14:15", barcode: "250811100210", noAntrian: "UM1012 | Online - 250811100210", shift: "Shift 2", klinik: "THT", fastTrack: "YA", // Fast Track Patient pembayaran: "BPJS", status: "waiting", processStage: "loket", createdAt: new Date().toISOString(), }, ]; const cloneSeed = () => seedPatients.map(p => ({ ...p })); // State const allPatients = ref(cloneSeed()); const quotaUsed = ref(5); const currentProcessingPatient = ref({ loket: null, klinik: null, penunjang: null, }); // Daftar klinik untuk dropdown diambil 1 pintu dari clinicStore const kliniks = computed(() => { const baseList = typeof clinicStore.getClinicsForDropdown === 'function' ? clinicStore.getClinicsForDropdown() : []; // Bentuk objek disesuaikan dengan yang dipakai di useQueue (id, name, kode) return baseList.map((c) => ({ id: c.id, name: c.name, kode: c.kode, icon: c.icon, available: c.available, })); }); const penunjangs = ref([ { id: 1, name: "LABORATORIUM" }, { id: 2, name: "RADIOLOGI" }, { id: 3, name: "USG" }, { id: 4, name: "CT SCAN" }, { id: 5, name: "MRI" }, { id: 6, name: "EKG" }, { id: 7, name: "ENDOSKOPI" }, { id: 8, name: "FISIOTERAPI" }, { id: 9, name: "FARMASI" }, ]); // Computed - Filter berdasarkan process stage dan status const getPatientsByStage = (stage) => { return computed(() => { const patients = allPatients.value.filter(p => p.processStage === stage); // Debug log console.log(`getPatientsByStage(${stage}):`, patients.length, 'patients'); if (patients.length > 0) { console.log('Sample patient properties:', Object.keys(patients[0])); console.log('Sample fastTrack values:', patients.map(p => p.fastTrack)); } return { all: patients, waiting: patients.filter(p => p.status === 'waiting'), diLoket: patients.filter(p => p.status === 'di-loket'), terlambat: patients.filter(p => p.status === 'terlambat'), pending: patients.filter(p => p.status === 'pending'), }; }); }; // Total pasien per stage const getTotalPasienByStage = (stage) => { return computed(() => allPatients.value.filter(p => p.processStage === stage).length ); }; const totalPasien = computed(() => allPatients.value.length); const resetPatients = () => { allPatients.value = cloneSeed(); quotaUsed.value = 5; currentProcessingPatient.value = { loket: null, klinik: null, penunjang: null }; }; // Actions const callNext = (adminType = 'loket') => { const stageMap = { 'loket': 'loket', 'klinik': 'klinik', 'penunjang': 'penunjang' }; const targetStage = stageMap[adminType]; const nextPatient = allPatients.value.find(p => p.status === 'waiting' && p.processStage === targetStage ); if (!nextPatient) { return { success: false, message: "Tidak ada pasien selanjutnya" }; } if (quotaUsed.value >= 150) { return { success: false, message: "Quota sudah penuh" }; } // Update dengan cara yang Vue reactive const index = allPatients.value.findIndex(p => p.no === nextPatient.no); if (index !== -1) { allPatients.value[index] = { ...allPatients.value[index], status: "di-loket" }; } quotaUsed.value++; return { success: true, message: `Memanggil pasien ${nextPatient.noAntrian.split(" |")[0]}`, }; }; const callMultiplePatients = (count, adminType = 'loket') => { const stageMap = { 'loket': 'loket', 'klinik': 'klinik', 'penunjang': 'penunjang' }; const targetStage = stageMap[adminType]; const waitingList = allPatients.value.filter(p => p.status === 'waiting' && p.processStage === targetStage ); const patientsToCall = waitingList.slice(0, count); if (patientsToCall.length === 0) { return { success: false, message: "Tidak ada pasien yang menunggu" }; } if (quotaUsed.value + patientsToCall.length > 150) { return { success: false, message: "Quota tidak mencukupi" }; } // Update dengan cara yang Vue reactive patientsToCall.forEach((patient) => { const index = allPatients.value.findIndex(p => p.no === patient.no); if (index !== -1) { allPatients.value[index] = { ...allPatients.value[index], status: "di-loket" }; } }); quotaUsed.value += patientsToCall.length; return { success: true, message: `Memanggil ${patientsToCall.length} pasien ke loket`, }; }; const processPatient = (patient, action, adminType = 'loket') => { const patientCode = patient.noAntrian.split(" |")[0]; let message = ""; // Find patient index for reactive update const patientIndex = allPatients.value.findIndex(p => p.no === patient.no); if (patientIndex === -1) { return { success: false, message: "Pasien tidak ditemukan" }; } switch (action) { case "check-in": // Jika check-in di loket, pindahkan ke klinik dengan status di-loket if (adminType === 'loket') { allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "di-loket", processStage: "klinik" }; message = `Pasien ${patientCode} berhasil check in dan masuk ke Tabel Loket Klinik`; } // Jika check-in di klinik, selesai else if (adminType === 'klinik') { allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "processed" }; message = `Pasien ${patientCode} berhasil check in di Klinik`; } // Jika check-in di penunjang, selesai else if (adminType === 'penunjang') { allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "processed" }; message = `Pasien ${patientCode} berhasil check in di Penunjang`; } // Clear current processing di admin yang melakukan check-in if (currentProcessingPatient.value[adminType]?.no === patient.no) { currentProcessingPatient.value[adminType] = null; } break; case "terlambat": allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "terlambat" }; if (currentProcessingPatient.value[adminType]?.no === patient.no) { currentProcessingPatient.value[adminType] = null; } message = `Pasien ${patientCode} ditandai terlambat`; break; case "pending": allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "pending" }; if (currentProcessingPatient.value[adminType]?.no === patient.no) { currentProcessingPatient.value[adminType] = null; } message = `Pasien ${patientCode} di-pending`; break; case "aktifkan": const currentStatus = allPatients.value[patientIndex].status; if (currentStatus === "terlambat" || currentStatus === "pending") { // PERBAIKAN: Update dengan cara yang Vue reactive allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "di-loket" }; message = `Pasien ${patientCode} diaktifkan kembali dan masuk ke tabel Di Loket`; } else { message = `Pasien ${patientCode} tidak dapat diaktifkan (status: ${currentStatus})`; } break; case "proses": // Ambil data terbaru dari array currentProcessingPatient.value[adminType] = allPatients.value[patientIndex]; message = `Memproses pasien ${patientCode}`; break; } return { success: true, message }; }; const processNextQueue = (adminType = 'loket') => { const stageMap = { 'loket': 'loket', 'klinik': 'klinik', 'penunjang': 'penunjang' }; const targetStage = stageMap[adminType]; const nextPatient = allPatients.value.find(p => p.status === 'di-loket' && p.processStage === targetStage ); if (!nextPatient) { return { success: false, message: "Tidak ada pasien di loket yang dapat diproses" }; } // Set sebagai current processing patient currentProcessingPatient.value[adminType] = nextPatient; return { success: true, message: `Memproses pasien ${nextPatient.noAntrian.split(" |")[0]}`, }; }; const createAntreanKlinik = (klinik, patient = null, adminType = 'loket') => { const newNo = allPatients.value.length + 1; const timestamp = new Date(); const barcode = patient ? patient.barcode : `250811${String(timestamp.getTime()).slice(-6)}`; const newPatient = { no: newNo, jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String( timestamp.getMinutes() ).padStart(2, "0")}`, barcode: barcode, noAntrian: `KL${String(newNo).padStart(4, "0")} | Klinik - ${barcode}`, shift: "Shift 1", klinik: klinik.name, fastTrack: "TIDAK", pembayaran: patient ? patient.pembayaran : "UMUM", status: "di-loket", processStage: "klinik", createdAt: timestamp.toISOString(), referencePatient: patient ? patient.noAntrian : null, }; allPatients.value.push(newPatient); return { success: true, message: `Antrean klinik ${klinik.name} berhasil dibuat dan masuk ke Tabel Loket Klinik`, patient: newPatient, }; }; const createAntreanPenunjang = (penunjang, patient = null, adminType = 'loket') => { const newNo = allPatients.value.length + 1; const timestamp = new Date(); const barcode = patient ? patient.barcode : `250811${String(timestamp.getTime()).slice(-6)}`; const newPatient = { no: newNo, jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String( timestamp.getMinutes() ).padStart(2, "0")}`, barcode: barcode, noAntrian: `PN${String(newNo).padStart(4, "0")} | Penunjang - ${barcode}`, shift: "Shift 1", klinik: penunjang.name, fastTrack: "TIDAK", pembayaran: patient ? patient.pembayaran : "UMUM", status: "di-loket", processStage: "penunjang", createdAt: timestamp.toISOString(), referencePatient: patient ? patient.noAntrian : null, }; allPatients.value.push(newPatient); return { success: true, message: `Antrean penunjang ${penunjang.name} berhasil dibuat dan masuk ke Tabel Loket Penunjang`, patient: newPatient, }; }; const changeKlinik = (patient, newKlinik, adminType = 'loket') => { const patientIndex = allPatients.value.findIndex((p) => p.no === patient.no); if (patientIndex !== -1) { // Update dengan cara yang Vue reactive allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], klinik: newKlinik.name }; // Update current processing if it's the same patient if (currentProcessingPatient.value[adminType]?.no === patient.no) { currentProcessingPatient.value[adminType] = { ...currentProcessingPatient.value[adminType], klinik: newKlinik.name }; } return { success: true, message: `Klinik berhasil diubah ke ${newKlinik.name}`, }; } return { success: false, message: "Pasien tidak ditemukan" }; }; const getCurrentProcessing = (adminType) => { return computed(() => currentProcessingPatient.value[adminType]); }; const setCurrentProcessing = (patient, adminType) => { currentProcessingPatient.value[adminType] = patient; }; return { // State allPatients, quotaUsed, currentProcessingPatient, kliniks, penunjangs, // Computed totalPasien, // Actions callNext, callMultiplePatients, processPatient, createAntreanKlinik, createAntreanPenunjang, changeKlinik, processNextQueue, getPatientsByStage, getTotalPasienByStage, getCurrentProcessing, setCurrentProcessing, }; });