ifrs9-ecl-copilot / app /ui /src /format.js
Preetomsorkar's picture
UI v3: fintech-direction design pass (5 grafts), waterfall historical-mode fix, explain-strip placement + shared hook, palette contrast fix
20abf65 verified
Raw
History Blame Contribute Delete
1.75 kB
// Display formatting ONLY β€” no business arithmetic happens in the UI.
// Every number shown comes from the engine via the API.
export const fmtMillions = (v) =>
v == null || Number.isNaN(Number(v)) ? 'β€”' : `$${Number(v).toFixed(1)}m`;
export const fmtPct = (v, dp = 2) =>
v == null || Number.isNaN(Number(v)) ? 'β€”' : `${(Number(v) * 100).toFixed(dp)}%`;
export const fmtRatio = (v) =>
v == null || Number.isNaN(Number(v)) ? 'β€”' : `${Number(v).toFixed(3)}Γ—`;
export const fmtSignedM = (v) =>
v == null || Number.isNaN(Number(v))
? 'β€”'
: `${Number(v) >= 0 ? '+' : 'βˆ’'}$${Math.abs(Number(v)).toFixed(1)}m`;
export const fmtTime = (ms) => {
const d = new Date(ms);
const p = (n) => String(n).padStart(2, '0');
return `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
};
// A "*_pct" field is already 0-100 scale (per the contract's convention) β€”
// never re-multiply it.
export const fmtPctScale = (v, dp = 1) =>
v == null || Number.isNaN(Number(v)) ? 'β€”' : `${Number(v).toFixed(dp)}%`;
export const fmtNum = (v, dp = 0) =>
v == null || Number.isNaN(Number(v))
? 'β€”'
: Number(v).toLocaleString(undefined, {
minimumFractionDigits: dp,
maximumFractionDigits: dp,
});
export const fmtHazard = (v) =>
v == null || Number.isNaN(Number(v)) ? 'β€”' : Number(v).toFixed(4);
export const fmtSigned = (v, dp = 1) =>
v == null || Number.isNaN(Number(v))
? 'β€”'
: `${Number(v) >= 0 ? '+' : 'βˆ’'}${Math.abs(Number(v)).toFixed(dp)}`;
// Exhibit-footer "run <date>" stamp (FINAL_SPEC Β§5.2) β€” the client's own
// clock; the app has no server-side "as-of build" timestamp endpoint.
export const runDate = () => new Date().toISOString().slice(0, 10);