Merge the partnership settings into one section, arrays and scalars together

They were two sections drawing the same endpoint, split on whether a value happened to be a list — with two Save buttons for one subject.
This commit is contained in:
Gmer4Lfe
2026-08-17 16:11:52 -04:00
parent e024a29157
commit 25e055a7ac
+20 -103
View File
@@ -172,8 +172,6 @@ textarea.vv-pt-set-input { resize:vertical; white-space:pre; }
Collapsed together they cost one line until opened. -->
<div id="vv-pt-settings-body" style="display:none;margin-top:10px;">
<div id="vv-pt-arrays-wrap" style="display:none;margin-bottom:16px;"></div>
<div id="vv-pt-settings-conf"></div>
</div>
</div>
@@ -418,103 +416,10 @@ async function vvPtStopSeed(btn) {
// ── Array settings cards ───────────────────────────────────────────────────────
let _vvArrFiles = null;
function _vvInitArrayCards() {
if (_vvArrFiles !== null) return;
_vvArrFiles = false; // loading sentinel
fetch('/plugins/varaverk/api/partnership_settings.php?_=' + Date.now())
.then(r => r.json())
.then(d => {
_vvArrFiles = (d.ok && d.files) ? d.files : [];
_vvRenderArrayCards();
})
.catch(() => { _vvArrFiles = []; document.getElementById('vv-pt-arrays-wrap').style.display = 'none'; });
}
function _vvRenderArrayCards() {
const files = _vvArrFiles;
if (!files || !files.length) { document.getElementById('vv-pt-arrays-wrap').style.display = 'none'; return; }
let html = '';
let hasArrays = false;
for (const f of files) {
for (const g of f.groups) {
for (const fld of g.fields) {
if (fld.type === 'scalar') continue;
hasArrays = true;
const rows = Math.min(16, (fld.value.match(/\n/g) || []).length + 2);
const attrs = `class="vv-pt-set-input" data-key="${_vvSetEsc(fld.key)}" `
+ `data-file="${_vvSetEsc(fld.file)}" data-type="${_vvSetEsc(fld.type)}" `
+ `data-orig="${_vvSetEsc(fld.value)}" oninput="vvPtArrChanged(this)"`;
html += `<div class="vv-pt-set-file">
<div class="vv-pt-set-file-hdr" onclick="vvPtToggleNode(this)">
<span>${_vvSetEsc(fld.key)}</span>
<div style="display:flex;align-items:center;gap:6px;">
${fld.desc ? `<span style="font-size:9px;color:#444;font-weight:normal;font-family:inherit;">${_vvSetEsc(fld.desc)}</span>` : ''}
<span class="vv-pt-set-chev">▸</span>
</div>
</div>
<div class="vv-pt-set-file-body" style="display:none;">
<textarea ${attrs} rows="${rows}">${_vvSetEsc(fld.value)}</textarea>
</div>
</div>`;
}
}
}
const wrap = document.getElementById('vv-pt-arrays-wrap');
if (!hasArrays) { wrap.style.display = 'none'; return; }
wrap.style.display = '';
wrap.innerHTML = `<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
<div class="vv-cl-title" style="margin:0;">Array settings</div>
<button id="vv-pt-arrays-save" class="vv-pt-action-btn run"
onclick="vvPtSaveArrays(this)" style="display:none;">Save Changes</button>
</div>
${html}
</div>`;
}
function vvPtArrChanged(el) {
el.classList.toggle('changed', el.value !== el.dataset.orig);
const any = document.querySelector('#vv-pt-arrays-wrap .vv-pt-set-input.changed');
const saveBtn = document.getElementById('vv-pt-arrays-save');
if (saveBtn) saveBtn.style.display = any ? '' : 'none';
}
async function vvPtSaveArrays(btn) {
const changedEls = document.querySelectorAll('#vv-pt-arrays-wrap .vv-pt-set-input.changed');
if (!changedEls.length) return;
const changes = [];
changedEls.forEach(el => changes.push({key: el.dataset.key, file: el.dataset.file, type: el.dataset.type, value: el.value}));
if (!await vvConfirm(`Save ${changes.length} changed setting(s)?`)) return;
btn.disabled = true; btn.textContent = '⟳ Saving…';
fetch('/plugins/varaverk/api/confform.php', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token: typeof csrf_token !== 'undefined' ? csrf_token : '',
id: '__settings__',
changes: JSON.stringify(changes)
})
})
.then(r => r.json())
.then(d => {
btn.disabled = false;
if (d.ok) {
changedEls.forEach(el => { el.dataset.orig = el.value; el.classList.remove('changed'); });
btn.textContent = '✓ Saved';
_vvArrFiles = null; // invalidate so next init re-fetches
setTimeout(() => _vvInitArrayCards(), 600);
} else {
btn.textContent = 'Save Changes';
vvAlert('Save failed: ' + (d.error ?? 'Unknown error'));
}
})
.catch(e => { btn.disabled = false; btn.textContent = 'Save Changes'; vvAlert('Error: ' + e); });
}
// ── Settings panel ─────────────────────────────────────────────────────────────
let _vvSetLoaded = false;
@@ -555,29 +460,43 @@ function vvPtLoadSettings() {
.catch(e => { body.innerHTML = '<div style="color:#f88;font-size:12px;padding:8px 0;">Error: ' + e + '</div>'; });
}
// Every partnership field, scalars and arrays together, in the order the conf declares them.
//
// These were two sections drawing the same endpoint and splitting it on whether a value happened
// to be a list: 25 scalars under "Partnership settings", 10 arrays under "Array settings", each
// with its own Save. Nothing about the subject is divided that way — PROVISION_SHARES and
// PARTNERSHIP_ENABLED are the same conversation — and the split meant looking in two places for
// one setting, with the arrays half easy to mistake for something belonging to another page.
//
// Both render into the same .vv-pt-set-field wrapper so a list and a value read as siblings; the
// only difference is a textarea instead of an input, sized to the content.
function vvPtRenderSettings(files) {
let html = '';
for (const f of files) {
const label = f.file === 'master.conf' ? 'master.conf — shared' : f.file + ' — this host';
const scalarFields = f.groups.flatMap(g => g.fields.filter(fld => fld.type === 'scalar'));
if (!scalarFields.length) continue;
const fields = f.groups.flatMap(g => g.fields);
if (!fields.length) continue;
html += `<div class="vv-pt-set-file">
<div class="vv-pt-set-file-hdr" onclick="vvPtToggleNode(this)">
<span>${_vvSetEsc(label)}</span><span class="vv-pt-set-chev">▾</span>
</div>
<div class="vv-pt-set-file-body">`;
for (const fld of scalarFields) {
for (const fld of fields) {
const isArr = fld.type !== 'scalar';
const attrs = `class="vv-pt-set-input" data-key="${_vvSetEsc(fld.key)}" `
+ `data-file="${_vvSetEsc(fld.file)}" data-type="${_vvSetEsc(fld.type)}" `
+ `data-orig="${_vvSetEsc(fld.value)}" oninput="vvPtSetChanged(this)"`;
html += `<div class="vv-pt-set-field">
<div class="vv-pt-set-key">${_vvSetEsc(fld.key)}</div>`;
<div class="vv-pt-set-key">${_vvSetEsc(fld.key)}${isArr ? ' <span style="color:#3a3a3a;font-weight:normal;">list</span>' : ''}</div>`;
if (fld.desc) html += `<div class="vv-pt-set-desc">${_vvSetEsc(fld.desc)}</div>`;
html += `<input type="text" ${attrs} value="${_vvSetEsc(fld.value)}"></div>`;
html += isArr
? `<textarea ${attrs} rows="${Math.min(16, (fld.value.match(/\n/g) || []).length + 2)}">${_vvSetEsc(fld.value)}</textarea>`
: `<input type="text" ${attrs} value="${_vvSetEsc(fld.value)}">`;
html += `</div>`;
}
html += `</div></div>`;
}
return html || '<div style="color:#555;font-size:12px;padding:8px 0;">No scalar settings available.</div>';
return html || '<div style="color:#555;font-size:12px;padding:8px 0;">No settings available.</div>';
}
function vvPtToggleNode(hdr) {
@@ -1304,8 +1223,6 @@ function _render(data) {
// Actions
document.getElementById('vv-pt-actions-body').innerHTML = _renderActions(nodes, cfg);
// Array settings cards above Actions — load once
_vvInitArrayCards();
// Settings panel — host1 only
document.getElementById('vv-pt-settings-card').style.display = isOwner ? '' : 'none';