This commit is contained in:
Zuev
2026-10-02 04:07:56 +03:00
parent 97bf3cc50a
commit fc5801d683
42 changed files with 1201 additions and 305 deletions

View File

@@ -1,4 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { CustomSelect } from '../../shared/ui/CustomSelect.jsx';
import {
academicYearsFromSemesters,
@@ -1368,7 +1369,7 @@ export function ScheduleTab() {
<div className="version-constructor-controls">
<div className="version-constructor-field">
<label htmlFor="schedule-version-year">Учебный год</label>
<select id="schedule-version-year" value={versionYearId} onChange={handleVersionYearChange}>
<CustomSelect id="schedule-version-year" value={versionYearId} onChange={handleVersionYearChange}>
{initialPhase === 'loading' ? (
<option value="">Загрузка годов…</option>
) : years.length ? (
@@ -1376,11 +1377,11 @@ export function ScheduleTab() {
) : (
<option value="">Учебные годы не настроены</option>
)}
</select>
</CustomSelect>
</div>
<div className="version-constructor-field">
<label htmlFor="schedule-version-semester">Семестр</label>
<select id="schedule-version-semester" value={versionSemesterId} onChange={handleVersionSemesterChange}>
<CustomSelect id="schedule-version-semester" value={versionSemesterId} onChange={handleVersionSemesterChange}>
{initialPhase === 'loading' ? (
<option value="">Сначала выберите год</option>
) : yearSemesters.length ? (
@@ -1390,11 +1391,11 @@ export function ScheduleTab() {
) : (
<option value="">Семестры не настроены</option>
)}
</select>
</CustomSelect>
</div>
<div className="version-constructor-field version-constructor-field-wide">
<label htmlFor="schedule-version-select">Версия расписания</label>
<select id="schedule-version-select" value={versionId} onChange={handleVersionChange}>
<CustomSelect id="schedule-version-select" value={versionId} onChange={handleVersionChange}>
{initialPhase === 'loading' || !versionSemesterId ? (
<option value="">Сначала выберите семестр</option>
) : scheduleVersions.length ? (
@@ -1410,7 +1411,7 @@ export function ScheduleTab() {
) : (
<option value="">Расписания не созданы</option>
)}
</select>
</CustomSelect>
</div>
<div className="version-constructor-actions">
<button type="button" className="btn btn-sm btn-primary" id="schedule-version-create-draft" onClick={openDraftModal} disabled={!selectedVersion}>Создать черновик</button>
@@ -1466,7 +1467,7 @@ export function ScheduleTab() {
<div className="form-row">
<div className="form-group">
<label htmlFor="schedule-rule-subject">Дисциплина</label>
<select id="schedule-rule-subject" required disabled={editorDisabled} value={ruleSubjectId} onChange={handleSubjectChange}>
<CustomSelect id="schedule-rule-subject" required disabled={editorDisabled} value={ruleSubjectId} onChange={handleSubjectChange}>
{!subjects.length ? (
<option value="">Загрузка...</option>
) : (
@@ -1475,11 +1476,11 @@ export function ScheduleTab() {
{subjects.map(subject => (
<option key={subject.id} value={subject.id}>{subject.name}</option>
))}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label htmlFor="schedule-rule-semester">Семестр</label>
<select id="schedule-rule-semester" required disabled value={ruleSemesterId} onChange={() => {}}>
<CustomSelect id="schedule-rule-semester" required disabled value={ruleSemesterId} onChange={() => {}}>
{!semesters.length ? (
<option value="">Загрузка...</option>
) : (
@@ -1488,7 +1489,7 @@ export function ScheduleTab() {
{semesters.map(semester => (
<option key={semester.id} value={semester.id}>{semesterLabel(semester)}</option>
))}
</select>
</CustomSelect>
</div>
</div>
@@ -1723,7 +1724,7 @@ export function ScheduleTab() {
<div className="form-row">
<div className="form-group">
<label htmlFor="schedule-move-slot-day">День</label>
<select
<CustomSelect
id="schedule-move-slot-day"
required
value={moveSlot.dayOfWeek}
@@ -1733,11 +1734,11 @@ export function ScheduleTab() {
>
<option value="">Выберите</option>
{DAY_OPTIONS.map(day => <option key={day.value} value={day.value}>{day.label}</option>)}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label htmlFor="schedule-move-slot-time">Пара</label>
<select
<CustomSelect
id="schedule-move-slot-time"
required
value={moveSlot.timeSlotId}
@@ -1749,7 +1750,7 @@ export function ScheduleTab() {
{timeSlotOptions.map(option => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</select>
</CustomSelect>
</div>
</div>
<FormAlert id="schedule-move-slot-alert" alert={moveSlotAlert} />
@@ -1863,14 +1864,14 @@ export function ScheduleTab() {
<div className="schedule-visual-period-grid">
<div className="form-group">
<label htmlFor="schedule-visual-year">Учебный год</label>
<select id="schedule-visual-year" value={effectiveVisualPeriod.yearId} onChange={handleVisualYearChange}>
<CustomSelect id="schedule-visual-year" value={effectiveVisualPeriod.yearId} onChange={handleVisualYearChange}>
<option value="">Выберите учебный год</option>
{academicYears.map(year => <option key={year.id} value={year.id}>{year.title}</option>)}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label htmlFor="schedule-visual-semester">Семестр</label>
<select id="schedule-visual-semester" value={effectiveVisualPeriod.semesterId} onChange={handleVisualSemesterChange}>
<CustomSelect id="schedule-visual-semester" value={effectiveVisualPeriod.semesterId} onChange={handleVisualSemesterChange}>
{effectiveVisualPeriod.yearSemesters.length ? (
<>
<option value="">Выберите семестр</option>
@@ -1881,7 +1882,7 @@ export function ScheduleTab() {
) : (
<option value="">Нет семестров</option>
)}
</select>
</CustomSelect>
</div>
</div>
<div className="schedule-visual-filter-head">
@@ -1950,24 +1951,24 @@ function RuleSlotRow({
<div className={`schedule-slot-row${laboratory ? ' has-subgroup' : ''}`} data-index={index} data-slot-id={slot.id || ''}>
<div className="form-group">
<label>День</label>
<select className="slot-day" required disabled={disabled} value={slot.dayOfWeek} onChange={event => onChange(index, { dayOfWeek: Number(event.target.value) })}>
<CustomSelect className="slot-day" required disabled={disabled} value={slot.dayOfWeek} onChange={event => onChange(index, { dayOfWeek: Number(event.target.value) })}>
<option value="">Выберите</option>
{DAY_OPTIONS.map(day => <option key={day.value} value={day.value}>{day.label}</option>)}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label>Чётность</label>
<select className="slot-parity" required disabled={disabled} value={slot.parity} onChange={event => onChange(index, { parity: event.target.value })}>
<CustomSelect className="slot-parity" required disabled={disabled} value={slot.parity} onChange={event => onChange(index, { parity: event.target.value })}>
<option value="">Выберите</option>
{PARITY_OPTIONS.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label>Пара</label>
<select className="slot-time" required disabled={disabled} value={slot.timeSlotId} onChange={event => onChange(index, { timeSlotId: Number(event.target.value) })}>
<CustomSelect className="slot-time" required disabled={disabled} value={slot.timeSlotId} onChange={event => onChange(index, { timeSlotId: Number(event.target.value) })}>
<option value="">Выберите</option>
{timeSlotOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label>Преподаватель</label>
@@ -2005,10 +2006,10 @@ function RuleSlotRow({
</div>
<div className="form-group">
<label>Тип</label>
<select className="slot-lesson-type" required disabled={disabled} value={slot.lessonTypeId} onChange={event => onChange(index, { lessonTypeId: Number(event.target.value) })}>
<CustomSelect className="slot-lesson-type" required disabled={disabled} value={slot.lessonTypeId} onChange={event => onChange(index, { lessonTypeId: Number(event.target.value) })}>
<option value="">Выберите</option>
{lessonTypeOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</CustomSelect>
</div>
{laboratory && (selectedGroupIds.length > 1 ? (
<div className="form-group slot-subgroup-multi">
@@ -2068,7 +2069,7 @@ function RuleSlotRow({
) : (
<div className="form-group">
<label>Подгруппа</label>
<select
<CustomSelect
className="slot-subgroup"
disabled={disabled}
value={slot.subgroupIds[0] == null ? WHOLE_GROUP_SUBGROUP_VALUE : String(slot.subgroupIds[0])}
@@ -2088,15 +2089,15 @@ function RuleSlotRow({
))}
</>
)}
</select>
</CustomSelect>
</div>
))}
<div className="form-group">
<label>Формат</label>
<select className="slot-format" required disabled={disabled} value={slot.lessonFormat} onChange={event => onChange(index, { lessonFormat: event.target.value })}>
<CustomSelect className="slot-format" required disabled={disabled} value={slot.lessonFormat} onChange={event => onChange(index, { lessonFormat: event.target.value })}>
<option value="">Выберите</option>
{LESSON_FORMAT_OPTIONS.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</CustomSelect>
</div>
<button
type="button"
@@ -2142,24 +2143,24 @@ function ConflictSlotRow({ slot, index, conflictFields, timeSlotOptions, onSlotC
</div>
<div className="form-group">
<label>День</label>
<select className="conflict-slot-day" required value={slot.dayOfWeek} onChange={event => onSlotChange(index, { dayOfWeek: Number(event.target.value) })}>
<CustomSelect className="conflict-slot-day" required value={slot.dayOfWeek} onChange={event => onSlotChange(index, { dayOfWeek: Number(event.target.value) })}>
<option value="">Выберите</option>
{DAY_OPTIONS.map(day => <option key={day.value} value={day.value}>{day.label}</option>)}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label>Чётность</label>
<select className="conflict-slot-parity" required value={slot.parity} onChange={event => onSlotChange(index, { parity: event.target.value })}>
<CustomSelect className="conflict-slot-parity" required value={slot.parity} onChange={event => onSlotChange(index, { parity: event.target.value })}>
<option value="">Выберите</option>
{PARITY_OPTIONS.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</CustomSelect>
</div>
<div className="form-group">
<label>Пара</label>
<select className="conflict-slot-time" required value={slot.timeSlotId} onChange={event => onSlotChange(index, { timeSlotId: Number(event.target.value) })}>
<CustomSelect className="conflict-slot-time" required value={slot.timeSlotId} onChange={event => onSlotChange(index, { timeSlotId: Number(event.target.value) })}>
<option value="">Выберите</option>
{timeSlotOptions.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
</select>
</CustomSelect>
</div>
<div className={`form-group ${conflictFieldClass('teacher', conflictFields)}`}>
<label>Преподаватель</label>