mirror of https://github.com/cocosolos/ixion.git
Move alerts and progress into context
This commit is contained in:
parent
ddf74e8343
commit
5f215050b7
|
|
@ -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<ServerData[]>([]);
|
||||
|
||||
const [searchState, setSearchState] =
|
||||
|
|
@ -24,13 +20,12 @@ export function App() {
|
|||
return (
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<Header
|
||||
setAlertInfo={setAlertInfo}
|
||||
servers={servers}
|
||||
setServers={setServers}
|
||||
searchState={searchState}
|
||||
setSearchState={setSearchState}
|
||||
/>
|
||||
<AlertComponent alertInfo={alertInfo} setAlertInfo={setAlertInfo} />
|
||||
<AlertComponent />
|
||||
<Container className="grow p-0">
|
||||
<Box className="m-2">
|
||||
<Routes>
|
||||
|
|
@ -38,10 +33,7 @@ export function App() {
|
|||
path="/"
|
||||
element={<Home servers={servers} searchState={searchState} />}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:url"
|
||||
element={<ServerDetails setAlertInfo={setAlertInfo} />}
|
||||
/>
|
||||
<Route path="/server/:url" element={<ServerDetails />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
|
|
|
|||
|
|
@ -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<React.SetStateAction<AlertResponse>>;
|
||||
type AddServerProps = {
|
||||
servers: ServerData[];
|
||||
setServers: React.Dispatch<React.SetStateAction<ServerData[]>>;
|
||||
}) {
|
||||
};
|
||||
|
||||
export default function AddServer({ servers, setServers }: AddServerProps) {
|
||||
const { showAlert } = useLoadingContext();
|
||||
const navigate = useNavigate();
|
||||
const containerRef = useRef<HTMLElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(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',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<React.SetStateAction<AlertResponse>>;
|
||||
}) {
|
||||
return (
|
||||
<Box className="m-0">
|
||||
{alertInfo && (
|
||||
{alert && (
|
||||
<Collapse in>
|
||||
<Alert
|
||||
severity={alertInfo.severity}
|
||||
onClose={() => setAlertInfo(null)}
|
||||
severity={alert.severity}
|
||||
onClose={() => showAlert(null)}
|
||||
variant="filled"
|
||||
className="py-0"
|
||||
square
|
||||
>
|
||||
{alertInfo.message}
|
||||
{alert.message}
|
||||
</Alert>
|
||||
</Collapse>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<Card className="mb-2">
|
||||
<CardContent className="p-4">
|
||||
|
|
|
|||
|
|
@ -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<React.SetStateAction<AlertResponse>>;
|
||||
type HeaderProps = {
|
||||
servers: ServerData[];
|
||||
setServers: React.Dispatch<React.SetStateAction<ServerData[]>>;
|
||||
searchState: SearchState;
|
||||
setSearchState: React.Dispatch<React.SetStateAction<SearchState>>;
|
||||
}) {
|
||||
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
|
||||
</Typography>
|
||||
<AddServer
|
||||
setAlertInfo={setAlertInfo}
|
||||
servers={servers}
|
||||
setServers={setServers}
|
||||
/>
|
||||
<AddServer servers={servers} setServers={setServers} />
|
||||
<Tooltip arrow disableInteractive title="Refresh server data.">
|
||||
<IconButton onClick={fetchServerData} disabled={fetchLoading !== 0}>
|
||||
<IconButton onClick={fetchServerData} disabled={progress !== 0}>
|
||||
<Refresh />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
|
@ -100,10 +96,10 @@ export default function Header({
|
|||
</AppBar>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={fetchLoading}
|
||||
value={progress}
|
||||
sx={{
|
||||
visibility: fetchLoading === 0 ? 'hidden' : 'visible',
|
||||
height: fetchLoading === 0 ? 0 : '1px',
|
||||
visibility: progress === 0 ? 'hidden' : 'visible',
|
||||
height: progress === 0 ? 0 : '1px',
|
||||
}}
|
||||
/>
|
||||
<SearchServers
|
||||
|
|
|
|||
|
|
@ -12,15 +12,17 @@ import {
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
import { SearchState } from '../data/SearchState';
|
||||
|
||||
type SearchServersProps = {
|
||||
showSearchServer: boolean;
|
||||
searchState: SearchState;
|
||||
setSearchState: React.Dispatch<React.SetStateAction<SearchState>>;
|
||||
};
|
||||
|
||||
export default function SearchServers({
|
||||
showSearchServer,
|
||||
searchState,
|
||||
setSearchState,
|
||||
}: {
|
||||
showSearchServer: boolean;
|
||||
searchState: SearchState;
|
||||
setSearchState: React.Dispatch<React.SetStateAction<SearchState>>;
|
||||
}) {
|
||||
}: SearchServersProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [contentHeight, setContentHeight] = useState(0);
|
||||
const minDistance = 0;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ const expansions = [
|
|||
</ToggleButton>,
|
||||
];
|
||||
|
||||
export default function ExpansionBar({ server }: { server: ServerData }) {
|
||||
type ExpansionBarProps = { server: ServerData };
|
||||
|
||||
export default function ExpansionBar({ server }: ExpansionBarProps) {
|
||||
return (
|
||||
<ToggleButtonGroup
|
||||
size="small"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import {
|
|||
ServerSettingsInfo,
|
||||
} from '../../data/ServerData';
|
||||
|
||||
export default function SettingsChipCloud({ server }: { server: ServerData }) {
|
||||
type SettingsChipCloudProps = { server: ServerData };
|
||||
|
||||
export default function SettingsChipCloud({ server }: SettingsChipCloudProps) {
|
||||
const renderSettingsChip = ([key, value]: [
|
||||
string,
|
||||
boolean | string | number,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
import {
|
||||
ReactNode,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
export type AlertInfo = {
|
||||
message: string;
|
||||
severity: 'error' | 'warning' | 'info' | 'success';
|
||||
} | null;
|
||||
|
||||
type LoadingState = {
|
||||
alert: AlertInfo;
|
||||
showAlert: (alertInfo: AlertInfo) => void;
|
||||
progress: number;
|
||||
setProgress: (progress: number) => void;
|
||||
};
|
||||
|
||||
const LoadingContext = createContext<LoadingState | null>(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<AlertInfo>(null);
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
const showAlert = useCallback((alertInfo: AlertInfo) => {
|
||||
setAlert(alertInfo);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LoadingContext.Provider
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
value={{ alert, showAlert, progress, setProgress }}
|
||||
>
|
||||
{children}
|
||||
</LoadingContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,7 +16,16 @@ type ThemeModeState = {
|
|||
setThemeMode: React.Dispatch<React.SetStateAction<ThemeMode>>;
|
||||
};
|
||||
|
||||
export const ThemeModeContext = createContext<ThemeModeState | null>(null);
|
||||
const ThemeModeContext = createContext<ThemeModeState | null>(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({
|
|||
</StyledEngineProvider>
|
||||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
<StrictMode>
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeContextProvider>
|
||||
<CssBaseline />
|
||||
<WrappedApp />
|
||||
<LoadingContextProvider>
|
||||
<CssBaseline />
|
||||
<WrappedApp />
|
||||
</LoadingContextProvider>
|
||||
</ThemeContextProvider>
|
||||
</StyledEngineProvider>
|
||||
</StrictMode>
|
||||
|
|
|
|||
|
|
@ -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<React.SetStateAction<AlertResponse>>;
|
||||
}) {
|
||||
export default function ServerDetails() {
|
||||
const { showAlert } = useLoadingContext();
|
||||
const { url } = useParams();
|
||||
const [server, setServer] = useState<ServerData>();
|
||||
const [error, setError] = useState<string>('');
|
||||
|
|
@ -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 (
|
||||
<Box>
|
||||
|
|
|
|||
Loading…
Reference in New Issue