Перенесён диапазон arm-web 0f2bbc8..4a02512 (23 коммитов, 51 файлов набора синхронизации) скриптом docs/scripts/sync-web.sh. После переноса web/ и arm-web@test идентичны по набору. Коммиты arm-web: - 4fb477d Подпись пустой цели «цель не заполнена», тесты null/null (ревью I-1, M-1, M-2, M-4) - cb36ec8 Нарушения проверок: expected/actual и индекс колонки могут быть null (#45) - b4b8c04 openspec(statements-v2): решения по итогам прогона в браузере (T11.D-1…D-5, #43) - aa3054e docs(arm-web): экран формы v5.19 — README и карта модулей (#43) - 88a4920 fix(arm-web): нераспознанный текст суммы, пустые подстроки без расшифровок, свёрнутый список предупреждений (#43) - a3199d6 fix(arm-web): вёрстка экрана проверки формы — ширины колонок, значки в ячейке, шапка, ссылка кнопкой (#43) - 12fcea7 fix(statement): защитные значения по умолчанию для ядра без v5.19 (M-2, #43) - 4783593 fix(statement): ссылка на строки без кода всегда видна, сортировка по row_index, Alert для ошибки скрытого поля кода, постоянный aria-label (T9.1, T9.2, T9.4, T7.1, T8.1, T3.3, M-3, #43) - d965fdc fix(statement): округление уверенности в подсказках шапки, пустые тексты в подсказках, комментарий шапки (T5.1, T5.2, M-1, T3.1, #43) - 6556e3d fix(statement): не отправлять поля шапки, равные текущим (I-2, #43) - 8d2deb9 docs(openspec): решения без обсуждения из исполнения и имя кнопки «Подтвердить данные» (#43) - fc5cc16 fix(files): клики внутри popover не открывают строку, ключи и aria-label (#43) - fdf9358 feat(files): предупреждения файла структурами и предупреждения документа в панели файлов и у ОСВ (#43) - 5b11f2d feat(statement): блокировка подтверждения при строках без кода, LINES_UNRESOLVED и DUPLICATE_FORM (#43) - a779d2a feat(statement): подтверждение ячеек, дата колонки, наименование организации и подтверждение даты (#43) - d416422 feat(statement): правка строк — код с подсказкой из справочника, меню строки, LINE_CODE_TAKEN (#43) - ef4d325 refactor(statement): один черновик правок и сборка единственного PATCH (#43) - 8ab116f feat(statement): шапка — организация, поля на проверку и подсказки распознанного текста (#43) - 4ae8643 feat(statement): предупреждения и нарушения у строк, ячеек, колонок и в списке документа (#43) - 34d523a feat(statement): вид строк по статусам, подсветка по needs_review, переключатель расшифровок (#43) - c2f33e7 feat(statement): раскладка строк без кода и расшифровок на своих местах (#43) - e4ee7f0 fix(fixtures): коды соотношения, column_index, порядок edition_lines (#43) - f83b035 feat(types): API формы v5.19 — статусы строк, needs_review, issues, предупреждения файла (#43) Co-Authored-By: sync-web.sh <noreply@aepif.ru>
47 lines
2.4 KiB
TypeScript
47 lines
2.4 KiB
TypeScript
import { ApiError } from '../../api/client';
|
||
import type { EditionLineOut, LineErrorDetail } from '../../types/api';
|
||
import type { GridRow } from './statementGrid';
|
||
|
||
export type CodeOption = { code: string; name: string };
|
||
|
||
const LIST_LIMIT = 20;
|
||
|
||
/**
|
||
* Подсказки кода (P6-1): сначала справочник редакции по порядку, затем коды строк `ok` файла вне справочника.
|
||
* Без повторов; строки без кода (unreadable / missing / detail) не попадают.
|
||
*/
|
||
export function codeOptions(editionLines: EditionLineOut[], rows: GridRow[]): CodeOption[] {
|
||
const result: CodeOption[] = [];
|
||
const seen = new Set<string>();
|
||
for (const line of editionLines) {
|
||
if (seen.has(line.code)) continue;
|
||
seen.add(line.code);
|
||
result.push({ code: line.code, name: line.name });
|
||
}
|
||
for (const row of rows) {
|
||
if (row.lineStatus !== 'ok' || !row.code || seen.has(row.code)) continue;
|
||
seen.add(row.code);
|
||
result.push({ code: row.code, name: row.referenceName ?? row.name ?? '' });
|
||
}
|
||
return result;
|
||
}
|
||
|
||
const fold = (text: string) => text.toLowerCase().replace(/ё/g, 'е');
|
||
|
||
/** Отбор подсказок: код начинается с запроса или имя содержит его (регистр и ё/е не важны). */
|
||
export function filterCodeOptions(options: CodeOption[], query: string, limit = LIST_LIMIT): CodeOption[] {
|
||
const q = fold(query.trim());
|
||
if (q === '') return options.slice(0, limit);
|
||
return options.filter((o) => o.code.startsWith(q) || fold(o.name).includes(q)).slice(0, limit);
|
||
}
|
||
|
||
const LINE_ERROR_CODES = new Set(['LINE_CODE_TAKEN', 'INVALID_LINE_CODE', 'INVALID_LINE_PATCH', 'UNKNOWN_LINE', 'LINE_CODE_REQUIRED']);
|
||
|
||
/** P6-9: к какой строке относится ошибка элемента `lines[]` ответа на сохранение; иная ошибка — null. */
|
||
export function lineErrorTarget(error: unknown): { lineId: string; lineCode: string | null } | null {
|
||
if (!(error instanceof ApiError) || !error.code || !LINE_ERROR_CODES.has(error.code)) return null;
|
||
const detail = (error.body as { detail?: Partial<Record<keyof LineErrorDetail, unknown>> } | undefined)?.detail;
|
||
if (typeof detail?.line_id !== 'string') return null;
|
||
return { lineId: detail.line_id, lineCode: typeof detail.line_code === 'string' ? detail.line_code : null };
|
||
}
|