diff --git a/app/Http/Controllers/DevicePopupController.php b/app/Http/Controllers/DevicePopupController.php
index f19a9af27d..6519fcecae 100644
--- a/app/Http/Controllers/DevicePopupController.php
+++ b/app/Http/Controllers/DevicePopupController.php
@@ -28,12 +28,14 @@
use App\Facades\LibrenmsConfig;
use App\Models\Device;
+use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
+use Illuminate\Support\Str;
use LibreNMS\Util\Graph;
class DevicePopupController
{
- public function __invoke(Device $device)
+ public function __invoke(Request $request, Device $device)
{
if (! LibrenmsConfig::get('web_mouseover', true)) {
return response('Disabled');
@@ -42,7 +44,31 @@ public function __invoke(Device $device)
// Check access permissions
Gate::authorize('view', $device);
- // Build graphs HTML using existing graph-row component
+ return view('device.popup', [
+ 'device' => $device,
+ 'osText' => LibrenmsConfig::getOsSetting($device->os ?? '', 'text'),
+ 'href' => route('device', ['device' => $device->device_id]),
+ 'graphs' => $this->buildGraphs($request, $device),
+ ]);
+ }
+
+ /**
+ * @return array[]
+ */
+ private function buildGraphs(Request $request, Device $device): array
+ {
+ $type = $request->string('type');
+ if ($type->isNotEmpty()) {
+ return [
+ [
+ 'device' => $device,
+ 'type' => $type,
+ 'title' => Str::title($type),
+ 'graphs' => [['from' => '-1d'], ['from' => '-7d'], ['from' => '-14d'], ['from' => '-30d']],
+ ],
+ ];
+ }
+
$graphs = [];
foreach (Graph::getOverviewGraphsForDevice($device) as $graph) {
if (isset($graph['text'], $graph['graph'])) {
@@ -55,11 +81,6 @@ public function __invoke(Device $device)
}
}
- return view('device.popup', [
- 'device' => $device,
- 'osText' => LibrenmsConfig::getOsSetting($device->os ?? '', 'text'),
- 'href' => route('device', ['device' => $device->device_id]),
- 'graphs' => $graphs,
- ]);
+ return $graphs;
}
}
diff --git a/app/Http/Controllers/DevicesController.php b/app/Http/Controllers/DevicesController.php
new file mode 100644
index 0000000000..e6cdf77b00
--- /dev/null
+++ b/app/Http/Controllers/DevicesController.php
@@ -0,0 +1,229 @@
+validate([
+ 'format' => 'in:list_basic,list_detail,graph_bits,graph_processor,graph_ucd_load,graph_mempool,graph_uptime,graph_storage,graph_diskio,graph_poller_perf,graph_icmp_perf,graph_temperature', // legacy
+ 'bare' => ['nullable', 'in:yes'],
+ 'searchbar' => ['nullable', 'in:hide'],
+ 'per_page' => ['nullable', 'integer'],
+ 'page' => ['nullable', 'integer'],
+ 'to' => ['nullable', 'date_or_relative'],
+ 'from' => ['nullable', 'date_or_relative'],
+ ...Device::filterValidationRules(),
+ 'sort' => Rule::in([
+ 'status',
+ 'device_id',
+ 'maintenance',
+ 'hostname',
+ 'hardware',
+ 'os',
+ 'uptime',
+ 'location',
+ ]),
+ ]);
+
+ $bare = $request->input('bare') === 'yes';
+ $hideFilter = $request->input('searchbar') === 'hide';
+ $perPage = $request->integer('per_page', 50);
+
+ $legacyFormat = (string) $request->string('format');
+ if ($legacyFormat) {
+ if (str_starts_with($legacyFormat, 'graph_')) {
+ $view ??= 'graph';
+ $graph ??= substr($legacyFormat, 6);
+ } else {
+ $view ??= match ($legacyFormat) {
+ 'list_basic' => 'basic',
+ default => 'detail',
+ };
+ $graph ??= '';
+ }
+ }
+
+ $graphTemplate = [
+ 'height' => 110,
+ 'width' => session('widescreen') ? 270 : 315,
+ 'id' => 0,
+ 'type' => 'device_' . $graph,
+ 'from' => $request->input('from', '-1d'),
+ 'legend' => 'no',
+ 'title' => 'yes',
+ ];
+ if ($request->input('to')) {
+ $graphTemplate['to'] = $request->input('to');
+ }
+
+ $devices = $this->getDevices($view, $perPage);
+
+ return view('device.index', [
+ 'view' => $view,
+ 'graph' => $graph,
+ 'detailed' => $view === 'detail',
+ 'devices' => $devices,
+ 'deviceGraphs' => $devices->map(function (Device $device) use ($graphTemplate) {
+ $graph = array_merge($graphTemplate, ['device' => $device->device_id]);
+
+ return [
+ 'link' => Url::graphPageUrl($graph['type'], Arr::except($graph, ['height', 'width', 'legend', 'title'])),
+ 'graphTag' => Url::lazyGraphTag($graph, 'tw:w-full tw:h-auto'),
+ 'deviceLinkOptions' => ['device_id' => $device->device_id, 'params' => ['type' => $graph['type']]],
+ ];
+ }),
+ 'perPage' => $perPage,
+ 'paginationOptions' => [50, 100, 250, -1],
+ 'nav' => [
+ 'detail' => ['text' => 'Detail', 'link' => route('devices', ['view' => 'detail', ...$request->query()])],
+ 'basic' => ['text' => 'Basic', 'link' => route('devices', ['view' => 'basic', ...$request->query()])],
+ ],
+ 'graphNav' => [
+ 'bits' => ['text' => 'Bits', 'link' => route('devices', ['view' => 'graph', 'graph' => 'bits', ...$request->query()])],
+ 'processor' => ['text' => 'CPU', 'link' => route('devices', ['view' => 'graph', 'graph' => 'processor', ...$request->query()])],
+ 'ucd_load' => ['text' => 'Load', 'link' => route('devices', ['view' => 'graph', 'graph' => 'ucd_load', ...$request->query()])],
+ 'mempool' => ['text' => 'Memory', 'link' => route('devices', ['view' => 'graph', 'graph' => 'mempool', ...$request->query()])],
+ 'uptime' => ['text' => 'Uptime', 'link' => route('devices', ['view' => 'graph', 'graph' => 'uptime', ...$request->query()])],
+ 'storage' => ['text' => 'Storage', 'link' => route('devices', ['view' => 'graph', 'graph' => 'storage', ...$request->query()])],
+ 'diskio' => ['text' => 'Disk I/O', 'link' => route('devices', ['view' => 'graph', 'graph' => 'diskio', ...$request->query()])],
+ 'poller_perf' => ['text' => 'Poller', 'link' => route('devices', ['view' => 'graph', 'graph' => 'poller_perf', ...$request->query()])],
+ 'icmp_perf' => ['text' => 'Ping', 'link' => route('devices', ['view' => 'graph', 'graph' => 'icmp_perf', ...$request->query()])],
+ 'temperature' => ['text' => 'Temperature', 'link' => route('devices', ['view' => 'graph', 'graph' => 'temperature', ...$request->query()])],
+ ],
+ 'bare' => $bare,
+ 'bareLink' => $bare ? $request->fullUrlWithoutQuery('bare') : $request->fullUrlWithQuery(['bare' => 'yes']),
+ 'filter' => $request->array('filter'),
+ 'hideFilterLink' => $hideFilter ? $request->fullUrlWithoutQuery('searchbar') : $request->fullUrlWithQuery(['searchbar' => 'hide']),
+ 'hideFilter' => $hideFilter,
+ 'filterFields' => $this->filterFields(),
+ 'graphTemplate' => $graphTemplate,
+ 'group' => $request->input('filter.groups\.id.eq'),
+ ]);
+ }
+
+ private function getDevices(?string $view, int $perPage): LengthAwarePaginator|Collection
+ {
+ if ($view !== 'graph') {
+ return new Collection;
+ }
+
+ $devicesQuery = Device::hasAccess(request()->user())
+ ->select(['devices.*'])
+ ->with('location')
+ ->when(request()->array('filter'), fn (Builder $query, $filters) => $query->applyFilters($filters));
+
+ $devicesQuery->orderBy('hostname');
+
+ return $devicesQuery->paginate($perPage);
+ }
+
+ private function filterFields(): array
+ {
+ return [
+ [
+ 'key' => 'search',
+ 'label' => __('Search'),
+ 'type' => 'text',
+ ],
+ [
+ 'key' => 'state',
+ 'label' => __('device.status'),
+ 'type' => 'select',
+ 'options' => [
+ 'up' => __('device.status_up'),
+ 'down' => __('device.status_down'),
+ ],
+ ],
+ [
+ 'key' => 'os',
+ 'label' => __('device.os'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.device-field'),
+ 'params' => [
+ 'field' => 'os',
+ ],
+ ],
+ [
+ 'key' => 'version',
+ 'label' => __('Version'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.device-field'),
+ 'params' => [
+ 'field' => 'version',
+ ],
+ ],
+ [
+ 'key' => 'hardware',
+ 'label' => __('Platform'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.device-field'),
+ 'params' => [
+ 'field' => 'hardware',
+ ],
+ ],
+ [
+ 'key' => 'features',
+ 'label' => __('Featureset'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.device-field'),
+ 'params' => [
+ 'field' => 'features',
+ ],
+ ],
+ [
+ 'key' => 'location_id',
+ 'label' => __('Location'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.location'),
+ ],
+ [
+ 'key' => 'type',
+ 'label' => __('device.device_type'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.device-field'),
+ 'params' => [
+ 'field' => 'type',
+ ],
+ ],
+ [
+ 'key' => 'groups.id',
+ 'label' => __('device.device_group'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.device-group'),
+ ],
+ [
+ 'key' => 'poller_group',
+ 'label' => __('device.edit.poller_group'),
+ 'type' => 'select',
+ 'endpoint' => route('ajax.select.poller-group'),
+ ],
+ [
+ 'key' => 'disabled',
+ 'label' => __('Disabled'),
+ 'type' => 'boolean',
+ ],
+ [
+ 'key' => 'ignore',
+ 'label' => __('Ignored'),
+ 'type' => 'boolean',
+ ],
+ [
+ 'key' => 'disable_notify',
+ 'label' => __('device.alerts_disabled'),
+ 'type' => 'boolean',
+ ],
+ ];
+ }
+}
diff --git a/app/Http/Controllers/Table/DeviceController.php b/app/Http/Controllers/Table/DeviceController.php
index bd36bdb080..0e86e1aa21 100644
--- a/app/Http/Controllers/Table/DeviceController.php
+++ b/app/Http/Controllers/Table/DeviceController.php
@@ -53,6 +53,7 @@ protected function rules(): array
{
return [
'format' => 'nullable|in:list_basic,list_detail',
+ ...Device::filterValidationRules(),
'os' => 'nullable|string',
'version' => 'nullable|string',
'hardware' => 'nullable|string',
@@ -101,6 +102,7 @@ protected function baseQuery(Request $request): Builder
/** @var Builder $query */
$query = Device::hasAccess($request->user())
->with(['location', 'groups'])
+ ->applyFilters($request->array('filter'))
->withCount(['ports', 'sensors', 'wirelessSensors']);
// if searching or sorting the location field, join the locations table
diff --git a/app/Models/Device.php b/app/Models/Device.php
index e4df3d084d..a17cbe6d53 100644
--- a/app/Models/Device.php
+++ b/app/Models/Device.php
@@ -3,6 +3,7 @@
namespace App\Models;
use App\Facades\LibrenmsConfig;
+use App\Models\Traits\Filterable;
use App\View\SimpleTemplate;
use Carbon\Carbon;
use Fico7489\Laravel\Pivot\Traits\PivotEventTrait;
@@ -38,7 +39,7 @@
*/
class Device extends BaseModel
{
- use PivotEventTrait, HasFactory;
+ use PivotEventTrait, HasFactory, Filterable;
private ?MaintenanceStatus $maintenanceStatus = null;
@@ -88,6 +89,27 @@ class Device extends BaseModel
'uptime',
];
+ protected array $filterable = [
+ 'device_id',
+ 'hostname',
+ 'sysName',
+ 'display',
+ 'hardware',
+ 'os',
+ 'location_id',
+ 'version',
+ 'features',
+ 'type',
+ 'status',
+ 'disabled',
+ 'ignore',
+ 'disable_notify',
+ 'poller_group',
+ 'groups.id',
+ 'search',
+ 'state',
+ ];
+
/**
* @return array{inserted: 'datetime', last_discovered: 'datetime', last_polled: 'datetime', last_ping: 'datetime', status: 'boolean'}
*/
@@ -540,6 +562,25 @@ public function setSysNameAttribute(?string $sysName): void
// ---- Query scopes ----
+ public function filterState(Builder $query, mixed $value, array $config): void
+ {
+ $this->applyMappedFilter($query, $value, $config, fn (Builder $q, $state) => match ($state) {
+ 'up' => $q->where('status', 1)->where('disabled', 0)->where('disable_notify', 0),
+ 'down' => $q->where('status', 0)->where('disabled', 0)->where('disable_notify', 0),
+ default => $q,
+ });
+ }
+
+ public function filterSearch(Builder $query, mixed $value, array $config): void
+ {
+ $this->applyFilterSearch(
+ ['sysName', 'hostname', 'display', 'hardware', 'os', 'location.location'],
+ $query,
+ $value,
+ $config,
+ );
+ }
+
public function scopeIsUp($query)
{
return $query->where([
diff --git a/includes/html/pages/devices.inc.php b/includes/html/pages/devices.inc.php
index 33d02d4b50..cfd64cde4f 100644
--- a/includes/html/pages/devices.inc.php
+++ b/includes/html/pages/devices.inc.php
@@ -1,433 +1,3 @@
where('id', $device_group_id)->value('name') ?? __('Group not found');
- }
- ?>
-
-
-
-
-
-
- ' . __('Lists') . ': ';
-
-$menu_options = ['basic' => __('Basic'), 'detail' => __('Detail')];
-
-$sep = '';
-foreach ($menu_options as $option => $text) {
- $listoptions .= $sep;
- if ($vars['format'] == 'list_' . $option) {
- $listoptions .= '';
- }
- $sep = ' | ';
-}
-
-$listoptions .= ' ' . __('Graphs') . ': ';
-
-$menu_options = ['bits' => __('Bits'),
- 'processor' => __('CPU'),
- 'ucd_load' => __('Load'),
- 'mempool' => __('Memory'),
- 'uptime' => __('Uptime'),
- 'storage' => __('Storage'),
- 'diskio' => __('Disk I/O'),
- 'poller_perf' => __('Poller'),
- 'icmp_perf' => __('Ping'),
- 'temperature' => __('sensors.temperature.long'),
-];
-$sep = '';
-foreach ($menu_options as $option => $text) {
- $listoptions .= $sep;
- if ($vars['format'] == 'graph_' . $option) {
- $listoptions .= '';
- }
- $sep = ' | ';
-}
-
-$headeroptions = '';
-
-if (isset($vars['searchbar']) && $vars['searchbar'] == 'hide') {
- $headeroptions .= '' . __('Restore Search') . '';
-} else {
- $headeroptions .= '' . __('Remove Search') . '';
-}
-
-$headeroptions .= ' | ';
-
-if (isset($vars['bare']) && $vars['bare'] == 'yes') {
- $headeroptions .= '' . __('Restore Header') . '';
-} else {
- $headeroptions .= '' . __('Remove Header') . '';
-}
-
-[$format, $subformat] = explode('_', $vars['format'], 2);
-$detailed = $subformat == 'detail';
-$no_refresh = $format == 'list';
-
-if ($format == 'graph') {
- if (empty($vars['from'])) {
- $graph_array['from'] = LibrenmsConfig::get('time.day');
- } else {
- $graph_array['from'] = $vars['from'];
- }
- if (empty($vars['to'])) {
- $graph_array['to'] = LibrenmsConfig::get('time.now');
- } else {
- $graph_array['to'] = $vars['to'];
- }
-
- echo '';
- echo '
';
- echo '
';
- echo '
' . $listoptions . '
';
- echo '
' . $headeroptions . '
';
- echo '
';
- include_once 'includes/html/print-date-selector.inc.php';
- echo '
';
- echo '
';
- echo '
';
- echo '
';
-
- $where = '';
- $sql_param = [];
-
- if (isset($vars['state'])) {
- if ($vars['state'] == 'up') {
- $state = '1';
- } elseif ($vars['state'] == 'down') {
- $state = '0';
- }
- }
-
- if (! empty($vars['searchquery'])) {
- $where .= ' AND (sysName LIKE ? OR hostname LIKE ? OR display LIKE ? OR hardware LIKE ? OR os LIKE ? OR location LIKE ?)';
- $sql_param += array_fill(0, 6, '%' . $vars['searchquery'] . '%');
- }
- if (! empty($vars['os'])) {
- $where .= ' AND os = ?';
- $sql_param[] = $vars['os'];
- }
- if (! empty($vars['version'])) {
- $where .= ' AND version = ?';
- $sql_param[] = $vars['version'];
- }
- if (! empty($vars['hardware'])) {
- $where .= ' AND hardware = ?';
- $sql_param[] = $vars['hardware'];
- }
- if (! empty($vars['features'])) {
- $where .= ' AND features = ?';
- $sql_param[] = $vars['features'];
- }
-
- if (! empty($vars['type'])) {
- if ($vars['type'] == 'generic') {
- $where .= " AND ( type = ? OR type = '')";
- $sql_param[] = $vars['type'];
- } else {
- $where .= ' AND type = ?';
- $sql_param[] = $vars['type'];
- }
- }
- if (! empty($vars['state'])) {
- $where .= ' AND status= ?';
- $sql_param[] = $state;
- $where .= " AND disabled='0' AND `disable_notify`='0'";
- }
- if (! empty($vars['disabled'])) {
- $where .= ' AND disabled= ?';
- $sql_param[] = $vars['disabled'];
- }
- if (! empty($vars['ignore'])) {
- $where .= ' AND `ignore`= ?';
- $sql_param[] = $vars['ignore'];
- }
- if (! empty($vars['disable_notify'])) {
- $where .= ' AND `disable_notify`= ?';
- $sql_param[] = $vars['disable_notify'];
- }
- if (! empty($vars['location']) && $vars['location'] != 'Unset') {
- if (is_numeric($vars['location'])) {
- $where .= ' AND `locations`.`id`= ?';
- $sql_param[] = $vars['location'];
- } else {
- $where .= ' AND `locations`.`location`= ?';
- $sql_param[] = $vars['location'];
- }
- }
- if (isset($vars['poller_group'])) {
- $where .= ' AND `poller_group`= ?';
- $sql_param[] = $vars['poller_group'];
- }
- if (! empty($vars['group'])) {
- $where .= ' AND ( ';
- foreach (DB::table('device_group_device')->where('device_group_id', $vars['group'])->pluck('device_id') as $dev) {
- $where .= 'device_id = ? OR ';
- $sql_param[] = $dev;
- }
- $where = substr($where, 0, strlen($where) - 3);
- $where .= ' )';
-
- show_device_group($vars['group']);
- }
-
- $query = 'SELECT * FROM `devices` LEFT JOIN `locations` ON `devices`.`location_id` = `locations`.`id` WHERE 1';
-
- if (isset($where)) {
- $query .= $where;
- }
-
- $query .= ' ORDER BY hostname';
-
- $row = 1;
- foreach (dbFetchRows($query, $sql_param) as $device) {
- if (is_int($row / 2)) {
- $row_colour = LibrenmsConfig::get('list_colour.even');
- } else {
- $row_colour = LibrenmsConfig::get('list_colour.odd');
- }
-
- if (device_permitted($device['device_id'])) {
- $graph_type = 'device_' . $subformat;
-
- if (session('widescreen')) {
- $width = 270;
- } else {
- $width = 315;
- }
-
- $graph_array_new = [];
- $graph_array_new['type'] = $graph_type;
- $graph_array_new['device'] = $device['device_id'];
- $graph_array_new['height'] = '110';
- $graph_array_new['width'] = $width;
- $graph_array_new['legend'] = 'no';
- $graph_array_new['title'] = 'yes';
- $graph_array_new['from'] = $graph_array['from'];
- $graph_array_new['to'] = $graph_array['to'];
-
- $graph_array_zoom = $graph_array_new;
- $graph_array_zoom['height'] = '150';
- $graph_array_zoom['width'] = '400';
- $graph_array_zoom['legend'] = 'yes';
-
- $link_array = $graph_array;
- $link_array['page'] = 'graphs';
- $link_array['type'] = $graph_type;
- $link_array['device'] = $device['device_id'];
- unset($link_array['height'], $link_array['width']);
- $overlib_link = Url::generate($link_array);
- echo '
';
- echo '
';
- echo Url::overlibLink($overlib_link, Url::lazyGraphTag($graph_array_new), Url::graphTag($graph_array_zoom));
- echo "
\n\n";
- }
- }
- echo '
';
-} else {
- $state = $vars['state'] ?? '';
- $state_selection = "
';
-
- $features_selected = isset($vars['features']) ? json_encode(['id' => $vars['features'], 'text' => $vars['features']]) : '""';
- $hardware_selected = isset($vars['hardware']) ? json_encode(['id' => $vars['hardware'], 'text' => $vars['hardware']]) : '""';
- $os_selected = isset($vars['os']) ? json_encode(['id' => $vars['os'], 'text' => $vars['os']]) : '""';
- $type_selected = isset($vars['type']) ? json_encode(['id' => $vars['type'], 'text' => ucfirst($vars['type'])]) : '""';
- $version_selected = isset($vars['version']) ? json_encode(['id' => $vars['version'], 'text' => $vars['version']]) : '""';
-
- $os_selected = '""';
- if (isset($vars['os'])) {
- $os_selected = json_encode(['id' => $vars['os'], 'text' => LibrenmsConfig::getOsSetting($vars['os'], 'text', $vars['os'])]);
- }
-
- $location_selected = '""';
- if (isset($vars['location'])) {
- $location_text = $vars['location'];
- if (is_numeric($vars['location'])) {
- $location_text = Location::where('id', $vars['location'])->value('location') ?: $vars['location'];
- }
- $location_selected = json_encode(['id' => $vars['location'], 'text' => $location_text]);
- } ?>
-
-
-
-
-
-
-
- |
- = __('Id') ?> |
- |
- = __('Vendor') ?> |
- >= __('Device') ?> |
- = __('Metrics')?> |
- = __('Platform') ?> |
- = __('device.attributes.os') ?> |
- = __('Up/Down Time') ?> |
- = __('device.attributes.location') ?> |
- = __('Actions') ?> |
-
-
-
-
-
-
- 'Please select',
'warning_monitored' => 'Warning, this will remove the device from being monitored!',
'warning_data' => 'It will also remove historical data about this device such as:',
+ 'show_filter' => 'Show Filter',
+ 'show_header' => 'Show Header',
+ 'os' => 'OS',
+ 'status' => 'Status',
+ 'status_up' => 'Up',
+ 'status_down' => 'Down',
+ 'device_type' => 'Device Type',
+ 'device_group' => 'Device Group',
+ 'alerts_disabled' => 'Alerts Disabled',
'edit' => [
'delete_device' => 'Delete Device',
diff --git a/lang/en/port.php b/lang/en/port.php
index 613b4a91e1..a804e8a4cd 100644
--- a/lang/en/port.php
+++ b/lang/en/port.php
@@ -1,7 +1,7 @@
'Show Filter',
+ 'show_filter' => 'Show Filter',
'show_header' => 'Show Header',
'purge' => 'Purge all deleted',
'purged' => 'Purged',
diff --git a/resources/views/device/graphs.blade.php b/resources/views/device/graphs.blade.php
new file mode 100644
index 0000000000..f8a8a799c3
--- /dev/null
+++ b/resources/views/device/graphs.blade.php
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+ {{ __('Device Group') }}:
+
+
+
+
+ @foreach($deviceGraphs as $graph)
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+
{{ $devices->appends(request()->all())->links() }}
+
+
+
+@push('scripts')
+
+@endpush
diff --git a/resources/views/device/index.blade.php b/resources/views/device/index.blade.php
new file mode 100644
index 0000000000..a48597005b
--- /dev/null
+++ b/resources/views/device/index.blade.php
@@ -0,0 +1,35 @@
+@extends('layouts.librenmsv1')
+
+@section('title', __('Devices'))
+
+@section('content')
+
+
+
+
+
+ @if($view === 'graph')
+ @include('device.graphs')
+ @else
+ @include('device.list')
+ @endif
+
+
+
+@endsection
diff --git a/resources/views/device/list.blade.php b/resources/views/device/list.blade.php
new file mode 100644
index 0000000000..860ea951ce
--- /dev/null
+++ b/resources/views/device/list.blade.php
@@ -0,0 +1,91 @@
+
+
+
+
+ {{ __('Device Group') }}:
+
+
+
+
+
+
+ | {{ $detailed ? 'S.' : __('Status') }} |
+ {{ __('Id') }} |
+ {{ $detailed ? 'M.' : __('Maintenance') }} |
+ {{ __('Vendor') }} |
+ {{ __('Device') }} |
+ {{ __('Metrics') }} |
+ {{ __('Platform') }} |
+ {{ __('device.attributes.os') }} |
+ {{ __('Up/Down Time') }} |
+ {{ __('device.attributes.location') }} |
+ {{ __('Actions') }} |
+
+
+
+
+
+ @push('scripts')
+
+ @endpush
+
diff --git a/routes/web.php b/routes/web.php
index 6f522efe99..543f5e6fee 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -15,6 +15,7 @@
use App\Http\Controllers\Device;
use App\Http\Controllers\DeviceController;
use App\Http\Controllers\DeviceGroupController;
+use App\Http\Controllers\DevicesController;
use App\Http\Controllers\GraphController;
use App\Http\Controllers\Install;
use App\Http\Controllers\LegacyController;
@@ -90,6 +91,7 @@
Route::middleware(['auth'])->group(function (): void {
// pages
Route::post('alert/{alert}/ack', [AlertController::class, 'ack'])->name('alert.ack');
+ Route::get('devices/{view?}/{graph?}', [DevicesController::class, 'index'])->name('devices');
Route::resource('device-groups', DeviceGroupController::class);
Route::any('inventory', App\Http\Controllers\InventoryController::class)->name('inventory');
Route::get('inventory/purge', [App\Http\Controllers\InventoryController::class, 'purge'])->name('inventory.purge');