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 @@ - - - Jenis Pembayaran - - - - 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",