update tampilan dan flow di loket dan master
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-4">
|
||||
<!-- Patient Info Card -->
|
||||
<!-- patient info card -->
|
||||
<div v-if="patient" class="patient-card mb-4">
|
||||
<h4 class="card-title">Informasi Pasien</h4>
|
||||
<v-row>
|
||||
@@ -26,7 +26,7 @@
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<!-- Search Field -->
|
||||
<!-- search field -->
|
||||
<v-text-field
|
||||
v-model="searchModel"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
@@ -37,7 +37,7 @@
|
||||
class="mb-4 search-field"
|
||||
/>
|
||||
|
||||
<!-- Options Grid -->
|
||||
<!-- options grid -->
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="option in filteredOptions"
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Button for non-clickable states -->
|
||||
|
||||
<div v-if="!isClickable && patient.status === 'terlambat'" class="card-actions mt-3">
|
||||
<v-btn
|
||||
block
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
:value="status.value"
|
||||
:class="{ 'active-chip': selectedStatusModel === status.value }"
|
||||
>
|
||||
<v-icon v-if="status.icon" start size="16">{{ status.icon }}</v-icon>
|
||||
{{ status.label }} ({{ status.count }})
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
@@ -76,21 +77,6 @@
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<v-select
|
||||
v-model="selectedFastTrack"
|
||||
:items="fastTrackOptions"
|
||||
label="Filter Fast Track"
|
||||
density="compact"
|
||||
hide-details
|
||||
clearable
|
||||
class="filter-select"
|
||||
variant="outlined"
|
||||
>
|
||||
<template #prepend-inner>
|
||||
<v-icon size="20">mdi-flash</v-icon>
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<v-btn
|
||||
v-if="hasActiveFilters"
|
||||
variant="text"
|
||||
@@ -130,12 +116,12 @@
|
||||
{{ selectedShift }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="selectedFastTrack"
|
||||
v-if="selectedFastTrackModel"
|
||||
size="small"
|
||||
closable
|
||||
@click:close="selectedFastTrack = null"
|
||||
@click:close="selectedFastTrackModel = null"
|
||||
>
|
||||
Fast Track: {{ selectedFastTrack }}
|
||||
Fast Track: {{ selectedFastTrackModel }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -231,21 +217,55 @@ const props = defineProps({
|
||||
terlambat: 'Terlambat',
|
||||
pending: 'Pending'
|
||||
})
|
||||
},
|
||||
selectedFastTrack: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
fastTrackOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:selectedStatus', 'update:searchQuery', 'action']);
|
||||
const emit = defineEmits(['update:selectedStatus', 'update:searchQuery', 'update:selectedFastTrack', 'action']);
|
||||
|
||||
const currentPage = ref(1);
|
||||
const selectedKlinik = ref(null);
|
||||
const selectedPembayaran = ref(null);
|
||||
const selectedShift = ref(null);
|
||||
const selectedFastTrack = ref(null);
|
||||
|
||||
const selectedStatusModel = computed({
|
||||
get: () => props.selectedStatus,
|
||||
const selectedFastTrackModel = computed({
|
||||
get: () => props.selectedFastTrack,
|
||||
set: (value) => {
|
||||
currentPage.value = 1;
|
||||
emit('update:selectedFastTrack', value);
|
||||
}
|
||||
});
|
||||
|
||||
const selectedStatusModel = computed({
|
||||
get: () => {
|
||||
// If Fast Track is selected, return 'fasttrack' as status
|
||||
if (selectedFastTrackModel.value === 'YA') {
|
||||
return 'fasttrack';
|
||||
}
|
||||
return props.selectedStatus;
|
||||
},
|
||||
set: (value) => {
|
||||
currentPage.value = 1;
|
||||
|
||||
// Handle Fast Track selection
|
||||
if (value === 'fasttrack') {
|
||||
selectedFastTrackModel.value = 'YA';
|
||||
// Emit 'all' as status since Fast Track is a separate filter
|
||||
emit('update:selectedStatus', 'all');
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset Fast Track when other status is selected
|
||||
if (selectedFastTrackModel.value) {
|
||||
selectedFastTrackModel.value = null;
|
||||
}
|
||||
emit('update:selectedStatus', value);
|
||||
}
|
||||
});
|
||||
@@ -280,6 +300,16 @@ const statusOptions = computed(() => {
|
||||
{ value: 'pending', label: props.statusLabels.pending, count: props.pendingCount }
|
||||
);
|
||||
|
||||
// Add Fast Track as a status option
|
||||
if (fastTrackYaCount.value > 0) {
|
||||
baseOptions.push({
|
||||
value: 'fasttrack',
|
||||
label: 'Fast Track',
|
||||
count: fastTrackYaCount.value,
|
||||
icon: 'mdi-flash'
|
||||
});
|
||||
}
|
||||
|
||||
return baseOptions;
|
||||
});
|
||||
|
||||
@@ -299,28 +329,36 @@ const shiftOptions = computed(() => {
|
||||
return shifts.sort();
|
||||
});
|
||||
|
||||
const fastTrackOptions = computed(() => {
|
||||
const normalizedValues = props.items
|
||||
.map((p) => (p.fastTrack ?? "").toString().trim().toUpperCase())
|
||||
.filter((v) => v.length > 0);
|
||||
|
||||
const uniqueTracks = [...new Set(normalizedValues)];
|
||||
return uniqueTracks.sort();
|
||||
// Count Fast Track "YA"
|
||||
const fastTrackYaCount = computed(() => {
|
||||
return props.items.filter(p => {
|
||||
const patientFastTrack = (p.fastTrack ?? "").toString().trim().toUpperCase();
|
||||
return patientFastTrack === 'YA';
|
||||
}).length;
|
||||
});
|
||||
|
||||
|
||||
const hasActiveFilters = computed(() => {
|
||||
return !!(selectedKlinik.value || selectedPembayaran.value || selectedShift.value || selectedFastTrack.value);
|
||||
return !!(selectedKlinik.value || selectedPembayaran.value || selectedShift.value || selectedFastTrackModel.value);
|
||||
});
|
||||
|
||||
const clearAllFilters = () => {
|
||||
selectedKlinik.value = null;
|
||||
selectedPembayaran.value = null;
|
||||
selectedShift.value = null;
|
||||
selectedFastTrack.value = null;
|
||||
selectedFastTrackModel.value = null;
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
const filteredItems = computed(() => {
|
||||
// Handle Fast Track as a status category
|
||||
if (selectedStatusModel.value === 'fasttrack') {
|
||||
return props.items.filter(p => {
|
||||
const patientFastTrack = (p.fastTrack ?? "").toString().trim().toUpperCase();
|
||||
return patientFastTrack === 'YA';
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedStatusModel.value === 'all') return props.items;
|
||||
return props.items.filter(p => p.status === selectedStatusModel.value);
|
||||
});
|
||||
@@ -338,9 +376,7 @@ const filteredAndSearchedItems = computed(() => {
|
||||
if (selectedShift.value) {
|
||||
result = result.filter(p => p.shift === selectedShift.value);
|
||||
}
|
||||
if (selectedFastTrack.value) {
|
||||
result = result.filter(p => p.fastTrack === selectedFastTrack.value);
|
||||
}
|
||||
// Fast Track filtering is now handled in filteredItems as a status category
|
||||
|
||||
// Apply search
|
||||
if (searchModel.value) {
|
||||
|
||||
+214
-57
@@ -13,71 +13,127 @@
|
||||
<v-row class="content-grid" dense>
|
||||
<!-- Left Column: Current Patient & Queue Actions -->
|
||||
<v-col cols="12" md="5">
|
||||
<!-- Current Patient Card -->
|
||||
<CurrentPatientCard
|
||||
:patient="currentProcessingPatient"
|
||||
theme="secondary"
|
||||
:has-next-queue="(diLoketPatients || []).length > 0"
|
||||
:next-queue-info="nextQueueInfo"
|
||||
@action="handlePatientAction"
|
||||
@change-klinik="showChangeKlinikDialog = true"
|
||||
@process-next="handleProcessNext"
|
||||
/>
|
||||
<div class="sticky-wrapper">
|
||||
<!-- Current Patient Card -->
|
||||
<CurrentPatientCard
|
||||
:patient="currentProcessingPatient"
|
||||
theme="secondary"
|
||||
:has-next-queue="(diLoketPatients || []).length > 0"
|
||||
:next-queue-info="nextQueueInfo"
|
||||
@action="handlePatientAction"
|
||||
@change-klinik="showChangeKlinikDialog = true"
|
||||
@process-next="handleProcessNext"
|
||||
/>
|
||||
|
||||
<!-- Queue Actions Card -->
|
||||
<QueueActionsCard
|
||||
class="mt-3"
|
||||
:total-quota="150"
|
||||
:used-quota="quotaUsed"
|
||||
:has-next="!!nextPatient"
|
||||
@call="handleCall"
|
||||
/>
|
||||
<!-- Queue Actions Card -->
|
||||
<QueueActionsCard
|
||||
class="mt-3"
|
||||
:total-quota="150"
|
||||
:used-quota="quotaUsed"
|
||||
:has-next="!!nextPatient"
|
||||
@call="handleCall"
|
||||
/>
|
||||
|
||||
<!-- Create Queue Buttons -->
|
||||
<div class="create-buttons mt-3">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="6" class="pr-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6"
|
||||
color="primary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="showKlinikDialog = true"
|
||||
>
|
||||
<v-icon start>mdi-hospital-building</v-icon>
|
||||
Buat Antrean Klinik
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<!-- Create Queue Buttons -->
|
||||
<div class="create-buttons mt-3">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="6" class="pr-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6"
|
||||
color="primary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="showKlinikDialog = true"
|
||||
>
|
||||
<v-icon start>mdi-hospital-building</v-icon>
|
||||
Buat Antrean Klinik
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="6" class="pl-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6 text-white"
|
||||
color="secondary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="openPenunjangDialog()"
|
||||
>
|
||||
<v-icon start>mdi-clipboard-pulse</v-icon>
|
||||
Buat Antrean Penunjang
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-col cols="6" class="pl-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6 text-white"
|
||||
color="secondary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="openPenunjangDialog()"
|
||||
>
|
||||
<v-icon start>mdi-clipboard-pulse</v-icon>
|
||||
Buat Antrean Penunjang
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<!-- Right Column: Patient Table -->
|
||||
<v-col cols="12" md="7">
|
||||
<PatientDataTable
|
||||
:items="allPatients"
|
||||
v-model:selected-status="selectedStatus"
|
||||
v-model:search-query="searchQuery"
|
||||
:di-loket-count="(diLoketPatients || []).length"
|
||||
:terlambat-count="(terlambatPatients || []).length"
|
||||
:pending-count="(pendingPatients || []).length"
|
||||
:status-labels="statusLabels"
|
||||
:show-diproses="false"
|
||||
@action="handleTableAction"
|
||||
/>
|
||||
<v-card class="patient-data-container" elevation="0">
|
||||
<v-card-text class="pa-4">
|
||||
<!-- Header with Filters -->
|
||||
<div class="data-header mb-4">
|
||||
<div class="section-label">DATA PASIEN</div>
|
||||
|
||||
<div class="filters">
|
||||
<!-- Status Filter -->
|
||||
<v-chip-group v-model="selectedStatus" mandatory class="status-filter">
|
||||
<v-chip value="all" :class="{ 'active-chip': selectedStatus === 'all' }">
|
||||
Semua ({{ allPatients.length }})
|
||||
</v-chip>
|
||||
<v-chip value="diloket" :class="{ 'active-chip': selectedStatus === 'diloket' }">
|
||||
Di Klinik ({{ (diLoketPatients || []).length }})
|
||||
</v-chip>
|
||||
<v-chip value="terlambat" :class="{ 'active-chip': selectedStatus === 'terlambat' }">
|
||||
Terlambat ({{ (terlambatPatients || []).length }})
|
||||
</v-chip>
|
||||
<v-chip value="pending" :class="{ 'active-chip': selectedStatus === 'pending' }">
|
||||
Pending ({{ (pendingPatients || []).length }})
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
|
||||
<!-- Fast Track Filter -->
|
||||
<v-select
|
||||
v-model="selectedFastTrack"
|
||||
:items="fastTrackOptions"
|
||||
label="Filter Fast Track"
|
||||
density="compact"
|
||||
hide-details
|
||||
clearable
|
||||
class="fast-track-filter"
|
||||
variant="outlined"
|
||||
>
|
||||
<template #prepend-inner>
|
||||
<v-icon size="20">mdi-flash</v-icon>
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<!-- Search Field -->
|
||||
<v-text-field
|
||||
v-model="searchQuery"
|
||||
placeholder="Cari barcode, nomor antrian..."
|
||||
density="compact"
|
||||
hide-details
|
||||
class="search-field"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PatientDataTable
|
||||
:items="allPatients"
|
||||
v-model:selected-status="selectedStatus"
|
||||
v-model:search-query="searchQuery"
|
||||
v-model:selected-fast-track="selectedFastTrack"
|
||||
:di-loket-count="(diLoketPatients || []).length"
|
||||
:terlambat-count="(terlambatPatients || []).length"
|
||||
:pending-count="(pendingPatients || []).length"
|
||||
:status-labels="statusLabels"
|
||||
:show-diproses="false"
|
||||
@action="handleTableAction"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -170,6 +226,16 @@ const currentDate = ref(
|
||||
|
||||
const selectedStatus = ref("all");
|
||||
const searchQuery = ref("");
|
||||
const selectedFastTrack = ref(null);
|
||||
|
||||
// Fast Track options from all patients
|
||||
const fastTrackOptions = computed(() => {
|
||||
const normalizedValues = allPatients.value
|
||||
.map((p) => (p.fastTrack ?? "").toString().trim().toUpperCase())
|
||||
.filter((v) => v.length > 0);
|
||||
const uniqueTracks = [...new Set(normalizedValues)];
|
||||
return uniqueTracks.sort();
|
||||
});
|
||||
|
||||
// Custom status labels for Klinik
|
||||
const statusLabels = {
|
||||
@@ -254,9 +320,100 @@ const handleProcessNext = () => {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
align-self: flex-start;
|
||||
max-height: calc(100vh - 32px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.status-filter {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.status-filter .v-chip {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
height: 32px;
|
||||
border: 1px solid var(--color-neutral-500);
|
||||
background: var(--color-neutral-100);
|
||||
color: var(--color-neutral-600);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.status-filter .v-chip:hover {
|
||||
background: var(--color-neutral-300);
|
||||
}
|
||||
|
||||
.status-filter .v-chip.active-chip {
|
||||
background: var(--color-secondary-600);
|
||||
color: var(--color-neutral-100);
|
||||
border-color: var(--color-secondary-600);
|
||||
}
|
||||
|
||||
.fast-track-filter {
|
||||
min-width: 180px;
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
.search-field {
|
||||
max-width: 300px;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--color-neutral-600);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.data-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.patient-data-container {
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--color-neutral-500);
|
||||
background: var(--color-neutral-100);
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.loket-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
position: relative;
|
||||
top: 0;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.filters {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.status-filter {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.search-field,
|
||||
.fast-track-filter {
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+176
-57
@@ -13,71 +13,95 @@
|
||||
<v-row class="content-grid" dense>
|
||||
<!-- Left Column: Current Patient & Queue Actions -->
|
||||
<v-col cols="12" md="5">
|
||||
<!-- Current Patient Card -->
|
||||
<CurrentPatientCard
|
||||
:patient="currentProcessingPatient"
|
||||
:has-next-queue="(diLoketPatients || []).length > 0"
|
||||
:next-queue-info="nextQueueInfo"
|
||||
@action="handlePatientAction"
|
||||
@change-klinik="showChangeKlinikDialog = true"
|
||||
@process-next="handleProcessNext"
|
||||
/>
|
||||
<div class="sticky-wrapper">
|
||||
<!-- Current Patient Card -->
|
||||
<CurrentPatientCard
|
||||
:patient="currentProcessingPatient"
|
||||
:has-next-queue="(diLoketPatients || []).length > 0"
|
||||
:next-queue-info="nextQueueInfo"
|
||||
@action="handlePatientAction"
|
||||
@change-klinik="showChangeKlinikDialog = true"
|
||||
@process-next="handleProcessNext"
|
||||
/>
|
||||
|
||||
<!-- Queue Actions Card -->
|
||||
<QueueActionsCard
|
||||
class="mt-3"
|
||||
:total-quota="150"
|
||||
:used-quota="quotaUsed"
|
||||
:has-next="!!nextPatient"
|
||||
@call="handleCall"
|
||||
/>
|
||||
<!-- Queue Actions Card -->
|
||||
<QueueActionsCard
|
||||
class="mt-3"
|
||||
:total-quota="150"
|
||||
:used-quota="quotaUsed"
|
||||
:has-next="!!nextPatient"
|
||||
@call="handleCall"
|
||||
/>
|
||||
|
||||
<!-- Create Queue Buttons -->
|
||||
<div class="create-buttons mt-3">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="6" class="pr-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6"
|
||||
color="primary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="showKlinikDialog = true"
|
||||
>
|
||||
<v-icon start>mdi-hospital-building</v-icon>
|
||||
Buat Antrean Klinik
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<!-- Create Queue Buttons -->
|
||||
<div class="create-buttons mt-3">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="6" class="pr-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6"
|
||||
color="primary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="showKlinikDialog = true"
|
||||
>
|
||||
<v-icon start>mdi-hospital-building</v-icon>
|
||||
Buat Antrean Klinik
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="6" class="pl-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6 text-white"
|
||||
color="secondary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="openPenunjangDialog()"
|
||||
>
|
||||
<v-icon start>mdi-clipboard-pulse</v-icon>
|
||||
Buat Antrean Penunjang
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-col cols="6" class="pl-2">
|
||||
<v-btn
|
||||
block
|
||||
class="py-6 text-white"
|
||||
color="secondary-600"
|
||||
:disabled="!currentProcessingPatient"
|
||||
@click="openPenunjangDialog()"
|
||||
>
|
||||
<v-icon start>mdi-clipboard-pulse</v-icon>
|
||||
Buat Antrean Penunjang
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</v-col>
|
||||
|
||||
<!-- Right Column: Patient Table -->
|
||||
<v-col cols="12" md="7">
|
||||
<PatientDataTable
|
||||
:items="allPatientsForStage"
|
||||
v-model:selected-status="selectedStatus"
|
||||
v-model:search-query="searchQuery"
|
||||
:di-loket-count="diLoketCount"
|
||||
:diproses-count="currentProcessingPatient ? 1 : 0"
|
||||
:terlambat-count="(terlambatPatients || []).length"
|
||||
:pending-count="(pendingPatients || []).length"
|
||||
:show-diproses="false"
|
||||
@action="handleTableAction"
|
||||
/>
|
||||
<v-card class="patient-data-container" elevation="0">
|
||||
<v-card-text class="pa-4">
|
||||
<!-- Header with Filters -->
|
||||
<div class="data-header mb-4">
|
||||
<div class="section-label">DATA PASIEN</div>
|
||||
|
||||
<div class="filters">
|
||||
<!-- Search Field -->
|
||||
<v-text-field
|
||||
v-model="searchQuery"
|
||||
placeholder="Cari barcode, nomor antrian..."
|
||||
density="compact"
|
||||
hide-details
|
||||
class="search-field"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PatientDataTable
|
||||
:items="allPatientsForStage"
|
||||
v-model:selected-status="selectedStatus"
|
||||
v-model:search-query="searchQuery"
|
||||
v-model:selected-fast-track="selectedFastTrack"
|
||||
:di-loket-count="diLoketCount"
|
||||
:diproses-count="currentProcessingPatient ? 1 : 0"
|
||||
:terlambat-count="(terlambatPatients || []).length"
|
||||
:pending-count="(pendingPatients || []).length"
|
||||
:show-diproses="false"
|
||||
:fast-track-options="fastTrackOptions"
|
||||
@action="handleTableAction"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -170,6 +194,16 @@ const currentDate = ref(
|
||||
|
||||
const selectedStatus = ref("all");
|
||||
const searchQuery = ref("");
|
||||
const selectedFastTrack = ref(null);
|
||||
|
||||
// Fast Track options from all patients
|
||||
const fastTrackOptions = computed(() => {
|
||||
const normalizedValues = allPatientsForStage.value
|
||||
.map((p) => (p.fastTrack ?? "").toString().trim().toUpperCase())
|
||||
.filter((v) => v.length > 0);
|
||||
const uniqueTracks = [...new Set(normalizedValues)];
|
||||
return uniqueTracks.sort();
|
||||
});
|
||||
|
||||
// Combine all patients with status - PRESERVE ALL PROPERTIES
|
||||
const allPatientsForStage = computed(() => {
|
||||
@@ -266,9 +300,94 @@ const handleProcessNext = () => {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
align-self: flex-start;
|
||||
max-height: calc(100vh - 32px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.status-filter {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.status-filter .v-chip {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
height: 32px;
|
||||
border: 1px solid var(--color-neutral-500);
|
||||
background: var(--color-neutral-100);
|
||||
color: var(--color-neutral-600);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.status-filter .v-chip:hover {
|
||||
background: var(--color-neutral-300);
|
||||
}
|
||||
|
||||
.status-filter .v-chip.active-chip {
|
||||
background: var(--color-primary-600);
|
||||
color: var(--color-neutral-100);
|
||||
border-color: var(--color-primary-600);
|
||||
}
|
||||
|
||||
.search-field {
|
||||
max-width: 300px;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--color-neutral-600);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.data-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.patient-data-container {
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--color-neutral-500);
|
||||
background: var(--color-neutral-100);
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.loket-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
position: relative;
|
||||
top: 0;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.filters {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.status-filter {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.search-field {
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+82
-66
@@ -18,79 +18,81 @@
|
||||
<v-row class="content-grid" dense>
|
||||
<!-- Left Column: Current Patient -->
|
||||
<v-col cols="12" md="5">
|
||||
<v-card class="current-patient-card" elevation="0">
|
||||
<v-card-text class="pa-4">
|
||||
<div class="section-label mb-3">SEDANG DIPROSES</div>
|
||||
|
||||
<div v-if="currentProcessingPatient" class="patient-details">
|
||||
<div class="patient-number mb-2">{{ currentProcessingPatient.noAntrian.split(" |")[0] }}</div>
|
||||
<div class="patient-info-text mb-3">
|
||||
<div>{{ currentProcessingPatient.barcode }}</div>
|
||||
<div>{{ currentProcessingPatient.klinik }} | {{ currentProcessingPatient.pembayaran }}</div>
|
||||
<div class="sticky-wrapper">
|
||||
<v-card class="current-patient-card" elevation="0">
|
||||
<v-card-text class="pa-4">
|
||||
<div class="section-label mb-3">SEDANG DIPROSES</div>
|
||||
|
||||
<div v-if="currentProcessingPatient" class="patient-details">
|
||||
<div class="patient-number mb-2">{{ currentProcessingPatient.noAntrian.split(" |")[0] }}</div>
|
||||
<div class="patient-info-text mb-3">
|
||||
<div>{{ currentProcessingPatient.barcode }}</div>
|
||||
<div>{{ currentProcessingPatient.klinik }} | {{ currentProcessingPatient.pembayaran }}</div>
|
||||
</div>
|
||||
|
||||
<div class="action-grid">
|
||||
<v-btn color="#10b981" variant="flat" block size="large" @click="processPatient(currentProcessingPatient, 'check-in')">
|
||||
<v-icon start size="20">mdi-check</v-icon>
|
||||
Selesai
|
||||
</v-btn>
|
||||
<v-btn color="#f59e0b" variant="flat" block size="large" @click="processPatient(currentProcessingPatient, 'terlambat')">
|
||||
<v-icon start size="20">mdi-clock-alert</v-icon>
|
||||
Terlambat
|
||||
</v-btn>
|
||||
<v-btn color="#ef4444" variant="flat" block size="large" @click="processPatient(currentProcessingPatient, 'pending')">
|
||||
<v-icon start size="20">mdi-pause</v-icon>
|
||||
Pending
|
||||
</v-btn>
|
||||
<v-btn color="#3b82f6" variant="flat" block size="large" @click="showChangeKlinikDialog = true">
|
||||
<v-icon start size="20">mdi-swap-horizontal</v-icon>
|
||||
Ubah Ruang
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-grid">
|
||||
<v-btn color="#10b981" variant="flat" block size="large" @click="processPatient(currentProcessingPatient, 'check-in')">
|
||||
<v-icon start size="20">mdi-check</v-icon>
|
||||
Selesai
|
||||
</v-btn>
|
||||
<v-btn color="#f59e0b" variant="flat" block size="large" @click="processPatient(currentProcessingPatient, 'terlambat')">
|
||||
<v-icon start size="20">mdi-clock-alert</v-icon>
|
||||
Terlambat
|
||||
</v-btn>
|
||||
<v-btn color="#ef4444" variant="flat" block size="large" @click="processPatient(currentProcessingPatient, 'pending')">
|
||||
<v-icon start size="20">mdi-pause</v-icon>
|
||||
Pending
|
||||
</v-btn>
|
||||
<v-btn color="#3b82f6" variant="flat" block size="large" @click="showChangeKlinikDialog = true">
|
||||
<v-icon start size="20">mdi-swap-horizontal</v-icon>
|
||||
Ubah Ruang
|
||||
</v-btn>
|
||||
<div v-else class="empty-state">
|
||||
<v-icon size="48" color="grey-lighten-2">mdi-account-off-outline</v-icon>
|
||||
<div class="empty-text">Tidak ada pasien yang diproses</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="empty-state">
|
||||
<v-icon size="48" color="grey-lighten-2">mdi-account-off-outline</v-icon>
|
||||
<div class="empty-text">Tidak ada pasien yang diproses</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- Queue Actions -->
|
||||
<v-card class="queue-actions-card mt-3" elevation="0">
|
||||
<v-card-text class="pa-4">
|
||||
<div class="section-label mb-3">PANGGIL ANTREAN</div>
|
||||
|
||||
<div class="quota-info mb-3">
|
||||
<div class="quota-item">
|
||||
<span class="quota-label">Kuota</span>
|
||||
<span class="quota-value">150</span>
|
||||
<!-- Queue Actions -->
|
||||
<v-card class="queue-actions-card mt-3" elevation="0">
|
||||
<v-card-text class="pa-4">
|
||||
<div class="section-label mb-3">PANGGIL ANTREAN</div>
|
||||
|
||||
<div class="quota-info mb-3">
|
||||
<div class="quota-item">
|
||||
<span class="quota-label">Kuota</span>
|
||||
<span class="quota-value">150</span>
|
||||
</div>
|
||||
<div class="quota-item">
|
||||
<span class="quota-label">Tersedia</span>
|
||||
<span class="quota-value quota-available">{{ 150 - quotaUsed }}</span>
|
||||
</div>
|
||||
<div class="quota-item full-width">
|
||||
<v-progress-linear :model-value="(quotaUsed / 150) * 100" color="#10b981" height="6" rounded class="mt-1"></v-progress-linear>
|
||||
<span class="quota-used">Terpakai: {{ quotaUsed }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quota-item">
|
||||
<span class="quota-label">Tersedia</span>
|
||||
<span class="quota-value quota-available">{{ 150 - quotaUsed }}</span>
|
||||
</div>
|
||||
<div class="quota-item full-width">
|
||||
<v-progress-linear :model-value="(quotaUsed / 150) * 100" color="#10b981" height="6" rounded class="mt-1"></v-progress-linear>
|
||||
<span class="quota-used">Terpakai: {{ quotaUsed }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="call-buttons">
|
||||
<v-btn color="#10b981" variant="outlined" @click="callNext" :disabled="!nextPatient">1</v-btn>
|
||||
<v-btn color="#3b82f6" variant="outlined" @click="callMultiplePatients(5)">5</v-btn>
|
||||
<v-btn color="#f59e0b" variant="outlined" @click="callMultiplePatients(10)">10</v-btn>
|
||||
<v-btn color="#ef4444" variant="outlined" @click="callMultiplePatients(20)">20</v-btn>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<div class="call-buttons">
|
||||
<v-btn color="#10b981" variant="outlined" @click="callNext" :disabled="!nextPatient">1</v-btn>
|
||||
<v-btn color="#3b82f6" variant="outlined" @click="callMultiplePatients(5)">5</v-btn>
|
||||
<v-btn color="#f59e0b" variant="outlined" @click="callMultiplePatients(10)">10</v-btn>
|
||||
<v-btn color="#ef4444" variant="outlined" @click="callMultiplePatients(20)">20</v-btn>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- Create Queue Button -->
|
||||
<div class="create-buttons mt-3">
|
||||
<v-btn color="#3b82f6" variant="flat" block size="large" @click="showPenunjangDialog = true">
|
||||
<v-icon start size="20">mdi-clipboard-pulse</v-icon>
|
||||
Buat Antrean Penunjang
|
||||
</v-btn>
|
||||
<!-- Create Queue Button -->
|
||||
<div class="create-buttons mt-3">
|
||||
<v-btn color="#3b82f6" variant="flat" block size="large" @click="showPenunjangDialog = true">
|
||||
<v-icon start size="20">mdi-clipboard-pulse</v-icon>
|
||||
Buat Antrean Penunjang
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
@@ -424,6 +426,14 @@ const getStatusLabel = (status) => {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
align-self: flex-start;
|
||||
max-height: calc(100vh - 32px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.current-patient-card,
|
||||
.queue-actions-card,
|
||||
.patient-table-card {
|
||||
@@ -663,6 +673,12 @@ const getStatusLabel = (status) => {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
position: relative;
|
||||
top: 0;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.filters {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
+303
-181
@@ -8,18 +8,24 @@
|
||||
</div>
|
||||
<div class="header-text">
|
||||
<h1 class="page-title">{{ anjunganData.namaAnjungan }}</h1>
|
||||
<p class="page-subtitle">Pilih Klinik untuk Pendaftaran - {{ anjunganData.jenisPasien }}</p>
|
||||
<p class="page-subtitle">
|
||||
Pilih Klinik untuk Pendaftaran - {{ anjunganData.jenisPasien }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="info-guide">
|
||||
<div class="guide-item">
|
||||
<span class="guide-number">1</span>
|
||||
<span class="guide-text">Pilih Poli (Hijau = Buka, Merah = Tutup)</span>
|
||||
<span class="guide-text"
|
||||
>Pilih Poli (Hijau = Buka, Merah = Tutup)</span
|
||||
>
|
||||
</div>
|
||||
<div class="guide-item">
|
||||
<span class="guide-number">2</span>
|
||||
<span class="guide-text">Pilih: Daftar Sekarang atau Jadwal Lain</span>
|
||||
<span class="guide-text"
|
||||
>Pilih: Daftar Sekarang atau Jadwal Lain</span
|
||||
>
|
||||
</div>
|
||||
<div class="guide-item">
|
||||
<span class="guide-number">3</span>
|
||||
@@ -58,24 +64,42 @@
|
||||
</h3>
|
||||
|
||||
<div class="doctor-info">
|
||||
<v-icon size="16" :color="clinic.available ? 'var(--color-primary-600)' : 'var(--color-neutral-600)'">
|
||||
<v-icon
|
||||
size="16"
|
||||
:color="
|
||||
clinic.available
|
||||
? 'var(--color-primary-600)'
|
||||
: 'var(--color-neutral-600)'
|
||||
"
|
||||
>
|
||||
mdi-doctor
|
||||
</v-icon>
|
||||
<span>{{ getDisplayDoctorInfo(clinic) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="schedule-info">
|
||||
<v-icon size="14" :color="clinic.available ? 'var(--color-success-600)' : 'var(--color-neutral-600)'">
|
||||
<v-icon
|
||||
size="14"
|
||||
:color="
|
||||
clinic.available
|
||||
? 'var(--color-success-600)'
|
||||
: 'var(--color-neutral-600)'
|
||||
"
|
||||
>
|
||||
mdi-clock-outline
|
||||
</v-icon>
|
||||
<span>{{ clinic.schedule || 'Tidak tersedia' }}</span>
|
||||
<span>{{ clinic.schedule || "Tidak tersedia" }}</span>
|
||||
</div>
|
||||
|
||||
<div class="clinic-icon-wrapper">
|
||||
<v-icon
|
||||
:icon="clinic.icon"
|
||||
size="40"
|
||||
:color="clinic.available ? 'var(--color-success-600)' : 'var(--color-neutral-100)'"
|
||||
:color="
|
||||
clinic.available
|
||||
? 'var(--color-success-600)'
|
||||
: 'var(--color-neutral-100)'
|
||||
"
|
||||
></v-icon>
|
||||
</div>
|
||||
</v-card-text>
|
||||
@@ -84,14 +108,22 @@
|
||||
</v-row>
|
||||
|
||||
<div v-if="filteredClinics.length === 0" class="empty-state">
|
||||
<v-icon size="64" color="grey-lighten-1">mdi-hospital-marker-outline</v-icon>
|
||||
<v-icon size="64" color="grey-lighten-1"
|
||||
>mdi-hospital-marker-outline</v-icon
|
||||
>
|
||||
<h3 class="empty-title">Tidak ada klinik yang sesuai</h3>
|
||||
<p class="empty-subtitle">Anjungan ini belum memiliki klinik terpilih</p>
|
||||
<p class="empty-subtitle">
|
||||
Anjungan ini belum memiliki klinik terpilih
|
||||
</p>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-dialog v-model="showVisitTypeDialog" max-width="400" @click:outside="showVisitTypeDialog = false">
|
||||
<v-dialog
|
||||
v-model="showVisitTypeDialog"
|
||||
max-width="400"
|
||||
@click:outside="showVisitTypeDialog = false"
|
||||
>
|
||||
<v-card class="dialog-card">
|
||||
<v-card-title class="dialog-header">
|
||||
<v-icon class="mr-2">mdi-check-circle</v-icon>
|
||||
@@ -101,7 +133,11 @@
|
||||
<v-card-text class="pa-6" v-if="selectedClinic">
|
||||
<div class="dialog-content">
|
||||
<div class="dialog-icon mb-3">
|
||||
<v-icon :icon="selectedClinic.icon" size="48" color="primary-600"></v-icon>
|
||||
<v-icon
|
||||
:icon="selectedClinic.icon"
|
||||
size="48"
|
||||
color="primary-600"
|
||||
></v-icon>
|
||||
</div>
|
||||
<h3 class="dialog-clinic-name">{{ selectedClinic.name }}</h3>
|
||||
<p v-if="selectedClinic.subtitle" class="dialog-subtitle">
|
||||
@@ -119,44 +155,56 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="quota-section">
|
||||
<p><strong>Kuota Hari Ini (Shift {{ getCurrentShiftNumber() }}):</strong></p>
|
||||
<div class="current-quota-display" :class="{ 'quota-empty': getCurrentShiftQuota() === 0 }">
|
||||
<span class="quota-large-number">{{ getCurrentShiftQuota() }}</span>
|
||||
<p>
|
||||
<strong
|
||||
>Kuota Hari Ini (Shift
|
||||
{{ getCurrentShiftNumber() }}):</strong
|
||||
>
|
||||
</p>
|
||||
<div
|
||||
class="current-quota-display"
|
||||
:class="{ 'quota-empty': getCurrentShiftQuota() === 0 }"
|
||||
>
|
||||
<span class="quota-large-number">{{
|
||||
getCurrentShiftQuota()
|
||||
}}</span>
|
||||
<span class="quota-label">pasien tersedia</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p><strong>Jadwal:</strong> {{ selectedClinic.schedule }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
|
||||
<v-divider />
|
||||
|
||||
|
||||
<v-card-text class="pa-4">
|
||||
<v-row dense>
|
||||
<v-col cols="6">
|
||||
<v-btn
|
||||
color="success-600"
|
||||
class="text-white"
|
||||
size="large"
|
||||
block
|
||||
<v-btn
|
||||
color="success-600"
|
||||
class="text-white"
|
||||
size="large"
|
||||
block
|
||||
@click="selectVisitType('SEKARANG')"
|
||||
:disabled="isShift1Full(selectedClinic)"
|
||||
:class="{ 'btn-disabled': isShift1Full(selectedClinic) }"
|
||||
>
|
||||
DAFTAR SEKARANG
|
||||
</v-btn>
|
||||
<p v-if="isShift1Full(selectedClinic)" class="disabled-hint">Kuota hari ini habis</p>
|
||||
<p v-if="isShift1Full(selectedClinic)" class="disabled-hint">
|
||||
Kuota hari ini habis
|
||||
</p>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-btn
|
||||
color="primary-600"
|
||||
class="text-white"
|
||||
size="large"
|
||||
block
|
||||
<v-btn
|
||||
color="primary-600"
|
||||
class="text-white"
|
||||
size="large"
|
||||
block
|
||||
@click="selectVisitType('JADWAL_LAIN')"
|
||||
>
|
||||
JADWAL LAIN
|
||||
@@ -164,36 +212,66 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
|
||||
<v-card-actions class="justify-center pa-4 pt-0">
|
||||
<v-btn variant="outlined" color="neutral-600" @click="showVisitTypeDialog = false">
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
color="neutral-600"
|
||||
@click="showVisitTypeDialog = false"
|
||||
>
|
||||
Tutup
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="showPaymentTypeDialog" max-width="400" @click:outside="showPaymentTypeDialog = false">
|
||||
<v-dialog
|
||||
v-model="showPaymentTypeDialog"
|
||||
max-width="400"
|
||||
@click:outside="showPaymentTypeDialog = false"
|
||||
>
|
||||
<v-card class="dialog-card">
|
||||
<v-card-title class="dialog-header-simple">Jenis Pembayaran</v-card-title>
|
||||
<v-card-title class="dialog-header-simple"
|
||||
>Jenis Pembayaran</v-card-title
|
||||
>
|
||||
<v-divider />
|
||||
<v-card-text class="pa-4">
|
||||
<v-btn color="success-600" class="mb-3 payment-btn text-white" size="x-large" block @click="selectPaymentType('BPJS')">
|
||||
<v-btn
|
||||
color="success-600"
|
||||
class="mb-3 payment-btn text-white"
|
||||
size="x-large"
|
||||
block
|
||||
@click="selectPaymentType('BPJS')"
|
||||
>
|
||||
BPJS
|
||||
</v-btn>
|
||||
<v-btn color="secondary-600" class="payment-btn text-white" size="x-large" block @click="selectPaymentType('UMUM')">
|
||||
<v-btn
|
||||
color="secondary-600"
|
||||
class="payment-btn text-white"
|
||||
size="x-large"
|
||||
block
|
||||
@click="selectPaymentType('UMUM')"
|
||||
>
|
||||
UMUM / JKMM / SPM / DLL
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center pa-4 pt-0">
|
||||
<v-btn variant="outlined" color="neutral-600" @click="showPaymentTypeDialog = false">
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
color="neutral-600"
|
||||
@click="showPaymentTypeDialog = false"
|
||||
>
|
||||
Tutup
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="showBookingFormDialog" max-width="500" @click:outside="showBookingFormDialog = false">
|
||||
<v-dialog
|
||||
v-model="showBookingFormDialog"
|
||||
max-width="500"
|
||||
@click:outside="showBookingFormDialog = false"
|
||||
>
|
||||
<v-card class="dialog-card">
|
||||
<v-card-title class="dialog-header">
|
||||
Pilih Jadwal Kunjungan
|
||||
@@ -236,17 +314,31 @@
|
||||
</v-card-text>
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer />
|
||||
<v-btn @click="showBookingFormDialog = false" variant="outlined" color="neutral-600">
|
||||
<v-btn
|
||||
@click="showBookingFormDialog = false"
|
||||
variant="outlined"
|
||||
color="neutral-600"
|
||||
>
|
||||
Tutup
|
||||
</v-btn>
|
||||
<v-btn color="success-600" class="text-white" variant="flat" @click="submitBooking">
|
||||
<v-btn
|
||||
color="success-600"
|
||||
class="text-white"
|
||||
variant="flat"
|
||||
@click="submitBooking"
|
||||
>
|
||||
Simpan
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-snackbar v-model="snackbar" :color="snackbarColor" :timeout="3000" location="top right">
|
||||
<v-snackbar
|
||||
v-model="snackbar"
|
||||
:color="snackbarColor"
|
||||
:timeout="3000"
|
||||
location="top right"
|
||||
>
|
||||
{{ snackbarText }}
|
||||
<template v-slot:actions>
|
||||
<v-btn icon @click="snackbar = false">
|
||||
@@ -263,10 +355,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import { useRoute } from '#app';
|
||||
import { useAnjunganStore } from '@/stores/anjunganStore';
|
||||
import { useClinicStore } from '@/stores/clinicStore';
|
||||
import { ref, computed, onMounted, nextTick } from "vue";
|
||||
import { useRoute } from "#app";
|
||||
import { useAnjunganStore } from "@/stores/anjunganStore";
|
||||
import { useClinicStore } from "@/stores/clinicStore";
|
||||
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
@@ -287,54 +379,47 @@ const anjunganData = computed(() => {
|
||||
const id = anjunganId.value;
|
||||
if (!id) return null;
|
||||
|
||||
const fromGetter = typeof anjunganStore.getAnjunganById === 'function'
|
||||
? anjunganStore.getAnjunganById(id)
|
||||
: null;
|
||||
const fromGetter =
|
||||
typeof anjunganStore.getAnjunganById === "function"
|
||||
? anjunganStore.getAnjunganById(id)
|
||||
: null;
|
||||
if (fromGetter?.value) {
|
||||
return fromGetter.value;
|
||||
}
|
||||
|
||||
const list =
|
||||
anjunganStore.anjunganItems?.value ||
|
||||
anjunganStore.anjunganItems ||
|
||||
[];
|
||||
anjunganStore.anjunganItems?.value || anjunganStore.anjunganItems || [];
|
||||
|
||||
if (!Array.isArray(list)) return null;
|
||||
|
||||
return list.find((a) => Number(a.id) === Number(id)) || null;
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// IMPROVED: API Integration dengan Manual Mapping Table
|
||||
// ============================================================
|
||||
|
||||
// 🗺️ KODE MAPPING TABLE
|
||||
// Mapping dari kode 2 karakter (store) ke kode lengkap (API)
|
||||
// PENTING: Sesuaikan dengan kode yang sebenarnya digunakan di API Anda
|
||||
//kode mapping table
|
||||
const KODE_MAPPING = {
|
||||
'AN': 'ANA', // Anak
|
||||
'AS': 'ANESTESI', // Anestesi
|
||||
'BD': 'BEDAH', // Bedah
|
||||
'GR': 'GERIATRI', // Geriatri
|
||||
'GI': 'GIGI', // Gigi dan Mulut
|
||||
'GZ': 'GIZI', // Gizi
|
||||
'HO': 'HEMATO', // Hemato Onkologi
|
||||
'IP': 'INTERNA', // IPD / Penyakit Dalam
|
||||
'JT': 'JANTUNG', // Jantung / Cardiologi
|
||||
'JW': 'JIWA', // Jiwa / Psikiatri
|
||||
'OB': 'OBGYN', // Kandungan
|
||||
'KH': 'KEMOTERAPI', // Kemoterapi
|
||||
'KN': 'NYERI', // Komplementer Nyeri
|
||||
'KK': 'KULIT', // Kulit Kelamin
|
||||
'MT': 'MATA', // Mata
|
||||
'MC': 'MCU', // MCU
|
||||
'ON': 'ONKOLOGI', // Onkologi
|
||||
'PR': 'PARU', // Paru
|
||||
'TD': 'TINDAKAN', // R. Tindakan
|
||||
'RT': 'RADIOTERAPI', // Radioterapi
|
||||
'RM': 'REHAB', // Rehab Medik
|
||||
'SR': 'SARAF', // Saraf / Neurologi
|
||||
'TH': 'THT', // THT
|
||||
AN: "ANA", // Anak
|
||||
AS: "ANESTESI", // Anestesi
|
||||
BD: "BEDAH", // Bedah
|
||||
GR: "GERIATRI", // Geriatri
|
||||
GI: "GIGI", // Gigi dan Mulut
|
||||
GZ: "GIZI", // Gizi
|
||||
HO: "HEMATO", // Hemato Onkologi
|
||||
IP: "INTERNA", // IPD / Penyakit Dalam
|
||||
JT: "JANTUNG", // Jantung / Cardiologi
|
||||
JW: "JIWA", // Jiwa / Psikiatri
|
||||
OB: "OBGYN", // Kandungan
|
||||
KH: "KEMOTERAPI", // Kemoterapi
|
||||
KN: "NYERI", // Komplementer Nyeri
|
||||
KK: "KULIT", // Kulit Kelamin
|
||||
MT: "MATA", // Mata
|
||||
MC: "MCU", // MCU
|
||||
ON: "ONKOLOGI", // Onkologi
|
||||
PR: "PARU", // Paru
|
||||
TD: "TINDAKAN", // R. Tindakan
|
||||
RT: "RADIOTERAPI", // Radioterapi
|
||||
RM: "REHAB", // Rehab Medik
|
||||
SR: "SARAF", // Saraf / Neurologi
|
||||
TH: "THT", // THT
|
||||
};
|
||||
|
||||
const todayString = new Date().toISOString().substring(0, 10);
|
||||
@@ -342,77 +427,91 @@ const spesialisList = ref([]);
|
||||
const doctorsByKode = ref({});
|
||||
const loadingDoctors = ref({});
|
||||
|
||||
// Load list spesialis dari API
|
||||
// load list spesialis dari API
|
||||
const loadSpesialisForDate = async (tanggal) => {
|
||||
try {
|
||||
const data = await $fetch(
|
||||
`http://10.10.150.131:8088/api/jadwaldokter/tanggal/${tanggal}`
|
||||
);
|
||||
spesialisList.value = Array.isArray(data) ? data : [];
|
||||
console.log(`✅ Loaded ${spesialisList.value.length} spesialis for ${tanggal}`);
|
||||
console.log(
|
||||
`✅ Loaded ${spesialisList.value.length} spesialis for ${tanggal}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('❌ Gagal mengambil data spesialis dari API', error);
|
||||
console.error("❌ Gagal mengambil data spesialis dari API", error);
|
||||
spesialisList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// IMPROVED: Get Spesialis ID dengan Manual Mapping + Fallback
|
||||
//get spesialis ID
|
||||
const getSpesialisIdForKode = (kodeKlinik) => {
|
||||
if (!kodeKlinik) return null;
|
||||
|
||||
// 1. Normalize kode menggunakan mapping table
|
||||
|
||||
// 1. normalize kode menggunakan mapping table
|
||||
const kodeUpper = kodeKlinik.toUpperCase();
|
||||
const normalizedKode = KODE_MAPPING[kodeUpper] || kodeUpper;
|
||||
|
||||
|
||||
console.log(`🔄 Mapping: "${kodeKlinik}" → "${normalizedKode}"`);
|
||||
|
||||
// 2. Cari exact match dulu
|
||||
|
||||
// 2. cari exact match dulu
|
||||
let entry = spesialisList.value.find((item) => {
|
||||
const apiKode = (item.Kode || item.kode || '').toUpperCase();
|
||||
const apiKode = (item.Kode || item.kode || "").toUpperCase();
|
||||
return apiKode === normalizedKode;
|
||||
});
|
||||
|
||||
// 3. Fallback: startsWith untuk fleksibilitas
|
||||
|
||||
// 3. jika tidak ada exact match, coba prefix match
|
||||
if (!entry) {
|
||||
entry = spesialisList.value.find((item) => {
|
||||
const apiKode = (item.Kode || item.kode || '').toUpperCase();
|
||||
const apiKode = (item.Kode || item.kode || "").toUpperCase();
|
||||
return apiKode.startsWith(normalizedKode);
|
||||
});
|
||||
|
||||
|
||||
if (entry) {
|
||||
console.log(`⚠️ Using prefix match: "${normalizedKode}" → "${entry.Kode || entry.kode}"`);
|
||||
console.log(
|
||||
`⚠️ Using prefix match: "${normalizedKode}" → "${
|
||||
entry.Kode || entry.kode
|
||||
}"`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const spesialisId = entry ? (entry.id || entry.ID || entry.FK_daftar_spesialis_ID) : null;
|
||||
|
||||
|
||||
const spesialisId = entry
|
||||
? entry.id || entry.ID || entry.FK_daftar_spesialis_ID
|
||||
: null;
|
||||
|
||||
if (!spesialisId) {
|
||||
console.warn(`❌ No match for: "${kodeKlinik}" (normalized: "${normalizedKode}")`);
|
||||
console.log('Available API kodes:', spesialisList.value.map(s => s.Kode || s.kode));
|
||||
console.warn(
|
||||
`❌ No match for: "${kodeKlinik}" (normalized: "${normalizedKode}")`
|
||||
);
|
||||
console.log(
|
||||
"Available API kodes:",
|
||||
spesialisList.value.map((s) => s.Kode || s.kode)
|
||||
);
|
||||
} else {
|
||||
console.log(`✅ Found spesialisId: ${spesialisId} for "${kodeKlinik}"`);
|
||||
}
|
||||
|
||||
|
||||
return spesialisId;
|
||||
};
|
||||
|
||||
// IMPROVED: Load doctors dengan better error handling & caching
|
||||
const loadDoctorsForKode = async (kodeKlinik) => {
|
||||
if (!kodeKlinik) return;
|
||||
|
||||
// Cek apakah sudah ada data atau sedang loading
|
||||
|
||||
// cek apakah sudah ada data atau sedang loading
|
||||
if (doctorsByKode.value[kodeKlinik] || loadingDoctors.value[kodeKlinik]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
loadingDoctors.value[kodeKlinik] = true;
|
||||
|
||||
|
||||
const spesialisId = getSpesialisIdForKode(kodeKlinik);
|
||||
|
||||
|
||||
if (!spesialisId) {
|
||||
console.warn(`⚠️ Skip loading doctors untuk kode: ${kodeKlinik} (spesialisId not found)`);
|
||||
console.warn(
|
||||
`⚠️ Skip loading doctors untuk kode: ${kodeKlinik} (spesialisId not found)`
|
||||
);
|
||||
loadingDoctors.value[kodeKlinik] = false;
|
||||
// Set empty array agar tidak retry terus-menerus
|
||||
// set empty array agar tidak retry terus-menerus
|
||||
doctorsByKode.value[kodeKlinik] = [];
|
||||
return;
|
||||
}
|
||||
@@ -428,99 +527,108 @@ const loadDoctorsForKode = async (kodeKlinik) => {
|
||||
const doctorNames = list
|
||||
.map((d) => d.nama_lengkap || d.Nama_dokter || d.name)
|
||||
.filter(Boolean);
|
||||
|
||||
|
||||
doctorsByKode.value[kodeKlinik] = doctorNames;
|
||||
|
||||
console.log(`✅ Loaded ${doctorNames.length} doctors untuk ${kodeKlinik}:`, doctorNames);
|
||||
|
||||
console.log(
|
||||
`✅ Loaded ${doctorNames.length} doctors untuk ${kodeKlinik}:`,
|
||||
doctorNames
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`❌ Gagal mengambil dokter untuk kode ${kodeKlinik}:`, error);
|
||||
// Set empty array agar tidak retry
|
||||
// set empty array agar tidak retry
|
||||
doctorsByKode.value[kodeKlinik] = [];
|
||||
} finally {
|
||||
loadingDoctors.value[kodeKlinik] = false;
|
||||
}
|
||||
};
|
||||
|
||||
// IMPROVED: Prefetch dengan comprehensive logging
|
||||
const prefetchDoctorsForCurrentAnjungan = async () => {
|
||||
if (!anjunganData.value || !anjunganData.value.klinik) {
|
||||
console.warn('⚠️ No anjungan data or klinik data available');
|
||||
console.warn("⚠️ No anjungan data or klinik data available");
|
||||
return;
|
||||
}
|
||||
|
||||
const uniqueKodes = Array.from(new Set(anjunganData.value.klinik));
|
||||
console.log(`🔄 Prefetching doctors for ${uniqueKodes.length} klinik:`, uniqueKodes);
|
||||
console.log(
|
||||
`🔄 Prefetching doctors for ${uniqueKodes.length} klinik:`,
|
||||
uniqueKodes
|
||||
);
|
||||
|
||||
await Promise.all(uniqueKodes.map((kode) => loadDoctorsForKode(kode)));
|
||||
|
||||
console.log('✅ Prefetch complete. Doctors by kode:', doctorsByKode.value);
|
||||
|
||||
console.log("✅ Prefetch complete. Doctors by kode:", doctorsByKode.value);
|
||||
};
|
||||
|
||||
// IMPROVED: Dialog doctors dengan explicit dependency
|
||||
const dialogDoctors = computed(() => {
|
||||
const kode = selectedClinic.value?.kode;
|
||||
if (!kode) return [];
|
||||
|
||||
|
||||
const apiDoctors = doctorsByKode.value[kode];
|
||||
|
||||
// Prioritas: API > Static
|
||||
|
||||
// prioritas: API > Static
|
||||
if (apiDoctors && apiDoctors.length > 0) {
|
||||
return apiDoctors;
|
||||
}
|
||||
|
||||
// Fallback ke static data
|
||||
|
||||
// fallback ke static data
|
||||
const staticDoctors = selectedClinic.value?.doctors || [];
|
||||
|
||||
|
||||
if (staticDoctors.length > 0) {
|
||||
console.log(`ℹ️ Using static doctors for ${kode} (API data not available)`);
|
||||
}
|
||||
|
||||
|
||||
return staticDoctors;
|
||||
});
|
||||
|
||||
// IMPROVED: onMounted dengan sequential loading + debug
|
||||
onMounted(async () => {
|
||||
console.log('🚀 Component mounted, starting data load...');
|
||||
|
||||
console.log("🚀 Component mounted, starting data load...");
|
||||
|
||||
// Step 1: Load spesialis list
|
||||
await loadSpesialisForDate(todayString);
|
||||
|
||||
|
||||
// Step 2: Wait for computed values to be ready
|
||||
await nextTick();
|
||||
|
||||
// 🔍 DEBUG: Tampilkan comparison table
|
||||
|
||||
if (anjunganData.value?.klinik) {
|
||||
console.group('📊 Kode Mapping Comparison');
|
||||
console.group("📊 Kode Mapping Comparison");
|
||||
console.table(
|
||||
anjunganData.value.klinik.map(storeKode => {
|
||||
anjunganData.value.klinik.map((storeKode) => {
|
||||
const clinic = clinicStore.getClinicByKode(storeKode);
|
||||
const normalizedKode = KODE_MAPPING[storeKode.toUpperCase()] || storeKode;
|
||||
const normalizedKode =
|
||||
KODE_MAPPING[storeKode.toUpperCase()] || storeKode;
|
||||
const spesialisId = getSpesialisIdForKode(storeKode);
|
||||
const apiMatch = spesialisList.value.find(s =>
|
||||
(s.id || s.ID || s.FK_daftar_spesialis_ID) === spesialisId
|
||||
const apiMatch = spesialisList.value.find(
|
||||
(s) => (s.id || s.ID || s.FK_daftar_spesialis_ID) === spesialisId
|
||||
);
|
||||
|
||||
|
||||
return {
|
||||
'Store Kode': storeKode,
|
||||
'Clinic Name': clinic?.name || '❌ Not found',
|
||||
'Normalized': normalizedKode,
|
||||
'API Kode': apiMatch ? (apiMatch.Kode || apiMatch.kode) : '❌ Not matched',
|
||||
'Spesialis ID': spesialisId || '❌',
|
||||
'Status': spesialisId ? '✅' : '❌'
|
||||
"Store Kode": storeKode,
|
||||
"Clinic Name": clinic?.name || "❌ Not found",
|
||||
Normalized: normalizedKode,
|
||||
"API Kode": apiMatch
|
||||
? apiMatch.Kode || apiMatch.kode
|
||||
: "❌ Not matched",
|
||||
"Spesialis ID": spesialisId || "❌",
|
||||
Status: spesialisId ? "✅" : "❌",
|
||||
};
|
||||
})
|
||||
);
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
|
||||
// Step 3: Prefetch doctors
|
||||
await prefetchDoctorsForCurrentAnjungan();
|
||||
|
||||
console.log('✅ Initial data load complete');
|
||||
|
||||
console.log("✅ Initial data load complete");
|
||||
});
|
||||
|
||||
const filteredClinics = computed(() => {
|
||||
if (!anjunganData.value || !anjunganData.value.klinik || anjunganData.value.klinik.length === 0) {
|
||||
if (
|
||||
!anjunganData.value ||
|
||||
!anjunganData.value.klinik ||
|
||||
anjunganData.value.klinik.length === 0
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -545,13 +653,12 @@ const bookingForm = ref({
|
||||
payment: null,
|
||||
});
|
||||
|
||||
// IMPROVED: Display doctor info dengan prioritas API
|
||||
// tampilkan doctor info dengan prioritas API
|
||||
const getDisplayDoctorInfo = (clinic) => {
|
||||
const kode = clinic.kode;
|
||||
const apiDoctors = kode ? doctorsByKode.value[kode] : null;
|
||||
const doctors = (apiDoctors && apiDoctors.length > 0)
|
||||
? apiDoctors
|
||||
: (clinic.doctors || []);
|
||||
const doctors =
|
||||
apiDoctors && apiDoctors.length > 0 ? apiDoctors : clinic.doctors || [];
|
||||
|
||||
if (!doctors || doctors.length === 0) {
|
||||
return "Tidak ada dokter";
|
||||
@@ -570,7 +677,7 @@ const getDisplayDoctorInfo = (clinic) => {
|
||||
|
||||
const isShift1Full = (clinic) => {
|
||||
if (!clinic || !clinic.shifts || clinic.shifts.length === 0) return true;
|
||||
const shift1 = clinic.shifts.find(s => s.name === "Shift 1");
|
||||
const shift1 = clinic.shifts.find((s) => s.name === "Shift 1");
|
||||
return shift1 ? shift1.quota === 0 : true;
|
||||
};
|
||||
|
||||
@@ -580,28 +687,30 @@ const getCurrentShiftNumber = () => {
|
||||
|
||||
const getCurrentShiftQuota = () => {
|
||||
if (!selectedClinic.value || !selectedClinic.value.shifts) return 0;
|
||||
const currentShift = selectedClinic.value.shifts.find(s => s.name === "Shift 1");
|
||||
const currentShift = selectedClinic.value.shifts.find(
|
||||
(s) => s.name === "Shift 1"
|
||||
);
|
||||
return currentShift ? currentShift.quota : 0;
|
||||
};
|
||||
|
||||
const getAvailableShiftsForBooking = () => {
|
||||
if (!selectedClinic.value || !selectedClinic.value.shifts) return [];
|
||||
|
||||
|
||||
const selectedDate = bookingForm.value.date;
|
||||
const today = new Date().toISOString().substring(0, 10);
|
||||
const isToday = selectedDate === today;
|
||||
|
||||
|
||||
return selectedClinic.value.shifts
|
||||
.filter(shift => {
|
||||
.filter((shift) => {
|
||||
if (isToday) {
|
||||
return shift.name !== "Shift 1" && shift.quota > 0;
|
||||
} else {
|
||||
return shift.quota > 0;
|
||||
}
|
||||
})
|
||||
.map(shift => ({
|
||||
.map((shift) => ({
|
||||
label: `${shift.name} (Kuota: ${shift.quota} pasien)`,
|
||||
value: shift.name
|
||||
value: shift.name,
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -638,7 +747,7 @@ const selectVisitType = (type) => {
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const tomorrowStr = tomorrow.toISOString().substring(0, 10);
|
||||
|
||||
|
||||
bookingForm.value = {
|
||||
date: tomorrowStr,
|
||||
shift: "",
|
||||
@@ -672,7 +781,7 @@ const submitBooking = () => {
|
||||
};
|
||||
|
||||
const backToList = () => {
|
||||
navigateTo('/anjungan/anjungan');
|
||||
navigateTo("/anjungan/anjungan");
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -685,7 +794,11 @@ const backToList = () => {
|
||||
}
|
||||
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-primary-700) 100%);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--color-primary-600) 0%,
|
||||
var(--color-primary-700) 100%
|
||||
);
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 4px 16px rgba(255, 155, 27, 0.25);
|
||||
@@ -952,12 +1065,12 @@ const backToList = () => {
|
||||
.dialog-info {
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
|
||||
|
||||
p {
|
||||
margin: 8px 0;
|
||||
color: var(--color-neutral-800);
|
||||
}
|
||||
|
||||
|
||||
strong {
|
||||
color: var(--color-neutral-900);
|
||||
}
|
||||
@@ -965,7 +1078,7 @@ const backToList = () => {
|
||||
|
||||
.doctor-list-section {
|
||||
margin-bottom: 12px;
|
||||
|
||||
|
||||
p {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -975,7 +1088,7 @@ const backToList = () => {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
|
||||
li {
|
||||
padding: 6px 12px;
|
||||
margin: 4px 0;
|
||||
@@ -988,7 +1101,7 @@ const backToList = () => {
|
||||
|
||||
.quota-section {
|
||||
margin-bottom: 16px;
|
||||
|
||||
|
||||
p {
|
||||
margin-bottom: 12px;
|
||||
font-size: 15px;
|
||||
@@ -1000,19 +1113,27 @@ const backToList = () => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, var(--color-success-100) 0%, var(--color-success-200) 100%);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--color-success-100) 0%,
|
||||
var(--color-success-200) 100%
|
||||
);
|
||||
border-radius: 12px;
|
||||
border: 2px solid var(--color-success-400);
|
||||
margin-bottom: 12px;
|
||||
|
||||
|
||||
&.quota-empty {
|
||||
background: linear-gradient(135deg, var(--color-neutral-200) 0%, var(--color-neutral-300) 100%);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--color-neutral-200) 0%,
|
||||
var(--color-neutral-300) 100%
|
||||
);
|
||||
border-color: var(--color-neutral-500);
|
||||
|
||||
|
||||
.quota-large-number {
|
||||
color: var(--color-neutral-600);
|
||||
}
|
||||
|
||||
|
||||
.quota-label {
|
||||
color: var(--color-neutral-700);
|
||||
}
|
||||
@@ -1066,20 +1187,20 @@ const backToList = () => {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
.header-right {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.info-guide {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.guide-item {
|
||||
font-size: 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.guide-number {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
@@ -1089,11 +1210,11 @@ const backToList = () => {
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
|
||||
.clinic-card {
|
||||
height: 170px;
|
||||
}
|
||||
|
||||
|
||||
.clinic-name {
|
||||
font-size: 20px;
|
||||
}
|
||||
@@ -1132,11 +1253,12 @@ const backToList = () => {
|
||||
.clinic-icon-wrapper .v-icon {
|
||||
font-size: 32px !important;
|
||||
}
|
||||
|
||||
.doctor-info, .schedule-info {
|
||||
|
||||
.doctor-info,
|
||||
.schedule-info {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
.doctor-info {
|
||||
padding-right: 60px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user