update klinik ruang dan status fast track

This commit is contained in:
bagus-arie05
2026-01-12 08:51:12 +07:00
parent 9ea8300973
commit 27209def4b
3 changed files with 246 additions and 108 deletions
+20 -2
View File
@@ -8,7 +8,10 @@
<v-card
class="patient-card"
elevation="2"
:class="{ 'clickable-card': isClickable }"
:class="{
'clickable-card': isClickable,
'fast-track-card': isFastTrack
}"
@click="handleCardClick"
v-bind="isClickable ? tooltipProps : {}"
>
@@ -18,7 +21,7 @@
<div class="header-left">
<div class="queue-number">{{ patient.noAntrian.split(" |")[0] }}</div>
<v-icon
v-if="patient.fastTrack === 'YA'"
v-if="isFastTrack"
color="warning"
size="20"
class="fast-track-icon"
@@ -107,6 +110,11 @@ const isClickable = computed(() => {
return props.patient.status === 'diloket' || props.patient.status === 'pending';
});
const isFastTrack = computed(() => {
const fastTrack = (props.patient.fastTrack ?? "").toString().trim().toUpperCase();
return fastTrack === 'YA';
});
const handleCardClick = () => {
if (isClickable.value) {
emit('action', props.patient, 'proses');
@@ -161,6 +169,16 @@ const getStatusLabel = (status) => {
transform: translateY(0);
}
}
&.fast-track-card {
background: rgba(255, 230, 198, 0.35) !important;
border-color: rgba(255, 185, 95, 0.4);
&:hover {
background: rgba(255, 230, 198, 0.45) !important;
border-color: rgba(255, 185, 95, 0.5);
}
}
}
.card-header {
+225 -99
View File
@@ -38,16 +38,6 @@
</div>
<div class="patient-details">
<div class="action-buttons mt-3">
<v-btn
color="primary"
variant="flat"
size="small"
class="text-white mr-2 mb-2"
@click="handleCallPatientByTipe(ruang, 'Pemeriksaan Awal')"
>
<v-icon start size="18">mdi-bullhorn</v-icon>
Panggil Pemeriksaan Awal
</v-btn>
<v-btn
color="secondary"
variant="flat"
@@ -103,71 +93,78 @@
:key="patient.no"
:class="getPatientCardClass(patient)"
>
<div class="patient-queue-number">
{{ patient.noAntrian?.split(" |")[0] || patient.barcode || '-' }}
<!-- Left Section: Number and Status -->
<div class="patient-card-left">
<div class="patient-queue-number">
{{ patient.noAntrian?.split(" |")[0] || patient.barcode || '-' }}
</div>
<div class="patient-queue-status-chips">
<!-- Compact status indicators - only show icons if called -->
<v-chip
v-if="patient.calledPemeriksaanAwal"
size="x-small"
color="primary"
class="status-chip"
>
<v-icon start size="10">mdi-check-circle</v-icon>
P.Awal
</v-chip>
<v-chip
v-if="patient.calledTindakan"
size="x-small"
color="secondary"
class="status-chip"
>
<v-icon start size="10">mdi-check-circle</v-icon>
Tindakan
</v-chip>
</div>
</div>
<div class="patient-queue-info">
<!-- Penanda Panggilan Pemeriksaan Awal -->
<v-chip
v-if="patient.calledPemeriksaanAwal"
size="x-small"
color="primary"
class="mr-1"
>
<v-icon start size="12">mdi-check-circle</v-icon>
Pemeriksaan Awal
</v-chip>
<!-- Penanda Panggilan Tindakan -->
<v-chip
v-if="patient.calledTindakan"
size="x-small"
color="secondary"
class="mr-1"
>
<v-icon start size="12">mdi-check-circle</v-icon>
Tindakan
</v-chip>
<!-- Penanda Status Pending -->
<v-chip
v-if="patient.status === 'pending'"
size="x-small"
color="error"
class="mr-1"
>
<v-icon start size="12">mdi-pause-circle</v-icon>
Pending
</v-chip>
</div>
<div class="patient-queue-actions">
<v-btn
icon
size="x-small"
variant="text"
@click="showPatientDetail(patient)"
>
<v-icon size="16">mdi-eye</v-icon>
<v-tooltip activator="parent">Lihat Detail</v-tooltip>
</v-btn>
<v-btn
icon
size="x-small"
variant="text"
color="warning"
@click="handlePindahRuang(patient, ruang)"
>
<v-icon size="16">mdi-swap-horizontal</v-icon>
<v-tooltip activator="parent">Pindah Ruang</v-tooltip>
</v-btn>
<v-btn
size="x-small"
variant="flat"
color="primary"
class="text-white"
@click="patient.status === 'pending' ? handleProcessPendingPatient(ruang, patient) : handleProcessPatient(ruang, patient)"
>
<v-icon start size="14">mdi-play</v-icon>
Proses
</v-btn>
<!-- Right Section: Actions -->
<div class="patient-card-right">
<!-- Action Buttons -->
<div class="patient-queue-actions">
<v-btn
icon
size="x-small"
variant="text"
@click="showPatientDetail(patient)"
>
<v-icon size="16">mdi-eye</v-icon>
<v-tooltip activator="parent">Lihat Detail</v-tooltip>
</v-btn>
<v-btn
icon
size="x-small"
variant="text"
color="warning"
@click="handlePindahRuang(patient, ruang)"
>
<v-icon size="16">mdi-swap-horizontal</v-icon>
<v-tooltip activator="parent">Pindah Ruang</v-tooltip>
</v-btn>
<v-btn
size="x-small"
variant="flat"
color="primary"
class="text-white call-btn"
@click="handleCallPatientFromList(ruang, patient, 'Pemeriksaan Awal')"
>
<v-icon start size="14">mdi-bullhorn</v-icon>
Pemeriksaan Awal
</v-btn>
<v-btn
size="x-small"
variant="flat"
color="success"
class="text-white"
@click="patient.status === 'pending' ? handleProcessPendingPatient(ruang, patient) : handleProcessPatient(ruang, patient)"
>
<v-icon start size="14">mdi-play</v-icon>
Proses
</v-btn>
</div>
</div>
</div>
</div>
@@ -602,15 +599,31 @@ const confirmPindahRuang = () => {
selectedRuangBaru.value = null;
};
const handleCallPatientByTipe = (ruang, tipeLayanan) => {
const current = getCurrentProcessingForRoom(ruang);
if (!current || !current.no) {
showSnackbar('Tidak ada pasien yang sedang diproses', 'error');
// Handle call patient from list (dari card pasien di daftar)
const handleCallPatientFromList = (ruang, patient, tipeLayanan) => {
if (!patient || !patient.no) {
showSnackbar('Pasien tidak valid', 'error');
return;
}
// Pastikan pasien sudah diproses terlebih dahulu
const current = getCurrentProcessingForRoom(ruang);
if (!current || current.no !== patient.no) {
// Jika pasien belum diproses, proses dulu
const processResult = queueStore.processPatientKlinikRuang(
patient,
'proses',
klinikData.value.kodeKlinik,
ruang.nomorRuang
);
if (!processResult.success) {
showSnackbar(processResult.message, 'error');
return;
}
}
// Update tipeLayanan pasien untuk tracking panggilan terakhir
const patientIndex = queueStore.allPatients.findIndex(p => p.no === current.no);
const patientIndex = queueStore.allPatients.findIndex(p => p.no === patient.no);
if (patientIndex === -1) {
showSnackbar('Pasien tidak ditemukan', 'error');
return;
@@ -737,6 +750,50 @@ const getStatusLabel = (status) => {
};
// Handle call patient by tipe (untuk pasien yang sedang diproses)
const handleCallPatientByTipe = (ruang, tipeLayanan) => {
const current = getCurrentProcessingForRoom(ruang);
if (!current || !current.no) {
showSnackbar('Tidak ada pasien yang sedang diproses', 'error');
return;
}
// Update tipeLayanan pasien untuk tracking panggilan terakhir
const patientIndex = queueStore.allPatients.findIndex(p => p.no === current.no);
if (patientIndex === -1) {
showSnackbar('Pasien tidak ditemukan', 'error');
return;
}
// Update tracking panggilan berdasarkan tipeLayanan
// Update tipeLayanan pasien agar muncul di kolom yang sesuai di Anjungan
const updateData = {
...queueStore.allPatients[patientIndex],
status: 'di-loket',
tipeLayanan: tipeLayanan, // Update tipeLayanan untuk display di Anjungan
lastCalledAt: new Date().toISOString(),
lastCalledTipeLayanan: tipeLayanan
};
// Set penanda panggilan sesuai tipeLayanan
if (tipeLayanan === 'Pemeriksaan Awal') {
updateData.calledPemeriksaanAwal = true;
} else if (tipeLayanan === 'Tindakan') {
updateData.calledTindakan = true;
}
queueStore.allPatients[patientIndex] = updateData;
// Update current processing (tetap 1 pasien, tidak dipisah per tipe)
const key = `klinik-ruang-${klinikData.value.kodeKlinik}-${ruang.nomorRuang}`;
queueStore.currentProcessingPatient[key] = queueStore.allPatients[patientIndex];
const patientCode = queueStore.allPatients[patientIndex].noAntrian?.split(" |")[0] ||
queueStore.allPatients[patientIndex].barcode ||
'-';
showSnackbar(`Memanggil pasien ${patientCode} untuk ${tipeLayanan}`, 'success');
};
const handlePatientAction = (ruang, action) => {
const current = getCurrentProcessingForRoom(ruang);
if (!current) return;
@@ -822,12 +879,16 @@ const paginatedAllPatients = (ruang) => {
// Helper untuk menentukan class card berdasarkan status pasien
const getPatientCardClass = (patient) => {
const baseClass = 'patient-queue-item';
// Pasien yang sudah digenerate/diproses & pending: warna berbeda
if (patient.tipeLayanan || patient.status === 'pending') {
// Pasien dengan status pending: warna merah
if (patient.status === 'pending') {
return `${baseClass} patient-queue-item-pending-status`;
}
// Pasien yang sudah digenerate/diproses: warna berbeda
if (patient.tipeLayanan || patient.calledPemeriksaanAwal || patient.calledTindakan) {
return `${baseClass} patient-queue-item-processed`;
}
// Pasien yang perlu digenerate: warna default
return `${baseClass} patient-queue-item-pending`;
return `${baseClass} patient-queue-item-default`;
};
@@ -1188,57 +1249,122 @@ onMounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-radius: 6px;
padding: 10px 14px;
border-radius: 8px;
transition: all 0.2s ease;
gap: 8px;
gap: 12px;
min-height: 56px;
}
/* Pasien yang sudah digenerate/diproses & pending */
/* Pasien dengan status pending: warna merah */
.patient-queue-item-pending-status {
background: linear-gradient(135deg, #ffebee 0%, #ffcdd2 100%);
border: 2px solid #ef5350;
}
.patient-queue-item-pending-status:hover {
background: linear-gradient(135deg, #ffcdd2 0%, #ef9a9a 100%);
border-color: #e53935;
}
/* Pasien yang sudah digenerate/diproses */
.patient-queue-item-processed {
background: var(--color-primary-50);
border: 1px solid var(--color-primary-200);
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border: 1px solid #90caf9;
}
.patient-queue-item-processed:hover {
background: var(--color-primary-100);
border-color: var(--color-primary-400);
background: linear-gradient(135deg, #bbdefb 0%, #90caf9 100%);
border-color: #64b5f6;
}
/* Pasien yang perlu digenerate */
.patient-queue-item-pending {
/* Pasien default */
.patient-queue-item-default {
background: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300);
}
.patient-queue-item-pending:hover {
background: var(--color-warning-100);
border-color: var(--color-warning-400);
.patient-queue-item-default:hover {
background: var(--color-neutral-200);
border-color: var(--color-neutral-400);
}
/* Left Section: Number and Status */
.patient-card-left {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 100px;
flex-shrink: 0;
}
.patient-queue-number {
font-size: 14px;
font-size: 15px;
font-weight: 700;
color: var(--color-neutral-900);
min-width: 80px;
line-height: 1.2;
}
.patient-queue-info {
.patient-queue-status-chips {
display: flex;
gap: 4px;
align-items: center;
flex-wrap: wrap;
}
.status-chip {
font-size: 10px !important;
height: 18px !important;
padding: 0 6px !important;
}
.status-chip .v-icon {
font-size: 10px !important;
margin-right: 2px;
}
/* Right Section: Actions */
.patient-card-right {
display: flex;
align-items: center;
flex: 1;
justify-content: flex-end;
min-width: 0;
}
.patient-queue-actions {
display: flex;
gap: 8px;
gap: 6px;
align-items: center;
flex-wrap: nowrap;
}
.patient-queue-actions .v-btn {
text-transform: none;
font-weight: 600;
flex-shrink: 0;
}
.call-btn {
min-width: 130px;
font-size: 11px;
white-space: nowrap;
}
.call-btn .v-icon {
margin-right: 4px;
}
/* Responsive untuk card */
@media (max-width: 1400px) {
.call-btn {
min-width: 100px;
font-size: 10px;
}
.call-btn .v-icon {
margin-right: 2px;
}
}
@media (max-width: 1400px) {
+1 -7
View File
@@ -514,15 +514,11 @@ const buatAntreanKlinikRuang = async (klinikRuang, ruang) => {
}
.content-grid {
margin: 0;
width: 100%;
box-sizing: border-box;
margin: 0 -8px;
}
.content-grid > .v-col {
padding: 8px;
box-sizing: border-box;
min-width: 0; /* Prevent flex item overflow */
}
.content-grid > .v-col:first-child {
@@ -570,8 +566,6 @@ const buatAntreanKlinikRuang = async (klinikRuang, ruang) => {
position: sticky;
top: 16px;
align-self: flex-start;
max-height: calc(100vh - 32px);
overflow-y: auto;
}
.filters {