01Name your event
02Who's organizing this?
|
${lbl1}
${s.dateLabel}
${s.timeLabel}
${s.availLabel}
|
|
#${s.rank}
${s.dateLabel}
${s.timeLabel}
${s.availLabel}
|
${heading}
${subtext}
${cardHtml} ${ctaBtn}${url}
`; return buildEmailTemplate(body, lang.dir); } // ══════════════════════════════════════ // SCREEN B: DRAG INTERACTION // ══════════════════════════════════════ let gridDragging = false, gridDragMode = 'add'; function onGridDown(e) { if (!S.currentUser) return; e.preventDefault(); gridDragging = true; const id = e.currentTarget.dataset.id; gridDragMode = S.currentUser.availability.has(id) ? 'remove' : 'add'; toggleCell(id); } function onGridEnter(e) { if (!S.currentUser) return; if (gridDragging) toggleCell(e.currentTarget.dataset.id); showTooltip(e.currentTarget, e); } function toggleCell(id) { if (!S.currentUser) return; // Optimistic local update — instant UI response if (gridDragMode === 'add') S.currentUser.availability.add(id); else S.currentUser.availability.delete(id); renderHeatmap(); scheduleNotifyOrganizer(S.currentUser.name); // Persist to Firestore atomically const op = gridDragMode === 'add' ? firebase.firestore.FieldValue.arrayUnion(id) : firebase.firestore.FieldValue.arrayRemove(id); db.collection('events').doc(S.eventSlug).update({ [`participants.${S.currentUser.name}.availability`]: op, }).catch(e => console.error('Failed to update availability:', e)); } // ══════════════════════════════════════ // SCREEN B: TOOLTIP // ══════════════════════════════════════ const tooltip = document.getElementById('grid-tooltip'); function showTooltip(cell, e) { const all = allParticipants(); const id = cell.dataset.id; const avail = all.filter(p => p.availability.has(id)); if (!avail.length) { tooltip.style.display = 'none'; return; } const ttSuffix = t().ofPeople(avail.length, all.length).replace(/^\d+\s*/, ''); const header = `