feat(records): bulk close — multi-select toolbar + per-record транзакция
Backend POST /api/v1/dictionaries/{name}/records/bulk-close принимает
businessKeys[] (1..500) + reason. BulkRecordService крутит цикл через
inject'ed DictionaryRecordService — каждый close в своей транзакции
(SERIALIZABLE), partial success возможен. Result: closed[] / skipped[]
(already_closed, not_found) / errors[].
Frontend: checkbox column + sticky toolbar когда selection.size > 0.
Header checkbox с indeterminate state для partial select. selectAll
включает все ВИДИМЫЕ ключи (не игнорирует фильтры). При изменении
фильтра — useEffect выкидывает невидимые ключи из selection (WYSIWYG).
Bulk close dialog: count + reason input → confirm → результат панель
"Закрыто N, пропущено M, ошибок K" с детальным списком skipped/errors.
После успеха selection остаётся для skipped/errors — юзер видит что
ещё не сделано.
Лимит 500: защита от случайного огромного селекта или бага UI.
Дедупликация внутри batch'а silent skip — не error.
This commit is contained in:
@@ -145,6 +145,22 @@ i18n
|
||||
'Запись «{{key}}» будет помечена как закрытая (valid_to = now). Действие обратимо через создание новой версии.',
|
||||
'dict.confirmClose.reason': 'Причина (опционально)',
|
||||
'dict.confirmClose.reasonPlaceholder': 'например, «дубликат» или «выведено из эксплуатации»',
|
||||
'dict.bulk.selectAll': 'Выбрать все на странице',
|
||||
'dict.bulk.selectRow': 'Выбрать запись',
|
||||
'dict.bulk.selectedCount_one': 'Выбрано {{count}} из {{total}}',
|
||||
'dict.bulk.selectedCount_few': 'Выбрано {{count}} из {{total}}',
|
||||
'dict.bulk.selectedCount_many': 'Выбрано {{count}} из {{total}}',
|
||||
'dict.bulk.selectedCount_other': 'Выбрано {{count}} из {{total}}',
|
||||
'dict.bulk.clearSelection': 'Снять выделение',
|
||||
'dict.bulk.closeAction': 'Закрыть выбранные',
|
||||
'dict.bulk.confirmTitle': 'Массовое закрытие записей',
|
||||
'dict.bulk.confirmBody':
|
||||
'{{count}} запис(ей) будут помечены как закрытые (valid_to = now). Действие обратимо через создание новой версии. Каждая запись закрывается отдельно — если часть уже закрыта, остальные обработаются.',
|
||||
'dict.bulk.confirmAction': 'Закрыть {{count}}',
|
||||
'dict.bulk.resultClosed': 'Закрыто: {{count}}',
|
||||
'dict.bulk.resultSkipped': 'Пропущено (уже закрыто или не найдено): {{count}}',
|
||||
'dict.bulk.resultErrors': 'Ошибок: {{count}}',
|
||||
'dict.bulk.dismiss': 'Готово',
|
||||
'form.identity': 'Идентификация',
|
||||
'form.idSourceNote': 'Бизнес-ключ создастся автоматически из поля «{{field}}»',
|
||||
'form.tabs.identity': 'Идентификация',
|
||||
@@ -410,6 +426,20 @@ i18n
|
||||
'Record "{{key}}" will be marked as closed (valid_to = now). Reversible by creating a new version.',
|
||||
'dict.confirmClose.reason': 'Reason (optional)',
|
||||
'dict.confirmClose.reasonPlaceholder': 'e.g., "duplicate" or "decommissioned"',
|
||||
'dict.bulk.selectAll': 'Select all on page',
|
||||
'dict.bulk.selectRow': 'Select record',
|
||||
'dict.bulk.selectedCount_one': '{{count}} of {{total}} selected',
|
||||
'dict.bulk.selectedCount_other': '{{count}} of {{total}} selected',
|
||||
'dict.bulk.clearSelection': 'Clear selection',
|
||||
'dict.bulk.closeAction': 'Close selected',
|
||||
'dict.bulk.confirmTitle': 'Bulk close records',
|
||||
'dict.bulk.confirmBody':
|
||||
'{{count}} record(s) will be marked as closed (valid_to = now). Reversible by creating a new version. Each record is closed individually — if some are already closed, others will still be processed.',
|
||||
'dict.bulk.confirmAction': 'Close {{count}}',
|
||||
'dict.bulk.resultClosed': 'Closed: {{count}}',
|
||||
'dict.bulk.resultSkipped': 'Skipped (already closed or not found): {{count}}',
|
||||
'dict.bulk.resultErrors': 'Errors: {{count}}',
|
||||
'dict.bulk.dismiss': 'Done',
|
||||
'form.identity': 'Identity',
|
||||
'form.idSourceNote': 'Business key auto-derived from "{{field}}" field',
|
||||
'form.tabs.identity': 'Identity',
|
||||
|
||||
Reference in New Issue
Block a user