fix: jalali date defaults, calendar arrow direction, payment status label, edit page load, and session holding notification
This commit is contained in:
@@ -10,5 +10,6 @@ export const sessionApi = {
|
|||||||
delete: (id) => axiosInstance.delete(`/sessions/admin/delete/${id}`),
|
delete: (id) => axiosInstance.delete(`/sessions/admin/delete/${id}`),
|
||||||
bulkDelete: (ids) => axiosInstance.post('/sessions/admin/bulk-delete', { ids }),
|
bulkDelete: (ids) => axiosInstance.post('/sessions/admin/bulk-delete', { ids }),
|
||||||
bulkUpdateStatus: (ids, status) => axiosInstance.post('/sessions/admin/bulk-status', { ids, status }),
|
bulkUpdateStatus: (ids, status) => axiosInstance.post('/sessions/admin/bulk-status', { ids, status }),
|
||||||
recordAttendance: (sessionId, data) => axiosInstance.put(`/sessions/admin/${sessionId}/attendance`, data)
|
recordAttendance: (sessionId, data) => axiosInstance.put(`/sessions/admin/${sessionId}/attendance`, data),
|
||||||
|
notifyHolding: (id) => axiosInstance.post(`/sessions/admin/${id}/notify-holding`)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ a {
|
|||||||
display: inline-flex !important;
|
display: inline-flex !important;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
justify-content: center !important;
|
justify-content: center !important;
|
||||||
|
transform: none !important;
|
||||||
width: 32px !important;
|
width: 32px !important;
|
||||||
height: 32px !important;
|
height: 32px !important;
|
||||||
border-radius: 8px !important;
|
border-radius: 8px !important;
|
||||||
@@ -143,6 +144,7 @@ a {
|
|||||||
width: 14px !important;
|
width: 14px !important;
|
||||||
height: 14px !important;
|
height: 14px !important;
|
||||||
fill: var(--text-primary) !important;
|
fill: var(--text-primary) !important;
|
||||||
|
transform: none !important;
|
||||||
|
|
||||||
path {
|
path {
|
||||||
fill: var(--text-primary) !important;
|
fill: var(--text-primary) !important;
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
<StatusTag :status="data.status || 'scheduled'" type="session" />
|
<StatusTag :status="data.status || 'scheduled'" type="session" />
|
||||||
</template>
|
</template>
|
||||||
</Column>
|
</Column>
|
||||||
<Column header="عملیات" style="width: 140px">
|
<Column header="عملیات" style="width: 170px">
|
||||||
<template #body="{ data }">
|
<template #body="{ data }">
|
||||||
<div class="flex align-items-center gap-1">
|
<div class="flex align-items-center gap-1">
|
||||||
<Button
|
<Button
|
||||||
@@ -86,6 +86,16 @@
|
|||||||
v-tooltip.top="'حضور و غیاب'"
|
v-tooltip.top="'حضور و غیاب'"
|
||||||
@click="$router.push(`/sessions/attendance/${data._id || data.id}`)"
|
@click="$router.push(`/sessions/attendance/${data._id || data.id}`)"
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
icon="pi pi-send"
|
||||||
|
text
|
||||||
|
rounded
|
||||||
|
size="small"
|
||||||
|
severity="info"
|
||||||
|
v-tooltip.top="'اطلاعرسانی برگزاری جلسه'"
|
||||||
|
:loading="notifyingSessionId === (data._id || data.id)"
|
||||||
|
@click="openNotifyHoldingDialog(data)"
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon="pi pi-pencil"
|
icon="pi pi-pencil"
|
||||||
text
|
text
|
||||||
@@ -118,6 +128,34 @@
|
|||||||
@confirm="handleDeleteConfirm"
|
@confirm="handleDeleteConfirm"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
v-model:visible="notifyDialogVisible"
|
||||||
|
header="اطلاعرسانی برگزاری جلسه"
|
||||||
|
modal
|
||||||
|
:style="{ width: '450px' }"
|
||||||
|
>
|
||||||
|
<div class="flex flex-column gap-3 py-2">
|
||||||
|
<p class="m-0 line-height-3 text-sm">
|
||||||
|
آیا از ارسال پیامک برگزاری جلسه طبق برنامه به تمامی دانشجویان این کلاس اطمینان دارید؟
|
||||||
|
</p>
|
||||||
|
<div v-if="selectedSessionForNotify" class="surface-100 p-3 border-round text-xs flex flex-column gap-1">
|
||||||
|
<div><strong>موضوع جلسه:</strong> {{ selectedSessionForNotify.topic || 'طبق برنامه' }}</div>
|
||||||
|
<div><strong>تاریخ:</strong> {{ formatJalali(selectedSessionForNotify.day || selectedSessionForNotify.date) }}</div>
|
||||||
|
<div><strong>ساعت:</strong> {{ selectedSessionForNotify.startTime || '—' }} تا {{ selectedSessionForNotify.endTime || '—' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<Button label="انصراف" text severity="secondary" @click="notifyDialogVisible = false" />
|
||||||
|
<Button
|
||||||
|
label="ارسال پیامک"
|
||||||
|
icon="pi pi-send"
|
||||||
|
severity="info"
|
||||||
|
:loading="isNotifying"
|
||||||
|
@click="handleSendSessionHolding"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
<!-- Bulk auto-create: only when no sessions yet -->
|
<!-- Bulk auto-create: only when no sessions yet -->
|
||||||
<div v-if="!hasSessions" class="surface-card p-4 sm:p-5 border-round border-1 border-color shadow-sm">
|
<div v-if="!hasSessions" class="surface-card p-4 sm:p-5 border-round border-1 border-color shadow-sm">
|
||||||
<h2 class="text-lg font-bold text-color mb-1">ایجاد جلسات کلاس</h2>
|
<h2 class="text-lg font-bold text-color mb-1">ایجاد جلسات کلاس</h2>
|
||||||
@@ -249,6 +287,7 @@ import SelectButton from 'primevue/selectbutton';
|
|||||||
import DatePicker from 'vue3-persian-datetime-picker';
|
import DatePicker from 'vue3-persian-datetime-picker';
|
||||||
import DataTable from 'primevue/datatable';
|
import DataTable from 'primevue/datatable';
|
||||||
import Column from 'primevue/column';
|
import Column from 'primevue/column';
|
||||||
|
import Dialog from 'primevue/dialog';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
classId: { type: [String, Object], required: true },
|
classId: { type: [String, Object], required: true },
|
||||||
@@ -260,7 +299,7 @@ const props = defineProps({
|
|||||||
endTime: { type: String, default: '' }
|
endTime: { type: String, default: '' }
|
||||||
});
|
});
|
||||||
|
|
||||||
const { toPersianDigits, toLatinDigits, formatJalali } = usePersianDate();
|
const { toPersianDigits, toLatinDigits, formatJalali, getTodayJalali } = usePersianDate();
|
||||||
const { showSuccess, showError } = useToast();
|
const { showSuccess, showError } = useToast();
|
||||||
|
|
||||||
const todayJalali = () => moment().locale('fa').format('jYYYY/jMM/jDD');
|
const todayJalali = () => moment().locale('fa').format('jYYYY/jMM/jDD');
|
||||||
@@ -278,6 +317,35 @@ const bulkStatus = ref(null);
|
|||||||
const generatedSessions = ref([]);
|
const generatedSessions = ref([]);
|
||||||
const manualSessions = ref([{ date: todayJalali(), startTime: '19:00', endTime: '20:30', topic: '' }]);
|
const manualSessions = ref([{ date: todayJalali(), startTime: '19:00', endTime: '20:30', topic: '' }]);
|
||||||
|
|
||||||
|
const notifyDialogVisible = ref(false);
|
||||||
|
const selectedSessionForNotify = ref(null);
|
||||||
|
const notifyingSessionId = ref(null);
|
||||||
|
const isNotifying = ref(false);
|
||||||
|
|
||||||
|
const openNotifyHoldingDialog = (session) => {
|
||||||
|
selectedSessionForNotify.value = session;
|
||||||
|
notifyDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSendSessionHolding = async () => {
|
||||||
|
if (!selectedSessionForNotify.value) return;
|
||||||
|
const id = selectedSessionForNotify.value._id || selectedSessionForNotify.value.id;
|
||||||
|
notifyingSessionId.value = id;
|
||||||
|
isNotifying.value = true;
|
||||||
|
try {
|
||||||
|
const res = await sessionApi.notifyHolding(id);
|
||||||
|
const count = res.data?.sentCount || res.data?.totalStudents || '';
|
||||||
|
showSuccess(`پیامک برگزاری جلسه با موفقیت به ${count ? toPersianDigits(count) + ' نفر از ' : ''}دانشجویان ارسال شد`);
|
||||||
|
notifyDialogVisible.value = false;
|
||||||
|
selectedSessionForNotify.value = null;
|
||||||
|
} catch (err) {
|
||||||
|
showError(err);
|
||||||
|
} finally {
|
||||||
|
notifyingSessionId.value = null;
|
||||||
|
isNotifying.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ label: 'طبق برنامه', value: 'scheduled' },
|
{ label: 'طبق برنامه', value: 'scheduled' },
|
||||||
{ label: 'برگزار شده', value: 'held' },
|
{ label: 'برگزار شده', value: 'held' },
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const NOTIFICATION_LABELS = {
|
|||||||
|
|
||||||
const PAYMENT_LABELS = {
|
const PAYMENT_LABELS = {
|
||||||
paid: 'تسویهشده',
|
paid: 'تسویهشده',
|
||||||
partial: 'پرداخت جزئی',
|
partial: 'پیش پرداخت',
|
||||||
pending: 'در انتظار پرداخت',
|
pending: 'در انتظار پرداخت',
|
||||||
overdue: 'معوق',
|
overdue: 'معوق',
|
||||||
cancelled: 'لغوشده'
|
cancelled: 'لغوشده'
|
||||||
@@ -63,7 +63,7 @@ const severity = computed(() => {
|
|||||||
|
|
||||||
if (props.type === 'payment') {
|
if (props.type === 'payment') {
|
||||||
if (val === 'paid' || val === 'تسویه شده' || val === 'تسویهشده') return 'success';
|
if (val === 'paid' || val === 'تسویه شده' || val === 'تسویهشده') return 'success';
|
||||||
if (val === 'partial' || val === 'پرداخت جزئی') return 'contrast';
|
if (val === 'partial' || val === 'پرداخت جزئی' || val === 'پیش پرداخت') return 'contrast';
|
||||||
if (val === 'pending' || val === 'در انتظار پرداخت') return 'info';
|
if (val === 'pending' || val === 'در انتظار پرداخت') return 'info';
|
||||||
if (val === 'overdue' || val === 'معوق شده' || val === 'معوق') return 'danger';
|
if (val === 'overdue' || val === 'معوق شده' || val === 'معوق') return 'danger';
|
||||||
if (val === 'cancelled' || val === 'canceled' || val === 'لغوشده') return 'secondary';
|
if (val === 'cancelled' || val === 'canceled' || val === 'لغوشده') return 'secondary';
|
||||||
|
|||||||
@@ -5,9 +5,48 @@
|
|||||||
:title="$t('attendances.title')"
|
:title="$t('attendances.title')"
|
||||||
:subtitle="`دوره: ${session.course?.title || '-'} | کلاس: ${session.class?.name || '-'} | تاریخ: ${formatJalali(session.day)} (${session.startTime || ''} تا ${session.endTime || ''})`"
|
:subtitle="`دوره: ${session.course?.title || '-'} | کلاس: ${session.class?.name || '-'} | تاریخ: ${formatJalali(session.day)} (${session.startTime || ''} تا ${session.endTime || ''})`"
|
||||||
>
|
>
|
||||||
<Button label="بازگشت" icon="pi pi-arrow-left" text severity="secondary" @click="$router.push('/attendance')" />
|
<div class="flex align-items-center gap-2">
|
||||||
|
<Button
|
||||||
|
label="ارسال پیامک برگزاری جلسه"
|
||||||
|
icon="pi pi-send"
|
||||||
|
severity="info"
|
||||||
|
outlined
|
||||||
|
size="small"
|
||||||
|
:loading="isNotifying"
|
||||||
|
@click="notifyDialogVisible = true"
|
||||||
|
/>
|
||||||
|
<Button label="بازگشت" icon="pi pi-arrow-left" text severity="secondary" @click="$router.push('/attendance')" />
|
||||||
|
</div>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
v-model:visible="notifyDialogVisible"
|
||||||
|
header="اطلاعرسانی برگزاری جلسه"
|
||||||
|
modal
|
||||||
|
:style="{ width: '450px' }"
|
||||||
|
>
|
||||||
|
<div class="flex flex-column gap-3 py-2">
|
||||||
|
<p class="m-0 line-height-3 text-sm">
|
||||||
|
آیا از ارسال پیامک برگزاری جلسه طبق برنامه به تمامی دانشجویان کلاس «{{ session?.class?.name || session?.course?.title || '' }}» اطمینان دارید؟
|
||||||
|
</p>
|
||||||
|
<div class="surface-100 p-3 border-round text-xs flex flex-column gap-1">
|
||||||
|
<div><strong>موضوع جلسه:</strong> {{ session.topic || 'طبق برنامه' }}</div>
|
||||||
|
<div><strong>تاریخ:</strong> {{ formatJalali(session.day || session.date) }}</div>
|
||||||
|
<div><strong>ساعت:</strong> {{ session.startTime || '—' }} تا {{ session.endTime || '—' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<Button label="انصراف" text severity="secondary" @click="notifyDialogVisible = false" />
|
||||||
|
<Button
|
||||||
|
label="ارسال پیامک"
|
||||||
|
icon="pi pi-send"
|
||||||
|
severity="info"
|
||||||
|
:loading="isNotifying"
|
||||||
|
@click="handleSendSessionHolding"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="pendingStudents.length"
|
v-if="pendingStudents.length"
|
||||||
class="surface-card p-3 sm:p-4 border-round border-1 border-orange-300 bg-orange-50 mb-3 text-sm text-orange-900"
|
class="surface-card p-3 sm:p-4 border-round border-1 border-orange-300 bg-orange-50 mb-3 text-sm text-orange-900"
|
||||||
@@ -129,6 +168,7 @@ import Column from 'primevue/column';
|
|||||||
import SelectButton from 'primevue/selectbutton';
|
import SelectButton from 'primevue/selectbutton';
|
||||||
import InputText from 'primevue/inputtext';
|
import InputText from 'primevue/inputtext';
|
||||||
import Tag from 'primevue/tag';
|
import Tag from 'primevue/tag';
|
||||||
|
import Dialog from 'primevue/dialog';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -140,6 +180,23 @@ const { showSuccess, showError } = useToast();
|
|||||||
const session = ref(null);
|
const session = ref(null);
|
||||||
const studentList = ref([]);
|
const studentList = ref([]);
|
||||||
const isSubmitting = ref(false);
|
const isSubmitting = ref(false);
|
||||||
|
const notifyDialogVisible = ref(false);
|
||||||
|
const isNotifying = ref(false);
|
||||||
|
|
||||||
|
const handleSendSessionHolding = async () => {
|
||||||
|
if (!sessionId) return;
|
||||||
|
isNotifying.value = true;
|
||||||
|
try {
|
||||||
|
const res = await sessionApi.notifyHolding(sessionId);
|
||||||
|
const count = res.data?.sentCount || res.data?.totalStudents || '';
|
||||||
|
showSuccess(`پیامک برگزاری جلسه با موفقیت به ${count ? toPersianDigits(count) + ' نفر از ' : ''}دانشجویان ارسال شد`);
|
||||||
|
notifyDialogVisible.value = false;
|
||||||
|
} catch (err) {
|
||||||
|
showError(err);
|
||||||
|
} finally {
|
||||||
|
isNotifying.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ label: 'حاضر', value: 'present' },
|
{ label: 'حاضر', value: 'present' },
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<div class="flex align-items-center justify-content-between mb-3">
|
<div class="flex align-items-center justify-content-between mb-3">
|
||||||
<h3 class="text-lg font-bold m-0">تاریخچه تراکنشهای دریافتی</h3>
|
<h3 class="text-lg font-bold m-0">تاریخچه تراکنشهای دریافتی</h3>
|
||||||
<PermissionGate permission="payments:update">
|
<PermissionGate permission="payments:update">
|
||||||
<Button label="ثبت تراکنش جدید" icon="pi pi-plus" size="small" severity="success" @click="showTransactionModal = true" />
|
<Button label="ثبت تراکنش جدید" icon="pi pi-plus" size="small" severity="success" @click="openTransactionModal" />
|
||||||
</PermissionGate>
|
</PermissionGate>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -316,6 +316,16 @@ const defaultTrxForm = () => ({
|
|||||||
|
|
||||||
const trxForm = reactive(defaultTrxForm());
|
const trxForm = reactive(defaultTrxForm());
|
||||||
|
|
||||||
|
const openTransactionModal = () => {
|
||||||
|
Object.assign(trxForm, defaultTrxForm());
|
||||||
|
if (remainingAmount.value > 0) {
|
||||||
|
trxForm.amount = remainingAmount.value;
|
||||||
|
}
|
||||||
|
trxForm.dueDate = getTodayJalali();
|
||||||
|
trxForm.date = getTodayJalali();
|
||||||
|
showTransactionModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
const editForm = reactive({
|
const editForm = reactive({
|
||||||
amount: 0,
|
amount: 0,
|
||||||
status: 'paid',
|
status: 'paid',
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ const createForm = reactive({
|
|||||||
amount: 0,
|
amount: 0,
|
||||||
discount: 0,
|
discount: 0,
|
||||||
notes: '',
|
notes: '',
|
||||||
dueDate: ''
|
dueDate: getTodayJalali()
|
||||||
});
|
});
|
||||||
const hasDiscount = ref(false);
|
const hasDiscount = ref(false);
|
||||||
const createNotify = reactive({ sms: true, email: true, bot: true });
|
const createNotify = reactive({ sms: true, email: true, bot: true });
|
||||||
@@ -287,10 +287,8 @@ const onClassesSelected = () => {
|
|||||||
if ((createForm.discount || 0) > (createForm.amount || 0)) {
|
if ((createForm.discount || 0) > (createForm.amount || 0)) {
|
||||||
createForm.discount = createForm.amount || 0;
|
createForm.discount = createForm.amount || 0;
|
||||||
}
|
}
|
||||||
|
if (!createForm.dueDate) {
|
||||||
const firstSelectedClass = classesList.value.find((item) => String(item._id || item.id) === String(createForm.classes[0]));
|
createForm.dueDate = getTodayJalali();
|
||||||
if (firstSelectedClass) {
|
|
||||||
createForm.dueDate = calculateClassMidDate(firstSelectedClass);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ import Button from 'primevue/button';
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { showSuccess, showError } = useToast();
|
const { showSuccess, showError } = useToast();
|
||||||
const { toLatinDigits } = usePersianDate();
|
const { toLatinDigits, getTodayJalali } = usePersianDate();
|
||||||
|
|
||||||
const sessionId = route.params.id;
|
const sessionId = route.params.id;
|
||||||
const isEditMode = computed(() => !!sessionId);
|
const isEditMode = computed(() => !!sessionId);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
||||||
<Column header="عملیات" style="width: 170px">
|
<Column header="عملیات" style="width: 200px">
|
||||||
<template #body="{ data }">
|
<template #body="{ data }">
|
||||||
<div class="flex align-items-center gap-1">
|
<div class="flex align-items-center gap-1">
|
||||||
<PermissionGate permission="sessions:attendance">
|
<PermissionGate permission="sessions:attendance">
|
||||||
@@ -131,6 +131,19 @@
|
|||||||
/>
|
/>
|
||||||
</PermissionGate>
|
</PermissionGate>
|
||||||
|
|
||||||
|
<PermissionGate permission="sessions:read">
|
||||||
|
<Button
|
||||||
|
icon="pi pi-send"
|
||||||
|
text
|
||||||
|
rounded
|
||||||
|
size="small"
|
||||||
|
severity="info"
|
||||||
|
v-tooltip.top="'اطلاعرسانی برگزاری جلسه'"
|
||||||
|
:loading="notifyingSessionId === (data._id || data.id)"
|
||||||
|
@click="openNotifyHoldingDialog(data)"
|
||||||
|
/>
|
||||||
|
</PermissionGate>
|
||||||
|
|
||||||
<PermissionGate permission="sessions:update">
|
<PermissionGate permission="sessions:update">
|
||||||
<Button icon="pi pi-pencil" text rounded size="small" severity="warning" v-tooltip.top="'ویرایش'" @click="$router.push(`/sessions/edit/${data._id || data.id}`)" />
|
<Button icon="pi pi-pencil" text rounded size="small" severity="warning" v-tooltip.top="'ویرایش'" @click="$router.push(`/sessions/edit/${data._id || data.id}`)" />
|
||||||
</PermissionGate>
|
</PermissionGate>
|
||||||
@@ -150,6 +163,34 @@
|
|||||||
:message="bulkDeleteMessage"
|
:message="bulkDeleteMessage"
|
||||||
@confirm="handleDeleteConfirm"
|
@confirm="handleDeleteConfirm"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
v-model:visible="notifyDialogVisible"
|
||||||
|
header="اطلاعرسانی برگزاری جلسه"
|
||||||
|
modal
|
||||||
|
:style="{ width: '450px' }"
|
||||||
|
>
|
||||||
|
<div class="flex flex-column gap-3 py-2">
|
||||||
|
<p class="m-0 line-height-3 text-sm">
|
||||||
|
آیا از ارسال پیامک برگزاری جلسه طبق برنامه به دانشجویان کلاس «{{ selectedSessionForNotify?.class?.name || selectedSessionForNotify?.course?.title || '' }}» اطمینان دارید؟
|
||||||
|
</p>
|
||||||
|
<div v-if="selectedSessionForNotify" class="surface-100 p-3 border-round text-xs flex flex-column gap-1">
|
||||||
|
<div><strong>موضوع جلسه:</strong> {{ selectedSessionForNotify.topic || 'طبق برنامه' }}</div>
|
||||||
|
<div><strong>تاریخ:</strong> {{ formatJalali(selectedSessionForNotify.day || selectedSessionForNotify.date) }}</div>
|
||||||
|
<div><strong>ساعت:</strong> {{ selectedSessionForNotify.startTime || '—' }} تا {{ selectedSessionForNotify.endTime || '—' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<Button label="انصراف" text severity="secondary" @click="notifyDialogVisible = false" />
|
||||||
|
<Button
|
||||||
|
label="ارسال پیامک"
|
||||||
|
icon="pi pi-send"
|
||||||
|
severity="info"
|
||||||
|
:loading="isNotifying"
|
||||||
|
@click="handleSendSessionHolding"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -170,6 +211,7 @@ import Column from 'primevue/column';
|
|||||||
import Tag from 'primevue/tag';
|
import Tag from 'primevue/tag';
|
||||||
import Select from 'primevue/select';
|
import Select from 'primevue/select';
|
||||||
import Dropdown from 'primevue/select';
|
import Dropdown from 'primevue/select';
|
||||||
|
import Dialog from 'primevue/dialog';
|
||||||
|
|
||||||
const { formatJalali, toPersianDigits } = usePersianDate();
|
const { formatJalali, toPersianDigits } = usePersianDate();
|
||||||
const { showSuccess, showError } = useToast();
|
const { showSuccess, showError } = useToast();
|
||||||
@@ -200,6 +242,35 @@ const selectedSession = ref(null);
|
|||||||
const bulkDeleteMode = ref(false);
|
const bulkDeleteMode = ref(false);
|
||||||
const isDeleting = ref(false);
|
const isDeleting = ref(false);
|
||||||
|
|
||||||
|
const notifyDialogVisible = ref(false);
|
||||||
|
const selectedSessionForNotify = ref(null);
|
||||||
|
const notifyingSessionId = ref(null);
|
||||||
|
const isNotifying = ref(false);
|
||||||
|
|
||||||
|
const openNotifyHoldingDialog = (session) => {
|
||||||
|
selectedSessionForNotify.value = session;
|
||||||
|
notifyDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSendSessionHolding = async () => {
|
||||||
|
if (!selectedSessionForNotify.value) return;
|
||||||
|
const id = selectedSessionForNotify.value._id || selectedSessionForNotify.value.id;
|
||||||
|
notifyingSessionId.value = id;
|
||||||
|
isNotifying.value = true;
|
||||||
|
try {
|
||||||
|
const res = await sessionApi.notifyHolding(id);
|
||||||
|
const count = res.data?.sentCount || res.data?.totalStudents || '';
|
||||||
|
showSuccess(`پیامک برگزاری جلسه با موفقیت به ${count ? toPersianDigits(count) + ' نفر از ' : ''}دانشجویان ارسال شد`);
|
||||||
|
notifyDialogVisible.value = false;
|
||||||
|
selectedSessionForNotify.value = null;
|
||||||
|
} catch (err) {
|
||||||
|
showError(err);
|
||||||
|
} finally {
|
||||||
|
notifyingSessionId.value = null;
|
||||||
|
isNotifying.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ label: 'طبق برنامه', value: 'scheduled' },
|
{ label: 'طبق برنامه', value: 'scheduled' },
|
||||||
{ label: 'برگزار شده', value: 'held' },
|
{ label: 'برگزار شده', value: 'held' },
|
||||||
|
|||||||
Reference in New Issue
Block a user