Files
OTSSignsTheme/ots-signs/views/dashboard-icon-page.twig
Matt Batchelder bbe8c1860c pre-img swap
2026-03-23 21:09:27 -04:00

538 lines
24 KiB
Twig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{#
/**
* OTS Signage Theme - Icon Dashboard Override
*
* Custom stylized icon dashboard that uses card-based buttons
* matching the OTS dashboard design system.
*
* Based on Xibo CMS dashboard-icon-page.twig
*/
#}
{% extends "authed.twig" %}
{% import "inline.twig" as inline %}
{% block pageContent %}
{% include "theme-dashboard-message.twig" ignore missing %}
<div class="dashboard-page">
<div class="page-header">
<h1>{% trans "Dashboard" %}</h1>
<p class="text-muted">{% trans "Quick access to all areas of your signage network" %}</p>
</div>
{# ── Status Bar ──────────────────────────────────────────── #}
<div class="ots-stat-bar">
{% if currentUser.featureEnabled("displays.view") %}
<a class="ots-stat-tile" href="{{ url_for("display.view") }}">
<div class="ots-stat-tile-icon ots-stat-tile-icon--green">
<i class="fa fa-desktop"></i>
</div>
<div class="ots-stat-tile-content">
<span class="ots-stat-tile-number" id="ots-stat-displays">—</span>
<span class="ots-stat-tile-label">{% trans "Displays" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("layout.view") %}
<a class="ots-stat-tile" href="{{ url_for("layout.view") }}">
<div class="ots-stat-tile-icon ots-stat-tile-icon--blue">
<i class="fa fa-columns"></i>
</div>
<div class="ots-stat-tile-content">
<span class="ots-stat-tile-number" id="ots-stat-layouts">—</span>
<span class="ots-stat-tile-label">{% trans "Layouts" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("library.view") %}
<a class="ots-stat-tile" href="{{ url_for("library.view") }}">
<div class="ots-stat-tile-icon ots-stat-tile-icon--orange">
<i class="fa fa-image"></i>
</div>
<div class="ots-stat-tile-content">
<span class="ots-stat-tile-number" id="ots-stat-media">—</span>
<span class="ots-stat-tile-label">{% trans "Media Files" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("schedule.view") %}
<a class="ots-stat-tile" href="{{ url_for("schedule.view") }}">
<div class="ots-stat-tile-icon ots-stat-tile-icon--purple">
<i class="fa fa-calendar-check-o"></i>
</div>
<div class="ots-stat-tile-content">
<span class="ots-stat-tile-number" id="ots-stat-schedules">—</span>
<span class="ots-stat-tile-label">{% trans "Scheduled Events" %}</span>
</div>
</a>
{% endif %}
</div>
{# ── Scheduling ────────────────────────────────────────────── #}
{% set scheduleCount = currentUser.featureEnabledCount(["schedule.view", "daypart.view"]) %}
{% if scheduleCount > 0 %}
<div class="icon-dash-section">
<details open>
<summary class="section-title"><i class="fa fa-calendar"></i> {% trans "Scheduling" %}</summary>
<div class="icon-dash-grid">
{% if currentUser.featureEnabled("schedule.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("schedule.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
<i class="fa fa-calendar-check-o"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Schedule" %}</span>
<span class="icon-dash-card-desc">{% trans "Manage scheduled events" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("daypart.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("daypart.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--indigo">
<i class="fa fa-clock-o"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Dayparting" %}</span>
<span class="icon-dash-card-desc">{% trans "Define time segments" %}</span>
</div>
</a>
{% endif %}
</div>
</details>
</div>
{% endif %}
{# ── Design ────────────────────────────────────────────────── #}
{% set countViewable = currentUser.featureEnabledCount(["campaign.view", "layout.view", "template.view", "resolution.view"]) %}
{% if countViewable > 0 %}
<div class="icon-dash-section">
<details open>
<summary class="section-title"><i class="fa fa-paint-brush"></i> {% trans "Design" %}</summary>
<div class="icon-dash-grid">
{% if currentUser.featureEnabled("campaign.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("campaign.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--green">
<i class="fa fa-bullhorn"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Campaigns" %}</span>
<span class="icon-dash-card-desc">{% trans "Organise layout playlists" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("layout.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("layout.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
<i class="fa fa-columns"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Layouts" %}</span>
<span class="icon-dash-card-desc">{% trans "Design screen content" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("template.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("template.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--purple">
<i class="fa fa-clone"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Templates" %}</span>
<span class="icon-dash-card-desc">{% trans "Reusable layout patterns" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("resolution.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("resolution.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--teal">
<i class="fa fa-expand"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Resolutions" %}</span>
<span class="icon-dash-card-desc">{% trans "Screen size presets" %}</span>
</div>
</a>
{% endif %}
</div>
</details>
</div>
{% endif %}
{# ── Library ───────────────────────────────────────────────── #}
{% set countViewable = currentUser.featureEnabledCount(["library.view", "playlist.view", "dataset.view", "menuBoard.view"]) %}
{% if countViewable > 0 %}
<div class="icon-dash-section">
<details open>
<summary class="section-title"><i class="fa fa-picture-o"></i> {% trans "Library" %}</summary>
<div class="icon-dash-grid">
{% if currentUser.featureEnabled("library.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("library.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--orange">
<i class="fa fa-image"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Library" %}</span>
<span class="icon-dash-card-desc">{% trans "Upload and manage media" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("playlist.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("playlist.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
<i class="fa fa-list"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Playlists" %}</span>
<span class="icon-dash-card-desc">{% trans "Content play sequences" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("dataset.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("dataset.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--indigo">
<i class="fa fa-database"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "DataSets" %}</span>
<span class="icon-dash-card-desc">{% trans "Structured data sources" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("menuBoard.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("menuBoard.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--red">
<i class="fa fa-cutlery"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Menu Boards" %}</span>
<span class="icon-dash-card-desc">{% trans "Digital menu layouts" %}</span>
</div>
</a>
{% endif %}
</div>
</details>
</div>
{% endif %}
{# ── Displays ──────────────────────────────────────────────── #}
{% set countViewable = currentUser.featureEnabledCount(["displays.view", "displaygroup.view", "displayprofile.view"]) %}
{% if countViewable > 0 %}
<div class="icon-dash-section">
<details open>
<summary class="section-title"><i class="fa fa-desktop"></i> {% trans "Displays" %}</summary>
<div class="icon-dash-grid">
{% if currentUser.featureEnabled("displays.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("display.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--green">
<i class="fa fa-desktop"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Displays" %}</span>
<span class="icon-dash-card-desc">{% trans "Monitor your screens" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("displaygroup.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("displaygroup.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
<i class="fa fa-object-group"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Display Groups" %}</span>
<span class="icon-dash-card-desc">{% trans "Group displays together" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.featureEnabled("displayprofile.view") %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("displayprofile.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--purple">
<i class="fa fa-cog"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Display Settings" %}</span>
<span class="icon-dash-card-desc">{% trans "Configure display profiles" %}</span>
</div>
</a>
{% endif %}
</div>
</details>
</div>
{% endif %}
{# ── Administration ────────────────────────────────────────── #}
{% set showAdmin = false %}
{% if currentUser.featureEnabled("users.view") and (currentUser.isGroupAdmin() or currentUser.isSuperAdmin()) %}
{% set showAdmin = true %}
{% endif %}
{% if currentUser.isSuperUser() %}
{% set showAdmin = true %}
{% endif %}
{% if showAdmin %}
<div class="icon-dash-section">
<details open>
<summary class="section-title"><i class="fa fa-cogs"></i> {% trans "Administration" %}</summary>
<div class="icon-dash-grid">
{% if currentUser.featureEnabled("users.view") and (currentUser.isGroupAdmin() or currentUser.isSuperAdmin()) %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("user.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--indigo">
<i class="fa fa-users"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Users" %}</span>
<span class="icon-dash-card-desc">{% trans "Manage user accounts" %}</span>
</div>
</a>
{% endif %}
{% if currentUser.isSuperUser() %}
<a class="icon-dash-card dashboard-card" href="{{ url_for("admin.view") }}">
<div class="icon-dash-card-icon icon-dash-card-icon--orange">
<i class="fa fa-cogs"></i>
</div>
<div class="icon-dash-card-body">
<span class="icon-dash-card-title">{% trans "Settings" %}</span>
<span class="icon-dash-card-desc">{% trans "System configuration" %}</span>
</div>
</a>
{% endif %}
</div>
</details>
</div>
{% endif %}
</div>
<style nonce="{{ cspNonce }}">
/* ===================================================================
ICON DASHBOARD Card Button Styles
Matches the OTS dashboard-card design system
=================================================================== */
/* Section spacing */
.icon-dash-section {
margin-top: 32px;
}
.icon-dash-section:first-of-type {
margin-top: 24px;
}
/* Grid layout responsive card grid */
.icon-dash-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 18px;
margin-top: 14px;
}
/* Individual card inherits .dashboard-card base from override.css */
.icon-dash-card {
display: flex;
flex-direction: row;
align-items: center;
gap: 18px;
padding: 22px 24px;
text-decoration: none !important;
color: var(--color-text-primary) !important;
cursor: pointer;
position: relative;
overflow: hidden;
/* Override rigid dashboard-card flex-direction:column if set */
flex-direction: row !important;
}
/* Icon container */
.icon-dash-card-icon {
flex-shrink: 0;
width: 52px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
font-size: 22px;
}
/* Icon colour variants — flat tinted backgrounds, no gradient formula */
.icon-dash-card-icon--blue {
background: rgba(50, 110, 220, 0.14);
color: #5c9bff;
}
.icon-dash-card-icon--green {
background: rgba(22, 175, 120, 0.14);
color: #2eb88a;
}
.icon-dash-card-icon--orange {
background: rgba(232, 120, 0, 0.14);
color: #e87800;
}
.icon-dash-card-icon--red {
background: rgba(220, 70, 70, 0.14);
color: #e26060;
}
.icon-dash-card-icon--purple {
background: rgba(145, 80, 220, 0.14);
color: #b37dd9;
}
.icon-dash-card-icon--indigo {
background: rgba(95, 100, 210, 0.14);
color: #8d91e8;
}
.icon-dash-card-icon--teal {
background: rgba(20, 175, 158, 0.14);
color: #24bfae;
}
/* Text area */
.icon-dash-card-body {
display: flex;
flex-direction: column;
min-width: 0;
/* Reset inherited dashboard-card body padding */
padding: 0 !important;
background: transparent !important;
}
.icon-dash-card-title {
font-size: 15px;
font-weight: 600;
color: var(--color-text-primary);
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Hover effects */
.icon-dash-card:hover {
border-color: rgba(232, 120, 0, 0.4) !important;
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(8, 15, 30, 0.3) !important;
}
.icon-dash-card:active {
transform: translateY(0px);
box-shadow: 0 3px 8px rgba(8, 15, 30, 0.2) !important;
}
/* Section title with icon */
.icon-dash-section .section-title i {
margin-right: 8px;
opacity: 0.65;
}
/* ── Stat bar link reset ── */
.ots-stat-tile {
text-decoration: none !important;
color: inherit !important;
}
/* ── Light mode overrides ─────────────────────────────────────── */
body.ots-light-mode .icon-dash-card {
background: #ffffff !important;
border-color: rgba(148, 163, 184, 0.25) !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05) !important;
}
body.ots-light-mode .icon-dash-card:hover {
background: #ffffff !important;
border-color: rgba(232, 120, 0, 0.4) !important;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.09) !important;
}
/* ── Responsive adjustments ───────────────────────────────────── */
@media (max-width: 768px) {
.icon-dash-grid {
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.icon-dash-card {
padding: 16px 18px;
gap: 14px;
}
.icon-dash-card-icon {
width: 44px;
height: 44px;
font-size: 18px;
border-radius: 6px;
}
.icon-dash-card-title {
font-size: 13px;
}
.icon-dash-card-desc {
display: none;
}
}
@media (max-width: 480px) {
.icon-dash-grid {
grid-template-columns: 1fr;
}
}
</style>
{% endblock %}
{% block javaScript %}
{# ── Dashboard stat tile counters ── #}
<script type="text/javascript" nonce="{{ cspNonce }}">
(function() {
'use strict';
var $ = window.jQuery;
if (!$) return;
function fetchCount(url, elId, key) {
$.ajax({
url: url,
type: 'GET',
dataType: 'json',
data: { start: 0, length: 1 },
success: function(resp) {
var count = 0;
if (resp && typeof resp.recordsTotal !== 'undefined') {
count = resp.recordsTotal;
} else if (resp && Array.isArray(resp.data)) {
count = resp.data.length;
} else if (resp && typeof resp.total !== 'undefined') {
count = resp.total;
}
var el = document.getElementById(elId);
if (el) el.textContent = count.toLocaleString();
},
error: function() {
var el = document.getElementById(elId);
if (el) el.textContent = '—';
}
});
}
$(function() {
{% if currentUser.featureEnabled("displays.view") %}
fetchCount('{{ url_for("display.search") }}', 'ots-stat-displays');
{% endif %}
{% if currentUser.featureEnabled("layout.view") %}
fetchCount('{{ url_for("layout.search") }}', 'ots-stat-layouts');
{% endif %}
{% if currentUser.featureEnabled("library.view") %}
fetchCount('{{ url_for("library.search") }}', 'ots-stat-media');
{% endif %}
{% if currentUser.featureEnabled("schedule.view") %}
fetchCount('{{ url_for("schedule.search") }}', 'ots-stat-schedules');
{% endif %}
});
})();
</script>
{% endblock %}