diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 467e15ed8..2109222ed 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -9,10 +9,16 @@ import ErrorAlert from "./alerts/error"; import NoticeAlert from "./alerts/notice"; import SuccessAlert from "./alerts/success"; import CrashErrorComponent from "./components/CrashErrorComponent"; +import FetchErrorComponent from "./components/fetchErrorComponent"; import LoadingComponent from "./components/loadingComponent"; +import { + FETCH_ERROR_DESCRIPION, + FETCH_ERROR_MESSAGE, +} from "./constants/constants"; import { alertContext } from "./contexts/alertContext"; import { locationContext } from "./contexts/locationContext"; import { TabsContext } from "./contexts/tabsContext"; +import { typesContext } from "./contexts/typesContext"; import Router from "./routes"; export default function App() { @@ -38,6 +44,7 @@ export default function App() { setSuccessOpen, loading, } = useContext(alertContext); + const { fetchError } = useContext(typesContext); // Initialize state variable for the list of alerts const [alertsList, setAlertsList] = useState< @@ -137,7 +144,14 @@ export default function App() { > {loading ? (
- + {fetchError ? ( + + ) : ( + + )}
) : ( <> diff --git a/src/frontend/src/components/fetchErrorComponent/index.tsx b/src/frontend/src/components/fetchErrorComponent/index.tsx new file mode 100644 index 000000000..6004d9dfc --- /dev/null +++ b/src/frontend/src/components/fetchErrorComponent/index.tsx @@ -0,0 +1,16 @@ +import { fetchErrorComponentType } from "../../types/components"; +import IconComponent from "../genericIconComponent"; + +export default function FetchErrorComponent({ + message, + description, +}: fetchErrorComponentType) { + return ( +
+ +

+ {message} + {description} +
+ ); +} diff --git a/src/frontend/src/components/genericIconComponent/index.tsx b/src/frontend/src/components/genericIconComponent/index.tsx index 32647f159..a06707d74 100644 --- a/src/frontend/src/components/genericIconComponent/index.tsx +++ b/src/frontend/src/components/genericIconComponent/index.tsx @@ -7,5 +7,11 @@ export default function IconComponent({ iconColor, }: IconComponentProps): JSX.Element { const TargetIcon = nodeIconsLucide[name] ?? nodeIconsLucide["unknown"]; - return ; + return ( + + ); } diff --git a/src/frontend/src/components/loadingComponent/index.tsx b/src/frontend/src/components/loadingComponent/index.tsx index 31e78d474..a1e406710 100644 --- a/src/frontend/src/components/loadingComponent/index.tsx +++ b/src/frontend/src/components/loadingComponent/index.tsx @@ -1,6 +1,4 @@ -type LoadingComponentProps = { - remSize: number; -}; +import { LoadingComponentProps } from "../../types/components"; export default function LoadingComponent({ remSize }: LoadingComponentProps) { return ( diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index 3f698c64e..aa9f1555a 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -511,3 +511,7 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [ ]; export const skipNodeUpdate = ["CustomComponent"]; + +export const FETCH_ERROR_MESSAGE = "Couldn't establish a connection."; +export const FETCH_ERROR_DESCRIPION = + "Check if everything is working properly and try again."; diff --git a/src/frontend/src/contexts/typesContext.tsx b/src/frontend/src/contexts/typesContext.tsx index ecdfb95a9..05346838d 100644 --- a/src/frontend/src/contexts/typesContext.tsx +++ b/src/frontend/src/contexts/typesContext.tsx @@ -6,7 +6,7 @@ import { useState, } from "react"; import { Node } from "reactflow"; -import { getAll } from "../controllers/API"; +import { getAll, getHealth } from "../controllers/API"; import { APIKindType } from "../types/api"; import { typesContextType } from "../types/typesContext"; import { alertContext } from "./alertContext"; @@ -23,6 +23,8 @@ const initialValue: typesContextType = { setTemplates: () => {}, data: {}, setData: () => {}, + setFetchError: () => {}, + fetchError: false, }; export const typesContext = createContext(initialValue); @@ -32,14 +34,10 @@ export function TypesProvider({ children }: { children: ReactNode }) { const [reactFlowInstance, setReactFlowInstance] = useState(null); const [templates, setTemplates] = useState({}); const [data, setData] = useState({}); + const [fetchError, setFetchError] = useState(false); const { setLoading } = useContext(alertContext); useEffect(() => { - let delay = 1000; // Start delay of 1 second - let intervalId = null; - let retryCount = 0; // Count of retry attempts - const maxRetryCount = 5; // Max retry attempts - // We will keep a flag to handle the case where the component is unmounted before the API call resolves. let isMounted = true; @@ -47,7 +45,7 @@ export function TypesProvider({ children }: { children: ReactNode }) { try { const result = await getAll(); // Make sure to only update the state if the component is still mounted. - if (isMounted) { + if (isMounted && result?.status === 200) { setLoading(false); setData(result.data); setTemplates( @@ -77,22 +75,15 @@ export function TypesProvider({ children }: { children: ReactNode }) { }, {}) ); } - // Clear the interval if successful. - clearInterval(intervalId); } catch (error) { console.error("An error has occurred while fetching types."); + await getHealth().catch((e) => { + setFetchError(true); + }); } } - // Start the initial interval. - intervalId = setInterval(getTypes, delay); - - return () => { - // This will clear the interval when the component unmounts, or when the dependencies of the useEffect hook change. - clearInterval(intervalId); - // Indicate that the component has been unmounted. - isMounted = false; - }; + getTypes(); }, []); function deleteNode(idx: string) { @@ -117,6 +108,8 @@ export function TypesProvider({ children }: { children: ReactNode }) { templates, data, setData, + fetchError, + setFetchError, }} > {children} diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 5cf87b8d3..0ebe768c1 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -171,3 +171,11 @@ export type IconComponentProps = { export interface languageMap { [key: string]: string | undefined; } +export type fetchErrorComponentType = { + message: string; + description: string; +}; + +export type LoadingComponentProps = { + remSize: number; +}; diff --git a/src/frontend/src/types/typesContext/index.ts b/src/frontend/src/types/typesContext/index.ts index 64b6e3e9d..57b64d7e2 100644 --- a/src/frontend/src/types/typesContext/index.ts +++ b/src/frontend/src/types/typesContext/index.ts @@ -15,4 +15,6 @@ export type typesContextType = { setTemplates: (newState: {}) => void; data: typeof data; setData: (newState: {}) => void; + fetchError: boolean; + setFetchError: (newState: boolean) => void; }; diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 42fbe8773..ca100b5c3 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -56,6 +56,7 @@ import { TerminalSquare, Trash2, Undo, + Unplug, Upload, Users2, Variable, @@ -275,4 +276,5 @@ export const nodeIconsLucide = { Upload, MessageSquare, MoreHorizontal, + Unplug, };