+
+
+
+
+
+
TINDAKAN
+
+
SEKARANG
+
+ {{ ruang.tindakan.currentQueue.noAntrian.split(' |')[0] }}
+
+
+
+
SELANJUTNYA
+
+
+ {{ queue.noAntrian.split(' |')[0] }}
+
+
+
+
+
mdi-clock-outline
+
Tidak Ada Antrian
+
+
+
+
+
mdi-clock-outline
Tidak Ada Antrian
@@ -181,7 +216,11 @@ const currentDate = ref('')
let timeInterval = null
const klinikPatients = computed(() => {
- return queueStore.getPatientsByStage('klinik').value.all
+ // Get patients from klinik-ruang stage (created from AdminKlinikRuang)
+ return queueStore.allPatients.filter(p =>
+ p.processStage === 'klinik-ruang' &&
+ p.kodeKlinik === kodeKlinik.value
+ )
})
// Get ruang list for this specific klinik
@@ -221,8 +260,9 @@ const displayedRuang = computed(() => {
}
return ruangListForKlinik.value.map(ruang => {
- const queues = klinikPatients.value
- .filter(p => p.klinik === ruang.namaKlinik && (p.ruang === ruang.namaRuang || !p.ruang))
+ // Get queues for this specific room
+ const allQueues = klinikPatients.value
+ .filter(p => p.nomorRuang === ruang.nomorRuang)
.sort((a, b) => {
const statusPriority = {
'di-loket': 1,
@@ -234,18 +274,28 @@ const displayedRuang = computed(() => {
const priorityDiff = (statusPriority[a.status] || 99) - (statusPriority[b.status] || 99)
if (priorityDiff !== 0) return priorityDiff
- const timeA = a.jamPanggil.split(':').map(Number)
- const timeB = b.jamPanggil.split(':').map(Number)
- return timeA[0] * 60 + timeA[1] - (timeB[0] * 60 + timeB[1])
+ // Sort by queue number
+ const numA = parseInt(a.noAntrian.match(/\d+/)?.[0] || '999')
+ const numB = parseInt(b.noAntrian.match(/\d+/)?.[0] || '999')
+ return numA - numB
})
- const currentQueue = queues.find(q => q.status === 'di-loket') ||
- (queues.find(q => q.status === 'waiting') || null)
-
- const nextQueues = queues.filter(q =>
- q.no !== currentQueue?.no &&
- (q.status === 'waiting' || q.status === 'di-loket')
- )
+ // Split by tipeLayanan
+ const pemeriksaanAwalQueues = allQueues.filter(q => q.tipeLayanan === 'Pemeriksaan Awal')
+ const tindakanQueues = allQueues.filter(q => q.tipeLayanan === 'Tindakan')
+
+ // Get current and next queues for each tipeLayanan
+ const getCurrentAndNext = (queues) => {
+ const current = queues.find(q => q.status === 'di-loket') || null
+ const next = queues.filter(q =>
+ q.no !== current?.no &&
+ (q.status === 'waiting' || q.status === 'di-loket')
+ ).slice(0, 3) // Show max 3 next queues
+ return { current, next }
+ }
+
+ const pemeriksaanAwal = getCurrentAndNext(pemeriksaanAwalQueues)
+ const tindakan = getCurrentAndNext(tindakanQueues)
return {
kodeKlinik: ruang.kodeKlinik,
@@ -253,9 +303,17 @@ const displayedRuang = computed(() => {
nomorRuang: ruang.nomorRuang,
namaRuang: ruang.namaRuang,
nomorScreen: ruang.nomorScreen,
- currentQueue: currentQueue ? { ...currentQueue, ruang: ruang.namaRuang } : null,
- nextQueues: nextQueues,
- totalQueues: queues.length
+ pemeriksaanAwal: {
+ currentQueue: pemeriksaanAwal.current,
+ nextQueues: pemeriksaanAwal.next,
+ totalQueues: pemeriksaanAwalQueues.length
+ },
+ tindakan: {
+ currentQueue: tindakan.current,
+ nextQueues: tindakan.next,
+ totalQueues: tindakanQueues.length
+ },
+ totalQueues: allQueues.length
}
})
})
@@ -263,16 +321,16 @@ const displayedRuang = computed(() => {
const currentCalledQueue = computed(() => {
if (!kodeKlinik.value || !klinikData.value) return null
+ // Get most recently called patient (status di-loket)
const calledQueues = klinikPatients.value
- .filter(p => p.status === 'di-loket' && p.klinik === klinikData.value.namaKlinik)
+ .filter(p => p.status === 'di-loket')
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
if (calledQueues.length > 0) {
const patient = calledQueues[0]
- const ruang = ruangListForKlinik.value.find(r => r.namaKlinik === patient.klinik)
return {
...patient,
- ruang: ruang ? ruang.namaRuang : ruangListForKlinik.value[0]?.namaRuang || 'Ruang 1'
+ ruang: patient.ruang || patient.namaRuang || 'Ruang 1'
}
}
@@ -284,7 +342,7 @@ const statistics = computed(() => {
return { total: 0, waiting: 0, active: 0 }
}
- const all = klinikPatients.value.filter(p => p.klinik === klinikData.value.namaKlinik)
+ const all = klinikPatients.value
return {
total: all.length,
waiting: all.filter(p => p.status === 'waiting').length,
@@ -516,6 +574,31 @@ onUnmounted(() => {
background: var(--color-success-100);
}
+.tipe-layanan-section {
+ margin-bottom: 8px;
+}
+
+.tipe-label {
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--color-success-700);
+ margin-bottom: 8px;
+ letter-spacing: 0.5px;
+ text-transform: uppercase;
+}
+
+.empty-state-small {
+ text-align: center;
+ padding: 16px 0;
+}
+
+.empty-text-small {
+ font-size: 12px;
+ color: var(--color-neutral-600);
+ margin-top: 8px;
+ font-weight: 500;
+}
+
.current-section {
text-align: center;
margin-bottom: 12px;
diff --git a/stores/navItems1.ts b/stores/navItems1.ts
index 68e6bfb..e7149f4 100644
--- a/stores/navItems1.ts
+++ b/stores/navItems1.ts
@@ -25,36 +25,37 @@ const defaultNavItems: NavItem[] = [
},
{ id: 4, name: "Admin Loket", icon: "mdi-account-supervisor-outline", path: "/AdminLoket" },
{ id: 5, name: "Admin Klinik", icon: "mdi-text-box-plus-outline", path: "/AdminKlinik" },
- { id: 6, name: "Admin Penunjang", icon: "mdi-plus-box-outline", path: "/AdminPenunjang" },
- { id: 7, name: "Buat Antrean", icon: "mdi-account-multiple-plus-outline", path: "/BuatAntrean" },
- { id: 8, name: "Monitoring Pasien", icon: "mdi-account-group-outline", path: "/MonitoringPasien/monitoringPasien" },
+ { id: 6, name: "Admin Klinik Ruang", icon: "mdi-door-open", path: "/AdminKlinikRuang" },
+ { id: 7, name: "Admin Penunjang", icon: "mdi-plus-box-outline", path: "/AdminPenunjang" },
+ { id: 8, name: "Buat Antrean", icon: "mdi-account-multiple-plus-outline", path: "/BuatAntrean" },
+ { id: 9, name: "Monitoring Pasien", icon: "mdi-account-group-outline", path: "/MonitoringPasien/monitoringPasien" },
{
- id: 9,
+ id: 10,
name: "Layar Informasi",
icon: "mdi-monitor-multiple",
path: "",
children: [
- { id: 10, name: "Anjungan", path: "/anjungan/anjungan", icon: "mdi-circle-small" },
- { id: 11, name: "Klinik", path: "/anjungan/AntrianKlinik", icon: "mdi-circle-small" },
- { id: 12, name: "Klinik Ruang", path: "/anjungan/AntrianKlinikRuang", icon: "mdi-circle-small"},
- { id: 13, name: "Penunjang", path: "/anjungan/AntrianPenunjang", icon: "mdi-circle-small"},
+ { id: 11, name: "Anjungan", path: "/anjungan/anjungan", icon: "mdi-circle-small" },
+ { id: 12, name: "Klinik", path: "/anjungan/AntrianKlinik", icon: "mdi-circle-small" },
+ { id: 13, name: "Klinik Ruang", path: "/anjungan/AntrianKlinikRuang", icon: "mdi-circle-small"},
+ { id: 14, name: "Penunjang", path: "/anjungan/AntrianPenunjang", icon: "mdi-circle-small"},
],
},
{
- id: 14,
+ id: 15,
name: "Master Data",
icon: "mdi-cog-outline",
path: "",
children: [
- { id: 15, name: "Hak Akses", path: "/setting/HakAkses", icon: "mdi-circle-small" },
- { id: 16, name: "User Login", path: "/setting/UserLogin", icon: "mdi-circle-small" },
- { id: 17, name: "Master Anjungan", path: "/setting/masteranjungan", icon: "mdi-circle-small" },
- { id: 18, name: "Master Loket", path: "/setting/masterloket", icon: "mdi-circle-small" },
- { id: 19, name: "Master Klinik", path: "/setting/masterklinik", icon: "mdi-circle-small" },
- { id: 20, name: "Master Klinik Ruang", path: "/setting/masterklinikruang", icon: "mdi-circle-small" },
- { id: 21, name: "Master Penunjang", path: "/setting/masterpenunjang", icon: "mdi-circle-small" },
- { id: 22, name: "Screen", path: "/setting/screen", icon: "mdi-circle-small" },
+ { id: 16, name: "Hak Akses", path: "/setting/HakAkses", icon: "mdi-circle-small" },
+ { id: 17, name: "User Login", path: "/setting/UserLogin", icon: "mdi-circle-small" },
+ { id: 18, name: "Master Anjungan", path: "/setting/masteranjungan", icon: "mdi-circle-small" },
+ { id: 19, name: "Master Loket", path: "/setting/masterloket", icon: "mdi-circle-small" },
+ { id: 20, name: "Master Klinik", path: "/setting/masterklinik", icon: "mdi-circle-small" },
+ { id: 21, name: "Master Klinik Ruang", path: "/setting/masterklinikruang", icon: "mdi-circle-small" },
+ { id: 22, name: "Master Penunjang", path: "/setting/masterpenunjang", icon: "mdi-circle-small" },
+ { id: 23, name: "Screen", path: "/setting/screen", icon: "mdi-circle-small" },
],
},
];
diff --git a/stores/queueStore.js b/stores/queueStore.js
index e00652b..bd3184e 100644
--- a/stores/queueStore.js
+++ b/stores/queueStore.js
@@ -564,6 +564,246 @@ export const useQueueStore = defineStore('queue', () => {
};
};
+ // Scan barcode dan generate antrean klinik ruang baru
+ const scanAndCreateAntreanKlinikRuang = (barcodeInput, klinikRuang, ruang, tipeLayanan = 'Pemeriksaan Awal') => {
+ // Clean input - remove whitespace and handle prefix letters
+ const cleanInput = String(barcodeInput).trim().toUpperCase();
+ // Remove leading letters if any (e.g., "J200730100005" -> "200730100005")
+ const numericInput = cleanInput.replace(/^[A-Z]+/, '');
+
+ // Find patient by barcode or noAntrian
+ const sourcePatient = allPatients.value.find(p => {
+ // Exact barcode match
+ if (p.barcode === cleanInput || p.barcode === numericInput) return true;
+
+ // Check if noAntrian includes the input
+ const noAntrianUpper = (p.noAntrian || '').toUpperCase();
+ if (noAntrianUpper.includes(cleanInput) || noAntrianUpper.includes(numericInput)) return true;
+
+ // Try extracting number from noAntrian
+ const noAntrianNumber = noAntrianUpper.match(/([A-Z]+)(\d+)/);
+ if (noAntrianNumber) {
+ const extractedNumber = noAntrianNumber[2];
+ if (extractedNumber.includes(numericInput) || numericInput.includes(extractedNumber)) return true;
+ }
+
+ return false;
+ });
+
+ if (!sourcePatient) {
+ return { success: false, message: "Pasien tidak ditemukan. Pastikan barcode/nomor antrian benar." };
+ }
+
+ // Check if patient already has antrean klinik ruang for this room
+ const existingAntrean = allPatients.value.find(p =>
+ p.referencePatient === sourcePatient.noAntrian &&
+ p.kodeKlinik === klinikRuang.kodeKlinik &&
+ p.nomorRuang === ruang.nomorRuang &&
+ p.processStage === 'klinik-ruang'
+ );
+
+ if (existingAntrean) {
+ return {
+ success: false,
+ message: `Pasien sudah memiliki antrean di ${klinikRuang.namaKlinik} Ruang ${ruang.nomorRuang}`
+ };
+ }
+
+ // Generate queue number for this specific room and tipeLayanan
+ const roomQueues = allPatients.value.filter(p =>
+ p.kodeKlinik === klinikRuang.kodeKlinik &&
+ p.nomorRuang === ruang.nomorRuang &&
+ p.tipeLayanan === tipeLayanan &&
+ p.processStage === 'klinik-ruang'
+ );
+
+ const queueNumber = roomQueues.length + 1;
+ const prefix = tipeLayanan === 'Pemeriksaan Awal' ? 'PA' : 'TD';
+ const newNo = allPatients.value.length + 1;
+ const timestamp = new Date();
+
+ const newPatient = {
+ no: newNo,
+ jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String(
+ timestamp.getMinutes()
+ ).padStart(2, "0")}`,
+ barcode: sourcePatient.barcode,
+ noAntrian: `${prefix}${String(queueNumber).padStart(3, "0")} | ${klinikRuang.namaKlinik} - ${ruang.namaRuang} - ${tipeLayanan}`,
+ noAntrianRuang: `${klinikRuang.namaKlinik} - ${ruang.namaRuang} | ${prefix}${String(queueNumber).padStart(3, "0")}`,
+ shift: sourcePatient.shift || "Shift 1",
+ klinik: klinikRuang.namaKlinik,
+ ruang: ruang.namaRuang,
+ kodeKlinik: klinikRuang.kodeKlinik,
+ nomorRuang: ruang.nomorRuang,
+ nomorScreen: ruang.nomorScreen,
+ tipeLayanan: tipeLayanan,
+ fastTrack: sourcePatient.fastTrack || "TIDAK",
+ pembayaran: sourcePatient.pembayaran || "UMUM",
+ status: "waiting",
+ processStage: "klinik-ruang",
+ createdAt: timestamp.toISOString(),
+ referencePatient: sourcePatient.noAntrian,
+ sourcePatientNo: sourcePatient.no,
+ };
+
+ allPatients.value.push(newPatient);
+
+ return {
+ success: true,
+ message: `Antrean ${tipeLayanan} berhasil dibuat: ${newPatient.noAntrian.split(" |")[0]} untuk ${klinikRuang.namaKlinik} Ruang ${ruang.nomorRuang}`,
+ patient: newPatient,
+ };
+ };
+
+ // Get patients by klinik and ruang
+ const getPatientsByKlinikRuang = (kodeKlinik, nomorRuang, tipeLayanan = null) => {
+ return computed(() => {
+ let patients = allPatients.value.filter(p =>
+ p.kodeKlinik === kodeKlinik &&
+ p.nomorRuang === nomorRuang &&
+ p.processStage === 'klinik-ruang'
+ );
+
+ if (tipeLayanan) {
+ patients = patients.filter(p => p.tipeLayanan === tipeLayanan);
+ }
+
+ 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'),
+ };
+ });
+ };
+
+ // Call next patient for a specific room and tipeLayanan
+ const callNextKlinikRuang = (kodeKlinik, nomorRuang, tipeLayanan, allowMultiple = false) => {
+ const patients = allPatients.value.filter(p =>
+ p.kodeKlinik === kodeKlinik &&
+ p.nomorRuang === nomorRuang &&
+ p.tipeLayanan === tipeLayanan &&
+ p.processStage === 'klinik-ruang' &&
+ (p.status === 'waiting' || (allowMultiple && p.status === 'di-loket'))
+ ).sort((a, b) => {
+ // Sort by status priority first
+ const statusPriority = {
+ 'waiting': 1,
+ 'di-loket': 2
+ };
+ const priorityDiff = (statusPriority[a.status] || 99) - (statusPriority[b.status] || 99);
+ if (priorityDiff !== 0) return priorityDiff;
+
+ // Then sort by queue number (extract from noAntrian)
+ const numA = parseInt(a.noAntrian.match(/\d+/)?.[0] || '999');
+ const numB = parseInt(b.noAntrian.match(/\d+/)?.[0] || '999');
+ return numA - numB;
+ });
+
+ if (patients.length === 0) {
+ return { success: false, message: `Tidak ada antrian ${tipeLayanan} yang menunggu di ruang ini` };
+ }
+
+ const nextPatient = patients[0];
+ const patientIndex = allPatients.value.findIndex(p => p.no === nextPatient.no);
+
+ if (patientIndex !== -1) {
+ // If allowMultiple, we can call even if already di-loket (just update timestamp)
+ // Otherwise, only update if status is waiting
+ if (allowMultiple || nextPatient.status === 'waiting') {
+ allPatients.value[patientIndex] = {
+ ...allPatients.value[patientIndex],
+ status: "di-loket",
+ lastCalledAt: new Date().toISOString() // Track last call time
+ };
+ }
+ }
+
+ return {
+ success: true,
+ message: `Memanggil pasien ${nextPatient.noAntrian.split(" |")[0]} untuk ${tipeLayanan}`,
+ patient: allPatients.value[patientIndex],
+ };
+ };
+
+ // Process patient in klinik ruang (set as current processing)
+ const processPatientKlinikRuang = (patient, action, kodeKlinik, nomorRuang) => {
+ const patientIndex = allPatients.value.findIndex(p => p.no === patient.no);
+
+ if (patientIndex === -1) {
+ return { success: false, message: "Pasien tidak ditemukan" };
+ }
+
+ const patientCode = patient.noAntrian.split(" |")[0];
+ const key = `klinik-ruang-${kodeKlinik}-${nomorRuang}`;
+ let message = "";
+
+ switch (action) {
+ case "proses":
+ // Set as current processing for this room
+ if (!currentProcessingPatient.value[key]) {
+ // Use Vue's reactive assignment
+ currentProcessingPatient.value = {
+ ...currentProcessingPatient.value,
+ [key]: {}
+ };
+ }
+ // Update nested property reactively
+ currentProcessingPatient.value[key] = {
+ ...currentProcessingPatient.value[key],
+ [patient.tipeLayanan]: allPatients.value[patientIndex]
+ };
+ message = `Memproses pasien ${patientCode}`;
+ break;
+
+ case "selesai":
+ allPatients.value[patientIndex] = {
+ ...allPatients.value[patientIndex],
+ status: "processed"
+ };
+ // Clear current processing
+ if (currentProcessingPatient.value[key]) {
+ currentProcessingPatient.value[key] = {
+ ...currentProcessingPatient.value[key],
+ [patient.tipeLayanan]: null
+ };
+ }
+ message = `Pasien ${patientCode} selesai diproses`;
+ break;
+
+ case "terlambat":
+ allPatients.value[patientIndex] = {
+ ...allPatients.value[patientIndex],
+ status: "terlambat"
+ };
+ if (currentProcessingPatient.value[key]) {
+ currentProcessingPatient.value[key] = {
+ ...currentProcessingPatient.value[key],
+ [patient.tipeLayanan]: null
+ };
+ }
+ message = `Pasien ${patientCode} ditandai terlambat`;
+ break;
+
+ case "pending":
+ allPatients.value[patientIndex] = {
+ ...allPatients.value[patientIndex],
+ status: "pending"
+ };
+ if (currentProcessingPatient.value[key]) {
+ currentProcessingPatient.value[key] = {
+ ...currentProcessingPatient.value[key],
+ [patient.tipeLayanan]: null
+ };
+ }
+ message = `Pasien ${patientCode} di-pending`;
+ break;
+ }
+
+ return { success: true, message };
+ };
+
const changeKlinik = (patient, newKlinik, adminType = 'loket') => {
const patientIndex = allPatients.value.findIndex((p) => p.no === patient.no);
@@ -765,6 +1005,10 @@ export const useQueueStore = defineStore('queue', () => {
createAntreanKlinik,
createAntreanPenunjang,
createAntreanKlinikRuang,
+ scanAndCreateAntreanKlinikRuang,
+ getPatientsByKlinikRuang,
+ callNextKlinikRuang,
+ processPatientKlinikRuang,
changeKlinik,
processNextQueue,
getPatientsByStage,