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

@@ -6,9 +6,9 @@ import { mkdtemp, readFile, writeFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createHash } from "node:crypto";
import { parseWeekRanges, parseTextDates } from "../shared/weeks.js";
import { parseWeekRanges, parseTextDates, DATE_LIST } from "../shared/weeks.js";
const run = promisify(execFile);
export const PARSER_VERSION = 10;
export const PARSER_VERSION = 11;
const days = [
"понедельник",
"вторник",
@@ -86,6 +86,8 @@ export function details(raw) {
.trim()
.replace(/[–—,\-]+$/, "")
.trim();
// A typo'd date before the subject ("19,11, 03.12 Физика") leaves a stray comma behind.
subject = subject.replace(/^[\s,;.]+/, "");
if (!subject) subject = rest;
const defence = /^защита/i.test(leading?.[1] || "");
const token =
@@ -408,7 +410,8 @@ export function parseDocx(buffer, source = {}) {
cell.merge === "restart" &&
rows[i + 1]?.find((c) => c.col === cell.col)?.merge === "continue";
for (const alternative of alternatives(raw)) {
const data = details(alternative);
// "Единый день информирования 10.09;15.10;19.11;17.12": the dates are not part of the subject.
const data = details(alternative.replace(DATE_LIST, "").replace(/[ \t]{2,}/g, " ").trim());
if (credits && data.type === "Занятие") data.type = "Зачёт";
const dateCandidate = alternative.match(
/^(\d{2})[.](\d{2})(?:[.](\d{4}))?\s/,