Files
web-antrean/components/features/master/loket/LoketFormDialog.vue
T
2026-01-12 11:52:45 +07:00

327 lines
8.4 KiB
Vue

<template>
<v-dialog v-model="dialogModel" max-width="700px" persistent scrollable>
<v-card class="dialog-card">
<v-card-title class="dialog-header">
<span class="headline-4">{{ isEdit ? 'Edit Loket' : 'Tambah Loket Baru' }}</span>
<v-btn icon variant="text" size="small" class="btn-close" @click="handleClose">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-divider/>
<v-card-text class="dialog-content">
<v-form ref="formRef">
<!-- Informasi Loket -->
<FormFieldGroup title="Informasi Loket">
<v-text-field
v-model="formModel.namaLoket"
label="Nama Loket"
variant="outlined"
density="compact"
:rules="[v => !!v || 'Nama loket harus diisi']"
hide-details="auto"
class="mb-3 input-field"
placeholder="Loket A"
/>
<v-text-field
v-model.number="formModel.kuota"
label="Kuota Bangku"
type="number"
variant="outlined"
density="compact"
:rules="[v => !!v || 'Kuota harus diisi', v => v > 0 || 'Kuota minimal 1']"
hide-details="auto"
class="mb-3 input-field"
placeholder="500"
/>
</FormFieldGroup>
<v-divider class="my-4 divider-section"/>
<!-- Konfigurasi -->
<FormFieldGroup title="Konfigurasi">
<v-select
v-model="formModel.statusPelayanan"
label="Status Pelayanan"
:items="['RAWAT JALAN', 'RAWAT INAP']"
variant="outlined"
density="compact"
:rules="[v => !!v || 'Status pelayanan harus dipilih']"
hide-details="auto"
class="mb-3 input-field"
/>
<v-row dense>
<v-col cols="6">
<v-select
v-model="formModel.pembayaran"
label="Pembayaran"
:items="['JKN', 'UMUM', 'SPM', 'JKMM', 'JAMPERSAL', 'T4', 'KARYAWAN']"
variant="outlined"
density="compact"
:rules="[v => !!v || 'Pembayaran harus dipilih']"
hide-details="auto"
class="input-field"
/>
</v-col>
<v-col cols="6">
<v-select
v-model="formModel.keterangan"
label="Keterangan"
:items="['ONLINE', 'OFFLINE']"
variant="outlined"
density="compact"
:rules="[v => !!v || 'Keterangan harus dipilih']"
hide-details="auto"
class="input-field"
/>
</v-col>
</v-row>
</FormFieldGroup>
<v-divider class="my-4 divider-section"/>
<!-- Pelayanan -->
<FormFieldGroup title="Pelayanan" icon="mdi-hospital-box">
<v-select
v-model="formModel.pelayanan"
label="Pilih Pelayanan"
:items="availableServices"
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"
class="input-field"
>
<template #chip="{ props, item }">
<v-chip
v-bind="props"
closable
size="small"
class="chip-primary"
>
{{ item.raw.nama }}
</v-chip>
</template>
<template #item="{ props, item }">
<v-list-item v-bind="props">
<template #prepend>
<v-chip size="x-small" class="chip-primary-small">{{ item.raw.id }}</v-chip>
</template>
<v-list-item-title class="body-3">{{ item.raw.nama }}</v-list-item-title>
</v-list-item>
</template>
</v-select>
<!-- Selected Services Preview -->
<div v-if="formModel.pelayanan.length > 0" class="selected-preview">
<v-icon size="14" class="icon-success">mdi-check-circle</v-icon>
<small class="caption-2">{{ formModel.pelayanan.length }} pelayanan dipilih</small>
</div>
</FormFieldGroup>
</v-form>
</v-card-text>
<v-divider/>
<v-card-actions class="dialog-actions">
<v-spacer/>
<v-btn
variant="outlined"
class="btn-cancel"
@click="handleClose"
>
<v-icon left size="18">mdi-close</v-icon>
Batal
</v-btn>
<v-btn
variant="flat"
class="btn-submit"
@click="handleSubmit"
>
<v-icon left size="18">mdi-content-save</v-icon>
Simpan
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import FormFieldGroup from '@/components/common/FormFieldGroup.vue';
const props = defineProps({
modelValue: {
type: Boolean,
required: true
},
isEdit: {
type: Boolean,
default: false
},
formData: {
type: Object,
required: true
},
availableServices: {
type: Array,
default: () => []
}
});
const emit = defineEmits(['update:modelValue', 'update:formData', 'submit', 'close']);
const formRef = ref(null);
const dialogModel = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
});
const formModel = computed({
get: () => props.formData,
set: (value) => emit('update:formData', value)
});
const handleClose = () => {
emit('close');
};
const handleSubmit = async () => {
const { valid } = await formRef.value.validate();
if (valid) {
emit('submit', formModel.value);
}
};
</script>
<style scoped lang="scss">
$neutral-100: #FFFFFF;
$neutral-300: #F5F7FA;
$neutral-400: #E5F7FA;
$neutral-600: #89939E;
$neutral-700: #717171;
$neutral-800: #4D4D4D;
$primary-600: #FFA532;
$primary-700: #FF9B1B;
$success-200: #F1FBF8;
$success-300: #84DFC1;
$success-600: #009262;
$font-family-base: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
$font-weight-regular: 400;
$font-weight-medium: 500;
$font-weight-semibold: 600;
.dialog-card {
font-family: $font-family-base;
}
.dialog-header {
background: linear-gradient(135deg, $primary-600 0%, $primary-700 100%);
color: $neutral-100;
padding: 20px 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.headline-4 {
font-size: 20px;
line-height: 28px;
font-weight: $font-weight-semibold;
margin: 0;
}
.btn-close {
color: $neutral-100 !important;
}
.dialog-content {
padding: 24px !important;
background: $neutral-300;
}
.dialog-actions {
padding: 16px 24px;
background: $neutral-300;
}
.input-field {
font-size: 14px;
line-height: 20px;
}
.divider-section {
border-color: $neutral-400 !important;
}
.body-3 {
font-size: 14px;
line-height: 20px;
font-weight: $font-weight-regular;
}
.caption-2 {
font-size: 12px;
line-height: 16px;
font-weight: $font-weight-regular;
color: $neutral-700;
}
.chip-primary {
background-color: $primary-600 !important;
color: $neutral-100 !important;
font-weight: $font-weight-medium;
}
.chip-primary-small {
background-color: $primary-600 !important;
color: $neutral-100 !important;
font-weight: $font-weight-semibold;
font-size: 12px;
line-height: 16px;
}
.selected-preview {
margin-top: 8px;
padding: 8px 12px;
background: $success-200;
border-radius: 6px;
display: flex;
align-items: center;
gap: 4px;
border: 1px solid $success-300;
}
.icon-success {
color: $success-600 !important;
}
.btn-cancel {
border-color: $neutral-600 !important;
color: $neutral-800 !important;
text-transform: none;
font-weight: $font-weight-semibold;
font-size: 16px;
line-height: 24px;
min-width: 100px;
}
.btn-submit {
background-color: $primary-600 !important;
color: $neutral-100 !important;
text-transform: none;
font-weight: $font-weight-semibold;
font-size: 16px;
line-height: 24px;
min-width: 100px;
}
</style>