versheaders

This commit is contained in:
CyberMind-FR 2025-12-29 07:51:33 +01:00
parent bd96ab1d31
commit 76955f48d0
24 changed files with 619 additions and 98 deletions

View File

@ -169,6 +169,8 @@ graph TB
**REQUIREMENT:** Every module view MUST begin with this compact `.sh-page-header`. Do not introduce bespoke hero sections or oversized banners; the header keeps height predictable (title + subtitle on the left, stats on the right) and guarantees consistency across SecuBox dashboards. If no stats are needed, keep the container but supply an empty `.sh-stats-grid` for future metrics.
**Slim variant:** When the page only needs 23 metrics, use `.sh-page-header-lite` + `.sh-header-chip` (see `luci-app-vhost-manager` and `luci-app-secubox` settings). Chips carry an emoji/icon, a tiny label, and the value; colors (`.success`, `.danger`, `.warn`) communicate state. This variant replaces the bulky hero blocks from older demos.
**HTML Structure:**
```javascript
E('div', { 'class': 'sh-page-header' }, [

View File

@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-cdn-cache
PKG_VERSION:=0.4.1
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>

View File

@ -431,3 +431,79 @@ pre {
grid-template-columns: 1fr;
}
}
/* Slim header utility */
.sh-page-header-lite {
background: #f7f9fc;
border: 1px solid rgba(148, 163, 184, 0.35);
border-radius: 18px;
padding: 14px 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
}
.sh-header-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.sh-header-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.3);
font-size: 13px;
font-weight: 600;
color: var(--sh-text-secondary);
}
.sh-header-chip strong {
display: block;
color: var(--sh-text-primary);
font-size: 14px;
}
.sh-chip-text {
line-height: 1.1;
}
.sh-chip-label {
display: block;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sh-text-muted, #94a3b8);
}
.sh-chip-icon {
font-size: 16px;
}
.sh-header-chip.success {
background: rgba(34, 197, 94, 0.12);
border-color: rgba(34, 197, 94, 0.4);
color: #15803d;
}
.sh-header-chip.danger {
background: rgba(239, 68, 68, 0.12);
border-color: rgba(239, 68, 68, 0.45);
color: #b91c1c;
}
.sh-header-chip.warn {
background: rgba(245, 158, 11, 0.12);
border-color: rgba(245, 158, 11, 0.45);
color: #b45309;
}

View File

@ -82,12 +82,12 @@ return view.extend({
renderHeader: function(status) {
var stats = [
{ label: _('Service'), value: status.running ? _('Running') : _('Stopped') },
{ label: _('Uptime'), value: formatUptime(status.uptime || 0) },
{ label: _('Cache files'), value: (status.cache_files || 0).toLocaleString() }
{ icon: '🟢', label: _('Service'), value: status.running ? _('Running') : _('Stopped'), tone: status.running ? 'success' : 'danger' },
{ icon: '⏱', label: _('Uptime'), value: formatUptime(status.uptime || 0) },
{ icon: '📁', label: _('Cache files'), value: (status.cache_files || 0).toLocaleString() }
];
return E('div', { 'class': 'sh-page-header' }, [
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '📦'),
@ -96,14 +96,17 @@ return view.extend({
E('p', { 'class': 'sh-page-subtitle' },
_('Edge caching for media, firmware, and downloads'))
]),
E('div', { 'class': 'sh-stats-grid' }, stats.map(this.renderHeaderStat, this))
E('div', { 'class': 'sh-header-meta' }, stats.map(this.renderHeaderChip, this))
]);
},
renderHeaderStat: function(stat) {
return E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, stat.value),
E('div', { 'class': 'sh-stat-label' }, stat.label)
renderHeaderChip: function(stat) {
return E('div', { 'class': 'sh-header-chip' + (stat.tone ? ' ' + stat.tone : '') }, [
E('span', { 'class': 'sh-chip-icon' }, stat.icon || '•'),
E('div', { 'class': 'sh-chip-text' }, [
E('span', { 'class': 'sh-chip-label' }, stat.label),
E('strong', {}, stat.value)
])
]);
},

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-network-modes
PKG_VERSION:=0.4.6
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>

View File

@ -431,3 +431,79 @@ pre {
grid-template-columns: 1fr;
}
}
/* Slim header utility */
.sh-page-header-lite {
background: #f7f9fc;
border: 1px solid rgba(148, 163, 184, 0.35);
border-radius: 18px;
padding: 14px 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
}
.sh-header-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.sh-header-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.3);
font-size: 13px;
font-weight: 600;
color: var(--sh-text-secondary);
}
.sh-header-chip strong {
display: block;
color: var(--sh-text-primary);
font-size: 14px;
}
.sh-chip-text {
line-height: 1.1;
}
.sh-chip-label {
display: block;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sh-text-muted, #94a3b8);
}
.sh-chip-icon {
font-size: 16px;
}
.sh-header-chip.success {
background: rgba(34, 197, 94, 0.12);
border-color: rgba(34, 197, 94, 0.4);
color: #15803d;
}
.sh-header-chip.danger {
background: rgba(239, 68, 68, 0.12);
border-color: rgba(239, 68, 68, 0.45);
color: #b91c1c;
}
.sh-header-chip.warn {
background: rgba(245, 158, 11, 0.12);
border-color: rgba(245, 158, 11, 0.45);
color: #b45309;
}

View File

@ -367,12 +367,12 @@ return view.extend({
renderHeader: function(status, currentModeInfo) {
var modeName = currentModeInfo ? currentModeInfo.name : (status.current_mode || 'router');
var stats = [
{ label: _('Mode'), value: modeName },
{ label: _('WAN IP'), value: status.wan_ip || _('Unknown') },
{ label: _('LAN IP'), value: status.lan_ip || _('Unknown') }
{ label: _('Mode'), value: modeName, icon: '🧭' },
{ label: _('WAN IP'), value: status.wan_ip || _('Unknown'), icon: '🌍' },
{ label: _('LAN IP'), value: status.lan_ip || _('Unknown'), icon: '🏠' }
];
return E('div', { 'class': 'sh-page-header' }, [
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '🌐'),
@ -381,14 +381,17 @@ return view.extend({
E('p', { 'class': 'sh-page-subtitle' },
_('Switch between curated router, bridge, relay, and travel modes.'))
]),
E('div', { 'class': 'sh-stats-grid' }, stats.map(this.renderHeaderStat, this))
E('div', { 'class': 'sh-header-meta' }, stats.map(this.renderHeaderChip, this))
]);
},
renderHeaderStat: function(stat) {
return E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, stat.value || '-'),
E('div', { 'class': 'sh-stat-label' }, stat.label)
renderHeaderChip: function(stat) {
return E('div', { 'class': 'sh-header-chip' }, [
E('span', { 'class': 'sh-chip-icon' }, stat.icon || '•'),
E('div', { 'class': 'sh-chip-text' }, [
E('span', { 'class': 'sh-chip-label' }, stat.label),
E('strong', {}, stat.value || '-')
])
]);
},

View File

@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-secubox
PKG_VERSION:=0.4.6
PKG_RELEASE:=3
PKG_RELEASE:=4
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>

View File

@ -431,3 +431,79 @@ pre {
grid-template-columns: 1fr;
}
}
/* Slim header utility */
.sh-page-header-lite {
background: #f7f9fc;
border: 1px solid rgba(148, 163, 184, 0.35);
border-radius: 18px;
padding: 14px 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
}
.sh-header-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.sh-header-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.3);
font-size: 13px;
font-weight: 600;
color: var(--sh-text-secondary);
}
.sh-header-chip strong {
display: block;
color: var(--sh-text-primary);
font-size: 14px;
}
.sh-chip-text {
line-height: 1.1;
}
.sh-chip-label {
display: block;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sh-text-muted, #94a3b8);
}
.sh-chip-icon {
font-size: 16px;
}
.sh-header-chip.success {
background: rgba(34, 197, 94, 0.12);
border-color: rgba(34, 197, 94, 0.4);
color: #15803d;
}
.sh-header-chip.danger {
background: rgba(239, 68, 68, 0.12);
border-color: rgba(239, 68, 68, 0.45);
color: #b91c1c;
}
.sh-header-chip.warn {
background: rgba(245, 158, 11, 0.12);
border-color: rgba(245, 158, 11, 0.45);
color: #b45309;
}

View File

@ -43,6 +43,7 @@ return view.extend({
render: function(data) {
var self = this;
var container = E('div', { 'class': 'secubox-alerts-page' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
SecuNav.renderTabs('alerts'),
this.renderHeader(),
this.renderControls(),

View File

@ -48,6 +48,7 @@ return view.extend({
render: function() {
var container = E('div', { 'class': 'secubox-dashboard' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/dashboard.css') }),
SecuNav.renderTabs('dashboard'),
this.renderHeader(),

View File

@ -45,6 +45,7 @@ return view.extend({
var modules = this.modulesData;
var container = E('div', { 'class': 'secubox-modules-page' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
SecuNav.renderTabs('modules'),
this.renderHeader(modules),
this.renderFilterTabs(),

View File

@ -55,6 +55,7 @@ return view.extend({
render: function() {
var container = E('div', { 'class': 'secubox-monitoring-page' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/secubox.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/monitoring.css') }),
SecuNav.renderTabs('monitoring'),

View File

@ -28,8 +28,7 @@ return view.extend({
SecuNav.renderTabs('settings'),
// Modern header
E('div', { 'class': 'sh-page-header' }, [
E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '⚙️'),
@ -38,24 +37,25 @@ return view.extend({
E('p', { 'class': 'sh-page-subtitle' },
'Configure global settings for the SecuBox security suite')
]),
E('div', { 'class': 'sh-stats-grid' }, [
E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, status.version || 'v0.1.2'),
E('div', { 'class': 'sh-stat-label' }, 'Version')
]),
E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value', 'style': status.enabled ? 'color: #22c55e;' : 'color: #ef4444;' },
status.enabled ? 'ON' : 'OFF'),
E('div', { 'class': 'sh-stat-label' }, 'Status')
]),
E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, status.modules_count || '14'),
E('div', { 'class': 'sh-stat-label' }, 'Modules')
])
E('div', { 'class': 'sh-header-meta' }, [
this.renderHeaderChip('🏷️', _('Version'), status.version || '0.1.2'),
this.renderHeaderChip('⚡', _('Status'), status.enabled ? _('On') : _('Off'), status.enabled ? 'success' : 'danger'),
this.renderHeaderChip('🧩', _('Modules'), status.modules_count || '14')
])
])
]);
renderHeaderChip: function(icon, label, value, tone) {
var display = (value == null ? '—' : value).toString();
return E('div', { 'class': 'sh-header-chip' + (tone ? ' ' + tone : '') }, [
E('span', { 'class': 'sh-chip-icon' }, icon),
E('div', { 'class': 'sh-chip-text' }, [
E('span', { 'class': 'sh-chip-label' }, label),
E('strong', {}, display)
])
]);
},
// Create form
m = new form.Map('secubox', null, null);

View File

@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-system-hub
PKG_VERSION:=0.4.6
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>

View File

@ -549,3 +549,79 @@ pre {
background: var(--sh-bg-secondary);
border-color: var(--sh-border);
}
/* Slim header utility */
.sh-page-header-lite {
background: #f7f9fc;
border: 1px solid rgba(148, 163, 184, 0.35);
border-radius: 18px;
padding: 14px 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
}
.sh-header-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.sh-header-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.3);
font-size: 13px;
font-weight: 600;
color: var(--sh-text-secondary);
}
.sh-header-chip strong {
display: block;
color: var(--sh-text-primary);
font-size: 14px;
}
.sh-chip-text {
line-height: 1.1;
}
.sh-chip-label {
display: block;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sh-text-muted, #94a3b8);
}
.sh-chip-icon {
font-size: 16px;
}
.sh-header-chip.success {
background: rgba(34, 197, 94, 0.12);
border-color: rgba(34, 197, 94, 0.4);
color: #15803d;
}
.sh-header-chip.danger {
background: rgba(239, 68, 68, 0.12);
border-color: rgba(239, 68, 68, 0.45);
color: #b91c1c;
}
.sh-header-chip.warn {
background: rgba(245, 158, 11, 0.12);
border-color: rgba(245, 158, 11, 0.45);
color: #b45309;
}

View File

@ -29,10 +29,13 @@ return view.extend({
HubNav.renderTabs('components'),
E('div', { 'class': 'sh-components-header' }, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-title-icon' }, '🧩'),
' System Components'
E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '🧩'),
_('System Components')
]),
E('p', { 'class': 'sh-page-subtitle' }, _('Installed modules grouped by category'))
]),
this.renderFilterTabs()
]),
@ -99,9 +102,17 @@ return view.extend({
},
renderComponentsGrid: function(components, filter) {
var list = components.slice().sort(function(a, b) {
if ((a.installed ? 1 : 0) !== (b.installed ? 1 : 0))
return a.installed ? -1 : 1;
if ((a.running ? 1 : 0) !== (b.running ? 1 : 0))
return a.running ? -1 : 1;
return (a.name || '').localeCompare(b.name || '');
});
var filtered = filter === 'all'
? components
: components.filter(function(c) { return c.category === filter; });
? list
: list.filter(function(c) { return c.category === filter; });
if (filtered.length === 0) {
return E('div', { 'class': 'sh-empty-state' }, [

View File

@ -61,13 +61,13 @@ return view.extend({
var score = (this.healthData.score || 0);
var stats = [
{ label: _('Uptime'), value: uptime },
{ label: _('Hostname'), value: hostname },
{ label: _('Kernel'), value: kernel, copy: kernel },
{ label: _('Health'), value: score + '/100' }
{ label: _('Uptime'), value: uptime, icon: '⏱' },
{ label: _('Hostname'), value: hostname, icon: '🖥' },
{ label: _('Kernel'), value: kernel, copy: kernel, icon: '🧬' },
{ label: _('Health'), value: score + '/100', icon: '❤️' }
];
return E('div', { 'class': 'sh-page-header' }, [
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '⚙️'),
@ -75,26 +75,29 @@ return view.extend({
]),
E('p', { 'class': 'sh-page-subtitle' }, _('Unified telemetry & orchestration'))
]),
E('div', { 'class': 'sh-stats-grid' }, stats.map(this.renderHeaderStat, this))
E('div', { 'class': 'sh-header-meta' }, stats.map(this.renderHeaderChip, this))
]);
},
renderHeaderStat: function(stat) {
var badge = E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, stat.value || '-'),
E('div', { 'class': 'sh-stat-label' }, stat.label)
renderHeaderChip: function(stat) {
var chip = E('div', { 'class': 'sh-header-chip' }, [
E('span', { 'class': 'sh-chip-icon' }, stat.icon || '•'),
E('div', { 'class': 'sh-chip-text' }, [
E('span', { 'class': 'sh-chip-label' }, stat.label),
E('strong', {}, stat.value || '-')
])
]);
if (stat.copy && navigator.clipboard) {
badge.style.cursor = 'pointer';
badge.addEventListener('click', function() {
chip.style.cursor = 'pointer';
chip.addEventListener('click', function() {
navigator.clipboard.writeText(stat.copy).then(function() {
ui.addNotification(null, E('p', {}, _('Copied to clipboard')), 'info');
});
});
}
return badge;
return chip;
},
renderInfoGrid: function() {

View File

@ -58,16 +58,29 @@ return view.extend({
renderHeader: function() {
var stats = this.getStats();
return E('section', { 'class': 'sh-services-hero' }, [
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h1', {}, _('Service Control Center')),
E('p', {}, _('Start, stop, enable, and inspect all init.d services'))
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '🧩'),
_('Service Control Center')
]),
E('p', { 'class': 'sh-page-subtitle' }, _('Start, stop, enable, and inspect all init.d services'))
]),
E('div', { 'class': 'sh-services-stats', 'id': 'sh-services-stats' }, [
this.createStatCard('sh-stat-total', _('Total'), stats.total),
this.createStatCard('sh-stat-running', _('Running'), stats.running, 'success'),
this.createStatCard('sh-stat-stopped', _('Stopped'), stats.stopped, 'danger'),
this.createStatCard('sh-stat-enabled', _('Enabled'), stats.enabled, 'info')
E('div', { 'class': 'sh-header-meta', 'id': 'sh-services-stats' }, [
this.renderHeaderChip(_('Total'), stats.total, '📦'),
this.renderHeaderChip(_('Running'), stats.running, '🟢', stats.running > 0 ? 'success' : ''),
this.renderHeaderChip(_('Enabled'), stats.enabled, '✅'),
this.renderHeaderChip(_('Stopped'), stats.stopped, '⏹️', stats.stopped > 0 ? 'danger' : '')
])
]);
},
renderHeaderChip: function(label, value, icon, tone) {
return E('div', { 'class': 'sh-header-chip' + (tone ? ' ' + tone : '') }, [
E('span', { 'class': 'sh-chip-icon' }, icon),
E('div', { 'class': 'sh-chip-text' }, [
E('span', { 'class': 'sh-chip-label' }, label),
E('strong', {}, value.toString())
])
]);
},
@ -136,7 +149,15 @@ return view.extend({
},
getFilteredServices: function() {
return this.services.filter(function(service) {
var ordered = this.services.slice().sort(function(a, b) {
if (a.running !== b.running)
return a.running ? -1 : 1;
if (a.enabled !== b.enabled)
return a.enabled ? -1 : 1;
return (a.name || '').localeCompare(b.name || '');
});
return ordered.filter(function(service) {
var matchesFilter = true;
switch (this.activeFilter) {
case 'running': matchesFilter = service.running; break;
@ -145,7 +166,7 @@ return view.extend({
case 'disabled': matchesFilter = !service.enabled; break;
}
var matchesSearch = !this.searchQuery ||
service.name.toLowerCase().includes(this.searchQuery);
(service.name || '').toLowerCase().includes(this.searchQuery);
return matchesFilter && matchesSearch;
}, this);
},

View File

@ -4,8 +4,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-vhost-manager
PKG_VERSION:=0.2.2
PKG_RELEASE:=1
PKG_VERSION:=0.4.1
PKG_RELEASE:=2
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>

View File

@ -431,3 +431,79 @@ pre {
grid-template-columns: 1fr;
}
}
/* Slim header utility */
.sh-page-header-lite {
background: #f7f9fc;
border: 1px solid rgba(148, 163, 184, 0.35);
border-radius: 18px;
padding: 14px 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
}
.sh-header-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.sh-header-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.3);
font-size: 13px;
font-weight: 600;
color: var(--sh-text-secondary);
}
.sh-header-chip strong {
display: block;
color: var(--sh-text-primary);
font-size: 14px;
}
.sh-chip-text {
line-height: 1.1;
}
.sh-chip-label {
display: block;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sh-text-muted, #94a3b8);
}
.sh-chip-icon {
font-size: 16px;
}
.sh-header-chip.success {
background: rgba(34, 197, 94, 0.12);
border-color: rgba(34, 197, 94, 0.4);
color: #15803d;
}
.sh-header-chip.danger {
background: rgba(239, 68, 68, 0.12);
border-color: rgba(239, 68, 68, 0.45);
color: #b91c1c;
}
.sh-header-chip.warn {
background: rgba(245, 158, 11, 0.12);
border-color: rgba(245, 158, 11, 0.45);
color: #b45309;
}

View File

@ -69,7 +69,7 @@ return view.extend({
return days !== null && days <= 30;
}).length;
return E('div', { 'class': 'sh-page-header' }, [
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '🌐'),
@ -78,18 +78,23 @@ return view.extend({
E('p', { 'class': 'sh-page-subtitle' },
_('Reverse proxy, SSL automation and hardened headers for SecuBox deployments.'))
]),
E('div', { 'class': 'sh-stats-grid' }, [
this.renderStatBadge(status.vhost_count || vhosts.length, _('Virtual Hosts')),
this.renderStatBadge(sslEnabled, _('TLS Enabled')),
this.renderStatBadge(expiringSoon, _('Expiring Certs'))
E('div', { 'class': 'sh-header-meta' }, [
this.renderHeaderChip('🏷️', _('Version'), status.version || '0.4.1'),
this.renderHeaderChip('📁', _('Virtual Hosts'), (status.vhost_count || vhosts.length)),
this.renderHeaderChip('🔒', _('TLS Enabled'), sslEnabled),
this.renderHeaderChip('⏳', _('Expiring'), expiringSoon, expiringSoon > 0 ? 'warn' : '')
])
]);
},
renderStatBadge: function(value, label) {
return E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, value.toString()),
E('div', { 'class': 'sh-stat-label' }, label)
renderHeaderChip: function(icon, label, value, tone) {
var display = (value == null ? '—' : value).toString();
return E('div', { 'class': 'sh-header-chip' + (tone ? ' ' + tone : '') }, [
E('span', { 'class': 'sh-chip-icon' }, icon),
E('div', { 'class': 'sh-chip-text' }, [
E('span', { 'class': 'sh-chip-label' }, label),
E('strong', {}, display)
])
]);
},

View File

@ -187,7 +187,7 @@ case "$1" in
json_init
json_add_boolean "enabled" 1
json_add_string "module" "vhost-manager"
json_add_string "version" "1.0.0"
json_add_string "version" "0.4.1"
# Check nginx status
if pgrep -x nginx > /dev/null 2>&1; then

View File

@ -247,53 +247,66 @@ pre {
/* === Navigation Tabs === */
.sh-nav-tabs {
display: flex;
gap: 8px;
gap: 6px;
margin-bottom: 24px;
padding: 8px;
background: var(--sh-bg-secondary);
border-radius: 12px;
border: 1px solid var(--sh-border);
padding: 12px;
background: #f7f9fc;
border-radius: 18px;
border: 1px solid rgba(148, 163, 184, 0.35);
box-shadow: 0 12px 35px rgba(15, 23, 42, 0.08);
align-items: center;
flex-wrap: wrap;
position: sticky;
top: 0;
z-index: 100;
backdrop-filter: blur(10px);
}
.sh-nav-tab {
padding: 10px 20px;
border-radius: 8px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
border-radius: 12px;
background: transparent;
border: none;
border: 1px solid transparent;
color: var(--sh-text-secondary);
font-weight: 500;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transition: all 0.25s ease;
position: relative;
font-size: 14px;
}
.sh-nav-tab:hover {
color: var(--sh-text-primary);
background: var(--sh-hover-bg);
background: rgba(99, 102, 241, 0.08);
}
.sh-nav-tab.active {
color: var(--sh-primary);
background: var(--sh-bg-card);
box-shadow: 0 2px 4px var(--sh-shadow);
background: #ffffff;
border-color: rgba(99, 102, 241, 0.25);
box-shadow: 0 6px 14px rgba(99, 102, 241, 0.15);
}
.sh-nav-tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 20%;
right: 20%;
height: 2px;
left: 18px;
right: 18px;
bottom: 6px;
height: 3px;
background: linear-gradient(90deg, var(--sh-primary), var(--sh-primary-end));
border-radius: 2px;
border-radius: 3px;
}
.sh-tab-icon {
font-size: 16px;
width: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* === Filter Tabs === */
.sh-filter-tabs {
display: flex;
@ -431,3 +444,79 @@ pre {
grid-template-columns: 1fr;
}
}
/* Slim header utility */
.sh-page-header-lite {
background: #f7f9fc;
border: 1px solid rgba(148, 163, 184, 0.35);
border-radius: 18px;
padding: 14px 20px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
}
.sh-header-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.sh-header-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.3);
font-size: 13px;
font-weight: 600;
color: var(--sh-text-secondary);
}
.sh-header-chip strong {
display: block;
color: var(--sh-text-primary);
font-size: 14px;
}
.sh-chip-text {
line-height: 1.1;
}
.sh-chip-label {
display: block;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sh-text-muted, #94a3b8);
}
.sh-chip-icon {
font-size: 16px;
}
.sh-header-chip.success {
background: rgba(34, 197, 94, 0.12);
border-color: rgba(34, 197, 94, 0.4);
color: #15803d;
}
.sh-header-chip.danger {
background: rgba(239, 68, 68, 0.12);
border-color: rgba(239, 68, 68, 0.45);
color: #b91c1c;
}
.sh-header-chip.warn {
background: rgba(245, 158, 11, 0.12);
border-color: rgba(245, 158, 11, 0.45);
color: #b45309;
}