secubox-openwrt/luci-app-network-modes/htdocs/luci-static/resources/view/network-modes/accesspoint.js

194 lines
7.8 KiB
JavaScript

'use strict';
'require view';
'require dom';
'require ui';
'require network-modes.api as api';
'require network-modes.helpers as helpers';
'require secubox-theme/theme as Theme';
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
(navigator.language ? navigator.language.split('-')[0] : 'en');
Theme.init({ language: nmLang });
function buildWifiToggle(flag, label, desc, active) {
return E('div', { 'class': 'nm-toggle' }, [
E('div', { 'class': 'nm-toggle-info' }, [
E('span', { 'class': 'nm-toggle-icon' }, '⚙️'),
E('div', {}, [
E('div', { 'class': 'nm-toggle-label' }, label),
E('div', { 'class': 'nm-toggle-desc' }, desc)
])
]),
E('div', {
'class': 'nm-toggle-switch' + (active ? ' active' : ''),
'data-setting': flag
})
]);
}
return view.extend({
title: _('Access Point Mode'),
load: function() {
return api.getApConfig();
},
render: function(data) {
var config = data || {};
var wifiTweaks = config.wifi_tweaks || {};
var hero = helpers.createHero({
icon: '📡',
title: _('Access Point Mode'),
subtitle: _('Dedicated WiFi uplink bridge with roaming optimizations, band steering, and 802.11r/k/v enhancements.'),
gradient: 'linear-gradient(135deg,#0ea5e9,#06b6d4)',
actions: [
E('button', { 'class': 'nm-btn nm-btn-primary', 'type': 'button', 'data-action': 'ap-save' }, ['💾 ', _('Save & Apply')]),
E('button', { 'class': 'nm-btn', 'type': 'button', 'data-action': 'ap-config' }, ['📝 ', _('Preview Config')])
]
});
var stats = E('div', { 'style': 'display:flex;flex-wrap:wrap;gap:12px;margin-bottom:24px;' }, [
helpers.createStatBadge({ label: _('Channel'), value: config.wifi_channel || 'AUTO' }),
helpers.createStatBadge({ label: _('Width'), value: config.wifi_htmode || 'VHT80' }),
helpers.createStatBadge({ label: _('TX Power'), value: (config.wifi_txpower || 20) + ' dBm' }),
helpers.createStatBadge({ label: _('PHY'), value: (config.wifi_phy || 'phy0').toUpperCase() })
]);
var radioSection = helpers.createSection({
title: _('Radio Parameters'),
icon: '📶',
badge: (config.wifi_driver || 'mac80211').toUpperCase(),
body: [
E('div', { 'class': 'nm-form-grid' }, [
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('WiFi Channel')),
E('select', { 'class': 'nm-select', 'id': 'wifi-channel' }, [
E('option', { 'value': 'auto', 'selected': config.wifi_channel === 'auto' }, _('Auto')),
E('optgroup', { 'label': _('2.4 GHz') },
(config.available_channels_2g || [1, 6, 11]).map(function(ch) {
return E('option', { 'value': ch, 'selected': Number(ch) === Number(config.wifi_channel) }, _('Channel ') + ch);
})
),
E('optgroup', { 'label': _('5 GHz') },
(config.available_channels_5g || [36, 40, 44, 48]).map(function(ch) {
return E('option', { 'value': ch, 'selected': Number(ch) === Number(config.wifi_channel) }, _('Channel ') + ch);
})
)
])
]),
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('Channel Width (HT mode)')),
E('select', { 'class': 'nm-select', 'id': 'wifi-htmode' },
(config.available_htmodes || ['HT20', 'HT40', 'VHT40', 'VHT80']).map(function(mode) {
return E('option', { 'value': mode, 'selected': mode === config.wifi_htmode }, mode);
})
)
])
]),
E('div', { 'class': 'nm-slider-container' }, [
E('div', { 'class': 'nm-slider-header' }, [
E('span', { 'class': 'nm-slider-label' }, _('Transmit Power')),
E('span', { 'class': 'nm-slider-value', 'id': 'txpower-value' }, (config.wifi_txpower || 20) + ' dBm')
]),
E('input', {
'type': 'range',
'class': 'nm-slider',
'id': 'wifi-txpower',
'min': '1',
'max': '30',
'value': config.wifi_txpower || 20
})
])
]
});
var optimSection = helpers.createSection({
title: _('Roaming & Optimizations'),
icon: '🚀',
body: [
buildWifiToggle('roaming_80211r', _('802.11r Fast BSS Transition'), _('Seamless roaming between APs'), wifiTweaks.roaming_80211r),
buildWifiToggle('rrm_80211k', _('802.11k Radio Resource'), _('Neighbor reports for smart handovers'), wifiTweaks.rrm_80211k),
buildWifiToggle('wnm_80211v', _('802.11v BSS Transition'), _('AP-assisted steering & WNM sleep'), wifiTweaks.wnm_80211v),
buildWifiToggle('band_steering', _('Band Steering'), _('Prefer 5 GHz when possible'), wifiTweaks.band_steering),
buildWifiToggle('airtime_fairness', _('Airtime Fairness'), _('Equal airtime for all clients'), wifiTweaks.airtime_fairness),
buildWifiToggle('beamforming', _('Beamforming'), _('Directional signal shaping'), wifiTweaks.beamforming !== false)
]
});
var bridgeSection = helpers.createSection({
title: _('Bridging & Uplink'),
icon: '🌉',
body: [
helpers.createList([
{ title: _('Bridge LAN to upstream'), description: _('LAN + uplink ports merged in br-lan'), suffix: E('span', { 'class': 'nm-badge' }, 'L2 Bridge') },
{ title: _('Disable NAT & DHCP'), description: _('Delegated gateway handles routing & IP leases'), suffix: E('span', { 'class': 'nm-badge' }, 'Policy') },
{ title: _('Multicast optimizations'), description: _('IGMP/MLD snooping for IPTV/AP isolation'), suffix: E('span', { 'class': 'nm-badge' }, 'Optimized') }
])
]
});
var container = E('div', { 'class': 'network-modes-dashboard accesspoint-mode' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('network-modes/common.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('network-modes/dashboard.css') }),
helpers.createNavigationTabs('accesspoint'),
hero,
stats,
radioSection,
optimSection,
bridgeSection
]);
container.querySelectorAll('.nm-toggle-switch').forEach(function(toggle) {
toggle.addEventListener('click', function() {
this.classList.toggle('active');
});
});
this.bindAccessPointActions(container);
return container;
},
bindAccessPointActions: function(container) {
var slider = container.querySelector('#wifi-txpower');
var valueDisplay = container.querySelector('#txpower-value');
if (slider && valueDisplay) {
slider.addEventListener('input', function() {
valueDisplay.textContent = this.value + ' dBm';
});
}
var saveBtn = container.querySelector('[data-action="ap-save"]');
var configBtn = container.querySelector('[data-action="ap-config"]');
if (saveBtn)
saveBtn.addEventListener('click', ui.createHandlerFn(this, 'saveAccessPointSettings', container));
if (configBtn)
configBtn.addEventListener('click', ui.createHandlerFn(helpers, helpers.showGeneratedConfig, 'accesspoint'));
},
saveAccessPointSettings: function(container) {
var toggles = {};
container.querySelectorAll('.nm-toggle-switch[data-setting]').forEach(function(toggle) {
var key = toggle.getAttribute('data-setting');
toggles[key] = helpers.isToggleActive(toggle);
});
var payload = {
wifi_channel: container.querySelector('#wifi-channel') ? container.querySelector('#wifi-channel').value : 'auto',
wifi_htmode: container.querySelector('#wifi-htmode') ? container.querySelector('#wifi-htmode').value : 'VHT80',
wifi_txpower: container.querySelector('#wifi-txpower') ? parseInt(container.querySelector('#wifi-txpower').value, 10) : 20,
roaming_enabled: toggles.roaming_80211r ? 1 : 0,
band_steering: toggles.band_steering ? 1 : 0,
rrm_enabled: toggles.rrm_80211k ? 1 : 0,
wnm_enabled: toggles.wnm_80211v ? 1 : 0,
airtime_fairness: toggles.airtime_fairness ? 1 : 0,
beamforming: toggles.beamforming ? 1 : 0
};
return helpers.persistSettings('accesspoint', payload);
}
});