diff --git a/pages/AdminKlinikRuang/[kodeKlinik].vue b/pages/AdminKlinikRuang/[kodeKlinik].vue
index 768cbe2..e21e9ec 100644
--- a/pages/AdminKlinikRuang/[kodeKlinik].vue
+++ b/pages/AdminKlinikRuang/[kodeKlinik].vue
@@ -108,20 +108,20 @@
- mdi-check-circle
+ mdi-check-circle
P.Awal
- mdi-check-circle
+ mdi-check-circle
Tindakan
@@ -1417,10 +1417,11 @@ onMounted(() => {
}
.patient-queue-number {
- font-size: 15px;
+ font-size: 20px;
font-weight: 700;
color: var(--color-neutral-900);
line-height: 1.2;
+ letter-spacing: 0.5px;
}
.patient-queue-status-chips {
@@ -1428,17 +1429,46 @@ onMounted(() => {
gap: 4px;
align-items: center;
flex-wrap: wrap;
+ margin-top: 4px;
}
.status-chip {
font-size: 10px !important;
- height: 18px !important;
+ font-weight: 700 !important;
+ height: 20px !important;
padding: 0 6px !important;
+ min-width: 60px !important;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08) !important;
+ border: 1px solid transparent !important;
+ letter-spacing: 0.1px !important;
+}
+
+.status-chip-pawal {
+ background: #6366f1 !important;
+ color: white !important;
+ border-color: #4f46e5 !important;
+}
+
+.status-chip-pawal:hover {
+ background: #4f46e5 !important;
+ box-shadow: 0 1px 3px rgba(99, 102, 241, 0.15) !important;
+}
+
+.status-chip-tindakan {
+ background: #14b8a6 !important;
+ color: white !important;
+ border-color: #0d9488 !important;
+}
+
+.status-chip-tindakan:hover {
+ background: #0d9488 !important;
+ box-shadow: 0 1px 3px rgba(20, 184, 166, 0.15) !important;
}
.status-chip .v-icon {
- font-size: 10px !important;
- margin-right: 2px;
+ font-size: 11px !important;
+ margin-right: 2px !important;
+ color: white !important;
}
/* Right Section: Actions */
diff --git a/pages/Anjungan/Anjungan/[id].vue b/pages/Anjungan/Anjungan/[id].vue
index 63a2379..50cf657 100644
--- a/pages/Anjungan/Anjungan/[id].vue
+++ b/pages/Anjungan/Anjungan/[id].vue
@@ -24,13 +24,9 @@
2
Pilih: Daftar Sekarang atau Jadwal LainPilih jenis pembayaran dan buat nomor antrian
-
- 3
- Pilih Jenis Pembayaran
-
@@ -237,39 +233,112 @@
-
-
-
- DAFTAR SEKARANG
-
-
- Kuota hari ini habis
-
-
- Pilih dokter terlebih dahulu
-
-
-
+
+
+
+
+
+ DAFTAR SEKARANG
+
+
+ Kuota hari ini habis
+
+
+ Pilih dokter terlebih dahulu
+
+
+
+
+
+
+
+
+
+
+ BPJS
+
+
+
+
+ UMUM / JKMM / SPM / DLL
+
+
+
+
+ JADWAL LAIN
+
+
+
+
+ Kuota hari ini habis
+
+
+
+
+
+
+ Fast Track
+
+
+ Kuota hari ini habis
+
+
+
+
+
+
+
+
JADWAL LAIN
-
+
Pilih dokter terlebih dahulu
@@ -288,47 +357,6 @@
-
-
-
-
-
-
- BPJS
-
-
- UMUM / JKMM / SPM / DLL
-
-
-
-
- Tutup
-
-
-
-
{
});
const showVisitTypeDialog = ref(false);
-const showPaymentTypeDialog = ref(false);
const showBookingFormDialog = ref(false);
const showDoctorSelectionDialog = ref(false);
const showPrintDialog = ref(false);
const lastRegisteredPatient = ref(null);
const selectedClinic = ref(null);
const selectedDoctor = ref(null);
-const pendingPaymentType = ref(null);
const snackbar = ref(false);
const snackbarText = ref("");
const snackbarColor = ref("success");
@@ -1003,9 +1029,7 @@ const selectVisitType = (type) => {
showVisitTypeDialog.value = false;
- if (type === "SEKARANG") {
- showPaymentTypeDialog.value = true;
- } else if (type === "JADWAL_LAIN") {
+ if (type === "JADWAL_LAIN") {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const tomorrowStr = tomorrow.toISOString().substring(0, 10);
@@ -1020,14 +1044,30 @@ const selectVisitType = (type) => {
}
};
-const selectPaymentType = (paymentType) => {
- showPaymentTypeDialog.value = false;
+const registerPatientDirectly = async (paymentType) => {
+ // Validasi: untuk pasien Eksekutif, dokter harus dipilih
+ if (isEksekutif.value && !selectedDoctor.value) {
+ showSnackbar("Mohon pilih dokter terlebih dahulu", "error");
+ return;
+ }
+
+ // Validasi kuota
+ if (isShift1Full(selectedClinic.value)) {
+ showSnackbar("Kuota hari ini sudah habis", "error");
+ return;
+ }
+
+ showVisitTypeDialog.value = false;
// Untuk pasien Eksekutif, dokter sudah dipilih di dialog sebelumnya
// Untuk Reguler/BPJS, tidak perlu dokter
const namaDokter = isEksekutif.value ? selectedDoctor.value : null;
- registerPatient('SEKARANG', paymentType, namaDokter);
+ // Handle Fast Track - paymentType akan menjadi 'UMUM' atau 'BPJS' tapi dengan flag fastTrack
+ const isFastTrack = paymentType === 'FAST_TRACK';
+ const actualPaymentType = isFastTrack ? 'UMUM' : paymentType;
+
+ await registerPatient('SEKARANG', actualPaymentType, namaDokter, isFastTrack);
};
// Fungsi ini tidak lagi digunakan karena dokter dipilih di dialog konfirmasi
@@ -1041,10 +1081,11 @@ const confirmDoctorSelection = () => {
showDoctorSelectionDialog.value = false;
// Register dengan dokter yang dipilih untuk kunjungan hari ini
- registerPatient('SEKARANG', pendingPaymentType.value, selectedDoctor.value);
+ // Untuk eksekutif, paymentType adalah "Eksekutif"
+ registerPatient('SEKARANG', 'Eksekutif', selectedDoctor.value);
};
-const registerPatient = async (visitType, paymentType, namaDokter) => {
+const registerPatient = async (visitType, paymentType, namaDokter, isFastTrack = false) => {
try {
// Validasi bahwa fungsi registerPatientFromAnjungan ada
if (!queueStore) {
@@ -1068,12 +1109,14 @@ const registerPatient = async (visitType, paymentType, namaDokter) => {
visitType,
null,
'Shift 1',
- namaDokter
+ namaDokter,
+ isFastTrack
);
if (result && result.success && result.patient) {
const doctorInfo = namaDokter ? ` dengan dokter ${namaDokter}` : '';
- const message = `Pendaftaran ${selectedClinic.value.name} untuk kunjungan HARI INI dengan pembayaran ${paymentType}${doctorInfo} berhasil diproses. Nomor antrean: ${result.patient.noAntrian?.split(" |")[0] || result.patient.noAntrian || 'N/A'}`;
+ const fastTrackInfo = isFastTrack ? ' (Fast Track)' : '';
+ const message = `Pendaftaran ${selectedClinic.value.name} untuk kunjungan HARI INI dengan pembayaran ${paymentType}${fastTrackInfo}${doctorInfo} berhasil diproses. Nomor antrean: ${result.patient.noAntrian?.split(" |")[0] || result.patient.noAntrian || 'N/A'}`;
showSnackbar(message, "success");
// Simpan data pasien untuk print
@@ -1094,7 +1137,6 @@ const registerPatient = async (visitType, paymentType, namaDokter) => {
} finally {
selectedClinic.value = null;
selectedDoctor.value = null;
- pendingPaymentType.value = null;
}
};
diff --git a/pages/Anjungan/AntrianKlinikRuang/[kodeKlinik].vue b/pages/Anjungan/AntrianKlinikRuang/[kodeKlinik].vue
index 87a0f98..e8fd5d2 100644
--- a/pages/Anjungan/AntrianKlinikRuang/[kodeKlinik].vue
+++ b/pages/Anjungan/AntrianKlinikRuang/[kodeKlinik].vue
@@ -188,9 +188,12 @@ let timeInterval = null
const klinikPatients = computed(() => {
// Get patients from klinik-ruang stage (created from AdminKlinikRuang)
+ // Filter out finished patients (status: 'processed' or 'selesai')
return queueStore.allPatients.filter(p =>
p.processStage === 'klinik-ruang' &&
- p.kodeKlinik === kodeKlinik.value
+ p.kodeKlinik === kodeKlinik.value &&
+ p.status !== 'processed' &&
+ p.status !== 'selesai'
)
})
@@ -789,12 +792,16 @@ watch(anjunganClientId, (newClientId, oldClientId) => {
}
.tipe-label {
- font-size: 12px;
+ font-size: 20px;
font-weight: 700;
color: var(--color-success-700);
- margin-bottom: 8px;
- letter-spacing: 0.5px;
+ margin-bottom: 12px;
+ letter-spacing: 1px;
text-transform: uppercase;
+ padding: 8px 12px;
+ background: var(--color-success-200);
+ border-radius: 8px;
+ display: inline-block;
}
.empty-state-small {
@@ -1036,6 +1043,11 @@ watch(anjunganClientId, (newClientId, oldClientId) => {
font-size: clamp(56px, 4.17vw, 80px);
}
+ .tipe-label {
+ font-size: clamp(16px, 1.04vw, 20px);
+ padding: clamp(6px, 0.42vw, 8px) clamp(10px, 0.63vw, 12px);
+ }
+
.footer-stats-bar {
padding: clamp(12px, 1.04vw, 20px) clamp(24px, 2.08vw, 40px);
gap: clamp(16px, 1.67vw, 32px);
@@ -1125,6 +1137,11 @@ watch(anjunganClientId, (newClientId, oldClientId) => {
font-size: 72px;
}
+ .tipe-label {
+ font-size: 18px;
+ padding: 6px 10px;
+ }
+
.next-item {
font-size: 20px;
padding: 6px 14px;
diff --git a/stores/queueStore.js b/stores/queueStore.js
index 791a08b..a50701b 100644
--- a/stores/queueStore.js
+++ b/stores/queueStore.js
@@ -1105,7 +1105,7 @@ export const useQueueStore = defineStore('queue', () => {
};
// Register patient from Anjungan (onsite registration)
- const registerPatientFromAnjungan = (clinic, paymentType, visitType = 'SEKARANG', visitDate = null, shift = 'Shift 1', namaDokter = null) => {
+ const registerPatientFromAnjungan = (clinic, paymentType, visitType = 'SEKARANG', visitDate = null, shift = 'Shift 1', namaDokter = null, isFastTrack = false) => {
const newNo = allPatients.value.length > 0
? Math.max(...allPatients.value.map(p => p.no)) + 1
: 1;
@@ -1124,8 +1124,10 @@ export const useQueueStore = defineStore('queue', () => {
// Generate nomor antrean dengan format baru: R/E + Loket (A-N) + 3 digit
// Format: RA001 (Reguler), EA001 (Eksekutif/Grand Pavilion), RB001, RB002, dll
+ // Untuk Fast Track, tambahkan prefix "F" di depan: FRA001, FEA001, dll
const queueNumber = generateQueueNumber(clinic, paymentType, isEksekutif);
- const noAntrian = `${queueNumber} | Onsite - ${barcode}`;
+ const finalQueueNumber = isFastTrack ? `F${queueNumber}` : queueNumber;
+ const noAntrian = `${finalQueueNumber} | 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)
@@ -1138,7 +1140,7 @@ export const useQueueStore = defineStore('queue', () => {
noAntrian: noAntrian,
shift: shift,
klinik: clinic.name || clinic,
- fastTrack: "TIDAK", // Default, bisa diubah nanti
+ fastTrack: isFastTrack ? "YA" : "TIDAK",
pembayaran: paymentType,
status: status,
processStage: "loket",