From 7458020880e8a1f524a0e88d56c42d6ece7932df Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Wed, 29 Jul 2026 16:52:12 +0500 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B2=D0=BE=D1=80=D0=BD=D0=B8=D0=BD=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/screens/ResolveScreen.tsx | 40 ++++++++++++++++++- .../components/ui/DataTable.module.scss | 15 +++++++ .../approvalAdmin/components/ui/DataTable.tsx | 9 +++++ .../webparts/approvalAdmin/models/index.ts | 2 + 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/frontend/src/webparts/approvalAdmin/components/screens/ResolveScreen.tsx b/frontend/src/webparts/approvalAdmin/components/screens/ResolveScreen.tsx index bfbd0d3..4691e1a 100644 --- a/frontend/src/webparts/approvalAdmin/components/screens/ResolveScreen.tsx +++ b/frontend/src/webparts/approvalAdmin/components/screens/ResolveScreen.tsx @@ -15,6 +15,7 @@ import { AsyncSearchableSelect, Breadcrumbs, Button, + CellNote, DataTable, Field, FormGrid, @@ -34,6 +35,14 @@ export interface IResolveScreenProps { const STATUS_OPTIONS = [RouteStepStatus.Approval, RouteStepStatus.Execution] .map(v => ({ value: v, label: routeStepStatusLabels[v] })); +function stepsWord(count: number): string { + const mod100 = count % 100; + const mod10 = count % 10; + if (mod100 >= 11 && mod100 <= 14) return 'шагах'; + if (mod10 === 1) return 'шаге'; + return 'шагах'; +} + export const ResolveScreen: React.FC = ({ api, dictionary, currentUserEmail }) => { const [requestTypes, setRequestTypes] = React.useState([]); const [requestTypeId, setRequestTypeId] = React.useState(''); @@ -60,6 +69,8 @@ export const ResolveScreen: React.FC = ({ api, dictionary, [dictionary] ); + const unresolvedCount = result ? result.steps.filter(s => !s.executorEmail).length : 0; + const run = (): void => { if (!requestTypeId) { setError('Выберите тип заявки.'); @@ -140,11 +151,36 @@ export const ResolveScreen: React.FC = ({ api, dictionary, Маршрут #{result.routeId} · {routeTypeLabels[result.routeType]} {result.requiresControl ? ' · требуется контроль исполнения' : ''} + {unresolvedCount > 0 && ( + + Исполнитель не определён на {unresolvedCount} {stepsWord(unresolvedCount)} — причина указана в строке шага. + + )} r.orderNo }, - { key: 'executor', header: 'Исполнитель', render: r => {r.executorEmail ?? '—'} }, - { key: 'position', header: 'Должность', render: r => r.position ?? '—' }, + { + key: 'executor', + header: 'Исполнитель', + render: r => ( +
+ {r.executorEmail ?? '—'} + {r.warning && {r.warning}} +
+ ) + }, + { + key: 'position', + header: 'Должность', + render: r => ( +
+ {r.position ?? '—'} + {!r.position && !r.warning && r.executorEmail && ( + Должность не указана в карточке сотрудника. + )} +
+ ) + }, { key: 'reassign', header: 'Переназначение', diff --git a/frontend/src/webparts/approvalAdmin/components/ui/DataTable.module.scss b/frontend/src/webparts/approvalAdmin/components/ui/DataTable.module.scss index 3279f4f..755d63f 100644 --- a/frontend/src/webparts/approvalAdmin/components/ui/DataTable.module.scss +++ b/frontend/src/webparts/approvalAdmin/components/ui/DataTable.module.scss @@ -70,6 +70,21 @@ gap: 10px; } +.cellNote { + display: flex; + align-items: flex-start; + gap: 6px; + margin-top: 4px; + max-width: 420px; + font-size: 11.5px; + line-height: 1.4; + color: $gray-500; + + svg { margin-top: 1px; } +} + +.cellNoteWarn { color: $amber-700; } + .actionsCell { display: flex; gap: 4px; diff --git a/frontend/src/webparts/approvalAdmin/components/ui/DataTable.tsx b/frontend/src/webparts/approvalAdmin/components/ui/DataTable.tsx index 5c103f5..4b76fde 100644 --- a/frontend/src/webparts/approvalAdmin/components/ui/DataTable.tsx +++ b/frontend/src/webparts/approvalAdmin/components/ui/DataTable.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import styles from './DataTable.module.scss'; +import { Icon } from './Icon'; export interface IColumn { key: string; @@ -66,3 +67,11 @@ export const PrimaryCell: React.FC<{ children: React.ReactNode }> = ({ children export const ActionsCell: React.FC<{ children: React.ReactNode }> = ({ children }) => ( {children} ); + +// Пояснение под значением ячейки — например, причина, по которой значение пустое. +export const CellNote: React.FC<{ children: React.ReactNode; kind?: 'warn' | 'muted' }> = ({ children, kind = 'muted' }) => ( + + {kind === 'warn' && } + {children} + +); diff --git a/frontend/src/webparts/approvalAdmin/models/index.ts b/frontend/src/webparts/approvalAdmin/models/index.ts index 196f37b..941cde6 100644 --- a/frontend/src/webparts/approvalAdmin/models/index.ts +++ b/frontend/src/webparts/approvalAdmin/models/index.ts @@ -192,6 +192,8 @@ export interface ResolvedRouteStep { executorEmail: string | null; position: string | null; allowReassignToOtherApprovers: boolean; + /** Причина, по которой исполнитель шага не определился (заполняется бэкендом). */ + warning: string | null; } export interface ResolveTemplateRouteResult {