Files
BetterDesk/web-nodejs/views/remote-dashboard.ejs
UNITRONIX 2e01955f6c feat(rdclient): enhance desktop client with server validation, LAN discovery, and settings management
- Introduced server URL validation via `GET /api/bd/server-info` and `probe_server_url`.
- Added LAN discovery capabilities using UDP and optional mDNS for local network panel detection.
- Implemented a local settings window for managing URL, TLS settings, and user preferences.
- Enhanced dashboard with a unified sidebar and improved scrolling behavior.
- Updated documentation and pre-release checklist to reflect new features and requirements.
2026-06-14 21:12:41 +02:00

171 lines
9.8 KiB
HTML

<%- include('layouts/remote-desk', {
title: typeof title !== 'undefined' ? title : _('remote_dashboard.title'),
pageStyles: ['remote-dashboard'],
pageScripts: ['remote-dashboard'],
body: `
<div class="rd-desk-app" id="rd-desk-app">
<header class="rd-desk-header">
<div class="rd-desk-brand">
<div class="rd-desk-logo" id="rd-desk-logo"></div>
<div class="rd-desk-brand-text">
<h1 class="rd-desk-title">${_('remote_dashboard.title')}</h1>
<p class="rd-desk-subtitle">${_('remote_dashboard.subtitle')}</p>
</div>
</div>
<div class="rd-desk-header-actions">
<select id="rd-desk-lang" class="rd-desk-lang-select" data-language-select aria-label="${_('settings.language')}"></select>
<button type="button" class="rd-desk-icon-btn" id="rd-desk-settings" title="${_('rdclient_settings.open') || 'Settings'}" style="display:none;">
<span class="material-icons">settings</span>
</button>
<span class="rd-desk-user" id="rd-desk-user"></span>
<button type="button" class="rd-desk-icon-btn" id="rd-desk-refresh" title="${_('remote_dashboard.sync_now')}">
<span class="material-icons">sync</span>
</button>
<a href="/devices" class="rd-desk-btn rd-desk-btn-ghost" target="_blank" rel="noopener noreferrer">
<span class="material-icons">dashboard</span>
${_('remote_dashboard.open_panel')}
</a>
</div>
</header>
<section class="rd-desk-quick-connect">
<div class="rd-desk-quick-connect-inner">
<span class="material-icons rd-desk-quick-icon">connected_tv</span>
<label class="rd-desk-quick-label" for="rd-desk-quick-id">${_('remote_dashboard.quick_connect')}</label>
<input type="text" id="rd-desk-quick-id" class="rd-desk-quick-input"
placeholder="${_('remote_dashboard.quick_connect_placeholder')}" autocomplete="off" spellcheck="false">
<button type="button" class="rd-desk-btn" id="rd-desk-quick-btn">
<span class="material-icons">play_arrow</span>
${_('remote_dashboard.connect')}
</button>
</div>
</section>
<div class="rd-desk-workspace">
<aside class="rd-desk-sidebar" id="rd-desk-sidebar">
<div class="rd-desk-sidebar-scroll" id="rd-desk-sidebar-scroll">
<nav class="rd-desk-nav-section" data-collapsible="folders">
<button type="button" class="rd-desk-nav-heading rd-desk-nav-heading-btn" id="rd-desk-nav-folders-toggle" aria-expanded="true">
<span>${_('remote_dashboard.nav_folders')}</span>
<span class="material-icons rd-desk-nav-chevron" aria-hidden="true">expand_more</span>
</button>
<div class="rd-desk-nav-body" id="rd-desk-nav-folders-body">
<ul class="rd-desk-nav-list" id="rd-desk-folders">
<li><button type="button" class="rd-desk-nav-item active" data-nav="folder" data-id="all">
<span class="material-icons">devices</span>
<span class="rd-desk-nav-label">${_('remote_dashboard.filter_all')}</span>
<span class="rd-desk-nav-count" id="rd-count-all">0</span>
</button></li>
<li><button type="button" class="rd-desk-nav-item" data-nav="folder" data-id="unassigned">
<span class="material-icons">folder_off</span>
<span class="rd-desk-nav-label">${_('remote_dashboard.unassigned')}</span>
<span class="rd-desk-nav-count" id="rd-count-unassigned">0</span>
</button></li>
</ul>
<ul class="rd-desk-nav-list rd-desk-nav-custom" id="rd-desk-folders-custom"></ul>
</div>
</nav>
<nav class="rd-desk-nav-section" data-collapsible="groups">
<button type="button" class="rd-desk-nav-heading rd-desk-nav-heading-btn" id="rd-desk-nav-groups-toggle" aria-expanded="true">
<span>${_('remote_dashboard.nav_groups')}</span>
<span class="material-icons rd-desk-nav-chevron" aria-hidden="true">expand_more</span>
</button>
<div class="rd-desk-nav-body" id="rd-desk-nav-groups-body">
<ul class="rd-desk-nav-list" id="rd-desk-groups">
<li class="rd-desk-nav-empty hidden" id="rd-desk-groups-empty">${_('remote_dashboard.no_groups')}</li>
</ul>
</div>
</nav>
<nav class="rd-desk-nav-section" data-collapsible="tags">
<button type="button" class="rd-desk-nav-heading rd-desk-nav-heading-btn" id="rd-desk-nav-tags-toggle" aria-expanded="true">
<span>${_('remote_dashboard.nav_tags')}</span>
<span class="material-icons rd-desk-nav-chevron" aria-hidden="true">expand_more</span>
</button>
<div class="rd-desk-nav-body" id="rd-desk-nav-tags-body">
<ul class="rd-desk-nav-list rd-desk-nav-tags" id="rd-desk-tags">
<li class="rd-desk-nav-empty hidden" id="rd-desk-tags-empty">${_('remote_dashboard.no_tags')}</li>
</ul>
</div>
</nav>
</div>
</aside>
<div class="rd-desk-content">
<div class="rd-desk-toolbar">
<div class="rd-desk-toolbar-left">
<button type="button" class="rd-desk-icon-btn rd-desk-panel-toggle" id="rd-desk-devices-toggle"
title="${_('remote_dashboard.collapse_devices')}" aria-expanded="true" aria-controls="rd-desk-devices-scroll">
<span class="material-icons" id="rd-desk-devices-toggle-icon">expand_more</span>
</button>
<span class="rd-desk-section-title" id="rd-desk-section-title">${_('remote_dashboard.nav_address_book')}</span>
</div>
<div class="rd-desk-search-wrap">
<span class="material-icons rd-desk-search-icon">search</span>
<input type="search" id="rd-desk-search" class="rd-desk-search"
placeholder="${_('remote_dashboard.search_placeholder')}" autocomplete="off">
</div>
<div class="rd-desk-filters" role="tablist">
<button type="button" class="rd-desk-filter active" data-filter="all">${_('remote_dashboard.filter_all')}</button>
<button type="button" class="rd-desk-filter" data-filter="online">${_('remote_dashboard.filter_online')}</button>
<button type="button" class="rd-desk-filter" data-filter="offline">${_('remote_dashboard.filter_offline')}</button>
</div>
<div class="rd-desk-view-toggle" role="group">
<button type="button" class="rd-desk-icon-btn active" id="rd-view-grid" title="${_('remote_dashboard.view_grid')}">
<span class="material-icons">grid_view</span>
</button>
<button type="button" class="rd-desk-icon-btn" id="rd-view-list" title="${_('remote_dashboard.view_list')}">
<span class="material-icons">view_list</span>
</button>
</div>
<span class="rd-desk-count" id="rd-desk-count"></span>
</div>
<main class="rd-desk-main">
<div class="rd-desk-devices-panel" id="rd-desk-devices-panel">
<div class="rd-desk-devices-scroll" id="rd-desk-devices-scroll">
<div class="rd-desk-loading" id="rd-desk-loading">
<span class="material-icons rd-desk-spin">sync</span>
<span>${_('remote_dashboard.loading')}</span>
</div>
<div class="rd-desk-error hidden" id="rd-desk-error">
<span class="material-icons">error_outline</span>
<span id="rd-desk-error-text">${_('remote_dashboard.error_load')}</span>
<button type="button" class="rd-desk-btn" id="rd-desk-retry">${_('remote_dashboard.retry')}</button>
</div>
<div class="rd-desk-empty hidden" id="rd-desk-empty">
<span class="material-icons">devices_other</span>
<p>${_('remote_dashboard.empty')}</p>
</div>
<div class="rd-desk-grid hidden" id="rd-desk-grid"></div>
<div class="rd-desk-table-wrap hidden" id="rd-desk-table-wrap">
<table class="rd-desk-table">
<thead>
<tr>
<th>${_('remote_dashboard.col_status')}</th>
<th>${_('remote_dashboard.col_id')}</th>
<th>${_('remote_dashboard.col_hostname')}</th>
<th>${_('remote_dashboard.col_platform')}</th>
<th>${_('remote_dashboard.col_last_seen')}</th>
<th></th>
</tr>
</thead>
<tbody id="rd-desk-tbody"></tbody>
</table>
</div>
</div>
</div>
</main>
</div>
</div>
<footer class="rd-desk-footer">
<span class="rd-desk-status-dot online" id="rd-desk-status-dot"></span>
<span id="rd-desk-status-text">${_('remote_dashboard.status_ready')}</span>
<span class="rd-desk-sync-time" id="rd-desk-sync-time"></span>
</footer>
</div>
`
}) %>