@@ -20,67 +20,102 @@ document.addEventListener("DOMContentLoaded", () => {
2020
2121 const spotsLeft = details . max_participants - details . participants . length ;
2222
23- // Create participants list with delete icon
24- let participantsHTML = "" ;
25- if ( details . participants . length > 0 ) {
26- participantsHTML = `
27- <div class="participants-section">
28- <strong>Participants:</strong>
29- <div class="participants-list">
30- ${ details . participants
31- . map (
32- ( email ) =>
33- `<div class="participant-item">
34- <span class="participant-email">${ email } </span>
35- <span class="delete-icon" title="Remove participant" data-activity="${ name } " data-email="${ email } ">🗑</span>
36- </div>`
37- )
38- . join ( "" ) }
39- </div>
40- </div>
41- ` ;
42- } else {
43- participantsHTML = `
44- <div class="participants-section">
45- <strong>Participants:</strong>
46- <p class="participants-none">No participants yet.</p>
47- </div>
48- ` ;
49- }
23+ // Create title
24+ const title = document . createElement ( "h4" ) ;
25+ title . textContent = name ;
26+ activityCard . appendChild ( title ) ;
27+
28+ // Create description
29+ const description = document . createElement ( "p" ) ;
30+ description . textContent = details . description ;
31+ activityCard . appendChild ( description ) ;
32+
33+ // Create schedule
34+ const schedule = document . createElement ( "p" ) ;
35+ const scheduleLabel = document . createElement ( "strong" ) ;
36+ scheduleLabel . textContent = "Schedule: " ;
37+ schedule . appendChild ( scheduleLabel ) ;
38+ schedule . appendChild ( document . createTextNode ( details . schedule ) ) ;
39+ activityCard . appendChild ( schedule ) ;
40+
41+ // Create availability
42+ const availability = document . createElement ( "p" ) ;
43+ const availabilityLabel = document . createElement ( "strong" ) ;
44+ availabilityLabel . textContent = "Availability: " ;
45+ availability . appendChild ( availabilityLabel ) ;
46+ availability . appendChild ( document . createTextNode ( `${ spotsLeft } spots left` ) ) ;
47+ activityCard . appendChild ( availability ) ;
48+
49+ // Create participants section
50+ const participantsSection = document . createElement ( "div" ) ;
51+ participantsSection . className = "participants-section" ;
52+
53+ const participantsLabel = document . createElement ( "strong" ) ;
54+ participantsLabel . textContent = "Participants:" ;
55+ participantsSection . appendChild ( participantsLabel ) ;
5056
51- activityCard . innerHTML = `
52- <h4>${ name } </h4>
53- <p>${ details . description } </p>
54- <p><strong>Schedule:</strong> ${ details . schedule } </p>
55- <p><strong>Availability:</strong> ${ spotsLeft } spots left</p>
56- ${ participantsHTML }
57- ` ;
58- // Add delete icon event listeners
59- setTimeout ( ( ) => {
60- const deleteIcons = activityCard . querySelectorAll ( ".delete-icon" ) ;
61- deleteIcons . forEach ( ( icon ) => {
62- icon . addEventListener ( "click" , async ( e ) => {
63- const activityName = icon . getAttribute ( "data-activity" ) ;
64- const email = icon . getAttribute ( "data-email" ) ;
65- if ( ! activityName || ! email ) return ;
66- if ( ! confirm ( `Remove ${ email } from ${ activityName } ?` ) ) return ;
67- try {
68- const response = await fetch ( `/activities/${ encodeURIComponent ( activityName ) } /unregister?email=${ encodeURIComponent ( email ) } ` , {
69- method : "POST" ,
70- } ) ;
71- if ( response . ok ) {
72- fetchActivities ( ) ;
73- } else {
74- const result = await response . json ( ) ;
75- alert ( result . detail || "Failed to remove participant." ) ;
57+ if ( details . participants . length > 0 ) {
58+ const participantsList = document . createElement ( "div" ) ;
59+ participantsList . className = "participants-list" ;
60+
61+ details . participants . forEach ( ( email ) => {
62+ const participantItem = document . createElement ( "div" ) ;
63+ participantItem . className = "participant-item" ;
64+
65+ const emailSpan = document . createElement ( "span" ) ;
66+ emailSpan . className = "participant-email" ;
67+ emailSpan . textContent = email ;
68+ participantItem . appendChild ( emailSpan ) ;
69+
70+ const deleteIcon = document . createElement ( "span" ) ;
71+ deleteIcon . className = "delete-icon" ;
72+ deleteIcon . title = "Remove participant" ;
73+ deleteIcon . textContent = "🗑️" ;
74+ deleteIcon . setAttribute ( "role" , "button" ) ;
75+ deleteIcon . setAttribute ( "aria-label" , "Remove participant" ) ;
76+ deleteIcon . setAttribute ( "tabindex" , "0" ) ;
77+
78+ const handleDelete = async ( ) => {
79+ // Create a safe confirmation message
80+ const confirmMsg = `Remove participant from activity?\n\nParticipant: ${ email } \nActivity: ${ name } ` ;
81+ if ( ! confirm ( confirmMsg ) ) return ;
82+ try {
83+ const response = await fetch ( `/activities/${ encodeURIComponent ( name ) } /unregister?email=${ encodeURIComponent ( email ) } ` , {
84+ method : "POST" ,
85+ } ) ;
86+ if ( response . ok ) {
87+ fetchActivities ( ) ;
88+ } else {
89+ const result = await response . json ( ) ;
90+ alert ( result . detail || "Failed to remove participant." ) ;
91+ }
92+ } catch ( error ) {
93+ alert ( "Error removing participant." ) ;
7694 }
77- } catch ( error ) {
78- alert ( "Error removing participant." ) ;
79- }
95+ } ;
96+
97+ // Store data in closure instead of data attributes for better security
98+ deleteIcon . addEventListener ( "click" , handleDelete ) ;
99+ deleteIcon . addEventListener ( "keydown" , ( e ) => {
100+ if ( e . key === "Enter" || e . key === " " ) {
101+ e . preventDefault ( ) ;
102+ handleDelete ( ) ;
103+ }
104+ } ) ;
105+
106+ participantItem . appendChild ( deleteIcon ) ;
107+ participantsList . appendChild ( participantItem ) ;
80108 } ) ;
81- } ) ;
82- } , 0 ) ;
83109
110+ participantsSection . appendChild ( participantsList ) ;
111+ } else {
112+ const noParticipants = document . createElement ( "p" ) ;
113+ noParticipants . className = "participants-none" ;
114+ noParticipants . textContent = "No participants yet." ;
115+ participantsSection . appendChild ( noParticipants ) ;
116+ }
117+
118+ activityCard . appendChild ( participantsSection ) ;
84119 activitiesList . appendChild ( activityCard ) ;
85120
86121 // Add option to select dropdown
0 commit comments