149 lines
4.5 KiB
JavaScript
149 lines
4.5 KiB
JavaScript
'use strict';
|
|
|
|
const PERSIAN_WEEKDAYS = ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'];
|
|
const IRAN_WEEK_ORDER = [6, 0, 1, 2, 3, 4, 5];
|
|
|
|
const toDate = (value) => {
|
|
if (!value) return null;
|
|
const date = value instanceof Date ? value : new Date(value);
|
|
return Number.isNaN(date.getTime()) ? null : date;
|
|
};
|
|
|
|
const formatClockPart = (time) => {
|
|
const raw = String(time || '').trim();
|
|
if (!raw) return '';
|
|
const [hoursRaw, minutesRaw] = raw.split(':');
|
|
const hours = String(parseInt(hoursRaw, 10));
|
|
if (hours === 'NaN') return '';
|
|
if (!minutesRaw || minutesRaw === '00') return hours;
|
|
return `${hours}:${minutesRaw}`;
|
|
};
|
|
|
|
const formatCourseTime = (startTime, endTime) => {
|
|
const start = formatClockPart(startTime);
|
|
const end = formatClockPart(endTime);
|
|
if (!start) return '';
|
|
if (!end) return start;
|
|
return `از ساعت ${start} تا ${end}`;
|
|
};
|
|
|
|
const weekdayIndex = (value) => {
|
|
const date = toDate(value);
|
|
return date ? date.getDay() : null;
|
|
};
|
|
|
|
const joinFaList = (items) => {
|
|
if (!items.length) return '';
|
|
if (items.length === 1) return items[0];
|
|
if (items.length === 2) return `${items[0]} و ${items[1]}`;
|
|
return `${items.slice(0, -1).join('، ')} و ${items[items.length - 1]}`;
|
|
};
|
|
|
|
const normalizeWeekdays = (days) => {
|
|
if (!Array.isArray(days)) return [];
|
|
const unique = [];
|
|
const seen = new Set();
|
|
for (const value of days) {
|
|
const index = Number(value);
|
|
if (!Number.isInteger(index) || index < 0 || index > 6 || seen.has(index)) continue;
|
|
seen.add(index);
|
|
unique.push(index);
|
|
}
|
|
unique.sort((a, b) => IRAN_WEEK_ORDER.indexOf(a) - IRAN_WEEK_ORDER.indexOf(b));
|
|
return unique;
|
|
};
|
|
|
|
const normalizeClockTime = (value) => {
|
|
if (value == null) return '';
|
|
const raw = String(value).trim();
|
|
if (!raw) return '';
|
|
const match = raw.match(/^(\d{1,2})(?::(\d{1,2}))?$/);
|
|
if (!match) return '';
|
|
const hours = Number(match[1]);
|
|
const minutes = Number(match[2] || 0);
|
|
if (hours > 23 || minutes > 59) return '';
|
|
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
};
|
|
|
|
const formatClassDaysFromIndexes = (days = []) => {
|
|
const indexes = normalizeWeekdays(days);
|
|
return joinFaList(indexes.map((index) => PERSIAN_WEEKDAYS[index]).filter(Boolean));
|
|
};
|
|
|
|
const formatClassDays = (sessions = []) => {
|
|
const indexes = [];
|
|
const seen = new Set();
|
|
for (const session of sessions) {
|
|
const dayIndex = weekdayIndex(session?.day);
|
|
if (dayIndex == null || seen.has(dayIndex)) continue;
|
|
seen.add(dayIndex);
|
|
indexes.push(dayIndex);
|
|
}
|
|
return formatClassDaysFromIndexes(indexes);
|
|
};
|
|
|
|
const formatClassStartDate = (value) => {
|
|
const date = toDate(value);
|
|
if (!date) return '';
|
|
try {
|
|
return date.toLocaleDateString('fa-IR');
|
|
} catch {
|
|
return date.toISOString().slice(0, 10);
|
|
}
|
|
};
|
|
|
|
const buildClassScheduleContext = (classDoc, sessions = [], currentSession = null) => {
|
|
const sortedSessions = [...sessions].sort((a, b) => {
|
|
const left = toDate(a?.day)?.getTime() || 0;
|
|
const right = toDate(b?.day)?.getTime() || 0;
|
|
return left - right;
|
|
});
|
|
const sessionForTime = currentSession || sortedSessions[0] || null;
|
|
const storedDays = formatClassDaysFromIndexes(classDoc?.days);
|
|
const storedTime = formatCourseTime(classDoc?.startTime, classDoc?.endTime);
|
|
|
|
return {
|
|
classStartDate: formatClassStartDate(classDoc?.startDate || sortedSessions[0]?.day),
|
|
classDays: storedDays || formatClassDays(sortedSessions),
|
|
courseTime: storedTime || formatCourseTime(sessionForTime?.startTime, sessionForTime?.endTime)
|
|
};
|
|
};
|
|
|
|
const calculateClassEndDate = (startDate, days = [], numberOfSessions = 0) => {
|
|
const date = toDate(startDate);
|
|
const countNeeded = parseInt(numberOfSessions, 10);
|
|
if (!date || isNaN(countNeeded) || countNeeded < 1) return null;
|
|
const validDays = normalizeWeekdays(days);
|
|
if (!validDays.length) return null;
|
|
|
|
let current = new Date(date);
|
|
let count = 0;
|
|
let lastMatchingDate = null;
|
|
let safetyLimit = 365 * 5;
|
|
|
|
while (count < countNeeded && safetyLimit > 0) {
|
|
safetyLimit--;
|
|
if (validDays.includes(current.getDay())) {
|
|
count++;
|
|
lastMatchingDate = new Date(current);
|
|
}
|
|
if (count >= countNeeded) break;
|
|
current.setDate(current.getDate() + 1);
|
|
}
|
|
|
|
return lastMatchingDate;
|
|
};
|
|
|
|
module.exports = {
|
|
IRAN_WEEK_ORDER,
|
|
PERSIAN_WEEKDAYS,
|
|
formatCourseTime,
|
|
formatClassDays,
|
|
formatClassDaysFromIndexes,
|
|
formatClassStartDate,
|
|
normalizeWeekdays,
|
|
normalizeClockTime,
|
|
buildClassScheduleContext,
|
|
calculateClassEndDate
|
|
};
|