update logika pemanggilan, sticky konten, tampilan card
This commit is contained in:
@@ -254,7 +254,7 @@ defineEmits(['action', 'change-klinik', 'process-next', 'call', 'open-klinik-rua
|
||||
}
|
||||
|
||||
.patient-number {
|
||||
font-size: 28px;
|
||||
font-size: 32px;
|
||||
line-height: 36px;
|
||||
font-weight: 700;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
elevation="2"
|
||||
:class="{
|
||||
'clickable-card': isClickable,
|
||||
'fast-track-card': isFastTrack
|
||||
'fast-track-card': isFastTrack && !isCurrentlyProcessing,
|
||||
'processing-card': isCurrentlyProcessing
|
||||
}"
|
||||
@click="handleCardClick"
|
||||
v-bind="isClickable ? tooltipProps : {}"
|
||||
@@ -92,6 +93,9 @@
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useQueueStore } from '~/stores/queueStore';
|
||||
|
||||
const queueStore = useQueueStore();
|
||||
|
||||
const props = defineProps({
|
||||
patient: {
|
||||
@@ -111,6 +115,13 @@ const isFastTrack = computed(() => {
|
||||
return fastTrack === 'YA';
|
||||
});
|
||||
|
||||
const isCurrentlyProcessing = computed(() => {
|
||||
const currentPatient = queueStore.currentProcessingPatient?.loket;
|
||||
if (!currentPatient || !props.patient) return false;
|
||||
return currentPatient.no === props.patient.no ||
|
||||
currentPatient.barcode === props.patient.barcode;
|
||||
});
|
||||
|
||||
const isEksekutif = computed(() => {
|
||||
const pembayaran = (props.patient.pembayaran ?? "").toString().trim().toUpperCase();
|
||||
return pembayaran === 'EKSEKUTIF';
|
||||
@@ -222,6 +233,18 @@ const getStatusLabel = (status) => {
|
||||
border-color: rgba(255, 132, 65, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
&.processing-card {
|
||||
background: var(--color-primary-100) !important;
|
||||
border-color: var(--color-primary-400);
|
||||
box-shadow: 0 4px 12px rgba(25, 118, 210, 0.2) !important;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-primary-200) !important;
|
||||
border-color: var(--color-primary-500);
|
||||
box-shadow: 0 6px 16px rgba(25, 118, 210, 0.25) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/>
|
||||
|
||||
<!-- Global Search and Actions -->
|
||||
<div class="global-search-section mb-4">
|
||||
<div class="global-search-section sticky-search-section">
|
||||
<div class="search-actions-row">
|
||||
<v-text-field
|
||||
v-model="globalSearchQuery"
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Rooms Grid -->
|
||||
<div class="rooms-grid-wrapper">
|
||||
<div class="rooms-grid-wrapper mt-4">
|
||||
<div class="rooms-grid">
|
||||
<v-card
|
||||
v-for="ruang in ruangList"
|
||||
@@ -1601,13 +1601,8 @@ const getPatientCardClass = (patient) => {
|
||||
const baseClass = 'patient-queue-item';
|
||||
const isFastTrack = (patient.fastTrack ?? '').toString().trim().toUpperCase() === 'YA';
|
||||
|
||||
// Pasien yang sudah digenerate/diproses: warna berbeda
|
||||
if (patient.tipeLayanan || patient.calledPemeriksaanAwal || patient.calledTindakan) {
|
||||
return isFastTrack
|
||||
? `${baseClass} patient-queue-item-processed patient-queue-item-fast-track`
|
||||
: `${baseClass} patient-queue-item-processed`;
|
||||
}
|
||||
// Pasien yang perlu digenerate: warna default atau fast track
|
||||
// Jika pasien fast track, tambahkan class fast track
|
||||
// Tidak perlu mengubah warna card jika sudah pemeriksaan awal/tindakan
|
||||
return isFastTrack
|
||||
? `${baseClass} patient-queue-item-default patient-queue-item-fast-track`
|
||||
: `${baseClass} patient-queue-item-default`;
|
||||
@@ -1743,6 +1738,14 @@ onMounted(() => {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.sticky-search-section {
|
||||
position: -webkit-sticky; /* Safari */
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
z-index: 100;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.search-actions-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
@@ -1821,7 +1824,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.current-patient-section {
|
||||
background: linear-gradient(135deg, var(--color-primary-200) 0%, var(--color-primary-300) 100%);
|
||||
background: linear-gradient(135deg, var(--color-primary-50) 0%, var(--color-primary-100) 100%);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--color-primary-400);
|
||||
@@ -2142,17 +2145,6 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
|
||||
/* Pasien yang sudah digenerate/diproses */
|
||||
.patient-queue-item-processed {
|
||||
background: linear-gradient(135deg, var(--color-primary-200) 0%, var(--color-primary-300) 100%);
|
||||
border: 1px solid var(--color-primary-400);
|
||||
}
|
||||
|
||||
.patient-queue-item-processed:hover {
|
||||
background: linear-gradient(135deg, var(--color-primary-300) 0%, var(--color-primary-400) 100%);
|
||||
border-color: var(--color-primary-500);
|
||||
}
|
||||
|
||||
/* Pasien default */
|
||||
.patient-queue-item-default {
|
||||
background: var(--color-neutral-100);
|
||||
@@ -2175,17 +2167,6 @@ onMounted(() => {
|
||||
border-color: rgba(255, 132, 65, 0.6) !important;
|
||||
}
|
||||
|
||||
/* Fast track dengan processed - override processed styling */
|
||||
.patient-queue-item-processed.patient-queue-item-fast-track {
|
||||
background: #FBE9E8 !important;
|
||||
border-color: rgba(255, 132, 65, 0.5) !important;
|
||||
}
|
||||
|
||||
.patient-queue-item-processed.patient-queue-item-fast-track:hover {
|
||||
background: #F1B3AE !important;
|
||||
border-color: rgba(255, 132, 65, 0.6) !important;
|
||||
}
|
||||
|
||||
/* Fast track dengan default - override default styling */
|
||||
.patient-queue-item-default.patient-queue-item-fast-track {
|
||||
background: #FBE9E8 !important;
|
||||
|
||||
+37
-7
@@ -12,7 +12,7 @@
|
||||
<!-- Main Content Grid -->
|
||||
<v-row class="content-grid" dense>
|
||||
<!-- Left Column: Current Patient & Queue Actions -->
|
||||
<v-col cols="12" md="5">
|
||||
<v-col cols="12" md="5" class="sticky-column">
|
||||
<div class="sticky-wrapper">
|
||||
<!-- Current Patient Card -->
|
||||
<CurrentPatientCard
|
||||
@@ -396,11 +396,18 @@ const handleProcessNext = () => {
|
||||
};
|
||||
|
||||
const handleCallPatient = () => {
|
||||
// TODO: Integrate text-to-speech library here
|
||||
// Example: speak(`Nomor antrian ${currentProcessingPatient.value?.noAntrian.split(" |")[0]}, silakan menuju ke loket`)
|
||||
// Panggil pasien yang sedang diproses
|
||||
if (currentProcessingPatient.value) {
|
||||
console.log('Calling patient:', currentProcessingPatient.value);
|
||||
// Placeholder for text-to-speech integration
|
||||
const result = queueStore.callProcessingPatient('loket');
|
||||
if (result.success) {
|
||||
snackbarText.value = result.message;
|
||||
snackbarColor.value = "success";
|
||||
snackbar.value = true;
|
||||
} else {
|
||||
snackbarText.value = result.message;
|
||||
snackbarColor.value = "error";
|
||||
snackbar.value = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -408,6 +415,29 @@ const openKlinikRuangDialog = () => {
|
||||
showKlinikRuangDialog.value = true;
|
||||
};
|
||||
|
||||
// Calculate sticky top offset based on header height
|
||||
const stickyTopOffset = ref(100);
|
||||
|
||||
const updateStickyOffset = () => {
|
||||
const header = document.querySelector('.page-header');
|
||||
if (header) {
|
||||
const headerHeight = header.offsetHeight;
|
||||
const containerPadding = 16;
|
||||
stickyTopOffset.value = headerHeight + containerPadding + 8; // header + padding + minimal space
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
updateStickyOffset();
|
||||
window.addEventListener('resize', updateStickyOffset);
|
||||
// Use nextTick to ensure DOM is fully rendered
|
||||
setTimeout(updateStickyOffset, 100);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateStickyOffset);
|
||||
});
|
||||
|
||||
const closeKlinikRuangDialog = () => {
|
||||
showKlinikRuangDialog.value = false;
|
||||
klinikRuangSearch.value = "";
|
||||
@@ -485,7 +515,6 @@ const buatAntreanKlinikRuang = async (klinikRuang, ruang) => {
|
||||
background: var(--color-neutral-300);
|
||||
min-height: 100vh;
|
||||
padding: 16px;
|
||||
overflow-x: hidden; /* Prevent horizontal scroll */
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
box-sizing: border-box;
|
||||
@@ -508,9 +537,10 @@ const buatAntreanKlinikRuang = async (klinikRuang, ruang) => {
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
position: -webkit-sticky; /* Safari */
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
align-self: flex-start;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.filters {
|
||||
|
||||
@@ -614,12 +614,18 @@ const isInTTSWindow = (queue) => {
|
||||
}
|
||||
|
||||
// Current called queue - tiket yang sedang diproses di admin loket
|
||||
// Nomor antrian menjadi "dipanggil" jika diproses pada AdminLoket
|
||||
// Nomor antrian menjadi "dipanggil" jika diproses pada AdminLoket DAN sudah dipanggil oleh admin
|
||||
const currentCalledQueue = computed(() => {
|
||||
// Prioritas 1: Pasien yang sedang diproses di admin loket (currentProcessingPatient)
|
||||
// Ini adalah pasien yang sedang dipanggil/diproses di AdminLoket
|
||||
// Hanya tampilkan jika sudah dipanggil eksplisit oleh admin (calledByAdmin === true)
|
||||
if (currentProcessingPatient.value) {
|
||||
const processingPatient = currentProcessingPatient.value
|
||||
|
||||
// Cek apakah sudah dipanggil oleh admin
|
||||
if (!processingPatient.calledByAdmin) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Pastikan pasien memiliki noAntrian dan status 'di-loket' (sudah check-in)
|
||||
if (processingPatient.noAntrian && processingPatient.status === 'di-loket') {
|
||||
// Dapatkan nama klinik dari nomor antrian
|
||||
|
||||
+38
-3
@@ -754,7 +754,8 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
allPatients.value[patientIndex] = {
|
||||
...allPatients.value[patientIndex],
|
||||
status: "di-loket",
|
||||
processStage: "klinik"
|
||||
processStage: "klinik",
|
||||
calledByAdmin: false // Reset flag saat pindah stage
|
||||
};
|
||||
message = `Pasien ${patientCode} berhasil check in dan masuk ke Tabel Loket Klinik`;
|
||||
}
|
||||
@@ -784,7 +785,8 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
case "terlambat":
|
||||
allPatients.value[patientIndex] = {
|
||||
...allPatients.value[patientIndex],
|
||||
status: "terlambat"
|
||||
status: "terlambat",
|
||||
calledByAdmin: false // Reset flag
|
||||
};
|
||||
if (currentProcessingPatient.value[adminType]?.no === patient.no) {
|
||||
currentProcessingPatient.value[adminType] = null;
|
||||
@@ -795,7 +797,8 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
case "pending":
|
||||
allPatients.value[patientIndex] = {
|
||||
...allPatients.value[patientIndex],
|
||||
status: "pending"
|
||||
status: "pending",
|
||||
calledByAdmin: false // Reset flag
|
||||
};
|
||||
if (currentProcessingPatient.value[adminType]?.no === patient.no) {
|
||||
currentProcessingPatient.value[adminType] = null;
|
||||
@@ -891,6 +894,35 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
};
|
||||
};
|
||||
|
||||
const callProcessingPatient = (adminType = 'loket') => {
|
||||
// Panggil pasien yang sedang diproses untuk ditampilkan di layar anjungan
|
||||
const processingPatient = currentProcessingPatient.value[adminType];
|
||||
|
||||
if (!processingPatient) {
|
||||
return { success: false, message: "Tidak ada pasien yang sedang diproses" };
|
||||
}
|
||||
|
||||
// Update flag calledByAdmin
|
||||
const patientIndex = allPatients.value.findIndex(p => p.no === processingPatient.no);
|
||||
if (patientIndex !== -1) {
|
||||
allPatients.value[patientIndex] = {
|
||||
...allPatients.value[patientIndex],
|
||||
calledByAdmin: true,
|
||||
lastCalledAt: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Update currentProcessingPatient with the new data
|
||||
currentProcessingPatient.value[adminType] = allPatients.value[patientIndex];
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Memanggil pasien ${processingPatient.noAntrian.split(" |")[0]}`
|
||||
};
|
||||
}
|
||||
|
||||
return { success: false, message: "Gagal memanggil pasien" };
|
||||
};
|
||||
|
||||
const createAntreanKlinik = (klinik, patient = null, adminType = 'loket') => {
|
||||
const newNo = allPatients.value.length + 1;
|
||||
const timestamp = new Date();
|
||||
@@ -1016,6 +1048,7 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
calledTindakan: false,
|
||||
lastCalledAt: null,
|
||||
lastCalledTipeLayanan: null,
|
||||
calledByAdmin: false, // Flag untuk tracking apakah sudah dipanggil oleh admin
|
||||
};
|
||||
|
||||
allPatients.value.push(newPatient);
|
||||
@@ -1535,6 +1568,7 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
visitType: visitType,
|
||||
visitDate: visitDate || timestamp.toISOString().substring(0, 10),
|
||||
namaDokter: namaDokter || null, // Nama dokter hanya untuk pasien Eksekutif
|
||||
calledByAdmin: false, // Flag untuk tracking apakah sudah dipanggil oleh admin
|
||||
// Fast Track data
|
||||
penanggungJawab: (isFastTrack && fastTrackData) ? fastTrackData.penanggungJawab : null,
|
||||
alasanFastTrack: (isFastTrack && fastTrackData) ? fastTrackData.alasanFastTrack : null,
|
||||
@@ -1664,6 +1698,7 @@ export const useQueueStore = defineStore('queue', () => {
|
||||
callNext,
|
||||
callMultiplePatients,
|
||||
processPatient,
|
||||
callProcessingPatient,
|
||||
createAntreanKlinik,
|
||||
createAntreanPenunjang,
|
||||
createAntreanKlinikRuang,
|
||||
|
||||
Reference in New Issue
Block a user