|
264 | 264 | font-weight: var(--font-semibold); |
265 | 265 | color: var(--color-text-heading); |
266 | 266 | } |
| 267 | + .event-group-section { |
| 268 | + margin-top: var(--space-4); |
| 269 | + padding: var(--space-4); |
| 270 | + background: var(--color-bg-subtle); |
| 271 | + border-radius: var(--radius-md); |
| 272 | + border: var(--border-1) solid var(--color-gray-200); |
| 273 | + } |
| 274 | + .event-group-section h4 { |
| 275 | + margin: 0 0 var(--space-2) 0; |
| 276 | + font-size: var(--text-sm); |
| 277 | + color: var(--color-text-heading); |
| 278 | + } |
| 279 | + .event-group-info { |
| 280 | + display: flex; |
| 281 | + align-items: center; |
| 282 | + gap: var(--space-3); |
| 283 | + flex-wrap: wrap; |
| 284 | + } |
| 285 | + .event-group-stat { |
| 286 | + font-size: var(--text-sm); |
| 287 | + color: var(--color-text-secondary); |
| 288 | + } |
| 289 | + .slack-link { |
| 290 | + display: inline-flex; |
| 291 | + align-items: center; |
| 292 | + gap: var(--space-1); |
| 293 | + color: var(--color-brand); |
| 294 | + text-decoration: none; |
| 295 | + font-size: var(--text-sm); |
| 296 | + } |
| 297 | + .slack-link:hover { |
| 298 | + text-decoration: underline; |
| 299 | + } |
| 300 | + .badge-event-group { |
| 301 | + display: inline-block; |
| 302 | + padding: 2px var(--space-2); |
| 303 | + border-radius: var(--radius-sm); |
| 304 | + font-size: var(--text-xs); |
| 305 | + font-weight: var(--font-medium); |
| 306 | + background: var(--color-success-100); |
| 307 | + color: var(--color-success-700); |
| 308 | + } |
267 | 309 | </style> |
268 | 310 | </head> |
269 | 311 | <body> |
@@ -527,6 +569,44 @@ <h2 id="modalTitle">Add Event</h2> |
527 | 569 | </div> |
528 | 570 | </div> |
529 | 571 |
|
| 572 | + <!-- Attendee Group (only shown when editing existing event) --> |
| 573 | + <div id="eventGroupSection" style="display: none;"> |
| 574 | + <div class="section-divider"> |
| 575 | + <div class="section-title">Attendee Group</div> |
| 576 | + </div> |
| 577 | + <p style="font-size: var(--text-sm); color: var(--color-text-secondary); margin-bottom: var(--space-4);"> |
| 578 | + Create a Slack channel for attendees to connect before, during, and after the event. |
| 579 | + Anyone who joins the channel is automatically added to the attendee group. |
| 580 | + </p> |
| 581 | + |
| 582 | + <div id="eventGroupExists" style="display: none;" class="event-group-section"> |
| 583 | + <h4>Attendee Group Active</h4> |
| 584 | + <div class="event-group-info"> |
| 585 | + <span class="event-group-stat"> |
| 586 | + <strong id="eventGroupMemberCount">0</strong> members |
| 587 | + </span> |
| 588 | + <a id="eventGroupSlackLink" href="#" target="_blank" class="slack-link"> |
| 589 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z"/></svg> |
| 590 | + <span id="eventGroupChannelName">#event-channel</span> |
| 591 | + </a> |
| 592 | + <button class="btn btn-secondary btn-small" onclick="viewEventGroupMembers()">View Members</button> |
| 593 | + </div> |
| 594 | + </div> |
| 595 | + |
| 596 | + <div id="eventGroupNotExists" style="display: none;"> |
| 597 | + <button type="button" class="btn btn-primary" onclick="createEventGroup()"> |
| 598 | + Create Attendee Group + Slack Channel |
| 599 | + </button> |
| 600 | + <p style="font-size: var(--text-xs); color: var(--color-text-muted); margin-top: var(--space-2);"> |
| 601 | + This will create a public Slack channel for attendees to join. |
| 602 | + </p> |
| 603 | + </div> |
| 604 | + |
| 605 | + <div id="eventGroupLoading" style="display: none; color: var(--color-text-secondary); font-size: var(--text-sm);"> |
| 606 | + Loading attendee group info... |
| 607 | + </div> |
| 608 | + </div> |
| 609 | + |
530 | 610 | <div class="modal-buttons"> |
531 | 611 | <button type="button" class="btn btn-secondary" onclick="closeModal()">Cancel</button> |
532 | 612 | <button type="submit" class="btn btn-primary" id="saveBtn">Save Event</button> |
@@ -641,6 +721,7 @@ <h3>${escapeHtml(event.title)}</h3> |
641 | 721 | document.getElementById('eventForm').reset(); |
642 | 722 | document.getElementById('sponsorshipTiers').innerHTML = ''; |
643 | 723 | document.getElementById('stripeProductInfo').style.display = 'none'; |
| 724 | + document.getElementById('eventGroupSection').style.display = 'none'; |
644 | 725 | sponsorshipTierCount = 0; |
645 | 726 | toggleLocationFields(); |
646 | 727 | toggleSponsorshipTiers(); |
@@ -711,9 +792,99 @@ <h3>${escapeHtml(event.title)}</h3> |
711 | 792 | } |
712 | 793 |
|
713 | 794 | toggleLocationFields(); |
| 795 | + |
| 796 | + // Show event group section for existing events |
| 797 | + document.getElementById('eventGroupSection').style.display = 'block'; |
| 798 | + loadEventGroupInfo(id); |
| 799 | + |
714 | 800 | document.getElementById('eventModal').style.display = 'flex'; |
715 | 801 | } |
716 | 802 |
|
| 803 | + // Load event group info |
| 804 | + async function loadEventGroupInfo(eventId) { |
| 805 | + const section = document.getElementById('eventGroupSection'); |
| 806 | + const exists = document.getElementById('eventGroupExists'); |
| 807 | + const notExists = document.getElementById('eventGroupNotExists'); |
| 808 | + const loading = document.getElementById('eventGroupLoading'); |
| 809 | + |
| 810 | + // Show loading |
| 811 | + exists.style.display = 'none'; |
| 812 | + notExists.style.display = 'none'; |
| 813 | + loading.style.display = 'block'; |
| 814 | + |
| 815 | + try { |
| 816 | + const res = await fetch(`/api/admin/events/${eventId}/event-group`); |
| 817 | + if (!res.ok) throw new Error('Failed to load event group'); |
| 818 | + |
| 819 | + const data = await res.json(); |
| 820 | + loading.style.display = 'none'; |
| 821 | + |
| 822 | + if (data.event_group) { |
| 823 | + // Event group exists |
| 824 | + exists.style.display = 'block'; |
| 825 | + document.getElementById('eventGroupMemberCount').textContent = data.member_count || 0; |
| 826 | + |
| 827 | + if (data.event_group.slack_channel_url) { |
| 828 | + document.getElementById('eventGroupSlackLink').href = data.event_group.slack_channel_url; |
| 829 | + document.getElementById('eventGroupChannelName').textContent = |
| 830 | + data.event_group.slack_channel_id ? `#${data.event_group.slug}` : 'Slack Channel'; |
| 831 | + } |
| 832 | + } else { |
| 833 | + // No event group yet |
| 834 | + notExists.style.display = 'block'; |
| 835 | + } |
| 836 | + } catch (error) { |
| 837 | + console.error('Error loading event group:', error); |
| 838 | + loading.style.display = 'none'; |
| 839 | + notExists.style.display = 'block'; |
| 840 | + } |
| 841 | + } |
| 842 | + |
| 843 | + // Create event group |
| 844 | + async function createEventGroup() { |
| 845 | + if (!editingEventId) return; |
| 846 | + |
| 847 | + const btn = document.querySelector('#eventGroupNotExists button'); |
| 848 | + btn.disabled = true; |
| 849 | + btn.textContent = 'Creating...'; |
| 850 | + |
| 851 | + try { |
| 852 | + const res = await fetch(`/api/admin/events/${editingEventId}/event-group`, { |
| 853 | + method: 'POST', |
| 854 | + headers: { 'Content-Type': 'application/json' }, |
| 855 | + body: JSON.stringify({ create_slack_channel: true }) |
| 856 | + }); |
| 857 | + |
| 858 | + if (!res.ok) { |
| 859 | + const error = await res.json(); |
| 860 | + throw new Error(error.message || 'Failed to create event group'); |
| 861 | + } |
| 862 | + |
| 863 | + const data = await res.json(); |
| 864 | + |
| 865 | + // Refresh the event group info |
| 866 | + await loadEventGroupInfo(editingEventId); |
| 867 | + |
| 868 | + if (data.slack_channel_created) { |
| 869 | + alert('Attendee group and Slack channel created! Share the channel link with attendees.'); |
| 870 | + } else { |
| 871 | + alert('Attendee group created. Note: Slack channel could not be created automatically - you may need to create it manually.'); |
| 872 | + } |
| 873 | + } catch (error) { |
| 874 | + alert(error.message); |
| 875 | + btn.disabled = false; |
| 876 | + btn.textContent = 'Create Attendee Group + Slack Channel'; |
| 877 | + } |
| 878 | + } |
| 879 | + |
| 880 | + // View event group members |
| 881 | + function viewEventGroupMembers() { |
| 882 | + // Navigate to working groups admin filtered by this event group |
| 883 | + if (editingEventId) { |
| 884 | + window.open('/admin/working-groups?type=event', '_blank'); |
| 885 | + } |
| 886 | + } |
| 887 | + |
717 | 888 | // Save event |
718 | 889 | async function saveEvent(e) { |
719 | 890 | e.preventDefault(); |
@@ -822,6 +993,11 @@ <h3>${escapeHtml(event.title)}</h3> |
822 | 993 | function closeModal() { |
823 | 994 | editingEventId = null; |
824 | 995 | document.getElementById('eventModal').style.display = 'none'; |
| 996 | + // Reset event group section |
| 997 | + document.getElementById('eventGroupSection').style.display = 'none'; |
| 998 | + document.getElementById('eventGroupExists').style.display = 'none'; |
| 999 | + document.getElementById('eventGroupNotExists').style.display = 'none'; |
| 1000 | + document.getElementById('eventGroupLoading').style.display = 'none'; |
825 | 1001 | } |
826 | 1002 |
|
827 | 1003 | // Toggle location fields based on event format |
|
0 commit comments