update print adminloket, logika klinik ruang, numbering antrian baru klinik ruang

This commit is contained in:
bagus-arie05
2026-01-08 14:38:37 +07:00
parent 14153a2f03
commit 676bdc076f
5 changed files with 252 additions and 239 deletions
+20 -1
View File
@@ -5,6 +5,8 @@ export interface ThermalPrintData {
noAntrian: string;
barcode: string;
klinik: string;
ruang?: string;
nomorRuang?: string;
shift: string;
pembayaran: string;
tanggal: string;
@@ -103,6 +105,21 @@ export const useThermalPrint = () => {
// Format nomor antrian (hilangkan bagian "| Onsite - barcode")
const noAntrianDisplay = data.noAntrian.split(' |')[0];
// Format informasi ruang: "Poli Anak - Ruang A"
let ruangInfo = '';
if (data.klinik && data.nomorRuang) {
// Konversi nomor ruang ke abjad (1 = A, 2 = B, 3 = C, dst)
const ruangNumber = parseInt(data.nomorRuang) || 1;
const ruangLetter = String.fromCharCode(64 + ruangNumber); // 64 = '@', 65 = 'A', 66 = 'B', dst
ruangInfo = `${data.klinik} - Ruang ${ruangLetter}`;
} else if (data.klinik && data.ruang) {
// Fallback jika hanya ada nama ruang
ruangInfo = `${data.klinik} - ${data.ruang}`;
} else if (data.klinik) {
// Hanya klinik jika tidak ada info ruang
ruangInfo = data.klinik;
}
const html = `
<!DOCTYPE html>
@@ -333,7 +350,7 @@ export const useThermalPrint = () => {
<div class="no-antrian">${noAntrianDisplay}</div>
<div class="klinik-name">${data.klinik}</div>
${ruangInfo ? `<div class="klinik-name">${ruangInfo}</div>` : `<div class="klinik-name">${data.klinik}</div>`}
<div class="qr-code-section">
<div class="qr-code">
@@ -502,6 +519,8 @@ export const useThermalPrint = () => {
noAntrian: patient.noAntrian || '',
barcode: patient.barcode || '',
klinik: patient.klinik || '',
ruang: patient.ruang || undefined,
nomorRuang: patient.nomorRuang || undefined,
shift: patient.shift || 'Shift 1',
pembayaran: patient.pembayaran || '',
tanggal: tanggalKunjungan,
+180 -231
View File
@@ -9,31 +9,6 @@
theme="warning"
/>
<!-- Generate Ticket Section -->
<v-card class="generate-ticket-card mb-4" elevation="0">
<v-card-text class="pa-4">
<div class="section-label mb-3">GENERATE TIKET</div>
<v-row dense align="center">
<v-col cols="12" md="6">
<v-text-field
v-model="barcodeInput"
placeholder="Masukan Barcode"
density="compact"
hide-details
prepend-inner-icon="mdi-barcode-scan"
@keyup.enter="handleScanBarcode"
autofocus
/>
</v-col>
<v-col cols="12" md="6">
<div class="instruction-text">
Tekan Enter. (Apabila barcode depan nomor ada huruf lain, Ex: J200730100005 "Hiraukan huruf 'j' nya")
</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
<!-- Rooms Grid -->
<div class="rooms-grid">
<v-card
@@ -58,7 +33,7 @@
<div class="section-label-small mb-2">SEDANG DIPROSES</div>
<div class="current-patient-info">
<div class="patient-number-large">
{{ getCurrentProcessingForRoom(ruang)?.noAntrian.split(" |")[0] }}
{{ getCurrentProcessingForRoom(ruang)?.noAntrian?.split(" |")[0] || '-' }}
</div>
<div class="patient-details">
<div class="action-buttons mt-3">
@@ -113,24 +88,56 @@
<div class="empty-text">Tidak ada pasien yang diproses</div>
</div>
<!-- Pending Patients (Belum Generate Tiket) -->
<div v-if="getPendingPatientsForRoom(ruang).length > 0" class="pending-section">
<!-- Daftar Pasien -->
<div v-if="getAllPatientsForRoom(ruang).length > 0" class="patient-list-section">
<div class="section-label-small mb-2">
DAFTAR PASIEN PERLU GENERATE TIKET
DAFTAR PASIEN
<v-chip size="x-small" color="warning" class="ml-2">
{{ getPendingPatientsForRoom(ruang).length }}
{{ getAllPatientsForRoom(ruang).length }}
</v-chip>
</div>
<div class="pending-queue-list">
<div class="patient-queue-list">
<div
v-for="patient in paginatedPendingPatients(ruang)"
v-for="patient in paginatedAllPatients(ruang)"
:key="patient.no"
class="pending-queue-item"
:class="getPatientCardClass(patient)"
>
<div class="pending-queue-number">
{{ patient.noAntrian.split(" |")[0] }}
<div class="patient-queue-number">
{{ patient.noAntrian?.split(" |")[0] || patient.barcode || '-' }}
</div>
<div class="pending-queue-actions">
<div class="patient-queue-info">
<!-- Penanda Panggilan Pemeriksaan Awal -->
<v-chip
v-if="patient.calledPemeriksaanAwal"
size="x-small"
color="primary"
class="mr-1"
>
<v-icon start size="12">mdi-check-circle</v-icon>
Pemeriksaan Awal
</v-chip>
<!-- Penanda Panggilan Tindakan -->
<v-chip
v-if="patient.calledTindakan"
size="x-small"
color="secondary"
class="mr-1"
>
<v-icon start size="12">mdi-check-circle</v-icon>
Tindakan
</v-chip>
<!-- Penanda Status Pending -->
<v-chip
v-if="patient.status === 'pending'"
size="x-small"
color="error"
class="mr-1"
>
<v-icon start size="12">mdi-pause-circle</v-icon>
Pending
</v-chip>
</div>
<div class="patient-queue-actions">
<v-btn
icon
size="x-small"
@@ -151,35 +158,23 @@
<v-tooltip activator="parent">Pindah Ruang</v-tooltip>
</v-btn>
<v-btn
v-if="patient.status === 'pending'"
size="x-small"
variant="flat"
color="primary"
class="text-white"
@click="handleProcessPendingPatient(ruang, patient)"
@click="patient.status === 'pending' ? handleProcessPendingPatient(ruang, patient) : handleProcessPatient(ruang, patient)"
>
<v-icon start size="14">mdi-play</v-icon>
Proses
</v-btn>
<v-btn
v-else
size="x-small"
variant="flat"
color="primary"
class="text-white"
@click="handleGenerateTicket(patient, ruang)"
>
<v-icon start size="14">mdi-ticket</v-icon>
Generate
</v-btn>
</div>
</div>
</div>
<!-- Pagination -->
<v-pagination
v-if="getPendingPatientsForRoom(ruang).length > 10"
v-if="getAllPatientsForRoom(ruang).length > 10"
v-model="pendingPage[ruang.nomorRuang]"
:length="Math.ceil(getPendingPatientsForRoom(ruang).length / 10)"
:length="Math.ceil(getAllPatientsForRoom(ruang).length / 10)"
:total-visible="5"
size="small"
class="mt-3"
@@ -190,48 +185,6 @@
</v-card>
</div>
<!-- Dialog Select Ruang -->
<v-dialog v-model="showRuangDialog" max-width="500px" persistent>
<v-card>
<v-card-title class="dialog-header">
<span>Pilih Ruang</span>
<v-btn icon variant="text" size="small" @click="showRuangDialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-divider />
<v-card-text class="pa-4">
<div class="mb-3">
<div class="body-2 text-semibold mb-2">Pasien: {{ scannedPatient?.noAntrian?.split(" |")[0] }}</div>
<div class="caption-2 text-muted">{{ scannedPatient?.barcode }}</div>
</div>
<v-radio-group v-model="selectedRuang" class="mt-0">
<v-radio
v-for="ruang in ruangList"
:key="ruang.nomorRuang"
:value="ruang"
:label="`${ruang.namaRuang} (R.${ruang.nomorRuang})`"
/>
</v-radio-group>
<v-select
v-model="selectedTipeLayanan"
:items="['Pemeriksaan Awal', 'Tindakan']"
label="Tipe Layanan"
variant="outlined"
density="compact"
class="mt-4"
/>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn variant="text" @click="showRuangDialog = false">Batal</v-btn>
<v-btn color="primary" variant="flat" :disabled="!selectedRuang || !selectedTipeLayanan" @click="confirmCreateAntrean">
Buat Antrean
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Dialog Pindah Ruang -->
<v-dialog v-model="showPindahRuangDialog" max-width="500px" persistent>
<v-card>
@@ -403,14 +356,9 @@ const currentDate = ref(
})
);
const barcodeInput = ref('');
const snackbar = ref(false);
const snackbarText = ref('');
const snackbarColor = ref('success');
const showRuangDialog = ref(false);
const scannedPatient = ref(null);
const selectedRuang = ref(null);
const selectedTipeLayanan = ref('Pemeriksaan Awal');
const showDetailDialog = ref(false);
const selectedPatientDetail = ref(null);
const pendingPage = ref({});
@@ -424,105 +372,29 @@ const showSnackbar = (message, color = 'success') => {
snackbar.value = true;
};
const handleScanBarcode = () => {
if (!barcodeInput.value.trim()) {
showSnackbar('Masukkan barcode/nomor antrian', 'error');
return;
}
// Find patient from queueStore
const cleanInput = String(barcodeInput.value).trim().toUpperCase();
const numericInput = cleanInput.replace(/^[A-Z]+/, '');
const patient = queueStore.allPatients.find(p => {
if (p.barcode === cleanInput || p.barcode === numericInput) return true;
const noAntrianUpper = (p.noAntrian || '').toUpperCase();
if (noAntrianUpper.includes(cleanInput) || noAntrianUpper.includes(numericInput)) return true;
return false;
});
if (!patient) {
showSnackbar('Pasien tidak ditemukan', 'error');
barcodeInput.value = '';
return;
}
// Check if patient already has antrean klinik ruang
const existingAntrean = queueStore.allPatients.find(p =>
p.referencePatient === patient.noAntrian &&
p.kodeKlinik === klinikData.value?.kodeKlinik &&
p.processStage === 'klinik-ruang'
);
if (existingAntrean) {
showSnackbar(`Pasien sudah memiliki antrean di ${existingAntrean.ruang}`, 'warning');
barcodeInput.value = '';
return;
}
scannedPatient.value = patient;
selectedRuang.value = null;
selectedTipeLayanan.value = 'Pemeriksaan Awal';
showRuangDialog.value = true;
barcodeInput.value = '';
};
const confirmCreateAntrean = () => {
if (!selectedRuang.value || !selectedTipeLayanan.value || !scannedPatient.value) {
showSnackbar('Pilih ruang dan tipe layanan', 'error');
return;
}
const klinikRuang = {
kodeKlinik: klinikData.value.kodeKlinik,
namaKlinik: klinikData.value.namaKlinik
};
const result = queueStore.scanAndCreateAntreanKlinikRuang(
scannedPatient.value.barcode,
klinikRuang,
selectedRuang.value,
selectedTipeLayanan.value
);
if (result.success && result.patient) {
// Auto proses pasien yang baru digenerate tiket
const processResult = queueStore.processPatientKlinikRuang(
result.patient,
'proses',
klinikData.value.kodeKlinik,
selectedRuang.value.nomorRuang
);
showSnackbar(processResult.message, processResult.success ? 'success' : 'error');
} else {
showSnackbar(result.message, result.success ? 'success' : 'error');
}
showRuangDialog.value = false;
scannedPatient.value = null;
};
// Get pending patients (belum generate tiket - dari AdminLoket/AdminKlinik)
// Juga include pasien yang sudah digenerate tiket tapi status pending
const getPendingPatientsForRoom = (ruang) => {
// Get all patients for room (hanya pasien dari processStage 'klinik-ruang')
const getAllPatientsForRoom = (ruang) => {
return queueStore.allPatients
.filter(p =>
p.kodeKlinik === klinikData.value?.kodeKlinik &&
p.nomorRuang === ruang.nomorRuang &&
p.ruang === ruang.namaRuang &&
(
// Belum generate tiket (belum punya tipeLayanan)
(!p.tipeLayanan && (p.processStage === 'klinik' || p.processStage === 'klinik-ruang') &&
(p.status === 'waiting' || p.status === 'di-loket' || p.status === 'terlambat')) ||
// Sudah generate tiket tapi status pending
(p.tipeLayanan && p.processStage === 'klinik-ruang' && p.status === 'pending')
)
p.processStage === 'klinik-ruang' &&
// Exclude pasien yang sedang diproses (current processing)
p.no !== getCurrentProcessingForRoom(ruang)?.no &&
// Include semua pasien dengan status yang relevan
(p.status === 'waiting' || p.status === 'di-loket' || p.status === 'terlambat' || p.status === 'pending')
)
.sort((a, b) => {
// Prioritaskan yang belum digenerate (pending status di akhir)
if (!a.tipeLayanan && b.tipeLayanan) return -1;
if (a.tipeLayanan && !b.tipeLayanan) return 1;
// Prioritaskan yang sudah digenerate/diproses & pending di atas
const aHasTiket = !!a.tipeLayanan;
const bHasTiket = !!b.tipeLayanan;
// Jika satu sudah punya tiket dan yang lain belum, yang sudah tiket di atas
if (aHasTiket && !bHasTiket) return -1;
if (!aHasTiket && bHasTiket) return 1;
// Jika keduanya sudah punya tiket atau keduanya belum, sort by status
const statusPriority = {
'di-loket': 1,
'waiting': 2,
@@ -532,12 +404,18 @@ const getPendingPatientsForRoom = (ruang) => {
const priorityDiff = (statusPriority[a.status] || 99) - (statusPriority[b.status] || 99);
if (priorityDiff !== 0) return priorityDiff;
// Sort by time
const timeA = a.jamPanggil?.split(':').map(Number) || [0, 0];
const timeB = b.jamPanggil?.split(':').map(Number) || [0, 0];
return timeA[0] * 60 + timeA[1] - (timeB[0] * 60 + timeB[1]);
});
};
// Get pending patients (untuk backward compatibility jika masih digunakan)
const getPendingPatientsForRoom = (ruang) => {
return getAllPatientsForRoom(ruang).filter(p => !p.tipeLayanan || p.status === 'pending');
};
const getCurrentProcessingForRoom = (ruang) => {
@@ -610,7 +488,7 @@ const handleGenerateTicket = (patient, currentRuang) => {
}
} else {
// Pasien belum punya ruang, isi field dan trigger scan (akan muncul dialog)
barcodeInput.value = patient.noAntrian.split(" |")[0] || patient.barcode;
barcodeInput.value = patient.noAntrian?.split(" |")[0] || patient.barcode || '';
handleScanBarcode();
}
};
@@ -638,16 +516,17 @@ const confirmPindahRuang = () => {
// Jika pasien sudah punya antrean klinik ruang (sudah digenerate tiket), update antreannya juga
if (patient.processStage === 'klinik-ruang' && patient.tipeLayanan) {
// Update antrean yang sudah ada
queueStore.allPatients[patientIndex] = {
...queueStore.allPatients[patientIndex],
ruang: selectedRuangBaru.value.namaRuang,
nomorRuang: selectedRuangBaru.value.nomorRuang,
nomorScreen: selectedRuangBaru.value.nomorScreen,
// Update noAntrian untuk reflect ruang baru
noAntrian: `${patient.noAntrian.split(" |")[0]} | ${klinikData.value.namaKlinik} - ${selectedRuangBaru.value.namaRuang} - ${patient.tipeLayanan}`,
noAntrianRuang: `${klinikData.value.namaKlinik} - ${selectedRuangBaru.value.namaRuang} | ${patient.noAntrian.split(" |")[0]}`
};
// Update antrean yang sudah ada
const baseNoAntrian = patient.noAntrian?.split(" |")[0] || patient.barcode || '';
queueStore.allPatients[patientIndex] = {
...queueStore.allPatients[patientIndex],
ruang: selectedRuangBaru.value.namaRuang,
nomorRuang: selectedRuangBaru.value.nomorRuang,
nomorScreen: selectedRuangBaru.value.nomorScreen,
// Update noAntrian untuk reflect ruang baru
noAntrian: `${baseNoAntrian} | ${klinikData.value.namaKlinik} - ${selectedRuangBaru.value.namaRuang} - ${patient.tipeLayanan}`,
noAntrianRuang: `${klinikData.value.namaKlinik} - ${selectedRuangBaru.value.namaRuang} | ${baseNoAntrian}`
};
// Clear current processing dari ruang lama jika ada
const oldRuang = ruangList.value.find(r => r.nomorRuang === patient.nomorRuang);
@@ -668,7 +547,7 @@ const confirmPindahRuang = () => {
}
showSnackbar(
`Pasien ${selectedPatientForPindah.value.noAntrian.split(" |")[0]} berhasil dipindah ke ${selectedRuangBaru.value.namaRuang}`,
`Pasien ${selectedPatientForPindah.value.noAntrian?.split(" |")[0] || selectedPatientForPindah.value.barcode || '-'} berhasil dipindah ke ${selectedRuangBaru.value.namaRuang}`,
'success'
);
@@ -679,27 +558,43 @@ const confirmPindahRuang = () => {
const handleCallPatientByTipe = (ruang, tipeLayanan) => {
const current = getCurrentProcessingForRoom(ruang);
if (!current) return;
if (!current || !current.no) {
showSnackbar('Tidak ada pasien yang sedang diproses', 'error');
return;
}
// Update tipeLayanan pasien untuk tracking panggilan terakhir
const patientIndex = queueStore.allPatients.findIndex(p => p.no === current.no);
if (patientIndex !== -1) {
// Update status to di-loket untuk ditampilkan di anjungan
// Simpan tipeLayanan yang dipanggil untuk display di anjungan
queueStore.allPatients[patientIndex] = {
...queueStore.allPatients[patientIndex],
status: 'di-loket',
tipeLayanan: tipeLayanan, // Update tipeLayanan untuk display di anjungan
lastCalledAt: new Date().toISOString(),
lastCalledTipeLayanan: tipeLayanan // Track tipe layanan terakhir yang dipanggil
};
// Update current processing (tetap 1 pasien, tidak dipisah per tipe)
const key = `klinik-ruang-${klinikData.value.kodeKlinik}-${ruang.nomorRuang}`;
queueStore.currentProcessingPatient[key] = queueStore.allPatients[patientIndex];
if (patientIndex === -1) {
showSnackbar('Pasien tidak ditemukan', 'error');
return;
}
showSnackbar(`Memanggil pasien ${current.noAntrian.split(" |")[0]} untuk ${tipeLayanan}`, 'success');
// Update tracking panggilan berdasarkan tipeLayanan
const updateData = {
...queueStore.allPatients[patientIndex],
status: 'di-loket',
lastCalledAt: new Date().toISOString(),
lastCalledTipeLayanan: tipeLayanan
};
// Set penanda panggilan sesuai tipeLayanan
if (tipeLayanan === 'Pemeriksaan Awal') {
updateData.calledPemeriksaanAwal = true;
} else if (tipeLayanan === 'Tindakan') {
updateData.calledTindakan = true;
}
queueStore.allPatients[patientIndex] = updateData;
// Update current processing (tetap 1 pasien, tidak dipisah per tipe)
const key = `klinik-ruang-${klinikData.value.kodeKlinik}-${ruang.nomorRuang}`;
queueStore.currentProcessingPatient[key] = queueStore.allPatients[patientIndex];
const patientCode = queueStore.allPatients[patientIndex].noAntrian?.split(" |")[0] ||
queueStore.allPatients[patientIndex].barcode ||
'-';
showSnackbar(`Memanggil pasien ${patientCode} untuk ${tipeLayanan}`, 'success');
};
const showPatientDetail = (patient) => {
@@ -758,6 +653,25 @@ const handlePatientAction = (ruang, action) => {
showSnackbar(result.message, result.success ? 'success' : 'error');
};
// Handle proses pasien (untuk pasien yang belum diproses)
const handleProcessPatient = (ruang, patient) => {
const patientIndex = queueStore.allPatients.findIndex(p => p.no === patient.no);
if (patientIndex === -1) {
showSnackbar('Pasien tidak ditemukan', 'error');
return;
}
// Set sebagai current processing
const result = queueStore.processPatientKlinikRuang(
queueStore.allPatients[patientIndex],
'proses',
klinikData.value.kodeKlinik,
ruang.nomorRuang
);
showSnackbar(result.message, result.success ? 'success' : 'error');
};
const handleProcessPendingPatient = (ruang, patient) => {
// Proses kembali pasien yang dipending
const patientIndex = queueStore.allPatients.findIndex(p => p.no === patient.no);
@@ -766,10 +680,10 @@ const handleProcessPendingPatient = (ruang, patient) => {
return;
}
// Update status pasien dari pending menjadi di-loket
// Update status pasien dari pending menjadi waiting
queueStore.allPatients[patientIndex] = {
...queueStore.allPatients[patientIndex],
status: 'di-loket'
status: 'waiting'
};
// Set sebagai current processing
@@ -783,12 +697,23 @@ const handleProcessPendingPatient = (ruang, patient) => {
showSnackbar(result.message, result.success ? 'success' : 'error');
};
const paginatedPendingPatients = (ruang) => {
const allPending = getPendingPatientsForRoom(ruang);
const paginatedAllPatients = (ruang) => {
const allPatients = getAllPatientsForRoom(ruang);
const page = pendingPage.value[ruang.nomorRuang] || 1;
const startIndex = (page - 1) * 10;
const endIndex = startIndex + 10;
return allPending.slice(startIndex, endIndex);
return allPatients.slice(startIndex, endIndex);
};
// Helper untuk menentukan class card berdasarkan status pasien
const getPatientCardClass = (patient) => {
const baseClass = 'patient-queue-item';
// Pasien yang sudah digenerate/diproses & pending: warna berbeda
if (patient.tipeLayanan || patient.status === 'pending') {
return `${baseClass} patient-queue-item-processed`;
}
// Pasien yang perlu digenerate: warna default
return `${baseClass} patient-queue-item-pending`;
};
@@ -981,7 +906,7 @@ onMounted(() => {
color: var(--color-neutral-700);
}
.pending-section {
.patient-list-section {
background: var(--color-warning-50);
border: 1px solid var(--color-warning-200);
border-radius: 8px;
@@ -989,7 +914,7 @@ onMounted(() => {
margin-bottom: 16px;
}
.pending-queue-list {
.patient-queue-list {
display: flex;
flex-direction: column;
gap: 6px;
@@ -997,35 +922,59 @@ onMounted(() => {
overflow-y: auto;
}
.pending-queue-item {
.patient-queue-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
border-radius: 6px;
transition: all 0.2s ease;
gap: 8px;
}
.pending-queue-item:hover {
/* Pasien yang sudah digenerate/diproses & pending */
.patient-queue-item-processed {
background: var(--color-primary-50);
border: 1px solid var(--color-primary-200);
}
.patient-queue-item-processed:hover {
background: var(--color-primary-100);
border-color: var(--color-primary-400);
}
/* Pasien yang perlu digenerate */
.patient-queue-item-pending {
background: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
}
.patient-queue-item-pending:hover {
background: var(--color-warning-100);
border-color: var(--color-warning-400);
}
.pending-queue-number {
.patient-queue-number {
font-size: 14px;
font-weight: 700;
color: var(--color-neutral-900);
min-width: 80px;
}
.pending-queue-actions {
.patient-queue-info {
display: flex;
gap: 4px;
align-items: center;
flex: 1;
}
.patient-queue-actions {
display: flex;
gap: 8px;
align-items: center;
}
.pending-queue-actions .v-btn {
.patient-queue-actions .v-btn {
text-transform: none;
font-weight: 600;
}
+1 -1
View File
@@ -58,7 +58,7 @@ const klinikRuangList = computed(() => {
});
const navigateToKlinik = (kodeKlinik) => {
router.push(`/admin-klinik-ruang/${kodeKlinik}`);
router.push(`/adminklinikruang/${kodeKlinik}`);
};
</script>
+17 -1
View File
@@ -255,9 +255,11 @@ import QueueActionsCard from "@/components/features/queue/QueueActionsCard.vue";
import PatientDataTable from "@/components/features/queue/TabelPatientData.vue";
import SelectionDialog from "@/components/common/SelectionDialog.vue";
import AppSnackbar from "@/components/common/AppSnackbar.vue";
import { useThermalPrint } from "@/composables/useThermalPrint";
const masterStore = useMasterStore();
const queueStore = useQueueStore();
const { printTicketFromPatient } = useThermalPrint();
const {
snackbar,
@@ -446,7 +448,7 @@ const closeKlinikRuangDialog = () => {
klinikRuangSearch.value = "";
};
const buatAntreanKlinikRuang = (klinikRuang, ruang) => {
const buatAntreanKlinikRuang = async (klinikRuang, ruang) => {
// Pastikan currentProcessingPatient valid, jika tidak, coba dapatkan dari store
let patient = currentProcessingPatient.value;
if (!patient) {
@@ -491,6 +493,20 @@ const buatAntreanKlinikRuang = (klinikRuang, ruang) => {
"loket"
);
if (result.success && result.patient) {
// Print ticket dengan nomor antrian baru
try {
await printTicketFromPatient(result.patient);
} catch (error) {
console.error('Error printing ticket:', error);
snackbarText.value = `${result.message}. Gagal print tiket.`;
snackbarColor.value = "warning";
snackbar.value = true;
closeKlinikRuangDialog();
return;
}
}
snackbarText.value = result.message;
snackbarColor.value = result.success ? "success" : "error";
snackbar.value = true;
+34 -5
View File
@@ -570,32 +570,61 @@ export const useQueueStore = defineStore('queue', () => {
const timestamp = new Date();
const barcode = patient ? patient.barcode : `250811${String(timestamp.getTime()).slice(-6)}`;
// Generate nomor antrian baru dengan format: [huruf pertama poli + urutan abjad ruang + nomor antrian ruang]
// Contoh: "Anak" ruang 1 = "AA001" (A dari Anak, A dari ruang 1, 001 nomor antrian)
// 1. Ambil huruf pertama dari nama klinik/poli
const firstLetter = klinikRuang.namaKlinik.charAt(0).toUpperCase();
// 2. Konversi nomor ruang ke abjad (1 = A, 2 = B, 3 = C, dst)
const ruangNumber = parseInt(ruang.nomorRuang) || 1;
const ruangLetter = String.fromCharCode(64 + ruangNumber); // 64 = '@', 65 = 'A', 66 = 'B', dst
// 3. Hitung nomor antrian ruang (dimulai dari 1, maksimal 3 digit)
const roomQueues = allPatients.value.filter(p =>
p.kodeKlinik === klinikRuang.kodeKlinik &&
p.nomorRuang === ruang.nomorRuang &&
p.processStage === 'klinik-ruang'
);
const queueNumber = roomQueues.length + 1;
const queueNumberStr = String(queueNumber).padStart(3, "0");
// 4. Format nomor antrian: AA001, AB002, dst
const newNoAntrian = `${firstLetter}${ruangLetter}${queueNumberStr}`;
const newPatient = {
no: newNo,
jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String(
timestamp.getMinutes()
).padStart(2, "0")}`,
barcode: barcode,
noAntrian: `KR${String(newNo).padStart(4, "0")} | ${klinikRuang.namaKlinik} - Ruang ${ruang.nomorRuang} - ${barcode}`,
shift: "Shift 1",
noAntrian: `${newNoAntrian} | ${klinikRuang.namaKlinik} - ${ruang.namaRuang}`,
noAntrianRuang: `${klinikRuang.namaKlinik} - ${ruang.namaRuang} | ${newNoAntrian}`,
shift: patient ? (patient.shift || "Shift 1") : "Shift 1",
klinik: klinikRuang.namaKlinik,
ruang: ruang.namaRuang,
kodeKlinik: klinikRuang.kodeKlinik,
nomorRuang: ruang.nomorRuang,
nomorScreen: ruang.nomorScreen,
fastTrack: "TIDAK",
fastTrack: patient ? (patient.fastTrack || "TIDAK") : "TIDAK",
pembayaran: patient ? patient.pembayaran : "UMUM",
status: "waiting",
processStage: "klinik",
processStage: "klinik-ruang", // Set ke klinik-ruang langsung
createdAt: timestamp.toISOString(),
referencePatient: patient ? patient.noAntrian : null,
sourcePatientNo: patient ? patient.no : null,
// Tracking panggilan
calledPemeriksaanAwal: false,
calledTindakan: false,
lastCalledAt: null,
lastCalledTipeLayanan: null,
};
allPatients.value.push(newPatient);
return {
success: true,
message: `Antrean ${klinikRuang.namaKlinik} Ruang ${ruang.nomorRuang} berhasil dibuat dan akan ditampilkan di layar antrian`,
message: `Antrean ${klinikRuang.namaKlinik} Ruang ${ruang.nomorRuang} berhasil dibuat: ${newNoAntrian}`,
patient: newPatient,
};
};