feat(record): RecordDrawer slide-over per Stage 3.5 handoff (Phase A)
Заменяет Modal-based record create/edit на right slide-over drawer per
design_handoff_ordinis_mdm/README.md Screen 3.
What's в Phase A (drawer shell + sticky chrome):
- src/components/record/RecordDrawer.tsx (NEW)
- Two widths: 520px default / 880px wide, toggle через ‹/› button.
Persists в localStorage 'ord-drawer-wide'.
- Sticky header: caption (cap style) + recordId mono + width-toggle +
history button (edit mode) + ×.
- Sticky footer: [Удалить] (danger, edit mode) · 'не сохранено · N полей'
caption · [Отмена] · [Сохранить] (drives form submit через form attr).
- Built на shadcn Sheet primitive + Radix Dialog. Override sheetVariants
inline для transition-able max-width (variant size не toggle'ится
smoothly без animate prop).
- aria-labelledby wires DialogTitle к содержимому header.
- Mobile <880px: w-full fullscreen (per handoff responsive table).
- src/components/form/SchemaDrivenForm.tsx
- New optional props: formId, onDirtyChange.
- Когда formId передан — внутренний FormActions footer прячется (drawer
footer driving submit через <button form={formId} type=submit>).
- Dirty-count считается из RHF formState.dirtyFields (top-level + data.*).
- useEffect surfaces count к parent — drawer footer caption updates live.
- src/routes/dictionaries.$name.tsx
- Modal create/edit -> RecordDrawer.
- State: + recordDirtyCount (reset на close).
- onDelete wires к existing close-confirm flow (kind: 'close-confirm').
- onHistory wires к existing setHistoryKey RecordHistoryDrawer.
- src/i18n.ts
- + form.delete, form.unsavedNFields_one/few/many/other (i18next plural rules).
- + drawer.{collapse,expand,close,captionEdit,captionCreate}.
Phase B (TODO Stage 3.5b — separate MR):
- Section anchor chips (sticky strip)
- Wide-mode left ToC rail (180px)
- Sticky toolbar: search + 'только заполненные' toggle + counter
- Requires x-section schema metadata в backend properties (CEO open question #5 в handoff)
Tests: 8 files, 116 tests pass. TS strict clean.
This commit is contained in:
@@ -324,7 +324,17 @@ i18n
|
||||
'form.cancel': 'Отмена',
|
||||
'form.save': 'Сохранить',
|
||||
'form.saving': 'Сохранение...',
|
||||
'form.delete': 'Удалить',
|
||||
'form.error.required': 'Обязательное поле',
|
||||
'form.unsavedNFields_one': 'не сохранено · {{count}} поле',
|
||||
'form.unsavedNFields_few': 'не сохранено · {{count}} поля',
|
||||
'form.unsavedNFields_many': 'не сохранено · {{count}} полей',
|
||||
'form.unsavedNFields_other': 'не сохранено · {{count}} полей',
|
||||
'drawer.collapse': 'Свернуть до 520px',
|
||||
'drawer.expand': 'Развернуть до 880px',
|
||||
'drawer.close': 'Закрыть',
|
||||
'drawer.captionEdit': 'редактирование записи · {{dict}}',
|
||||
'drawer.captionCreate': 'новая запись · {{dict}}',
|
||||
'scope.PUBLIC': 'PUBLIC — публичный',
|
||||
'scope.INTERNAL': 'INTERNAL — внутренний',
|
||||
'scope.RESTRICTED': 'RESTRICTED — ограниченный',
|
||||
@@ -822,6 +832,14 @@ i18n
|
||||
'form.cancel': 'Cancel',
|
||||
'form.save': 'Save',
|
||||
'form.saving': 'Saving...',
|
||||
'form.delete': 'Delete',
|
||||
'form.unsavedNFields_one': 'unsaved · {{count}} field',
|
||||
'form.unsavedNFields_other': 'unsaved · {{count}} fields',
|
||||
'drawer.collapse': 'Collapse to 520px',
|
||||
'drawer.expand': 'Expand to 880px',
|
||||
'drawer.close': 'Close',
|
||||
'drawer.captionEdit': 'edit record · {{dict}}',
|
||||
'drawer.captionCreate': 'new record · {{dict}}',
|
||||
'form.error.required': 'Required field',
|
||||
'scope.PUBLIC': 'PUBLIC',
|
||||
'scope.INTERNAL': 'INTERNAL',
|
||||
|
||||
Reference in New Issue
Block a user