Andrey Epifancev df6ec795a3 feat(table): визуально отличать редактируемые ячейки от расчётных и закрытых (AURORA-1890)
- 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>
2026-11-01 13:00:40 +04:00

26 lines
1.0 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { formatDeadline } from './deadlineFormat.js';
test('ISO closes_at is formatted as "до DD.MM HH:mm" in local time', () => {
const closesAt = new Date(2026, 9, 31, 18, 0, 0); // 31 октября, 18:00, локальное время
assert.equal(formatDeadline(closesAt.toISOString()), 'до 31.10 18:00');
});
test('single-digit day/month/hour/minute are zero-padded', () => {
const closesAt = new Date(2026, 0, 5, 9, 3, 0); // 5 января, 09:03
assert.equal(formatDeadline(closesAt.toISOString()), 'до 05.01 09:03');
});
test('a Date instance is accepted directly, not only an ISO string', () => {
const closesAt = new Date(2026, 11, 1, 0, 0, 0);
assert.equal(formatDeadline(closesAt), 'до 01.12 00:00');
});
test('falsy or invalid input returns null instead of throwing', () => {
assert.equal(formatDeadline(null), null);
assert.equal(formatDeadline(undefined), null);
assert.equal(formatDeadline(''), null);
assert.equal(formatDeadline('not-a-date'), null);
});