Files
rm-circle-team-router/public/announce.js
T
martbost e3f6b00f95 Make event pop-up admin-toggleable; drop it from join pages
The Weekly Huddle overlay now reads its content from /api/announce (config-
driven) instead of a baked-in block, and Admin -> Settings -> Event pop-up
exposes a toggle plus editable fields (eyebrow, flyer, join URL, date, times,
auto-hide, event id). Defaults are seeded into config on boot so the panel
reflects real state. Removed the include from join.html so it never interrupts
the join flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 05:28:03 -05:00

124 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* announce.js — dismissible event pop-up overlay (RM Circle).
Content is admin-controlled: fetched from /api/announce (edit it under
Admin → Settings → Event pop-up; no deploy needed). Self-gating: shows once
per browser until dismissed, and never after `expiresUTC`. */
(function () {
function parseTimes(s) {
return String(s || '').split(/\r?\n+/).map(function (l) { return l.trim(); }).filter(Boolean)
.map(function (l) { var i = l.indexOf('|'); return i < 0 ? [l, ''] : [l.slice(0, i).trim(), l.slice(i + 1).trim()]; });
}
function start(EV) {
if (!EV || !EV.enabled || !EV.id || !EV.img) return;
if (EV.expiresUTC) { var exp = Date.parse(EV.expiresUTC); if (exp && Date.now() > exp) return; }
var KEY = 'rmc-announce-' + EV.id;
try { if (localStorage.getItem(KEY) === 'done') return; } catch (e) {}
EV.timesArr = parseTimes(EV.times);
var back, card;
function done() { try { localStorage.setItem(KEY, 'done'); } catch (e) {} }
function close() {
if (!back) return;
back.style.opacity = '0';
setTimeout(function () { if (back && back.parentNode) back.parentNode.removeChild(back); back = null; }, 260);
document.removeEventListener('keydown', onKey);
}
function dismiss() { done(); close(); }
function onKey(e) { if (e.key === 'Escape') dismiss(); }
function build() {
back = document.createElement('div');
back.setAttribute('role', 'dialog');
back.setAttribute('aria-label', (EV.eyebrow || 'Event') + ' invitation');
back.style.cssText = 'position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;' +
'padding:20px;background:rgba(3,7,14,.82);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);' +
'opacity:0;transition:opacity .28s ease;overflow:auto;';
back.addEventListener('click', function (e) { if (e.target === back) dismiss(); });
card = document.createElement('div');
card.style.cssText = 'position:relative;width:100%;max-width:428px;max-height:94vh;overflow:auto;border-radius:20px;' +
'background:#0a1119;border:1px solid rgba(212,175,55,.55);box-shadow:0 24px 70px rgba(0,0,0,.7),0 0 0 1px rgba(212,175,55,.15);' +
'font-family:system-ui,Segoe UI,Arial,sans-serif;';
var x = document.createElement('button');
x.setAttribute('aria-label', 'Close');
x.innerHTML = '&times;';
x.style.cssText = 'position:absolute;top:10px;right:12px;z-index:2;width:38px;height:38px;border-radius:50%;border:none;cursor:pointer;' +
'background:rgba(6,10,16,.72);color:#fff;font-size:24px;line-height:1;font-weight:700;display:grid;place-items:center;box-shadow:0 2px 10px rgba(0,0,0,.5);';
x.addEventListener('click', dismiss);
var eyebrow = document.createElement('div');
eyebrow.textContent = EV.eyebrow || '';
eyebrow.style.cssText = 'text-align:center;letter-spacing:2px;text-transform:uppercase;font-size:11px;font-weight:700;color:#d4af37;padding:14px 44px 10px;';
var img = document.createElement('img');
img.src = EV.img;
img.alt = EV.eyebrow || 'Event flyer';
img.style.cssText = 'display:block;width:100%;height:auto;';
var foot = document.createElement('div');
foot.style.cssText = 'padding:16px 20px 20px;';
if (EV.dateLabel) {
var dateRow = document.createElement('div');
dateRow.innerHTML = '🗓 <b></b>';
dateRow.querySelector('b').textContent = EV.dateLabel;
dateRow.style.cssText = 'text-align:center;color:#eef2f7;font-size:15px;margin-bottom:12px;';
foot.appendChild(dateRow);
}
if (EV.timesArr.length) {
var grid = document.createElement('div');
grid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;';
EV.timesArr.forEach(function (t) {
var cell = document.createElement('div');
cell.style.cssText = 'background:rgba(212,175,55,.08);border:1px solid rgba(212,175,55,.22);border-radius:10px;padding:8px 10px;';
var time = document.createElement('div');
time.style.cssText = 'font-size:15px;font-weight:800;color:#f3c34a;'; time.textContent = t[0];
var region = document.createElement('div');
region.style.cssText = 'font-size:11px;color:#9fb0c2;margin-top:1px;'; region.textContent = t[1] || ' ';
cell.appendChild(time); cell.appendChild(region); grid.appendChild(cell);
});
foot.appendChild(grid);
}
var join = document.createElement('a');
join.href = EV.meetUrl || '#'; join.target = '_blank'; join.rel = 'noopener';
join.textContent = 'Join Google Meet →';
join.style.cssText = 'display:block;text-align:center;text-decoration:none;background:linear-gradient(180deg,#e9c249,#d4af37);' +
'color:#0a1119;font-size:18px;font-weight:800;padding:15px;border-radius:12px;box-shadow:0 8px 22px rgba(212,175,55,.35);';
join.addEventListener('click', done);
foot.appendChild(join);
if (EV.meetUrl) {
var link = document.createElement('div');
link.textContent = EV.meetUrl.replace(/^https?:\/\//, '');
link.style.cssText = 'text-align:center;color:#9fb0c2;font-size:13px;margin-top:10px;font-family:ui-monospace,Menlo,Consolas,monospace;word-break:break-all;';
foot.appendChild(link);
}
var later = document.createElement('button');
later.textContent = 'Maybe later';
later.style.cssText = 'display:block;margin:12px auto 0;background:none;border:none;color:#7d8ea0;font-size:13px;cursor:pointer;text-decoration:underline;';
later.addEventListener('click', dismiss);
foot.appendChild(later);
card.appendChild(x); card.appendChild(eyebrow); card.appendChild(img); card.appendChild(foot);
back.appendChild(card); document.body.appendChild(back);
document.addEventListener('keydown', onKey);
requestAnimationFrame(function () { back.style.opacity = '1'; });
}
setTimeout(build, 1100);
}
function fetchAndGo() {
fetch('/api/announce', { headers: { 'Accept': 'application/json' } })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (EV) { if (EV) start(EV); })
.catch(function () {});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fetchAndGo);
else fetchAndGo();
})();