feat: Enhance session handling in timetable and UI

- Introduced session types (exams, credits, consultations, coursework defenses) with distinct icons and colors for better visibility in the UI.
- Updated PlansScreen, ScheduleScreen, and WeekGrid to display session information appropriately.
- Modified CalendarView and PlansView to include session indicators in the calendar and plans.
- Enhanced LessonCard and CompactLesson to show session details and adjust styling based on session presence.
- Added new SessionMark component for consistent session representation across Android and iOS.
- Updated server logic to handle session entries from external documents, ensuring accurate timetable representation.
- Improved date parsing to accommodate session-related dates in various formats.
- Added tests to verify session handling and date parsing functionality.
This commit is contained in:
2026-10-19 20:49:13 +03:00
parent db9a3f8243
commit 15851b26cd
21 changed files with 421 additions and 42 deletions

View File

@@ -350,7 +350,32 @@ export async function directoryCalendar() {
}
export { todayIso };
export const EXAM_TYPES = ["Экзамен", "Зачёт", "Консультация"];
export const EXAM_TYPES = ["Экзамен", "Зачёт", "Консультация", "Защита курсовой работы"];
/** The index as it is now, without waiting for a first build (which this starts). */
export function readyIndex() {
getIndex().catch(() => {});
return index;
}
/**
* Dated session events of a group (exams, credits, consultations, coursework defences) from
* every catalog document except `exceptDocId`, as index entries, so a timetable can show the
* group's credits even when the university publishes them in a separate "Расписание зачетов".
*/
export function sessionEntriesOf(idx, group, faculty, exceptDocId) {
const seen = new Map();
for (const entry of idx?.entries || []) {
const { doc, lesson } = entry;
if (doc.id === exceptDocId || !EXAM_TYPES.includes(lesson.type) || !lesson.groups.includes(group)) continue;
if (!lesson.date && !lesson.dates?.length) continue;
if (faculty && doc.schedule.source.faculty && doc.schedule.source.faculty !== faculty) continue;
const subgroup = lesson.subgroups?.find((s) => s.group === group)?.label || "";
const key = [lesson.date || lesson.dates.join(","), lesson.start, normalize(lesson.subject), lesson.type, subgroup].join("|");
if (!seen.has(key)) seen.set(key, entry);
}
return [...seen.values()];
}
/** Dated exams, credits and consultations of a group across every document. */
export function examsOf(idx, group, faculty) {
const seen = new Map();