perbaikan generate dan scan tiket
This commit is contained in:
No files matched your search
@@ -18,82 +18,9 @@ export interface ThermalPrintData {
|
|||||||
export const useThermalPrint = () => {
|
export const useThermalPrint = () => {
|
||||||
const isPrinting = ref(false);
|
const isPrinting = ref(false);
|
||||||
|
|
||||||
/**
|
// NOTE: generateBarcode telah dihapus dari useThermalPrint
|
||||||
* Generate barcode dengan format: YYMMDD + 5 digit sequential
|
// Gunakan generateBarcode dari queueStore.js untuk konsistensi
|
||||||
* Format: YY (tahun 2 digit terakhir) + MM (bulan 2 digit) + DD (tanggal 2 digit) + XXXXX (5 digit sequential)
|
// Semua barcode generation harus melalui queueStore.generateBarcode()
|
||||||
* Contoh: 26011400001, 26011400002, dst
|
|
||||||
* Counter akan reset setiap ganti tanggal (mulai dari 00001 lagi)
|
|
||||||
*
|
|
||||||
* @param existingBarcodes - Array barcode yang sudah ada (optional, untuk validasi uniqueness)
|
|
||||||
* @returns Barcode string dengan format YYMMDDXXXXX
|
|
||||||
*/
|
|
||||||
const generateBarcode = (existingBarcodes: string[] = []): string => {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
// Fallback untuk SSR: gunakan counter berdasarkan existingBarcodes
|
|
||||||
const now = new Date();
|
|
||||||
const year = String(now.getFullYear()).slice(-2); // 2 digit tahun terakhir
|
|
||||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(now.getDate()).padStart(2, '0');
|
|
||||||
const datePrefix = `${year}${month}${day}`; // YYMMDD
|
|
||||||
|
|
||||||
// Gunakan counter berdasarkan existingBarcodes untuk sequential
|
|
||||||
const existingCount = existingBarcodes.filter(b => b && b.startsWith(datePrefix)).length;
|
|
||||||
const counter = existingCount + 1;
|
|
||||||
const counterCode = String(counter).padStart(5, '0');
|
|
||||||
return `${datePrefix}${counterCode}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
const year = String(now.getFullYear()).slice(-2); // 2 digit tahun terakhir
|
|
||||||
const month = String(now.getMonth() + 1).padStart(2, '0'); // 2 digit bulan
|
|
||||||
const day = String(now.getDate()).padStart(2, '0'); // 2 digit tanggal
|
|
||||||
const datePrefix = `${year}${month}${day}`; // YYMMDD
|
|
||||||
|
|
||||||
// Key untuk localStorage berdasarkan tanggal
|
|
||||||
const STORAGE_KEY = `barcode_counter_${datePrefix}`;
|
|
||||||
const LAST_DATE_KEY = 'barcode_last_date';
|
|
||||||
|
|
||||||
// Cek apakah tanggal sudah berubah (reset counter)
|
|
||||||
const lastDate = localStorage.getItem(LAST_DATE_KEY);
|
|
||||||
const currentDate = datePrefix;
|
|
||||||
|
|
||||||
let counter = 1; // Default mulai dari 1
|
|
||||||
|
|
||||||
if (lastDate === currentDate) {
|
|
||||||
// Tanggal sama, lanjutkan counter dari localStorage
|
|
||||||
const storedCounter = localStorage.getItem(STORAGE_KEY);
|
|
||||||
if (storedCounter) {
|
|
||||||
counter = parseInt(storedCounter, 10) || 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Tanggal berbeda, reset counter ke 1
|
|
||||||
counter = 1;
|
|
||||||
// Hapus counter lama untuk tanggal sebelumnya (cleanup)
|
|
||||||
if (lastDate) {
|
|
||||||
localStorage.removeItem(`barcode_counter_${lastDate}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate barcode dengan counter saat ini
|
|
||||||
let barcode = `${datePrefix}${String(counter).padStart(5, '0')}`;
|
|
||||||
|
|
||||||
// Cek apakah barcode sudah ada di existingBarcodes
|
|
||||||
let attempts = 0;
|
|
||||||
const maxAttempts = 1000; // Maksimal 1000 pasien per hari
|
|
||||||
|
|
||||||
while (existingBarcodes.includes(barcode) && attempts < maxAttempts) {
|
|
||||||
counter++;
|
|
||||||
barcode = `${datePrefix}${String(counter).padStart(5, '0')}`;
|
|
||||||
attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment counter untuk next call dan simpan
|
|
||||||
counter++;
|
|
||||||
localStorage.setItem(STORAGE_KEY, String(counter));
|
|
||||||
localStorage.setItem(LAST_DATE_KEY, currentDate);
|
|
||||||
|
|
||||||
return barcode;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate QR Code data URL
|
* Generate QR Code data URL
|
||||||
@@ -699,7 +626,6 @@ export const useThermalPrint = () => {
|
|||||||
isPrinting,
|
isPrinting,
|
||||||
printTicket,
|
printTicket,
|
||||||
printTicketFromPatient,
|
printTicketFromPatient,
|
||||||
generateQRCode,
|
generateQRCode
|
||||||
generateBarcode
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -1517,14 +1517,14 @@ watch(lastRegisteredPatient, (newVal) => {
|
|||||||
|
|
||||||
.clinic-available {
|
.clinic-available {
|
||||||
border-color: var(--color-success-600);
|
border-color: var(--color-success-600);
|
||||||
background: var(--color-success-200);
|
background: var(--color-success-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.clinic-closed {
|
.clinic-closed {
|
||||||
opacity: 0.65;
|
opacity: 0.65;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
border-color: var(--color-danger-600);
|
border-color: var(--color-danger-600);
|
||||||
background: var(--color-danger-200);
|
background: var(--color-danger-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.clinic-content {
|
.clinic-content {
|
||||||
|
|||||||
@@ -2295,48 +2295,24 @@ const onDetect = async (decodedText: string) => {
|
|||||||
console.log('🔍 Cleaned input:', cleanInput);
|
console.log('🔍 Cleaned input:', cleanInput);
|
||||||
console.log('📊 Total patients in store:', queueStore.allPatients.length);
|
console.log('📊 Total patients in store:', queueStore.allPatients.length);
|
||||||
|
|
||||||
// Cari pasien di queueStore berdasarkan berbagai kriteria
|
// IMPORTANT: Hanya cari dengan EXACT barcode match untuk menghindari false positive
|
||||||
// Prioritas: exact barcode match (case-insensitive, whitespace-insensitive)
|
// Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
|
||||||
// Kemudian: noAntrian match dengan kode + angka (tidak hanya angka saja)
|
// Jangan gunakan fallback ke noAntrian atau no karena bisa menyebabkan false positive
|
||||||
|
// Contoh masalah: RA020 dengan barcode 26011500001 terdeteksi sebagai RA002 dengan barcode 26011500198
|
||||||
const foundPatient = queueStore.allPatients.find(p => {
|
const foundPatient = queueStore.allPatients.find(p => {
|
||||||
if (!p) return false;
|
if (!p) return false;
|
||||||
|
|
||||||
// Normalize barcode untuk comparison (remove whitespace, case-insensitive)
|
// Normalize barcode untuk comparison (remove whitespace, case-insensitive)
|
||||||
const patientBarcode = String(p.barcode || '').trim();
|
const patientBarcode = String(p.barcode || '').trim();
|
||||||
const patientBarcodeUpper = patientBarcode.toUpperCase();
|
|
||||||
const patientBarcodeNormalized = patientBarcodeUpper.replace(/\s+/g, '');
|
|
||||||
const cleanInputNormalized = cleanInputUpper.replace(/\s+/g, '');
|
|
||||||
|
|
||||||
// 1. Exact barcode match (case-insensitive, whitespace-insensitive) - PRIORITAS TERTINGGI
|
// EXACT barcode match (case-insensitive, whitespace-insensitive)
|
||||||
|
// Ini adalah satu-satunya cara yang aman untuk match pasien
|
||||||
if (patientBarcode === cleanInput ||
|
if (patientBarcode === cleanInput ||
|
||||||
patientBarcodeNormalized === cleanInputNormalized ||
|
patientBarcode.toLowerCase() === cleanInput.toLowerCase()) {
|
||||||
patientBarcodeUpper === cleanInputUpper) {
|
|
||||||
console.log('✅ Found by exact barcode match:', patientBarcode, '===', cleanInput);
|
console.log('✅ Found by exact barcode match:', patientBarcode, '===', cleanInput);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Match noAntrian dengan kode + angka (tidak hanya angka saja)
|
|
||||||
// Ini mencegah false positive ketika angka sama tapi kode beda (misalnya UM0014 vs AN0014)
|
|
||||||
if (p.noAntrian && matchNoAntrian(cleanInput, p.noAntrian)) {
|
|
||||||
console.log('✅ Found by noAntrian match (kode + angka):', p.noAntrian);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Try parsing as number and match with no (fallback)
|
|
||||||
// IMPORTANT: Hanya gunakan fallback jika input TIDAK punya kode (hanya angka)
|
|
||||||
// Ini mencegah false positive: "RA004" (kode: RA) tidak boleh match dengan pasien no: 4 yang punya kode berbeda
|
|
||||||
const inputHasCode = /^[A-Z]+/.test(cleanInput);
|
|
||||||
if (!inputHasCode) {
|
|
||||||
// Input hanya angka, boleh match dengan no (tapi tetap prioritas ke barcode dan noAntrian)
|
|
||||||
const parsedNo = parseInt(cleanInput.replace(/[^0-9]/g, '')) || parseInt(searchBarcode);
|
|
||||||
if (!isNaN(parsedNo) && p.no === parsedNo) {
|
|
||||||
console.log('✅ Found by no (input tanpa kode):', p.no);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Jika input punya kode (seperti "RA004"), jangan gunakan fallback ke no
|
|
||||||
// karena bisa match dengan pasien yang punya kode berbeda
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2378,13 +2354,13 @@ const onDetect = async (decodedText: string) => {
|
|||||||
|
|
||||||
// IMPORTANT: Refresh data pasien dari queueStore untuk mendapatkan status REAL-TIME
|
// IMPORTANT: Refresh data pasien dari queueStore untuk mendapatkan status REAL-TIME
|
||||||
// Jangan gunakan foundPatient.status karena mungkin sudah stale
|
// Jangan gunakan foundPatient.status karena mungkin sudah stale
|
||||||
// Cari ulang pasien dari queueStore untuk mendapatkan data terbaru
|
// Cari ulang pasien dari queueStore dengan EXACT barcode match
|
||||||
const freshPatient = queueStore.allPatients.find(p => {
|
const freshPatient = queueStore.allPatients.find(p => {
|
||||||
const patientBarcode = String(p.barcode || '').trim();
|
const patientBarcode = String(p.barcode || '').trim();
|
||||||
|
// Hanya exact barcode match untuk menghindari false positive
|
||||||
return patientBarcode === foundPatient.barcode ||
|
return patientBarcode === foundPatient.barcode ||
|
||||||
patientBarcode === searchBarcode ||
|
patientBarcode === searchBarcode ||
|
||||||
patientBarcode === decodedText ||
|
patientBarcode === cleanInput;
|
||||||
p.no === foundPatient.no;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!freshPatient) {
|
if (!freshPatient) {
|
||||||
@@ -2606,49 +2582,24 @@ const checkInManual = async () => {
|
|||||||
console.log('🔍 Cleaned input:', cleanInput);
|
console.log('🔍 Cleaned input:', cleanInput);
|
||||||
console.log('📊 Total patients in store:', queueStore.allPatients.length);
|
console.log('📊 Total patients in store:', queueStore.allPatients.length);
|
||||||
|
|
||||||
// Cari pasien di queueStore menggunakan logika yang sama dengan onDetect
|
// IMPORTANT: Hanya cari dengan EXACT barcode match untuk menghindari false positive
|
||||||
// Prioritas: exact barcode match (case-insensitive, whitespace-insensitive)
|
// Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
|
||||||
// Kemudian: noAntrian match dengan kode + angka (tidak hanya angka saja)
|
// Jangan gunakan fallback ke noAntrian atau no karena bisa menyebabkan false positive
|
||||||
// Fallback: hanya jika input tidak punya kode (hanya angka)
|
// Contoh masalah: RA020 dengan barcode 26011500001 terdeteksi sebagai RA002 dengan barcode 26011500198
|
||||||
const foundPatient = queueStore.allPatients.find(p => {
|
const foundPatient = queueStore.allPatients.find(p => {
|
||||||
if (!p) return false;
|
if (!p) return false;
|
||||||
|
|
||||||
// Normalize barcode untuk comparison (remove whitespace, case-insensitive)
|
// Normalize barcode untuk comparison (remove whitespace, case-insensitive)
|
||||||
const patientBarcode = String(p.barcode || '').trim();
|
const patientBarcode = String(p.barcode || '').trim();
|
||||||
const patientBarcodeUpper = patientBarcode.toUpperCase();
|
|
||||||
const patientBarcodeNormalized = patientBarcodeUpper.replace(/\s+/g, '');
|
|
||||||
const cleanInputNormalized = cleanInputUpper.replace(/\s+/g, '');
|
|
||||||
|
|
||||||
// 1. Exact barcode match (case-insensitive, whitespace-insensitive) - PRIORITAS TERTINGGI
|
// EXACT barcode match (case-insensitive, whitespace-insensitive)
|
||||||
|
// Ini adalah satu-satunya cara yang aman untuk match pasien
|
||||||
if (patientBarcode === cleanInput ||
|
if (patientBarcode === cleanInput ||
|
||||||
patientBarcodeNormalized === cleanInputNormalized ||
|
patientBarcode.toLowerCase() === cleanInput.toLowerCase()) {
|
||||||
patientBarcodeUpper === cleanInputUpper) {
|
|
||||||
console.log('✅ Found by exact barcode match:', patientBarcode, '===', cleanInput);
|
console.log('✅ Found by exact barcode match:', patientBarcode, '===', cleanInput);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Match noAntrian dengan kode + angka (tidak hanya angka saja)
|
|
||||||
// Ini mencegah false positive ketika angka sama tapi kode beda (misalnya UM0014 vs AN0014)
|
|
||||||
if (p.noAntrian && matchNoAntrian(cleanInput, p.noAntrian)) {
|
|
||||||
console.log('✅ Found by noAntrian match (kode + angka):', p.noAntrian);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Try parsing as number and match with no (fallback)
|
|
||||||
// IMPORTANT: Hanya gunakan fallback jika input TIDAK punya kode (hanya angka)
|
|
||||||
// Ini mencegah false positive: "RA004" (kode: RA) tidak boleh match dengan pasien no: 4 yang punya kode berbeda
|
|
||||||
const inputHasCode = /^[A-Z]+/.test(cleanInput);
|
|
||||||
if (!inputHasCode) {
|
|
||||||
// Input hanya angka, boleh match dengan no (tapi tetap prioritas ke barcode dan noAntrian)
|
|
||||||
const parsedNo = parseInt(cleanInput.replace(/[^0-9]/g, '')) || parseInt(searchBarcode);
|
|
||||||
if (!isNaN(parsedNo) && p.no === parsedNo) {
|
|
||||||
console.log('✅ Found by no (input tanpa kode):', p.no);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Jika input punya kode (seperti "RA004"), jangan gunakan fallback ke no
|
|
||||||
// karena bisa match dengan pasien yang punya kode berbeda
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2692,13 +2643,13 @@ const checkInManual = async () => {
|
|||||||
|
|
||||||
// IMPORTANT: Refresh data pasien dari queueStore untuk mendapatkan status REAL-TIME
|
// IMPORTANT: Refresh data pasien dari queueStore untuk mendapatkan status REAL-TIME
|
||||||
// Jangan gunakan foundPatient.status karena mungkin sudah stale
|
// Jangan gunakan foundPatient.status karena mungkin sudah stale
|
||||||
// Cari ulang pasien dari queueStore untuk mendapatkan data terbaru
|
// Cari ulang pasien dari queueStore dengan EXACT barcode match
|
||||||
const freshPatient = queueStore.allPatients.find(p => {
|
const freshPatient = queueStore.allPatients.find(p => {
|
||||||
const patientBarcode = String(p.barcode || '').trim();
|
const patientBarcode = String(p.barcode || '').trim();
|
||||||
|
// Hanya exact barcode match untuk menghindari false positive
|
||||||
return patientBarcode === foundPatient.barcode ||
|
return patientBarcode === foundPatient.barcode ||
|
||||||
patientBarcode === searchBarcode ||
|
patientBarcode === searchBarcode ||
|
||||||
patientBarcode === inputValue ||
|
patientBarcode === cleanInput;
|
||||||
p.no === foundPatient.no;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!freshPatient) {
|
if (!freshPatient) {
|
||||||
|
|||||||
+128
-73
@@ -17,6 +17,26 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
return loket1 ? loket1.namaLoket : 'Loket A';
|
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
|
// 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)
|
// Format: YY (tahun 2 digit terakhir) + MM (bulan 2 digit) + DD (tanggal 2 digit) + XXXXX (5 digit sequential)
|
||||||
// Contoh: 26011400001, 26011400002, dst
|
// Contoh: 26011400001, 26011400002, dst
|
||||||
@@ -78,9 +98,11 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
attempts++;
|
attempts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment counter untuk next call dan simpan
|
// IMPORTANT: JANGAN tulis counter ke localStorage di sini!
|
||||||
counter++;
|
// Counter hanya di-increment dan di-save setelah barcode benar-benar digunakan
|
||||||
localStorage.setItem(STORAGE_KEY, String(counter));
|
// 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);
|
localStorage.setItem(LAST_DATE_KEY, currentDate);
|
||||||
|
|
||||||
return barcode;
|
return barcode;
|
||||||
@@ -100,24 +122,73 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
// IMPORTANT: Setiap pasien HARUS memiliki barcode UNIK untuk menghindari konflik
|
// IMPORTANT: Setiap pasien HARUS memiliki barcode UNIK untuk menghindari konflik
|
||||||
// Format barcode menggunakan generateBarcode() untuk konsistensi dengan format baru
|
// Format barcode menggunakan generateBarcode() untuk konsistensi dengan format baru
|
||||||
// Generate barcode dengan memastikan uniqueness menggunakan existingBarcodes array
|
// Generate barcode dengan memastikan uniqueness menggunakan existingBarcodes array
|
||||||
const seedBarcodes = [];
|
// NOTE: Seed data menggunakan barcode statis untuk menghindari increment counter saat refresh
|
||||||
const seedBarcode1 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode1);
|
// Counter hanya di-increment ketika pasien baru benar-benar dibuat dari Anjungan
|
||||||
const seedBarcode2 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode2);
|
|
||||||
const seedBarcode3 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode3);
|
// Gunakan barcode statis untuk seed data (tidak memanggil generateBarcode)
|
||||||
const seedBarcode4 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode4);
|
// Format: YYMMDD + 5 digit sequential dimulai dari 00001
|
||||||
const seedBarcode5 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode5);
|
// Ini mencegah counter naik setiap kali halaman di-refresh
|
||||||
const seedBarcode6 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode6);
|
const getSeedBarcode = (index) => {
|
||||||
const seedBarcode7 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode7);
|
if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {
|
||||||
const seedBarcode8 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode8);
|
const now = new Date();
|
||||||
const seedBarcode9 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode9);
|
const year = String(now.getFullYear()).slice(-2);
|
||||||
const seedBarcode10 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode10);
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||||
const seedBarcode11 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode11);
|
const day = String(now.getDate()).padStart(2, '0');
|
||||||
const seedBarcode12 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcode12);
|
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
|
// Barcode untuk pasien Eksekutif
|
||||||
const seedBarcodeE1 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE1);
|
const seedBarcodeE1 = getSeedBarcode(13);
|
||||||
const seedBarcodeE2 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE2);
|
const seedBarcodeE2 = getSeedBarcode(14);
|
||||||
const seedBarcodeE3 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE3);
|
const seedBarcodeE3 = getSeedBarcode(15);
|
||||||
const seedBarcodeE4 = generateBarcode(seedBarcodes); seedBarcodes.push(seedBarcodeE4);
|
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 = [
|
const seedPatients = [
|
||||||
{
|
{
|
||||||
@@ -843,6 +914,11 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
allPatients.value.push(newPatient);
|
allPatients.value.push(newPatient);
|
||||||
|
|
||||||
|
// Increment counter setelah barcode digunakan
|
||||||
|
if (!patient) {
|
||||||
|
incrementBarcodeCounter();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -874,6 +950,11 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
allPatients.value.push(newPatient);
|
allPatients.value.push(newPatient);
|
||||||
|
|
||||||
|
// Increment counter setelah barcode digunakan
|
||||||
|
if (!patient) {
|
||||||
|
incrementBarcodeCounter();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -938,6 +1019,11 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
allPatients.value.push(newPatient);
|
allPatients.value.push(newPatient);
|
||||||
|
|
||||||
|
// Increment counter setelah barcode digunakan
|
||||||
|
if (!patient) {
|
||||||
|
incrementBarcodeCounter();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -1455,6 +1541,11 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
allPatients.value.push(newPatient);
|
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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -1464,78 +1555,42 @@ export const useQueueStore = defineStore('queue', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check-in patient (update status from waiting to di-loket)
|
// 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) => {
|
const checkInPatient = (patientIdOrBarcode) => {
|
||||||
console.log('🔍 checkInPatient called with:', patientIdOrBarcode);
|
console.log('🔍 checkInPatient called with:', patientIdOrBarcode);
|
||||||
console.log('📊 Total patients in store:', allPatients.value.length);
|
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
|
// Clean input - remove whitespace and normalize
|
||||||
const cleanInput = String(patientIdOrBarcode).trim();
|
const cleanInput = String(patientIdOrBarcode).trim();
|
||||||
const cleanInputUpper = cleanInput.toUpperCase();
|
|
||||||
|
|
||||||
// Try to find by multiple criteria:
|
// PRIORITAS: Hanya cari dengan EXACT barcode match (case-insensitive, whitespace-insensitive)
|
||||||
// 1. Exact barcode match (case-insensitive, whitespace-insensitive)
|
// Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
|
||||||
// 2. Parse as number and match with no
|
// Jangan gunakan fallback ke noAntrian atau no karena bisa menyebabkan false positive
|
||||||
// 3. Extract number from input (e.g., "UM0014" -> "0014" or "14") and match with noAntrian
|
|
||||||
// 4. Check if noAntrian includes the input
|
|
||||||
const patientIndex = allPatients.value.findIndex(p => {
|
const patientIndex = allPatients.value.findIndex(p => {
|
||||||
// Normalize barcode untuk comparison
|
// Normalize barcode untuk comparison
|
||||||
const patientBarcode = String(p.barcode || '').trim();
|
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 ||
|
if (patientBarcode === cleanInput ||
|
||||||
patientBarcodeUpper === cleanInputUpper ||
|
patientBarcode.toLowerCase() === cleanInput.toLowerCase()) {
|
||||||
patientBarcode === patientIdOrBarcode) {
|
console.log('✅ Found by exact barcode match:', patientBarcode, '===', cleanInput);
|
||||||
console.log('✅ Found by barcode:', patientBarcode, '===', cleanInput);
|
|
||||||
return true;
|
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;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (patientIndex === -1) {
|
if (patientIndex === -1) {
|
||||||
console.log('❌ Patient not found. Searched for:', cleanInput);
|
console.log('❌ Patient not found. Searched for barcode:', cleanInput);
|
||||||
console.log('📋 Available noAntrian values:', allPatients.value.map(p => p.noAntrian));
|
console.log('📋 Available barcodes (first 10):', allPatients.value.slice(0, 10).map(p => ({
|
||||||
return { success: false, message: "Pasien tidak ditemukan" };
|
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
|
// IMPORTANT: Get fresh patient data from array to avoid stale data
|
||||||
|
|||||||
Reference in New Issue
Block a user