- processColumns (tableColumns.jsx): enableEditing для каждой листовой колонки
через isCellEditable (PR #281), а не только для vsp_dropdown — MRT больше не
открывает редактор (даже disabled) на нередактируемой ячейке, сервер не берёт
блокировку, коллеги не видят 🔒.
- Одиночный клик по редактируемой, не заблокированной ячейке открывает
редактор (muiTableBodyCellProps.onClick, RealtimeTable.jsx); клик по
нередактируемой/занятой ячейке, как раньше, выделяет строку. Удалён мёртвый
onClick/handleClick из Cell.jsx.
- Клавиатура на выделенной ячейке: Enter/F2 — открыть редактор; Delete/Backspace
— открыть с пустым значением; печатный символ — открыть и подставить его
вместо текущего значения (хранится в state RealtimeTable, читается через
table.options.meta.getEditorInitialInput).
- EditCell.jsx/VspDropdownEditCell.jsx: Esc — отмена без cell_updated; Tab/
Shift+Tab и Enter — сохранить и перейти к следующей/предыдущей редактируемой
ячейке через table.options.meta.openNextEditableCell → utils/cellNavigation.js
(чистые findNextEditableColumnId/findNextEditableRowId по
getOrderedVisibleColumns и table.getRowModel().flatRows).
- utils/formulaUtils.js: isSameCellValue — handleSave не шлёт cell_updated,
если значение не изменилось (число/строка, «1 500,0», пусто/null); формула
всегда считается изменённой.
- Фокус: setTimeout(50) → requestAnimationFrame; после закрытия редактора
фокус возвращается в <td> ячейки.
Тесты: utils/cellNavigation.test.js, utils/formulaUtils.test.js (новые,
15 тестов). node --test src/components/RealtimeTable/utils/*.test.js:
85 тестов, 84 passed, 1 известный pre-existing fail (columnLayoutUtils.test.js,
нет node_modules в этом worktree, см. PR #281/#283).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 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 зелёных.