feat(settings): allow adding/removing dynamic variables in SMS templates
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<!-- /src/views/settings/SettingsView.vue -->
|
||||
<template>
|
||||
<div class="settings-view w-full max-w-3xl mx-auto">
|
||||
<div class="settings-view w-full max-w-4xl mx-auto">
|
||||
<PageHeader
|
||||
title="تنظیمات سیستم"
|
||||
subtitle="قالبهای پیامک، راهاندازی پایگاه داده و وارد کردن دادهها"
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="flex-grow-1">
|
||||
<h2 class="text-lg font-bold text-color m-0 mb-1">قالبهای پیامک</h2>
|
||||
<p class="text-sm text-muted m-0 line-height-3">
|
||||
شناسه قالب و نام متغیرهای sms.ir را وارد کنید. نام متغیر باید دقیقاً با قالب پنل یکی باشد. اگر قالبی در پایگاه داده نباشد، با ذخیره ایجاد میشود.
|
||||
شناسه قالب و تعداد و نام متغیرهای sms.ir را بر اساس قالب تعریف شده در پنل پیامک تنظیم کنید. میتوانید هر تعداد متغیر که میخواهید تعریف یا حذف نمایید.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,47 +28,104 @@
|
||||
<div
|
||||
v-for="template in smsTemplates"
|
||||
:key="template.key"
|
||||
class="p-3 border-round surface-ground border-1 border-color flex flex-column gap-3"
|
||||
class="p-4 border-round-lg surface-ground border-1 border-color flex flex-column gap-3"
|
||||
>
|
||||
<template v-if="templateForm[template.key]">
|
||||
<label class="font-semibold text-sm text-color" :for="`sms-template-${template.key}`">
|
||||
<div class="flex flex-column sm:flex-row sm:align-items-center justify-content-between gap-2 border-bottom-1 border-color pb-2">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="pi pi-bookmark text-primary"></i>
|
||||
<label class="font-bold text-base text-color" :for="`sms-template-${template.key}`">
|
||||
{{ template.label }}
|
||||
</label>
|
||||
</div>
|
||||
<span class="text-xs text-muted font-mono">{{ template.key }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-column sm:flex-row sm:align-items-center gap-2">
|
||||
<label
|
||||
class="text-sm font-semibold text-color sm:w-10rem flex-shrink-0"
|
||||
:for="`sms-template-${template.key}`"
|
||||
>
|
||||
شناسه قالب sms.ir:
|
||||
</label>
|
||||
<div class="flex-grow-1">
|
||||
<InputText
|
||||
:id="`sms-template-${template.key}`"
|
||||
v-model.trim="templateForm[template.key].templateId"
|
||||
class="w-full text-sm"
|
||||
dir="ltr"
|
||||
inputmode="numeric"
|
||||
placeholder="شناسه قالب sms.ir"
|
||||
placeholder="مثلاً: 123456"
|
||||
/>
|
||||
<div class="flex flex-column gap-2">
|
||||
<span class="text-xs text-muted">نام متغیرها در قالب sms.ir</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-column gap-3 pt-2">
|
||||
<div class="flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<span class="text-sm font-semibold text-color">متغیرهای ارسالی در قالب</span>
|
||||
<span class="text-xs text-muted block mt-1">متغیرهایی که در پنل sms.ir در متن قالب قرار دادهاید</span>
|
||||
</div>
|
||||
<Button
|
||||
label="افزودن متغیر"
|
||||
icon="pi pi-plus"
|
||||
size="small"
|
||||
outlined
|
||||
class="text-xs font-semibold"
|
||||
@click="addVariable(template.key)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="variable in template.variables"
|
||||
:key="variable.slot"
|
||||
class="flex flex-column sm:flex-row sm:align-items-center gap-2"
|
||||
v-if="!templateForm[template.key].variables || templateForm[template.key].variables.length === 0"
|
||||
class="p-3 text-center border-1 border-dashed border-round surface-card text-muted text-xs line-height-3"
|
||||
>
|
||||
<label
|
||||
class="text-sm text-color sm:w-10rem flex-shrink-0"
|
||||
:for="`sms-var-${template.key}-${variable.slot}`"
|
||||
هیچ متغیری برای این قالب تعریف نشده است. پیامک بدون متغیر ارسال خواهد شد.
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-column gap-2">
|
||||
<div
|
||||
v-for="(variable, vIdx) in templateForm[template.key].variables"
|
||||
:key="vIdx"
|
||||
class="flex flex-column sm:flex-row sm:align-items-center gap-2 p-2 border-round surface-card border-1 border-color"
|
||||
>
|
||||
{{ variable.label }}
|
||||
</label>
|
||||
<div class="flex align-items-center gap-2 sm:w-14rem flex-shrink-0">
|
||||
<span class="text-xs text-muted font-bold ml-1">{{ vIdx + 1 }}.</span>
|
||||
<Select
|
||||
v-model="variable.slot"
|
||||
:options="template.availableSlots || []"
|
||||
optionLabel="label"
|
||||
optionValue="slot"
|
||||
placeholder="انتخاب مقدار داده"
|
||||
class="w-full text-sm"
|
||||
@change="onSlotChange(template.key, vIdx)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-grow-1 flex align-items-center gap-2">
|
||||
<InputText
|
||||
:id="`sms-var-${template.key}-${variable.slot}`"
|
||||
v-model.trim="templateForm[template.key].variables[variable.slot]"
|
||||
v-model.trim="variable.name"
|
||||
class="w-full text-sm"
|
||||
dir="ltr"
|
||||
:placeholder="variable.defaultName"
|
||||
:placeholder="getSlotDefaultName(template, variable.slot) || 'نام متغیر در sms.ir'"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<Tag
|
||||
:value="`#${templateForm[template.key].variables[variable.slot] || variable.defaultName}#`"
|
||||
:value="`#${variable.name || getSlotDefaultName(template, variable.slot) || '...'}#`"
|
||||
severity="secondary"
|
||||
class="text-xs flex-shrink-0"
|
||||
class="text-xs flex-shrink-0 font-mono"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-trash"
|
||||
severity="danger"
|
||||
text
|
||||
rounded
|
||||
size="small"
|
||||
class="p-button-sm flex-shrink-0"
|
||||
title="حذف متغیر"
|
||||
@click="removeVariable(template.key, vIdx)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -195,6 +252,7 @@ import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import Button from 'primevue/button';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Select from 'primevue/select';
|
||||
import Tag from 'primevue/tag';
|
||||
import PageHeader from '@/components/common/PageHeader.vue';
|
||||
import { seedApi } from '@/api/seedApi';
|
||||
@@ -227,6 +285,44 @@ const lockedAtLabel = computed(() => {
|
||||
return `قفل شده در ${date.toLocaleString('fa-IR')}`;
|
||||
});
|
||||
|
||||
const getSlotDefaultName = (template, slotKey) => {
|
||||
const slot = (template?.availableSlots || []).find((s) => s.slot === slotKey);
|
||||
return slot?.defaultName || '';
|
||||
};
|
||||
|
||||
const onSlotChange = (templateKey, vIdx) => {
|
||||
const form = templateForm[templateKey];
|
||||
if (!form || !form.variables[vIdx]) return;
|
||||
const template = smsTemplates.value.find((t) => t.key === templateKey);
|
||||
const currentVar = form.variables[vIdx];
|
||||
if (!currentVar.name) {
|
||||
currentVar.name = getSlotDefaultName(template, currentVar.slot);
|
||||
}
|
||||
};
|
||||
|
||||
const addVariable = (templateKey) => {
|
||||
const form = templateForm[templateKey];
|
||||
if (!form) return;
|
||||
const template = smsTemplates.value.find((t) => t.key === templateKey);
|
||||
const availableSlots = template?.availableSlots || [];
|
||||
|
||||
const existingSlots = new Set((form.variables || []).map((v) => v.slot));
|
||||
const unusedSlot = availableSlots.find((s) => !existingSlots.has(s.slot));
|
||||
const selectedSlot = unusedSlot || availableSlots[0] || { slot: 'param', defaultName: 'param' };
|
||||
|
||||
form.variables.push({
|
||||
slot: selectedSlot.slot,
|
||||
name: selectedSlot.defaultName || ''
|
||||
});
|
||||
};
|
||||
|
||||
const removeVariable = (templateKey, index) => {
|
||||
const form = templateForm[templateKey];
|
||||
if (form && form.variables) {
|
||||
form.variables.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const applyStatus = (payload) => {
|
||||
const status = payload?.data || payload || {};
|
||||
isLocked.value = Boolean(status.locked);
|
||||
@@ -313,12 +409,12 @@ const confirmImport = () => {
|
||||
|
||||
const emptyFormEntry = (template) => ({
|
||||
templateId: template?.templateId || '',
|
||||
variables: Object.fromEntries(
|
||||
(template?.variables || []).map((variable) => [
|
||||
variable.slot,
|
||||
variable.name || variable.defaultName || ''
|
||||
])
|
||||
)
|
||||
variables: Array.isArray(template?.variables)
|
||||
? template.variables.map((variable) => ({
|
||||
slot: variable.slot,
|
||||
name: variable.name || ''
|
||||
}))
|
||||
: []
|
||||
});
|
||||
|
||||
const applySmsTemplates = (payload) => {
|
||||
@@ -353,7 +449,12 @@ const saveSmsTemplates = async () => {
|
||||
const entry = templateForm[template.key] || emptyFormEntry(template);
|
||||
smsTemplatesPayload[template.key] = {
|
||||
templateId: entry.templateId || '',
|
||||
variables: { ...(entry.variables || {}) }
|
||||
variables: (entry.variables || [])
|
||||
.filter((v) => v.slot)
|
||||
.map((v) => ({
|
||||
slot: v.slot,
|
||||
name: v.name || getSlotDefaultName(template, v.slot) || v.slot
|
||||
}))
|
||||
};
|
||||
});
|
||||
const response = await settingsApi.save({ smsTemplates: smsTemplatesPayload });
|
||||
|
||||
Reference in New Issue
Block a user