Редактирование заявок у конкретного пользователя (#14)
Tests / frontend-test (push) Successful in 45s
Tests / backend-test (push) Successful in 7m18s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/14
This commit was merged in pull request #14.
This commit is contained in:
2026-08-19 12:58:07 +00:00
parent 82512eb1db
commit 1ee1b71e3e
36 changed files with 1862 additions and 333 deletions
@@ -50,8 +50,14 @@
line-height: 1.6;
}
// wrap обязателен, а не косметика: кнопки .btn — white-space: nowrap, сжаться по
// тексту не могут, а justify-content: flex-end выталкивает не влезающие ВЛЕВО,
// за границу .box — и они просто исчезают из виду (так пропадала кнопка «Нет,
// пропустить» в диалоге на четыре варианта, см. ManageByUserDuplicateModal).
// С переносом лишние кнопки уезжают на вторую строку и остаются доступны.
.footer {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 10px;
padding: 16px 26px 22px;
@@ -0,0 +1,62 @@
@import '../../../../styles/design-tokens';
// Ниже Modal.module.scss/.overlay (z-index 300) — диалоги, которые может
// открыть сама длительная операция (например, разрешение конфликта), должны
// быть ПОВЕРХ этой подложки, а не под ней.
.overlay {
position: fixed;
inset: 0;
z-index: 250;
display: flex;
align-items: center;
justify-content: center;
background: rgba(10, 27, 48, .45);
}
.box {
min-width: 320px;
max-width: 420px;
padding: 24px 26px;
border-radius: $r-lg;
background: #fff;
box-shadow: $sh-modal;
text-align: center;
}
.label {
font-size: 13.5px;
font-weight: 600;
color: $gray-900;
}
.counter {
margin-top: 4px;
font-size: 12px;
color: $gray-500;
}
.track {
margin-top: 14px;
height: 6px;
border-radius: $r-full;
background: $gray-200;
overflow: hidden;
}
.fill {
height: 100%;
border-radius: $r-full;
background: $grad-gold;
transition: width .2s;
}
// Общее число шагов заранее неизвестно — бегущая полоса вместо доли.
.indeterminate {
width: 40%;
animation: slide 1.1s ease-in-out infinite;
}
@keyframes slide {
0% { transform: translateX(-100%); }
100% { transform: translateX(250%); }
}
@@ -0,0 +1,38 @@
import * as React from 'react';
import styles from './ProgressOverlay.module.scss';
export interface IProgressOverlayProps {
label: string;
/** Сколько шагов уже сделано. */
done: number;
/** Всего шагов. 0/undefined — полоса «бежит», доля не показывается. */
total?: number;
}
/**
* Модальная подложка с полосой прогресса на время длительной пакетной операции:
* показывает, что работа идёт и сколько осталось, и заодно физически не даёт
* кликать по интерфейсу под ней (иначе можно запустить вторую замену поверх
* первой или уйти на другую вкладку посреди записи).
*
* Закрыть её нельзя намеренно — операция уже пишет в SharePoint/БД, и «отмена»
* оставила бы часть строк заменённой, а часть нет, без всякого следа об этом.
*/
export const ProgressOverlay: React.FC<IProgressOverlayProps> = ({ label, done, total }) => {
const percent = total && total > 0 ? Math.min(100, Math.round((done / total) * 100)) : undefined;
return (
<div className={styles.overlay} role="alert" aria-busy="true">
<div className={styles.box}>
<div className={styles.label}>{label}</div>
{total ? <div className={styles.counter}>{`${done} / ${total}`}</div> : undefined}
<div className={styles.track}>
<div
className={`${styles.fill} ${percent === undefined ? styles.indeterminate : ''}`}
style={percent === undefined ? undefined : { width: `${percent}%` }}
/>
</div>
</div>
</div>
);
};
@@ -4,6 +4,7 @@ export * from './IconButton';
export * from './Badge';
export * from './Alert';
export * from './Toast';
export * from './ProgressOverlay';
export * from './Panel';
export * from './Breadcrumbs';
export * from './PageHeader';