Separa heatmap mobile/desktop, opt-out analytics per operatori, dashboard costi con KPI e trend mensili. Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
3.6 KiB
JavaScript
133 lines
3.6 KiB
JavaScript
(function () {
|
|
var trend = window.adminCostTrend || [];
|
|
var i18n = window.adminCostI18n || {};
|
|
if (!trend.length || typeof Chart === "undefined") return;
|
|
|
|
function monthLabel(monthStr) {
|
|
var parts = monthStr.split("-");
|
|
if (parts.length < 2) return monthStr;
|
|
var d = new Date(parseInt(parts[0], 10), parseInt(parts[1], 10) - 1, 1);
|
|
return d.toLocaleDateString([], { month: "short", year: "2-digit" });
|
|
}
|
|
|
|
var labels = trend.map(function (row) {
|
|
return monthLabel(row.month);
|
|
});
|
|
|
|
var chartOptions = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
animation: { duration: 300 },
|
|
scales: {
|
|
x: {
|
|
ticks: { maxTicksLimit: 12, color: "#9a9aad", font: { size: 10 } },
|
|
grid: { color: "rgba(255,255,255,0.06)" }
|
|
},
|
|
y: {
|
|
beginAtZero: true,
|
|
ticks: { color: "#9a9aad", font: { size: 10 } },
|
|
grid: { color: "rgba(255,255,255,0.06)" }
|
|
}
|
|
},
|
|
plugins: { legend: { labels: { color: "#ccc", boxWidth: 12 } } }
|
|
};
|
|
|
|
var euroTicks = {
|
|
ticks: {
|
|
color: "#9a9aad",
|
|
font: { size: 10 },
|
|
callback: function (v) {
|
|
return "€" + v;
|
|
}
|
|
}
|
|
};
|
|
|
|
var costRevenueCanvas = document.getElementById("chart-cost-revenue");
|
|
if (costRevenueCanvas) {
|
|
new Chart(costRevenueCanvas, {
|
|
type: "bar",
|
|
data: {
|
|
labels: labels,
|
|
datasets: [
|
|
{
|
|
label: i18n.cost || "Cost",
|
|
data: trend.map(function (r) { return (r.cost_cents || 0) / 100; }),
|
|
backgroundColor: "rgba(229, 57, 53, 0.65)"
|
|
},
|
|
{
|
|
label: i18n.revenue || "Revenue",
|
|
data: trend.map(function (r) { return (r.revenue_cents || 0) / 100; }),
|
|
backgroundColor: "rgba(67, 160, 71, 0.65)"
|
|
},
|
|
{
|
|
label: i18n.margin || "Margin",
|
|
data: trend.map(function (r) { return (r.margin_cents || 0) / 100; }),
|
|
backgroundColor: "rgba(255, 193, 7, 0.55)"
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
...chartOptions,
|
|
scales: {
|
|
x: chartOptions.scales.x,
|
|
y: { ...chartOptions.scales.y, ...euroTicks }
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
var costHourCanvas = document.getElementById("chart-cost-hour");
|
|
if (costHourCanvas) {
|
|
new Chart(costHourCanvas, {
|
|
type: "line",
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: i18n.costPerHour || "Cost/hour",
|
|
data: trend.map(function (r) {
|
|
return r.cost_per_hour_cents ? r.cost_per_hour_cents / 100 : 0;
|
|
}),
|
|
borderColor: "#e53935",
|
|
backgroundColor: "rgba(229, 57, 53, 0.12)",
|
|
fill: true,
|
|
tension: 0.35,
|
|
pointRadius: 2,
|
|
borderWidth: 2
|
|
}]
|
|
},
|
|
options: {
|
|
...chartOptions,
|
|
scales: {
|
|
x: chartOptions.scales.x,
|
|
y: { ...chartOptions.scales.y, ...euroTicks }
|
|
},
|
|
plugins: { legend: { display: false } }
|
|
}
|
|
});
|
|
}
|
|
|
|
var hoursCanvas = document.getElementById("chart-hours");
|
|
if (hoursCanvas) {
|
|
new Chart(hoursCanvas, {
|
|
type: "line",
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: i18n.hours || "Hours",
|
|
data: trend.map(function (r) { return r.hours || 0; }),
|
|
borderColor: "#43a047",
|
|
backgroundColor: "rgba(67, 160, 71, 0.12)",
|
|
fill: true,
|
|
tension: 0.35,
|
|
pointRadius: 2,
|
|
borderWidth: 2
|
|
}]
|
|
},
|
|
options: {
|
|
...chartOptions,
|
|
plugins: { legend: { display: false } }
|
|
}
|
|
});
|
|
}
|
|
})();
|