feat: add messaging toggles, password reset UI, and payment discounts
Add notification channel toggles to settings, password reset SMS action on user form, and discount/payable amount display on payment views.
This commit is contained in:
@@ -3,9 +3,76 @@
|
||||
<div class="settings-view w-full max-w-4xl mx-auto">
|
||||
<PageHeader
|
||||
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-bell 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">
|
||||
ارسال پیامک، ایمیل و پیام ربات را از داشبورد فعال یا غیرفعال کنید. هر کانال فقط وقتی ارسال میشود که هم اینجا و هم متغیر محیطی سرور فعال باشد.
|
||||
</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-3">
|
||||
<div
|
||||
v-for="channel in messagingChannels"
|
||||
:key="channel.key"
|
||||
class="p-4 border-round-lg surface-ground border-1 border-color flex flex-column sm:flex-row sm:align-items-center justify-content-between gap-3"
|
||||
>
|
||||
<div class="flex align-items-start gap-3">
|
||||
<div class="w-2rem h-2rem border-round-lg flex align-items-center justify-content-center flex-shrink-0 bg-primary-light text-primary">
|
||||
<i :class="channel.icon"></i>
|
||||
</div>
|
||||
<div>
|
||||
<label class="font-bold text-base text-color cursor-pointer" :for="`messaging-${channel.key}`">
|
||||
{{ channel.label }}
|
||||
</label>
|
||||
<p class="text-xs text-muted m-0 mt-1 line-height-3">
|
||||
متغیر محیطی: <code dir="ltr">{{ channel.envName }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex align-items-center gap-3 flex-shrink-0">
|
||||
<Tag
|
||||
:value="channelStatus(channel.key).label"
|
||||
:severity="channelStatus(channel.key).severity"
|
||||
class="text-xs"
|
||||
/>
|
||||
<InputSwitch
|
||||
:inputId="`messaging-${channel.key}`"
|
||||
v-model="messagingForm[channel.flag]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-muted m-0 line-height-3">
|
||||
اگر وضعیت «غیرفعال در سرور» باشد، روشن کردن سوییچ داشبورد بهتنهایی کافی نیست و باید متغیر محیطی مربوط هم فعال شود.
|
||||
</p>
|
||||
|
||||
<div class="flex justify-content-end">
|
||||
<Button
|
||||
label="ذخیره کانالها"
|
||||
icon="pi pi-save"
|
||||
class="font-bold"
|
||||
:loading="isSavingMessaging"
|
||||
@click="saveMessaging"
|
||||
/>
|
||||
</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="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">
|
||||
@@ -253,6 +320,7 @@
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import Button from 'primevue/button';
|
||||
import InputSwitch from 'primevue/toggleswitch';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Select from 'primevue/select';
|
||||
import Tag from 'primevue/tag';
|
||||
@@ -266,8 +334,34 @@ const { showSuccess, showError } = useToast();
|
||||
|
||||
const isSettingsLoading = ref(true);
|
||||
const isSavingSettings = ref(false);
|
||||
const isSavingMessaging = ref(false);
|
||||
const smsTemplates = ref([]);
|
||||
const templateForm = ref({});
|
||||
const messagingForm = ref({
|
||||
smsEnabled: true,
|
||||
emailEnabled: true,
|
||||
botEnabled: true
|
||||
});
|
||||
const messagingEnv = ref({
|
||||
smsEnabled: false,
|
||||
emailEnabled: true,
|
||||
botEnabled: true
|
||||
});
|
||||
|
||||
const messagingChannels = [
|
||||
{ key: 'sms', flag: 'smsEnabled', label: 'پیامک', icon: 'pi pi-mobile', envName: 'SMS_ENABLED' },
|
||||
{ key: 'email', flag: 'emailEnabled', label: 'ایمیل', icon: 'pi pi-envelope', envName: 'EMAIL_ENABLED' },
|
||||
{ key: 'bot', flag: 'botEnabled', label: 'پیام ربات', icon: 'pi pi-comments', envName: 'BOT_ENABLED' }
|
||||
];
|
||||
|
||||
const channelStatus = (channelKey) => {
|
||||
const flag = `${channelKey}Enabled`;
|
||||
const dbOn = messagingForm.value[flag] !== false;
|
||||
const envOn = Boolean(messagingEnv.value[flag]);
|
||||
if (envOn && dbOn) return { label: 'ارسال فعال', severity: 'success' };
|
||||
if (!envOn) return { label: 'غیرفعال در سرور', severity: 'warn' };
|
||||
return { label: 'غیرفعال از داشبورد', severity: 'secondary' };
|
||||
};
|
||||
|
||||
const isStatusLoading = ref(true);
|
||||
const isSeeding = ref(false);
|
||||
@@ -485,11 +579,31 @@ const applySmsTemplates = (payload) => {
|
||||
templateForm.value = newForm;
|
||||
};
|
||||
|
||||
const applyMessaging = (payload) => {
|
||||
const data = payload?.data?.data || payload?.data || payload || {};
|
||||
const messaging = data.messaging || {};
|
||||
messagingForm.value = {
|
||||
smsEnabled: messaging.smsEnabled !== false,
|
||||
emailEnabled: messaging.emailEnabled !== false,
|
||||
botEnabled: messaging.botEnabled !== false
|
||||
};
|
||||
messagingEnv.value = {
|
||||
smsEnabled: Boolean(messaging.env?.smsEnabled),
|
||||
emailEnabled: Boolean(messaging.env?.emailEnabled),
|
||||
botEnabled: Boolean(messaging.env?.botEnabled)
|
||||
};
|
||||
};
|
||||
|
||||
const applySettings = (payload) => {
|
||||
applySmsTemplates(payload);
|
||||
applyMessaging(payload);
|
||||
};
|
||||
|
||||
const loadSettings = async () => {
|
||||
isSettingsLoading.value = true;
|
||||
try {
|
||||
const response = await settingsApi.get();
|
||||
applySmsTemplates(response);
|
||||
applySettings(response);
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
} finally {
|
||||
@@ -497,6 +611,25 @@ const loadSettings = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const saveMessaging = async () => {
|
||||
isSavingMessaging.value = true;
|
||||
try {
|
||||
const response = await settingsApi.save({
|
||||
messaging: {
|
||||
smsEnabled: Boolean(messagingForm.value.smsEnabled),
|
||||
emailEnabled: Boolean(messagingForm.value.emailEnabled),
|
||||
botEnabled: Boolean(messagingForm.value.botEnabled)
|
||||
}
|
||||
});
|
||||
applySettings(response);
|
||||
showSuccess('تنظیمات کانالهای اطلاعرسانی با موفقیت ذخیره شد.');
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
} finally {
|
||||
isSavingMessaging.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const saveSmsTemplates = async () => {
|
||||
isSavingSettings.value = true;
|
||||
try {
|
||||
@@ -521,7 +654,7 @@ const saveSmsTemplates = async () => {
|
||||
};
|
||||
});
|
||||
const response = await settingsApi.save({ smsTemplates: smsTemplatesPayload });
|
||||
applySmsTemplates(response);
|
||||
applySettings(response);
|
||||
showSuccess('قالبهای پیامک با موفقیت ذخیره شدند.');
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
|
||||
Reference in New Issue
Block a user