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:
2026-08-16 06:58:09 +03:30
parent 5fe7eb090e
commit 92eb394466
8 changed files with 361 additions and 21 deletions
+61 -5
View File
@@ -39,7 +39,10 @@
<Column field="amount" header="مبلغ کل (تومان)" sortable>
<template #body="{ data }">
{{ toPersianDigits((data.amount || 0).toLocaleString()) }} تومان
<div>{{ toPersianDigits(getPayableAmount(data).toLocaleString()) }} تومان</div>
<small v-if="data.discount" class="text-muted text-xs">
تخفیف {{ toPersianDigits((data.discount || 0).toLocaleString()) }} تومان
</small>
</template>
</Column>
@@ -77,7 +80,7 @@
</DataTableWrapper>
<!-- Create Payment Modal -->
<Dialog v-model:visible="showCreateModal" header="ایجاد صورتحساب جدید" modal :style="{ width: '480px' }">
<Dialog v-model:visible="showCreateModal" header="ایجاد صورتحساب جدید" modal :style="{ width: '520px' }">
<div class="flex flex-column gap-3 py-2">
<div class="flex flex-column gap-2">
<label class="font-semibold text-sm">انتخاب یک یا چند کلاس *</label>
@@ -112,8 +115,28 @@
</div>
<div class="flex flex-column gap-2">
<label class="font-semibold text-sm">مبلغ کل صورتحساب (تومان) *</label>
<InputNumber v-model="createForm.amount" class="w-full text-sm" suffix=" تومان" />
<label class="font-semibold text-sm" for="invoice-amount">مبلغ کل صورتحساب (تومان) *</label>
<InputNumber inputId="invoice-amount" v-model="createForm.amount" class="w-full text-sm" suffix=" تومان" :min="0" />
</div>
<div class="flex align-items-center gap-2">
<Checkbox v-model="hasDiscount" binary inputId="invoice-discount" />
<label for="invoice-discount" class="font-semibold text-sm cursor-pointer">تخفیف ؟</label>
</div>
<div v-if="hasDiscount" class="flex flex-column gap-2">
<label class="font-semibold text-sm" for="invoice-discount-amount">مبلغ تخفیف (تومان)</label>
<InputNumber
inputId="invoice-discount-amount"
v-model="createForm.discount"
class="w-full text-sm"
suffix=" تومان"
:min="0"
:max="createForm.amount || 0"
/>
<small class="font-semibold text-color">
مبلغ نهایی: {{ toPersianDigits(createPayableAmount.toLocaleString()) }} تومان
</small>
</div>
<div class="flex flex-column gap-2">
@@ -121,6 +144,17 @@
<DatePicker v-model="createForm.dueDate" class="w-full text-sm" placeholder="1403/01/01" />
</div>
<div class="flex flex-column gap-2">
<label class="font-semibold text-sm" for="invoice-notes">یادداشت</label>
<Textarea
id="invoice-notes"
v-model="createForm.notes"
rows="3"
class="w-full text-sm"
placeholder="یادداشت داخلی درباره این صورتحساب…"
/>
</div>
<NotifyChannelsField :notify="createNotify" />
</div>
<template #footer>
@@ -151,6 +185,7 @@ import ConfirmDeleteDialog from '@/components/common/ConfirmDeleteDialog.vue';
import PermissionGate from '@/components/common/PermissionGate.vue';
import StatusTag from '@/components/common/StatusTag.vue';
import NotifyChannelsField from '@/components/common/NotifyChannelsField.vue';
import { getPayableAmount } from '@/utils/paymentAmount';
import Button from 'primevue/button';
import Column from 'primevue/column';
import Tag from 'primevue/tag';
@@ -158,6 +193,8 @@ import Dialog from 'primevue/dialog';
import Dropdown from 'primevue/select';
import MultiSelect from 'primevue/multiselect';
import InputNumber from 'primevue/inputnumber';
import Checkbox from 'primevue/checkbox';
import Textarea from 'primevue/textarea';
import DatePicker from 'vue3-persian-datetime-picker';
const { toPersianDigits, formatJalali } = usePersianDate();
@@ -183,9 +220,16 @@ const createForm = reactive({
user: null,
classes: [],
amount: 0,
discount: 0,
notes: '',
dueDate: ''
});
const hasDiscount = ref(false);
const createNotify = reactive({ sms: true, email: true, bot: true });
const createPayableAmount = computed(() => getPayableAmount({
amount: createForm.amount,
discount: hasDiscount.value ? createForm.discount : 0
}));
const deleteDialogVisible = ref(false);
const selectedPayment = ref(null);
@@ -241,7 +285,10 @@ const resetCreateForm = () => {
createForm.user = null;
createForm.classes = [];
createForm.amount = 0;
createForm.discount = 0;
createForm.notes = '';
createForm.dueDate = '';
hasDiscount.value = false;
createNotify.sms = true;
createNotify.email = true;
createNotify.bot = true;
@@ -275,9 +322,18 @@ const handleCreatePayment = async () => {
if (!createForm.amount) { showError('لطفا مبلغ را وارد کنید'); return; }
if (!createForm.dueDate) { showError('لطفا تاریخ سررسید را وارد کنید'); return; }
if (hasDiscount.value && (createForm.discount || 0) > createForm.amount) {
showError('مبلغ تخفیف نمی‌تواند بیشتر از مبلغ کل باشد');
return;
}
isCreating.value = true;
try {
await paymentApi.create({ ...createForm, notify: { ...createNotify } });
await paymentApi.create({
...createForm,
discount: hasDiscount.value ? (createForm.discount || 0) : 0,
notify: { ...createNotify }
});
showSuccess('صورتحساب جدید با موفقیت ایجاد شد');
showCreateModal.value = false;
loadData();