mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-10 01:27:11 +00:00
a957f3fe2a
Introduce full CDAP subsystem and devices UI overhaul. Adds a new CDAP WebSocket gateway (cdap/gateway.go) with auth, connection lifecycle, message loop, heartbeat monitor and APIs (cdap/api.go, cdap/auth.go, cdap/handler.go, cdap/manifest.go, cdap/messages.go). Wire CDAP into the server (api/server.go + handlers in api/cdap_handlers.go) exposing REST endpoints for status, device list, info, manifest, state and sending commands. Enhance peer handling: CDAP-connected overlay in peer list/get, device revocation/cascade support in handleDeletePeer (blocklist, connection teardown, events + audit), and new audit action ActionPeerRevoked. Frontend updates include CDAP device page, widgets, commands, styles and services; major devices page UI redesign (responsive folder chips, toolbar, slim table, kebab menu) plus related CSS/JS/views, translations, docs and assets. Overall adds CDAP features, revocation workflow, and a responsive devices UI.
149 lines
8.2 KiB
HTML
149 lines
8.2 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 Chips -->
|
|
<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>
|
|
</div>
|
|
<button class="folder-chip chip-add" id="add-folder-btn" title="${_('folders.create')}">
|
|
<span class="material-icons">add</span>
|
|
</button>
|
|
</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="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>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div class="devices-table-container">
|
|
<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 class="col-actions" data-column="actions"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="devices-tbody">
|
|
<tr class="loading-row">
|
|
<td colspan=\"7\" style=\"text-align: center; padding: 40px;\">
|
|
<span class="skeleton skeleton-text" style="width: 200px;"></span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<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-controls" id="pagination-controls"></div>
|
|
</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>
|
|
</form>
|
|
</template>
|
|
`
|
|
}) %>
|