/* * Getallen → Nederlands. Gedeeld door de twee uitvoervormen van `npm run health`: de terminal * (scripts/health.js) en de pagina (scripts/health-html.js). Ze staan hier zodat "20 u" en * "18% (702 van 3858)" op beide plekken hetzelfde betekenen én er hetzelfde uitzien; twee kopieën * van een afrondingsregel is één kopie te veel. */ import { GENDER_LABEL } from "../src/constants.js"; export const nl = (x, d = 1) => (x == null ? "—" : x.toFixed(d).replace(".", ",")); /* * Elk percentage draagt zijn n mee. Er wordt niets verzwegen bij kleine aantallen — 100% over twee * mensen mag er staan, zolang die twee er ook staat. Zonder noemer een streepje, want 0/0 is geen 0%. */ export const pct = (n, of) => (of ? `${Math.round((n / of) * 100)}% (${n} van ${of})` : "— (geen)"); export const pctShort = (n, of) => (of ? `${Math.round((n / of) * 100)}%` : "—"); export const share = (n, of) => (of ? n / of : 0); export const per100 = (n, of) => (of ? `${nl((n / of) * 100)} per 100` : "—"); /* Seconden → iets wat een mens leest */ export const span = (sec) => { if (sec == null) return "—"; const h = sec / 3600; if (h < 1) return `${Math.round(sec / 60)} min`; if (h < 48) return `${nl(h, 0)} u`; const d = h / 24; if (d < 21) return `${nl(d, 0)} d`; return `${nl(d / 7, 0)} wk`; }; export const spanH = (h) => span(h == null ? null : h * 3600); export const genderName = (g) => (g == null ? "iedereen" : GENDER_LABEL[g] ?? g); export const weekLabel = (d) => new Date(d).toLocaleDateString("nl-NL", { day: "numeric", month: "short", year: "2-digit" }); export const dateLabel = (d) => new Date(d).toLocaleDateString("nl-NL", { day: "numeric", month: "long", year: "numeric" }); /* * De wachttijdbakken, afgetopt op de horizon van --settle. Zonder die aftopping staat er "7 tot 30 * dagen" boven een telling die maar 14 dagen ver kijkt, en dat opschrift liegt dan. Bakken waarvan * de ondergrens al voorbij de horizon ligt vallen helemaal weg: die kunnen per definitie leeg zijn. */ export const waitBuckets = (row, settleDays) => { const horizon = settleDays * 24; return [[0, 24, row.b1], [24, 72, row.b2], [72, 168, row.b3], [168, 720, row.b4], [720, Infinity, row.b5]] .filter(([low]) => low < horizon) .map(([low, high, n]) => { const hi = Math.min(high, horizon) / 24; const label = low === 0 ? (hi === 1 ? "binnen een dag" : `binnen ${nl(hi, 0)} dagen`) : `${low / 24} tot ${nl(hi, 0)} dagen`; return { label, n: n ?? 0 }; }); }; /* De middelste waarde; bij een even aantal de laagste van de twee — geen half proefprofiel */ export const median = (xs) => (xs.length ? [...xs].sort((a, b) => a - b)[Math.floor((xs.length - 1) / 2)] : null);