feat: add SMS template IDs to dashboard settings
Let SuperAdmin save sms.ir template IDs in the database, creating missing template records on save.
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
// /src/api/settingsApi.js
|
||||||
|
import axiosInstance from './axiosInstance';
|
||||||
|
|
||||||
|
export const settingsApi = {
|
||||||
|
get: () => axiosInstance.get('/settings'),
|
||||||
|
save: (data) => axiosInstance.put('/settings', data)
|
||||||
|
};
|
||||||
@@ -3,9 +3,68 @@
|
|||||||
<div class="settings-view w-full max-w-3xl mx-auto">
|
<div class="settings-view w-full max-w-3xl mx-auto">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="تنظیمات سیستم"
|
title="تنظیمات سیستم"
|
||||||
subtitle="عملیات راهاندازی و وارد کردن دادههای دورهها و کاربران"
|
subtitle="قالبهای پیامک، راهاندازی پایگاه داده و وارد کردن دادهها"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="surface-card p-4 sm:p-5 border-round-xl border-1 border-color shadow-sm mb-4">
|
||||||
|
<div class="flex align-items-start gap-3 mb-4">
|
||||||
|
<div class="w-3rem h-3rem border-round-xl flex align-items-center justify-content-center flex-shrink-0 bg-primary-light text-primary">
|
||||||
|
<i class="pi pi-send text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<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 را وارد کنید. اگر قالبی در پایگاه داده نباشد، با ذخیره ایجاد میشود.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isSettingsLoading" class="flex align-items-center gap-2 text-muted text-sm">
|
||||||
|
<i class="pi pi-spin pi-spinner"></i>
|
||||||
|
<span>در حال بارگذاری تنظیمات پیامک…</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="flex flex-column gap-4">
|
||||||
|
<div
|
||||||
|
v-for="template in smsTemplates"
|
||||||
|
:key="template.key"
|
||||||
|
class="flex flex-column gap-2"
|
||||||
|
>
|
||||||
|
<label class="font-semibold text-sm text-color" :for="`sms-template-${template.key}`">
|
||||||
|
{{ template.label }}
|
||||||
|
</label>
|
||||||
|
<InputText
|
||||||
|
:id="`sms-template-${template.key}`"
|
||||||
|
v-model.trim="templateForm[template.key]"
|
||||||
|
class="w-full text-sm"
|
||||||
|
dir="ltr"
|
||||||
|
inputmode="numeric"
|
||||||
|
placeholder="شناسه قالب sms.ir"
|
||||||
|
/>
|
||||||
|
<div class="flex flex-wrap align-items-center gap-2">
|
||||||
|
<span class="text-xs text-muted">متغیرها:</span>
|
||||||
|
<Tag
|
||||||
|
v-for="variable in template.variables"
|
||||||
|
:key="variable"
|
||||||
|
:value="`#${variable}#`"
|
||||||
|
severity="secondary"
|
||||||
|
class="text-xs font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-content-end">
|
||||||
|
<Button
|
||||||
|
label="ذخیره قالبها"
|
||||||
|
icon="pi pi-save"
|
||||||
|
class="font-bold"
|
||||||
|
:loading="isSavingSettings"
|
||||||
|
@click="saveSmsTemplates"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="surface-card p-4 sm:p-5 border-round-xl border-1 border-color shadow-sm mb-4">
|
<div class="surface-card p-4 sm:p-5 border-round-xl border-1 border-color shadow-sm mb-4">
|
||||||
<div class="flex align-items-start gap-3 mb-4">
|
<div class="flex align-items-start gap-3 mb-4">
|
||||||
<div class="w-3rem h-3rem border-round-xl flex align-items-center justify-content-center flex-shrink-0 bg-primary-light text-primary">
|
<div class="w-3rem h-3rem border-round-xl flex align-items-center justify-content-center flex-shrink-0 bg-primary-light text-primary">
|
||||||
@@ -110,16 +169,24 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, reactive, ref } from 'vue';
|
||||||
import { useConfirm } from 'primevue/useconfirm';
|
import { useConfirm } from 'primevue/useconfirm';
|
||||||
import Button from 'primevue/button';
|
import Button from 'primevue/button';
|
||||||
|
import InputText from 'primevue/inputtext';
|
||||||
|
import Tag from 'primevue/tag';
|
||||||
import PageHeader from '@/components/common/PageHeader.vue';
|
import PageHeader from '@/components/common/PageHeader.vue';
|
||||||
import { seedApi } from '@/api/seedApi';
|
import { seedApi } from '@/api/seedApi';
|
||||||
|
import { settingsApi } from '@/api/settingsApi';
|
||||||
import { useToast } from '@/composables/useToast';
|
import { useToast } from '@/composables/useToast';
|
||||||
|
|
||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
const { showSuccess, showError } = useToast();
|
const { showSuccess, showError } = useToast();
|
||||||
|
|
||||||
|
const isSettingsLoading = ref(true);
|
||||||
|
const isSavingSettings = ref(false);
|
||||||
|
const smsTemplates = ref([]);
|
||||||
|
const templateForm = reactive({});
|
||||||
|
|
||||||
const isStatusLoading = ref(true);
|
const isStatusLoading = ref(true);
|
||||||
const isSeeding = ref(false);
|
const isSeeding = ref(false);
|
||||||
const isLocked = ref(false);
|
const isLocked = ref(false);
|
||||||
@@ -222,7 +289,51 @@ const confirmImport = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(loadStatus);
|
const applySmsTemplates = (payload) => {
|
||||||
|
const data = payload?.data || payload || {};
|
||||||
|
const templates = Array.isArray(data.smsTemplates) ? data.smsTemplates : [];
|
||||||
|
smsTemplates.value = templates;
|
||||||
|
Object.keys(templateForm).forEach((key) => {
|
||||||
|
delete templateForm[key];
|
||||||
|
});
|
||||||
|
templates.forEach((template) => {
|
||||||
|
templateForm[template.key] = template.templateId || '';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSettings = async () => {
|
||||||
|
isSettingsLoading.value = true;
|
||||||
|
try {
|
||||||
|
const response = await settingsApi.get();
|
||||||
|
applySmsTemplates(response);
|
||||||
|
} catch (err) {
|
||||||
|
showError(err);
|
||||||
|
} finally {
|
||||||
|
isSettingsLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveSmsTemplates = async () => {
|
||||||
|
isSavingSettings.value = true;
|
||||||
|
try {
|
||||||
|
const smsTemplateIds = {};
|
||||||
|
smsTemplates.value.forEach((template) => {
|
||||||
|
smsTemplateIds[template.key] = templateForm[template.key] || '';
|
||||||
|
});
|
||||||
|
const response = await settingsApi.save({ smsTemplates: smsTemplateIds });
|
||||||
|
applySmsTemplates(response);
|
||||||
|
showSuccess('قالبهای پیامک ذخیره شدند.');
|
||||||
|
} catch (err) {
|
||||||
|
showError(err);
|
||||||
|
} finally {
|
||||||
|
isSavingSettings.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadStatus();
|
||||||
|
loadSettings();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user