mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-20 08:28:48 -05:00
base page, auth header, language and theme switches
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import Logo from "@/assets/icons/checkmate-icon.svg?react";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import Alert from "@mui/material/Alert";
|
||||
import Link from "@mui/material/Link";
|
||||
import {
|
||||
@@ -9,6 +11,7 @@ import {
|
||||
} from "@/Components/v2/design-elements/Fallback";
|
||||
import { Breadcrumb } from "@/Components/v2/design-elements/Breadcrumb";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import { HeaderAuthControls } from "@/Pages/Auth/components/HeaderAuthControls";
|
||||
|
||||
import type { StackProps } from "@mui/material/Stack";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
@@ -201,3 +204,46 @@ export const MonitorBasePageWithStates = ({
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
interface BaseAuthPageProps extends React.PropsWithChildren {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
}
|
||||
|
||||
export const BaseAuthPage = ({ title, subtitle, children }: BaseAuthPageProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<BasePage
|
||||
breadcrumbOverride={[]}
|
||||
gap={theme.spacing(8)}
|
||||
alignItems={"center"}
|
||||
justifyContent={"center"}
|
||||
minHeight="100vh"
|
||||
position={"relative"}
|
||||
>
|
||||
<HeaderAuthControls
|
||||
hideLogo
|
||||
py={theme.spacing(4)}
|
||||
position={"absolute"}
|
||||
top={0}
|
||||
left={0}
|
||||
/>
|
||||
<Box width={{ xs: 60, sm: 70, md: 80 }}>
|
||||
<Logo
|
||||
width={"100%"}
|
||||
height={"100%"}
|
||||
/>
|
||||
</Box>
|
||||
<Stack alignItems={"center"}>
|
||||
<Typography
|
||||
variant="h1"
|
||||
mb={theme.spacing(2)}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="h1">{subtitle}</Typography>
|
||||
</Stack>
|
||||
{children}
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { Select } from "@/Components/v2/inputs";
|
||||
import "flag-icons/css/flag-icons.min.css";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { setLanguage } from "@/Features/UI/uiSlice";
|
||||
import type { RootState } from "@/Types/state";
|
||||
|
||||
const langMap: Record<string, string> = {
|
||||
cs: "cz",
|
||||
ja: "jp",
|
||||
uk: "ua",
|
||||
vi: "vn",
|
||||
};
|
||||
|
||||
export const LanguageSelector = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { language = "en" } = useSelector((state: RootState) => state.ui);
|
||||
const dispatch = useDispatch();
|
||||
const handleChange = (event: any) => {
|
||||
const newLang = event.target.value;
|
||||
dispatch(setLanguage(newLang));
|
||||
};
|
||||
|
||||
const languages = Object.keys(i18n.options.resources || {});
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={language}
|
||||
onChange={handleChange}
|
||||
size="small"
|
||||
// sx={{
|
||||
// minWidth: 80,
|
||||
// "& .MuiSelect-select": {
|
||||
// display: "flex",
|
||||
// alignItems: "center",
|
||||
// justifyContent: "center",
|
||||
// },
|
||||
// "& .MuiSelect-icon": {
|
||||
// alignSelf: "center",
|
||||
// },
|
||||
// }}
|
||||
>
|
||||
{languages.map((lang) => {
|
||||
let parsedLang = lang === "en" ? "gb" : lang;
|
||||
|
||||
if (parsedLang.includes("-")) {
|
||||
parsedLang = parsedLang.split("-")[1].toLowerCase();
|
||||
}
|
||||
|
||||
parsedLang = langMap[parsedLang] || parsedLang;
|
||||
|
||||
const flag = parsedLang ? `fi fi-${parsedLang}` : null;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={lang}
|
||||
value={lang}
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={theme.spacing(2)}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{flag && <span className={flag} />}
|
||||
</Box>
|
||||
<Box
|
||||
component="span"
|
||||
sx={{ textTransform: "uppercase" }}
|
||||
>
|
||||
{lang}
|
||||
</Box>
|
||||
</Stack>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageSelector;
|
||||
@@ -0,0 +1,48 @@
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
|
||||
import { Moon, Sun } from "lucide-react";
|
||||
|
||||
import { setMode } from "@/Features/UI/uiSlice.js";
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import type { RootState } from "@/Types/state";
|
||||
|
||||
export const SwitchTheme = () => {
|
||||
const mode = useSelector((state: RootState) => state.ui.mode);
|
||||
const dispatch = useDispatch();
|
||||
const theme = useTheme();
|
||||
|
||||
const handleChange = () => {
|
||||
dispatch(setMode(mode === "light" ? "dark" : "light"));
|
||||
};
|
||||
return (
|
||||
<IconButton
|
||||
id="theme-toggle"
|
||||
onClick={handleChange}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
"& svg": {
|
||||
transition: "stroke 0.2s ease",
|
||||
},
|
||||
"&:hover svg path, &:hover svg line, &:hover svg polyline, &:hover svg rect, &:hover svg circle":
|
||||
{
|
||||
stroke: theme.palette.primary.main,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{mode === "light" ? (
|
||||
<Moon
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
) : (
|
||||
<Sun
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
@@ -16,3 +16,5 @@ export * from "./ImageUpload";
|
||||
export * from "./ColorPicker";
|
||||
export { DatePickerComponent as DatePicker } from "./DatePicker";
|
||||
export { TimePickerComponent as TimePicker } from "./TimePicker";
|
||||
export * from "./LanguageSelector";
|
||||
export * from "./SwitchTheme";
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from "react";
|
||||
import AppBar from "@/Components/v1/Common/AppBar.jsx";
|
||||
import Footer from "@/Components/v1/Common/Footer.jsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AboutUs = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AppBar />
|
||||
<h1>{t("aboutus")}</h1>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutUs;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { BaseAuthPage } from "@/Components/v2/design-elements";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
const LoginPage = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<BaseAuthPage
|
||||
title={t("pages.auth.login.title")}
|
||||
subtitle={t("pages.auth.login.subtitle")}
|
||||
></BaseAuthPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
@@ -0,0 +1,53 @@
|
||||
import Stack, { type StackProps } from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Logo from "@/assets/icons/checkmate-icon.svg?react";
|
||||
import { LanguageSelector, SwitchTheme } from "@/Components/v2/inputs";
|
||||
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface HeaderAuthControlsProps extends StackProps {
|
||||
hideLogo?: boolean;
|
||||
}
|
||||
|
||||
export const HeaderAuthControls = ({
|
||||
hideLogo = false,
|
||||
...stackProps
|
||||
}: HeaderAuthControlsProps) => {
|
||||
// Hooks
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
width={"100%"}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
{...stackProps}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
{!hideLogo && (
|
||||
<>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>{t("common.appName")}</Typography>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={theme.spacing(2)}
|
||||
alignItems="center"
|
||||
>
|
||||
<LanguageSelector />
|
||||
<SwitchTheme />
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import { Navigate, Route, Routes as LibRoutes } from "react-router";
|
||||
import HomeLayout from "@/Components/v1/Layouts/HomeLayout";
|
||||
import NotFound from "../Pages/NotFound/index.jsx";
|
||||
// Auth
|
||||
import AuthLogin from "../Pages/Auth/Login/index.jsx";
|
||||
import AuthLogin from "../Pages/Auth/Login";
|
||||
import AuthRegister from "../Pages/Auth/Register/index.jsx";
|
||||
import AuthForgotPassword from "../Pages/Auth/ForgotPassword.jsx";
|
||||
import AuthCheckEmail from "../Pages/Auth/CheckEmail.jsx";
|
||||
@@ -360,7 +360,13 @@ const Routes = () => {
|
||||
|
||||
<Route
|
||||
path="/login"
|
||||
element={<AuthLogin />}
|
||||
element={
|
||||
<>
|
||||
<ThemeProvider theme={v2theme}>
|
||||
<AuthLogin />
|
||||
</ThemeProvider>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
|
||||
@@ -498,6 +498,26 @@
|
||||
"now": "Now",
|
||||
"os": "OS",
|
||||
"pages": {
|
||||
"auth": {
|
||||
"common": {
|
||||
"form": {
|
||||
"option": {
|
||||
"email": {
|
||||
"label": "Email",
|
||||
"placeholder": "me@example.com"
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"placeholder": "********"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"title": "Welcome back to Checkmate!",
|
||||
"subtitle": "Log in to continue"
|
||||
}
|
||||
},
|
||||
"checks": {
|
||||
"selects": {
|
||||
"monitor": {
|
||||
|
||||
Reference in New Issue
Block a user