feat(#371): редизайн модалки каталога ролей — карточки-наборы + per-role результаты импорта #375

Merged
vvzvlad merged 2 commits from feat/371-roles-catalog into develop 2026-07-05 16:43:20 +03:00

2 Commits

Author SHA1 Message Date
agent_coder b8cce4f814 fix(#371): skipped role is not 'allInstalled', test the reason->action branch (review round 1)
F1 [WARNING] bundlePhase returned 'allInstalled' when a bundle's only
non-installed role was skipped (0 installed for it), so the collapsed green 'All
installed · up to date' header contradicted the open 'Installed 0 · 1 skipped'
plaque. It now returns 'mixed' whenever a skipped role is present. Fixed the test
that encoded the wrong behavior.

F2 [WARNING] The reason->action branch (name-conflict -> transient overlay +
'Rename & install'; already-installed -> informational, no button) lived only in
the component, untested. Extracted the two decisions into pure, unit-tested
helpers nameConflictSlugs() and partialOffersRename() and wired them into the
modal; both reason values are now covered.

F3 [low] Removed the unused useRef import (client eslint no-unused-vars is off, so
it shipped silently).

F4 [low] Extracted bundleCounts() as the single tally pass; bundlePhase and the
panel both derive from it instead of rescanning the roles array ~5x per render
(the same model<->component consolidation this PR is about).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 06:09:58 +03:00
agent_coder a325ddbabd feat(#371): roles catalog modal redesign — bundle cards + per-role import results
Integrates the designer-handoff Roles Catalog modal, wired to the real API; the
parent ai-agent-roles.tsx and the { opened, onClose, roles } contract are
unchanged.

- Server importFromCatalog now returns per-role lists (createdRoles /
  skippedRoles with a reason) alongside the existing counters (compat-preserving),
  so the UI can name the conflicting/installed roles.
- New pure view-model (catalog-bundle-model.ts): bundlePhase (empty | allNew |
  allInstalled | updates | mixed, ignoring the transient 'skipped'),
  installedLangForRole (same-slug-different-language hint), mapCatalogRoleToView —
  all unit-tested without mounting.
- Bundle cards with a summary status in the collapsed header (eager useQueries
  fan-out over all bundles, sharing the existing per-bundle cache keys), a single
  primary action per bundle, checkboxes + select/deselect-all, an inline result
  plaque that keeps the modal open, per-bundle and global 'Update all' request
  series with progress, and the other-language hint.
- The partial-result plaque distinguishes the skip reason: only a name-conflict
  offers 'Rename & install'; an already-installed race is informational (a rename
  re-import would just skip again and self-heal into a false success).
- All strings i18n'd (en/ru); mock handoff code (SEED/mockImport/delay) removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 05:35:58 +03:00