110110 font-family : inherit;
111111 }
112112 .search-form input [type = "text" ] { flex : 1 ; min-width : 200px ; }
113+ .filter-row select {
114+ background : # 0d1117 ; border : 1px solid # 30363d ; color : # c9d1d9 ;
115+ padding : 0.5rem 0.75rem ; border-radius : 6px ; font-size : 0.875rem ;
116+ font-family : inherit;
117+ }
118+ .filter-row select : focus { outline : none; border-color : # 58a6ff ; }
113119 .search-form input [type = "text" ]: focus ,
114120 .search-form input [type = "date" ]: focus ,
115121 .search-form select : focus {
@@ -267,17 +273,23 @@ <h2 id="section-title">Live Feed</h2>
267273 < form class ="search-form " id ="search-form ">
268274 < input type ="text " id ="search-query " placeholder ="Search observations... " required >
269275 < button type ="submit " class ="btn "> Search</ button >
276+ < button type ="button " class ="btn btn-secondary " id ="search-clear " style ="display:none "> Clear</ button >
270277 </ form >
271278 < div class ="filter-row ">
272279 < label > Type:</ label >
273- < input type ="text " id ="search-type " placeholder ="e.g. discovery " style ="width:120px ">
280+ < select id ="search-type ">
281+ < option value =""> All types</ option >
282+ </ select >
274283 < label > Project:</ label >
275- < input type ="text " id ="search-project " placeholder ="project " style ="width:120px ">
284+ < select id ="search-project ">
285+ < option value =""> All projects</ option >
286+ </ select >
276287 < label > From:</ label >
277288 < input type ="date " id ="search-date-start ">
278289 < label > To:</ label >
279290 < input type ="date " id ="search-date-end ">
280291 </ div >
292+ < div id ="search-status " style ="margin-bottom:0.75rem;font-size:0.85rem;color:#8b949e;display:none "> </ div >
281293 < div id ="search-results "> </ div >
282294 </ div >
283295 </ div >
@@ -370,7 +382,8 @@ <h2 id="section-title">Live Feed</h2>
370382 overlay . classList . remove ( 'visible' ) ;
371383
372384 // Load data when switching to a section
373- if ( section === 'sessions' ) loadSessions ( ) ;
385+ if ( section === 'search' ) loadFilters ( ) ;
386+ else if ( section === 'sessions' ) loadSessions ( ) ;
374387 else if ( section === 'summaries' ) loadSummaries ( ) ;
375388 else if ( section === 'plans' ) loadPlans ( ) ;
376389 }
@@ -481,15 +494,61 @@ <h2 id="section-title">Live Feed</h2>
481494 // --- Search ---
482495 const searchForm = document . getElementById ( 'search-form' ) ;
483496 const searchResults = document . getElementById ( 'search-results' ) ;
497+ const searchClear = document . getElementById ( 'search-clear' ) ;
498+ const searchStatus = document . getElementById ( 'search-status' ) ;
499+ const searchTypeSelect = document . getElementById ( 'search-type' ) ;
500+ const searchProjectSelect = document . getElementById ( 'search-project' ) ;
501+ let filtersLoaded = false ;
502+
503+ async function loadFilters ( ) {
504+ if ( filtersLoaded ) return ;
505+ try {
506+ const resp = await fetch ( '/api/observations/filters' ) ;
507+ if ( ! resp . ok ) return ;
508+ const data = await resp . json ( ) ;
509+ if ( data . types ) {
510+ for ( const t of data . types ) {
511+ const opt = document . createElement ( 'option' ) ;
512+ opt . value = t ;
513+ opt . textContent = t ;
514+ searchTypeSelect . appendChild ( opt ) ;
515+ }
516+ }
517+ if ( data . projects ) {
518+ for ( const p of data . projects ) {
519+ const opt = document . createElement ( 'option' ) ;
520+ opt . value = p ;
521+ opt . textContent = p ;
522+ searchProjectSelect . appendChild ( opt ) ;
523+ }
524+ }
525+ filtersLoaded = true ;
526+ } catch ( err ) {
527+ console . error ( 'Load filters error:' , err ) ;
528+ }
529+ }
530+
531+ function clearSearch ( ) {
532+ document . getElementById ( 'search-query' ) . value = '' ;
533+ searchTypeSelect . value = '' ;
534+ searchProjectSelect . value = '' ;
535+ document . getElementById ( 'search-date-start' ) . value = '' ;
536+ document . getElementById ( 'search-date-end' ) . value = '' ;
537+ searchResults . innerHTML = '' ;
538+ searchStatus . style . display = 'none' ;
539+ searchClear . style . display = 'none' ;
540+ }
541+
542+ searchClear . addEventListener ( 'click' , clearSearch ) ;
484543
485544 searchForm . addEventListener ( 'submit' , async ( e ) => {
486545 e . preventDefault ( ) ;
487546 const q = document . getElementById ( 'search-query' ) . value . trim ( ) ;
488547 if ( ! q ) return ;
489548
490549 const params = new URLSearchParams ( { q } ) ;
491- const type = document . getElementById ( 'search-type' ) . value . trim ( ) ;
492- const project = document . getElementById ( 'search-project' ) . value . trim ( ) ;
550+ const type = searchTypeSelect . value ;
551+ const project = searchProjectSelect . value ;
493552 const dateStart = document . getElementById ( 'search-date-start' ) . value ;
494553 const dateEnd = document . getElementById ( 'search-date-end' ) . value ;
495554 if ( type ) params . set ( 'type' , type ) ;
@@ -498,17 +557,24 @@ <h2 id="section-title">Live Feed</h2>
498557 if ( dateEnd ) params . set ( 'dateEnd' , dateEnd ) ;
499558
500559 searchResults . innerHTML = '<div class="placeholder">Searching...</div>' ;
560+ searchStatus . style . display = 'none' ;
501561 try {
502562 const resp = await fetch ( '/api/observations/search?' + params ) ;
503563 const results = await resp . json ( ) ;
504564 if ( ! results || results . length === 0 ) {
505565 searchResults . innerHTML = '<div class="placeholder">No results found.</div>' ;
566+ searchStatus . textContent = '0 results' ;
567+ searchStatus . style . display = 'block' ;
568+ searchClear . style . display = 'inline-block' ;
506569 return ;
507570 }
508571 searchResults . innerHTML = '' ;
509572 for ( const obs of results ) {
510573 searchResults . appendChild ( createEventCard ( obs , fmtTime ( obs . CreatedAt ) ) ) ;
511574 }
575+ searchStatus . textContent = results . length + ' result' + ( results . length !== 1 ? 's' : '' ) ;
576+ searchStatus . style . display = 'block' ;
577+ searchClear . style . display = 'inline-block' ;
512578 } catch ( err ) {
513579 searchResults . innerHTML = '<div class="placeholder">Search failed.</div>' ;
514580 console . error ( 'Search error:' , err ) ;
0 commit comments