diff --git a/docs/FRONTEND.md b/docs/FRONTEND.md index a64d6b1..37b5a7e 100644 --- a/docs/FRONTEND.md +++ b/docs/FRONTEND.md @@ -463,6 +463,17 @@ if (!session || !AUTHORIZED_ROLES.includes(session.role)) { ### Темизация +Общий `ui-foundation.css` подключается последним во всех пяти оболочках. +Текст, заголовки и элементы управления используют прежний стек `Inter`, +`-apple-system`, `BlinkMacSystemFont`, `Segoe UI`, `sans-serif` без внешнего CDN. +Если Inter не установлен, браузер выбирает системный шрифт без засечек. +Базовые стили содержат запасной стек на случай недоступности общего файла. + +Список свободных аудиторий на дашборде имеет высоту 180 px и внутреннюю +вертикальную прокрутку, доступную с клавиатуры. В запросах преподавателей +статус, комментарий и кнопки размещаются под содержимым заявки; кнопки +переносятся целиком при нехватке ширины, статусы не растягиваются по высоте. + CSS-переменные позволяют поддерживать светлую/тёмную тему: ```css diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index 49c8645..35683b1 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -107,7 +107,9 @@ COPY proxy.conf /usr/local/apache2/conf/extra/magistr-proxy.conf Все базовые образы backend/frontend и локальный PostgreSQL зафиксированы одновременно точным tag и manifest digest. Первый этап frontend собирает зафиксированный OpenTelemetry bundle; второй раздаёт только runtime- -файлы, без `node_modules`, тестов и build-исходников. Apache подключает `mod_headers`, +файлы, без `node_modules`, тестов и build-исходников. В корень образа также копируются +`ui-foundation.css`, `schedule-overview.css` и `schedule-overview.js`: они необходимы +для общего шрифта, компонентов и обзора занятий в личных кабинетах. Apache подключает `mod_headers`, `mod_proxy` и `mod_proxy_http`; proxy сохраняет исходный `Host`, чтобы `localhost` корректно маршрутизировался в tenant `default`. Перед копированием runtime-файлов устанавливаются доступные исправления Alpine через `apk upgrade --no-cache`. Сервер diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 9e3d81c..bdb0cbf 100755 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -18,6 +18,7 @@ COPY edu-office/ /usr/local/apache2/htdocs/edu-office/ COPY student/ /usr/local/apache2/htdocs/student/ COPY teacher/ /usr/local/apache2/htdocs/teacher/ COPY index.html script.js style.css theme-toggle.js auth-session.js telemetry.js /usr/local/apache2/htdocs/ +COPY ui-foundation.css schedule-overview.css schedule-overview.js /usr/local/apache2/htdocs/ COPY --from=frontend-assets /build/dist/vendor/ /usr/local/apache2/htdocs/vendor/ COPY security.conf /usr/local/apache2/conf/extra/magistr-security.conf COPY proxy.conf /usr/local/apache2/conf/extra/magistr-proxy.conf diff --git a/frontend/admin/css/components.css b/frontend/admin/css/components.css index 89c81ba..0a28a66 100755 --- a/frontend/admin/css/components.css +++ b/frontend/admin/css/components.css @@ -219,6 +219,13 @@ align-content: flex-start; gap: 0.5rem; margin-top: 0.25rem; + height: 180px; + min-height: 180px; + max-height: 180px; + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-gutter: stable; + padding-right: 0.25rem; } .dashboard-actions { diff --git a/frontend/admin/css/main.css b/frontend/admin/css/main.css index 79b5c1c..ca4aebd 100755 --- a/frontend/admin/css/main.css +++ b/frontend/admin/css/main.css @@ -67,7 +67,7 @@ html { } body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + font-family: var(--ui-font-sans, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif); background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; diff --git a/frontend/admin/css/schedule-quality.css b/frontend/admin/css/schedule-quality.css index 8821c68..6a01e2d 100644 --- a/frontend/admin/css/schedule-quality.css +++ b/frontend/admin/css/schedule-quality.css @@ -66,7 +66,7 @@ .quality-hero h2 { max-width: 790px; margin: 0; - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: clamp(2rem, 4.5vw, 4.4rem); font-weight: 500; letter-spacing: -0.045em; @@ -229,7 +229,7 @@ .quality-score-inner strong { color: var(--text-primary); - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: 3.2rem; font-weight: 500; line-height: 0.95; @@ -271,7 +271,7 @@ .quality-score-copy h3 { margin: 0.55rem 0 0; color: var(--text-primary); - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: clamp(1.6rem, 3vw, 2.5rem); font-weight: 500; } @@ -338,7 +338,7 @@ .quality-metric-index { color: color-mix(in srgb, var(--text-secondary) 65%, transparent); - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: 0.78rem; } @@ -346,7 +346,7 @@ display: block; margin-top: 1.25rem; color: var(--text-primary); - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: 2rem; font-weight: 500; } @@ -402,7 +402,7 @@ .quality-recommendations h3 { margin: 0; color: var(--text-primary); - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: 1.65rem; font-weight: 500; } @@ -537,7 +537,7 @@ .quality-penalty { color: var(--quality-red); - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: 1.15rem; } @@ -583,7 +583,7 @@ background: color-mix(in srgb, var(--quality-cyan) 10%, transparent); border: 1px solid color-mix(in srgb, var(--quality-cyan) 35%, transparent); border-radius: 50%; - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: 1.6rem; } diff --git a/frontend/admin/css/schedule-versions.css b/frontend/admin/css/schedule-versions.css index 6e6bdfb..ee96dd1 100644 --- a/frontend/admin/css/schedule-versions.css +++ b/frontend/admin/css/schedule-versions.css @@ -30,7 +30,7 @@ right: -1.5rem; bottom: 1.2rem; color: rgba(255, 255, 255, .055); - font-family: Georgia, serif; + font-family: var(--ui-font-display); font-size: clamp(4rem, 10vw, 9rem); letter-spacing: -.08em; pointer-events: none; @@ -53,7 +53,7 @@ .version-hero h2 { max-width: 760px; margin: 0; - font-family: Georgia, 'Times New Roman', serif; + font-family: var(--ui-font-display); font-size: clamp(2rem, 4.5vw, 4.5rem); font-weight: 500; letter-spacing: -.05em; @@ -136,7 +136,7 @@ background: linear-gradient(105deg, color-mix(in srgb, var(--version-copper) 10%, var(--bg-card)), var(--bg-card)); border-color: color-mix(in srgb, var(--version-copper) 35%, var(--bg-card-border)); } -.version-constructor-bar h2 { margin: 0; color: var(--text-primary); font-family: Georgia, serif; font-size: 1.55rem; } +.version-constructor-bar h2 { margin: 0; color: var(--text-primary); font-family: var(--ui-font-display); font-size: 1.55rem; } .version-constructor-bar p { margin: .35rem 0 0; color: var(--text-secondary); font-size: .82rem; } .version-constructor-controls { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .65rem; align-items: end; } .version-constructor-field { min-width: 0; } @@ -219,7 +219,7 @@ margin-bottom: 1rem; } -.version-section-head > span { color: var(--version-copper); font-family: Georgia, serif; font-size: 2rem; } +.version-section-head > span { color: var(--version-copper); font-family: var(--ui-font-display); font-size: 2rem; } .version-draft-list { display: grid; gap: .8rem; } .version-card { @@ -237,7 +237,7 @@ right: .5rem; top: -.8rem; color: color-mix(in srgb, var(--version-copper) 12%, transparent); - font-family: Georgia, serif; + font-family: var(--ui-font-display); font-size: 5rem; } @@ -252,7 +252,7 @@ place-content: center; text-align: center; } -.version-empty > span { color: var(--version-copper); font-family: Georgia, serif; font-size: 4rem; } +.version-empty > span { color: var(--version-copper); font-family: var(--ui-font-display); font-size: 4rem; } .version-empty p { max-width: 360px; } .version-diff-score { @@ -262,7 +262,7 @@ margin: 1rem 0; } .version-diff-score div { padding: .75rem; background: var(--bg-input); border-radius: var(--radius-sm); text-align: center; } -.version-diff-score strong { display: block; color: var(--text-primary); font-family: Georgia, serif; font-size: 1.75rem; } +.version-diff-score strong { display: block; color: var(--text-primary); font-family: var(--ui-font-display); font-size: 1.75rem; } .version-diff-score span { color: var(--text-secondary); font-size: .68rem; text-transform: uppercase; } .version-change-list { display: grid; gap: .55rem; max-height: 390px; overflow: auto; } @@ -292,7 +292,7 @@ } .version-modal[hidden] { display: none; } .version-modal-card { position: relative; width: min(560px, 100%); padding: 1.6rem; background: var(--bg-primary); border: 1px solid var(--bg-card-border); border-radius: var(--radius-lg, 20px); box-shadow: 0 30px 80px rgba(0, 0, 0, .45); } -.version-modal-card h3 { margin: 0; color: var(--text-primary); font-family: Georgia, serif; font-size: 2rem; } +.version-modal-card h3 { margin: 0; color: var(--text-primary); font-family: var(--ui-font-display); font-size: 2rem; } .version-modal-card > p { margin: .6rem 0 1rem; color: var(--text-secondary); line-height: 1.55; } .version-modal-card form { display: grid; gap: .65rem; } .version-modal-card label { color: var(--text-secondary); font-size: .75rem; font-weight: 700; } diff --git a/frontend/admin/css/teacher-absences.css b/frontend/admin/css/teacher-absences.css index 0a9fe6e..9fc1a48 100644 --- a/frontend/admin/css/teacher-absences.css +++ b/frontend/admin/css/teacher-absences.css @@ -187,6 +187,7 @@ } .absence-registry-title { + flex-wrap: wrap; justify-content: space-between; gap: 1rem; } @@ -220,21 +221,31 @@ display: inline-flex; align-items: center; min-height: 28px; - padding: 0 0.65rem; + padding: 0.25rem 0.65rem; + flex: 0 0 auto; + align-self: flex-start; + max-width: 100%; + line-height: 1.4; border: 1px solid currentColor; border-radius: 999px; font-size: 0.69rem; font-weight: 800; letter-spacing: 0.03em; - white-space: nowrap; + white-space: normal; } .absence-status.is-pending { color: #f59e0b; } -.absence-status.is-approved { color: #fb7185; } +.absence-status.is-approved { color: #34d399; } .absence-status.is-resolved { color: #34d399; } .absence-status.is-rejected, .absence-status.is-cancelled { color: var(--text-secondary); } +[data-theme="light"] .absence-status.is-pending { color: #92400e; } +[data-theme="light"] .absence-status.is-approved, +[data-theme="light"] .absence-status.is-resolved { color: #047857; } +.absence-status.is-rejected { color: #fb7185; } +[data-theme="light"] .absence-status.is-rejected { color: #be123c; } + .absence-registry-buttons { justify-content: flex-end; flex-wrap: wrap; @@ -481,7 +492,7 @@ body.absence-wizard-open { .teacher-dialogue-grid { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; } @@ -604,7 +615,7 @@ body.absence-wizard-open { .teacher-dialogue-item { display: grid; - grid-template-columns: 7px minmax(0, 1fr) minmax(180px, .42fr); + grid-template-columns: 7px minmax(0, 1fr); gap: .85rem; padding: .85rem; border: 1px solid var(--bg-card-border); @@ -613,6 +624,7 @@ body.absence-wizard-open { } .teacher-dialogue-mark { + grid-row: 1 / 3; border-radius: 99px; background: #94a3b8; } @@ -631,6 +643,11 @@ body.absence-wizard-open { font-size: .68rem; } +.teacher-dialogue-copy { + min-width: 0; + overflow-wrap: anywhere; +} + .teacher-dialogue-copy h3 { margin: .22rem 0; font-size: .92rem; @@ -668,17 +685,32 @@ body.absence-wizard-open { .teacher-dialogue-copy ol { display: grid; gap: .3rem; padding-left: 1.15rem; } .teacher-dialogue-action { + grid-column: 2; + min-width: 0; display: flex; flex-direction: column; - align-items: flex-end; + align-items: stretch; gap: .55rem; } .teacher-dialogue-action > div { display: flex; - justify-content: flex-end; + justify-content: flex-start; flex-wrap: wrap; - gap: .35rem; + gap: .5rem; +} + +.teacher-dialogue-action > div > .btn { + flex: 1 1 120px; + min-width: 0; + white-space: normal; + line-height: 1.35; + padding-block: 0.4rem; +} + +.teacher-dialogue-toolbar > .btn { + align-self: center; + min-height: 44px; } .teacher-dialogue-comment { @@ -801,6 +833,6 @@ body.absence-wizard-open { .teacher-dialogue-action { grid-column: 2; - align-items: flex-start; + align-items: stretch; } } diff --git a/frontend/admin/index.html b/frontend/admin/index.html index a278051..5335343 100755 --- a/frontend/admin/index.html +++ b/frontend/admin/index.html @@ -15,6 +15,7 @@ + @@ -78,7 +79,7 @@ - + @@ -149,7 +150,7 @@ - + - + ${escapeHtml(subjectRowLabel(row))} diff --git a/frontend/admin/js/views/classrooms.js b/frontend/admin/js/views/classrooms.js index 4eb6a50..2200a4e 100755 --- a/frontend/admin/js/views/classrooms.js +++ b/frontend/admin/js/views/classrooms.js @@ -94,11 +94,13 @@ export async function initClassrooms() { - - - ${c.status === 'ARCHIVED' - ? `` - : ``} + +
+ + ${c.status === 'ARCHIVED' + ? `` + : ``} +
`; }).join(''); diff --git a/frontend/admin/js/views/groups.js b/frontend/admin/js/views/groups.js index f8505e7..34b7d3f 100644 --- a/frontend/admin/js/views/groups.js +++ b/frontend/admin/js/views/groups.js @@ -333,7 +333,7 @@ export async function initGroups() { ${group.status === 'ARCHIVED' - ? `` + ? `` : ``} diff --git a/frontend/admin/js/views/schedule-view.js b/frontend/admin/js/views/schedule-view.js index caf4789..7001c91 100644 --- a/frontend/admin/js/views/schedule-view.js +++ b/frontend/admin/js/views/schedule-view.js @@ -865,7 +865,7 @@ export async function initScheduleView() { const editButton = canEditScheduleOverrides(role) && lesson.scheduleRuleSlotId && sourceDate ? ` ` + ? `` : ``} diff --git a/frontend/admin/settings/css/main.css b/frontend/admin/settings/css/main.css index 97607dc..5469728 100644 --- a/frontend/admin/settings/css/main.css +++ b/frontend/admin/settings/css/main.css @@ -61,7 +61,7 @@ html { } body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + font-family: var(--ui-font-sans, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif); background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; diff --git a/frontend/admin/settings/index.html b/frontend/admin/settings/index.html index 3ffd53b..8b4e762 100644 --- a/frontend/admin/settings/index.html +++ b/frontend/admin/settings/index.html @@ -8,6 +8,7 @@ + diff --git a/frontend/admin/settings/views/time-slots.html b/frontend/admin/settings/views/time-slots.html index 20402bd..2dfb9d0 100644 --- a/frontend/admin/settings/views/time-slots.html +++ b/frontend/admin/settings/views/time-slots.html @@ -108,7 +108,7 @@

Новая ручная сетка

Создайте отдельную сетку времени, которую затем можно назначать датам в календарном графике.

- +
Дата diff --git a/frontend/admin/views/classrooms.html b/frontend/admin/views/classrooms.html index c93ed2c..073608d 100755 --- a/frontend/admin/views/classrooms.html +++ b/frontend/admin/views/classrooms.html @@ -70,7 +70,7 @@