Files
BetterDesk/web-nodejs/views/devices.ejs
T
UNITRONIX 24bf48d391 feat(devices): add pagination and context menu for device management
Introduced a new pagination feature allowing users to select the number of devices displayed per page. Added a context menu for device actions, enhancing user interaction with device management. Updated the CSS for better layout and usability, including a floating horizontal scrollbar for improved navigation. This update aims to streamline the device management experience.
2026-06-07 01:42:09 +02:00

204 lines
12 KiB
HTML

<%- include('layouts/main', {
title: _('nav.devices'),
pageStyles: ['devices', 'device-detail'],
pageScripts: ['devices', 'deviceDetail'],
currentPage: 'devices',
breadcrumb: [{ label: _('nav.devices') }],
body: `
<div class="devices-page">
<!-- Header -->
<div class="devices-header">
<div class="devices-title">
<h1>${_('devices.title')}</h1>
<span class="devices-count" id="devices-count">0</span>
</div>
<div class="devices-actions">
<button class="btn btn-secondary btn-sm" id="sync-btn">
<span class="material-icons">sync</span>
<span class="btn-label">${_('devices.sync_status')}</span>
</button>
</div>
</div>
<!-- Folder Tiles -->
<div class="folders-bar" id="folders-bar">
<div class="folders-chips" id="folders-chips">
<button class="folder-chip active" data-folder="all">
<span class="material-icons chip-icon">devices</span>
<span class="chip-label">${_('folders.all_devices')}</span>
<span class="chip-count" id="folder-count-all">0</span>
</button>
<button class="folder-chip" data-folder="unassigned">
<span class="material-icons chip-icon">folder_off</span>
<span class="chip-label">${_('folders.unassigned')}</span>
<span class="chip-count" id="folder-count-unassigned">0</span>
</button>
<span id="custom-folders"></span>
<button class="folder-chip chip-add" id="add-folder-btn" title="${_('folders.create')}">
<span class="material-icons">add</span>
<span class="chip-label">${_('folders.create')}</span>
</button>
</div>
</div>
<!-- Device Group Tiles -->
<div class="groups-bar" id="groups-bar">
<div class="groups-chips" id="groups-chips">
<button class="group-chip active" data-group="all">
<span class="material-icons chip-icon">hub</span>
<span class="chip-label">${_('devices.all_groups')}</span>
<span class="chip-count" id="group-count-all">0</span>
</button>
<span id="custom-device-groups"></span>
<button class="group-chip chip-add" id="add-device-group-btn" title="${_('devices.create_group')}">
<span class="material-icons">add</span>
<span class="chip-label">${_('devices.create_group')}</span>
</button>
</div>
</div>
<!-- Toolbar -->
<div class="devices-toolbar">
<div class="toolbar-search search-wrapper">
<span class="material-icons">search</span>
<input type="text" class="form-input" id="search-input"
placeholder="${_('devices.search_placeholder')}">
</div>
<div class="toolbar-filters">
<button class="filter-btn active" data-filter="all">${_('devices.filter_all')}</button>
<button class="filter-btn" data-filter="online">${_('devices.filter_online')}</button>
<button class="filter-btn" data-filter="offline">${_('devices.filter_offline')}</button>
<button class="filter-btn" data-filter="banned">${_('devices.filter_banned')}</button>
</div>
<div class="tag-filter-dropdown">
<button class="btn-icon toolbar-icon" id="tags-filter-btn" title="${_('devices.filter_by_tags')}">
<span class="material-icons">sell</span>
</button>
<div class="tag-filter-menu" id="tags-filter-menu">
<div class="tag-filter-empty">${_('devices.no_tags')}</div>
</div>
</div>
<div class="column-visibility-dropdown">
<button class="btn-icon toolbar-icon" id="columns-btn" title="${_('devices.toggle_columns')}">
<span class="material-icons">view_column</span>
</button>
<div class="column-visibility-menu" id="columns-menu">
<label class="column-toggle">
<input type="checkbox" data-column="device_type" checked>
<span>${_('devices.device_type')}</span>
</label>
<label class="column-toggle">
<input type="checkbox" data-column="platform" checked>
<span>${_('devices.platform')}</span>
</label>
<label class="column-toggle">
<input type="checkbox" data-column="last_online" checked>
<span>${_('devices.last_seen')}</span>
</label>
<label class="column-toggle">
<input type="checkbox" data-column="status" checked>
<span>${_('devices.status')}</span>
</label>
<label class="column-toggle">
<input type="checkbox" data-column="tags" checked>
<span>${_('devices.tags')}</span>
</label>
</div>
</div>
</div>
<!-- Table -->
<div class="devices-table-container">
<div class="devices-table-scroll" id="devices-table-scroll">
<table class="devices-table" id="devices-table">
<thead>
<tr>
<th class="sortable" data-sort="id" data-column="id">${_('devices.id')}</th>
<th class="sortable" data-sort="hostname" data-column="hostname">${_('devices.hostname')}</th>
<th class="sortable" data-sort="device_type" data-column="device_type">${_('devices.device_type')}</th>
<th class="sortable" data-sort="platform" data-column="platform">${_('devices.platform')}</th>
<th class="sortable" data-sort="last_online" data-column="last_online">${_('devices.last_seen')}</th>
<th data-column="status">${_('devices.status')}</th>
<th data-column="tags">${_('devices.tags')}</th>
<th class="col-actions" data-column="actions"></th>
</tr>
</thead>
<tbody id="devices-tbody">
<tr class="loading-row">
<td colspan=\"8\" style=\"text-align: center; padding: 40px;\">
<span class="skeleton skeleton-text" style="width: 200px;"></span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="devices-pagination" id="pagination">
<div class="pagination-info">
<span id="pagination-info">${_('devices.showing')} 0-0 ${_('devices.of')} 0</span>
</div>
<div class="pagination-per-page">
<label for="per-page-select">${_('devices.per_page')}</label>
<select id="per-page-select" class="pagination-per-page-select" aria-label="${_('devices.per_page')}">
<option value="10">10</option>
<option value="20" selected>20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
<div class="pagination-controls" id="pagination-controls"></div>
</div>
</div>
<!-- Shared context menu (devices, folders, groups) -->
<div class="devices-context-menu" id="devices-context-menu" role="menu" hidden> </div>
<!-- Floating horizontal scrollbar (fixed to visible content area) -->
<div class="devices-hscroll-float" id="devices-hscroll-float" aria-hidden="true">
<div class="devices-hscroll-spacer" id="devices-hscroll-spacer"></div>
</div>
<!-- Empty state -->
<div class="devices-empty hidden" id="devices-empty">
<span class="material-icons devices-empty-icon">devices_other</span>
<h3 class="devices-empty-title">${_('devices.empty_title')}</h3>
<p class="devices-empty-text">${_('devices.empty_text')}</p>
</div>
<!-- Kebab overlay for mobile bottom sheet -->
<div class="kebab-overlay" id="kebab-overlay"></div>
</div>
<!-- Folder Form Template -->
<template id="folder-form-template">
<form id="folder-form" class="folder-form">
<div class="form-group">
<label for="folder-name">${_('folders.name')}</label>
<input type="text" id="folder-name" name="name" class="form-input"
maxlength="50" required placeholder="${_('folders.name_placeholder')}">
</div>
<div class="form-group">
<label>${_('folders.color')}</label>
<div class="color-picker" id="folder-color-picker">
<button type="button" class="color-option active" data-color="#6366f1" style="background:#6366f1"></button>
<button type="button" class="color-option" data-color="#22c55e" style="background:#22c55e"></button>
<button type="button" class="color-option" data-color="#f59e0b" style="background:#f59e0b"></button>
<button type="button" class="color-option" data-color="#ef4444" style="background:#ef4444"></button>
<button type="button" class="color-option" data-color="#8b5cf6" style="background:#8b5cf6"></button>
<button type="button" class="color-option" data-color="#06b6d4" style="background:#06b6d4"></button>
<button type="button" class="color-option" data-color="#ec4899" style="background:#ec4899"></button>
<button type="button" class="color-option" data-color="#64748b" style="background:#64748b"></button>
</div>
<input type="hidden" id="folder-color" name="color" value="#6366f1">
</div>
<div class="form-group">
<label for="folder-allowed-users">${_('devices.group_allowed_users')}</label>
<input type="text" id="folder-allowed-users" name="allowed_users" class="form-input"
placeholder="operator1, operator2">
<p class="form-hint">${_('devices.group_allowed_users_hint')}</p>
</div>
</form>
</template>
`
}) %>