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:
@@ -37,7 +37,7 @@ function lessonOut(lesson, group, people, date, status, calendar) {
|
||||
* days: one entry per date from `from`, with the lessons that take place that day.
|
||||
* weekly: the Word rows by weekday, for when dates cannot be calculated.
|
||||
*/
|
||||
export function timetable({ file, schedule, calendar, group, from, days, range, today }) {
|
||||
export function timetable({ file, schedule, calendar, group, from, days, range, today, session: extra = [] }) {
|
||||
if (!schedule.groups.includes(group)) {
|
||||
throw apiError(404, "group_not_found", `В документе нет группы ${group}.`);
|
||||
}
|
||||
@@ -58,13 +58,19 @@ export function timetable({ file, schedule, calendar, group, from, days, range,
|
||||
: null;
|
||||
// range=semester: every day of the semester `today` falls in, so a client can keep
|
||||
// the whole term offline; a session schedule keeps its own span. Without either it
|
||||
// keeps the plain from/days window.
|
||||
// keeps the plain from/days window. Exams and credits from other documents may follow
|
||||
// the last week of the semester: the span then runs on to the last of them (up to 60 days).
|
||||
if (range === "semester") {
|
||||
const current = semesterView(calendar, schedule, today || from);
|
||||
const span = current?.semester || session;
|
||||
if (span) {
|
||||
from = span.start;
|
||||
const length = Math.round((Date.parse(span.end) - Date.parse(from)) / DAY_MS) + 1;
|
||||
const limit = addDays(span.end, 60);
|
||||
const end = [span.end, ...extra.flatMap((e) => (e.lesson.date ? [e.lesson.date] : e.lesson.dates))]
|
||||
.filter((d) => d >= from && d <= limit)
|
||||
.sort()
|
||||
.at(-1);
|
||||
const length = Math.round((Date.parse(end) - Date.parse(from)) / DAY_MS) + 1;
|
||||
count = Math.min(Math.max(length, 1), MAX_SEMESTER_DAYS);
|
||||
}
|
||||
}
|
||||
@@ -86,6 +92,10 @@ export function timetable({ file, schedule, calendar, group, from, days, range,
|
||||
if (status === "yes" || status === "maybe")
|
||||
dayLessons.push(lessonOut(entry.lesson, group, entry, date, status, calendar));
|
||||
}
|
||||
// Session events printed with their own date need no week table.
|
||||
for (const entry of extra)
|
||||
if (entry.lesson.date === date || entry.lesson.dates?.includes(date))
|
||||
dayLessons.push(lessonOut(entry.lesson, group, entry, date, "yes", calendar));
|
||||
dayLessons.sort((a, b) => a.start.localeCompare(b.start));
|
||||
out.push({
|
||||
date,
|
||||
@@ -117,7 +127,12 @@ export function timetable({ file, schedule, calendar, group, from, days, range,
|
||||
note: view || session
|
||||
? null
|
||||
: "Семестр этого расписания не найден в таблице недель: даты не рассчитаны, смотрите строки Word (weekly).",
|
||||
subgroups: subgroupsOf(schedule, group),
|
||||
subgroups: [
|
||||
...new Set([
|
||||
...subgroupsOf(schedule, group),
|
||||
...extra.map((e) => e.lesson.subgroups?.find((s) => s.group === group)?.label).filter(Boolean),
|
||||
]),
|
||||
].sort((a, b) => a.localeCompare(b, "ru", { numeric: true })),
|
||||
bells: calendar?.bells || [],
|
||||
from,
|
||||
days: out,
|
||||
|
||||
Reference in New Issue
Block a user