perbaikan generate dan scan tiket

This commit is contained in:
Fanrouver
2026-01-15 14:32:21 +07:00
parent eac6529a77
commit 8aa5ab2ded
4 changed files with 154 additions and 222 deletions
+128 -73
View File
@@ -17,6 +17,26 @@ export const useQueueStore = defineStore('queue', () => {
return loket1 ? loket1.namaLoket : 'Loket A';
};
// Helper function untuk increment barcode counter setelah barcode digunakan
const incrementBarcodeCounter = () => {
if (typeof window === 'undefined' || typeof localStorage === 'undefined') return;
const now = new Date();
const year = String(now.getFullYear()).slice(-2);
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const datePrefix = `${year}${month}${day}`;
const STORAGE_KEY = `barcode_counter_${datePrefix}`;
const LAST_DATE_KEY = 'barcode_last_date';
const storedCounter = localStorage.getItem(STORAGE_KEY);
if (storedCounter) {
const currentCounter = parseInt(storedCounter, 10) || 1;
localStorage.setItem(STORAGE_KEY, String(currentCounter + 1));
localStorage.setItem(LAST_DATE_KEY, datePrefix);
}
};
// Helper function untuk generate barcode dengan format: YYMMDD + 5 digit sequential
// Format: YY (tahun 2 digit terakhir) + MM (bulan 2 digit) + DD (tanggal 2 digit) + XXXXX (5 digit sequential)
// Contoh: 26011400001, 26011400002, dst
@@ -78,9 +98,11 @@ export const useQueueStore = defineStore('queue', () => {
attempts++;
}
// Increment counter untuk next call dan simpan
counter++;
localStorage.setItem(STORAGE_KEY, String(counter));
// IMPORTANT: JANGAN tulis counter ke localStorage di sini!
// Counter hanya di-increment dan di-save setelah barcode benar-benar digunakan
// Ini mencegah counter naik meskipun barcode tidak digunakan (misalnya saat refresh)
// Counter akan di-increment oleh incrementBarcodeCounter() setelah pasien dibuat
// Hanya update LAST_DATE_KEY untuk tracking tanggal
localStorage.setItem(LAST_DATE_KEY, currentDate);
return barcode;
@@ -100,24 +122,73 @@ export const useQueueStore = defineStore('queue', () => {
// IMPORTANT: Setiap pasien HARUS memiliki barcode UNIK untuk menghindari konflik
// Format barcode menggunakan generateBarcode() untuk konsistensi dengan format baru
// Generate barcode dengan memastikan uniqueness menggunakan existingBarcodes array
const seedBarcodes = [];
const seedBarcode1 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode1);
const seedBarcode2 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode2);
const seedBarcode3 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode3);
const seedBarcode4 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode4);
const seedBarcode5 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode5);
const seedBarcode6 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode6);
const seedBarcode7 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode7);
const seedBarcode8 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode8);
const seedBarcode9 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode9);
const seedBarcode10 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode10);
const seedBarcode11 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode11);
const seedBarcode12 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode12);
// NOTE: Seed data menggunakan barcode statis untuk menghindari increment counter saat refresh
// Counter hanya di-increment ketika pasien baru benar-benar dibuat dari Anjungan
// Gunakan barcode statis untuk seed data (tidak memanggil generateBarcode)
// Format: YYMMDD + 5 digit sequential dimulai dari 00001
// Ini mencegah counter naik setiap kali halaman di-refresh
const getSeedBarcode = (index) => {
if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {
const now = new Date();
const year = String(now.getFullYear()).slice(-2);
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const datePrefix = `${year}${month}${day}`;
// Gunakan barcode statis berdasarkan index (1-16)
// Format: YYMMDD + 5 digit (contoh: 26011500001, 26011500002, dst)
return `${datePrefix}${String(index).padStart(5, '0')}`;
}
// Fallback untuk SSR
const now = new Date();
const year = String(now.getFullYear()).slice(-2);
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const datePrefix = `${year}${month}${day}`;
return `${datePrefix}${String(index).padStart(5, '0')}`;
};
const seedBarcode1 = getSeedBarcode(1);
const seedBarcode2 = getSeedBarcode(2);
const seedBarcode3 = getSeedBarcode(3);
const seedBarcode4 = getSeedBarcode(4);
const seedBarcode5 = getSeedBarcode(5);
const seedBarcode6 = getSeedBarcode(6);
const seedBarcode7 = getSeedBarcode(7);
const seedBarcode8 = getSeedBarcode(8);
const seedBarcode9 = getSeedBarcode(9);
const seedBarcode10 = getSeedBarcode(10);
const seedBarcode11 = getSeedBarcode(11);
const seedBarcode12 = getSeedBarcode(12);
// Barcode untuk pasien Eksekutif
const seedBarcodeE1 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE1);
const seedBarcodeE2 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE2);
const seedBarcodeE3 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE3);
const seedBarcodeE4 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE4);
const seedBarcodeE1 = getSeedBarcode(13);
const seedBarcodeE2 = getSeedBarcode(14);
const seedBarcodeE3 = getSeedBarcode(15);
const seedBarcodeE4 = getSeedBarcode(16);
// IMPORTANT: Set counter ke nilai yang sesuai dengan jumlah seed data
// Ini mencegah counter naik tidak terkendali saat seed data di-generate
// Counter akan di-set ke jumlah seed data (16) + 1 untuk next barcode
// Hanya set jika counter belum ada atau lebih kecil dari jumlah seed data
if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {
const now = new Date();
const year = String(now.getFullYear()).slice(-2);
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const datePrefix = `${year}${month}${day}`;
const STORAGE_KEY = `barcode_counter_${datePrefix}`;
const LAST_DATE_KEY = 'barcode_last_date';
const storedCounter = localStorage.getItem(STORAGE_KEY);
const seedDataCount = 16; // Jumlah seed data
// Hanya set counter jika belum ada atau lebih kecil dari jumlah seed data
// Jangan overwrite counter yang sudah lebih besar (berarti sudah ada pasien baru)
if (!storedCounter || parseInt(storedCounter, 10) < seedDataCount) {
localStorage.setItem(STORAGE_KEY, String(seedDataCount + 1));
localStorage.setItem(LAST_DATE_KEY, datePrefix);
}
}
const seedPatients = [
{
@@ -843,6 +914,11 @@ export const useQueueStore = defineStore('queue', () => {
};
allPatients.value.push(newPatient);
// Increment counter setelah barcode digunakan
if (!patient) {
incrementBarcodeCounter();
}
return {
success: true,
@@ -874,6 +950,11 @@ export const useQueueStore = defineStore('queue', () => {
};
allPatients.value.push(newPatient);
// Increment counter setelah barcode digunakan
if (!patient) {
incrementBarcodeCounter();
}
return {
success: true,
@@ -938,6 +1019,11 @@ export const useQueueStore = defineStore('queue', () => {
};
allPatients.value.push(newPatient);
// Increment counter setelah barcode digunakan
if (!patient) {
incrementBarcodeCounter();
}
return {
success: true,
@@ -1455,6 +1541,11 @@ export const useQueueStore = defineStore('queue', () => {
};
allPatients.value.push(newPatient);
// IMPORTANT: Increment counter SETELAH barcode benar-benar digunakan untuk membuat pasien
// Ini mencegah counter naik meskipun barcode tidak digunakan
// registerPatientFromAnjungan selalu membuat pasien baru, jadi selalu increment counter
incrementBarcodeCounter();
return {
success: true,
@@ -1464,78 +1555,42 @@ export const useQueueStore = defineStore('queue', () => {
};
// Check-in patient (update status from waiting to di-loket)
// IMPORTANT: Hanya menggunakan EXACT barcode match untuk menghindari false positive
// Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
// Jangan gunakan fallback ke noAntrian atau no karena bisa menyebabkan false positive
const checkInPatient = (patientIdOrBarcode) => {
console.log('🔍 checkInPatient called with:', patientIdOrBarcode);
console.log('📊 Total patients in store:', allPatients.value.length);
console.log('📋 First few patients:', allPatients.value.slice(0, 3).map(p => ({
no: p.no,
noAntrian: p.noAntrian,
barcode: p.barcode,
status: p.status
})));
// Clean input - remove whitespace and normalize
const cleanInput = String(patientIdOrBarcode).trim();
const cleanInputUpper = cleanInput.toUpperCase();
// Try to find by multiple criteria:
// 1. Exact barcode match (case-insensitive, whitespace-insensitive)
// 2. Parse as number and match with no
// 3. Extract number from input (e.g., "UM0014" -> "0014" or "14") and match with noAntrian
// 4. Check if noAntrian includes the input
// PRIORITAS: Hanya cari dengan EXACT barcode match (case-insensitive, whitespace-insensitive)
// Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
// Jangan gunakan fallback ke noAntrian atau no karena bisa menyebabkan false positive
const patientIndex = allPatients.value.findIndex(p => {
// Normalize barcode untuk comparison
const patientBarcode = String(p.barcode || '').trim();
const patientBarcodeUpper = patientBarcode.toUpperCase();
// Exact barcode match (case-insensitive, whitespace-insensitive)
// EXACT barcode match (case-insensitive, whitespace-insensitive)
// Ini adalah satu-satunya cara yang aman untuk match pasien
if (patientBarcode === cleanInput ||
patientBarcodeUpper === cleanInputUpper ||
patientBarcode === patientIdOrBarcode) {
console.log('✅ Found by barcode:', patientBarcode, '===', cleanInput);
patientBarcode.toLowerCase() === cleanInput.toLowerCase()) {
console.log('✅ Found by exact barcode match:', patientBarcode, '===', cleanInput);
return true;
}
// Try parsing as number
const parsedNo = parseInt(cleanInput.replace(/[^0-9]/g, '')) || parseInt(patientIdOrBarcode);
if (!isNaN(parsedNo) && p.no === parsedNo) {
console.log('✅ Found by no:', p.no);
return true;
}
// Check if noAntrian includes the input (case insensitive)
// Handle format "F-RA001" by removing "F-" prefix for comparison
const noAntrianUpper = (p.noAntrian || '').toUpperCase();
const noAntrianWithoutPrefix = noAntrianUpper.replace(/^F-/, ''); // Remove "F-" prefix if exists
const cleanInputWithoutPrefix = cleanInputUpper.replace(/^F-/, ''); // Remove "F-" prefix from input if exists
if (noAntrianUpper.includes(cleanInput) ||
noAntrianUpper.includes(patientIdOrBarcode) ||
noAntrianWithoutPrefix.includes(cleanInputWithoutPrefix) ||
noAntrianWithoutPrefix === cleanInputWithoutPrefix) {
console.log('✅ Found by noAntrian:', p.noAntrian);
return true;
}
// Try to extract number from noAntrian (e.g., "F-RA001 | Onsite - ..." -> "RA001" -> "001")
// Handle format "F-RA001" by extracting after "F-" prefix
const noAntrianMatch = noAntrianUpper.match(/^(?:F-)?([A-Z]+)(\d+)/);
if (noAntrianMatch) {
const extractedNumber = noAntrianMatch[2];
const inputNumber = cleanInputWithoutPrefix.replace(/[^0-9]/g, '');
if (inputNumber && (extractedNumber.includes(inputNumber) || inputNumber.includes(extractedNumber))) {
console.log('✅ Found by extracted number from noAntrian');
return true;
}
}
return false;
});
if (patientIndex === -1) {
console.log('❌ Patient not found. Searched for:', cleanInput);
console.log('📋 Available noAntrian values:', allPatients.value.map(p => p.noAntrian));
return { success: false, message: "Pasien tidak ditemukan" };
console.log('❌ Patient not found. Searched for barcode:', cleanInput);
console.log('📋 Available barcodes (first 10):', allPatients.value.slice(0, 10).map(p => ({
no: p.no,
barcode: p.barcode,
noAntrian: p.noAntrian?.split(' |')[0]
})));
return { success: false, message: `Pasien dengan barcode ${cleanInput} tidak ditemukan. Pastikan barcode benar (format: YYMMDD + 5 digit, contoh: 26011500001).` };
}
// IMPORTANT: Get fresh patient data from array to avoid stale data