Replace platform checkboxes with pill toggles in social scheduler

Target selection is now a row of compact pills (per platform+target)
instead of grouped checkbox lists. Clicking a pill toggles it active;
the image icon inside only activates when the pill is selected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Bashy 2026-06-04 20:09:57 +03:00
parent 24c9deef94
commit 5b801514b2
2 changed files with 51 additions and 39 deletions

View file

@ -458,6 +458,21 @@ pre { color: #e6edf3; }
.social-platform-mastodon { background: rgba(99,100,255,.25); color: #a29cf4; border-color: rgba(99,100,255,.4); } .social-platform-mastodon { background: rgba(99,100,255,.25); color: #a29cf4; border-color: rgba(99,100,255,.4); }
.social-platform-linkedin { background: rgba(10,102,194,.25); color: #5ba3d9; border-color: rgba(10,102,194,.4); } .social-platform-linkedin { background: rgba(10,102,194,.25); color: #5ba3d9; border-color: rgba(10,102,194,.4); }
.social-pill {
display: inline-flex; align-items: center; gap: 5px;
padding: 3px 10px; border-radius: 999px;
border: 1px solid var(--hm-border); cursor: pointer;
font-size: .8rem; color: var(--hm-muted);
background: transparent; user-select: none; transition: all .15s;
}
.social-pill:hover { border-color: #8b949e; color: #c9d1d9; }
.social-pill.active[data-platform="discord"] { background: rgba(88,101,242,.2); border-color: rgba(88,101,242,.7); color: #9aa0f5; }
.social-pill.active[data-platform="mastodon"] { background: rgba(99,100,255,.2); border-color: rgba(99,100,255,.7); color: #a29cf4; }
.social-pill.active[data-platform="linkedin"] { background: rgba(10,102,194,.2); border-color: rgba(10,102,194,.7); color: #5ba3d9; }
.social-pill-img { opacity: .35; font-size: .75em; transition: opacity .15s; }
.social-pill:hover .social-pill-img { opacity: .6; }
.social-pill.img-on .social-pill-img { opacity: 1; }
.social-post-card .card-body { border-left: 3px solid transparent; border-radius: inherit; } .social-post-card .card-body { border-left: 3px solid transparent; border-radius: inherit; }
.social-post-card.status-pending .card-body { border-left-color: #e3b341; } .social-post-card.status-pending .card-body { border-left-color: #e3b341; }
.social-post-card.status-done .card-body { border-left-color: #3fb950; } .social-post-card.status-done .card-body { border-left-color: #3fb950; }

View file

@ -3896,55 +3896,52 @@ window.addEventListener('DOMContentLoaded', () => {
el.innerHTML = '<div class="text-muted small">No eligible projects configured. <a href="/settings">Go to Settings</a> to enable projects.</div>'; el.innerHTML = '<div class="text-muted small">No eligible projects configured. <a href="/settings">Go to Settings</a> to enable projects.</div>';
return; return;
} }
const platformIcon = { discord: 'bi-discord', mastodon: 'bi-mastodon', linkedin: 'bi-linkedin' };
const platformLabel = { discord: 'Discord', mastodon: 'Mastodon', linkedin: 'LinkedIn' };
el.innerHTML = _targets.map(group => { const pills = _targets.flatMap(group =>
const platforms = ['discord', 'mastodon', 'linkedin'].filter(p => group.targets[p]?.length); ['discord', 'mastodon', 'linkedin'].flatMap(platform =>
if (!platforms.length) return ''; (group.targets[platform] || []).map(t => {
return `<div class="mb-3"> const active = prefill?.some(x => x.project_id == group.project_id && x.platform === platform && x.target_key === t.key);
<div class="small fw-semibold text-muted mb-1">${esc(group.project_name)}</div> const imgOn = prefill?.some(x => x.project_id == group.project_id && x.platform === platform && x.target_key === t.key && x.include_image);
${platforms.map(platform => ` return `<div class="social-pill${active ? ' active' : ''}${imgOn ? ' img-on' : ''}"
<div class="mb-2"> data-pid="${group.project_id}" data-platform="${platform}" data-key="${esc(t.key)}"
<div class="small text-muted mb-1"><i class="bi ${platformIcon[platform]}"></i> ${platformLabel[platform]}</div> title="${esc(group.project_name)}">
${group.targets[platform].map(t => { <i class="bi bi-${platform}"></i>${esc(t.label)}
const cbId = `st_${group.project_id}_${platform}_${t.key}`; <span class="social-pill-img${_hasImage ? '' : ' d-none'}" title="Include image"><i class="bi bi-image-fill"></i></span>
const imgId = `si_${group.project_id}_${platform}_${t.key}`; </div>`;
const checked = prefill?.some(x => x.project_id == group.project_id && x.platform === platform && x.target_key === t.key) ? 'checked' : ''; })
const imgChecked = prefill?.some(x => x.project_id == group.project_id && x.platform === platform && x.target_key === t.key && x.include_image) ? 'checked' : ''; )
return `<div class="d-flex align-items-center gap-2 mb-1"> );
<input class="form-check-input social-target-cb" type="checkbox" id="${cbId}"
data-pid="${group.project_id}" data-platform="${platform}" data-key="${esc(t.key)}" ${checked}> el.innerHTML = `<div class="d-flex flex-wrap gap-2">${pills.join('')}</div>`;
<label class="form-check-label small flex-grow-1" for="${cbId}">${esc(t.label)}</label>
<input class="form-check-input social-img-cb ${_hasImage ? '' : 'd-none'}" type="checkbox" id="${imgId}" el.querySelectorAll('.social-pill').forEach(pill => {
data-for="${cbId}" title="Include image" ${imgChecked}> pill.addEventListener('click', e => {
<label class="form-check-label small text-muted ${_hasImage ? '' : 'd-none'}" for="${imgId}"> if (e.target.closest('.social-pill-img')) {
<i class="bi bi-image"></i> if (pill.classList.contains('active')) pill.classList.toggle('img-on');
</label> return;
</div>`; }
}).join('')} pill.classList.toggle('active');
</div>`).join('')} if (pill.classList.contains('active') && _hasImage) pill.classList.add('img-on');
</div>`; else if (!pill.classList.contains('active')) pill.classList.remove('img-on');
}).join(''); });
});
} }
function toggleImageCols(show) { function toggleImageCols(show) {
_hasImage = show; _hasImage = show;
page.querySelectorAll('.social-img-cb, .social-img-cb + label').forEach(el => { page.querySelectorAll('.social-pill-img').forEach(el => el.classList.toggle('d-none', !show));
el.classList.toggle('d-none', !show); if (show) page.querySelectorAll('.social-pill.active').forEach(p => p.classList.add('img-on'));
}); if (!show) page.querySelectorAll('.social-pill').forEach(p => p.classList.remove('img-on'));
if (show) page.querySelectorAll('.social-img-cb').forEach(cb => { cb.checked = true; });
} }
function collectTargets() { function collectTargets() {
const targets = []; const targets = [];
page.querySelectorAll('.social-target-cb:checked').forEach(cb => { page.querySelectorAll('.social-pill.active').forEach(pill => {
const imgCb = page.querySelector(`.social-img-cb[data-for="${cb.id}"]`);
targets.push({ targets.push({
project_id: +cb.dataset.pid, project_id: +pill.dataset.pid,
platform: cb.dataset.platform, platform: pill.dataset.platform,
target_key: cb.dataset.key, target_key: pill.dataset.key,
include_image: imgCb?.checked ? 1 : 0, include_image: pill.classList.contains('img-on') ? 1 : 0,
}); });
}); });
return targets; return targets;