first (big) draft
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
// common.js — petits utilitaires partagés entre app.js (page globale)
|
||||
// et tenant.js (page détail tenant).
|
||||
|
||||
const eurFormatter = new Intl.NumberFormat("fr-FR", {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
|
||||
function formatEUR(amount) {
|
||||
if (amount === null || amount === undefined) return "—";
|
||||
return eurFormatter.format(amount);
|
||||
}
|
||||
|
||||
function formatPct(value) {
|
||||
if (value === null || value === undefined) return null;
|
||||
const sign = value > 0 ? "+" : "";
|
||||
return `${sign}${value.toFixed(1)} %`;
|
||||
}
|
||||
|
||||
function deltaClass(value) {
|
||||
if (value === null || value === undefined) return "flat";
|
||||
if (value > 0.5) return "up";
|
||||
if (value < -0.5) return "down";
|
||||
return "flat";
|
||||
}
|
||||
|
||||
function deltaArrow(value) {
|
||||
if (value === null || value === undefined) return "";
|
||||
if (value > 0.5) return "↑";
|
||||
if (value < -0.5) return "↓";
|
||||
return "→";
|
||||
}
|
||||
|
||||
function monthLabel(monthKey) {
|
||||
const [y, m] = monthKey.split("-").map(Number);
|
||||
const d = new Date(Date.UTC(y, m - 1, 1));
|
||||
return new Intl.DateTimeFormat("fr-FR", { month: "short", year: "2-digit", timeZone: "UTC" }).format(d);
|
||||
}
|
||||
|
||||
function dayLabel(isoDate) {
|
||||
const d = new Date(`${isoDate}T00:00:00Z`);
|
||||
return new Intl.DateTimeFormat("fr-FR", { day: "2-digit", month: "short", timeZone: "UTC" }).format(d);
|
||||
}
|
||||
|
||||
function providerLabel(provider) {
|
||||
return provider === "aws" ? "AWS" : provider === "azure" ? "Azure" : provider;
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function cssVar(name) {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
||||
}
|
||||
|
||||
// Config partagée des axes/grilles Chart.js (gridlines discrètes, pas de
|
||||
// deuxième axe, tooltip alignée sur le design system).
|
||||
function chartBaseOptions() {
|
||||
return {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: "index", intersect: false },
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: cssVar("--surface-1"),
|
||||
titleColor: cssVar("--text-primary"),
|
||||
bodyColor: cssVar("--text-secondary"),
|
||||
borderColor: cssVar("--border"),
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
displayColors: true,
|
||||
boxPadding: 4,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
border: { color: cssVar("--axis") },
|
||||
ticks: { color: cssVar("--text-muted"), font: { size: 11 } },
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: { color: cssVar("--gridline") },
|
||||
border: { display: false },
|
||||
ticks: { color: cssVar("--text-muted"), font: { size: 11 } },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user