Skip to content

Commit 1bab862

Browse files
Anchel123gkorland
authored andcommitted
Add loading indicators for graph fetching and options loading
- Introduced `isFetchingGraph` state to manage loading state in the graph component. - Updated `CodeGraph` to display a loading spinner while fetching the graph. - Enhanced `Combobox` to show a loading state when fetching options.
1 parent c372a5e commit 1bab862

2 files changed

Lines changed: 67 additions & 31 deletions

File tree

app/src/components/code-graph.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
22
import { Graph, GraphData, Node, Link } from "./model";
33
import { Toolbar } from "./toolbar";
44
import { Labels } from "./labels";
5-
import { Download, GitFork, Search, X } from "lucide-react";
5+
import { Download, GitFork, Loader2, Search, X } from "lucide-react";
66
import { Button } from "@/components/ui/button";
77
import ElementMenu from "./elementMenu";
88
import Combobox from "./combobox";
@@ -28,6 +28,7 @@ interface Props {
2828
data: GraphData,
2929
setData: Dispatch<SetStateAction<GraphData>>,
3030
onFetchGraph: (graphName: string) => Promise<void>,
31+
isFetchingGraph: boolean,
3132
onFetchNode: (nodeIds: number[]) => Promise<GraphData>,
3233
options: string[]
3334
setOptions: Dispatch<SetStateAction<string[]>>
@@ -58,9 +59,8 @@ export function CodeGraph({
5859
data,
5960
setData,
6061
onFetchGraph,
62+
isFetchingGraph,
6163
onFetchNode,
62-
options,
63-
setOptions,
6464
isShowPath,
6565
setPath,
6666
canvasRef,
@@ -93,6 +93,7 @@ export function CodeGraph({
9393
const [commitIndex, setCommitIndex] = useState<number>(0);
9494
const [currentCommit, setCurrentCommit] = useState(0);
9595
const containerRef = useRef<HTMLDivElement>(null);
96+
const [options, setOptions] = useState<string[]>([]);
9697

9798
useEffect(() => {
9899
setData({ ...graph.Elements })
@@ -516,10 +517,18 @@ export function CodeGraph({
516517
</div>
517518
</div>
518519
</div>
519-
: <div className="flex flex-col items-center justify-center h-full text-muted-foreground">
520-
<GitFork className="md:w-24 md:h-24 w-16 h-16" />
521-
<h1 className="md:text-4xl text-2xl text-center">Select a repo to show its graph here</h1>
522-
</div>
520+
: (
521+
isFetchingGraph ?
522+
<div className="flex flex-col items-center justify-center h-full text-muted-foreground">
523+
<Loader2 className="md:w-24 md:h-24 w-16 h-16 animate-spin" />
524+
<h1 className="md:text-4xl text-2xl text-center">Fetching graph...</h1>
525+
</div>
526+
:
527+
<div className="flex flex-col items-center justify-center h-full text-muted-foreground">
528+
<GitFork className="md:w-24 md:h-24 w-16 h-16" />
529+
<h1 className="md:text-4xl text-2xl text-center">Select a repo to show its graph here</h1>
530+
</div>
531+
)
523532
}
524533
</main>
525534
{/* {

app/src/components/combobox.tsx

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
22
import { toast } from "@/components/ui/use-toast";
3+
import { Loader2 } from "lucide-react";
34
import { useEffect, useState } from "react";
45

56
const AUTH_HEADERS: HeadersInit = import.meta.env.VITE_SECRET_TOKEN
@@ -17,60 +18,86 @@ interface Props {
1718
export 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

Comments
 (0)