Files

348 lines
12 KiB
JavaScript

// composables/useQueue.js
import { ref, computed } from "vue";
import { useQueueStore } from "../stores/queueStore";
import { useLoketStore } from "../stores/loketStore";
export const useQueue = (adminType = "loket", specificId = null) => {
const queueStore = useQueueStore();
const loketStore = useLoketStore();
// Normalize specificId to a reactive value
const idValue = computed(() => {
if (typeof specificId === 'function') return specificId();
if (specificId && typeof specificId === 'object' && 'value' in specificId) return specificId.value;
return specificId;
});
// Local state
const snackbar = ref(false);
const snackbarText = ref("");
const snackbarColor = ref("success");
// Dialog states
const showKlinikDialog = ref(false);
const showPenunjangDialog = ref(false);
const showChangeKlinikDialog = ref(false);
const klinikSearch = ref("");
const penunjangSearch = ref("");
const changeKlinikSearch = ref("");
const selectedPatientForPenunjang = ref(null);
// FIXED: Get stage patients once, then derive others from it
const stagePatients = computed(() => {
// Don't use .value here - getPatientsByStage returns a computed already
const patients = queueStore.getPatientsByStage(adminType);
return patients.value;
});
// Computed from store - isolated by specificId if provided
const currentProcessingPatient = computed(() => {
const key = idValue.value ? `${adminType}-${idValue.value}` : adminType;
return queueStore.currentProcessingPatient[key];
});
// Derive from stagePatients - ADD DEBUG LOGS
const diLoketPatients = computed(() => {
const patients = stagePatients.value.diLoket || [];
console.log('🔍 useQueue - diLoketPatients:', patients.length);
if (patients.length > 0) {
console.log('🔍 First diLoket patient:', patients[0]);
console.log('🔍 First diLoket patient fastTrack:', patients[0].fastTrack);
}
return patients;
});
const terlambatPatients = computed(() => {
const patients = stagePatients.value.terlambat || [];
console.log('🔍 useQueue - terlambatPatients:', patients.length);
return patients;
});
const pendingPatients = computed(() => {
const patients = stagePatients.value.pending || [];
console.log('🔍 useQueue - pendingPatients:', patients.length);
return patients;
});
const anjunganPatients = computed(() => stagePatients.value.anjungan || []);
// Pasien yang belum dipanggil (status "menunggu")
const menungguPatients = computed(() => stagePatients.value.menunggu || []);
const nextPatient = computed(() => {
// Prioritaskan pasien menunggu, baru anjungan
return menungguPatients.value[0] || anjunganPatients.value[0] || null;
});
// Total pasien hanya untuk stage admin ini
const totalPasien = computed(() => {
const total = queueStore.getTotalPasienByStage(adminType);
return total.value;
});
// Helper to check if a patient is relevant to the current admin view
const isPatientRelevant = (p) => {
if (p.processStage !== adminType) return false;
const targetId = idValue.value;
if (!targetId) return true; // No specific ID, show all for this stage
if (adminType === 'loket') {
const thisLoket = loketStore.getLoketById(parseInt(targetId));
if (!thisLoket) return String(p.loketId) === String(targetId);
// Must be assigned to this loket OR unassigned but clinic is served by this loket
const isAssignedToThis = p.loketId && String(p.loketId) === String(targetId);
if (isAssignedToThis) return true;
const isServedByThis = !p.loketId && thisLoket.pelayanan && Array.isArray(thisLoket.pelayanan) && thisLoket.pelayanan.includes(p.kodeKlinik);
return isServedByThis;
}
if (adminType === 'klinik') {
// Filter by clinic code or clinic name
return String(p.kodeKlinik) === String(targetId) || String(p.klinik) === String(targetId);
}
if (adminType === 'penunjang') {
// Penunjang usually uses clinic name as the identifier in seeds
return String(p.klinik) === String(targetId) || String(p.kodeKlinik) === String(targetId);
}
return true;
};
// 1. Menunggu Count (Backlog ONLY - Strictly status 'menunggu')
const menungguCount = computed(() => {
let list = menungguPatients.value || [];
return list.filter(isPatientRelevant).length;
});
// 2. Calculated Quota Used (Dynamic - any ticket NOT 'menunggu' counts as used)
const calculatedQuotaUsed = computed(() => {
const list = queueStore.allPatients || [];
return list.filter(p => {
if (!isPatientRelevant(p)) return false;
// Status is NOT 'menunggu' (meaning it's being served or finished)
return p.status !== 'menunggu';
}).length;
});
const quotaUsed = computed(() => calculatedQuotaUsed.value);
// Expose dev helper to refresh seed data
const resetPatients = () => queueStore.resetPatients();
// Filtered lists
const filteredKliniks = computed(() => {
let result = (queueStore.kliniks.value || queueStore.kliniks || []).filter(k => k.jenisLayanan === 'Reguler');
if (klinikSearch.value) {
result = result.filter((k) =>
k.name.toLowerCase().includes(klinikSearch.value.toLowerCase())
);
}
// Sort alphabetically by name
return [...result].sort((a, b) => (a.name || "").localeCompare(b.name || ""));
});
const filteredPenunjangs = computed(() => {
let result = queueStore.penunjangs;
if (penunjangSearch.value) {
result = result.filter((p) =>
p.name.toLowerCase().includes(penunjangSearch.value.toLowerCase())
);
}
// Sort alphabetically by name
return [...result].sort((a, b) => (a.name || "").localeCompare(b.name || ""));
});
const filteredChangeKliniks = computed(() => {
let result = (queueStore.kliniks.value || queueStore.kliniks || []).filter(k => k.jenisLayanan === 'Reguler');
if (changeKlinikSearch.value) {
result = result.filter((k) =>
k.name.toLowerCase().includes(changeKlinikSearch.value.toLowerCase())
);
}
// Sort alphabetically by name
return [...result].sort((a, b) => (a.name || "").localeCompare(b.name || ""));
});
// Methods
const showSnackbar = (text, color = "success") => {
snackbarText.value = text;
snackbarColor.value = color;
snackbar.value = true;
};
const callNext = (specificIdOverride = null) => {
const targetId = specificIdOverride || idValue.value;
const result = queueStore.callNext(adminType, targetId);
showSnackbar(result.message, result.success ? "success" : "warning");
};
const callMultiplePatients = (count, specificIdOverride = null) => {
const targetId = specificIdOverride || idValue.value;
const result = queueStore.callMultiplePatients(count, adminType, targetId);
showSnackbar(result.message, result.success ? "success" : "warning");
};
const processPatient = (patient, action) => {
const result = queueStore.processPatient(patient, action, adminType, idValue.value);
let color = "success";
if (action === "terlambat") color = "warning";
else if (action === "pending") color = "info";
showSnackbar(result.message, color);
};
// Helper function untuk mendapatkan pasien yang sedang diproses dari store
const getCurrentProcessingPatientFromStore = () => {
try {
const key = idValue.value ? `${adminType}-${idValue.value}` : adminType;
const processingPatient = queueStore.currentProcessingPatient?.[key];
if (!processingPatient) return null;
// Dapatkan data terbaru dari allPatients langsung (bukan dari getPatientsByStage yang sudah difilter)
// allPatients adalah ref yang di-export dari Pinia store, bisa diakses langsung atau dengan .value
// Coba akses langsung dulu, jika undefined baru coba dengan .value
let allPatients = [];
if (queueStore.allPatients) {
// Jika allPatients adalah ref (punya .value)
allPatients = Array.isArray(queueStore.allPatients)
? queueStore.allPatients
: (queueStore.allPatients.value || []);
}
// Cari pasien berdasarkan no, barcode, atau noAntrian
const latestPatient = allPatients.find(
p => (p && p.no === processingPatient.no) ||
(p && p.barcode && p.barcode === processingPatient.barcode) ||
(p && p.noAntrian && p.noAntrian === processingPatient.noAntrian)
);
// Jika ditemukan, return data terbaru, jika tidak return data dari currentProcessingPatient
return latestPatient || processingPatient;
} catch (error) {
console.error('Error in getCurrentProcessingPatientFromStore:', error);
// Fallback: return processingPatient dari store jika ada
return queueStore.currentProcessingPatient?.[adminType] || null;
}
};
const selectKlinik = (klinik) => {
// Pastikan currentProcessingPatient valid, jika tidak, coba dapatkan dari store
let patient = currentProcessingPatient.value || getCurrentProcessingPatientFromStore();
if (!patient) {
showSnackbar("Tidak ada pasien yang sedang diproses", "error");
showKlinikDialog.value = false;
return;
}
const result = queueStore.createAntreanKlinik(klinik, patient, adminType);
showSnackbar(result.message, "success");
showKlinikDialog.value = false;
};
const selectPenunjang = (penunjang) => {
// Pastikan currentProcessingPatient valid, jika tidak, coba dapatkan dari store
let patient = currentProcessingPatient.value || getCurrentProcessingPatientFromStore();
if (!patient) {
showSnackbar("Tidak ada pasien yang sedang diproses", "error");
showPenunjangDialog.value = false;
selectedPatientForPenunjang.value = null;
return;
}
const result = queueStore.createAntreanPenunjang(
penunjang,
patient,
adminType
);
showSnackbar(result.message, "success");
showPenunjangDialog.value = false;
selectedPatientForPenunjang.value = null;
};
const openPenunjangDialog = (patient = null) => {
selectedPatientForPenunjang.value = patient;
showPenunjangDialog.value = true;
};
const changeKlinik = (klinik) => {
// Pastikan currentProcessingPatient valid, jika tidak, coba dapatkan dari store
let patient = currentProcessingPatient.value || getCurrentProcessingPatientFromStore();
if (!patient) {
showSnackbar("Tidak ada pasien yang sedang diproses", "error");
showChangeKlinikDialog.value = false;
return { success: false };
}
const result = queueStore.changeKlinik(
patient,
klinik,
adminType,
idValue.value
);
showSnackbar(result.message, result.success ? "success" : "error");
showChangeKlinikDialog.value = false;
return result;
};
const processNextQueue = () => {
const result = queueStore.processNextQueue(adminType, idValue.value);
showSnackbar(result.message, result.success ? "success" : "warning");
};
const getRowClass = (item) => {
if (item.status === "current") {
return "text-success font-weight-bold";
}
return "";
};
return {
// State
snackbar,
snackbarText,
snackbarColor,
showKlinikDialog,
showPenunjangDialog,
showChangeKlinikDialog,
klinikSearch,
penunjangSearch,
changeKlinikSearch,
selectedPatientForPenunjang,
// Computed
currentProcessingPatient,
diLoketPatients,
terlambatPatients,
pendingPatients,
anjunganPatients,
menungguPatients,
nextPatient,
totalPasien,
quotaUsed,
filteredKliniks,
filteredPenunjangs,
filteredChangeKliniks,
stagePatients,
// Methods
showSnackbar,
callNext,
callMultiplePatients,
processPatient,
selectKlinik,
selectPenunjang,
openPenunjangDialog,
changeKlinik,
processNextQueue,
getRowClass,
};
};