Files
simple-cost-dashboard/frontend/static/common.js
T
ludal c39364eda4 feat: add services page and API endpoints for service cost breakdown
- Implemented new API endpoints for fetching global services and service tenants details.
- Created a new services.html page with a sidebar for navigation and a chart for service costs.
- Updated frontend JavaScript to handle service data loading and rendering.
- Enhanced the dashboard with provider filtering options for AWS and Azure.
- Added a segmented toggle for selecting the date range for service costs.
- Improved styling for the sidebar and data tables.
- Refactored common utility functions for better reusability across pages.
2026-09-20 21:55:52 +02:00

101 lines
2.8 KiB
JavaScript

// 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 rangeLabel(days) {
return days === 1 ? "aujourd'hui" : `${days} derniers jours`;
}
function providerLabel(provider) {
return provider === "aws" ? "AWS" : provider === "azure" ? "Azure" : provider;
}
function escapeHtml(str) {
return String(str)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
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 } },
},
},
};
}