495 lines
12 KiB
Vue
495 lines
12 KiB
Vue
<template>
|
|
<v-container>
|
|
<v-card>
|
|
<!-- Header -->
|
|
<div class="page-header">
|
|
<div class="header-content">
|
|
<div class="header-left">
|
|
<div class="header-icon">
|
|
<v-icon size="32" color="white">mdi-view-dashboard</v-icon>
|
|
</div>
|
|
<div class="header-text">
|
|
<h1 class="page-title">Master Loket</h1>
|
|
<p class="page-subtitle">Rabu, 13 Agustus 2025 - Pelayanan</p>
|
|
</div>
|
|
</div>
|
|
<v-btn
|
|
color="white"
|
|
@click="openTambahDialog"
|
|
elevation="0"
|
|
class="add-btn"
|
|
>
|
|
<v-icon left size="20">mdi-plus-circle</v-icon>
|
|
Tambah Loket
|
|
</v-btn>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<v-card-text>
|
|
<v-data-table
|
|
:headers="loketHeaders"
|
|
:items="loketData"
|
|
:items-per-page="10"
|
|
class="elevation-0"
|
|
>
|
|
<template v-slot:item.pelayanan="{ item }">
|
|
<v-chip
|
|
v-for="(service, idx) in item.pelayanan.slice(0, 2)"
|
|
:key="idx"
|
|
size="small"
|
|
class="mr-1 mb-1"
|
|
color="primary"
|
|
variant="outlined"
|
|
>
|
|
{{ service }}
|
|
</v-chip>
|
|
<v-chip
|
|
v-if="item.pelayanan.length > 2"
|
|
size="small"
|
|
color="grey"
|
|
variant="flat"
|
|
>
|
|
+{{ item.pelayanan.length - 2 }}
|
|
</v-chip>
|
|
</template>
|
|
|
|
<template v-slot:item.aksi="{ item }">
|
|
<v-btn
|
|
size="small"
|
|
color="#FF9B1B"
|
|
@click="openEditDialog(item)"
|
|
variant="flat"
|
|
class="mr-2 text-white"
|
|
>
|
|
<v-icon size="16" left>mdi-pencil</v-icon>
|
|
Edit
|
|
</v-btn>
|
|
<v-btn
|
|
size="small"
|
|
color="#E51A08"
|
|
variant="outlined"
|
|
@click="deleteLoket(item)"
|
|
>
|
|
<v-icon size="16" left>mdi-delete</v-icon>
|
|
Delete
|
|
</v-btn>
|
|
</template>
|
|
</v-data-table>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Dialog Tambah/Edit -->
|
|
<v-dialog v-model="dialog" max-width="700px" persistent>
|
|
<v-card>
|
|
<v-card-title class="dialog-header">
|
|
<span class="dialog-title">{{ isEdit ? 'Edit Loket' : 'Tambah Loket Baru' }}</span>
|
|
<v-btn icon variant="text" @click="closeDialog" size="small">
|
|
<v-icon>mdi-close</v-icon>
|
|
</v-btn>
|
|
</v-card-title>
|
|
|
|
<v-divider></v-divider>
|
|
|
|
<v-card-text class="dialog-content">
|
|
<v-form ref="formRef">
|
|
<!-- Informasi Loket -->
|
|
<div class="field-group">
|
|
<div class="group-label">Informasi Loket</div>
|
|
|
|
<v-text-field
|
|
label="Nama Loket"
|
|
v-model="formData.namaLoket"
|
|
variant="outlined"
|
|
density="compact"
|
|
:rules="[v => !!v || 'Nama loket harus diisi']"
|
|
hide-details="auto"
|
|
class="mb-3"
|
|
></v-text-field>
|
|
|
|
<v-text-field
|
|
label="Kuota Bangku"
|
|
v-model="formData.kuota"
|
|
type="number"
|
|
variant="outlined"
|
|
density="compact"
|
|
:rules="[v => !!v || 'Kuota harus diisi', v => v > 0 || 'Kuota minimal 1']"
|
|
hide-details="auto"
|
|
class="mb-3"
|
|
></v-text-field>
|
|
</div>
|
|
|
|
<v-divider class="my-4"></v-divider>
|
|
|
|
<!-- Konfigurasi -->
|
|
<div class="field-group">
|
|
<div class="group-label">Konfigurasi</div>
|
|
|
|
<v-select
|
|
label="Status Pelayanan"
|
|
:items="['RAWAT JALAN', 'RAWAT INAP']"
|
|
v-model="formData.statusPelayanan"
|
|
variant="outlined"
|
|
density="compact"
|
|
:rules="[v => !!v || 'Status pelayanan harus dipilih']"
|
|
hide-details="auto"
|
|
class="mb-3"
|
|
></v-select>
|
|
|
|
<v-row dense>
|
|
<v-col cols="6">
|
|
<v-select
|
|
label="Pembayaran"
|
|
:items="['JKN', 'UMUM', 'SPM', 'JKMM', 'JAMPERSAL', 'T4', 'KARYAWAN']"
|
|
v-model="formData.pembayaran"
|
|
variant="outlined"
|
|
density="compact"
|
|
:rules="[v => !!v || 'Pembayaran harus dipilih']"
|
|
hide-details="auto"
|
|
></v-select>
|
|
</v-col>
|
|
<v-col cols="6">
|
|
<v-select
|
|
label="Keterangan"
|
|
:items="['ONLINE', 'OFFLINE']"
|
|
v-model="formData.keterangan"
|
|
variant="outlined"
|
|
density="compact"
|
|
:rules="[v => !!v || 'Keterangan harus dipilih']"
|
|
hide-details="auto"
|
|
></v-select>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
|
|
<v-divider class="my-4"></v-divider>
|
|
|
|
<!-- Pelayanan -->
|
|
<div class="field-group">
|
|
<div class="group-label">Pelayanan</div>
|
|
|
|
<v-select
|
|
label="Pilih Pelayanan"
|
|
:items="availableServices"
|
|
v-model="formData.pelayanan"
|
|
item-title="nama"
|
|
item-value="id"
|
|
variant="outlined"
|
|
density="compact"
|
|
multiple
|
|
chips
|
|
closable-chips
|
|
:rules="[v => v.length > 0 || 'Pilih minimal 1 pelayanan']"
|
|
hide-details="auto"
|
|
>
|
|
<template v-slot:chip="{ props, item }">
|
|
<v-chip
|
|
v-bind="props"
|
|
closable
|
|
size="small"
|
|
color="primary"
|
|
>
|
|
{{ item.raw.nama }}
|
|
</v-chip>
|
|
</template>
|
|
<template v-slot:item="{ props, item }">
|
|
<v-list-item v-bind="props">
|
|
<template v-slot:prepend>
|
|
<v-chip size="x-small" color="grey-lighten-2">{{ item.raw.id }}</v-chip>
|
|
</template>
|
|
<v-list-item-title>{{ item.raw.nama }}</v-list-item-title>
|
|
</v-list-item>
|
|
</template>
|
|
</v-select>
|
|
</div>
|
|
</v-form>
|
|
</v-card-text>
|
|
|
|
<v-divider></v-divider>
|
|
|
|
<v-card-actions class="dialog-actions">
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
variant="outlined"
|
|
@click="closeDialog"
|
|
class="action-btn"
|
|
>
|
|
<v-icon left size="18">mdi-close</v-icon>
|
|
Batal
|
|
</v-btn>
|
|
<v-btn
|
|
color="#FF9B1B"
|
|
variant="flat"
|
|
@click="submitForm"
|
|
class="action-btn"
|
|
>
|
|
<v-icon left size="18">mdi-content-save</v-icon>
|
|
Simpan
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</v-container>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue';
|
|
|
|
const dialog = ref(false);
|
|
const isEdit = ref(false);
|
|
const formRef = ref(null);
|
|
|
|
const loketHeaders = ref([
|
|
{ title: "No", value: "no" },
|
|
{ title: "Nama Loket", value: "namaLoket" },
|
|
{ title: "Kuota", value: "kuota" },
|
|
{ title: "Pelayanan", value: "pelayanan" },
|
|
{ title: "Pembayaran", value: "pembayaran" },
|
|
{ title: "Keterangan", value: "keterangan" },
|
|
{ title: "Aksi", value: "aksi", sortable: false },
|
|
]);
|
|
|
|
const loketData = ref([
|
|
{
|
|
id: 1,
|
|
no: 1,
|
|
namaLoket: "Loket 1",
|
|
kuota: 500,
|
|
pelayanan: ["RADIOTERAPI", "REHAB MEDIK", "TINDAKAN"],
|
|
pembayaran: "JKN",
|
|
keterangan: "ONLINE",
|
|
},
|
|
{
|
|
id: 2,
|
|
no: 2,
|
|
namaLoket: "Loket 2",
|
|
kuota: 666,
|
|
pelayanan: ["JIWA", "SARAF"],
|
|
pembayaran: "JKN",
|
|
keterangan: "ONLINE",
|
|
},
|
|
{
|
|
id: 3,
|
|
no: 3,
|
|
namaLoket: "Loket 3",
|
|
kuota: 666,
|
|
pelayanan: ["ANESTESI", "JANTUNG"],
|
|
pembayaran: "JKN",
|
|
keterangan: "ONLINE",
|
|
},
|
|
{
|
|
id: 4,
|
|
no: 4,
|
|
namaLoket: "Loket 4",
|
|
kuota: 3676,
|
|
pelayanan: ["KULIT KELAMIN", "PARU"],
|
|
pembayaran: "JKN",
|
|
keterangan: "ONLINE",
|
|
},
|
|
]);
|
|
|
|
const availableServices = ref([
|
|
{ id: 'AN', nama: 'ANAK' },
|
|
{ id: 'AS', nama: 'ANESTESI' },
|
|
{ id: 'BD', nama: 'BEDAH' },
|
|
{ id: 'GR', nama: 'GERIATRI' },
|
|
{ id: 'GI', nama: 'GIGI DAN MULUT' },
|
|
{ id: 'GZ', nama: 'GIZI' },
|
|
{ id: 'HO', nama: 'HOM' },
|
|
{ id: 'IP', nama: 'IPD' },
|
|
{ id: 'JT', nama: 'JANTUNG' },
|
|
{ id: 'JW', nama: 'JIWA' },
|
|
{ id: 'KK', nama: 'KULIT KELAMIN' },
|
|
{ id: 'PR', nama: 'PARU' },
|
|
{ id: 'RT', nama: 'RADIOTERAPI' },
|
|
{ id: 'RM', nama: 'REHAB MEDIK' },
|
|
{ id: 'SR', nama: 'SARAF' },
|
|
{ id: 'TD', nama: 'TINDAKAN' },
|
|
]);
|
|
|
|
const formData = ref({
|
|
id: null,
|
|
namaLoket: '',
|
|
kuota: null,
|
|
statusPelayanan: '',
|
|
pembayaran: '',
|
|
keterangan: '',
|
|
pelayanan: [],
|
|
});
|
|
|
|
const openTambahDialog = () => {
|
|
isEdit.value = false;
|
|
resetForm();
|
|
dialog.value = true;
|
|
};
|
|
|
|
const openEditDialog = (item) => {
|
|
isEdit.value = true;
|
|
formData.value = {
|
|
id: item.id,
|
|
namaLoket: item.namaLoket,
|
|
kuota: item.kuota,
|
|
statusPelayanan: item.statusPelayanan || 'RAWAT JALAN',
|
|
pembayaran: item.pembayaran,
|
|
keterangan: item.keterangan,
|
|
pelayanan: [...item.pelayanan],
|
|
};
|
|
dialog.value = true;
|
|
};
|
|
|
|
const closeDialog = () => {
|
|
dialog.value = false;
|
|
resetForm();
|
|
};
|
|
|
|
const resetForm = () => {
|
|
formData.value = {
|
|
id: null,
|
|
namaLoket: '',
|
|
kuota: null,
|
|
statusPelayanan: '',
|
|
pembayaran: '',
|
|
keterangan: '',
|
|
pelayanan: [],
|
|
};
|
|
if (formRef.value) {
|
|
formRef.value.reset();
|
|
}
|
|
};
|
|
|
|
const submitForm = async () => {
|
|
const { valid } = await formRef.value.validate();
|
|
|
|
if (!valid) {
|
|
return;
|
|
}
|
|
|
|
if (isEdit.value) {
|
|
// Update existing
|
|
const index = loketData.value.findIndex(l => l.id === formData.value.id);
|
|
if (index !== -1) {
|
|
loketData.value[index] = {
|
|
...loketData.value[index],
|
|
...formData.value,
|
|
};
|
|
}
|
|
} else {
|
|
// Add new
|
|
const newId = Math.max(...loketData.value.map(l => l.id)) + 1;
|
|
const newNo = loketData.value.length + 1;
|
|
loketData.value.push({
|
|
id: newId,
|
|
no: newNo,
|
|
...formData.value,
|
|
});
|
|
}
|
|
|
|
closeDialog();
|
|
};
|
|
|
|
const deleteLoket = (item) => {
|
|
if (confirm(`Hapus loket ${item.namaLoket}?`)) {
|
|
const index = loketData.value.findIndex(l => l.id === item.id);
|
|
if (index !== -1) {
|
|
loketData.value.splice(index, 1);
|
|
// Reorder no
|
|
loketData.value.forEach((l, idx) => {
|
|
l.no = idx + 1;
|
|
});
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-header {
|
|
background: linear-gradient(135deg, #f57c00 0%, #e65100 100%);
|
|
border-radius: 16px 16px 0 0;
|
|
box-shadow: 0 4px 16px rgba(245, 124, 0, 0.2);
|
|
}
|
|
|
|
.header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 32px;
|
|
color: white;
|
|
}
|
|
|
|
.header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.header-icon {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
border-radius: 16px;
|
|
padding: 16px;
|
|
margin-right: 20px;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 32px;
|
|
font-weight: 700;
|
|
margin: 0;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.page-subtitle {
|
|
margin: 4px 0 0 0;
|
|
opacity: 0.9;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.add-btn {
|
|
font-weight: 600;
|
|
text-transform: none;
|
|
letter-spacing: 0.5px;
|
|
padding: 0 24px !important;
|
|
height: 44px !important;
|
|
color: #f57c00 !important;
|
|
}
|
|
|
|
.dialog-header {
|
|
background: linear-gradient(135deg, #f57c00 0%, #e65100 100%);
|
|
color: white;
|
|
padding: 20px 24px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.dialog-title {
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.dialog-content {
|
|
padding: 24px !important;
|
|
}
|
|
|
|
.dialog-actions {
|
|
padding: 16px 24px;
|
|
background: #fafafa;
|
|
}
|
|
|
|
.action-btn {
|
|
text-transform: none;
|
|
font-weight: 600;
|
|
min-width: 100px;
|
|
}
|
|
|
|
.field-group {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.group-label {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #f57c00;
|
|
margin-bottom: 12px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
</style> |