11import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from "@/components/ui/select" ;
22import { toast } from "@/components/ui/use-toast" ;
3+ import { Loader2 } from "lucide-react" ;
34import { useEffect , useState } from "react" ;
45
56const AUTH_HEADERS : HeadersInit = import . meta. env . VITE_SECRET_TOKEN
@@ -17,60 +18,86 @@ interface Props {
1718export default function Combobox ( { options, setOptions, selectedValue, onSelectedValue } : Props ) {
1819
1920 const [ open , setOpen ] = useState ( false )
20- const [ lastOpened , setLastOpened ] = useState < number > ( ) ;
21+ const [ lastFetch , setLastFetch ] = useState < number > ( ) ;
22+ const [ isFetchingOptions , setIsFetchingOptions ] = useState ( false )
2123
2224 const fetchOptions = async ( ) => {
23- const result = await fetch ( `/api/list_repos` , {
24- method : 'GET' ,
25- headers : {
26- ...AUTH_HEADERS ,
27- } ,
28- } )
25+ setIsFetchingOptions ( true )
2926
30- if ( ! result . ok ) {
27+ try {
28+ const result = await fetch ( `/api/list_repos` , {
29+ method : 'GET' ,
30+ headers : {
31+ ...AUTH_HEADERS ,
32+ } ,
33+ } )
34+
35+ if ( ! result . ok ) {
36+ toast ( {
37+ variant : "destructive" ,
38+ title : "Uh oh! Something went wrong." ,
39+ description : await result . text ( ) ,
40+ } )
41+ return
42+ }
43+
44+ const json = await result . json ( )
45+ setOptions ( json . repositories )
46+ } catch {
3147 toast ( {
3248 variant : "destructive" ,
3349 title : "Uh oh! Something went wrong." ,
34- description : await result . text ( ) ,
50+ description : "Failed to fetch repositories. Please try again." ,
3551 } )
36- return
52+ } finally {
53+ setIsFetchingOptions ( false )
3754 }
38-
39- const json = await result . json ( )
40- setOptions ( json . repositories )
4155 }
4256
4357 useEffect ( ( ) => {
4458 fetchOptions ( )
4559 } , [ ] )
4660
61+ //fetch options when the combobox is opened
4762 useEffect ( ( ) => {
4863 if ( ! open ) return
4964
5065 const now = Date . now ( ) ;
5166
52- if ( lastOpened && now - lastOpened < 30000 ) return ;
53-
54- setLastOpened ( now ) ;
55-
67+ //check if last fetch was less than 30 seconds ago
68+ if ( lastFetch && now - lastFetch < 30000 ) return ;
69+
70+ setLastFetch ( now ) ;
71+
5672 fetchOptions ( )
5773 } , [ open ] )
5874
5975 return (
60- < Select open = { open } onOpenChange = { setOpen } value = { selectedValue } onValueChange = { onSelectedValue } >
76+ < Select open = { open } onOpenChange = { setOpen } disabled = { isFetchingOptions || options . length === 0 } value = { options . length !== 0 ? selectedValue : undefined } onValueChange = { onSelectedValue } >
6177 < SelectTrigger className = "z-10 md:z-0 rounded-md border border-border focus:ring-1 focus:ring-primary" >
6278 < SelectValue placeholder = "Select a repo" />
6379 </ SelectTrigger >
6480 < SelectContent >
6581 {
66- options . length !== 0 &&
67- options . map ( ( option ) => (
68- < SelectItem key = { option } value = { option } >
69- { option }
82+ isFetchingOptions ?
83+ < SelectItem value = "Fetching options..." >
84+ < div className = "flex flex-row items-center gap-2" >
85+ < Loader2 className = "w-4 h-4 animate-spin" />
86+ < p > Fetching options...</ p >
87+ </ div >
7088 </ SelectItem >
71- ) )
89+ : options . length !== 0 ?
90+ options . map ( ( option ) => (
91+ < SelectItem key = { option } value = { option } >
92+ { option }
93+ </ SelectItem >
94+ ) )
95+ :
96+ < SelectItem value = "No options found" >
97+ < p > No options found</ p >
98+ </ SelectItem >
7299 }
73100 </ SelectContent >
74101 </ Select >
75102 )
76- }
103+ }
0 commit comments