From 04fed23aaaf99decd347f73444a059fbdc50b93e Mon Sep 17 00:00:00 2001 From: Anchel135 Date: Wed, 26 Mar 2025 13:53:27 +0200 Subject: [PATCH 1/2] 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. --- app/components/code-graph.tsx | 23 ++++++++---- app/components/combobox.tsx | 67 +++++++++++++++++++++++------------ app/components/graphView.tsx | 1 - app/page.tsx | 43 ++++++++++++---------- 4 files changed, 85 insertions(+), 49 deletions(-) diff --git a/app/components/code-graph.tsx b/app/components/code-graph.tsx index fdf31e5f..207ef111 100644 --- a/app/components/code-graph.tsx +++ b/app/components/code-graph.tsx @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; import { Graph, GraphData, Node, Link } from "./model"; import { Toolbar } from "./toolbar"; import { Labels } from "./labels"; -import { Download, GitFork, Search, X } from "lucide-react"; +import { Download, GitFork, Loader2, Search, X } from "lucide-react"; import ElementMenu from "./elementMenu"; import Combobox from "./combobox"; import { toast } from '@/components/ui/use-toast'; @@ -22,6 +22,7 @@ interface Props { data: GraphData, setData: Dispatch>, onFetchGraph: (graphName: string) => Promise, + isFetchingGraph: boolean, onFetchNode: (nodeIds: number[]) => Promise, options: string[] setOptions: Dispatch> @@ -49,9 +50,8 @@ export function CodeGraph({ data, setData, onFetchGraph, + isFetchingGraph, onFetchNode, - options, - setOptions, isShowPath, setPath, chartRef, @@ -82,6 +82,7 @@ export function CodeGraph({ const [commitIndex, setCommitIndex] = useState(0); const [currentCommit, setCurrentCommit] = useState(0); const containerRef = useRef(null); + const [options, setOptions] = useState([]); useEffect(() => { setData({ ...graph.Elements }) @@ -370,10 +371,18 @@ export function CodeGraph({ - :
- -

Select a repo to show its graph here

-
+ : ( + isFetchingGraph ? +
+ +

Fetching graph...

+
+ : +
+ +

Select a repo to show its graph here

+
+ ) } {/* { diff --git a/app/components/combobox.tsx b/app/components/combobox.tsx index c22d9334..63bc877e 100644 --- a/app/components/combobox.tsx +++ b/app/components/combobox.tsx @@ -1,5 +1,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { toast } from "@/components/ui/use-toast"; +import { Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; interface Props { @@ -13,55 +14,75 @@ interface Props { export default function Combobox({ options, setOptions, selectedValue, onSelectedValue }: Props) { const [open, setOpen] = useState(false) - const [lastOpened, setLastOpened] = useState(); + const [lastFetch, setLastFetch] = useState(); + const [isFetchingOptions, setIsFetchingOptions] = useState(false) const fetchOptions = async () => { - const result = await fetch(`/api/repo`, { - method: 'GET', - }) + setIsFetchingOptions(true) - if (!result.ok) { - toast({ - variant: "destructive", - title: "Uh oh! Something went wrong.", - description: await result.text(), + try { + const result = await fetch(`/api/repo`, { + method: 'GET', }) - return - } - const json = await result.json() - setOptions(json.result) + if (!result.ok) { + toast({ + variant: "destructive", + title: "Uh oh! Something went wrong.", + description: await result.text(), + }) + return + } + + const json = await result.json() + setOptions(json.result) + } finally { + setIsFetchingOptions(false) + } } useEffect(() => { fetchOptions() }, []) + //fetch options when the combobox is opened useEffect(() => { if (!open) return const now = Date.now(); - if (lastOpened && now - lastOpened < 30000) return; - - setLastOpened(now); - + //check if last fetch was less than 30 seconds ago + if (lastFetch && now - lastFetch < 30000) return; + + setLastFetch(now); + fetchOptions() }, [open]) return ( - { - options.length !== 0 && - options.map((option) => ( - - {option} + isFetchingOptions ? + +
+ +

Fetching options...

+
- )) + : options.length !== 0 ? + options.map((option) => ( + + {option} + + )) + : + +

No options found

+
}
diff --git a/app/components/graphView.tsx b/app/components/graphView.tsx index c221e0ac..ea9387d1 100644 --- a/app/components/graphView.tsx +++ b/app/components/graphView.tsx @@ -352,7 +352,6 @@ export default function GraphView({ onZoom={() => unsetSelectedObjects()} onEngineStop={() => { setCooldownTicks(0) - debugger handleZoomToFit(chartRef, zoomedNodes.length === 1 ? 4 : 1, (n: NodeObject) => zoomedNodes.some(node => node.id === n.id)) setZoomedNodes([]) }} diff --git a/app/page.tsx b/app/page.tsx index ce2cab5a..d349482c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -79,6 +79,7 @@ export default function Home() { const [activeIndex, setActiveIndex] = useState(0) const [carouselApi, setCarouselApi] = useState() const [zoomedNodes, setZoomedNodes] = useState([]) + const [isFetchingGraph, setIsFetchingGraph] = useState(false) useEffect(() => { if (path?.start?.id && path?.end?.id) { @@ -138,27 +139,31 @@ export default function Home() { async function onFetchGraph(graphName: string) { setGraph(Graph.empty()) + setIsFetchingGraph(true) + try { + const result = await fetch(`/api/repo/${prepareArg(graphName)}`, { + method: 'GET' + }) - const result = await fetch(`/api/repo/${prepareArg(graphName)}`, { - method: 'GET' - }) + if (!result.ok) { + toast({ + variant: "destructive", + title: "Uh oh! Something went wrong.", + description: await result.text(), + }) + return + } - if (!result.ok) { - toast({ - variant: "destructive", - title: "Uh oh! Something went wrong.", - description: await result.text(), - }) - return + const json = await result.json() + const g = Graph.create(json.result.entities, graphName) + setGraph(g) + setIsPathResponse(false) + chatPanel.current?.expand() + // @ts-ignore + window.graph = g + } finally { + setIsFetchingGraph(false) } - - const json = await result.json() - const g = Graph.create(json.result.entities, graphName) - setGraph(g) - setIsPathResponse(false) - chatPanel.current?.expand() - // @ts-ignore - window.graph = g } // Send the user query to the server to expand a node @@ -391,6 +396,7 @@ export default function Home() { options={options} setOptions={setOptions} onFetchGraph={onFetchGraph} + isFetchingGraph={isFetchingGraph} onFetchNode={onFetchNode} setPath={setPath} isShowPath={!!path} @@ -509,6 +515,7 @@ export default function Home() { options={options} setOptions={setOptions} onFetchGraph={onFetchGraph} + isFetchingGraph={isFetchingGraph} onFetchNode={onFetchNode} setPath={setPath} isShowPath={!!path} From ef7dacae56ad8b9d3132d1a5d0b0afea00fec418 Mon Sep 17 00:00:00 2001 From: Anchel135 Date: Wed, 26 Mar 2025 14:47:13 +0200 Subject: [PATCH 2/2] Enhance Combobox to disable when no options are available - Updated the Combobox component to disable the select input when there are no options and not fetching options, improving user experience. --- app/components/combobox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/combobox.tsx b/app/components/combobox.tsx index 63bc877e..98eabbb1 100644 --- a/app/components/combobox.tsx +++ b/app/components/combobox.tsx @@ -60,7 +60,7 @@ export default function Combobox({ options, setOptions, selectedValue, onSelecte }, [open]) return ( -