«Удалить строку» на строке программы (GROUP) или проекта (ITEM) ничего не
делала: у этих строк нет line_id, окно подтверждения не открывалось, а
события program_deleted / project_deleted, которые сервер уже поддерживает,
фронт не отправлял.
- getDeleteTarget: по выделенной строке выбирает программу, проект или
строку бюджета; для непустых программы/проекта сразу показывает причину.
- RealtimeContext: deleteProgram / deleteProject и обработка ответов.
- useRealtimeData: строка убирается из дерева, пересчёт ROOT/GROUP из
ответа сопоставляется по section_code / program_id / project_id
(alignNodeRows), а не по sort_order — сервер нумерует строки заново, и у
листа без строк бюджета нет ROOT (найдено при проверке на стенде).
- Окно подтверждения называет удаляемую программу/проект.
- getWsErrorMessage срезает префикс "<class 'asyncpg...'>: " — иначе отказ
сервера (программа с проектами у устаревшего клиента) показывался с ним.
Refs #303
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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>
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>