Обходной лист (#7)
Tests / frontend-test (push) Successful in 1m16s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/7
This commit was merged in pull request #7.
This commit is contained in:
2026-08-17 05:43:38 +00:00
parent a937e33f2e
commit 364835eaf7
47 changed files with 1962 additions and 359 deletions
@@ -55,7 +55,7 @@
min-height: 78vh; // «присутствие» рейла даже когда контента мало
}
// Рейл-колонка: сам SideNav тянется по высоте (flex-стретч).
// Рейл-колонка: сам Sidebar тянется по высоте (flex-стретч).
.side {
flex: 0 0 auto;
display: flex;
@@ -100,5 +100,19 @@
}
}
// Полоса отладочной подмены личности (см. shared/impersonation.ts). Красная и
// во всю ширину намеренно: это не подсказка, а предупреждение о том, что портал
// показывает не ваши данные — незаметная плашка тут хуже, чем никакой.
.impersonationBar {
padding: $sp-3 $sp-4;
margin-bottom: $sp-4;
border: 1px solid $red-500;
border-radius: $r-md;
background: rgba($red-500, .08);
color: $navy-900;
font-size: $fs-sm;
line-height: 1.45;
}
// Стили конкретных фич живут в features/<name>/<Name>.module.scss.
// Здесь — только оболочка (root + раскладка колонок), общая для всех фич.
@@ -20,6 +20,7 @@ import {
OwnTab,
} from '../shared/routing';
import { onRequestsChanged, emitRequestsChanged } from '../shared/requestEvents';
import { getImpersonatedEmail } from '../shared/impersonation';
import { confirmDiscardFormChanges, markFormClean, markFormDirty } from '../shared/unsavedChanges';
import { Drawer } from './Drawer/Drawer';
import { CategoryTabs } from './CategoryTabs/CategoryTabs';
@@ -57,6 +58,11 @@ const RequestPortal: React.FC<IRequestPortalProps> = ({ hasTeamsContext, context
.filter(Boolean)
.join(' ');
// Читаем при рендере, а не через состояние: значение выставляет веб-часть
// (setImpersonatedEmail в render) до создания этого элемента, и меняется оно
// только вместе с новым render всей веб-части.
const impersonatedEmail = getImpersonatedEmail();
const route = useRoute();
const [navCollapsed, setNavCollapsed] = React.useState<boolean>(false);
const [mobileNavOpen, setMobileNavOpen] = React.useState<boolean>(false);
@@ -265,6 +271,15 @@ const RequestPortal: React.FC<IRequestPortalProps> = ({ hasTeamsContext, context
<section className={rootClasses}>
<IconSprite />
<ConfirmHost />
{/* Подмену личности легко забыть включённой — тогда портал молча показывал
бы чужие заявки и чужую очередь. Полосу поэтому не прячем и не даём
закрыть: она исчезает только когда свойство веб-части очищено. */}
{impersonatedEmail && (
<div className={styles.impersonationBar}>
Включена отладочная подмена: портал работает от имени <strong>{impersonatedEmail}</strong>.
Очистите поле в свойствах веб-части, чтобы вернуться к своей учётной записи.
</div>
)}
<div className={styles.layout}>
<div className={styles.side}>
<Sidebar