initial commit
This commit is contained in:
1 parent
14ba3016f6
commit
7ef10b4fcd
9 files changed
+138
-32
No files matched your search
+1
-1
@@ -8,7 +8,7 @@ import darkTheme from "./Utils/Theme/darkTheme";
|
||||
import { CssBaseline, GlobalStyles } from "@mui/material";
|
||||
import { logger } from "./Utils/Logger"; // Import the logger
|
||||
import { Routes } from "./Routes";
|
||||
import AppLayout from "@/Components/v1/Layouts/AppLayout";
|
||||
import AppLayout from "@/Components/v2/layout/AppLayout";
|
||||
import type { RootState } from "@/Types/state";
|
||||
|
||||
function App() {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import BackgroundSVG from "../../../../assets/Images/background.svg";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const AppLayout = ({ children }) => {
|
||||
const theme = useTheme();
|
||||
const ui = useSelector((state) => state.ui);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: "100vh",
|
||||
backgroundColor: theme.palette.primaryBackground.main,
|
||||
backgroundImage: ui?.mode === "dark" ? `url("${BackgroundSVG}")` : "none",
|
||||
backgroundSize: "100% 100%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
color: theme.palette.primary.contrastText,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
AppLayout.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default AppLayout;
|
||||
@@ -0,0 +1,76 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { WifiOff } from "lucide-react";
|
||||
|
||||
interface OfflineBannerProps {
|
||||
visible: boolean;
|
||||
onRetry?: () => void;
|
||||
isRetrying?: boolean;
|
||||
}
|
||||
|
||||
export const OfflineBanner = ({ visible, onRetry, isRetrying }: OfflineBannerProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: theme.zIndex.snackbar,
|
||||
backgroundColor: theme.palette.error.main,
|
||||
color: theme.palette.error.contrastText,
|
||||
px: theme.spacing(8),
|
||||
py: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<WifiOff size={20} />
|
||||
<Typography
|
||||
variant="body2"
|
||||
fontWeight={500}
|
||||
>
|
||||
{t("components.offlineBanner.serverUnreachable")}
|
||||
</Typography>
|
||||
{onRetry && (
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
onClick={onRetry}
|
||||
disabled={isRetrying}
|
||||
sx={{
|
||||
color: "inherit",
|
||||
borderColor: "currentColor",
|
||||
minWidth: "auto",
|
||||
py: 0.5,
|
||||
px: 2,
|
||||
"&:hover": {
|
||||
borderColor: "currentColor",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isRetrying
|
||||
? t("components.offlineBanner.retrying")
|
||||
: t("components.offlineBanner.retry")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -19,3 +19,4 @@ export * from "./Gauge";
|
||||
export * from "./Tabs";
|
||||
export * from "./SplitBox";
|
||||
export * from "./TextLink";
|
||||
export * from "./OfflineBanner";
|
||||
@@ -0,0 +1,39 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import { ThemeProvider, useTheme } from "@mui/material/styles";
|
||||
import { useSelector } from "react-redux";
|
||||
import BackgroundSVG from "@/assets/Images/background.svg";
|
||||
import type { RootState } from "@/Types/state";
|
||||
import { lightTheme, darkTheme } from "@/Utils/Theme/v2Theme";
|
||||
import { OfflineBanner } from "@/Components/v2/design-elements";
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
const theme = useTheme();
|
||||
const mode = useSelector((state: RootState) => state.ui.mode);
|
||||
const v2theme = mode === "dark" ? darkTheme : lightTheme;
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={v2theme}>
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: "100vh",
|
||||
// @ts-expect-error custom palette property
|
||||
backgroundColor: theme.palette.primaryBackground.main,
|
||||
backgroundImage: mode === "dark" ? `url("${BackgroundSVG}")` : "none",
|
||||
backgroundSize: "100% 100%",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
color: theme.palette.primary.contrastText,
|
||||
}}
|
||||
>
|
||||
<OfflineBanner visible={false} />
|
||||
{children}
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLayout;
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -15,6 +15,15 @@ type StoreType = {
|
||||
let storeInstance: StoreType | null = null;
|
||||
let interceptorsInitialized = false;
|
||||
|
||||
type ServerUnreachableCallback = (unreachable: boolean) => void;
|
||||
let serverUnreachableCallback: ServerUnreachableCallback | null = null;
|
||||
|
||||
export const setServerUnreachableCallback = (
|
||||
callback: ServerUnreachableCallback
|
||||
): void => {
|
||||
serverUnreachableCallback = callback;
|
||||
};
|
||||
|
||||
export const initApiClient = (store: StoreType): void => {
|
||||
storeInstance = store;
|
||||
|
||||
@@ -41,6 +50,12 @@ export const initApiClient = (store: StoreType): void => {
|
||||
|
||||
const onSuccess = (response: AxiosResponse) => response;
|
||||
const onError = (error: AxiosError) => {
|
||||
// Handle network errors (server unreachable)
|
||||
if (error.code === "ERR_NETWORK") {
|
||||
serverUnreachableCallback?.(true);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (error.response?.status === 401) {
|
||||
if (window.location.pathname !== "/login") {
|
||||
window.location.href = "/login";
|
||||
|
||||
@@ -325,6 +325,12 @@
|
||||
"week": "Showing statistics for past 7 days.",
|
||||
"month": "Showing statistics for past 30 days."
|
||||
}
|
||||
},
|
||||
"offlineBanner": {
|
||||
"serverUnreachable": "Unable to reach server",
|
||||
"retry": "Retry",
|
||||
"retrying": "Retrying...",
|
||||
"reconnected": "Connection restored"
|
||||
}
|
||||
},
|
||||
"configure": "Configure",
|
||||
|
||||
Reference in new issue
Block a user