init: Init React Application

This commit is contained in:
2025-05-29 13:21:33 +03:00
parent 9444939507
commit 17de7e495f
66 changed files with 10425 additions and 0 deletions

View File

@ -0,0 +1,9 @@
import { DevicesTable } from "@widgets";
export const DevicesPage = () => {
return (
<>
<DevicesTable />
</>
);
};

View File

@ -0,0 +1,119 @@
import {
TextField,
Box,
Button,
Typography,
Alert,
CircularProgress,
} from "@mui/material";
import { authStore } from "@shared";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
export const LoginPage = () => {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const { login } = authStore;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
setIsLoading(true);
try {
await login(email, password);
navigate("/sights");
toast.success("Вход в систему выполнен успешно");
} catch (err) {
setError(
err instanceof Error ? err.message : "Ошибка при входе в систему"
);
toast.error(
err instanceof Error ? err.message : "Ошибка при входе в систему"
);
} finally {
setIsLoading(false);
}
};
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
minHeight: "100vh",
gap: 3,
p: 3,
}}
>
<Typography variant="h4" component="h1" gutterBottom>
Вход в систему
</Typography>
<Box
component="form"
onSubmit={handleSubmit}
sx={{
display: "flex",
flexDirection: "column",
gap: 2,
width: "100%",
maxWidth: "400px",
}}
>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
<TextField
label="Email"
type="email"
variant="outlined"
fullWidth
required
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
disabled={isLoading}
error={!!error}
/>
<TextField
label="Пароль"
type="password"
variant="outlined"
fullWidth
required
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
error={!!error}
/>
<Button
variant="contained"
color="primary"
size="large"
type="submit"
disabled={isLoading}
sx={{
width: "100%",
height: "50px",
position: "relative",
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "10px",
}}
>
{isLoading ? <CircularProgress size={24} sx={{}} /> : "Войти"}
</Button>
</Box>
</Box>
);
};

View File

@ -0,0 +1,37 @@
import * as React from "react";
import Typography from "@mui/material/Typography";
export const MainPage: React.FC = () => {
return (
<>
<Typography sx={{ marginBottom: 2 }}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Rhoncus dolor purus
non enim praesent elementum facilisis leo vel. Risus at ultrices mi
tempus imperdiet. Semper risus in hendrerit gravida rutrum quisque non
tellus. Convallis convallis tellus id interdum velit laoreet id donec
ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl
suscipit adipiscing bibendum est ultricies integer quis. Cursus euismod
quis viverra nibh cras. Metus vulputate eu scelerisque felis imperdiet
proin fermentum leo. Mauris commodo quis imperdiet massa tincidunt. Cras
tincidunt lobortis feugiat vivamus at augue. At augue eget arcu dictum
varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt.
Lorem donec massa sapien faucibus et molestie ac.
</Typography>
<Typography sx={{ marginBottom: 2 }}>
Consequat mauris nunc congue nisi vitae suscipit. Fringilla est
ullamcorper eget nulla facilisi etiam dignissim diam. Pulvinar elementum
integer enim neque volutpat ac tincidunt. Ornare suspendisse sed nisi
lacus sed viverra tellus. Purus sit amet volutpat consequat mauris.
Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed
vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra
accumsan in. In hendrerit gravida rutrum quisque non tellus orci ac.
Pellentesque nec nam aliquam sem et tortor. Habitant morbi tristique
senectus et. Adipiscing elit duis tristique sollicitudin nibh sit.
Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra
maecenas accumsan lacus vel facilisis. Nulla posuere sollicitudin
aliquam ultrices sagittis orci a.
</Typography>
</>
);
};

View File

@ -0,0 +1,61 @@
import { Box, Tab, Tabs } from "@mui/material";
import { InformationTab, RightWidgetTab } from "@widgets";
import { LeftWidgetTab } from "@widgets";
import { useState } from "react";
function a11yProps(index: number) {
return {
id: `sight-tab-${index}`,
"aria-controls": `sight-tabpanel-${index}`,
};
}
export const SightPage = () => {
const [value, setValue] = useState(0);
const handleChange = (_: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
};
return (
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
minHeight: "100vh",
}}
>
<Box
sx={{
borderBottom: 1,
borderColor: "divider",
display: "flex",
justifyContent: "center",
}}
>
<Tabs
value={value}
onChange={handleChange}
aria-label="sight tabs"
sx={{
width: "100%",
"& .MuiTabs-flexContainer": {
justifyContent: "center",
},
}}
>
<Tab sx={{ flex: 1 }} label="Общая информация" {...a11yProps(0)} />
<Tab sx={{ flex: 1 }} label="Левый виджет" {...a11yProps(1)} />
<Tab sx={{ flex: 1 }} label="Правый виджет" {...a11yProps(2)} />
</Tabs>
</Box>
<div className="flex-1">
<InformationTab value={value} index={0} />
<LeftWidgetTab value={value} index={1} />
<RightWidgetTab value={value} index={2} />
</div>
</Box>
);
};

4
src/pages/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from "./MainPage";
export * from "./SightPage";
export * from "./LoginPage";
export * from "./DevicesPage";