Files
CRM_Montzar/apps/web/src/components/CustomFieldsSection.vue
T

167 lines
4.1 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { api } from '@/lib/api';
import JalaliDateInput from '@/components/JalaliDateInput.vue';
import { UiField, UiInput, UiSelect } from '@/components/ui';
import type {
CustomFieldDefinitionDto,
CustomFieldEntity,
} from '@mento/shared';
const props = defineProps<{
entityType: CustomFieldEntity;
modelValue: Record<string, unknown>;
}>();
const emit = defineEmits<{
'update:modelValue': [value: Record<string, unknown>];
}>();
const fields = ref<CustomFieldDefinitionDto[]>([]);
const loading = ref(false);
async function load() {
loading.value = true;
try {
fields.value = (
await api.get<CustomFieldDefinitionDto[]>('/custom-fields', {
params: { entityType: props.entityType, activeOnly: true },
})
).data;
} catch {
fields.value = [];
} finally {
loading.value = false;
}
}
function setValue(key: string, value: unknown) {
emit('update:modelValue', { ...props.modelValue, [key]: value });
}
function displayValue(key: string) {
const v = props.modelValue[key];
if (v == null) return '';
if (typeof v === 'boolean') return v;
return String(v);
}
watch(
() => props.entityType,
() => {
void load();
},
);
onMounted(load);
</script>
<template>
<div v-if="loading" class="muted">بارگذاری فیلدهای سفارشی…</div>
<div v-else-if="fields.length" class="custom-block">
<h3 class="custom-title">فیلدهای سفارشی</h3>
<div class="crm-form-grid-2">
<UiField
v-for="field in fields"
:key="field.id"
:label="field.label"
:required="field.required"
:for-id="`cf-${field.key}`"
>
<UiSelect
v-if="field.fieldType === 'select'"
:id="`cf-${field.key}`"
:model-value="String(displayValue(field.key))"
@update:model-value="setValue(field.key, $event)"
>
<option value="">انتخاب کنید</option>
<option v-for="opt in field.options" :key="opt" :value="opt">
{{ opt }}
</option>
</UiSelect>
<label v-else-if="field.fieldType === 'boolean'" class="cf-check">
<input
:id="`cf-${field.key}`"
type="checkbox"
:checked="Boolean(props.modelValue[field.key])"
@change="
setValue(field.key, ($event.target as HTMLInputElement).checked)
"
/>
<span>فعال</span>
</label>
<UiInput
v-else-if="field.fieldType === 'number'"
:id="`cf-${field.key}`"
type="number"
:model-value="props.modelValue[field.key] != null ? Number(props.modelValue[field.key]) : ''"
@update:model-value="
setValue(
field.key,
$event === '' || $event == null ? null : Number($event),
)
"
/>
<JalaliDateInput
v-else-if="field.fieldType === 'date'"
:id="`cf-${field.key}`"
:model-value="String(displayValue(field.key))"
@update:model-value="setValue(field.key, $event || null)"
/>
<UiInput
v-else
:id="`cf-${field.key}`"
type="text"
:model-value="String(displayValue(field.key))"
@update:model-value="setValue(field.key, $event)"
/>
</UiField>
</div>
</div>
</template>
<style scoped>
.custom-block {
display: flex;
flex-direction: column;
gap: var(--sp-3);
padding-top: var(--sp-4);
border-top: 1px dashed var(--line);
width: 100%;
}
.custom-title {
margin: 0;
font-size: var(--fs-sm);
font-weight: var(--fw-semibold);
color: var(--ink-secondary);
}
.muted {
color: var(--muted);
font-size: var(--fs-xs);
}
.cf-check {
display: inline-flex;
align-items: center;
gap: var(--sp-2);
min-height: var(--control-h-md);
font-size: var(--fs-base);
font-weight: var(--fw-medium);
color: var(--ink);
cursor: pointer;
}
.cf-check input[type='checkbox'] {
width: 18px;
height: 18px;
accent-color: var(--accent);
cursor: pointer;
}
</style>