feat(admin-ui): D-pack — history, breaking-change detection, x-unique, x-id-source, datetime

History:
- Drawer на детальной странице записи (timeline всех версий через GET /records/{key}/history)
- Иконка ClockCounterClockwise в actions колонке таблицы записей
- Каждая версия показывает v(version), validFrom/To, updatedBy + expand JSON

Schema diff (breaking detection):
- Util diffSchemas() сравнивает старую/новую схему и классифицирует изменения:
  breaking (removed/typeChanged/becameRequired/enumRemoved/maxLengthTightened/
  patternChanged/newRequiredField), compatible (added optional/enumAdded/
  becameOptional), metadata (description)
- В DictionaryEditorDialog при breaking → Alert с детальным списком + Save заблокирован
- При compatible → Alert info со списком
- suggestVersionBump: major для breaking, minor для compatible field changes,
  patch для metadata-only

x-unique:
- Чекбокс «Уникальное» на PropertyEditor → emit x-unique: true в schema
- Backend enforcement позже — отдельный PR с partial unique index

x-id-source:
- SingleSelect в Метаданных «Использовать как ID» → emit x-id-source на schema root
- В SchemaDrivenForm если установлен — businessKey input скрывается, на submit
  derived из data[idSource]
- Подсказка с именем поля

DateTime для validFrom/validTo:
- Заменил DatePicker (date-only) на DateTimeField компонент: DatePicker + TextInput
  type=time рядом
- Default time: validFrom=00:00, validTo=23:59 (запись действует до конца дня
  иначе зануляется в полночь)
- Поддерживает 2-часовые интервалы (запуск КА в 14:30 → сход в 02:15 etc.)

Bug fix:
- parseSchemaJson изменил сигнатуру с PropertyDef[] на {properties, idSource},
  старый код в DictionaryEditorDialog падал "{} is not iterable". Поправлено
  через destructuring const parsed = parseSchemaJson(...).

i18n: 25+ новых ключей (RU/EN) — schema.unique, schema.idSource, schema.diff.*,
history.*, form.idSourceNote.
This commit is contained in:
Zimin A.N.
2026-05-04 03:38:21 +03:00
parent 0289cf5ff2
commit baf76ac6e0
5 changed files with 221 additions and 35 deletions
+36
View File
@@ -32,6 +32,7 @@ i18n
'dict.confirmClose.reason': 'Причина (опционально)',
'dict.confirmClose.reasonPlaceholder': 'например, «дубликат» или «выведено из эксплуатации»',
'form.identity': 'Идентификация',
'form.idSourceNote': 'Бизнес-ключ создастся автоматически из поля «{{field}}»',
'form.tabs.identity': 'Идентификация',
'form.tabs.description': 'Описание',
'form.tabs.extra': 'Дополнительно',
@@ -69,6 +70,23 @@ i18n
'schema.enumValues': 'Значения enum (через запятую)',
'schema.enumValuesHint': 'Например: ACTIVE, INACTIVE, RETIRED',
'schema.arrayEnumOptional': 'Допустимые значения (опц., через запятую)',
'schema.unique': 'Уникальное',
'schema.uniqueHint': 'Дубликаты по этому полю запрещены',
'schema.idSource': 'Использовать как ID (бизнес-ключ)',
'schema.idSourceHint': 'Бизнес-ключ записи будет автоматически = значению этого поля',
'schema.idSourceManual': 'Ввод вручную',
'schema.versionSuggest': 'Рекомендуемая версия: {{v}}',
'schema.diff.blockedTitle': 'Несовместимое изменение схемы',
'schema.diff.blockedBody': 'Эти изменения сломают существующие записи. Сохранение заблокировано. Создайте новый справочник или откатите изменения.',
'schema.diff.compatibleTitle': 'Изменения схемы (совместимые)',
'history.title': 'История версий',
'history.empty': 'История пуста',
'history.current': 'текущая',
'history.closed': 'закрыта',
'history.validFrom': 'действует с',
'history.validTo': 'действует до',
'history.updatedBy': 'кем',
'history.viewData': 'данные записи (JSON)',
'schema.empty': 'Полей нет — добавь первое',
'schema.addProperty': 'Добавить поле',
'schema.delete': 'Удалить',
@@ -128,6 +146,7 @@ i18n
'dict.confirmClose.reason': 'Reason (optional)',
'dict.confirmClose.reasonPlaceholder': 'e.g., "duplicate" or "decommissioned"',
'form.identity': 'Identity',
'form.idSourceNote': 'Business key auto-derived from "{{field}}" field',
'form.tabs.identity': 'Identity',
'form.tabs.description': 'Description',
'form.tabs.extra': 'Additional',
@@ -165,6 +184,23 @@ i18n
'schema.enumValues': 'Enum values (comma-separated)',
'schema.enumValuesHint': 'E.g.: ACTIVE, INACTIVE, RETIRED',
'schema.arrayEnumOptional': 'Allowed values (optional, comma-separated)',
'schema.unique': 'Unique',
'schema.uniqueHint': 'Duplicates by this field are not allowed',
'schema.idSource': 'Use as ID (business key)',
'schema.idSourceHint': 'Record business key auto-derived from this field value',
'schema.idSourceManual': 'Manual input',
'schema.versionSuggest': 'Suggested version: {{v}}',
'schema.diff.blockedTitle': 'Breaking schema change',
'schema.diff.blockedBody': 'These changes break existing records. Save blocked. Create a new dictionary or revert.',
'schema.diff.compatibleTitle': 'Schema changes (compatible)',
'history.title': 'Version history',
'history.empty': 'No history',
'history.current': 'current',
'history.closed': 'closed',
'history.validFrom': 'valid from',
'history.validTo': 'valid to',
'history.updatedBy': 'by',
'history.viewData': 'record data (JSON)',
'schema.empty': 'No fields yet — add the first one',
'schema.addProperty': 'Add field',
'schema.delete': 'Delete',