feat(categories): editable migration target with type-ahead (#246) #252

Closed
maximus wants to merge 0 commits from issue-246-migration-cible-editable into main
Owner

Make the v1 target editable on EVERY row of the categories-migration preview (not only the "needs review" ones), reusing CategoryCombobox (accent-insensitive type-ahead, keyboard, hierarchical). A pure adapter flattens the full v1 taxonomy once in StepSimulate and passes it as a prop; the full tree is fed so mid-tree default targets like "Divertissement" (id 1710) display and can be re-picked. Reducer/guard behavior unchanged.

Resolves #246

Generated autonomously by /autopilot run of 2026-07-04

Make the v1 target editable on EVERY row of the categories-migration preview (not only the "needs review" ones), reusing CategoryCombobox (accent-insensitive type-ahead, keyboard, hierarchical). A pure adapter flattens the full v1 taxonomy once in StepSimulate and passes it as a prop; the full tree is fed so mid-tree default targets like "Divertissement" (id 1710) display and can be re-picked. Reducer/guard behavior unchanged. Resolves #246 Generated autonomously by /autopilot run of 2026-07-04
maximus added 1 commit 2026-07-04 22:17:25 +00:00
feat(categories): editable migration target with type-ahead (#246)
All checks were successful
PR Check / rust (pull_request) Successful in 22m32s
PR Check / frontend (pull_request) Successful in 2m28s
e45736bbde
Make the v1 target editable on EVERY row of the categories-migration
preview (StepSimulate), not only the "needs review" ones. Each row now
renders a reused CategoryCombobox (accent-insensitive type-ahead,
keyboard, hierarchical) in place of the read-only text / flat <select>.

A pure adapter (taxonomyToComboboxCategories) flattens the full v1
taxonomy into Category-shaped rows once in StepSimulate and passes it as
a prop to the rows. The full tree (not just leaves) is fed so mid-tree
default targets such as "Divertissement" (id 1710, the default for
"Jeux, Films & Livres") both display and can be re-picked. The target
cell stops click/keydown propagation so picker interaction never toggles
the row preview. Reducer/guard behavior unchanged: resolving a "none"
row still bumps confidence to medium, and the unresolved-count guard
stays functional.

Resolves #246
maximus added the
status:approved
autopilot:pending-human
labels 2026-07-04 22:17:33 +00:00
maximus added 1 commit 2026-07-05 00:40:32 +00:00
refactor(categories): migration target picker lists leaves only
All checks were successful
PR Check / rust (pull_request) Successful in 22m29s
PR Check / frontend (pull_request) Successful in 2m30s
ce2793fc2c
The v1 target combobox in the migration preview now offers only leaf
categories (the inputable end-categories), not intermediate parents, so a
transaction is never mapped to a grouping bucket. Leaves render as a flat,
un-indented list in taxonomy order.

A low-confidence default can still point a v2 category at a non-leaf parent
(e.g. 'Divertissement' #1710); MappingRow re-injects that current target for
its single row via findTaxonomyCategory so the input stays populated, while
new picks remain leaves-only. Adds a findTaxonomyCategory pure helper + tests
(adapter now returns 112 leaves, not the 150-node full tree).
Author
Owner

Revue adversariale — PR #252 (Resolves #246)

Verdict : APPROVE

Résumé

Change propre et bien cadré : la cible v1 de l'étape « aperçu » de la migration des catégories devient éditable sur chaque ligne via le CategoryCombobox partagé, alimenté par un adaptateur pur bien testé. Le reducer et la garde de continuation sont réellement inchangés, l'i18n et le CHANGELOG sont à jour, aucune régression détectée.

Vérifications

  • Correctness — Le writer (categoryMigrationService.buildMappingFromRows) n'utilise que row.v1TargetId, jamais splits. Re-piquer une ligne « split » (ex. v2 #26 → défaut non-feuille 1710 « Divertissement ») prend donc bien effet, et le splits résiduel laissé par RESOLVE_ROW est inerte (TransactionPreviewPanel ne le lit pas). Pas de bug de préséance splits/cible.
  • Cas limite non-feuille — Confirmé : DEFAULT_MAPPINGS[26].primaryV1Id = 1710, et 1710 a 5 enfants dans categoryTaxonomyV1.json. La ré-injection de la cible non-feuille courante pour éviter de vider l'input du combobox est justifiée.
  • Reducer inchangéRESOLVE_ROW : confidence: r.confidence === "none" ? "medium" : r.confidence. Résoudre une ligne « none » → medium ; éditer une ligne déjà résolue conserve sa confiance. Commentaires exacts.
  • AdaptateurtaxonomyToComboboxCategories produit un Category complet et type-safe (parent_id: undefined → liste plate, sort_order courant → ordre DFS préservé par sortHierarchical). Tests exacts : 150 nœuds / 112 feuilles / 38 parents vérifié contre le JSON bundlé.
  • Perf — adaptateur calculé une fois dans StepSimulate (pas par ligne), resolveTarget/targetCategories stables (deps [taxonomy], hook mémoïsé []), comboboxCategories mémoïsé par ligne. OK.
  • ÉvénementsstopPropagation sur onClick/onKeyDown de la cellule cible empêche le combobox de déclencher le select/preview de la ligne, sans casser la navigation clavier interne du combobox (bulle après le handler de l'input). OK.
  • i18n / CHANGELOG — clé editTargetAria ajoutée EN + FR ; entrées Changed/Modifié sous [Unreleased]/[Non publié] avec réf #246. Conforme.
  • Tests / régression — les helpers purs (migrationTargets.test.ts) et le reducer (useCategoryMigration.test.ts) couvrent la logique ; aucun test composant existant ne référence l'ancien <select>, donc pas de régression. PR ciblant maincheck.yml s'exécute (pas de pile).

Suggestions (non bloquantes)

  1. Commentaire légèrement imprécis — Le parent non-feuille ré-injecté (ex. 1710) est rendu comme option sélectionnable du combobox (CategoryCombobox ne filtre pas sur is_inputable), contrairement au commentaire « never offered as a new pick » dans MappingRow.tsx. Le pick est un no-op garanti inoffensif (garde targetCategories.find(...) dans handleTargetChange), et 1710 n'apparaît que sur la ligne qui pointe déjà dessus → le re-sélectionner = conserver la valeur courante. Bénin, mais la formulation du commentaire pourrait être resserrée.
  2. Couverture composant — La partie la plus subtile (ré-injection comboboxCategories + handleTargetChange dans MappingRow) n'a pas de test RTL. Les helpers purs et le reducer sont couverts ; un test composant fermerait le trou mais n'est pas requis (aucun MappingRow.test préexistant dans le repo).

Rien ne bloque le merge.

## Revue adversariale — PR #252 (Resolves #246) **Verdict : APPROVE** ✅ ### Résumé Change propre et bien cadré : la cible v1 de l'étape « aperçu » de la migration des catégories devient éditable sur **chaque** ligne via le `CategoryCombobox` partagé, alimenté par un adaptateur pur bien testé. Le reducer et la garde de continuation sont réellement inchangés, l'i18n et le CHANGELOG sont à jour, aucune régression détectée. ### Vérifications - **Correctness** — Le writer (`categoryMigrationService.buildMappingFromRows`) n'utilise **que** `row.v1TargetId`, jamais `splits`. Re-piquer une ligne « split » (ex. v2 #26 → défaut non-feuille 1710 « Divertissement ») prend donc bien effet, et le `splits` résiduel laissé par `RESOLVE_ROW` est inerte (`TransactionPreviewPanel` ne le lit pas). Pas de bug de préséance splits/cible. - **Cas limite non-feuille** — Confirmé : `DEFAULT_MAPPINGS[26].primaryV1Id = 1710`, et 1710 a 5 enfants dans `categoryTaxonomyV1.json`. La ré-injection de la cible non-feuille courante pour éviter de vider l'input du combobox est justifiée. - **Reducer inchangé** — `RESOLVE_ROW` : `confidence: r.confidence === "none" ? "medium" : r.confidence`. Résoudre une ligne « none » → medium ; éditer une ligne déjà résolue conserve sa confiance. Commentaires exacts. - **Adaptateur** — `taxonomyToComboboxCategories` produit un `Category` complet et type-safe (`parent_id: undefined` → liste plate, `sort_order` courant → ordre DFS préservé par `sortHierarchical`). Tests exacts : 150 nœuds / **112 feuilles** / 38 parents vérifié contre le JSON bundlé. - **Perf** — adaptateur calculé une fois dans `StepSimulate` (pas par ligne), `resolveTarget`/`targetCategories` stables (deps `[taxonomy]`, hook mémoïsé `[]`), `comboboxCategories` mémoïsé par ligne. OK. - **Événements** — `stopPropagation` sur `onClick`/`onKeyDown` de la cellule cible empêche le combobox de déclencher le select/preview de la ligne, sans casser la navigation clavier interne du combobox (bulle après le handler de l'input). OK. - **i18n / CHANGELOG** — clé `editTargetAria` ajoutée EN + FR ; entrées Changed/Modifié sous [Unreleased]/[Non publié] avec réf #246. Conforme. - **Tests / régression** — les helpers purs (`migrationTargets.test.ts`) et le reducer (`useCategoryMigration.test.ts`) couvrent la logique ; aucun test composant existant ne référence l'ancien `<select>`, donc pas de régression. PR ciblant `main` → `check.yml` s'exécute (pas de pile). ### Suggestions (non bloquantes) 1. **Commentaire légèrement imprécis** — Le parent non-feuille ré-injecté (ex. 1710) *est* rendu comme option sélectionnable du combobox (`CategoryCombobox` ne filtre pas sur `is_inputable`), contrairement au commentaire « never offered as a new pick » dans `MappingRow.tsx`. Le pick est un no-op garanti inoffensif (garde `targetCategories.find(...)` dans `handleTargetChange`), et 1710 n'apparaît que sur la ligne qui pointe déjà dessus → le re-sélectionner = conserver la valeur courante. Bénin, mais la formulation du commentaire pourrait être resserrée. 2. **Couverture composant** — La partie la plus subtile (ré-injection `comboboxCategories` + `handleTargetChange` dans `MappingRow`) n'a pas de test RTL. Les helpers purs et le reducer sont couverts ; un test composant fermerait le trou mais n'est pas requis (aucun `MappingRow.test` préexistant dans le repo). Rien ne bloque le merge.
maximus added 1 commit 2026-07-05 20:53:29 +00:00
polish(categories): honest doc + extract/test picker-options glue (#246 review)
All checks were successful
PR Check / rust (pull_request) Successful in 22m33s
PR Check / frontend (pull_request) Successful in 2m26s
4f87ce329c
Address the two non-blocking review notes on PR #252:
- The re-injected non-leaf parent (e.g. #1710) is technically clickable, not
  'never offered'; correct the MappingRow comment to state selecting it is a
  no-op (only leaves resolve).
- Extract the per-row option-building glue into a pure, unit-tested helper
  comboboxCategoriesForTarget (4 cases: null/leaf pass-through by reference,
  non-leaf append, unresolvable stale id).
Author
Owner

Merged into main locally as part of the milestone stack (merge commit 9124c88). Closing manually — the merge was done outside Forgejo's API, so it can't flag this PR as merged. The change is on main.

Merged into `main` locally as part of the milestone stack (merge commit `9124c88`). Closing manually — the merge was done outside Forgejo's API, so it can't flag this PR as *merged*. The change is on `main`.
maximus closed this pull request 2026-07-05 21:06:21 +00:00
All checks were successful
PR Check / rust (pull_request) Successful in 22m33s
PR Check / frontend (pull_request) Successful in 2m26s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#252
No description provided.