// Page script for sendMessage.html. Bootstrapping (auth cookie check, /info // fetch, header rendering, active-nav highlight) all live in js/app.js; // this file just wires the compose form. var modal = document.querySelector('.modal'); var overlay = document.querySelector('.overlay'); var message = new EasyMDE({ element: document.getElementById('my-text-area'), toolbar: [ 'bold', 'italic', '|', 'heading-1', 'heading-2', 'heading-3', '|', 'code', 'unordered-list', 'ordered-list', 'link', 'quote', '|', 'preview' ], maxHeight: '260px', }); // In-memory mirror of the server-side favorites list for this user + bot. // Kept in sync with every /user/groups/add and /user/groups/remove response // so the chip strip and the "already a favorite?" check stay authoritative // without a round-trip. var favoriteGroups = []; CollabCentral.init(function (ctx) { // User is authenticated and authorized. Reveal the form + populate the // group selectors from the backend. document.getElementById('formPanel').classList.remove('hidden'); loadFavoriteGroups(ctx.appName); loadNewGroups(ctx.appName); }); document.getElementById('sendMessage').onsubmit = function (event) { event.preventDefault(); var appName = CollabCentral.appName; var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState === this.DONE && this.status === 200) { var result = JSON.parse(xhttp.response); var scheduled = document.getElementById('scheduledFor').value; var text = 'This message will be sent to ' + result.memberList.length + ' recipient(s).

'; if (scheduled) { text += 'Scheduled for ' + CollabCentral.escapeHtml(scheduled) + '.'; } else { text += 'It will be sent immediately once you confirm.'; } document.getElementById('modalMessage').innerHTML = text; openModal(); } }; var formData = new FormData(); formData.append('uploadImage', document.getElementById('uploadImage').files[0]); formData.append('message', message.value()); formData.append('uploadCSV', document.getElementById('uploadCSV').files[0]); formData.append('appName', appName); var selectedGroups = []; for (var group of document.querySelector('#favGroups').getSelectedOptions()) { selectedGroups.push(group.value); } for (var group of document.querySelector('#newGroups').getSelectedOptions()) { selectedGroups.push(group.value); } formData.append('groups', selectedGroups); formData.append('scheduledFor', document.getElementById('scheduledFor').value); xhttp.open('POST', '/CollabCentral/' + appName + '/jobs/edit'); xhttp.send(formData); }; function showImagePreview(event) { if (!event.target.files || !event.target.files.length) return; var src = URL.createObjectURL(event.target.files[0]); var preview = document.getElementById('file-ip-1-preview'); preview.src = src; preview.style.display = 'block'; } // ---- Favorites ----------------------------------------------------------- function loadFavoriteGroups(appName) { var el = document.getElementById('favGroups'); fetch('/CollabCentral/' + appName + '/user/groups/list') .then(function (res) { return res.json(); }) .then(function (groups) { favoriteGroups = Array.isArray(groups) ? groups.slice() : []; for (var i = 0; i < favoriteGroups.length; i++) { var option = document.createElement('option'); option.text = favoriteGroups[i].name; option.value = favoriteGroups[i].id; el.add(option); } VirtualSelect.init({ ele: '#favGroups' }); renderFavoritesManager(); }); } // Renders the chip strip below the favorites picker. Each chip has an // × button that removes the group from favorites. The chip strip is // intentionally separate from the VirtualSelect widget: the picker answers // "which favorites am I sending to right now?", the chip strip answers // "which groups do I want to remember for next time?". function renderFavoritesManager() { var el = document.getElementById('favGroupsManager'); if (!el) return; el.innerHTML = ''; if (!favoriteGroups.length) { el.classList.add('hidden'); return; } el.classList.remove('hidden'); for (var i = 0; i < favoriteGroups.length; i++) { var g = favoriteGroups[i]; var chip = document.createElement('span'); chip.className = 'chip'; var label = document.createElement('span'); label.className = 'chip__label'; label.textContent = g.alias ? g.alias : g.name; if (g.alias && g.name && g.alias !== g.name) { chip.title = g.name; } var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'chip__close'; btn.setAttribute('aria-label', 'Remove ' + (g.name || 'favorite')); btn.textContent = '×'; btn.dataset.groupId = g.id; btn.addEventListener('click', onRemoveFavorite); chip.appendChild(label); chip.appendChild(btn); el.appendChild(chip); } } function onRemoveFavorite(event) { var groupId = event.currentTarget.dataset.groupId; if (!groupId) return; event.currentTarget.disabled = true; fetch('/CollabCentral/' + CollabCentral.appName + '/user/groups/remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: groupId }) }) .then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status); return res.json(); }) .then(function (updatedFavorites) { favoriteGroups = updatedFavorites || []; renderFavoritesManager(); flashFavoritesStatus('Removed from favorites.'); }) .catch(function (err) { console.error('Failed to remove favorite:', err); flashFavoritesStatus('Could not remove favorite.', true); event.currentTarget.disabled = false; }); } // Fires when the "Additional groups" multi-select changes. For each newly // selected id that isn't already a favorite we POST /user/groups/add. The // picker itself keeps the selection (so this send still uses the group); // the group starts appearing in the Favorite Groups picker on the next // page load, and immediately in the chip strip below. function onNewGroupsChange(newlySelectedIds) { if (!newlySelectedIds || !newlySelectedIds.length) return; var newGroupsEl = document.getElementById('newGroups'); var pending = 0; var successes = 0; newlySelectedIds.forEach(function (id) { if (favoriteGroups.some(function (g) { return g.id === id; })) return; pending++; fetch('/CollabCentral/' + CollabCentral.appName + '/user/groups/add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id }) }) .then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status); return res.json(); }) .then(function (updatedFavorites) { favoriteGroups = updatedFavorites || favoriteGroups; successes++; renderFavoritesManager(); }) .catch(function (err) { console.error('Failed to add favorite ' + id + ':', err); flashFavoritesStatus('Could not save one or more favorites.', true); }) .finally(function () { pending--; if (pending === 0 && successes > 0) { flashFavoritesStatus(successes === 1 ? 'Added to favorites.' : 'Added ' + successes + ' to favorites.'); } }); }); } function flashFavoritesStatus(text, isError) { var el = document.getElementById('favGroupsStatus'); if (!el) return; el.textContent = text; el.classList.toggle('fieldStatus--error', !!isError); clearTimeout(flashFavoritesStatus._t); flashFavoritesStatus._t = setTimeout(function () { el.textContent = ''; el.classList.remove('fieldStatus--error'); }, 4000); } function loadNewGroups(appName) { var el = document.getElementById('newGroups'); fetch('/CollabCentral/' + appName + '/user/groups/find') .then(function (res) { return res.json(); }) .then(function (groups) { for (var i = 0; i < groups.length; i++) { var option = document.createElement('option'); option.text = groups[i].displayName; option.value = groups[i].id; el.add(option); } VirtualSelect.init({ ele: '#newGroups' }); // VirtualSelect emits a standard 'change' event on the original //