From 60d7f8ca49cbeb08448508b7dc39021de07abe75 Mon Sep 17 00:00:00 2001 From: le king fu Date: Sat, 18 Jul 2026 18:06:23 -0400 Subject: [PATCH] feat(categories): merge custom categories into the standard taxonomy (#259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Custom categories with no standard match were shown as read-only text in the migration wizard and force-parented under a catch-all bucket. They now get the same inline target picker as seeded rows: picking a standard leaf merges the custom category — its transactions, budgets, keywords and suppliers are reassigned to the leaf — and deactivates it. Leaving a custom unmapped keeps the previous behaviour and never blocks the wizard. - Reducer: RESOLVE_ROW resolves rows in both plan.rows and plan.preserved; the Next-button guard still counts seeded rows only. - Writer: the rewrite mapping now includes resolved preserved rows; the catch-all parent is created only when a custom is left unmerged; merged customs are deactivated instead of re-parented (shared isResolvedTarget helper across the three sites). - UI: the preserved block renders MappingRow instead of plain text. - i18n (FR/EN) + CHANGELOG (FR/EN). Tests: reducer (resolve a preserved custom, guard unchanged, GO_NEXT still proceeds) + writer (reassign to the chosen leaf, soft-delete, no empty parent when all merged, orphan-free merge of a custom parent with an unresolved child). 836 vitest green, tsc + vite build clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.fr.md | 4 + CHANGELOG.md | 4 + .../categories-migration/StepSimulate.tsx | 24 ++-- src/hooks/useCategoryMigration.test.ts | 40 +++++++ src/hooks/useCategoryMigration.ts | 22 ++-- src/i18n/locales/en.json | 8 +- src/i18n/locales/fr.json | 8 +- src/services/categoryMigrationService.test.ts | 113 ++++++++++++++++++ src/services/categoryMigrationService.ts | 50 +++++--- 9 files changed, 227 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index efd6279..23b0d31 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -2,6 +2,10 @@ ## [Non publié] +### Ajouté + +- Migration des catégories : les catégories personnalisées sans correspondance standard peuvent désormais être **fusionnées** dans une catégorie standard, au lieu d'être seulement mises de côté. Chaque catégorie personnalisée du bloc « Catégories personnalisées » reçoit le même sélecteur de cible que les lignes du seed — choisissez une feuille standard et ses transactions, budgets, mots-clés et fournisseurs y sont réassignés, puis la catégorie personnalisée est retirée. Laisser une catégorie personnalisée non mappée conserve le comportement précédent (regroupée sous « Catégories personnalisées (migration) ») et ne bloque jamais la migration (#259). + ## [0.14.0] - 2026-07-18 ### Modifié diff --git a/CHANGELOG.md b/CHANGELOG.md index 0329f24..aab49ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Category migration: custom categories that have no standard match can now be **merged** into a standard category instead of only being set aside. Each custom category in the "Custom categories" block gets the same target picker as the seeded rows — choose a standard leaf and its transactions, budgets, keywords and suppliers are reassigned to it, then the custom category is removed. Leaving a custom category unmapped keeps the previous behaviour (grouped under "Custom categories (migration)") and never blocks the migration (#259). + ## [0.14.0] - 2026-07-18 ### Changed diff --git a/src/components/categories-migration/StepSimulate.tsx b/src/components/categories-migration/StepSimulate.tsx index e86c8d4..267f3a6 100644 --- a/src/components/categories-migration/StepSimulate.tsx +++ b/src/components/categories-migration/StepSimulate.tsx @@ -199,18 +199,20 @@ export default function StepSimulate({

-