Files
gameno-api/utils/classSchedule.js
T
kavehhn 4d60755a95 feat(classes): store weekdays and meeting times on the class
Keep the class schedule on the class itself so SMS and listings do not have to infer days and hours from sessions.
2026-08-15 23:59:07 +03:30

123 lines
3.8 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)
};
};
module.exports = {
IRAN_WEEK_ORDER,
PERSIAN_WEEKDAYS,
formatCourseTime,
formatClassDays,
formatClassDaysFromIndexes,
formatClassStartDate,
normalizeWeekdays,
normalizeClockTime,
buildClassScheduleContext
};