diff --git a/platform/static/css/style.css b/platform/static/css/style.css index 8028b1b..6021230 100644 --- a/platform/static/css/style.css +++ b/platform/static/css/style.css @@ -454,7 +454,7 @@ a.sidebar-brand-link { .btn-lg, .btn.lg { padding: 12px 24px; font-size: 14px; } /* ── RESTORE FORM ──────────────────────────────────────────── */ -.restore-form { max-width: 700px; display: flex; flex-direction: column; gap: 0; } +.restore-form { max-width: 820px; display: flex; flex-direction: column; gap: 0; } .form-section { padding: 20px 0; border-bottom: 1px solid var(--border); margin-bottom: 0; } .form-section:last-child { border-bottom: none; } .form-section-title { @@ -476,6 +476,7 @@ a.sidebar-brand-link { .form-input[readonly] { opacity: 0.6; cursor: default; } .form-row { display: flex; gap: 12px; } .form-row .form-group { flex: 1; } +.form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; align-items: end; } .form-note { font-size: 12px; color: var(--text3); margin-top: 8px; } .form-check { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; } .form-check input { accent-color: var(--accent); width: 14px; height: 14px; } @@ -498,6 +499,106 @@ a.sidebar-brand-link { .radio-desc { font-size: 11px; color: var(--text3); font-family: var(--mono); margin-top: 2px; } .radio-desc code { background: var(--border2); padding: 1px 5px; border-radius: 4px; font-family: var(--mono); } +/* ── APP PICKER (restore step 3) ───────────────────────────── */ +.app-picker-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); + gap: 10px; +} +.app-picker-card { cursor: pointer; display: block; position: relative; } +.app-picker-card input { + position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; +} +.app-picker-body { + display: flex; align-items: center; gap: 12px; + padding: 13px 14px; border: 1px solid var(--border2); + border-radius: var(--radius); background: var(--surface2); + transition: border-color var(--trans), background var(--trans), opacity var(--trans); +} +.app-picker-card:hover .app-picker-body { border-color: var(--border2); background: var(--bg4); } +.app-picker-card input:checked + .app-picker-body { + border-color: var(--accent); background: rgba(59,130,246,0.08); +} +.app-picker-card input:not(:checked) + .app-picker-body { opacity: 0.62; } +[data-theme="light"] .app-picker-card input:checked + .app-picker-body { background: rgba(37,99,235,0.06); } +.app-picker-icon { font-size: 20px; line-height: 1; flex-shrink: 0; } +.app-picker-name { font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.3; } +.app-picker-desc { font-size: 11px; color: var(--text3); margin-top: 2px; } +.app-picker-check { + margin-left: auto; width: 22px; height: 22px; border-radius: 50%; + border: 1.5px solid var(--border2); display: flex; align-items: center; justify-content: center; + font-size: 10px; color: transparent; flex-shrink: 0; + transition: background var(--trans), border-color var(--trans), color var(--trans); +} +.app-picker-card input:checked + .app-picker-body .app-picker-check { + background: var(--accent); border-color: var(--accent); color: #fff; +} +.app-picker-actions { margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap; } +.app-picker-note { + color: var(--text3); font-size: 12px; line-height: 1.55; margin-bottom: 14px; + padding: 10px 14px; border-radius: 8px; + background: rgba(59,130,246,0.06); border: 1px solid rgba(59,130,246,0.12); + display: flex; align-items: flex-start; gap: 10px; +} +.app-picker-note i { color: var(--accent2); margin-top: 2px; flex-shrink: 0; } + +/* ── USERS GRID ────────────────────────────────────────────── */ +.users-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 12px; +} +.user-card { + padding: 16px; border-radius: 12px; + background: var(--surface2); border: 1px solid var(--border); + cursor: pointer; + transition: border-color 0.15s, background 0.15s, box-shadow 0.15s; +} +.user-card:hover { border-color: var(--accent); background: var(--bg4); } +.user-card.selected { + border-color: var(--accent); + background: rgba(59,130,246,0.08); + box-shadow: 0 0 0 1px rgba(59,130,246,0.15); +} +[data-theme="light"] .user-card.selected { background: rgba(37,99,235,0.06); } +.user-card-top { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; } +.user-avatar { + width: 42px; height: 42px; border-radius: 10px; + display: flex; align-items: center; justify-content: center; + font-weight: 700; font-size: 17px; color: #fff; flex-shrink: 0; +} +.user-name { font-size: 14px; font-weight: 600; color: var(--text); line-height: 1.3; } +.user-uid { font-size: 11px; color: var(--text3); font-family: var(--mono); margin-top: 2px; } +.user-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; min-height: 24px; } +.user-tag { + display: inline-flex; align-items: center; gap: 5px; + padding: 3px 9px; border-radius: 20px; font-size: 10px; font-weight: 600; + font-family: var(--mono); letter-spacing: 0.03em; +} +.user-tag.docker { background: rgba(37,99,235,0.12); color: #60a5fa; border: 1px solid rgba(37,99,235,0.2); } +.user-tag.linger { background: rgba(167,139,250,0.12); color: #a78bfa; border: 1px solid rgba(167,139,250,0.2); } +.user-tag.vdisk { background: rgba(34,211,238,0.12); color: var(--cyan); border: 1px solid rgba(34,211,238,0.2); } +.user-stats { + display: flex; gap: 10px; padding-top: 12px; + border-top: 1px solid var(--border); +} +.user-stat { + flex: 1; display: flex; flex-direction: column; gap: 3px; + font-size: 12px; color: var(--text3); +} +.user-stat-label { + font-size: 9px; font-weight: 700; letter-spacing: 0.1em; + text-transform: uppercase; font-family: var(--mono); +} +.user-stat strong { font-size: 13px; color: var(--text); font-family: var(--mono); } +.user-detail-table-wrap { + padding: 0 4px 4px; overflow-x: auto; +} +.user-containers-table { margin-top: 4px; } +.user-containers-table th { white-space: nowrap; } +.user-containers-table .ct-image { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.user-containers-table .ct-ports { font-family: var(--mono); font-size: 11px; color: var(--text3); max-width: 180px; } + /* ── LOG CONSOLE ───────────────────────────────────────────── */ .log-header { display: flex; align-items: center; justify-content: space-between; @@ -522,8 +623,8 @@ a.sidebar-brand-link { background: var(--surface2); gap: 10px; flex-wrap: wrap; border: 1px solid var(--border); } -.backup-name { font-family: var(--mono); font-size: 12px; color: var(--text2); } -.backup-actions { display: flex; gap: 6px; align-items: center; flex-shrink: 0; } +.backup-name { font-family: var(--mono); font-size: 12px; color: var(--text2); flex: 1; min-width: 0; word-break: break-all; } +.backup-actions { display: flex; gap: 5px; align-items: center; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; } .btn-delete-backup { color: var(--red) !important; opacity: 0.7; transition: opacity 0.15s; } .btn-delete-backup:hover { opacity: 1; } .btn-audit { color: var(--cyan) !important; opacity: 0.85; } diff --git a/platform/static/js/platform.js b/platform/static/js/platform.js index 7c11589..340ff65 100644 --- a/platform/static/js/platform.js +++ b/platform/static/js/platform.js @@ -382,6 +382,10 @@ async function refreshBackupsList() { } catch (_) {} } +function jsStr(s) { + return String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'"); +} + function renderBackupList(items, id, source) { const node = el(id); if (!node) return; @@ -389,15 +393,26 @@ function renderBackupList(items, id, source) { node.innerHTML = '
- All apps are restored by default. Uncheck the ones you don't want touched — - only the checked apps will be restored, and they're force-restored even if - currently running and healthy. +
+ + All apps are selected by default. Uncheck any app you want to leave untouched — + only checked apps will be restored, and they are force-restored even if currently running and healthy.
-