almost functional

This commit is contained in:
Matt Batchelder
2026-02-04 15:26:44 -05:00
parent 2153d3c725
commit f392e5d016
39 changed files with 10115 additions and 602 deletions

View File

@@ -32,6 +32,32 @@
<p class="text-muted">{% trans "Overview of your digital signage network" %}</p>
</div>
<div class="quick-actions-grid">
<h3 class="section-title">{% trans "Quick Actions" %}</h3>
<div class="action-cards">
{% if currentUser.featureEnabled("schedule.view") %}
<a class="action-card action-card--modern dashboard-card" href="{{ url_for("schedule.view") }}">
<div class="action-icon"><i class="fa fa-calendar"></i></div>
<div class="action-label">{% trans "Create Schedule" %}</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("displays.view") %}
<a class="action-card action-card--modern dashboard-card" href="{{ url_for("display.view") }}">
<div class="action-icon"><i class="fa fa-desktop"></i></div>
<div class="action-label">{% trans "Manage Displays" %}</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("users.view") and (currentUser.isGroupAdmin() or currentUser.isSuperAdmin()) %}
<a class="action-card action-card--modern dashboard-card" href="{{ url_for("user.view") }}">
<div class="action-icon"><i class="fa fa-user-plus"></i></div>
<div class="action-label">{% trans "Add User" %}</div>
</a>
{% endif %}
</div>
</div>
<div class="kpi-section">
<div class="kpi-card dashboard-card kpi-card--modern">
<div class="kpi-header">
@@ -100,10 +126,10 @@
<div class="dashboard-panels">
<div class="widget dashboard-chart-card dashboard-chart-card--bandwidth dashboard-card">
<div class="widget-title dashboard-chart-header">
<div class="dashboard-chart-title">
<span class="dashboard-chart-icon"><i class="fa fa-cloud-download"></i></span>
<div>
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-arrow-down" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading">
{% if xmdsLimit != "" %}
{% trans %}Bandwidth Usage{% endtrans %}
@@ -120,10 +146,15 @@
</div>
</div>
</div>
{% if currentUser.featureEnabled("displays.reporting") %}
<a class="dashboard-chart-link" href="/report/form/bandwidth">{% trans "More Statistics" %}</a>
{% endif %}
<div class="clearfix"></div>
<div class="dashboard-chart-actions">
{% if currentUser.featureEnabled("displays.reporting") %}
<a class="dashboard-chart-link" href="/report/form/bandwidth">{% trans "More Statistics" %}</a>
{% endif %}
<div class="dashboard-chart-toggle" data-chart="bandwidthChart">
<button type="button" class="dashboard-chart-toggle-button is-active" data-chart-type="line" aria-label="{% trans 'Line chart' %}"><i class="fa fa-chart-line" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="bar" aria-label="{% trans 'Bar chart' %}"><i class="fa fa-chart-bar" aria-hidden="true"></i></button>
</div>
</div>
</div>
<div class="widget-body dashboard-chart-body">
<div class="dashboard-chart-canvas">
@@ -133,10 +164,10 @@
</div>
<div class="widget dashboard-chart-card dashboard-chart-card--library dashboard-card">
<div class="widget-title dashboard-chart-header">
<div class="dashboard-chart-title">
<span class="dashboard-chart-icon"><i class="fa fa-tasks"></i></span>
<div>
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-hdd-o" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading">
{% trans "Library Usage" %}
</div>
@@ -149,7 +180,13 @@
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="dashboard-chart-actions">
<div class="dashboard-chart-toggle" data-chart="libraryChart">
<button type="button" class="dashboard-chart-toggle-button is-active" data-chart-type="pie" aria-label="{% trans 'Pie chart' %}"><i class="fa fa-pie-chart" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="doughnut" aria-label="{% trans 'Doughnut chart' %}"><i class="fa fa-circle-o" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="bar" aria-label="{% trans 'Bar chart' %}"><i class="fa fa-chart-bar" aria-hidden="true"></i></button>
</div>
</div>
</div>
<div class="widget-body dashboard-chart-body">
<div class="dashboard-chart-canvas">
@@ -161,8 +198,13 @@
<div class="dashboard-panels">
<div class="panel dashboard-card">
<div class="panel-header">
<h3>{% trans "Display Activity" %}</h3>
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-desktop" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading">{% trans "Display Activity" %}</div>
</div>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
@@ -181,8 +223,13 @@
</div>
<div class="panel dashboard-card">
<div class="panel-header">
<h3>{% trans "Latest News" %}</h3>
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-newspaper-o" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading">{% trans "Latest News" %}</div>
</div>
</div>
</div>
<div class="panel-body">
{% if latestNews|length > 0 %}
@@ -202,51 +249,97 @@
</div>
<div class="dashboard-panels">
<div class="panel dashboard-card">
<div class="panel-header">
<h3>{% trans "Display Status" %}</h3>
<div class="widget dashboard-chart-card dashboard-card">
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-circle-o" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading">{% trans "Display Status" %}</div>
<div class="dashboard-chart-subtitle">{% trans "Click on the chart for a breakdown" %}</div>
</div>
</div>
<div class="dashboard-chart-actions">
<div class="dashboard-chart-toggle" data-chart="displayStatusChart">
<button type="button" class="dashboard-chart-toggle-button is-active" data-chart-type="doughnut" aria-label="{% trans 'Doughnut chart' %}"><i class="fa fa-circle-o" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="pie" aria-label="{% trans 'Pie chart' %}"><i class="fa fa-pie-chart" aria-hidden="true"></i></button>
</div>
</div>
</div>
<div class="panel-body" style="overflow: hidden;">
<div style="text-align: center; height: 10px; margin-bottom: 5px"><span>{% trans "Click on the chart for a breakdown" %}</span></div>
<div style="position: relative; height: 235px">
<canvas id="displayStatusChart" style="clear:both;"></canvas>
<div class="widget-body dashboard-chart-body">
<div class="dashboard-chart-canvas">
<canvas id="displayStatusChart" style="clear:both;" aria-label="{% trans "Display Status" %}" role="img"></canvas>
</div>
</div>
</div>
<div class="panel dashboard-card">
<div class="panel-header">
<h3>{% trans "Display Content Status" %}</h3>
<div class="widget dashboard-chart-card dashboard-card">
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-list-alt" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading">{% trans "Display Content Status" %}</div>
<div class="dashboard-chart-subtitle">{% trans "Click on the chart for a breakdown" %}</div>
</div>
</div>
<div class="dashboard-chart-actions">
<div class="dashboard-chart-toggle" data-chart="displayContentChart">
<button type="button" class="dashboard-chart-toggle-button is-active" data-chart-type="doughnut" aria-label="{% trans 'Doughnut chart' %}"><i class="fa fa-circle-o" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="pie" aria-label="{% trans 'Pie chart' %}"><i class="fa fa-pie-chart" aria-hidden="true"></i></button>
</div>
</div>
</div>
<div class="panel-body" style="overflow: hidden;">
<div style="text-align: center; height: 10px; margin-bottom: 5px"><span>{% trans "Click on the chart for a breakdown" %}</span></div>
<div style="position: relative; height: 235px">
<canvas id="displayContentChart" style="clear:both;"></canvas>
<div class="widget-body dashboard-chart-body">
<div class="dashboard-chart-canvas">
<canvas id="displayContentChart" style="clear:both;" aria-label="{% trans "Display Content Status" %}" role="img"></canvas>
</div>
</div>
</div>
</div>
<div class="dashboard-panels d-none" id="displayGroupStatusChartRow">
<div class="panel dashboard-card">
<div class="panel-header">
<h3 id="dGStatusTitle">{% trans "Display Groups Status" %}</h3>
<div class="widget dashboard-chart-card dashboard-card">
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-sitemap" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading" id="dGStatusTitle">{% trans "Display Groups Status" %}</div>
<div class="dashboard-chart-subtitle">{% trans "Grouped by status" %}</div>
</div>
</div>
<div class="dashboard-chart-actions">
<div class="dashboard-chart-toggle" data-chart="displayGroupStatusChart">
<button type="button" class="dashboard-chart-toggle-button is-active" data-chart-type="doughnut" aria-label="{% trans 'Doughnut chart' %}"><i class="fa fa-circle-o" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="pie" aria-label="{% trans 'Pie chart' %}"><i class="fa fa-pie-chart" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="bar" aria-label="{% trans 'Bar chart' %}"><i class="fa fa-chart-bar" aria-hidden="true"></i></button>
</div>
</div>
</div>
<div class="panel-body" style="overflow: hidden;">
<div style="text-align: center; height: 10px; margin-bottom: 5px"><span>{% trans "Click on the chart to view Display information" %}</span></div>
<div style="position: relative; height: 235px;">
<div class="widget-body dashboard-chart-body">
<div class="dashboard-chart-canvas">
<canvas id="displayGroupStatusChart" style="clear:both;"></canvas>
</div>
</div>
</div>
<div class="panel dashboard-card">
<div class="panel-header">
<h3 id="dGContentTitle">{% trans "Display Groups Content Status" %}</h3>
<div class="widget dashboard-chart-card dashboard-card">
<div class="dashboard-chart-header">
<div class="dashboard-chart-info">
<div class="dashboard-chart-icon"><i class="fa fa-folder-o" aria-hidden="true"></i></div>
<div class="dashboard-chart-meta">
<div class="dashboard-chart-heading" id="dGContentTitle">{% trans "Display Groups Content Status" %}</div>
<div class="dashboard-chart-subtitle">{% trans "Grouped by content status" %}</div>
</div>
</div>
<div class="dashboard-chart-actions">
<div class="dashboard-chart-toggle" data-chart="displayGroupContentStatusChart">
<button type="button" class="dashboard-chart-toggle-button is-active" data-chart-type="doughnut" aria-label="{% trans 'Doughnut chart' %}"><i class="fa fa-circle-o" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="pie" aria-label="{% trans 'Pie chart' %}"><i class="fa fa-pie-chart" aria-hidden="true"></i></button>
<button type="button" class="dashboard-chart-toggle-button" data-chart-type="bar" aria-label="{% trans 'Bar chart' %}"><i class="fa fa-chart-bar" aria-hidden="true"></i></button>
</div>
</div>
</div>
<div class="panel-body" style="overflow: hidden;">
<div style="text-align: center; height: 10px; margin-bottom: 5px"><span>{% trans "Click on the chart to view Display information" %}</span></div>
<div style="position: relative; height: 235px">
<div class="widget-body dashboard-chart-body">
<div class="dashboard-chart-canvas">
<canvas id="displayGroupContentStatusChart" style="clear:both;"></canvas>
</div>
</div>
@@ -322,32 +415,6 @@
</div>
</div>
</div>
<div class="quick-actions-grid">
<h3 class="section-title">{% trans "Quick Actions" %}</h3>
<div class="action-cards">
{% if currentUser.featureEnabled("schedule.view") %}
<a class="action-card action-card--modern dashboard-card" href="{{ url_for("schedule.view") }}">
<div class="action-icon"><i class="fa fa-calendar"></i></div>
<div class="action-label">{% trans "Create Schedule" %}</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("displays.view") %}
<a class="action-card action-card--modern dashboard-card" href="{{ url_for("display.view") }}">
<div class="action-icon"><i class="fa fa-desktop"></i></div>
<div class="action-label">{% trans "Manage Displays" %}</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("users.view") and (currentUser.isGroupAdmin() or currentUser.isSuperAdmin()) %}
<a class="action-card action-card--modern dashboard-card" href="{{ url_for("user.view") }}">
<div class="action-icon"><i class="fa fa-user-plus"></i></div>
<div class="action-label">{% trans "Add User" %}</div>
</a>
{% endif %}
</div>
</div>
</div>
{% endblock %}
@@ -377,50 +444,173 @@
var displayGroupIdsStatus = [];
var displayGridTable = null
// Create our chart
var bandwidthChart = new Chart($("#bandwidthChart"), {
type: "bar",
data: {{ bandwidthWidget|raw }},
options: {
scales: {
xAxes: [{
stacked: {% if xmdsLimit %}true{% else %}false{% endif %}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: "{{ bandwidthSuffix }}",
},
stacked: {% if xmdsLimit %}true{% else %}false{% endif %}
}]
},
legend: {
display: false
},
maintainAspectRatio: false,
}
});
// Create our charts
const bandwidthStacked = {% if xmdsLimit %}true{% else %}false{% endif %};
var libraryData = {{ libraryWidgetData|raw }};
const libraryLabels = {{ libraryWidgetLabels|raw }};
var colours = new Array();
for (var i = 0; i < libraryData.length; i++) {
colours.push(stringToColour(libraryLabels[i]));
}
var libraryChart = new Chart($("#libraryChart"), {
type: 'pie',
data: {
datasets: [{
data: libraryData,
backgroundColor: colours
}],
function pickColor(value, fallback) {
if (Array.isArray(value)) return value[0] || fallback;
return value || fallback;
}
labels: {{ libraryWidgetLabels|raw }}
},
options: {
maintainAspectRatio: false
}
});
function cacheDatasetStyles(dataset) {
if (!dataset._ots) {
dataset._ots = {
backgroundColor: dataset.backgroundColor,
borderColor: dataset.borderColor,
fill: dataset.fill
};
}
}
function applyDatasetType(dataset, type, fallbackColor) {
cacheDatasetStyles(dataset);
dataset.type = type;
if (type === 'line') {
const color = pickColor(dataset._ots.borderColor || dataset._ots.backgroundColor, fallbackColor);
dataset.borderColor = color;
dataset.backgroundColor = 'rgba(0, 0, 0, 0)';
dataset.fill = false;
dataset.tension = 0.35;
dataset.pointRadius = 2;
dataset.pointHoverRadius = 3;
dataset.pointBackgroundColor = color;
} else {
dataset.backgroundColor = dataset._ots.backgroundColor || dataset.backgroundColor || fallbackColor;
dataset.borderColor = dataset._ots.borderColor || dataset.borderColor;
dataset.fill = dataset._ots.fill;
dataset.tension = 0;
dataset.pointRadius = 0;
}
}
function setBandwidthScaleOptions(chart, type) {
if (!chart.options || !chart.options.scales) return;
const stacked = type === 'bar' ? bandwidthStacked : false;
chart.options.scales.xAxes[0].stacked = stacked;
chart.options.scales.yAxes[0].stacked = stacked;
}
function setChartType(chart, type, isBandwidth) {
chart.config.type = type;
chart.data.datasets.forEach(function(dataset) {
applyDatasetType(dataset, type, 'rgba(96, 165, 250, 0.9)');
});
if (isBandwidth) {
setBandwidthScaleOptions(chart, type);
}
chart.update();
}
function setLibraryChartType(chart, type) {
if (type === 'pie' || type === 'doughnut') {
chart.config.type = type;
chart.update();
} else if (type === 'bar') {
// Convert pie data to bar format
const labels = chart.data.labels;
const data = chart.data.datasets[0].data;
const colors = chart.data.datasets[0].backgroundColor;
chart.config.type = 'bar';
chart.data.datasets[0].type = 'bar';
chart.data.datasets[0].backgroundColor = colors;
chart.data.datasets[0].borderColor = colors;
chart.options.scales = {
xAxes: [{ stacked: false }],
yAxes: [{ stacked: false }]
};
chart.update();
}
}
var bandwidthChart = new Chart($("#bandwidthChart"), {
type: "line",
data: {{ bandwidthWidget|raw }},
options: {
scales: {
xAxes: [{
stacked: false
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: "{{ bandwidthSuffix }}",
},
stacked: false
}]
},
legend: {
display: false
},
maintainAspectRatio: false,
}
});
var libraryData = {{ libraryWidgetData|raw }};
const libraryLabels = {{ libraryWidgetLabels|raw }};
var colours = new Array();
for (var i = 0; i < libraryData.length; i++) {
colours.push(stringToColour(libraryLabels[i]));
}
var libraryChart = new Chart($("#libraryChart"), {
type: 'pie',
data: {
datasets: [{
data: libraryData,
backgroundColor: colours
}],
labels: {{ libraryWidgetLabels|raw }}
},
options: {
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
setChartType(libraryChart, 'pie', false);
$('.dashboard-chart-toggle').each(function() {
const toggle = $(this);
const chartId = toggle.data('chart');
let chart = null;
if (chartId === 'bandwidthChart') {
chart = bandwidthChart;
} else if (chartId === 'libraryChart') {
chart = libraryChart;
} else if (chartId === 'displayStatusChart') {
chart = displayStatusChart;
} else if (chartId === 'displayContentChart') {
chart = displayContentChart;
} else if (chartId === 'displayGroupStatusChart') {
chart = displayGroupStatusChart;
} else if (chartId === 'displayGroupContentStatusChart') {
chart = displayGroupContentStatusChart;
}
if (!chart) return;
toggle.find('.dashboard-chart-toggle-button').on('click', function(e) {
e.preventDefault();
const type = $(this).data('chart-type');
// Update active state
toggle.find('.dashboard-chart-toggle-button').removeClass('is-active');
$(this).addClass('is-active');
// Update chart type
if (chartId === 'libraryChart') {
setLibraryChartType(chart, type);
} else {
setChartType(chart, type, chartId === 'bandwidthChart');
}
});
});
$('.article_date').each(function(index, element) {
// Replace the ISO date with a nice formatted date "for humans"