A29: Design System and Routing UI Drag-and-Drop

This commit is contained in:
Hermes Team 2026-08-25 19:53:24 +07:00
parent d5429da63d
commit a8c37ca6e3
3 changed files with 492 additions and 1372 deletions

File diff suppressed because it is too large Load diff

View file

@ -177,15 +177,49 @@
</section> </section>
<!-- 3. ROUTING VIEW (Main Control Center) --> <!-- 3. ROUTING VIEW (Main Control Center) -->
<section id="view-routing" class="view-pane"> <section id="view-routing" class="view-pane">
<div class="view-header-note"> <div class="view-header-note" style="display:flex; justify-content: space-between; align-items: center;">
Главный центр управления маршрутизацией: перетаскивайте узлы для смены приоритета (Основной → Резерв 1 → Резерв 2), настраивайте рабочие модели и управляйте составом цепочек. <div>
<i class="fa-solid fa-circle-info" style="color:var(--status-warning);"></i>
Перетащите аккаунт из правой панели в нужную роль и установите порядок (приоритет) использования.
<br>
Система будет использовать аккаунты сверху вниз по списку при выполнении задач с учётом квот и состояния.
</div> </div>
<div class="routing-pipelines-list" id="routing-pipelines-container"> </div>
<div class="routing-grid" style="display: flex; gap: 24px; height: calc(100vh - 150px);">
<!-- LEFT COLUMN: Roles -->
<div class="routing-left-col" id="routing-roles-container" style="flex: 2; overflow-y: auto; padding-right: 8px;">
<!-- Rendered by app.js --> <!-- Rendered by app.js -->
</div> </div>
<!-- RIGHT COLUMN: Available Accounts -->
<div class="routing-right-col" style="flex: 1; display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius-md); background-color: var(--surface-muted);">
<div style="padding: 16px; border-bottom: 1px solid var(--border-subtle);">
<div style="display: flex; justify-content: space-between; margin-bottom: 12px;">
<h3 style="font-family: var(--font-title); font-size: 14px; text-transform: uppercase;">Доступные аккаунты</h3>
<span id="available-accounts-count" class="text-muted" style="font-size: 12px;">0 аккаунтов</span>
</div>
<div class="input-group">
<input type="text" id="routing-account-search" placeholder="Поиск по аккаунтам, ролям или моделям..." style="width: 100%;">
</div>
<div style="display: flex; gap: 8px; margin-top: 8px;">
<select id="routing-filter-provider" style="flex:1;"><option value="all">Все провайдеры</option></select>
<select id="routing-filter-status" style="flex:1;"><option value="all">Все статусы</option></select>
</div>
</div>
<div class="available-accounts-list" id="routing-available-container" style="flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 8px;">
<!-- Rendered by app.js -->
</div>
<div style="padding: 16px; text-align: center; font-size: 12px; color: var(--text-muted); border-top: 1px dashed var(--border-subtle);">
<i class="fa-solid fa-arrows-up-down-left-right"></i> Перетащите аккаунт в нужную роль или нажмите «+» для добавления
</div>
</div>
</div>
</section> </section>
<!-- 4. ANALYTICS VIEW (P0-1 & P0-5) --> <!-- 4. ANALYTICS VIEW (P0-1 & P0-5) -->
<section id="view-analytics" class="view-pane"> <section id="view-analytics" class="view-pane">
<div class="view-header-note"> <div class="view-header-note">
@ -505,5 +539,16 @@
<!-- Application Logic --> <!-- Application Logic -->
<script src="app.js"></script> <script src="app.js"></script>
<script>
const urlParams = new URLSearchParams(window.location.search);
const theme = urlParams.get('theme');
if (theme) {
document.body.setAttribute('data-theme', theme);
const sel = document.getElementById('theme-selector');
if (sel) sel.value = theme;
}
</script>
</body> </body>
</html> </html>

View file

@ -1,33 +1,7 @@
/* Hermes Hub Web Client — Dark Theme & Cockpit Design System */
/* Theme definitions according to A29 Design System "Крона" */
:root { :root {
--bg-base: #061916; /* Common variables */
--bg-sidebar: #08221E;
--bg-header: #071B18;
--bg-statusbar: #061512;
--bg-modal-backdrop: rgba(2, 11, 9, 0.85);
--surface: #0B2520;
--surface-hover: #12342C;
--surface-active: #194535;
--surface-selected: #173B2E;
--surface-muted: #091E1A;
--border: #36513B;
--border-subtle: #203A2D;
--border-accent: #B78525;
--border-hover: #537456;
--text-primary: #F8F0DC;
--text-secondary: #D1C7AE;
--text-muted: #8FA395;
--text-accent: #E0B84E;
--accent: #C89A2B;
--accent-hover: #E0B84E;
--accent-pressed: #A9781E;
--accent-dim: rgba(200, 154, 43, 0.15);
--status-healthy: #72C943; --status-healthy: #72C943;
--status-warning: #E1A62B; --status-warning: #E1A62B;
--status-error: #E45C4F; --status-error: #E45C4F;
@ -44,14 +18,107 @@
--radius-md: 6px; --radius-md: 6px;
--radius-lg: 8px; --radius-lg: 8px;
--font-ui: "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, sans-serif; --font-ui: 'Inter', "Segoe UI", -apple-system, sans-serif;
--font-title: "Cinzel", "Segoe UI", serif, sans-serif; --font-title: "Cinzel", "Cinzel Decorative", "Segoe UI", serif;
--font-mono: "Consolas", "Courier New", monospace; --font-mono: "Consolas", "Courier New", monospace;
--header-height: 58px; --header-height: 58px;
--sidebar-width: 230px; --sidebar-width: 230px;
} }
/* Default DARK theme */
:root, body[data-theme="dark"] {
--bg-base: #101510;
--bg-sidebar: #0D120D;
--bg-header: #0D120D;
--bg-statusbar: #0D120D;
--bg-modal-backdrop: rgba(16, 21, 16, 0.85);
--surface: #1A2A1F;
--surface-hover: #2F4A36;
--surface-active: #3C5C44;
--surface-selected: #28402F;
--surface-muted: #152219;
--border: #36513B;
--border-subtle: #203A2D;
--border-accent: #CDAA64;
--border-hover: #537456;
--text-primary: #F7F1E3;
--text-secondary: #B0B8B2;
--text-muted: #8FA395;
--text-accent: #CDAA64;
--accent: #CDAA64;
--accent-hover: #E0B84E;
--accent-pressed: #A9781E;
--accent-dim: rgba(205, 170, 100, 0.15);
}
/* MEDIUM theme (cream cards on green canvas) */
body[data-theme="medium"] {
--bg-base: #1A2A1F;
--bg-sidebar: #152219;
--bg-header: #152219;
--bg-statusbar: #152219;
--bg-modal-backdrop: rgba(26, 42, 31, 0.85);
--surface: #F7F1E3;
--surface-hover: #F0EAD6;
--surface-active: #E6DFCB;
--surface-selected: #EAE3CF;
--surface-muted: #2F4A36;
--border: #D1C7AE;
--border-subtle: #E0D7BF;
--border-accent: #CDAA64;
--border-hover: #B7A88D;
--text-primary: #101510;
--text-secondary: #2F4A36;
--text-muted: #537456;
--text-accent: #B78525;
--accent: #B78525;
--accent-hover: #CDAA64;
--accent-pressed: #9A6F1D;
--accent-dim: rgba(183, 133, 37, 0.15);
}
/* LIGHT theme */
body[data-theme="light"] {
--bg-base: #F7F1E3;
--bg-sidebar: #F0EAD6;
--bg-header: #F0EAD6;
--bg-statusbar: #E6DFCB;
--bg-modal-backdrop: rgba(247, 241, 227, 0.85);
--surface: #FFFFFF;
--surface-hover: #FDFBF7;
--surface-active: #F4EEDF;
--surface-selected: #FAF7F0;
--surface-muted: #F0EAD6;
--border: #D1C7AE;
--border-subtle: #EAE3CF;
--border-accent: #CDAA64;
--border-hover: #C5BAA1;
--text-primary: #101510;
--text-secondary: #2F4A36;
--text-muted: #537456;
--text-accent: #B78525;
--accent: #B78525;
--accent-hover: #CDAA64;
--accent-pressed: #9A6F1D;
--accent-dim: rgba(183, 133, 37, 0.15);
}
/* Hermes Hub Web Client — Dark Theme & Cockpit Design System */
* { * {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
@ -1559,20 +1626,114 @@ body {
} }
/* ── Light Theme Override ── */ /* ── Light Theme Override ── */
body[data-theme="light"], body.theme-light {
--bg-base: #F4F6F8;
--bg-sidebar: #E9ECEF; /* Drag and Drop Styles */
--bg-header: #FFFFFF; .draggable-item {
--bg-statusbar: #DEE2E6; cursor: grab;
--surface: #FFFFFF; user-select: none;
--surface-hover: #F1F3F5; }
--surface-active: #E9ECEF; .draggable-item:active {
--surface-selected: #E2E6EA; cursor: grabbing;
--surface-muted: #F8F9FA; }
--border: #CED4DA; .drop-zone {
--border-subtle: #DEE2E6; min-height: 40px;
--border-accent: #B78525; border: 1px dashed var(--border);
--text-primary: #212529; border-radius: var(--radius-sm);
--text-secondary: #495057; display: flex;
--text-muted: #6C757D; align-items: center;
justify-content: center;
font-size: 11px;
color: var(--text-muted);
transition: all 0.2s;
margin-top: 8px;
}
.drop-zone.drag-over {
border-color: var(--accent);
background-color: var(--accent-dim);
color: var(--accent);
}
.role-section {
background-color: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-md);
margin-bottom: 16px;
display: flex;
flex-direction: column;
}
.role-header {
padding: 12px 16px;
border-bottom: 1px solid var(--border-subtle);
display: flex;
justify-content: space-between;
align-items: center;
}
.role-header h4 {
font-family: var(--font-title);
color: var(--text-primary);
font-size: 14px;
margin: 0;
display: flex;
align-items: center;
gap: 8px;
}
.role-badge {
font-size: 10px;
background-color: var(--accent-dim);
color: var(--accent);
padding: 2px 6px;
border-radius: 4px;
font-family: var(--font-ui);
}
.account-row {
display: grid;
grid-template-columns: 24px 20px 200px 120px 120px 150px 80px 80px 24px;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-bottom: 1px solid var(--border-subtle);
background-color: var(--surface);
font-size: 12px;
}
.account-row:last-child {
border-bottom: none;
}
.account-row.drag-over {
border-top: 2px solid var(--accent);
}
.drag-handle {
color: var(--text-muted);
cursor: grab;
text-align: center;
}
.provider-logo-sm {
width: 16px;
height: 16px;
border-radius: 2px;
}
.available-account-card {
background-color: var(--surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
padding: 10px;
display: flex;
align-items: center;
gap: 10px;
cursor: grab;
}
.available-account-card:hover {
border-color: var(--border);
background-color: var(--surface-hover);
}
.grid-header {
display: grid;
grid-template-columns: 24px 20px 200px 120px 120px 150px 80px 80px 24px;
align-items: center;
gap: 8px;
padding: 8px 12px;
font-size: 10px;
color: var(--text-muted);
text-transform: uppercase;
border-bottom: 1px solid var(--border-subtle);
background-color: var(--surface-muted);
} }