278 files
This commit is contained in:
@@ -8,6 +8,15 @@ import { BUNDLE_VERSION } from "./helpers/bundle-version";
|
||||
// derives the real symbol from const.BUDGET_CURRENCIES[DEFAULT_BUDGET_CURRENCY].
|
||||
export const DEFAULT_CURRENCY_SYMBOL = "€";
|
||||
|
||||
/** The display symbol of a currency-bearing response block (`budget` in
|
||||
* settings / stats, a BudgetStatus) — the ONE fallback rule. Seven call
|
||||
* sites had forked into "€" (panel) vs "" (cards, complete dialog), so the
|
||||
* cards rendered a bare "12" where the panel showed "12 €" (DRY audit
|
||||
* 2026-09-12). */
|
||||
export function currencySymbolOf(budget: { currency_symbol?: string | null } | null | undefined): string {
|
||||
return budget?.currency_symbol || DEFAULT_CURRENCY_SYMBOL;
|
||||
}
|
||||
|
||||
// Moved to the dependency-free status-constants.ts so the dashboard strategy
|
||||
// bundle can share them without pulling Lit in; re-exported here so every
|
||||
// existing `from "./styles"` import keeps working.
|
||||
@@ -208,6 +217,25 @@ interface ProfilePrefs {
|
||||
}
|
||||
const _w = window as unknown as { __msDateTimePrefs?: ProfilePrefs };
|
||||
const DT_PREFS: ProfilePrefs = (_w.__msDateTimePrefs ??= {});
|
||||
/** Currency display prefs shared across bundles (a window singleton like the
|
||||
* date/time prefs): the global `currency_decimals` setting. Synced from every
|
||||
* response that carries a currency block (settings, budget_status,
|
||||
* statistics) so every amount — KPIs, budgets, costs, printables — rounds
|
||||
* the same way. Default 0 = whole numbers. */
|
||||
const MONEY_PREFS: { decimals?: number } = ((_w as unknown as { __msMoneyPrefs?: { decimals?: number } }).__msMoneyPrefs ??= {});
|
||||
|
||||
export function setCurrencyDecimals(decimals: number | null | undefined): void {
|
||||
MONEY_PREFS.decimals = typeof decimals === "number" && Number.isInteger(decimals) && decimals >= 0 && decimals <= 3 ? decimals : 0;
|
||||
}
|
||||
|
||||
export function currencyDecimals(): number {
|
||||
return MONEY_PREFS.decimals ?? 0;
|
||||
}
|
||||
|
||||
/** Take the decimals from a currency-bearing response block, when present. */
|
||||
export function syncCurrencyDecimals(src: { currency_decimals?: number | null } | null | undefined): void {
|
||||
if (src && src.currency_decimals !== undefined && src.currency_decimals !== null) setCurrencyDecimals(Number(src.currency_decimals));
|
||||
}
|
||||
|
||||
/** Feed HA's per-user date/time/number formats into the formatters below.
|
||||
* `country` is the SERVER's configured country (#140): with the profile
|
||||
@@ -263,10 +291,12 @@ export function formatNumber(n: number, lang?: string, digits?: number | Intl.Nu
|
||||
}
|
||||
}
|
||||
|
||||
/** Amount + currency symbol ("12.50 €"; the symbol is optional) — every cost
|
||||
* figure in the UI and the printables goes through here. */
|
||||
export function formatCost(amount: number, symbol?: string, lang?: string, digits = 2): string {
|
||||
const num = formatNumber(amount, lang, digits);
|
||||
/** Amount + currency symbol ("13 €" / "12.50 €"; the symbol is optional) —
|
||||
* every cost figure in the UI and the printables goes through here. The
|
||||
* fraction digits come from the global `currency_decimals` setting (default
|
||||
* 0) unless a caller pins them. */
|
||||
export function formatCost(amount: number, symbol?: string, lang?: string, digits?: number): string {
|
||||
const num = formatNumber(amount, lang, digits ?? currencyDecimals());
|
||||
return symbol ? `${num} ${symbol}` : num;
|
||||
}
|
||||
|
||||
@@ -358,6 +388,14 @@ export function formatInterval(
|
||||
return `${intervalDays} ${t("unit_" + (unit || "days"), lang)}`;
|
||||
}
|
||||
|
||||
/** A duration in minutes, e.g. "45 min" — profile-aware number plus the
|
||||
* localised unit. Five surfaces carried their own `${x} min` / `${x}m`
|
||||
* (DRY round 2026-09; tripwired in profile-format-single-source.test.ts). */
|
||||
export function formatDuration(minutes: number | null | undefined, lang?: string): string {
|
||||
if (minutes === null || minutes === undefined) return "—";
|
||||
return `${formatNumber(minutes, lang, { maximumFractionDigits: 1 })} ${t("minutes_short", lang)}`;
|
||||
}
|
||||
|
||||
/** Localized weekday name (0=Mon … 6=Sun) via Intl — 2024-01-01 is a Monday.
|
||||
* Single source for the dialog selectors AND formatRecurrence (DRY).
|
||||
*
|
||||
@@ -713,6 +751,14 @@ export const sharedStyles = css`
|
||||
contain-intrinsic-size: auto 48px;
|
||||
}
|
||||
.history-entry:last-child { border-bottom: none; }
|
||||
/* Dense variant (quick-actions dialog, overview "recent activities"):
|
||||
no status-icon column, tighter padding, smaller type. */
|
||||
.history-entry.compact {
|
||||
padding: 6px 0;
|
||||
font-size: 13px;
|
||||
contain-intrinsic-size: auto 40px;
|
||||
}
|
||||
.history-entry.compact .history-readings { font-size: 12px; }
|
||||
|
||||
.history-icon {
|
||||
flex-shrink: 0;
|
||||
|
||||
Reference in New Issue
Block a user