|
88 | 88 | </form> |
89 | 89 |
|
90 | 90 | <!-- CREATE / EDIT GROUP MODAL --> |
91 | | - <div ref="group_modal" class="ui small modal" style="display:none;"> |
92 | | - <i class="close icon" onclick="{ close_group_modal }"></i> |
93 | | - <div class="header">{ editing_group ? 'Modify group' : 'create group' }</div> |
94 | | - <div class="content"> |
95 | | - <div class="ui form"> |
96 | | - <div class="field"> |
97 | | - <label>name</label> |
98 | | - <input type="text" ref="group_name"> |
99 | | - </div> |
100 | | - |
101 | | - <div class="field"> |
102 | | - <label>Queue (optional)</label> |
103 | | - <select ref="group_queue" class="ui dropdown"> |
104 | | - <option value="">None</option> |
105 | | - <option each="{ q in available_queues }" value="{ q.id }">{ q.name }</option> |
106 | | - </select> |
107 | | - </div> |
| 91 | + <!-- CREATE / EDIT GROUP MODAL --> |
| 92 | +<div ref="group_modal" class="ui small modal" style="display:none;"> |
| 93 | + <i class="close icon" onclick="{ close_group_modal }"></i> |
| 94 | + <div class="header">{ editing_group ? 'Modify group' : 'Create group' }</div> |
| 95 | + <div class="content"> |
| 96 | + <div class="ui form"> |
| 97 | + |
| 98 | + <!-- NEW: select an existing group to edit / switch to create --> |
| 99 | + <div class="field"> |
| 100 | + <label>Select existing group (or choose 'Create new')</label> |
| 101 | + <select ref="choose_existing_group" class="ui fluid search dropdown"> |
| 102 | + <option value="">-- Create new group --</option> |
| 103 | + <option each="{ g in available_groups }" value="{ g.id }">{ g.name }</option> |
| 104 | + </select> |
| 105 | + </div> |
108 | 106 |
|
109 | | - <div class="field"> |
110 | | - <label>Select group members</label> |
| 107 | + <div class="field"> |
| 108 | + <label>name</label> |
| 109 | + <input type="text" ref="group_name"> |
| 110 | + </div> |
111 | 111 |
|
112 | | - <div style="display:flex; gap:.5rem; margin-bottom:.5rem; align-items:center;"> |
113 | | - <button type="button" class="ui mini button" onclick="{ select_all_users }">Select all</button> |
114 | | - <button type="button" class="ui mini basic button" onclick="{ clear_user_selection }">Clear</button> |
115 | | - <div class="ui right floated meta" style="margin-left:auto;"> |
116 | | - <span class="ui tiny basic label">Selected: <span ref="selected_count">0</span></span> |
117 | | - </div> |
118 | | - </div> |
| 112 | + <div class="field"> |
| 113 | + <label>Queue (optional)</label> |
| 114 | + <select ref="group_queue" class="ui dropdown"> |
| 115 | + <option value="">None</option> |
| 116 | + <option each="{ q in available_queues }" value="{ q.id }">{ q.name }</option> |
| 117 | + </select> |
| 118 | + </div> |
119 | 119 |
|
120 | | - <select ref="group_user_select" multiple class="ui fluid multiple search selection dropdown" style="width:100%;"> |
121 | | - <option each="{ u in available_users }" value="{ u.id }">{ u.username } <{ u.email }></option> |
122 | | - </select> |
| 120 | + <div class="field"> |
| 121 | + <label>Select group members</label> |
123 | 122 |
|
124 | | - <div class="ui segment" style="margin-top:.5rem;"> |
125 | | - <small class="muted">You can search and select multiple participants</small> |
| 123 | + <div style="display:flex; gap:.5rem; margin-bottom:.5rem; align-items:center;"> |
| 124 | + <button type="button" class="ui mini button" onclick="{ select_all_users }">Select all</button> |
| 125 | + <button type="button" class="ui mini basic button" onclick="{ clear_user_selection }">Clear</button> |
| 126 | + <div class="ui right floated meta" style="margin-left:auto;"> |
| 127 | + <span class="ui tiny basic label">Selected: <span ref="selected_count">0</span></span> |
126 | 128 | </div> |
127 | 129 | </div> |
128 | 130 |
|
129 | | - <div class="ui error message" ref="group_modal_error" style="display:none;"></div> |
| 131 | + <select ref="group_user_select" multiple class="ui fluid multiple search selection dropdown" style="width:100%;"> |
| 132 | + <option each="{ u in available_users }" value="{ u.id }">{ u.username } <{ u.email }></option> |
| 133 | + </select> |
| 134 | + |
| 135 | + <div class="ui segment" style="margin-top:.5rem;"> |
| 136 | + <small class="muted">You can search and select multiple participants</small> |
| 137 | + </div> |
130 | 138 | </div> |
131 | | - </div> |
132 | 139 |
|
133 | | - <div class="actions"> |
134 | | - <div class="ui cancel button" onclick="{ close_group_modal }">Cancel</div> |
135 | | - <div class="ui primary button" onclick="{ submit_group }">{ editing_group ? 'Edit' : 'Create' }</div> |
| 140 | + <div class="ui error message" ref="group_modal_error" style="display:none;"></div> |
136 | 141 | </div> |
137 | 142 | </div> |
138 | 143 |
|
| 144 | + <div class="actions"> |
| 145 | + <div class="ui cancel button" onclick="{ close_group_modal }">Cancel</div> |
| 146 | + <div class="ui primary button" onclick="{ submit_group }">{ editing_group ? 'Edit' : 'Create' }</div> |
| 147 | + </div> |
| 148 | +</div> |
| 149 | + |
139 | 150 | <script> |
140 | 151 | let self = this |
141 | 152 |
|
|
146 | 157 | self.available_users = [] |
147 | 158 | self.editing_group = null |
148 | 159 | self._scheduledUpdate = false |
| 160 | + const getGroupById = (id) => { |
| 161 | + if (!id) return null |
| 162 | + const strId = String(id) |
| 163 | + return (self.available_groups || []).find(g => String(g.id) === strId) || null |
| 164 | + } |
149 | 165 |
|
150 | 166 | const initUI = () => { |
151 | 167 | try { $('.ui.checkbox', self.root).checkbox() } catch(e) {} |
|
168 | 184 | } catch(e) {} |
169 | 185 | } |
170 | 186 |
|
| 187 | + try { |
| 188 | + if (self.refs && self.refs.choose_existing_group) { |
| 189 | + $(self.refs.choose_existing_group).dropdown({ |
| 190 | + fullTextSearch: true, |
| 191 | + onChange: (value) => { |
| 192 | + if (!value || value === '') { |
| 193 | + self.editing_group = null |
| 194 | + try { self.refs.group_name.value = '' } catch(e){} |
| 195 | + try { $(self.refs.group_queue).dropdown('clear') } catch(e){} |
| 196 | + try { $(self.refs.group_user_select).dropdown('clear') } catch(e){} |
| 197 | + try { self.refs.selected_count.textContent = '0' } catch(e){} |
| 198 | + try { self.refs.group_modal_error.style.display = 'none' } catch(e){} |
| 199 | + } else { |
| 200 | + const g = getGroupById(value) |
| 201 | + if (g) { |
| 202 | + self.open_edit_group(g) |
| 203 | + } |
| 204 | + } |
| 205 | + } |
| 206 | + }) |
| 207 | + } |
| 208 | + } catch(e){} |
| 209 | + |
| 210 | + |
171 | 211 | const compPk = () => { |
172 | 212 | try { |
173 | 213 | if (self && self.opts) { |
|
338 | 378 | self.isValidEmail = function (email) { |
339 | 379 | // Regular expression pattern to match a valid email address |
340 | 380 | const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/ |
341 | | - |
342 | 381 | // Test the email against the pattern and return the result (boolean) |
343 | 382 | return emailPattern.test(email) |
344 | 383 | } |
345 | 384 |
|
346 | 385 | self.open_create_group = () => { |
347 | 386 | self.editing_group = null |
| 387 | + try { $(self.refs.choose_existing_group).dropdown('clear') } catch(e){} |
348 | 388 | try { self.refs.group_name.value = '' } catch(e){} |
349 | 389 | try { $(self.refs.group_queue).dropdown('clear') } catch(e){} |
350 | 390 | try { $(self.refs.group_user_select).dropdown('clear') } catch(e){} |
|
354 | 394 | } |
355 | 395 |
|
356 | 396 | const membersToIds = (membersArr) => { |
357 | | - // membersArr can contain ids, usernames or emails; return matching ids as strings |
358 | 397 | if (!membersArr || !membersArr.length) return [] |
359 | 398 | const ids = [] |
360 | 399 | const users = self.available_users || [] |
|
371 | 410 |
|
372 | 411 | self.open_edit_group = (group) => { |
373 | 412 | self.editing_group = group |
| 413 | + try { $(self.refs.choose_existing_group).dropdown('set selected', String(group.id)) } catch(e){} |
| 414 | + |
374 | 415 | try { self.refs.group_name.value = group.name || '' } catch(e){} |
375 | 416 | try { |
376 | 417 | let queueId = null |
|
0 commit comments