Drop Mirror Sync from the Partnership page — rsync gates have one home, on the Rsync page
This commit is contained in:
@@ -126,17 +126,11 @@ textarea.vv-pt-set-input { resize:vertical; white-space:pre; }
|
||||
<div id="vv-pt-xfer-body" style="color:#444;font-size:12px;">Loading…</div>
|
||||
</div>
|
||||
|
||||
<!-- Mirror sync + Actions side by side -->
|
||||
<div style="display:flex;gap:12px;margin-bottom:12px;align-items:stretch;flex-wrap:wrap;">
|
||||
<div class="vv-card" id="vv-pt-sync-card" style="flex:1 1 220px;min-width:0;display:flex;flex-direction:column;">
|
||||
<h3>Mirror Sync</h3>
|
||||
<div id="vv-pt-sync-body" style="flex:1;color:#444;font-size:12px;">Loading…</div>
|
||||
</div>
|
||||
<div class="vv-card" id="vv-pt-actions-card" style="flex:2 1 300px;min-width:0;">
|
||||
<!-- Actions -->
|
||||
<div class="vv-card" id="vv-pt-actions-card" style="margin-bottom:12px;">
|
||||
<h3>Actions</h3>
|
||||
<div id="vv-pt-actions-body" style="color:#444;font-size:12px;">Loading…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Assistant -->
|
||||
<?php if (vv_ai_ui_on()): ?>
|
||||
@@ -682,17 +676,6 @@ function _renderOfflineWarn(cfg) {
|
||||
|
||||
// ── Mirror sync health ──────────────────────────────────────────────────────────
|
||||
|
||||
function _syncToggle(gate, dimmed) {
|
||||
if (!gate) return '';
|
||||
const on = gate.on;
|
||||
const col = on ? '#4caf50' : '#f44336';
|
||||
const bg = on ? '#0a1a0a' : '#2a1212';
|
||||
const op = dimmed ? 'opacity:.4;' : '';
|
||||
return `<span onclick="vvPtToggleSync(this,'${gate.var}',${on ? 0 : 1})"
|
||||
style="cursor:pointer;font-size:9px;font-weight:600;padding:2px 9px;border-radius:10px;
|
||||
background:${bg};color:${col};border:1px solid ${col};white-space:nowrap;margin-left:8px;${op}"
|
||||
title="${gate.var} — click to ${on ? 'disable' : 'enable'}">${on ? 'ON' : 'OFF'}</span>`;
|
||||
}
|
||||
|
||||
// ── Data transferred ──────────────────────────────────────────────────────────
|
||||
//
|
||||
@@ -752,101 +735,6 @@ function _renderXfer(x) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function _renderSync(sync) {
|
||||
const jobs = sync.jobs || {};
|
||||
const gates = sync.gates || {};
|
||||
const intervalMin = sync.interval_min || 30;
|
||||
|
||||
const sCol = s => s === 'ok' ? '#4caf50' : s === 'running' ? '#4a9eff'
|
||||
: s === 'warn' ? '#ff9800' : s === 'error' ? '#f44336' : '#555';
|
||||
const sLbl = s => s === 'ok' ? '✓' : s === 'running' ? '⟳' : s === 'warn' ? '⚠' : s === 'error' ? '✗' : '—';
|
||||
|
||||
function _dur(sec) {
|
||||
if (!sec || sec < 0) return '';
|
||||
if (sec < 60) return sec + 's';
|
||||
if (sec < 3600) return Math.floor(sec / 60) + 'm ' + (sec % 60) + 's';
|
||||
return Math.floor(sec / 3600) + 'h ' + Math.floor((sec % 3600) / 60) + 'm';
|
||||
}
|
||||
|
||||
function _countdown(ts) {
|
||||
if (!ts) return null;
|
||||
const sec = ts - Math.floor(Date.now() / 1000);
|
||||
if (sec <= 0) return 'due';
|
||||
if (sec < 60) return 'in ' + sec + 's';
|
||||
if (sec < 3600) return 'in ' + Math.floor(sec / 60) + 'm';
|
||||
return 'in ' + Math.floor(sec / 3600) + 'h ' + Math.floor((sec % 3600) / 60) + 'm';
|
||||
}
|
||||
|
||||
const globalOff = gates.global && !gates.global.on;
|
||||
|
||||
// Wrap everything in a flex-column so the bottom section anchors naturally
|
||||
let html = '<div style="display:flex;flex-direction:column;gap:0;height:100%;">';
|
||||
|
||||
// Master toggle row
|
||||
if (gates.global) {
|
||||
html += `<div class="vv-pt-row" style="margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid #1c1c1c;">
|
||||
<span class="vv-pt-lbl" style="font-weight:600;color:#999;">All mirroring</span>
|
||||
<span class="vv-pt-val">${_syncToggle(gates.global, false)}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Per-tier rows
|
||||
const tierMeta = {
|
||||
critical: {label: 'Critical', sched: 'every 30 min', desc: 'appdata · partnership · play state'},
|
||||
daily: {label: 'Daily', sched: 'nightly', desc: 'media shares · arr sync · docker'},
|
||||
weekly: {label: 'Weekly', sched: 'weekly', desc: 'bulk shares · updates · cleanup'},
|
||||
};
|
||||
|
||||
// Critical next-run estimate
|
||||
const critJob = jobs.critical;
|
||||
const critNextTs = critJob && critJob.end && critJob.status !== 'running'
|
||||
? critJob.end + intervalMin * 60 : null;
|
||||
|
||||
html += '<div style="display:flex;flex-direction:column;gap:10px;flex:1;">';
|
||||
|
||||
for (const key of ['critical', 'daily', 'weekly']) {
|
||||
const j = jobs[key];
|
||||
const meta = tierMeta[key];
|
||||
const status = j ? j.status : null;
|
||||
const when = j && j.start
|
||||
? (j.status === 'running' ? 'started ' + _relTime(j.start) : _relTime(j.end || j.start))
|
||||
: null;
|
||||
const dur = j && j.start && j.end && j.status !== 'running' ? _dur(j.end - j.start) : '';
|
||||
const summary = j && j.summary ? j.summary : '';
|
||||
|
||||
html += `<div style="background:#0f0f0f;border:1px solid #1e1e1e;border-radius:4px;padding:7px 9px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:3px;">
|
||||
<span style="font-size:11px;font-weight:500;color:#aaa;">${meta.label}
|
||||
<span style="font-size:9px;color:#333;font-weight:normal;margin-left:3px;">${meta.sched}</span>
|
||||
</span>
|
||||
<span style="display:flex;align-items:center;gap:5px;">
|
||||
<span style="font-size:11px;color:${sCol(status)};">${sLbl(status)}</span>
|
||||
${dur ? `<span style="font-size:9px;color:#3a3a3a;">${dur}</span>` : ''}
|
||||
${when ? `<span style="font-size:9px;color:#2e2e2e;">${when}</span>` : ''}
|
||||
${_syncToggle(gates[key], globalOff)}
|
||||
</span>
|
||||
</div>
|
||||
<div style="font-size:9px;color:#2a2a2a;">${meta.desc}</div>
|
||||
${summary ? `<div style="font-size:9px;color:#3a5a3a;margin-top:3px;font-family:monospace;">${_vvSetEsc(summary)}</div>` : ''}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
html += '</div>'; // flex:1
|
||||
|
||||
// Bottom info strip
|
||||
const nextLabel = critNextTs ? _countdown(critNextTs) : null;
|
||||
html += `<div style="margin-top:10px;padding-top:8px;border-top:1px solid #1a1a1a;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:6px;">
|
||||
<span style="font-size:10px;color:#2a2a2a;">interval: ${intervalMin}min</span>
|
||||
${nextLabel ? `<span style="font-size:10px;color:#2e3e2e;">next critical ${nextLabel}</span>` : ''}
|
||||
</div>`;
|
||||
|
||||
if (globalOff) {
|
||||
html += `<div style="font-size:10px;color:#f44336;margin-top:6px;">⚠ All mirroring is off — per-tier switches have no effect until re-enabled.</div>`;
|
||||
}
|
||||
|
||||
html += '</div>'; // outer flex column
|
||||
return html;
|
||||
}
|
||||
|
||||
// ── Config bar ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -1404,7 +1292,6 @@ function _render(data) {
|
||||
|
||||
// Mirror sync health
|
||||
_renderXfer(data.xfer || {});
|
||||
document.getElementById('vv-pt-sync-body').innerHTML = _renderSync(data.sync || {});
|
||||
|
||||
// Node grid — columns based on count
|
||||
const cols = nodes.length <= 2 ? nodes.length : nodes.length <= 4 ? 2 : 3;
|
||||
|
||||
Reference in New Issue
Block a user