@@ -115,6 +115,7 @@ export function mount(el, context) {
115115 const queueMetaEl = el . querySelector ( '#twQueueMeta' ) ;
116116
117117 let profiles = { } ;
118+ let profileGroups = { } ;
118119 let pendingConfirmation = null ;
119120 let lastTimerStatus = 'idle' ;
120121 let lastPendingConfirmVisible = null ;
@@ -186,17 +187,70 @@ export function mount(el, context) {
186187 const selectedBefore = profSel . value || '' ;
187188 const r = await fetch ( apiBase ( ) + '/api/timer/profiles?_=' + Date . now ( ) , { cache : 'no-store' } ) ; const d = await r . json ( ) ;
188189 profiles = d . profiles || { } ;
189- profSel . innerHTML = '' ;
190- const names = Object . keys ( profiles ) ;
191- names . forEach ( n => { const opt = document . createElement ( 'option' ) ; opt . value = n ; opt . textContent = n ; profSel . appendChild ( opt ) ; } ) ;
192- if ( selectedBefore ) {
193- const opt = Array . from ( profSel . options ) . find ( o => o . value === selectedBefore ) ;
194- if ( opt ) profSel . value = selectedBefore ;
195- }
190+ profileGroups = d . profile_groups || { } ;
191+ renderProfileOptions ( selectedBefore ) ;
196192 if ( ! profSel . value ) applyProfileSelection ( ) ;
197193 } catch { }
198194 }
199195
196+ function renderProfileOptions ( selectedBefore = '' ) {
197+ profSel . innerHTML = '' ;
198+ const names = Object . keys ( profiles || { } ) ;
199+ const known = new Set ( names ) ;
200+ const groups = ( profileGroups && typeof profileGroups === 'object' ) ? profileGroups : { } ;
201+ const assigned = new Set ( ) ;
202+ let hasGroups = false ;
203+
204+ Object . entries ( groups ) . forEach ( ( [ label , entries ] ) => {
205+ if ( ! Array . isArray ( entries ) ) return ;
206+ const groupNames = [ ] ;
207+ entries . forEach ( ( raw ) => {
208+ const name = String ( raw || '' ) . trim ( ) ;
209+ if ( ! name || ! known . has ( name ) || assigned . has ( name ) ) return ;
210+ groupNames . push ( name ) ;
211+ assigned . add ( name ) ;
212+ } ) ;
213+ if ( ! groupNames . length ) return ;
214+ hasGroups = true ;
215+ const optgroup = document . createElement ( 'optgroup' ) ;
216+ optgroup . label = label ;
217+ groupNames . forEach ( ( name ) => {
218+ const opt = document . createElement ( 'option' ) ;
219+ opt . value = name ;
220+ opt . textContent = name ;
221+ optgroup . appendChild ( opt ) ;
222+ } ) ;
223+ profSel . appendChild ( optgroup ) ;
224+ } ) ;
225+
226+ const remaining = names . filter ( ( name ) => ! assigned . has ( name ) ) ;
227+ if ( hasGroups && remaining . length ) {
228+ const other = document . createElement ( 'optgroup' ) ;
229+ other . label = 'Other' ;
230+ remaining . forEach ( ( name ) => {
231+ const opt = document . createElement ( 'option' ) ;
232+ opt . value = name ;
233+ opt . textContent = name ;
234+ other . appendChild ( opt ) ;
235+ } ) ;
236+ profSel . appendChild ( other ) ;
237+ }
238+
239+ if ( ! hasGroups ) {
240+ names . forEach ( ( name ) => {
241+ const opt = document . createElement ( 'option' ) ;
242+ opt . value = name ;
243+ opt . textContent = name ;
244+ profSel . appendChild ( opt ) ;
245+ } ) ;
246+ }
247+
248+ if ( selectedBefore ) {
249+ const opt = Array . from ( profSel . options ) . find ( ( o ) => o . value === selectedBefore ) ;
250+ if ( opt ) profSel . value = selectedBefore ;
251+ }
252+ }
253+
200254 async function loadSettings ( ) {
201255 try {
202256 const r = await fetch ( apiBase ( ) + '/api/timer/settings' ) ; const d = await r . json ( ) ;
0 commit comments