Perubahan format No Antrean
This commit is contained in:
+230
-106
@@ -123,8 +123,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="scanner-instruction">
|
||||
<v-icon :color="primaryColor" size="20" class="mr-2">mdi-information</v-icon>
|
||||
<span class="text-body-2">Arahkan kamera ke QR code. Pastikan QR code berada dalam kotak pemindaian.</span>
|
||||
<v-icon :color="primaryColor" size="16">mdi-information</v-icon>
|
||||
<span class="text-caption">Arahkan kamera ke QR code. Pastikan QR code berada dalam kotak pemindaian.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -313,72 +313,75 @@
|
||||
:class="getStatusClass(item.status)"
|
||||
>
|
||||
<v-card-text class="pa-3">
|
||||
<div class="d-flex justify-space-between align-start">
|
||||
<div class="flex-grow-1">
|
||||
<!-- Status & Method Chips -->
|
||||
<div class="d-flex align-center flex-wrap gap-1 mb-2">
|
||||
<v-chip
|
||||
:color="getStatusColor(item.status)"
|
||||
size="x-small"
|
||||
density="compact"
|
||||
>
|
||||
<v-icon start size="12">{{ getStatusIcon(item.status) }}</v-icon>
|
||||
{{ getStatusText(item.status) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
color="grey-lighten-1"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
density="compact"
|
||||
>
|
||||
{{ item.method }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<!-- Patient ID -->
|
||||
<div class="mb-2">
|
||||
<p class="text-body-2 font-weight-medium mb-0">
|
||||
<v-icon size="14" class="mr-1" :color="primaryColor">mdi-account-circle</v-icon>
|
||||
{{ item.patientId }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Antrean & Pembayaran Chips -->
|
||||
<div class="d-flex align-center flex-wrap gap-1 mb-2">
|
||||
<v-chip
|
||||
v-if="item.klinikQueueNumber"
|
||||
size="x-small"
|
||||
:color="secondaryColor"
|
||||
variant="tonal"
|
||||
density="compact"
|
||||
>
|
||||
<v-icon start size="12">mdi-ticket</v-icon>
|
||||
{{ item.klinikQueueNumber }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="item.pembayaran"
|
||||
size="x-small"
|
||||
:color="primaryColor"
|
||||
variant="tonal"
|
||||
density="compact"
|
||||
>
|
||||
<v-icon start size="12">mdi-credit-card</v-icon>
|
||||
{{ item.pembayaran }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<!-- Time -->
|
||||
<div class="d-flex align-center">
|
||||
<v-chip
|
||||
size="x-small"
|
||||
variant="text"
|
||||
color="grey-darken-1"
|
||||
density="compact"
|
||||
>
|
||||
<v-icon start size="12">mdi-clock-outline</v-icon>
|
||||
{{ formatTime(item.checkInTime) }}
|
||||
</v-chip>
|
||||
</div>
|
||||
<div class="history-item-content">
|
||||
<!-- Status & Method Chips -->
|
||||
<div class="d-flex align-center flex-wrap gap-1 mb-2">
|
||||
<v-chip
|
||||
:color="getStatusColor(item.status)"
|
||||
size="x-small"
|
||||
density="compact"
|
||||
class="history-status-chip"
|
||||
>
|
||||
<v-icon start size="12">{{ getStatusIcon(item.status) }}</v-icon>
|
||||
{{ getStatusText(item.status) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
color="grey-lighten-1"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
density="compact"
|
||||
class="history-method-chip"
|
||||
>
|
||||
{{ item.method }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<!-- Nomor Antrean -->
|
||||
<div class="mb-2">
|
||||
<p class="text-body-2 font-weight-medium mb-0 history-queue-number">
|
||||
<v-icon size="14" class="mr-1" :color="secondaryColor">mdi-ticket</v-icon>
|
||||
{{ item.klinikQueueNumber || item.queueNumber || 'N/A' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Patient ID & Pembayaran Chips -->
|
||||
<div class="d-flex align-center flex-wrap gap-1 mb-2">
|
||||
<v-chip
|
||||
v-if="item.patientId"
|
||||
size="x-small"
|
||||
:color="primaryColor"
|
||||
variant="tonal"
|
||||
density="compact"
|
||||
class="history-patient-chip"
|
||||
>
|
||||
<v-icon start size="12">mdi-account-circle</v-icon>
|
||||
{{ item.patientId }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="item.pembayaran && item.pembayaran !== 'N/A'"
|
||||
size="x-small"
|
||||
:color="primaryColor"
|
||||
variant="tonal"
|
||||
density="compact"
|
||||
class="history-payment-chip"
|
||||
>
|
||||
<v-icon start size="12">mdi-credit-card</v-icon>
|
||||
{{ item.pembayaran }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<!-- Time -->
|
||||
<div class="d-flex align-center">
|
||||
<v-chip
|
||||
size="x-small"
|
||||
variant="text"
|
||||
color="grey-darken-1"
|
||||
density="compact"
|
||||
class="history-time-chip"
|
||||
>
|
||||
<v-icon start size="12">mdi-clock-outline</v-icon>
|
||||
{{ formatTime(item.checkInTime) }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
@@ -887,24 +890,24 @@
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<!-- Patient Info -->
|
||||
<!-- Nomor Antrean Info -->
|
||||
<div class="mb-3">
|
||||
<p class="text-body-1 font-weight-bold mb-2">
|
||||
<v-icon size="18" class="mr-1" :color="primaryColor">mdi-account-circle</v-icon>
|
||||
{{ item.patientId }}
|
||||
<v-icon size="18" class="mr-1" :color="secondaryColor">mdi-ticket</v-icon>
|
||||
{{ item.klinikQueueNumber || item.queueNumber || 'N/A' }}
|
||||
</p>
|
||||
|
||||
<!-- Antrean & Pembayaran Info Grid -->
|
||||
<!-- Patient ID & Pembayaran Info Grid -->
|
||||
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||
<v-chip
|
||||
v-if="item.klinikQueueNumber"
|
||||
v-if="item.patientId"
|
||||
size="small"
|
||||
:color="secondaryColor"
|
||||
:color="primaryColor"
|
||||
variant="tonal"
|
||||
density="comfortable"
|
||||
>
|
||||
<v-icon start size="16">mdi-ticket</v-icon>
|
||||
{{ item.klinikQueueNumber }}
|
||||
<v-icon start size="16">mdi-account-circle</v-icon>
|
||||
{{ item.patientId }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="item.pembayaran"
|
||||
@@ -3131,11 +3134,63 @@ if (typeof window !== 'undefined') {
|
||||
.recent-history-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.recent-history-list::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.recent-history-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.recent-history-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.history-item-content {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.history-item-compact :deep(.v-card-text) {
|
||||
padding: 12px 16px !important;
|
||||
border-left: none !important;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.history-item-compact {
|
||||
transition: all 0.2s ease;
|
||||
border-left: 3px solid transparent;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
.history-item-compact::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
background: transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.history-item-compact :deep(.v-card__underlay) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.history-item-compact :deep(.v-card__border) {
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
.history-item-compact :deep(.v-card__border)::before {
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
.history-item-compact:hover {
|
||||
@@ -3143,19 +3198,69 @@ if (typeof window !== 'undefined') {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.history-item-compact.history-success::before {
|
||||
background: #4caf50 !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-success {
|
||||
border-left-color: #4caf50;
|
||||
background: rgba(76, 175, 80, 0.03);
|
||||
background: rgba(76, 175, 80, 0.03) !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-success :deep(.v-card-text) {
|
||||
background: rgba(76, 175, 80, 0.03) !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-failed::before {
|
||||
background: #f44336 !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-failed {
|
||||
border-left-color: #f44336;
|
||||
background: rgba(244, 67, 54, 0.03);
|
||||
background: rgba(244, 67, 54, 0.03) !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-failed :deep(.v-card-text) {
|
||||
background: rgba(244, 67, 54, 0.03) !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-pending::before {
|
||||
background: #ff9800 !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-pending {
|
||||
border-left-color: #ff9800;
|
||||
background: rgba(255, 152, 0, 0.03);
|
||||
background: rgba(255, 152, 0, 0.03) !important;
|
||||
}
|
||||
|
||||
.history-item-compact.history-pending :deep(.v-card-text) {
|
||||
background: rgba(255, 152, 0, 0.03) !important;
|
||||
}
|
||||
|
||||
.history-status-chip {
|
||||
font-weight: 600;
|
||||
font-size: 11px !important;
|
||||
}
|
||||
|
||||
.history-method-chip {
|
||||
font-size: 10px !important;
|
||||
color: #757575 !important;
|
||||
}
|
||||
|
||||
.history-queue-number {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.history-queue-number .v-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.history-patient-chip,
|
||||
.history-payment-chip {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.history-time-chip {
|
||||
font-size: 11px !important;
|
||||
}
|
||||
|
||||
/* Gap utility untuk flex-wrap */
|
||||
@@ -3245,8 +3350,8 @@ if (typeof window !== 'undefined') {
|
||||
|
||||
.qr-placeholder {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: 300px;
|
||||
max-width: 320px;
|
||||
height: 320px;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #e3f2fd 100%);
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
@@ -3261,7 +3366,7 @@ if (typeof window !== 'undefined') {
|
||||
|
||||
.qr-reader-container {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
max-width: 320px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
@@ -3274,8 +3379,8 @@ if (typeof window !== 'undefined') {
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
||||
background: #000;
|
||||
position: relative;
|
||||
max-width: 500px;
|
||||
max-height: 500px;
|
||||
max-width: 320px;
|
||||
max-height: 320px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -3360,16 +3465,25 @@ if (typeof window !== 'undefined') {
|
||||
.scanner-instruction {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 12px;
|
||||
padding: 10px 12px;
|
||||
justify-content: flex-start;
|
||||
margin-top: 8px;
|
||||
padding: 6px 10px;
|
||||
background: linear-gradient(135deg, rgba(21, 101, 192, 0.1) 0%, rgba(13, 71, 161, 0.1) 100%);
|
||||
border-radius: 10px;
|
||||
border-radius: 8px;
|
||||
color: #1565C0;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
border: 1px solid rgba(21, 101, 192, 0.2);
|
||||
max-width: 320px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.scanner-instruction .v-icon {
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.scanner-loading-overlay {
|
||||
@@ -3951,7 +4065,7 @@ if (typeof window !== 'undefined') {
|
||||
|
||||
.qr-placeholder {
|
||||
max-width: 100%;
|
||||
height: 280px;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.qr-reader-container {
|
||||
@@ -3959,13 +4073,14 @@ if (typeof window !== 'undefined') {
|
||||
}
|
||||
|
||||
.qr-reader-wrapper {
|
||||
min-height: 300px;
|
||||
max-height: 70vh;
|
||||
min-height: 250px;
|
||||
max-height: 250px;
|
||||
max-width: 250px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.qr-reader-wrapper :deep(video) {
|
||||
max-height: 70vh;
|
||||
max-height: 250px;
|
||||
object-fit: contain;
|
||||
transform: scaleX(-1); /* Mirror effect */
|
||||
-webkit-transform: scaleX(-1); /* Safari support */
|
||||
@@ -3975,8 +4090,15 @@ if (typeof window !== 'undefined') {
|
||||
}
|
||||
|
||||
.scanner-instruction {
|
||||
font-size: 12px;
|
||||
padding: 12px;
|
||||
font-size: 10px;
|
||||
padding: 6px 8px;
|
||||
margin-top: 6px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.scanner-instruction .v-icon {
|
||||
font-size: 14px !important;
|
||||
margin-right: 6px !important;
|
||||
}
|
||||
|
||||
.stats-footer-modern {
|
||||
@@ -4023,24 +4145,26 @@ if (typeof window !== 'undefined') {
|
||||
|
||||
.qr-placeholder {
|
||||
max-width: 100%;
|
||||
height: 240px;
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
.qr-reader-wrapper {
|
||||
min-height: 240px;
|
||||
max-height: 280px;
|
||||
min-height: 220px;
|
||||
max-height: 220px;
|
||||
max-width: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile Landscape */
|
||||
@media (max-width: 900px) and (orientation: landscape) {
|
||||
.qr-reader-wrapper {
|
||||
min-height: 50vh;
|
||||
max-height: 60vh;
|
||||
min-height: 250px;
|
||||
max-height: 250px;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.qr-reader-wrapper :deep(video) {
|
||||
max-height: 60vh;
|
||||
max-height: 250px;
|
||||
transform: scaleX(-1); /* Mirror effect */
|
||||
-webkit-transform: scaleX(-1); /* Safari support */
|
||||
/* Reduce brightness to prevent overexposure/bloom */
|
||||
|
||||
+41
-2
@@ -881,6 +881,43 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
currentProcessingPatient.value[adminType] = patient;
|
||||
};
|
||||
|
||||
// Helper function untuk generate nomor antrean baru
|
||||
// Format: 2 huruf (kode klinik) + 3 digit (001-999)
|
||||
// Jika pembayaran BPJS: gunakan kode klinik dari master
|
||||
// Jika pembayaran UMUM/JKMM/SPM/DLL: gunakan "UM" sebagai prefix
|
||||
const generateQueueNumber = (clinic, paymentType) => {
|
||||
// Tentukan prefix berdasarkan jenis pembayaran
|
||||
let prefix = 'UM'; // Default untuk UMUM/JKMM/SPM/DLL
|
||||
|
||||
if (paymentType === 'BPJS') {
|
||||
// Gunakan kode klinik dari master (2 huruf)
|
||||
const clinicCode = clinic?.kode || clinic?.code || 'UM';
|
||||
prefix = clinicCode.length >= 2 ? clinicCode.substring(0, 2).toUpperCase() : 'UM';
|
||||
}
|
||||
|
||||
// Get counter untuk klinik ini per hari
|
||||
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
||||
const counterKey = `queue_counter_${prefix}_${today}`;
|
||||
|
||||
// Get current counter dari localStorage
|
||||
let counter = 0;
|
||||
if (typeof window !== 'undefined') {
|
||||
const stored = localStorage.getItem(counterKey);
|
||||
counter = stored ? parseInt(stored, 10) : 0;
|
||||
}
|
||||
|
||||
// Increment counter (max 999)
|
||||
counter = Math.min(counter + 1, 999);
|
||||
|
||||
// Save counter back to localStorage
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(counterKey, counter.toString());
|
||||
}
|
||||
|
||||
// Format: prefix (2 huruf) + 3 digit
|
||||
return `${prefix}${String(counter).padStart(3, '0')}`;
|
||||
};
|
||||
|
||||
// Register patient from Anjungan (onsite registration)
|
||||
const registerPatientFromAnjungan = (clinic, paymentType, visitType = 'SEKARANG', visitDate = null, shift = 'Shift 1', namaDokter = null) => {
|
||||
const newNo = allPatients.value.length > 0
|
||||
@@ -896,8 +933,9 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
// Generate barcode (simulasi - bisa diganti dengan API call)
|
||||
const barcode = `250811${String(Date.now()).slice(-6)}${String(newNo).padStart(3, "0")}`;
|
||||
|
||||
// Format nomor antrean: UMXXXX | Onsite - barcode
|
||||
const noAntrian = `UM${String(newNo).padStart(4, "0")} | Onsite - ${barcode}`;
|
||||
// Generate nomor antrean dengan format baru: 2 huruf + 3 digit
|
||||
const queueNumber = generateQueueNumber(clinic, paymentType);
|
||||
const noAntrian = `${queueNumber} | Onsite - ${barcode}`;
|
||||
|
||||
// Status awal untuk pasien dari anjungan adalah "menunggu" (belum dipanggil)
|
||||
// Hanya setelah dipanggil oleh admin loket, status berubah menjadi "waiting" (bisa check-in)
|
||||
@@ -1066,6 +1104,7 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
setCurrentProcessing,
|
||||
registerPatientFromAnjungan,
|
||||
checkInPatient,
|
||||
generateQueueNumber,
|
||||
};
|
||||
}, {
|
||||
persist: {
|
||||
|
||||
Reference in New Issue
Block a user