diff --git a/client/src/App.tsx b/client/src/App.tsx index e003023..b05d9fe 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -12,10 +12,6 @@ import NotFound from './pages/NotFound'; import ServerDetails from './pages/ServerDetails'; export function App() { - const [alertInfo, setAlertInfo] = useState<{ - message: string; - severity: 'error' | 'warning' | 'info' | 'success'; - } | null>(null); const [servers, setServers] = useState([]); const [searchState, setSearchState] = @@ -24,13 +20,12 @@ export function App() { return (
- + @@ -38,10 +33,7 @@ export function App() { path="/" element={} /> - } - /> + } /> } /> } /> diff --git a/client/src/components/AddServer.tsx b/client/src/components/AddServer.tsx index bbf4300..5676aee 100644 --- a/client/src/components/AddServer.tsx +++ b/client/src/components/AddServer.tsx @@ -12,8 +12,8 @@ import { import { useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { postData } from '../apiUtil'; +import { useLoadingContext } from '../context/LoadingContext'; import { ServerData } from '../data/ServerData'; -import { AlertResponse } from './Alert'; const AddServerInput = styled('div')(({ theme }) => ({ position: 'relative', @@ -43,15 +43,13 @@ const StyledInputBase = styled(InputBase)(({ theme }) => ({ }, })); -export default function AddServer({ - setAlertInfo, - servers, - setServers, -}: { - setAlertInfo: React.Dispatch>; +type AddServerProps = { servers: ServerData[]; setServers: React.Dispatch>; -}) { +}; + +export default function AddServer({ servers, setServers }: AddServerProps) { + const { showAlert } = useLoadingContext(); const navigate = useNavigate(); const containerRef = useRef(null); const inputRef = useRef(null); @@ -68,7 +66,7 @@ export default function AddServer({ setIsLoading(true); const response = await postData(inputText); if (response.success) { - setAlertInfo({ + showAlert({ message: response.message, severity: 'success', }); @@ -81,7 +79,7 @@ export default function AddServer({ toggleShowAddServer(); } } else { - setAlertInfo({ + showAlert({ message: response.message || 'Failed to send data to backend.', severity: 'error', }); diff --git a/client/src/components/Alert.tsx b/client/src/components/Alert.tsx index 0134548..7d2a6b1 100644 --- a/client/src/components/Alert.tsx +++ b/client/src/components/Alert.tsx @@ -1,29 +1,21 @@ import { Alert, Box, Collapse } from '@mui/material'; +import { useLoadingContext } from '../context/LoadingContext'; -export type AlertResponse = { - message: string; - severity: 'error' | 'warning' | 'info' | 'success'; -} | null; +export default function AlertComponent() { + const { alert, showAlert } = useLoadingContext(); -export default function AlertComponent({ - alertInfo, - setAlertInfo, -}: { - alertInfo: AlertResponse; - setAlertInfo: React.Dispatch>; -}) { return ( - {alertInfo && ( + {alert && ( setAlertInfo(null)} + severity={alert.severity} + onClose={() => showAlert(null)} variant="filled" className="py-0" square > - {alertInfo.message} + {alert.message} )} diff --git a/client/src/components/ErrorCard.tsx b/client/src/components/ErrorCard.tsx index f184368..59c8cda 100644 --- a/client/src/components/ErrorCard.tsx +++ b/client/src/components/ErrorCard.tsx @@ -1,6 +1,8 @@ import { Card, CardContent, Typography } from '@mui/material'; -export default function ErrorCard({ error }: { error: string }) { +type ErrorCardProps = { error: string }; + +export default function ErrorCard({ error }: ErrorCardProps) { return ( diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx index 16f60df..7529811 100644 --- a/client/src/components/Header.tsx +++ b/client/src/components/Header.tsx @@ -11,26 +11,26 @@ import IconButton from '@mui/material/IconButton'; import { useCallback, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { fetchDataFromBackend } from '../apiUtil'; +import { useLoadingContext } from '../context/LoadingContext'; import { SearchState } from '../data/SearchState'; import { ServerData } from '../data/ServerData'; import AddServer from './AddServer'; -import { AlertResponse } from './Alert'; import SearchServers from './SearchServers'; -export default function Header({ - setAlertInfo, - servers, - setServers, - searchState, - setSearchState, -}: { - setAlertInfo: React.Dispatch>; +type HeaderProps = { servers: ServerData[]; setServers: React.Dispatch>; searchState: SearchState; setSearchState: React.Dispatch>; -}) { - const [fetchLoading, setFetchLoading] = useState(0); +}; + +export default function Header({ + servers, + setServers, + searchState, + setSearchState, +}: HeaderProps) { + const { progress, setProgress, showAlert } = useLoadingContext(); const [showSearchServer, setShowSearchServer] = useState(false); const toggleShowSearchServer = () => { setShowSearchServer((prev) => !prev); @@ -39,27 +39,27 @@ export default function Header({ const fetchServerData = useCallback(async () => { let data: ServerData[] = []; try { - setFetchLoading(25); + setProgress(25); data = await fetchDataFromBackend(); } catch (err) { if (err instanceof Error) { - setAlertInfo({ + showAlert({ message: err.message, severity: 'error', }); } else { - setAlertInfo({ + showAlert({ message: 'An unknown error occurred.', severity: 'error', }); } } setServers(data); - setFetchLoading(100); + setProgress(100); setTimeout(() => { - setFetchLoading(0); + setProgress(0); }, 500); - }, [setFetchLoading, setAlertInfo, setServers]); + }, [showAlert, setServers, setProgress]); useEffect(() => { fetchServerData(); @@ -81,13 +81,9 @@ export default function Header({ > IXION - + - + @@ -100,10 +96,10 @@ export default function Header({ >; +}; + export default function SearchServers({ showSearchServer, searchState, setSearchState, -}: { - showSearchServer: boolean; - searchState: SearchState; - setSearchState: React.Dispatch>; -}) { +}: SearchServersProps) { const containerRef = useRef(null); const [contentHeight, setContentHeight] = useState(0); const minDistance = 0; diff --git a/client/src/components/Server/ExpansionsBar.tsx b/client/src/components/Server/ExpansionsBar.tsx index b43bcb9..0087928 100644 --- a/client/src/components/Server/ExpansionsBar.tsx +++ b/client/src/components/Server/ExpansionsBar.tsx @@ -49,7 +49,9 @@ const expansions = [ , ]; -export default function ExpansionBar({ server }: { server: ServerData }) { +type ExpansionBarProps = { server: ServerData }; + +export default function ExpansionBar({ server }: ExpansionBarProps) { return ( void; + progress: number; + setProgress: (progress: number) => void; +}; + +const LoadingContext = createContext(null); + +// eslint-disable-next-line react-refresh/only-export-components +export function useLoadingContext() { + const context = useContext(LoadingContext); + if (!context) { + throw new Error('Loading context error.'); + } + return context; +} + +type LoadingContextProviderProps = { + children: ReactNode; +}; + +export function LoadingContextProvider({ + children, +}: LoadingContextProviderProps) { + const [alert, setAlert] = useState(null); + const [progress, setProgress] = useState(0); + + const showAlert = useCallback((alertInfo: AlertInfo) => { + setAlert(alertInfo); + }, []); + + return ( + + {children} + + ); +} diff --git a/client/src/context/ThemeContext.tsx b/client/src/context/ThemeContext.tsx index 3271184..2de8aab 100644 --- a/client/src/context/ThemeContext.tsx +++ b/client/src/context/ThemeContext.tsx @@ -16,7 +16,16 @@ type ThemeModeState = { setThemeMode: React.Dispatch>; }; -export const ThemeModeContext = createContext(null); +const ThemeModeContext = createContext(null); + +// eslint-disable-next-line react-refresh/only-export-components +export function useThemeModeContext() { + const context = useContext(ThemeModeContext); + if (!context) { + throw new Error('Theme context error.'); + } + return context; +} type ThemeModeContextProviderProps = { children: ReactNode; @@ -82,12 +91,3 @@ export function ThemeContextProvider({ ); } - -// eslint-disable-next-line react-refresh/only-export-components -export function useThemeModeContext() { - const context = useContext(ThemeModeContext); - if (!context) { - throw new Error('Theme context error.'); - } - return context; -} diff --git a/client/src/main.tsx b/client/src/main.tsx index f537b6e..f10be0c 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -3,6 +3,7 @@ import { StyledEngineProvider } from '@mui/material/styles'; import { StrictMode } from 'react'; import ReactDOM from 'react-dom/client'; import { WrappedApp } from './App'; +import { LoadingContextProvider } from './context/LoadingContext'; import { ThemeContextProvider } from './context/ThemeContext'; import './index.css'; @@ -13,8 +14,10 @@ root.render( - - + + + + diff --git a/client/src/pages/ServerDetails.tsx b/client/src/pages/ServerDetails.tsx index e3c2d1f..6158a5f 100644 --- a/client/src/pages/ServerDetails.tsx +++ b/client/src/pages/ServerDetails.tsx @@ -2,17 +2,14 @@ import { Box } from '@mui/material'; import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { fetchDataFromBackend, fetchDemo } from '../apiUtil'; -import { AlertResponse } from '../components/Alert'; import ErrorCard from '../components/ErrorCard'; import SettingsDataGrid from '../components/Server/SettingsDataGrid'; import ServerCard from '../components/ServerCard'; +import { useLoadingContext } from '../context/LoadingContext'; import { ServerData } from '../data/ServerData'; -export default function ServerDetails({ - setAlertInfo, -}: { - setAlertInfo: React.Dispatch>; -}) { +export default function ServerDetails() { + const { showAlert } = useLoadingContext(); const { url } = useParams(); const [server, setServer] = useState(); const [error, setError] = useState(''); @@ -28,7 +25,7 @@ export default function ServerDetails({ } } catch (err) { if (err instanceof Error) { - setAlertInfo({ + showAlert({ message: err.message, severity: 'error', }); @@ -41,7 +38,7 @@ export default function ServerDetails({ }; fetchServerData(); - }, [url, setServer, setAlertInfo, setError]); + }, [url, setServer, showAlert, setError]); return (