249 lines
7.4 KiB
Vue
249 lines
7.4 KiB
Vue
<template>
|
|
<v-card
|
|
class="rounded-lg elevation-2 d-flex flex-column ticket-card"
|
|
color="white"
|
|
height="100%"
|
|
>
|
|
<!-- Header dengan background biru -->
|
|
<v-card-title class="ticket-header">
|
|
<div class="ticket-header-content">
|
|
<v-icon color="white" class="mr-2">mdi-hospital-box-outline</v-icon>
|
|
<span class="ticket-header-title">{{ cleanTitle }}</span>
|
|
<v-chip
|
|
v-if="extractedQueueNumber"
|
|
size="small"
|
|
class="ml-2 ticket-header-chip"
|
|
color="primary-700"
|
|
>
|
|
Antrean: {{ extractedQueueNumber }}
|
|
</v-chip>
|
|
</div>
|
|
</v-card-title>
|
|
|
|
<!-- Content -->
|
|
<v-card-text class="ticket-content pa-4">
|
|
|
|
<div class="timeline-scroll-container flex-grow-1">
|
|
<v-timeline density="compact" align="start" line-inset="12" line-color="#567EE7">
|
|
<v-timeline-item
|
|
v-for="(step, index) in steps"
|
|
:key="index"
|
|
:dot-color="getStepColor(index)"
|
|
:icon="getStepIcon(index)"
|
|
size="small"
|
|
icon-color="white"
|
|
>
|
|
<div class="d-flex flex-column align-start">
|
|
<span
|
|
class="font-weight-bold"
|
|
:class="getStepTextClass(index)"
|
|
>
|
|
{{ step.label }}
|
|
</span>
|
|
<span class="text-caption" :class="getStepDateClass(index)">
|
|
{{ step.date !== '-' ? `${step.date}, ${step.time}` : 'Menunggu' }}
|
|
</span>
|
|
</div>
|
|
</v-timeline-item>
|
|
</v-timeline>
|
|
</div>
|
|
|
|
<v-divider class="mt-4 mb-4"></v-divider>
|
|
|
|
<v-btn
|
|
size="large"
|
|
variant="tonal"
|
|
color="#567EE7"
|
|
class="mt-auto font-weight-bold reprint-button text-secondary-500"
|
|
prepend-icon="mdi-printer"
|
|
block
|
|
>
|
|
Cetak Ulang Tiket
|
|
</v-btn>
|
|
</v-card-text>
|
|
</v-card>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
title: String,
|
|
steps: Array,
|
|
color: String,
|
|
currentStepLabel: String,
|
|
queueNumber: String, // Nomor antrean untuk ditampilkan di chip
|
|
});
|
|
|
|
const currentStepIndex = computed(() => {
|
|
return props.steps.findIndex(step => step.label === props.currentStepLabel);
|
|
});
|
|
|
|
// Extract nomor antrean dari title jika queueNumber tidak disediakan
|
|
const extractedQueueNumber = computed(() => {
|
|
if (props.queueNumber) {
|
|
return props.queueNumber;
|
|
}
|
|
// Extract dari title format: "JIWA (Antrean: 1)" atau "RADIOLOGI (Antrean: 5)"
|
|
const match = props.title.match(/\(Antrean:\s*(\d+)\)/);
|
|
return match ? match[1] : null;
|
|
});
|
|
|
|
// Clean title tanpa nomor antrean untuk ditampilkan di header
|
|
const cleanTitle = computed(() => {
|
|
// Hapus bagian "(Antrean: X)" dari title
|
|
return props.title.replace(/\s*\(Antrean:\s*\d+\)/g, '').trim();
|
|
});
|
|
|
|
const getStepIcon = (index) => {
|
|
if (index < currentStepIndex.value) {
|
|
return 'mdi-check';
|
|
} else if (index === currentStepIndex.value) {
|
|
return 'mdi-progress-check';
|
|
} else {
|
|
return 'mdi-circle-outline';
|
|
}
|
|
};
|
|
|
|
const getStepColor = (index) => {
|
|
// Warna berbeda berdasarkan status step (dari _colors.scss)
|
|
if (index < currentStepIndex.value) {
|
|
return '#33A484'; // success-600 - hijau untuk completed
|
|
} else if (index === currentStepIndex.value) {
|
|
return '#FF8441'; // secondary-500 - orange untuk current
|
|
}
|
|
return '#CDD4DC'; // neutral-500 - abu-abu untuk pending
|
|
};
|
|
|
|
const getStepTextClass = (index) => {
|
|
// Warna text label sesuai dengan warna dot
|
|
if (index < currentStepIndex.value) {
|
|
return 'text-success-600'; // hijau untuk completed
|
|
} else if (index === currentStepIndex.value) {
|
|
return 'text-secondary-500'; // orange untuk current
|
|
}
|
|
return 'text-neutral-600'; // abu-abu untuk pending
|
|
};
|
|
|
|
const getStepDateClass = (index) => {
|
|
// Warna text date/time lebih terang
|
|
if (index < currentStepIndex.value) {
|
|
return 'text-success-400'; // hijau terang untuk completed
|
|
} else if (index === currentStepIndex.value) {
|
|
return 'text-secondary-400'; // orange terang untuk current
|
|
}
|
|
return 'text-neutral-500'; // abu-abu untuk pending
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
@import '@/assets/scss/_colors.scss';
|
|
|
|
/* =============================================== */
|
|
/* SCROLLABLE TIMELINE STYLES (Menggunakan Tinggi Tetap) */
|
|
/* =============================================== */
|
|
.timeline-scroll-container {
|
|
/* Flex grow untuk mengisi ruang yang tersedia */
|
|
flex: 1 1 auto;
|
|
min-height: 200px;
|
|
max-height: 400px;
|
|
|
|
/* Membuat konten scrollable di sumbu Y jika melebihi height */
|
|
overflow-y: auto;
|
|
|
|
/* Memberi jarak internal di bagian scrollable */
|
|
padding-right: 8px;
|
|
}
|
|
|
|
/* PENTING: Menghilangkan padding bawah dari V-Timeline bawaan */
|
|
.v-timeline {
|
|
padding-bottom: 0 !important;
|
|
margin-bottom: 0 !important;
|
|
}
|
|
|
|
/* Kustomisasi scrollbar untuk tampilan yang lebih bersih (Opsional) */
|
|
.timeline-scroll-container::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.timeline-scroll-container::-webkit-scrollbar-thumb {
|
|
background-color: rgba(0, 0, 0, 0.2);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
/* =============================================== */
|
|
/* V-TIMELINE KUSTOMISASI */
|
|
/* =============================================== */
|
|
.v-timeline-item :deep(.v-timeline-item__body) {
|
|
padding-inline-start: 16px !important;
|
|
}
|
|
|
|
.v-timeline-item :deep(.v-timeline-item__opposite) {
|
|
display: none;
|
|
}
|
|
|
|
/* =============================================== */
|
|
/* TICKET CARD - FIXED HEIGHT */
|
|
/* =============================================== */
|
|
.ticket-card {
|
|
min-height: 550px !important;
|
|
display: flex !important;
|
|
flex-direction: column !important;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* =============================================== */
|
|
/* TICKET HEADER */
|
|
/* =============================================== */
|
|
.ticket-header {
|
|
background: linear-gradient(135deg, $primary-600 0%, $primary-700 100%);
|
|
color: $neutral-100;
|
|
padding: 16px 20px;
|
|
box-shadow: 0 2px 8px rgba(58, 97, 201, 0.2);
|
|
border-radius: 8px 8px 0 0;
|
|
}
|
|
|
|
.ticket-header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
font-weight: 700;
|
|
font-size: 16px;
|
|
gap: 8px;
|
|
width: 100%;
|
|
}
|
|
|
|
.ticket-header-title {
|
|
flex: 1;
|
|
color: $neutral-100;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.ticket-header-chip {
|
|
color: $neutral-100 !important;
|
|
font-weight: 600;
|
|
font-size: 12px;
|
|
}
|
|
|
|
/* =============================================== */
|
|
/* TICKET CONTENT */
|
|
/* =============================================== */
|
|
.ticket-content {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 24px !important;
|
|
}
|
|
|
|
/* =============================================== */
|
|
/* REPRINT BUTTON - FIXED SIZE */
|
|
/* =============================================== */
|
|
.reprint-button {
|
|
min-height: 48px !important;
|
|
height: 48px !important;
|
|
max-height: 48px !important;
|
|
font-size: 14px !important;
|
|
letter-spacing: 0.5px;
|
|
flex-shrink: 0 !important;
|
|
margin-top: auto !important;
|
|
}
|
|
</style> |