feat: enhance lesson plan handling with new parser and color-coded room assignments

This commit is contained in:
2026-10-23 00:33:13 +03:00
parent c0487e4c2a
commit 02e6404731
11 changed files with 121 additions and 18 deletions

View File

@@ -58,6 +58,7 @@ import {
diffSchedules,
lessonKey,
personalFilter,
planFor,
subgroupsOf,
} from "../shared/lessons";
import { placesFor, placesOn, type DayPlace } from "../shared/places";
@@ -149,7 +150,33 @@ interface ListContext {
/** Minutes since midnight when the list shows today; drives the live pair chip. */
clock: number | null;
isHidden: (lesson: Lesson) => boolean;
/** The week shown, or only its colour for white or green Word rows; null for all rows. */
week: { n: number | null; color: string } | null;
}
const unique = (list: string[]) => [...new Set(list)];
const weeksLabel = (ranges: [number, number | null][]) =>
"недели " + ranges.map(([a, b]) => (b === null ? `с ${a}` : a === b ? `${a}` : `${a}–${b}`)).join(", ");
/**
* Room and teacher of a lesson in a week, when Word names them by week ("1-9н. 502н",
* "ауд.533н-б.н.,420н-з.н."). For all Word rows at once: the rooms of each week colour.
*/
function inWeek(lesson: Lesson, week: ListContext["week"]) {
const planned = planFor(lesson.plan, week);
const rooms = unique(planned.flatMap((p) => p.rooms)),
teachers = unique(planned.flatMap((p) => p.teachers));
const byColor = !week && !lesson.plan?.some((p) => p.weeks)
? (lesson.plan || []).filter((p) => p.color && p.rooms.length).map((p) => `${p.rooms.join(", ")} (${p.color === "white" ? "бел." : "зел."} н.)`)
: [];
return {
room: rooms.join(", ") || byColor.join(", ") || lesson.room,
teacher: teachers.join(", ") || lesson.teacher,
};
}
/** "533Н — белые недели", "502Н — недели 1–9": the plan for the details. */
const planLine = (p: NonNullable<Lesson["plan"]>[number]) =>
[p.rooms.join(", "), p.teachers.join(", ")].filter(Boolean).join(", ") +
" — " +
[p.weeks && weeksLabel(p.weeks), p.color && (p.color === "white" ? "белые недели" : "зелёные недели")].filter(Boolean).join(", ");
const duration = (m: number) =>
m < 60 ? `${m} мин` : `${Math.floor(m / 60)} ч${m % 60 ? ` ${m % 60} мин` : ""}`;
function LessonCard({
@@ -163,6 +190,7 @@ function LessonCard({
}) {
const theme = lessonTheme(lesson.type);
const subgroup = lesson.subgroups.find((item) => item.group === ctx.group);
const { room, teacher } = inWeek(lesson, ctx.week);
const dates = ctx.calendar && hasWeekLimit(lesson) ? ctx.datesOf(lesson) : undefined;
const hidden = ctx.isHidden(lesson);
return (
@@ -192,16 +220,16 @@ function LessonCard({
</span>
<strong>{lesson.subject}</strong>
<span className="lesson-meta">
{lesson.teacher && (
{teacher && (
<span>
<Icon name="person" size={20} />
{lesson.teacher}
{teacher}
</span>
)}
{(lesson.room || lesson.place) && (
{(room || lesson.place) && (
<span>
<Icon name="location_on" size={20} />
{whereLabel(lesson, "Ауд.")}
{whereLabel({ ...lesson, room }, "Ауд.")}
</span>
)}
{subgroup && (
@@ -1062,6 +1090,11 @@ export default function App() {
today,
clock: showsToday ? clock : null,
isHidden,
week: calendarMode
? { n: week!.n, color: week!.color }
: mode === "upper" || mode === "lower"
? { n: null, color: mode === "upper" ? "white" : "green" }
: null,
});
const scheduleDated =
!!schedule?.lessons.length && schedule.lessons.every((l) => l.date);
@@ -2205,6 +2238,11 @@ export default function App() {
<Icon name="location_on" size={20} />
<small>Аудитория</small>
<b>{details.room || "Не указана"}</b>
{details.plan?.some((p) => p.rooms.length || p.teachers.length) && (
<small className="detail-halves">
{details.plan.filter((p) => p.rooms.length || p.teachers.length).map(planLine).join("; ")}
</small>
)}
</span>
{details.place && (
<span>

View File

@@ -1,5 +1,6 @@
import type { WeekRange } from "../shared/weeks";
import type { PlaceNote } from "../shared/places";
import type { PlanEntry } from "../shared/lessons";
export interface Lesson {
id: string;
day: number;
@@ -12,6 +13,8 @@ export interface Lesson {
room: string;
/** "Полоцк": the town, when the cell or the day's note names one (parser 14). */
place?: string;
/** Rooms and teachers by week number or colour: "1-9н. 502н", "ауд.533н-б.н.,420н-з.н.". */
plan?: PlanEntry[];
type: string;
condition: string;
groups: string[];