fix(settings): keep extra SMS variables after save

Avoid Vue's reserved slot field on the variable dropdown so newly added rows survive save.
This commit is contained in:
2026-08-15 22:55:54 +03:30
parent 196eb77b31
commit 84cd291dc2
+38 -29
View File
@@ -95,7 +95,7 @@
v-model="variable.slot" v-model="variable.slot"
:options="getAvailableSlots(template.key)" :options="getAvailableSlots(template.key)"
optionLabel="label" optionLabel="label"
optionValue="slot" optionValue="value"
placeholder="انتخاب مقدار داده" placeholder="انتخاب مقدار داده"
class="w-full text-sm" class="w-full text-sm"
@change="onSlotChange(template.key, vIdx)" @change="onSlotChange(template.key, vIdx)"
@@ -283,30 +283,30 @@ const importResult = ref(null);
const TEMPLATE_SLOT_DEFS = { const TEMPLATE_SLOT_DEFS = {
accountCreated: [ accountCreated: [
{ slot: 'username', label: 'نام کاربری', defaultName: 'user' }, { value: 'username', label: 'نام کاربری', defaultName: 'user' },
{ slot: 'password', label: 'رمز عبور', defaultName: 'password' }, { value: 'password', label: 'رمز عبور', defaultName: 'password' },
{ slot: 'fullName', label: 'نام و نام خانوادگی', defaultName: 'name' }, { value: 'fullName', label: 'نام و نام خانوادگی', defaultName: 'name' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' } { value: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
], ],
classRegistered: [ classRegistered: [
{ slot: 'className', label: 'نام کلاس', defaultName: 'className' }, { value: 'className', label: 'نام کلاس', defaultName: 'className' },
{ slot: 'courseName', label: 'نام دوره', defaultName: 'courseName' }, { value: 'courseName', label: 'نام دوره', defaultName: 'courseName' },
{ slot: 'fullName', label: 'نام کارآموز', defaultName: 'fullName' }, { value: 'fullName', label: 'نام کارآموز', defaultName: 'fullName' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' } { value: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
], ],
classReminder: [ classReminder: [
{ slot: 'className', label: 'نام کلاس', defaultName: 'className' }, { value: 'className', label: 'نام کلاس', defaultName: 'className' },
{ slot: 'time', label: 'ساعت برگزاری', defaultName: 'time' }, { value: 'time', label: 'ساعت برگزاری', defaultName: 'time' },
{ slot: 'place', label: 'مکان برگزاری', defaultName: 'place' }, { value: 'place', label: 'مکان برگزاری', defaultName: 'place' },
{ slot: 'courseName', label: 'نام دوره', defaultName: 'courseName' }, { value: 'courseName', label: 'نام دوره', defaultName: 'courseName' },
{ slot: 'fullName', label: 'نام کاربر', defaultName: 'fullName' }, { value: 'fullName', label: 'نام کاربر', defaultName: 'fullName' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' } { value: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
], ],
invoiceCreated: [ invoiceCreated: [
{ slot: 'amount', label: 'مبلغ صورتحساب (تومان)', defaultName: 'PAYMENT_PRICE' }, { value: 'amount', label: 'مبلغ صورتحساب (تومان)', defaultName: 'PAYMENT_PRICE' },
{ slot: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' }, { value: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ slot: 'course', label: 'نام دوره / کلاس', defaultName: 'COURSE' }, { value: 'course', label: 'نام دوره / کلاس', defaultName: 'COURSE' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'MOBILE' } { value: 'phoneNumber', label: 'شماره همراه', defaultName: 'MOBILE' }
] ]
}; };
@@ -316,7 +316,7 @@ const getAvailableSlots = (templateKey) => {
const getSlotDefaultName = (templateKey, slotKey) => { const getSlotDefaultName = (templateKey, slotKey) => {
const slots = getAvailableSlots(templateKey); const slots = getAvailableSlots(templateKey);
const found = slots.find((s) => s.slot === slotKey); const found = slots.find((s) => s.value === slotKey);
return found?.defaultName || ''; return found?.defaultName || '';
}; };
@@ -341,15 +341,18 @@ const onSlotChange = (templateKey, vIdx) => {
const addVariable = (templateKey) => { const addVariable = (templateKey) => {
const form = templateForm.value[templateKey]; const form = templateForm.value[templateKey];
if (!form) return; if (!form) return;
if (!Array.isArray(form.variables)) {
form.variables = [];
}
const availableSlots = getAvailableSlots(templateKey); const availableSlots = getAvailableSlots(templateKey);
const existingSlots = new Set((form.variables || []).map((v) => v.slot)); const existingSlots = new Set(form.variables.map((v) => v.slot));
const unusedSlot = availableSlots.find((s) => !existingSlots.has(s.slot)); const unusedSlot = availableSlots.find((s) => !existingSlots.has(s.value));
const selectedSlot = unusedSlot || availableSlots[0] || { slot: 'amount', defaultName: 'PAYMENT_PRICE' }; const selectedSlot = unusedSlot || availableSlots[0] || { value: 'amount', defaultName: 'PAYMENT_PRICE' };
form.variables.push({ form.variables.push({
id: nextVarId(), id: nextVarId(),
slot: selectedSlot.slot, slot: selectedSlot.value,
name: selectedSlot.defaultName || '' name: selectedSlot.defaultName || ''
}); });
}; };
@@ -448,16 +451,19 @@ const confirmImport = () => {
const emptyFormEntry = (template) => { const emptyFormEntry = (template) => {
const slots = getAvailableSlots(template?.key); const slots = getAvailableSlots(template?.key);
let vars = []; let vars = [];
if (Array.isArray(template?.variables) && template.variables.length > 0) { const incomingVars = Array.isArray(template?.variables)
vars = template.variables.map((variable) => ({ ? template.variables
: Object.values(template?.variables || {});
if (incomingVars.length > 0) {
vars = incomingVars.map((variable) => ({
id: nextVarId(), id: nextVarId(),
slot: variable.slot || slots[0]?.slot || 'amount', slot: variable.slot || slots[0]?.value || 'amount',
name: variable.name || getSlotDefaultName(template?.key, variable.slot) || '' name: variable.name || getSlotDefaultName(template?.key, variable.slot) || ''
})); }));
} else if (!template?.variables) { } else if (!template?.variables) {
vars = slots.map((s) => ({ vars = slots.map((s) => ({
id: nextVarId(), id: nextVarId(),
slot: s.slot, slot: s.value,
name: s.defaultName name: s.defaultName
})); }));
} }
@@ -497,9 +503,12 @@ const saveSmsTemplates = async () => {
const smsTemplatesPayload = {}; const smsTemplatesPayload = {};
smsTemplates.value.forEach((template) => { smsTemplates.value.forEach((template) => {
const entry = templateForm.value[template.key] || emptyFormEntry(template); const entry = templateForm.value[template.key] || emptyFormEntry(template);
const rawVariables = Array.isArray(entry.variables)
? entry.variables
: Object.values(entry.variables || {});
smsTemplatesPayload[template.key] = { smsTemplatesPayload[template.key] = {
templateId: String(entry.templateId || '').trim(), templateId: String(entry.templateId || '').trim(),
variables: (entry.variables || []) variables: rawVariables
.filter((v) => v && v.slot) .filter((v) => v && v.slot)
.map((v) => { .map((v) => {
const raw = String(v.name || '').trim().replace(/^#+|#+$/g, ''); const raw = String(v.name || '').trim().replace(/^#+|#+$/g, '');