Files
Tobias GesellchenandClaude Sonnet 5 57c0895063 fix(admin-ui): make Migration tab action buttons consistently reachable
Follow-up on #621: the Reboot Speaker button (plus Revert to Defaults,
Enable SSH, Disable SSH) was reachable only after expanding the
collapsed "Customize this migration" section and scrolling past three
fieldsets and the XML/telnet diff panes. Meanwhile every other real
action elsewhere in the admin UI (Save Settings, Apply Suggested Plan,
Start Sync, ...) is visible by default.

- Move Revert to Defaults and Reboot Speaker into an always-visible
  "Speaker controls" row directly under the Migration State card.
- Move Enable/Disable SSH into the Preconditions table, inline with the
  SSH (remote_services) status row, sized like the existing "Trust CA
  Now" button next to the CA/TLS row. script.js now only rewrites the
  inner status span on re-render (matching the CA/TLS pattern) so the
  buttons survive summary refreshes.
- Add shared .btn-primary/.btn-danger CSS classes so button color
  consistently means the same thing everywhere (primary = confirm,
  danger = destructive) instead of ad-hoc inline colors; applied to
  Save Settings, Apply Suggested/Custom Plan, Enable/Disable SSH,
  Revert to Defaults, and Trust CA Now. Removed decorative gray from
  Reboot Speaker and the connection/DNS test buttons.
- Replace the "Cancel" button (which only hid the whole summary panel,
  not any of the actions it sat beside) with a "✕ Hide" control next
  to the "Migration Summary for <device>" heading, alongside a new
  "↻ Reload" shortcut for refreshSummary().
- Remove the now-unneeded force-open-the-details hack in migrate()
  since Reboot no longer lives inside any collapsed container.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 20:58:51 +02:00

260 lines
6.9 KiB
CSS

body { font-family: sans-serif; margin: 20px; }
/* Shared accent bar (matches the app navbar and the landing page). It
breaks out of the body's 20px margin to sit full-bleed at the top. */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
height: 52px;
padding: 0 1.25rem;
margin: -20px -20px 16px;
background: #1a1a1a;
color: #fff;
}
.topbar .brand { display: flex; align-items: center; gap: .5rem; text-decoration: none; color: inherit; }
/* Keep the braille mark in its brand colours; the rest of the bar stays monochrome. */
.topbar .brand img { width: 24px; height: 24px; }
.topbar .brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.topbar .brand-name { font-size: 1.1rem; font-weight: 600; letter-spacing: .02em; }
.topbar .brand-subtitle { font-size: .7rem; font-weight: 400; opacity: .7; }
.topbar .bar-links { display: flex; align-items: center; gap: .25rem; }
.topbar .bar-links a {
display: inline-flex;
align-items: center;
gap: .3rem;
color: #fff;
font-size: .85rem;
opacity: .85;
text-decoration: none;
padding: .35rem .5rem;
border-radius: 4px;
}
.topbar .bar-links a:hover { opacity: 1; background: rgba(255, 255, 255, .12); }
@media (max-width: 480px) { .topbar .brand-subtitle { display: none; } }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
button { padding: 5px 10px; cursor: pointer; }
.status { margin-top: 10px; padding: 10px; border: 1px solid #ccc; display: none; }
.summary-box { margin-top: 20px; padding: 15px; border: 1px solid #aaa; background-color: #f9f9f9; }
pre { background-color: #eee; padding: 10px; overflow-x: auto; font-size: 12px; }
.diff-container { display: flex; gap: 10px; }
.diff-pane pre {
background: #f8f9fa;
border: 1px solid #eee;
padding: 10px;
font-size: 0.85em;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-all;
}
.diff-added {
background-color: #e6ffed !important;
text-decoration: none;
}
.diff-removed {
background-color: #ffeef0 !important;
text-decoration: line-through;
}
.config-header { font-weight: bold; margin-bottom: 5px; display: block; }
/* Tabs */
.tabs { margin-top: 20px; }
.tab-buttons { display: flex; border-bottom: 1px solid #ddd; margin-bottom: 20px; }
.tab-btn { background: #f8f8f8; border: 1px solid #ddd; border-bottom: none; padding: 10px 20px; margin-right: 5px; border-top-left-radius: 4px; border-top-right-radius: 4px; }
.tab-btn:hover { background: #eee; }
.tab-btn.active { background: white; border-bottom: 2px solid #2196F3; font-weight: bold; }
.tab-content { display: none; padding: 10px; }
.tab-content.active { display: block; }
.device-selection {
margin-bottom: 20px;
padding: 10px;
background-color: #f0f7ff;
border-radius: 4px;
border: 1px solid #d0e0f0;
}
.device-selection label {
font-weight: bold;
margin-right: 10px;
}
.device-selection select {
padding: 5px;
min-width: 250px;
}
/* Guide & Overview styles */
.guide-steps li {
margin-bottom: 15px;
line-height: 1.5;
}
.guide-steps strong {
color: #2196F3;
font-size: 1.1em;
}
.info-box {
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.prerequisite-box {
background-color: #fffde7;
border-left: 4px solid #fff176;
}
.safety-box {
background-color: #e3f2fd;
border-left: 4px solid #2196f3;
margin-top: 20px;
}
.info-box a {
color: #0d47a1;
font-weight: bold;
text-decoration: underline;
}
.info-box a:hover {
text-decoration: none;
}
.btn-danger {
background-color: #f44336;
color: white;
border: none;
padding: 5px 10px;
}
.btn-danger:hover {
background-color: #d32f2f;
}
/* .btn-primary marks the one "do the thing" confirm action of a panel
(Save Settings, Apply Suggested/Custom Plan, Enable SSH, …). Everything
else stays the plain default button so color consistently signals the
same two meanings everywhere: primary = confirm, danger = destructive. */
.btn-primary {
background-color: #2196f3;
color: white;
border: none;
padding: 5px 10px;
}
.btn-primary:hover {
background-color: #1769aa;
}
.badge {
padding: 2px 6px;
border-radius: 4px;
font-size: 0.85em;
font-weight: bold;
}
.stats-list {
list-style: none;
padding: 0;
margin: 0;
}
.stats-list li {
padding: 5px 0;
border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.stats-list li:last-child {
border-bottom: none;
}
.category-self { background-color: #e3f2fd; color: #0d47a1; }
.category-upstream { background-color: #f3e5f5; color: #7b1fa2; }
.status-success { background-color: #e8f5e9; color: #2e7d32; }
.status-error { background-color: #ffebee; color: #c62828; }
.info-toggle {
display: inline-block;
width: 18px;
height: 18px;
line-height: 18px;
text-align: center;
background-color: #607D8B;
color: white;
border-radius: 50%;
font-size: 12px;
cursor: pointer;
margin-left: 5px;
font-style: normal;
user-select: none;
}
.info-toggle:hover {
background-color: #455A64;
}
.info-details {
display: none;
background-color: #f0f7ff;
border: 1px solid #d0e0f0;
padding: 10px;
margin-top: 5px;
border-radius: 4px;
font-size: 0.85em;
color: #333;
line-height: 1.4;
max-width: 400px;
}
.info-details code {
background-color: #e3f2fd;
padding: 2px 4px;
border-radius: 3px;
font-family: monospace;
}
.badge {
padding: 2px 8px;
border-radius: 10px;
font-size: 0.8em;
font-weight: bold;
}
.logo {
height: 40px;
vertical-align: middle;
margin-right: 10px;
margin-top: -5px;
}
/* Integration panels (collapsible, status visible when collapsed) */
.integration-panel {
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 8px;
background: #fcfcfc;
}
.integration-summary {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
cursor: pointer;
list-style: none;
user-select: none;
}
.integration-summary::-webkit-details-marker { display: none; }
.integration-summary::before {
content: "▸";
color: #888;
transition: transform 0.15s ease;
}
.integration-panel[open] > .integration-summary::before { transform: rotate(90deg); }
.integration-name { font-weight: bold; flex: 1; }
.integration-body {
padding: 10px 14px 14px 30px;
border-top: 1px solid #eee;
}
.integration-badge {
font-size: 0.8em;
padding: 2px 8px;
border-radius: 10px;
white-space: nowrap;
}
.integration-badge.badge-active { background: #e6ffed; color: #137333; border: 1px solid #b7e0c0; }
.integration-badge.badge-saved { background: #fff4e5; color: #a5600a; border: 1px solid #f0d2a8; }
.integration-badge.badge-inactive { background: #f1f3f4; color: #666; border: 1px solid #ddd; }