- validationUtils.collectInvalidErrors теперь собирает ошибки по ЯЧЕЙКАМ
(id/ruleId/columnId/lineId/rowLabel/message), а не одну запись на колонку;
новая groupValidationErrors(errors) группирует их обратно по правилу+колонке
со счётчиком для меню плашки.
- useValidationRules: getCellErrors(columnId, value, row) отдаёт причины
конкретной ячейки (для подсказки), isCellInvalid выражен через него;
сравнение стабильности errors — по id (он уже кодирует правило+колонку+строку).
- RealtimeTable: handleNavigateToError({lineId, columnId}) — находит строку по
getRowId в table.getRowModel().flatRows (не .rows, т.к. flatRows не фильтруется
по свёрнутым группам), раскрывает родителей через setExpanded, включает
скрытую колонку через handleSetColumnsVisibility, выделяет ячейку как
activeSearchMatch и скроллит к ней — по образцу navigateToSearchMatch.
- ValidationErrorsAlert: красная плашка (#FDEDED/#D32F2F) со счётчиком
«Ошибок: N», кнопками ‹› для прохода по всем ошибкам и меню, сгруппированным
по правилу+колонке (до 20 строк, дальше «и ещё N»); перенесена в начало
SettingPanel, перед группами настроек.
- tableColumns/Cell: invalidMessage (из getCellErrors) доходит до Cell и
показывается подсказкой (MUI Tooltip) при наведении на невалидную ячейку.
Тесты (node --test .../validationUtils.test.js): 12/12, включая 5 существующих
тестов nonNegativeRule из AURORA-1892 (не менялись, подтверждают отсутствие
регрессии) и 7 новых для collectInvalidErrors/groupValidationErrors.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- utils/cellEditability.js: isCellEditable/isComputedColumn — чистые функции,
isComputedColumn повторяет часть v3._apply_form_cell/_apply_form3_cell
(0023_apply_cell_empty_string.sql), чтобы админ не видел вычисляемые
колонки как редактируемые. tableColumns.jsx использует isCellEditable
вместо инлайновой проверки.
- useRealtimeData.js: возвращает stageColumnDeadlines (closes_at по колонке
текущего этапа) рядом с columnsCurStage.
- Cell.jsx: редактируемая ячейка — белый фон, рамка 1px #7FA7C9 (2px при
наведении), курсор text; нередактируемая ячейка строки INPUT — фон
#F4F5F7, текст #5F6B7A; занятая коллегой — пунктирная оранжевая рамка
2px #F0A030 без изменения фона. Строки ITEM/GROUP/ROOT не затронуты.
Сохранён порядок стилей из AURORA-1894 (INVALID_STYLES последним) и
спред fontWeight для отрицательных чисел из AURORA-1892.
- TableHead.jsx: метка срока ввода «✎ до DD.MM HH:mm» в заголовке колонки
текущего этапа, только для исполнителя (utils/deadlineFormat.js).
- SettingPanel/CellLegend.jsx: иконка "?" с легендой (можно вводить /
расчёт или закрыто) рядом с плашкой контроля ошибок, без новой панели
и без новых цветов в palettes columnColors.js.
node --test: cellEditability.test.js, deadlineFormat.test.js — 18/18.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cell.jsx: formatNumber вынесен в utils/numberFormat.js и возвращает
{ text, isNegative }; отрицательные числа выделяются цветом (#C62828 на
светлом фоне, #FFCDD2 на тёмном) и font-weight 600, ноль остаётся «-».
constants/validation.js: фабрика nonNegativeRule и общий список
nonNegativeColumnKeys (план, бронь на 2026/2027, факт по месяцам и СПОД,
сумма платежа, предельная стоимость договора/ЦКК, сателлиты АХР
Аренда/Коммунальные/Безопасность, АХР Лимиты целиком). Корректировки и
переносы (adj_*, base_correction, revision_*, seq_*, target_change,
transfer_*) не включены — там минус — нормальная операция. Запрет
мягкий: подсветка и запись в «Контроль ошибок» через существующий
useValidationRules/isCellInvalid, сохранение не блокируется.
EditCell.jsx: handleSave предупреждает toast.warning при сохранении
отрицательного числа в защищённой колонке, значение всё равно
сохраняется. useValidationRules и RealtimeTable прокидывают
человекочитаемый заголовок колонки для текста предупреждения.
Тесты (node --test): numberFormat.test.js, validationUtils.test.js —
новые, 10/10 зелёных.
Cell.jsx: порядок спреда стилей — base → editable/not-editable → locked →
invalid (INVALID_STYLES последним), чтобы красная рамка 2px не перетиралась
EDITABLE_STYLES у вводимых ячеек с ошибкой.
utils/wsErrorUtils.js: getWsErrorMessage(error) — строка возвращается как
есть, объект берёт message || detail, известные технические префиксы
(computed_field:, role_not_allowed:, window_closed:, org_not_assigned)
заменяются понятным текстом, прочее отдаётся без "Server error:".
RealtimeContext.js: toast.error(getWsErrorMessage(data.error)) вместо
`Server error:${data.error.message}` || '', которое всегда давало undefined
для ошибок-строк (|| не срабатывает на непустой строке).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Копируются только строки на пути к затронутой, остальные сохраняют идентичность;
без изменений возвращается тот же data. Отсутствующие в row.data ключи теперь
вставляются (нужно для AURORA-1844/1848). Тесты с паритетом к прежней реализации.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Высота строки и ячейки задана явно в rem, виртуализатор не читает offsetHeight.
Мёртвая muiTableRowProps удалена. Overscan строк свода 2: 8 -> 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Модалки в DictVspModals (forwardRef), таблица в memo со стабильными колбэками,
enableRowVirtualization, size/minSize/maxSize колонок выверены под grid-раскладку.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>