feat(payments): auto-populate payment due date with middle session date upon class selection

This commit is contained in:
2026-08-22 00:15:03 +03:30
parent 2d124eb0eb
commit 60b21fd94a
3 changed files with 105 additions and 17 deletions
+50 -14
View File
@@ -42,6 +42,30 @@ export function formatClassSchedule(cls) {
return days || time || '';
}
/**
* Parses any date format (Jalali string, ISO string, Date object, Persian/Latin digits) into a valid moment object
*/
export function parseDateSmart(input) {
if (!input) return null;
if (input instanceof Date) {
const m = moment(input);
return m.isValid() ? m : null;
}
const latin = String(input)
.replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d))
.replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d))
.trim();
const isJalaliPattern = /^(1[34]\d{2})[/-](\d{1,2})[/-](\d{1,2})/.test(latin);
if (isJalaliPattern) {
const m = moment(latin, ['jYYYY/jMM/jDD', 'jYYYY-jMM-jDD', 'jYYYY/jM/jD', 'jYYYY-jM-jD']);
if (m.isValid()) return m;
}
const m = moment(latin);
if (m.isValid()) return m;
const mJalali = moment(latin, ['jYYYY/jMM/jDD', 'jYYYY-jMM-jDD']);
return mJalali.isValid() ? mJalali : null;
}
/**
* Calculates the class end date in Jalali (jYYYY/jMM/jDD) based on start date, selected days, and number of sessions.
* @param {string|Date} startDate - Jalali string (e.g. 1403/01/01) or ISO string / Date
@@ -58,13 +82,8 @@ export function calculateClassEndDate(startDate, days = [], numberOfSessions = 0
: [];
if (!validDays.length) return '';
const latinStartDate = String(startDate)
.replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d))
.replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d))
.trim();
let current = moment(latinStartDate, ['jYYYY/jMM/jDD', 'YYYY-MM-DD', 'YYYY/MM/DD', moment.ISO_8601]);
if (!current.isValid()) return '';
let current = parseDateSmart(startDate);
if (!current || !current.isValid()) return '';
let count = 0;
let lastMatchingDate = null;
@@ -85,11 +104,28 @@ export function calculateClassEndDate(startDate, days = [], numberOfSessions = 0
/**
* Calculates the halfway due date for a class in Jalali (jYYYY/jMM/jDD).
* If class has 10 sessions, due date is calculated until the 5th session.
* If class has 10 sessions, due date is calculated as the date of the 5th session.
* @param {Object} cls - Class object
* @param {Array} [sessions] - Optional array of session objects
* @returns {string} Jalali formatted date string, or today's date if not calculable
*/
export function calculateClassMidDate(cls) {
export function calculateClassMidDate(cls, sessions = []) {
if (Array.isArray(sessions) && sessions.length > 0) {
const validSessions = sessions
.filter((s) => s && (s.day || s.date))
.map((s) => ({
...s,
momentDate: parseDateSmart(s.day || s.date)
}))
.filter((s) => s.momentDate && s.momentDate.isValid());
if (validSessions.length > 0) {
validSessions.sort((a, b) => a.momentDate.valueOf() - b.momentDate.valueOf());
const midIndex = Math.ceil(validSessions.length / 2) - 1;
return validSessions[midIndex].momentDate.locale('fa').format('jYYYY/jMM/jDD');
}
}
if (!cls) return moment().locale('fa').format('jYYYY/jMM/jDD');
const totalSessions = parseInt(cls.numberOfSessions || cls.course?.sectionCount || 0, 10);
@@ -101,17 +137,17 @@ export function calculateClassMidDate(cls) {
}
if (cls.startDate && cls.endDate) {
const startM = moment(cls.startDate);
const endM = moment(cls.endDate);
if (startM.isValid() && endM.isValid()) {
const startM = parseDateSmart(cls.startDate);
const endM = parseDateSmart(cls.endDate);
if (startM && startM.isValid() && endM && endM.isValid()) {
const midTime = startM.valueOf() + (endM.valueOf() - startM.valueOf()) / 2;
return moment(midTime).locale('fa').format('jYYYY/jMM/jDD');
}
}
if (cls.startDate) {
const startM = moment(cls.startDate);
if (startM.isValid()) return startM.locale('fa').format('jYYYY/jMM/jDD');
const startM = parseDateSmart(cls.startDate);
if (startM && startM.isValid()) return startM.locale('fa').format('jYYYY/jMM/jDD');
}
return moment().locale('fa').format('jYYYY/jMM/jDD');