fix: changed SP list order field to StepOrder

This commit is contained in:
Тимур Городецкий
2026-08-04 12:36:40 +05:00
parent c2e785961b
commit ed21944628
17 changed files with 249 additions and 61 deletions
@@ -8,25 +8,40 @@ import { UserPicker } from './UserPicker';
export interface IApprovalMatrixProps {
editor: IUseMatrixEditor;
search: (query: string) => Promise<ISiteUser[]>;
/** Заголовок блока. По умолчанию — «Маршрут согласования». */
title?: string;
/** Подпись роли в шапке таблицы. По умолчанию — «Согласующий». */
roleLabel?: string;
/** Текст, когда участников нет. По умолчанию — «Согласующие не заданы.». */
emptyText?: string;
/** Плейсхолдер пикера добавления (только если editor.editable). */
pickerPlaceholder?: string;
}
/** Таблица маршрута согласования + (если editable) правка порядка/состава. */
export const ApprovalMatrix: React.FC<IApprovalMatrixProps> = ({ editor, search }) => {
/** Таблица маршрута (согласование или исполнение) + (если editable) правка состава. */
export const ApprovalMatrix: React.FC<IApprovalMatrixProps> = ({
editor,
search,
title = 'Маршрут согласования',
roleLabel = 'Согласующий',
emptyText = 'Согласующие не заданы.',
pickerPlaceholder = 'Добавить согласующего: начните вводить ФИО…',
}) => {
const { rows, loading, error, editable } = editor;
return (
<div className={styles.matrixWrap}>
<div className={styles.matrixTitle}>Маршрут согласования</div>
<div className={styles.matrixTitle}>{title}</div>
{loading && <div className={styles.matrixNote}>Загрузка маршрута…</div>}
{!loading && error && <div className={styles.matrixNote}>Маршрут недоступен: {error}</div>}
{!loading && !error && rows.length === 0 && (
<div className={styles.matrixNote}>Согласующие не заданы.</div>
<div className={styles.matrixNote}>{emptyText}</div>
)}
{!loading && !error && rows.length > 0 && (
<table className={styles.matrix}>
<thead>
<tr>
<th>Согласующий</th>
<th>{roleLabel}</th>
<th>Должность</th>
<th>Статус</th>
<th>Дата</th>
@@ -77,11 +92,7 @@ export const ApprovalMatrix: React.FC<IApprovalMatrixProps> = ({ editor, search
)}
{editable && !loading && (
<div className={styles.matrixPicker}>
<UserPicker
search={search}
onPick={editor.addApprover}
placeholder="Добавить согласующего: начните вводить ФИО…"
/>
<UserPicker search={search} onPick={editor.addApprover} placeholder={pickerPlaceholder} />
</div>
)}
</div>
@@ -3,6 +3,7 @@ import { useState, useEffect } from 'react';
import styles from './DecisionPanel.module.scss';
import { Button, Alert, FormField, TextArea, Icon } from '../../ui';
import { ApproverService, ApproverDecision } from '../approverDecision.service';
import { APPROVAL_PHASE } from '../models/statuses';
interface IDecisionPanelProps {
service: ApproverService;
@@ -17,6 +18,7 @@ interface IDecisionPanelProps {
export const DecisionPanel: React.FC<IDecisionPanelProps> = ({ service, requestId, onDecided }) => {
const [checking, setChecking] = useState<boolean>(true);
const [rowId, setRowId] = useState<number | undefined>(undefined);
const [phase, setPhase] = useState<string>(APPROVAL_PHASE.approval);
const [comment, setComment] = useState<string>('');
const [submitting, setSubmitting] = useState<boolean>(false);
const [resultStatus, setResultStatus] = useState<string | undefined>(undefined);
@@ -30,6 +32,9 @@ export const DecisionPanel: React.FC<IDecisionPanelProps> = ({ service, requestI
.then((row) => {
if (alive) {
setRowId(row?.id);
if (row) {
setPhase(row.phase);
}
setChecking(false);
}
})
@@ -67,14 +72,19 @@ export const DecisionPanel: React.FC<IDecisionPanelProps> = ({ service, requestI
}
}
const isExecution = phase === APPROVAL_PHASE.execution;
if (checking) {
return <div className={styles.state}>Проверяем вашу очередь на согласование…</div>;
return <div className={styles.state}>Проверяем вашу очередь…</div>;
}
if (resultStatus) {
return (
<div className={styles.panel}>
<Alert variant="ok">Решение сохранено. Статус заявки: «{resultStatus}».</Alert>
<Alert variant="ok">
{isExecution ? 'Отмечено как выполнено' : 'Решение сохранено'}. Статус заявки: «
{resultStatus}».
</Alert>
</div>
);
}
@@ -82,9 +92,33 @@ export const DecisionPanel: React.FC<IDecisionPanelProps> = ({ service, requestI
if (rowId === undefined) {
return (
<div className={styles.panel}>
<Alert variant="warn">
Сейчас не ваша очередь на согласование, либо заявка уже обработана.
</Alert>
<Alert variant="warn">Сейчас не ваша очередь, либо заявка уже обработана.</Alert>
</div>
);
}
// Фаза исполнения: одно действие «Выполнено» (без обязательного комментария),
// строка помечается как «Согласовано» → computeRequestStatus переведёт заявку в
// «Выполнение»/«Закрыта».
if (isExecution) {
return (
<div className={styles.panel}>
<div className={styles.title}>Исполнение</div>
{error && <Alert variant="err">{error}</Alert>}
<FormField label="Комментарий" htmlFor="decisionComment">
<TextArea
id="decisionComment"
value={comment}
onChange={setComment}
placeholder="Комментарий (необязательно)"
/>
</FormField>
<div className={styles.actions}>
<Button variant="accent" disabled={submitting} onClick={() => decide('approve')}>
<Icon name="check" size="sm" />
Выполнено
</Button>
</div>
</div>
);
}