interfaces: overview: small usability improvements

- include VLAN (or QinQ) tag in main grid for easy sorting, tooltip will show parent device as well
- present table structure for data nested 1 level deeper in details overview
- include links to either the interface settings or firewall rules pages
This commit is contained in:
Stephan de Wit 2023-12-07 09:43:00 +01:00
parent 54d98085c2
commit c17bc785e8
2 changed files with 110 additions and 12 deletions

View File

@ -54,7 +54,8 @@ class OverviewController extends ApiControllerBase
'gateways' => gettext('Gateways'),
'routes' => gettext('Routes'),
'macaddr' => gettext('MAC Address'),
'media_raw' => gettext('Media'),
'media' => gettext('Media'),
'media_raw' => gettext('Media (Raw)'),
'mediaopt' => gettext('Media Options'),
'capabilities' => gettext('Capabilities'),
'identifier' => gettext('Identifier'),
@ -87,6 +88,14 @@ class OverviewController extends ApiControllerBase
'packets for unknown protocol' => gettext('Packets for Unknown Protocol'),
'HW offload capabilities' => gettext('Hardware Offload Capabilities'),
'uptime at attach or stat reset' => gettext('Uptime at Attach or Statistics Reset'),
'laggoptions' => gettext('LAGG Options'),
'lagghash' => gettext('LAGG Hash'),
'laggproto' => gettext('LAGG Protocol'),
'laggstatistics' => gettext('LAGG Statistics'),
'groups' => gettext('Groups'),
'active ports' => gettext('Active Ports'),
'vlan' => gettext('VLAN details'),
'vlan_tag' => gettext('VLAN Tag'),
];
}
@ -104,14 +113,13 @@ class OverviewController extends ApiControllerBase
/* detailed information */
if ($detailed) {
$stats = json_decode($backend->configdpRun('interface list stats', [$interface]), true);
if (!$interface) {
foreach ($ifinfo as $if => $info) {
if (array_key_exists($if, $stats)) {
$ifinfo[$if]['statistics'] = $stats[$if];
}
foreach ($ifinfo as $if => $info) {
if ($interface !== null && $if !== $interface) {
continue;
}
} else {
$ifinfo[$interface]['statistics'] = $stats;
$ifinfo[$if]['statistics'] = $interface !== null ? $stats : $stats[$if];
}
}
@ -197,6 +205,12 @@ class OverviewController extends ApiControllerBase
}
}
/* parse VLAN configuration */
$tmp['vlan_tag'] = null;
if (!empty($details['vlan']) && !empty($details['vlan']['tag'])) {
$tmp['vlan_tag'] = $details['vlan']['tag'];
}
/* gateway(s) */
$gatewayv4 = $gateways->getInterfaceGateway($tmp['identifier'], 'inet');
$gatewayv6 = $gateways->getInterfaceGateway($tmp['identifier'], 'inet6');
@ -214,7 +228,7 @@ class OverviewController extends ApiControllerBase
$result = $this->parseIfInfo(null, $details);
return $this->searchRecordsetBase(
$result,
['status', 'description', 'device', 'link_type', 'ipv4', 'ipv6', 'gateways', 'routes']
['status', 'description', 'device', 'link_type', 'ipv4', 'ipv6', 'gateways', 'vlan_tag', 'routes']
);
}
@ -242,6 +256,7 @@ class OverviewController extends ApiControllerBase
}
unset($ifinfo['config']);
unset($ifinfo['carp']);
/* apply translations */
foreach ($ifinfo as $key => $value) {

View File

@ -26,6 +26,35 @@
<script>
$( document ).ready(function() {
function createTable(data) {
let table = $('<table class="table table-condensed">');
let headerRow = $('<tr>');
table.append(headerRow);
// Create table headers
for (let key in data) {
if (data.hasOwnProperty(key)) {
headerRow.append($('<th>').text(key));
}
}
// Create table rows
let dataRow = $('<tr>');
for (let key in data) {
if (data.hasOwnProperty(key)) {
let value = data[key];
if (Array.isArray(value)) {
// If the value is an array, join the elements with a newline
value = value.join('<br>');
}
dataRow.append($('<td>').html(value));
}
}
table.append(dataRow);
return table;
}
function format_linerate(value) {
if (!isNaN(value) && value > 0) {
let fileSizeTypes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"];
@ -49,7 +78,7 @@
$carp.attr('class', 'bootgrid-tooltip badge badge-pill');
$carp.css('background-color', ip['status'] == 'MASTER' ? 'green' : 'primary');
$carp.attr('data-toggle', 'tooltip');
$carp.attr('title', ip['status']);
$carp.attr('title', ip['status'] + ' (freq. ' + ip['advbase'] + '/' + ip['advskew'] + ')');
$span.append($carp);
}
$elements.append($span);
@ -70,6 +99,17 @@
}
return descr;
},
"vlan": function (column, row) {
if (row.vlan_tag) {
let $tooltip = $('<span></span>').attr('class', 'bootgrid-tooltip').text(row.vlan_tag);
$tooltip.attr('data-toggle', 'tooltip');
let standard = row.vlan.proto && row.vlan.proto == "802.1q" ? "QinQ" : "VLAN";
let parent = row.vlan.parent ? row.vlan.parent : 'none';
$tooltip.attr('title', standard + ' ' + row.vlan_tag + ' / Parent: ' + parent);
return $tooltip.prop('outerHTML');
}
return '';
},
"routes": function (column, row) {
let $elements = $('<div></div>').attr('class', 'route-container');
if (row.routes) {
@ -130,6 +170,7 @@
let $btn = $('<button type="button" class="btn btn-xs btn-default" data-toggle="tooltip"">\
<i></i></button>');
/* reload action for dynamic configurations */
if ('link_type' in row) {
if (["dhcp", "pppoe", "pptp", "l2tp", "ppp"].includes(row.link_type)) {
let $command = $btn.clone();
@ -138,8 +179,33 @@
$commands.append($command);
}
}
$btn.addClass('interface-info').attr('title', 'Info').attr('data-row-id', row.device);
/* go to interfaces assignment */
$anchor = $('<a class="btn btn-xs btn-default" data-toggle="tooltip"><i></i></a>');
if ('identifier' in row && row.identifier) {
if (!(row.config && row.config.internal_dynamic)) {
$a_interfaces = $anchor.clone().attr('href', '/interfaces_assign.php?if=' + row.identifier);
$a_interfaces.attr('title', 'Settings');
$a_interfaces.find('i').addClass('fa fa-fw fa-cog');
$commands.append($a_interfaces);
}
if (row.enabled) {
$a_fw = $anchor.clone().attr('href', '/firewall_rules.php?if=' + row.identifier);
$a_fw.attr('title', 'Firewall Rules');
$a_fw.find('i').addClass('fa fa-fw fa-fire');
$commands.append($a_fw);
}
} else {
$a_interfaces = $anchor.clone().attr('href', '/interfaces_assign.php');
$a_interfaces.attr('title', 'Assign');
$a_interfaces.find('i').addClass('fa fa-fw fa-plus');
$commands.append($a_interfaces);
}
$btn.addClass('interface-info').attr('title', 'Details').attr('data-row-id', row.device);
$btn.find('i').addClass('fa fa-fw fa-search');
$commands.append($btn);
return $commands.prop('outerHTML');
}
@ -190,10 +256,26 @@
continue;
}
key = data[key]['translation'];
$row.append($('<td/>').text(key));
if (typeof value === 'string' || Array.isArray(value)) {
value = value.toString().split(",").join("<br/>");
} else if (typeof value === 'object' && value !== null) {
// skip any deeper nested structures
let skip = false;
for (let key in value) {
if (typeof value[key] === 'object' && value !== null && !Array.isArray(value[key])) {
skip = true;
break;
}
}
if (skip) {
continue;
}
$table_sub = createTable(value);
value = $table_sub.prop('outerHTML');
}
$row.append($('<td/>').text(key));
$row.append($('<td/>').html(value));
$table_body.append($row);
}
@ -302,6 +384,7 @@
<th data-column-id="status" data-width="5em" data-formatter="status" data-type="string">{{ lang._('Status') }}</th>
<th data-column-id="description" data-formatter="interface" data-type="string">{{ lang._('Interface') }}</th>
<th data-column-id="device" data-identifier="true" data-width="5em" data-type="string">{{ lang._('Device') }}</th>
<th data-column-id="vlan_tag" data-formatter="vlan" data-type="string" data-width="3em">{{ lang._('VLAN') }}</th>
<th data-column-id="link_type" data-type="string">{{ lang._('Link Type') }}</th>
<th data-column-id="ipv4" data-formatter="ipv4" data-type="string">{{ lang._('IPv4') }}</th>
<th data-column-id="ipv6" data-formatter="ipv6" data-type="string">{{ lang._('IPv6') }}</th>