Enhance OTS Signage Theme with Icon Dashboard and UI Improvements

- Updated theme.js to refine dropdown initialization, excluding user menu handling.
- Modified authed.twig to allow CSS variable theming for background and text colors, and adjusted content wrapper classes based on navigation state.
- Adjusted override-styles.twig to improve layout responsiveness and ensure proper styling for horizontal navigation mode.
- Enhanced theme-scripts.twig to improve user menu functionality, including repositioning and click handling.
- Introduced a new dashboard-icon-page.twig for a stylized icon dashboard, featuring card-based buttons for quick access to various functionalities, along with custom styles for better user experience.
This commit is contained in:
Matt Batchelder
2026-02-09 18:47:14 -05:00
parent 86030cb881
commit 54e092ec01
7 changed files with 1820 additions and 103 deletions

View File

@@ -0,0 +1,461 @@
{#
/**
* 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>
{# ── Scheduling ────────────────────────────────────────────── #}
{% set scheduleCount = currentUser.featureEnabledCount(["schedule.view", "daypart.view"]) %}
{% if scheduleCount > 0 %}
<div class="icon-dash-section">
<h3 class="section-title"><i class="fa fa-calendar"></i> {% trans "Scheduling" %}</h3>
<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 event schedules" %}</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 slots" %}</span>
</div>
</a>
{% endif %}
</div>
</div>
{% endif %}
{# ── Design ────────────────────────────────────────────────── #}
{% set countViewable = currentUser.featureEnabledCount(["campaign.view", "layout.view", "template.view", "resolution.view"]) %}
{% if countViewable > 0 %}
<div class="icon-dash-section">
<h3 class="section-title"><i class="fa fa-paint-brush"></i> {% trans "Design" %}</h3>
<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 layouts" %}</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 templates" %}</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 resolution presets" %}</span>
</div>
</a>
{% endif %}
</div>
</div>
{% endif %}
{# ── Library ───────────────────────────────────────────────── #}
{% set countViewable = currentUser.featureEnabledCount(["library.view", "playlist.view", "dataset.view", "menuBoard.view"]) %}
{% if countViewable > 0 %}
<div class="icon-dash-section">
<h3 class="section-title"><i class="fa fa-picture-o"></i> {% trans "Library" %}</h3>
<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 & 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 playlists" %}</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 "Tabular 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 management" %}</span>
</div>
</a>
{% endif %}
</div>
</div>
{% endif %}
{# ── Displays ──────────────────────────────────────────────── #}
{% set countViewable = currentUser.featureEnabledCount(["displays.view", "displaygroup.view", "displayprofile.view"]) %}
{% if countViewable > 0 %}
<div class="icon-dash-section">
<h3 class="section-title"><i class="fa fa-desktop"></i> {% trans "Displays" %}</h3>
<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 "Manage all 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 "Organise screen groups" %}</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 "Player configuration profiles" %}</span>
</div>
</a>
{% endif %}
</div>
</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">
<h3 class="section-title"><i class="fa fa-cogs"></i> {% trans "Administration" %}</h3>
<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 "User accounts & permissions" %}</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 "CMS system configuration" %}</span>
</div>
</a>
{% endif %}
</div>
</div>
{% endif %}
<style>
/* ===================================================================
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;
}
/* 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;
}
/* Subtle radial glow matching kpi-card--modern */
.icon-dash-card::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at top right, rgba(59, 130, 246, 0.10), transparent 60%);
pointer-events: none;
transition: opacity 0.3s ease;
opacity: 0;
}
.icon-dash-card:hover::after {
opacity: 1;
}
/* Icon container */
.icon-dash-card-icon {
flex-shrink: 0;
width: 52px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 14px;
font-size: 22px;
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.icon-dash-card:hover .icon-dash-card-icon {
transform: scale(1.08);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}
/* Icon colour variants */
.icon-dash-card-icon--blue {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.28), rgba(59, 130, 246, 0.12));
color: #60a5fa;
}
.icon-dash-card-icon--green {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.28), rgba(16, 185, 129, 0.12));
color: #34d399;
}
.icon-dash-card-icon--orange {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.28), rgba(245, 158, 11, 0.12));
color: #fbbf24;
}
.icon-dash-card-icon--red {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.28), rgba(239, 68, 68, 0.12));
color: #f87171;
}
.icon-dash-card-icon--purple {
background: linear-gradient(135deg, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0.12));
color: #a78bfa;
}
.icon-dash-card-icon--indigo {
background: linear-gradient(135deg, rgba(99, 102, 241, 0.28), rgba(99, 102, 241, 0.12));
color: #818cf8;
}
.icon-dash-card-icon--teal {
background: linear-gradient(135deg, rgba(20, 184, 166, 0.28), rgba(20, 184, 166, 0.12));
color: #2dd4bf;
}
/* Text area */
.icon-dash-card-body {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
/* Reset inherited dashboard-card body padding */
padding: 0 !important;
background: transparent !important;
}
.icon-dash-card-title {
font-size: 15px;
font-weight: 700;
color: var(--color-text-primary);
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.icon-dash-card-desc {
font-size: 12px;
font-weight: 400;
color: var(--color-text-tertiary);
line-height: 1.4;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Hover effects matching action-card--modern */
.icon-dash-card:hover {
border-color: rgba(59, 130, 246, 0.45) !important;
transform: translateY(-3px);
box-shadow: 0 20px 40px rgba(8, 15, 30, 0.45) !important;
}
.icon-dash-card:active {
transform: translateY(0px);
box-shadow: 0 10px 20px rgba(8, 15, 30, 0.35) !important;
}
/* Section title with icon */
.icon-dash-section .section-title i {
margin-right: 8px;
opacity: 0.65;
}
/* ── Light mode overrides ─────────────────────────────────────── */
body.ots-light-mode .icon-dash-card {
background: linear-gradient(180deg, #ffffff, #f8fafc) !important;
border-color: rgba(148, 163, 184, 0.25) !important;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06) !important;
}
body.ots-light-mode .icon-dash-card:hover {
background: linear-gradient(180deg, #ffffff, #f1f5f9) !important;
border-color: rgba(59, 130, 246, 0.4) !important;
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1) !important;
}
body.ots-light-mode .icon-dash-card-desc {
color: #64748b;
}
/* ── 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: 12px;
}
.icon-dash-card-title {
font-size: 13px;
}
.icon-dash-card-desc {
display: none;
}
}
@media (max-width: 480px) {
.icon-dash-grid {
grid-template-columns: 1fr;
}
.icon-dash-card-desc {
display: block;
}
}
</style>
{% endblock %}