secubox-openwrt/package/secubox/luci-app-client-guardian/htdocs/luci-static/resources/view/client-guardian/zones.js
CyberMind-FR 7df952c2a7 feat: Add SecuBox portal header to Client Guardian, Media Flow, and Netdata views
Adds the unified SecuBox portal header navigation to:
- Client Guardian: overview, clients, zones, logs, alerts, parental, settings
- Media Flow: dashboard
- Netdata Dashboard: dashboard, settings

This hides the LuCI sidebar and provides consistent SecuBox navigation
across all dashboards when accessed from the SecuBox Portal.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 15:33:14 +01:00

247 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
'require view';
'require dom';
'require ui';
'require rpc';
'require client-guardian/nav as CgNav';
'require secubox-portal/header as SbHeader';
var callGetZones = rpc.declare({
object: 'luci.client-guardian',
method: 'zones',
expect: { zones: [] }
});
var callUpdateZone = rpc.declare({
object: 'luci.client-guardian',
method: 'update_zone',
params: ['id', 'name', 'bandwidth_limit', 'content_filter']
});
var callSyncZones = rpc.declare({
object: 'luci.client-guardian',
method: 'sync_zones'
});
return view.extend({
load: function() {
return callGetZones();
},
render: function(data) {
var zones = Array.isArray(data) ? data : (data.zones || []);
var self = this;
// Main wrapper with SecuBox header
var wrapper = E('div', { 'class': 'secubox-page-wrapper' });
wrapper.appendChild(SbHeader.render());
var view = E('div', { 'class': 'client-guardian-dashboard' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('client-guardian/dashboard.css') }),
CgNav.renderTabs('zones'),
E('div', { 'class': 'cg-header' }, [
E('div', { 'class': 'cg-logo' }, [
E('div', { 'class': 'cg-logo-icon' }, '🌐'),
E('div', { 'class': 'cg-logo-text' }, 'Zones Réseau')
]),
E('button', {
'class': 'cg-btn cg-btn-primary',
'click': L.bind(this.handleSyncZones, this),
'style': 'display: flex; align-items: center; gap: 8px;'
}, [
E('span', {}, '🔄'),
'Synchroniser Firewall'
])
]),
E('p', { 'style': 'color: var(--cg-text-secondary); margin-bottom: 24px' },
'Définissez les zones de sécurité avec leurs règles d\'accès, filtrage et limitations. Cliquez sur "Synchroniser Firewall" pour créer les zones dans la configuration firewall.'
),
E('div', { 'class': 'cg-zones-grid' },
zones.map(L.bind(this.renderZoneCard, this))
)
]);
wrapper.appendChild(view);
return wrapper;
},
renderZoneCard: function(zone) {
var self = this;
var color = zone.color || '#8a7575';
var icon = this.getZoneIcon(zone.icon);
var features = [];
if (zone.internet_access) features.push({ name: 'Internet', enabled: true });
else features.push({ name: 'Internet', enabled: false });
if (zone.local_access) features.push({ name: 'Local', enabled: true });
else features.push({ name: 'Local', enabled: false });
if (zone.inter_client) features.push({ name: 'Inter-client', enabled: true });
if (zone.time_restrictions) features.push({ name: 'Horaires', enabled: true });
if (zone.content_filter && zone.content_filter !== 'none')
features.push({ name: 'Filtrage', enabled: true });
if (zone.portal_required) features.push({ name: 'Portail', enabled: true });
if (zone.bandwidth_limit > 0)
features.push({ name: zone.bandwidth_limit + ' Mbps', enabled: true });
return E('div', {
'class': 'cg-zone-card',
'style': '--zone-color: ' + color
}, [
E('div', { 'class': 'cg-zone-header' }, [
E('div', { 'class': 'cg-zone-icon' }, icon),
E('div', {}, [
E('div', { 'class': 'cg-zone-title' }, zone.name),
E('div', { 'class': 'cg-zone-subtitle' }, zone.description || '')
])
]),
E('div', { 'class': 'cg-zone-features' },
features.map(function(f) {
return E('span', {
'class': 'cg-zone-feature ' + (f.enabled ? 'enabled' : 'disabled')
}, f.name);
})
),
E('div', { 'class': 'cg-zone-stats' }, [
E('div', { 'class': 'cg-zone-stat' }, [
E('div', { 'class': 'cg-zone-stat-value' }, String(zone.client_count || 0)),
E('div', { 'class': 'cg-zone-stat-label' }, 'Clients')
]),
E('div', { 'class': 'cg-zone-stat' }, [
E('div', { 'class': 'cg-zone-stat-value' }, zone.internet_access ? '✅' : '❌'),
E('div', { 'class': 'cg-zone-stat-label' }, 'Internet')
])
]),
E('button', {
'class': 'cg-btn',
'style': 'width: 100%; margin-top: 16px; justify-content: center',
'click': L.bind(this.handleEditZone, this, zone)
}, [
E('span', {}, ''),
' Configurer'
])
]);
},
getZoneIcon: function(icon) {
var icons = {
'home': '🏠',
'cpu': '🔧',
'child': '👶',
'users': '👥',
'shield-alert': '',
'ban': '🚫'
};
return icons[icon] || '🌐';
},
handleEditZone: function(zone, ev) {
var self = this;
ui.showModal(_('Configurer Zone: ') + zone.name, [
E('div', { 'class': 'cg-form-group' }, [
E('label', { 'class': 'cg-form-label' }, 'Limite de bande passante (Mbps, 0=illimité)'),
E('input', {
'type': 'number',
'id': 'zone-bandwidth',
'class': 'cg-input',
'value': zone.bandwidth_limit || '0'
})
]),
E('div', { 'class': 'cg-form-group' }, [
E('label', { 'class': 'cg-form-label' }, 'Filtre de contenu'),
E('select', { 'id': 'zone-filter', 'class': 'cg-input' }, [
E('option', { 'value': 'none', 'selected': zone.content_filter === 'none' }, 'Aucun'),
E('option', { 'value': 'kids', 'selected': zone.content_filter === 'kids' }, 'Enfants (strict)'),
E('option', { 'value': 'adult', 'selected': zone.content_filter === 'adult' }, 'Adulte (malware/phishing)'),
E('option', { 'value': 'strict', 'selected': zone.content_filter === 'strict' }, 'Strict (whitelist)')
])
]),
zone.time_restrictions ? E('div', {}, [
E('div', { 'class': 'cg-form-group' }, [
E('label', { 'class': 'cg-form-label' }, 'Horaires autorisés'),
E('div', { 'style': 'display: flex; gap: 12px; align-items: center' }, [
E('input', {
'type': 'time',
'id': 'zone-start',
'class': 'cg-input',
'style': 'width: auto',
'value': zone.schedule_start || '08:00'
}),
E('span', { 'style': 'color: var(--cg-text-muted)' }, 'à'),
E('input', {
'type': 'time',
'id': 'zone-end',
'class': 'cg-input',
'style': 'width: auto',
'value': zone.schedule_end || '21:00'
})
])
])
]) : E('span'),
E('div', { 'class': 'cg-btn-group', 'style': 'justify-content: flex-end; margin-top: 20px' }, [
E('button', { 'class': 'cg-btn', 'click': ui.hideModal }, _('Annuler')),
E('button', { 'class': 'cg-btn cg-btn-primary', 'click': L.bind(function() {
callUpdateZone(
zone.id,
zone.name,
parseInt(document.getElementById('zone-bandwidth').value) || 0,
document.getElementById('zone-filter').value
).then(L.bind(function() {
ui.hideModal();
ui.addNotification(null, E('p', _('Zone updated successfully')), 'success');
this.handleRefresh();
}, this));
}, this)}, _('Enregistrer'))
])
]);
},
handleSyncZones: function(ev) {
var btn = ev.currentTarget;
btn.disabled = true;
btn.innerHTML = '<span></span> Synchronisation...';
callSyncZones().then(function(result) {
if (result.success) {
ui.addNotification(null, E('p', {}, 'Zones firewall synchronisées avec succès'), 'success');
btn.innerHTML = '<span>✅</span> Synchronisé';
setTimeout(function() {
btn.disabled = false;
btn.innerHTML = '<span>🔄</span> Synchroniser Firewall';
}, 2000);
} else {
ui.addNotification(null, E('p', {}, 'Erreur lors de la synchronisation'), 'error');
btn.disabled = false;
btn.innerHTML = '<span>🔄</span> Synchroniser Firewall';
}
}).catch(function(err) {
ui.addNotification(null, E('p', {}, 'Erreur: ' + err), 'error');
btn.disabled = false;
btn.innerHTML = '<span>🔄</span> Synchroniser Firewall';
});
},
handleRefresh: function() {
return callGetZones().then(L.bind(function(data) {
var container = document.querySelector('.client-guardian-dashboard');
if (container) {
var newView = this.render(data);
dom.content(container.parentNode, newView);
}
}, this)).catch(function(err) {
console.error('Failed to refresh zones list:', err);
});
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});