mirror of
https://gh.wpcy.net/https://github.com/superdav42/wp-update-server-plugin.git
synced 2026-05-06 05:22:23 +08:00
358 lines
8.2 KiB
JavaScript
358 lines
8.2 KiB
JavaScript
/**
|
|
* Telemetry Admin Dashboard — Chart.js charts and AJAX data loading.
|
|
*
|
|
* Depends on: Chart.js (loaded via wp_enqueue_script from CDN or bundled).
|
|
* Data is passed via wp_localize_script as `wuTelemetry`.
|
|
*
|
|
* @package WP_Update_Server_Plugin
|
|
*/
|
|
|
|
/* global Chart, wuTelemetry */
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
// Colour palette — consistent across all charts.
|
|
var COLORS = {
|
|
blue: 'rgba(34, 113, 177, 0.85)',
|
|
blueBorder: 'rgba(34, 113, 177, 1)',
|
|
green: 'rgba(0, 163, 42, 0.85)',
|
|
greenBorder:'rgba(0, 163, 42, 1)',
|
|
purple: 'rgba(124, 58, 237, 0.85)',
|
|
purpleBorder:'rgba(124, 58, 237, 1)',
|
|
stripe: 'rgba(99, 91, 255, 0.85)',
|
|
stripeBorder:'rgba(99, 91, 255, 1)',
|
|
paypal: 'rgba(0, 48, 135, 0.85)',
|
|
paypalBorder:'rgba(0, 48, 135, 1)',
|
|
orange: 'rgba(219, 166, 23, 0.85)',
|
|
orangeBorder:'rgba(219, 166, 23, 1)',
|
|
grid: 'rgba(0, 0, 0, 0.06)',
|
|
text: '#646970',
|
|
};
|
|
|
|
// Shared Chart.js defaults.
|
|
var CHART_DEFAULTS = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
display: false,
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
},
|
|
},
|
|
scales: {
|
|
x: {
|
|
grid: { color: COLORS.grid },
|
|
ticks: { color: COLORS.text, font: { size: 11 } },
|
|
},
|
|
y: {
|
|
grid: { color: COLORS.grid },
|
|
ticks: { color: COLORS.text, font: { size: 11 } },
|
|
beginAtZero: true,
|
|
},
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Build a horizontal bar chart for distribution data.
|
|
*
|
|
* @param {string} canvasId Canvas element ID.
|
|
* @param {Array} labels Array of label strings.
|
|
* @param {Array} values Array of numeric values.
|
|
* @param {string} color Bar fill colour.
|
|
* @param {string} border Bar border colour.
|
|
* @param {string} [unit] Optional unit suffix for tooltip (e.g. 'sites', 'networks').
|
|
*/
|
|
function buildBarChart(canvasId, labels, values, color, border, unit) {
|
|
var canvas = document.getElementById(canvasId);
|
|
if (!canvas) {
|
|
return;
|
|
}
|
|
|
|
var suffix = unit ? ' ' + unit : '';
|
|
|
|
new Chart(canvas, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
data: values,
|
|
backgroundColor: color || COLORS.blue,
|
|
borderColor: border || COLORS.blueBorder,
|
|
borderWidth: 1,
|
|
borderRadius: 3,
|
|
}],
|
|
},
|
|
options: Object.assign({}, CHART_DEFAULTS, {
|
|
indexAxis: 'y',
|
|
plugins: Object.assign({}, CHART_DEFAULTS.plugins, {
|
|
tooltip: {
|
|
callbacks: {
|
|
label: function (ctx) {
|
|
return ' ' + ctx.parsed.x.toLocaleString() + suffix;
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
scales: {
|
|
x: Object.assign({}, CHART_DEFAULTS.scales.x, {
|
|
ticks: Object.assign({}, CHART_DEFAULTS.scales.x.ticks, {
|
|
callback: function (val) {
|
|
return val.toLocaleString();
|
|
},
|
|
}),
|
|
}),
|
|
y: Object.assign({}, CHART_DEFAULTS.scales.y, {
|
|
grid: { display: false },
|
|
}),
|
|
},
|
|
}),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Build a line chart for trend data.
|
|
*
|
|
* @param {string} canvasId Canvas element ID.
|
|
* @param {Array} labels Date labels.
|
|
* @param {Array} datasets Array of dataset objects {label, data, color, border}.
|
|
*/
|
|
function buildLineChart(canvasId, labels, datasets) {
|
|
var canvas = document.getElementById(canvasId);
|
|
if (!canvas) {
|
|
return;
|
|
}
|
|
|
|
var chartDatasets = datasets.map(function (ds) {
|
|
return {
|
|
label: ds.label,
|
|
data: ds.data,
|
|
backgroundColor: ds.color,
|
|
borderColor: ds.border,
|
|
borderWidth: 2,
|
|
pointRadius: labels.length > 60 ? 0 : 3,
|
|
tension: 0.3,
|
|
fill: true,
|
|
};
|
|
});
|
|
|
|
new Chart(canvas, {
|
|
type: 'line',
|
|
data: {
|
|
labels: labels,
|
|
datasets: chartDatasets,
|
|
},
|
|
options: Object.assign({}, CHART_DEFAULTS, {
|
|
plugins: Object.assign({}, CHART_DEFAULTS.plugins, {
|
|
legend: {
|
|
display: datasets.length > 1,
|
|
position: 'top',
|
|
},
|
|
}),
|
|
scales: {
|
|
x: Object.assign({}, CHART_DEFAULTS.scales.x, {
|
|
ticks: Object.assign({}, CHART_DEFAULTS.scales.x.ticks, {
|
|
maxTicksLimit: 12,
|
|
}),
|
|
}),
|
|
y: Object.assign({}, CHART_DEFAULTS.scales.y, {
|
|
ticks: Object.assign({}, CHART_DEFAULTS.scales.y.ticks, {
|
|
callback: function (val) {
|
|
return val.toLocaleString();
|
|
},
|
|
}),
|
|
}),
|
|
},
|
|
}),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Build a doughnut chart for gateway/addon usage.
|
|
*
|
|
* @param {string} canvasId Canvas element ID.
|
|
* @param {Array} labels Label strings.
|
|
* @param {Array} values Numeric values.
|
|
*/
|
|
function buildDoughnutChart(canvasId, labels, values) {
|
|
var canvas = document.getElementById(canvasId);
|
|
if (!canvas) {
|
|
return;
|
|
}
|
|
|
|
var palette = [
|
|
COLORS.blue, COLORS.green, COLORS.purple,
|
|
COLORS.stripe, COLORS.orange, COLORS.paypal,
|
|
'rgba(239, 68, 68, 0.85)',
|
|
'rgba(245, 158, 11, 0.85)',
|
|
'rgba(16, 185, 129, 0.85)',
|
|
'rgba(59, 130, 246, 0.85)',
|
|
];
|
|
|
|
new Chart(canvas, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
data: values,
|
|
backgroundColor: palette.slice(0, values.length),
|
|
borderWidth: 2,
|
|
borderColor: '#fff',
|
|
}],
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
display: true,
|
|
position: 'right',
|
|
labels: {
|
|
font: { size: 11 },
|
|
color: COLORS.text,
|
|
boxWidth: 12,
|
|
},
|
|
},
|
|
tooltip: {
|
|
callbacks: {
|
|
label: function (ctx) {
|
|
var total = ctx.dataset.data.reduce(function (a, b) { return a + b; }, 0);
|
|
var pct = total > 0 ? Math.round((ctx.parsed / total) * 100) : 0;
|
|
return ' ' + ctx.label + ': ' + ctx.parsed.toLocaleString() + ' (' + pct + '%)';
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialise all charts from localised data.
|
|
*/
|
|
function init() {
|
|
var d = wuTelemetry;
|
|
|
|
// PHP version distribution.
|
|
if (d.phpVersions && d.phpVersions.labels.length) {
|
|
buildBarChart(
|
|
'wu-chart-php',
|
|
d.phpVersions.labels,
|
|
d.phpVersions.values,
|
|
COLORS.blue,
|
|
COLORS.blueBorder,
|
|
d.phpVersions.unit || 'sites'
|
|
);
|
|
}
|
|
|
|
// WP version distribution.
|
|
if (d.wpVersions && d.wpVersions.labels.length) {
|
|
buildBarChart(
|
|
'wu-chart-wp',
|
|
d.wpVersions.labels,
|
|
d.wpVersions.values,
|
|
COLORS.green,
|
|
COLORS.greenBorder,
|
|
d.wpVersions.unit || 'sites'
|
|
);
|
|
}
|
|
|
|
// Plugin version distribution.
|
|
if (d.pluginVersions && d.pluginVersions.labels.length) {
|
|
buildBarChart(
|
|
'wu-chart-plugin',
|
|
d.pluginVersions.labels,
|
|
d.pluginVersions.values,
|
|
COLORS.purple,
|
|
COLORS.purpleBorder,
|
|
d.pluginVersions.unit || 'sites'
|
|
);
|
|
}
|
|
|
|
// Gateway usage doughnut.
|
|
if (d.gateways && d.gateways.labels.length) {
|
|
buildDoughnutChart(
|
|
'wu-chart-gateways',
|
|
d.gateways.labels,
|
|
d.gateways.values
|
|
);
|
|
}
|
|
|
|
// Addon usage doughnut.
|
|
if (d.addons && d.addons.labels.length) {
|
|
buildDoughnutChart(
|
|
'wu-chart-addons',
|
|
d.addons.labels,
|
|
d.addons.values
|
|
);
|
|
}
|
|
|
|
// Stripe daily trend.
|
|
if (d.stripeTrend && d.stripeTrend.labels.length) {
|
|
buildLineChart(
|
|
'wu-chart-stripe-trend',
|
|
d.stripeTrend.labels,
|
|
[
|
|
{
|
|
label: 'Gross Volume',
|
|
data: d.stripeTrend.grossVolume,
|
|
color: 'rgba(99, 91, 255, 0.15)',
|
|
border: COLORS.stripeBorder,
|
|
},
|
|
{
|
|
label: 'Application Fees',
|
|
data: d.stripeTrend.appFees,
|
|
color: 'rgba(0, 163, 42, 0.15)',
|
|
border: COLORS.greenBorder,
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
// Subsite distribution.
|
|
if (d.subsiteDist && d.subsiteDist.labels.length) {
|
|
buildBarChart(
|
|
'wu-chart-subsites',
|
|
d.subsiteDist.labels,
|
|
d.subsiteDist.values,
|
|
COLORS.purple,
|
|
COLORS.purpleBorder,
|
|
d.subsiteDist.unit || 'networks'
|
|
);
|
|
}
|
|
|
|
// Revenue distribution.
|
|
if (d.revenueDist && d.revenueDist.labels.length) {
|
|
buildBarChart(
|
|
'wu-chart-revenue',
|
|
d.revenueDist.labels,
|
|
d.revenueDist.values,
|
|
COLORS.green,
|
|
COLORS.greenBorder,
|
|
d.revenueDist.unit || 'networks'
|
|
);
|
|
}
|
|
|
|
// Hosting providers.
|
|
if (d.hostingProviders && d.hostingProviders.labels.length) {
|
|
buildBarChart(
|
|
'wu-chart-hosting',
|
|
d.hostingProviders.labels,
|
|
d.hostingProviders.values,
|
|
COLORS.orange,
|
|
COLORS.orangeBorder,
|
|
d.hostingProviders.unit || 'networks'
|
|
);
|
|
}
|
|
}
|
|
|
|
// Run after DOM is ready.
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
}());
|