fix(dashboard): fix date conversion offset, calendar arrow directions, payment duplicate warning, attendance class filter, and session auto-generation

This commit is contained in:
2026-08-23 14:15:26 +03:30
parent 497b065b5c
commit de87412bb1
11 changed files with 279 additions and 73 deletions
+47 -11
View File
@@ -47,14 +47,27 @@
@search-change="onSearch"
>
<template #toolbar>
<SelectButton
v-model="attendanceFilter"
:options="filterOptions"
optionLabel="label"
optionValue="value"
class="text-sm"
@change="onAttendanceFilterChange"
/>
<div class="flex align-items-center justify-content-between flex-wrap gap-2 w-full">
<SelectButton
v-model="attendanceFilter"
:options="filterOptions"
optionLabel="label"
optionValue="value"
class="text-sm"
@change="onAttendanceFilterChange"
/>
<Dropdown
v-model="selectedClassId"
:options="classesList"
optionLabel="name"
optionValue="_id"
placeholder="همه کلاس‌ها"
showClear
filter
class="w-16rem text-sm"
@change="onClassFilterChange"
/>
</div>
</template>
<Column field="topic" header="موضوع جلسه">
@@ -130,6 +143,7 @@ import { ref, onMounted } from 'vue';
import { useDataTable } from '@/composables/useDataTable';
import { usePersianDate } from '@/composables/usePersianDate';
import { sessionApi } from '@/api/sessionApi';
import { classApi } from '@/api/classApi';
import PageHeader from '@/components/common/PageHeader.vue';
import DataTableWrapper from '@/components/common/DataTableWrapper.vue';
import StatusTag from '@/components/common/StatusTag.vue';
@@ -137,6 +151,7 @@ import Button from 'primevue/button';
import Column from 'primevue/column';
import Tag from 'primevue/tag';
import SelectButton from 'primevue/selectbutton';
import Dropdown from 'primevue/dropdown';
const { formatJalali, toPersianDigits } = usePersianDate();
@@ -146,11 +161,14 @@ const filterOptions = [
{ label: 'ثبت شده', value: 'recorded' }
];
const selectedClassId = ref(null);
const classesList = ref([]);
const summary = ref({ pending: 0, recorded: 0, total: 0 });
const fetchAttendanceSessions = (params) =>
sessionApi.getAll({
...params,
class: selectedClassId.value || undefined,
attendanceScope: params.attendanceScope || attendanceFilter.value
});
@@ -185,11 +203,29 @@ const onAttendanceFilterChange = () => {
loadData();
};
const onClassFilterChange = () => {
queryParams.class = selectedClassId.value || undefined;
queryParams.page = 1;
loadData();
loadSummary();
};
const loadClasses = async () => {
try {
const res = await classApi.getAll({ limit: 500, sortBy: 'name', sortOrder: 'asc' });
const data = res.data || res;
classesList.value = data.items || data.classes || data.data || data || [];
} catch (err) {
classesList.value = [];
}
};
const loadSummary = async () => {
try {
const classParam = selectedClassId.value || undefined;
const [pendingRes, recordedRes] = await Promise.all([
sessionApi.getAll({ page: 1, limit: 1, attendanceScope: 'pending', sortBy: 'day', sortOrder: 'desc' }),
sessionApi.getAll({ page: 1, limit: 1, attendanceScope: 'recorded', sortBy: 'day', sortOrder: 'desc' })
sessionApi.getAll({ page: 1, limit: 1, attendanceScope: 'pending', class: classParam, sortBy: 'day', sortOrder: 'desc' }),
sessionApi.getAll({ page: 1, limit: 1, attendanceScope: 'recorded', class: classParam, sortBy: 'day', sortOrder: 'desc' })
]);
const pending = pendingRes.meta?.totalCount ?? 0;
const recorded = recordedRes.meta?.totalCount ?? 0;
@@ -204,6 +240,6 @@ const loadSummary = async () => {
};
onMounted(async () => {
await Promise.all([loadData(), loadSummary()]);
await Promise.all([loadData(), loadSummary(), loadClasses()]);
});
</script>