update API master LOKET dan antrian LOKET
This commit is contained in:
No files matched your search
@@ -43,7 +43,7 @@
|
||||
<template #item.jenisLayanan="{ item }">
|
||||
<v-chip
|
||||
size="small"
|
||||
:class="item.jenisLayanan === 'Reguler' ? 'chip-reguler' : 'chip-eksekutif'"
|
||||
:class="['Reguler', 'REGULER', 'JKN'].includes(item.jenisLayanan) ? 'chip-reguler' : 'chip-eksekutif'"
|
||||
>
|
||||
{{ item.jenisLayanan }}
|
||||
</v-chip>
|
||||
@@ -808,15 +808,15 @@ $font-weight-bold: 700;
|
||||
}
|
||||
|
||||
.chip-reguler {
|
||||
background-color: $success-600 !important;
|
||||
color: $neutral-100 !important;
|
||||
font-weight: $font-weight-medium;
|
||||
background-color: #009262 !important;
|
||||
color: #FFFFFF !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chip-eksekutif {
|
||||
background-color: $secondary-600 !important;
|
||||
color: $neutral-100 !important;
|
||||
font-weight: $font-weight-medium;
|
||||
background-color: #E67E22 !important;
|
||||
color: #FFFFFF !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
|
||||
+232
-16
@@ -36,7 +36,7 @@
|
||||
<v-card-text>
|
||||
<v-data-table
|
||||
:headers="loketHeaders"
|
||||
:items="masterStore.loketData"
|
||||
:items="loketStore.loketData"
|
||||
:items-per-page="10"
|
||||
class="elevation-0 data-table"
|
||||
>
|
||||
@@ -47,7 +47,7 @@
|
||||
size="small"
|
||||
class="mr-1 mb-1 chip-primary-outline"
|
||||
>
|
||||
{{ masterStore.getKlinikNameByKode(serviceKode) }}
|
||||
{{ getServiceName(item, serviceKode) }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="item.pelayanan.length > 2"
|
||||
@@ -58,7 +58,37 @@
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<template #item.pembayaran="{ item }">
|
||||
<v-chip
|
||||
size="small"
|
||||
:class="['JKN', 'Reguler', 'REGULER'].includes(item.pembayaran) ? 'chip-reguler' : 'chip-eksekutif'"
|
||||
>
|
||||
{{ item.pembayaran }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<template #item.loketAktif="{ item }">
|
||||
<v-chip
|
||||
size="small"
|
||||
:color="item.loketAktif ? 'success' : 'error'"
|
||||
variant="flat"
|
||||
class="text-capitalize"
|
||||
>
|
||||
{{ item.loketAktif ? 'Aktif' : 'Tidak Aktif' }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<template #item.aksi="{ item }">
|
||||
<v-btn
|
||||
size="small"
|
||||
@click="openPreview(item)"
|
||||
variant="flat"
|
||||
class="btn-preview mr-2"
|
||||
>
|
||||
<v-icon size="16" left>mdi-eye</v-icon>
|
||||
Preview
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="flat"
|
||||
@@ -146,7 +176,7 @@
|
||||
<v-select
|
||||
v-model="formData.pembayaran"
|
||||
label="Pembayaran"
|
||||
:items="['JKN', 'UMUM', 'SPM', 'JKMM', 'JAMPERSAL', 'T4', 'KARYAWAN']"
|
||||
:items="['JKN', 'UMUM', 'EKSEKUTIF', 'SPM', 'JKMM', 'JAMPERSAL', 'T4', 'KARYAWAN']"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[v => !!v || 'Pembayaran harus dipilih']"
|
||||
@@ -155,15 +185,13 @@
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-select
|
||||
v-model="formData.keterangan"
|
||||
label="Keterangan"
|
||||
:items="['ONLINE', 'OFFLINE']"
|
||||
variant="outlined"
|
||||
<v-switch
|
||||
v-model="formData.loketAktif"
|
||||
:label="formData.loketAktif ? 'Loket Aktif' : 'Loket Tidak Aktif'"
|
||||
color="success"
|
||||
hide-details
|
||||
density="compact"
|
||||
:rules="[v => !!v || 'Keterangan harus dipilih']"
|
||||
hide-details="auto"
|
||||
class="input-field"
|
||||
class="mt-1"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -246,6 +274,34 @@
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Preview Dialog -->
|
||||
<v-dialog v-model="previewDialog" max-width="95vw" max-height="95vh" persistent>
|
||||
<v-card class="preview-dialog-card">
|
||||
<v-card-title class="preview-dialog-header">
|
||||
<div class="preview-header-content">
|
||||
<v-icon size="24" class="mr-2">mdi-view-dashboard</v-icon>
|
||||
<span class="headline-4">Preview Loket: {{ previewItem?.namaLoket || '' }}</span>
|
||||
</div>
|
||||
<v-btn icon variant="text" size="small" class="btn-close" @click="closePreviewDialog">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text class="preview-content pa-0">
|
||||
<iframe
|
||||
v-if="previewItem"
|
||||
:src="previewUrl"
|
||||
class="preview-iframe"
|
||||
frameborder="0"
|
||||
></iframe>
|
||||
<div v-else class="preview-loading">
|
||||
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
||||
<p class="mt-4">Memuat preview...</p>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Snackbar -->
|
||||
<v-snackbar v-model="snackbar.show" :color="snackbar.color" :timeout="3000">
|
||||
<span class="body-3">{{ snackbar.message }}</span>
|
||||
@@ -258,11 +314,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useMasterStore } from '@/stores/masterStore';
|
||||
import { useLoketStore } from '@/stores/loketStore';
|
||||
|
||||
const masterStore = useMasterStore();
|
||||
const loketStore = useLoketStore();
|
||||
const dialog = ref(false);
|
||||
const previewDialog = ref(false);
|
||||
const previewItem = ref(null);
|
||||
const isEdit = ref(false);
|
||||
const formRef = ref(null);
|
||||
|
||||
@@ -272,13 +332,73 @@ const snackbar = ref({
|
||||
color: 'success'
|
||||
});
|
||||
|
||||
// Helper untuk mendapatkan nama layanan/klinik
|
||||
const getServiceName = (item, serviceId) => {
|
||||
// 1. Coba cari di _spesialisDetail (data dari API)
|
||||
// Convert ID ke string untuk perbandingan aman
|
||||
const idStr = String(serviceId);
|
||||
|
||||
if (item._spesialisDetail && Array.isArray(item._spesialisDetail)) {
|
||||
const detail = item._spesialisDetail.find(s => String(s.idklinik) === idStr);
|
||||
if (detail && detail.namaklinik) {
|
||||
return detail.namaklinik;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback ke masterStore.getKlinikById untuk local data (numeric ID seperti 1000, 1001)
|
||||
// Local data EKSEKUTIF menggunakan ID numeric, bukan kode
|
||||
const byId = masterStore.getKlinikById(serviceId);
|
||||
if (byId && byId.nama) return byId.nama;
|
||||
|
||||
// 3. Fallback ke masterStore.getKlinikNameByKode untuk data yang pakai kode
|
||||
return masterStore.getKlinikNameByKode(serviceId);
|
||||
};
|
||||
|
||||
// Auto-fetch loket dari API saat component mount
|
||||
onMounted(async () => {
|
||||
console.log('🚀 MasterLoket mounted, fetching loket dari API...');
|
||||
|
||||
try {
|
||||
const result = await loketStore.fetchLoketFromAPI(true); // force = true untuk fresh data
|
||||
|
||||
console.log('📊 Fetch result:', result);
|
||||
console.log('📊 loketStore.loketData length:', loketStore.loketData.value?.length);
|
||||
console.log('📊 loketStore.loketData:', loketStore.loketData.value);
|
||||
|
||||
if (result.success) {
|
||||
console.log('✅ Fetch successful:', result.message);
|
||||
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: result.message,
|
||||
color: 'success'
|
||||
};
|
||||
} else {
|
||||
console.error('❌ Fetch failed:', result.message);
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: result.message,
|
||||
color: 'warning'
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error in onMounted:', error);
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
message: `Error: ${error.message}`,
|
||||
color: 'error'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const loketHeaders = ref([
|
||||
{ title: "No", value: "no" },
|
||||
{ title: "ID", value: "id" },
|
||||
{ title: "Nama Loket", value: "namaLoket" },
|
||||
{ title: "Kuota", value: "kuota" },
|
||||
{ title: "Pelayanan", value: "pelayanan" },
|
||||
{ title: "Pembayaran", value: "pembayaran" },
|
||||
{ title: "Keterangan", value: "keterangan" },
|
||||
{ title: "Status Loket Aktif", value: "loketAktif" },
|
||||
{ title: "Aksi", value: "aksi", sortable: false },
|
||||
]);
|
||||
|
||||
@@ -288,8 +408,8 @@ const formData = ref({
|
||||
kuota: null,
|
||||
statusPelayanan: '',
|
||||
pembayaran: '',
|
||||
keterangan: '',
|
||||
pelayanan: [],
|
||||
loketAktif: true,
|
||||
});
|
||||
|
||||
const openTambahDialog = () => {
|
||||
@@ -352,6 +472,22 @@ const submitForm = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Computed property untuk preview URL
|
||||
const previewUrl = computed(() => {
|
||||
if (!previewItem.value) return '';
|
||||
return `/anjungan/antrianloket/${previewItem.value.id}`;
|
||||
});
|
||||
|
||||
const openPreview = (item) => {
|
||||
previewItem.value = item;
|
||||
previewDialog.value = true;
|
||||
};
|
||||
|
||||
const closePreviewDialog = () => {
|
||||
previewDialog.value = false;
|
||||
previewItem.value = null;
|
||||
};
|
||||
|
||||
const handleDelete = (item) => {
|
||||
if (confirm(`Hapus loket ${item.namaLoket}?`)) {
|
||||
const result = masterStore.deleteLoket(item.id);
|
||||
@@ -365,7 +501,7 @@ const handleDelete = (item) => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// Colors from Design System
|
||||
/* Colors from Design System */
|
||||
$neutral-900: #212121;
|
||||
$neutral-800: #4D4D4D;
|
||||
$neutral-700: #717171;
|
||||
@@ -478,11 +614,20 @@ $font-weight-semibold: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// DATA TABLE
|
||||
// ============================================
|
||||
.data-table {
|
||||
font-family: $font-family-base;
|
||||
|
||||
// Vertically center-align table headers
|
||||
:deep(th) {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
// Vertically center-align table cells
|
||||
:deep(td) {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
}
|
||||
|
||||
.chip-primary-outline {
|
||||
@@ -502,6 +647,18 @@ $font-weight-semibold: 600;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.chip-reguler {
|
||||
background-color: #009262 !important;
|
||||
color: #FFFFFF !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chip-eksekutif {
|
||||
background-color: #E67E22 !important;
|
||||
color: #FFFFFF !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
background-color: $primary-600 !important;
|
||||
color: $neutral-100 !important;
|
||||
@@ -511,6 +668,15 @@ $font-weight-semibold: 600;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.btn-preview {
|
||||
background-color: $secondary-600 !important;
|
||||
color: $neutral-100 !important;
|
||||
text-transform: none;
|
||||
font-weight: $font-weight-semibold;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
border-color: $danger-600 !important;
|
||||
color: $danger-600 !important;
|
||||
@@ -664,4 +830,54 @@ $font-weight-semibold: 600;
|
||||
line-height: 24px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// PREVIEW DIALOG
|
||||
// ============================================
|
||||
.preview-dialog-card {
|
||||
font-family: $font-family-base;
|
||||
height: 95vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.preview-dialog-header {
|
||||
background: linear-gradient(135deg, $primary-600 0%, $primary-700 100%);
|
||||
color: $neutral-100;
|
||||
padding: 16px 24px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preview-header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
padding: 0 !important;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: $neutral-800;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.preview-iframe {
|
||||
width: 100%;
|
||||
height: 75vh;
|
||||
min-height: 600px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.preview-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $neutral-100;
|
||||
padding: 40px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user