Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/few-rivers-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
---

Add topic Slack channels for working groups

- Working group topics can now have an optional Slack channel ID
- New invite mode 'slack_channel' allows scheduling meetings for Slack channel members
- Admin UI for managing topics with Slack channel linking
- Addie can manage topics via manage_committee_topics tool
107 changes: 105 additions & 2 deletions server/public/admin-meetings.html
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ <h2 id="modalTitle">Schedule Meeting</h2>

<div class="form-group">
<label>Working Group *</label>
<select id="workingGroupId" required>
<select id="workingGroupId" required onchange="onWorkingGroupChange()">
<option value="">Select a committee...</option>
</select>
</div>
Expand Down Expand Up @@ -365,9 +365,10 @@ <h2 id="modalTitle">Schedule Meeting</h2>

<div class="form-group">
<label>Who to Invite</label>
<select id="inviteMode">
<select id="inviteMode" onchange="updateInviteModeVisibility()">
<option value="all_members">All group members</option>
<option value="topic_subscribers">Only topic subscribers</option>
<option value="slack_channel">Slack channel members</option>
<option value="manual">Manual selection</option>
</select>
<small>Members will receive a calendar invite via email</small>
Expand All @@ -381,6 +382,16 @@ <h2 id="modalTitle">Schedule Meeting</h2>
</div>
</div>

<div id="slackChannelSection" style="display: none;">
<div class="form-group">
<label>Slack Channel</label>
<select id="inviteSlackChannelId">
<option value="">Select a channel...</option>
</select>
<small>Invite members from this Slack channel</small>
</div>
</div>

<div class="modal-buttons">
<button type="button" class="btn btn-secondary" onclick="closeModal()">Cancel</button>
<button type="submit" class="btn btn-primary" id="saveBtn">Schedule</button>
Expand Down Expand Up @@ -710,6 +721,14 @@ <h3>${escapeHtml(s.title)}</h3>
data.working_group_id = document.getElementById('workingGroupId').value;
data.duration_minutes = durationMinutes;
data.invite_mode = document.getElementById('inviteMode').value;
// Include Slack channel ID if that invite mode is selected
if (data.invite_mode === 'slack_channel') {
data.invite_slack_channel_id = document.getElementById('inviteSlackChannelId').value;
if (!data.invite_slack_channel_id) {
alert('Please select a Slack channel');
return;
}
}
}

const saveBtn = document.getElementById('saveBtn');
Expand Down Expand Up @@ -828,6 +847,90 @@ <h3>${escapeHtml(s.title)}</h3>
closeDetailsModal();
}

// Handle working group selection change
function onWorkingGroupChange() {
const inviteMode = document.getElementById('inviteMode').value;
// Reload channels if in slack_channel mode
if (inviteMode === 'slack_channel') {
const groupId = document.getElementById('workingGroupId').value;
if (groupId) {
loadSlackChannelsForGroup(groupId);
}
}
}

// Toggle visibility of invite mode-specific sections
function updateInviteModeVisibility() {
const inviteMode = document.getElementById('inviteMode').value;
const topicsSection = document.getElementById('topicsSection');
const slackChannelSection = document.getElementById('slackChannelSection');

// Show topics section only for topic_subscribers mode
topicsSection.style.display = inviteMode === 'topic_subscribers' ? 'block' : 'none';

// Show Slack channel section only for slack_channel mode
slackChannelSection.style.display = inviteMode === 'slack_channel' ? 'block' : 'none';

// Load channels if switching to slack_channel mode
if (inviteMode === 'slack_channel') {
const groupId = document.getElementById('workingGroupId').value;
if (groupId) {
loadSlackChannelsForGroup(groupId);
}
}
}

// Load available Slack channels for a working group (group channel + topic channels)
async function loadSlackChannelsForGroup(groupId) {
const channelSelect = document.getElementById('inviteSlackChannelId');
channelSelect.innerHTML = '<option value="">Loading...</option>';

try {
// Find the working group
const group = workingGroups.find(g => g.id === groupId);
if (!group) {
channelSelect.innerHTML = '<option value="">No group selected</option>';
return;
}

// Build list of available channels
const channels = [];

// Add the main working group channel if it has one
if (group.slack_channel_id) {
channels.push({
id: group.slack_channel_id,
name: `${group.name} (main channel)`,
});
}

// Add any topic channels
if (group.topics && group.topics.length > 0) {
for (const topic of group.topics) {
if (topic.slack_channel_id) {
channels.push({
id: topic.slack_channel_id,
name: `${topic.name} (topic)`,
});
}
}
}

if (channels.length === 0) {
channelSelect.innerHTML = '<option value="">No Slack channels configured for this group</option>';
return;
}

channelSelect.innerHTML = '<option value="">Select a channel...</option>';
channels.forEach(c => {
channelSelect.innerHTML += `<option value="${escapeHtml(c.id)}">${escapeHtml(c.name)}</option>`;
});
} catch (error) {
console.error('Error loading channels:', error);
channelSelect.innerHTML = '<option value="">Error loading channels</option>';
}
}

// Escape helpers
function escapeHtml(text) {
if (!text) return '';
Expand Down
Loading