Skip to content

Commit 31b09c3

Browse files
authored
Merge pull request #3251 from bluewave-labs/feat/v2-auth-login
feat: v2 auth login
2 parents 1c390a8 + 8f2072e commit 31b09c3

File tree

20 files changed

+465
-236
lines changed

20 files changed

+465
-236
lines changed

client/src/Components/v2/design-elements/BasePage.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import Logo from "@/assets/icons/checkmate-icon.svg?react";
12
import Stack from "@mui/material/Stack";
3+
import Box from "@mui/material/Box";
24
import Alert from "@mui/material/Alert";
35
import Link from "@mui/material/Link";
46
import {
@@ -9,6 +11,7 @@ import {
911
} from "@/Components/v2/design-elements/Fallback";
1012
import { Breadcrumb } from "@/Components/v2/design-elements/Breadcrumb";
1113
import CircularProgress from "@mui/material/CircularProgress";
14+
import { HeaderAuthControls } from "@/Pages/Auth/components/HeaderAuthControls";
1215

1316
import type { StackProps } from "@mui/material/Stack";
1417
import { useTheme } from "@mui/material/styles";
@@ -201,3 +204,61 @@ export const MonitorBasePageWithStates = ({
201204
</BasePage>
202205
);
203206
};
207+
208+
interface BaseAuthPageProps extends React.PropsWithChildren, StackProps {
209+
title: string;
210+
subtitle: string;
211+
}
212+
213+
export const BaseAuthPage = ({
214+
title,
215+
subtitle,
216+
children,
217+
...props
218+
}: BaseAuthPageProps) => {
219+
const theme = useTheme();
220+
return (
221+
<BasePage
222+
breadcrumbOverride={[]}
223+
gap={theme.spacing(8)}
224+
alignItems={"center"}
225+
justifyContent={"center"}
226+
minHeight="100vh"
227+
position={"relative"}
228+
{...props}
229+
>
230+
<HeaderAuthControls
231+
hideLogo
232+
py={theme.spacing(4)}
233+
position={"absolute"}
234+
top={0}
235+
left={0}
236+
/>
237+
<Box width={{ xs: 60, sm: 70, md: 80 }}>
238+
<Logo
239+
width={"100%"}
240+
height={"100%"}
241+
/>
242+
</Box>
243+
<Stack alignItems={"center"}>
244+
<Typography
245+
variant="h1"
246+
mb={theme.spacing(2)}
247+
>
248+
{title}
249+
</Typography>
250+
<Typography variant="h1">{subtitle}</Typography>
251+
</Stack>
252+
<Stack
253+
gap={theme.spacing(8)}
254+
width={{
255+
xs: "80%",
256+
md: "25%",
257+
lg: "15%",
258+
}}
259+
>
260+
{children}
261+
</Stack>
262+
</BasePage>
263+
);
264+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Typography from "@mui/material/Typography";
2+
import Stack from "@mui/material/Stack";
3+
import type { StackProps } from "@mui/material/Stack";
4+
import Link from "@mui/material/Link";
5+
import { Link as RouterLink } from "react-router-dom";
6+
7+
import { useTheme } from "@mui/material/styles";
8+
9+
interface TextLinkProps extends StackProps {
10+
text: string;
11+
linkText: string;
12+
href: string;
13+
target?: string;
14+
}
15+
16+
export const TextLink = ({
17+
text,
18+
linkText,
19+
href,
20+
target = "_self",
21+
...props
22+
}: TextLinkProps) => {
23+
const theme = useTheme();
24+
25+
return (
26+
<Stack
27+
direction="row"
28+
gap={theme.spacing(4)}
29+
{...props}
30+
>
31+
<Typography>{text}</Typography>
32+
<Link
33+
color="primary"
34+
to={href}
35+
component={RouterLink}
36+
target={target}
37+
>
38+
{linkText}
39+
</Link>
40+
</Stack>
41+
);
42+
};

client/src/Components/v2/design-elements/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ export * from "./BaseChart";
1818
export * from "./Gauge";
1919
export * from "./Tabs";
2020
export * from "./SplitBox";
21+
export * from "./TextLink";
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import Stack from "@mui/material/Stack";
2+
import Box from "@mui/material/Box";
3+
import MenuItem from "@mui/material/MenuItem";
4+
import { Select } from "@/Components/v2/inputs";
5+
import "flag-icons/css/flag-icons.min.css";
6+
7+
import { useTranslation } from "react-i18next";
8+
import { useTheme } from "@mui/material";
9+
import { useSelector } from "react-redux";
10+
import { useDispatch } from "react-redux";
11+
import { setLanguage } from "@/Features/UI/uiSlice";
12+
import type { RootState } from "@/Types/state";
13+
14+
const langMap: Record<string, string> = {
15+
cs: "cz",
16+
ja: "jp",
17+
uk: "ua",
18+
vi: "vn",
19+
};
20+
21+
export const LanguageSelector = () => {
22+
const { i18n } = useTranslation();
23+
const theme = useTheme();
24+
const { language = "en" } = useSelector((state: RootState) => state.ui);
25+
const dispatch = useDispatch();
26+
const handleChange = (event: any) => {
27+
const newLang = event.target.value;
28+
dispatch(setLanguage(newLang));
29+
};
30+
31+
const languages = Object.keys(i18n.options.resources || {});
32+
33+
return (
34+
<Select
35+
value={language}
36+
onChange={handleChange}
37+
size="small"
38+
// sx={{
39+
// minWidth: 80,
40+
// "& .MuiSelect-select": {
41+
// display: "flex",
42+
// alignItems: "center",
43+
// justifyContent: "center",
44+
// },
45+
// "& .MuiSelect-icon": {
46+
// alignSelf: "center",
47+
// },
48+
// }}
49+
>
50+
{languages.map((lang) => {
51+
let parsedLang = lang === "en" ? "gb" : lang;
52+
53+
if (parsedLang.includes("-")) {
54+
parsedLang = parsedLang.split("-")[1].toLowerCase();
55+
}
56+
57+
parsedLang = langMap[parsedLang] || parsedLang;
58+
59+
const flag = parsedLang ? `fi fi-${parsedLang}` : null;
60+
61+
return (
62+
<MenuItem
63+
key={lang}
64+
value={lang}
65+
sx={{
66+
display: "flex",
67+
justifyContent: "center",
68+
alignItems: "center",
69+
}}
70+
>
71+
<Stack
72+
direction="row"
73+
spacing={theme.spacing(2)}
74+
alignItems="center"
75+
justifyContent="center"
76+
>
77+
<Box
78+
component="span"
79+
sx={{
80+
display: "flex",
81+
alignItems: "center",
82+
}}
83+
>
84+
{flag && <span className={flag} />}
85+
</Box>
86+
<Box
87+
component="span"
88+
sx={{ textTransform: "uppercase" }}
89+
>
90+
{lang}
91+
</Box>
92+
</Stack>
93+
</MenuItem>
94+
);
95+
})}
96+
</Select>
97+
);
98+
};
99+
100+
export default LanguageSelector;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import IconButton from "@mui/material/IconButton";
2+
3+
import { Moon, Sun } from "lucide-react";
4+
5+
import { setMode } from "@/Features/UI/uiSlice.js";
6+
import { useTheme } from "@mui/material";
7+
import { useDispatch, useSelector } from "react-redux";
8+
import type { RootState } from "@/Types/state";
9+
10+
export const SwitchTheme = () => {
11+
const mode = useSelector((state: RootState) => state.ui.mode);
12+
const dispatch = useDispatch();
13+
const theme = useTheme();
14+
15+
const handleChange = () => {
16+
dispatch(setMode(mode === "light" ? "dark" : "light"));
17+
};
18+
return (
19+
<IconButton
20+
id="theme-toggle"
21+
onClick={handleChange}
22+
sx={{
23+
display: "flex",
24+
alignItems: "center",
25+
justifyContent: "center",
26+
"& svg": {
27+
transition: "stroke 0.2s ease",
28+
},
29+
"&:hover svg path, &:hover svg line, &:hover svg polyline, &:hover svg rect, &:hover svg circle":
30+
{
31+
stroke: theme.palette.primary.main,
32+
},
33+
}}
34+
>
35+
{mode === "light" ? (
36+
<Moon
37+
size={16}
38+
strokeWidth={1.5}
39+
/>
40+
) : (
41+
<Sun
42+
size={16}
43+
strokeWidth={1.5}
44+
/>
45+
)}
46+
</IconButton>
47+
);
48+
};

client/src/Components/v2/inputs/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ export * from "./ImageUpload";
1616
export * from "./ColorPicker";
1717
export { DatePickerComponent as DatePicker } from "./DatePicker";
1818
export { TimePickerComponent as TimePicker } from "./TimePicker";
19+
export * from "./LanguageSelector";
20+
export * from "./SwitchTheme";

client/src/Features/Auth/authSlice.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,13 @@ const authSlice = createSlice({
196196
state.success = true;
197197
state.msg = "Logged out successfully";
198198
},
199+
setAuthState: (state, action) => {
200+
state.isLoading = false;
201+
state.success = action.payload.success;
202+
state.msg = action.payload.msg;
203+
state.authToken = action.payload.data.token;
204+
state.user = action.payload.data.user;
205+
},
199206
},
200207
extraReducers: (builder) => {
201208
// Register thunk
@@ -249,4 +256,4 @@ const authSlice = createSlice({
249256
});
250257

251258
export default authSlice.reducer;
252-
export const { clearAuthState } = authSlice.actions;
259+
export const { clearAuthState, setAuthState } = authSlice.actions;

client/src/Hooks/useLoginForm.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useMemo } from "react";
2+
import { loginSchema, type LoginFormData } from "@/Validation/login";
3+
4+
export const useLoginForm = () => {
5+
return useMemo(() => {
6+
const defaults: LoginFormData = {
7+
email: "",
8+
password: "",
9+
};
10+
11+
return { schema: loginSchema, defaults };
12+
}, []);
13+
};

client/src/Pages/About/index.jsx

Lines changed: 0 additions & 18 deletions
This file was deleted.

client/src/Pages/Auth/Login/hooks/useLoadingSubmit.jsx

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)