-
-
-
-
-
-
+ `;
+ }).join('');
+ }
+ }
+
+ function startEditSession(id) {
+ const s = currentActivitySessions.find(ses => ses.id === id);
+ if (!s) return;
+
+ document.getElementById('s-title').value = s.title;
+ document.getElementById('s-desc').value = s.description || '';
+
+ // browsers require YYYY-MM-DDThh:mm format for datetime-local
+ const fmt = v => v ? String(v).replace(' ', 'T') : '';
+ document.getElementById('s-start').value = fmt(s.start_time);
+ document.getElementById('s-end').value = fmt(s.end_time);
+ document.getElementById('s-location').value = s.location || '';
+
+ document.getElementById('ses-form-title').innerHTML = '✏️ Edit Session';
+ document.getElementById('ses-fields-title').textContent = 'Editing Session Details';
+ document.getElementById('btn-submit-ses').textContent = 'Update Session';
+ document.getElementById('btn-cancel-ses').classList.remove('hidden');
+
+ editSessionId = id;
+ }
+
+ function cancelEditSession() {
+ document.getElementById('s-title').value = '';
+ document.getElementById('s-desc').value = '';
+ document.getElementById('s-start').value = '';
+ document.getElementById('s-end').value = '';
+ document.getElementById('s-location').value = '';
+
+ document.getElementById('ses-form-title').innerHTML = '📅 Add Session';
+ document.getElementById('ses-fields-title').textContent = 'New Session Details';
+ document.getElementById('btn-submit-ses').textContent = 'Add Session';
+ document.getElementById('btn-cancel-ses').classList.add('hidden');
+ editSessionId = null;
+ }
+
document.getElementById('form-session').addEventListener('submit', async e => {
e.preventDefault();
document.getElementById('ses-err').classList.add('hidden');
document.getElementById('ses-ok').classList.add('hidden');
- const btn = e.target.querySelector('button[type=submit]');
+
const actId = document.getElementById('s-activity').value;
if (!actId) { showMsg('ses-err', 'Please select an activity', true); return; }
- btn.textContent = 'Adding...'; btn.disabled = true;
+
+ const btn = e.target.querySelector('button[type=submit]');
+ btn.textContent = editSessionId ? 'Updating...' : 'Adding...';
+ btn.disabled = true;
+
const fmt = v => v ? v.replace('T',' ') : '';
+ const method = editSessionId ? 'PUT' : 'POST';
+ const url = editSessionId ? `/api/sessions/${editSessionId}` : '/api/sessions';
+
try {
- const res = await fetch('/api/sessions', {
- method:'POST', headers:{ 'Content-Type':'application/json', Authorization:'Bearer ' + token },
+ const res = await fetch(url, {
+ method, headers:{ 'Content-Type':'application/json', Authorization:'Bearer ' + token },
body: JSON.stringify({
activity_id: actId,
title: document.getElementById('s-title').value.trim(),
@@ -304,15 +451,14 @@
-
+
-
+
🌟 Create New Activity
-Descriptions are encrypted at rest in D1.
+ +
+
+
+
🌟 Create New Activity
+Descriptions are encrypted at rest in D1.
+
-
+
' +
'
';
@@ -252,17 +270,65 @@ 📅 Add Session
-Location and description are encrypted at rest.
+ +
+
+
+
📅 Add Session
+Location and description are encrypted at rest.
+' +
+ '' +
'View' +
'
' +
'My Hosted Activities
setTimeout(() => el.classList.add('hidden'), 4000); } + let editActivityId = null; + let editSessionId = null; + let currentActivitySessions = []; + let activityEditLoadSeq = 0; + let sessionListLoadSeq = 0; + + async function startEditActivity(id) { + const loadSeq = ++activityEditLoadSeq; + const a = hostedActivities.find(act => act.id === id); + if (!a) return; + + // fetch details and get description + const res = await fetch('/api/activities/' + id, { headers: { Authorization: 'Bearer ' + token } }); + const data = await res.json(); + if (loadSeq !== activityEditLoadSeq) return; + if (!res.ok) { showMsg('act-err', 'Failed to load activity details', true); return; } + + const fullAct = data.activity; + + document.getElementById('a-title').value = fullAct.title; + document.getElementById('a-desc').value = fullAct.description || ''; + document.getElementById('a-type').value = fullAct.type; + document.getElementById('a-format').value = fullAct.format; + document.getElementById('a-schedule').value = fullAct.schedule_type; + document.getElementById('a-tags').value = (fullAct.tags || []).join(', '); + + document.getElementById('act-form-title').innerHTML = '✏️ Edit Activity'; + document.getElementById('btn-submit-act').textContent = 'Update Activity'; + document.getElementById('btn-cancel-act').classList.remove('hidden'); + + editActivityId = id; + document.getElementById('act-section').scrollIntoView({ behavior: 'smooth' }); + } + + function cancelEditActivity() { + document.getElementById('form-activity').reset(); + document.getElementById('act-form-title').innerHTML = '🌟 Create New Activity'; + document.getElementById('btn-submit-act').textContent = 'Create Activity'; + document.getElementById('btn-cancel-act').classList.add('hidden'); + editActivityId = null; + } + document.getElementById('form-activity').addEventListener('submit', async e => { e.preventDefault(); document.getElementById('act-err').classList.add('hidden'); document.getElementById('act-ok').classList.add('hidden'); const btn = e.target.querySelector('button[type=submit]'); - btn.textContent = 'Creating...'; btn.disabled = true; + btn.textContent = editActivityId ? 'Updating...' : 'Creating...'; + btn.disabled = true; + const rawTags = document.getElementById('a-tags').value; const tags = rawTags ? rawTags.split(',').map(t => t.trim()).filter(Boolean) : []; + + const method = editActivityId ? 'PUT' : 'POST'; + const url = editActivityId ? `/api/activities/${editActivityId}` : '/api/activities'; + try { - const res = await fetch('/api/activities', { - method:'POST', headers:{ 'Content-Type':'application/json', Authorization:'Bearer ' + token }, + const res = await fetch(url, { + method, headers:{ 'Content-Type':'application/json', Authorization:'Bearer ' + token }, body: JSON.stringify({ title: document.getElementById('a-title').value.trim(), description: document.getElementById('a-desc').value.trim(), @@ -274,25 +340,106 @@My Hosted Activities
}); const data = await res.json(); if (!res.ok) throw new Error(data.error || 'Failed'); - showMsg('act-ok', 'Activity created: ' + data.data.title, false); - e.target.reset(); + + showMsg('act-ok', `Activity ${editActivityId ? 'updated' : 'created'} successfully!`, false); + cancelEditActivity(); await loadHostedActivities(); } catch (err) { showMsg('act-err', err.message, true); } - finally { btn.textContent = 'Create Activity'; btn.disabled = false; } + finally { btn.disabled = false; btn.textContent = editActivityId ? 'Update Activity' : 'Create Activity'; } }); + async function onActivitySelect() { + const actId = document.getElementById('s-activity').value; + const loadSeq = ++sessionListLoadSeq; + const listDiv = document.getElementById('existing-sessions'); + const ul = document.getElementById('s-session-list'); + cancelEditSession(); + + if (!actId) { + listDiv.classList.add('hidden'); + return; + } + + const res = await fetch('/api/activities/' + actId, { headers: { Authorization: 'Bearer ' + token } }); + if (!res.ok) { + showMsg('ses-err', 'Failed to load sessions for this activity', true); + listDiv.classList.add('hidden'); + return; + } + const data = await res.json(); + if (loadSeq !== sessionListLoadSeq || document.getElementById('s-activity').value !== actId) return; + currentActivitySessions = data.sessions || []; + + if (currentActivitySessions.length === 0) { + listDiv.classList.add('hidden'); + } else { + listDiv.classList.remove('hidden'); + ul.innerHTML = currentActivitySessions.map(s => { + return `
+ ${esc(s.title)}
+
+
+ ${esc(s.start_time || '')}
+