- 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>
73 lines
2.5 KiB
TypeScript
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
|
|
);
|
|
}
|