Simpl-Resultat/src/utils/renderCategoryLabel.ts
le king fu 344c27ee6d
All checks were successful
PR Check / rust (pull_request) Successful in 22m29s
PR Check / frontend (pull_request) Successful in 2m23s
feat(balance): account form vehicle field + category rename via custom_label (#203)
- AccountForm: optional vehicle_type dropdown (account mode, 6 fiscal
  envelopes + none) wired into create/update; custom_label field (category
  mode) written to custom_label, never i18n_key.
- AccountsPage rename: writes custom_label, leaves i18n_key intact (fixes
  bug I where renaming clobbered the translation key).
- New shared renderCategoryLabel helper (+ shape adapters) applied at the 5
  sites: AccountsPage, AccountForm, SnapshotEditor, BalanceAccountsTable,
  BalancePage.
- Hide v13-deactivated seeds: useBalanceAccounts passes includeInactive=false
  (keeps #202 behavior-neutral default + tests green).
- BALANCE_VEHICLE_TYPES exported from shared/types as single source of truth
  (service reuses it).
- i18n FR+EN: vehicleType.*, category.form.customLabel*, errors.vehicle_type_invalid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 20:50:40 -04:00

73 lines
2.5 KiB
TypeScript

// renderCategoryLabel — single source of truth for how a balance category's
// label is shown in the UI (Bilan axe véhicule, Étape 1 / Issue #203).
//
// Precedence: an explicit `custom_label` (trimmed, non-empty) always wins;
// otherwise fall back to the i18n translation of `i18n_key`, and finally to
// the raw `key` if no translation exists. This is what makes category
// renaming (which now writes `custom_label`, never `i18n_key`) coexist with
// the seeded translations and fixes bug I.
//
// The 5 call sites carry the same three scalars under DIFFERENT field names:
// - BalanceCategory → custom_label / i18n_key / key
// - BalanceAccountWithCategory → category_custom_label / category_i18n_key / category_key
// - AccountLatestSnapshot → category_custom_label / category_i18n_key / category_key
// To avoid a shape mismatch (the "affichage partiel" caveat), the core helper
// takes explicit scalar args; thin adapters normalize each object shape.
/** Minimal i18n translate signature (matches react-i18next's `t`). */
type TranslateFn = (
key: string,
options?: { defaultValue?: string }
) => string;
/**
* Resolve a category's display label from its three label scalars.
*
* @param customLabel User-facing override (`custom_label`). NULL/blank ignored.
* @param i18nKey Translation key (`i18n_key`), e.g. 'balance.category.cash'.
* @param key Stable lookup key (`key`), used as the last-resort label.
* @param t Translate function.
*/
export function renderCategoryLabel(
customLabel: string | null | undefined,
i18nKey: string,
key: string,
t: TranslateFn
): string {
const trimmed = customLabel?.trim();
if (trimmed) return trimmed;
return t(i18nKey, { defaultValue: key });
}
/** Adapter for a `BalanceCategory`-shaped object. */
export function renderCategoryLabelFromCategory(
cat: {
custom_label?: string | null;
i18n_key: string;
key: string;
},
t: TranslateFn
): string {
return renderCategoryLabel(cat.custom_label, cat.i18n_key, cat.key, t);
}
/**
* Adapter for the JOIN-prefixed shape carried by `BalanceAccountWithCategory`
* and `AccountLatestSnapshot` (`category_custom_label` / `category_i18n_key` /
* `category_key`).
*/
export function renderCategoryLabelFromAccount(
acc: {
category_custom_label?: string | null;
category_i18n_key: string;
category_key: string;
},
t: TranslateFn
): string {
return renderCategoryLabel(
acc.category_custom_label,
acc.category_i18n_key,
acc.category_key,
t
);
}