forked from VinokurovVE/tests
Layout, Pages, Dashboard, MUI
This commit is contained in:
2
frontend_reactjs/.env.example
Normal file
2
frontend_reactjs/.env.example
Normal file
@ -0,0 +1,2 @@
|
||||
VITE_API_URL=
|
||||
VITE_API_AUTH_URL=
|
@ -1,30 +1,18 @@
|
||||
# React + TypeScript + Vite
|
||||
# Experimental Frontend
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
## Структура проекта
|
||||
- `src/assets/` - Статические ассеты
|
||||
- `src/components/` - Компоненты
|
||||
- `src/constants/` - Константы
|
||||
- `src/layouts/` - Макеты для разных частей, пока есть MainLayout, используемый всеми роутами
|
||||
- `src/pages/` - Страницы
|
||||
- `src/services/` - сервисы / API
|
||||
|
||||
Currently, two official plugins are available:
|
||||
## UI
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
В основном, используется Material UI https://mui.com/material-ui
|
||||
Для кастомных компонентов следует создать директорию в `src/components/НазваниеКомпонента` со стилями, если необходимо
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
## Env vars
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default {
|
||||
// other rules...
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
||||
`.env.example` должен описывать используемые переменные, в работе же используется `.env.local` или `.env`
|
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
4721
frontend_reactjs/package-lock.json
generated
4721
frontend_reactjs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,13 +10,20 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"-": "^0.0.1",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@mui/icons-material": "^5.15.20",
|
||||
"@mui/material": "^5.15.20",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"axios": "^1.7.2",
|
||||
"postcss": "^8.4.38",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.51.5",
|
||||
"react-router-dom": "^6.23.1"
|
||||
"react-router-dom": "^6.23.1",
|
||||
"zod": "^3.23.8",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.66",
|
||||
@ -29,6 +36,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.2.0"
|
||||
"vite": "^5.2.0",
|
||||
"vite-plugin-pwa": "^0.20.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,29 @@
|
||||
import { BrowserRouter as Router, Routes,Route} from "react-router-dom"
|
||||
import { BrowserRouter as Router, Route, Routes } from "react-router-dom"
|
||||
import Main from "./pages/Main"
|
||||
import Users from "./pages/Users"
|
||||
import Roles from "./pages/Roles"
|
||||
import Main from "./pages/Main"
|
||||
import NotFound from "./pages/NotFound"
|
||||
import Navigate from "./components/Navigate"
|
||||
import DashboardLayout from "./layouts/DashboardLayout"
|
||||
import MainLayout from "./layouts/MainLayout"
|
||||
import SignIn from "./pages/auth/SignIn"
|
||||
import ApiTest from "./pages/ApiTest"
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<Navigate />
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route index path="/" element={ <Main />} />
|
||||
<Route element={<MainLayout/>}>
|
||||
<Route path="/auth/signin" element={<SignIn/>}/>
|
||||
</Route>
|
||||
|
||||
<Route element={<DashboardLayout />}>
|
||||
<Route path="/" element={<Main />} />
|
||||
<Route path="/user" element={<Users />} />
|
||||
<Route path="/role" element={<Roles />} />
|
||||
<Route path="/api-test" element={<ApiTest />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Router>
|
||||
</>
|
||||
|
@ -1,21 +0,0 @@
|
||||
export default function Navigate(){
|
||||
return(
|
||||
<>
|
||||
<nav className="bg-white border-gray-200 dark:bg-gray-900">
|
||||
<div className="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
||||
<ul className="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 rtl:space-x-reverse md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700">
|
||||
<li>
|
||||
<a href="/" className="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/user" className="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Пользователи</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/role" className="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Роли</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
import * as React from 'react';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Box from '@mui/material/Box';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import InboxIcon from '@mui/icons-material/MoveToInbox';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import MailIcon from '@mui/icons-material/Mail';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
export default function ResponsiveDrawer() {
|
||||
//const { window } = props;
|
||||
const [mobileOpen, setMobileOpen] = React.useState(false);
|
||||
const [isClosing, setIsClosing] = React.useState(false);
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
setIsClosing(true);
|
||||
setMobileOpen(false);
|
||||
};
|
||||
|
||||
const handleDrawerTransitionEnd = () => {
|
||||
setIsClosing(false);
|
||||
};
|
||||
|
||||
const handleDrawerToggle = () => {
|
||||
if (!isClosing) {
|
||||
setMobileOpen(!mobileOpen);
|
||||
}
|
||||
};
|
||||
|
||||
const drawer = (
|
||||
<div>
|
||||
<Toolbar />
|
||||
|
||||
<Divider />
|
||||
|
||||
<List>
|
||||
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
|
||||
<ListItem key={text} disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemIcon>
|
||||
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={text} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
|
||||
<Divider />
|
||||
|
||||
<List>
|
||||
{['All mail', 'Trash', 'Spam'].map((text, index) => (
|
||||
<ListItem key={text} disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemIcon>
|
||||
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={text} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<CssBaseline />
|
||||
<AppBar
|
||||
position="fixed"
|
||||
sx={{
|
||||
width: { sm: `calc(100% - ${drawerWidth}px)` },
|
||||
ml: { sm: `${drawerWidth}px` },
|
||||
}}
|
||||
>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
edge="start"
|
||||
onClick={handleDrawerToggle}
|
||||
sx={{ mr: 2, display: { sm: 'none' } }}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap component="div">
|
||||
Dashboard
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
<Box
|
||||
component="nav"
|
||||
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
|
||||
aria-label="mailbox folders"
|
||||
>
|
||||
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
|
||||
<Drawer
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onTransitionEnd={handleDrawerTransitionEnd}
|
||||
onClose={handleDrawerClose}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
sx={{
|
||||
display: { xs: 'block', sm: 'none' },
|
||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||
}}
|
||||
>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'block' },
|
||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||
}}
|
||||
open
|
||||
>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
component="main"
|
||||
sx={{ flexGrow: 1, p: 3, width: { sm: `calc(100% - ${drawerWidth}px)` } }}
|
||||
>
|
||||
<Toolbar />
|
||||
<Typography paragraph>
|
||||
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 paragraph>
|
||||
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>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
30
frontend_reactjs/src/components/navigation/NavTabs.tsx
Normal file
30
frontend_reactjs/src/components/navigation/NavTabs.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { Tab, Tabs } from "@mui/material"
|
||||
import { Link, matchPath, useLocation } from "react-router-dom"
|
||||
|
||||
function useRouteMatch(patterns: readonly string[]) {
|
||||
const { pathname } = useLocation()
|
||||
|
||||
for (let i = 0; i < patterns.length; i += 1) {
|
||||
const pattern = patterns[i]
|
||||
const possibleMatch = matchPath(pattern, pathname)
|
||||
if (possibleMatch !== null) {
|
||||
return possibleMatch
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default function NavTabs() {
|
||||
const routeMatch = useRouteMatch(['/', '/user', '/role']);
|
||||
const currentTab = routeMatch?.pattern?.path;
|
||||
|
||||
return (
|
||||
<Tabs value={currentTab}>
|
||||
<Tab label="Главная" value="/" to="/" component={Link} />
|
||||
<Tab label="Пользователи" value="/user" to="/user" component={Link} />
|
||||
<Tab label="Роли" value="/role" to="/role" component={Link} />
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
}
|
0
frontend_reactjs/src/constants/index.ts
Normal file
0
frontend_reactjs/src/constants/index.ts
Normal file
183
frontend_reactjs/src/layouts/DashboardLayout.tsx
Normal file
183
frontend_reactjs/src/layouts/DashboardLayout.tsx
Normal file
@ -0,0 +1,183 @@
|
||||
// Layout for dashboard with responsive drawer
|
||||
|
||||
import { Outlet } from "react-router-dom"
|
||||
import * as React from 'react';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Box from '@mui/material/Box';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { Api, ExitToApp, Home, People, Settings, Shield } from "@mui/icons-material";
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
export default function DashboardLayout() {
|
||||
//const { window } = props;
|
||||
const [mobileOpen, setMobileOpen] = React.useState(false);
|
||||
const [isClosing, setIsClosing] = React.useState(false);
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
setIsClosing(true);
|
||||
setMobileOpen(false);
|
||||
};
|
||||
|
||||
const handleDrawerTransitionEnd = () => {
|
||||
setIsClosing(false);
|
||||
};
|
||||
|
||||
const handleDrawerToggle = () => {
|
||||
if (!isClosing) {
|
||||
setMobileOpen(!mobileOpen);
|
||||
}
|
||||
};
|
||||
|
||||
const pages = [
|
||||
{
|
||||
label: "Главная",
|
||||
path: "/",
|
||||
icon: <Home />
|
||||
},
|
||||
{
|
||||
label: "Пользователи",
|
||||
path: "/user",
|
||||
icon: <People />
|
||||
},
|
||||
{
|
||||
label: "Роли",
|
||||
path: "/role",
|
||||
icon: <Shield />
|
||||
},
|
||||
{
|
||||
label: "API Test",
|
||||
path: "/api-test",
|
||||
icon: <Api />
|
||||
},
|
||||
]
|
||||
|
||||
const misc = [
|
||||
{
|
||||
label: "Настройки",
|
||||
path: "/settings",
|
||||
icon: <Settings />
|
||||
},
|
||||
{
|
||||
label: "Выход",
|
||||
path: "/signOut",
|
||||
icon: <ExitToApp />
|
||||
}
|
||||
]
|
||||
|
||||
const drawer = (
|
||||
<div>
|
||||
<Toolbar />
|
||||
|
||||
<Divider />
|
||||
|
||||
<List>
|
||||
{pages.map((item, index) => (
|
||||
<ListItem key={index} disablePadding>
|
||||
<ListItemButton href={item.path}>
|
||||
<ListItemIcon>
|
||||
{item.icon}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={item.label} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
|
||||
<Divider />
|
||||
|
||||
<List>
|
||||
{misc.map((item, index) => (
|
||||
<ListItem key={index} disablePadding>
|
||||
<ListItemButton href={item.path}>
|
||||
<ListItemIcon>
|
||||
{item.icon}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={item.label} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<CssBaseline />
|
||||
<AppBar
|
||||
position="fixed"
|
||||
sx={{
|
||||
width: { sm: `calc(100% - ${drawerWidth}px)` },
|
||||
ml: { sm: `${drawerWidth}px` },
|
||||
}}
|
||||
>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
edge="start"
|
||||
onClick={handleDrawerToggle}
|
||||
sx={{ mr: 2, display: { sm: 'none' } }}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap component="div">
|
||||
Dashboard
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
<Box
|
||||
component="nav"
|
||||
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
|
||||
aria-label="mailbox folders"
|
||||
>
|
||||
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
|
||||
<Drawer
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onTransitionEnd={handleDrawerTransitionEnd}
|
||||
onClose={handleDrawerClose}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
sx={{
|
||||
display: { xs: 'block', sm: 'none' },
|
||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||
}}
|
||||
>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'block' },
|
||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||
}}
|
||||
open
|
||||
>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
component="main"
|
||||
sx={{ flexGrow: 1, p: 3, width: { sm: `calc(100% - ${drawerWidth}px)` } }}
|
||||
>
|
||||
<Toolbar />
|
||||
<Outlet />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
11
frontend_reactjs/src/layouts/MainLayout.tsx
Normal file
11
frontend_reactjs/src/layouts/MainLayout.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
// Layout for fullscreen pages
|
||||
|
||||
import { Outlet } from "react-router-dom";
|
||||
|
||||
export default function MainLayout() {
|
||||
return (
|
||||
<>
|
||||
<Outlet/>
|
||||
</>
|
||||
)
|
||||
}
|
@ -2,8 +2,20 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
import { registerSW } from 'virtual:pwa-register'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
const updateSW = registerSW({
|
||||
onNeedRefresh() {
|
||||
if (confirm("New content available. Reload?")) {
|
||||
updateSW(true);
|
||||
}
|
||||
},
|
||||
onOfflineReady() {
|
||||
console.log("offline ready");
|
||||
},
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
|
23
frontend_reactjs/src/pages/ApiTest.tsx
Normal file
23
frontend_reactjs/src/pages/ApiTest.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import UserService from "../services/UserService"
|
||||
import AuthService from "../services/AuthService"
|
||||
import { Button } from "@mui/material"
|
||||
|
||||
export default function ApiTest() {
|
||||
const [temp, setTemp] = useState<any>(null)
|
||||
|
||||
const hello = async () => {
|
||||
await AuthService.hello().then(response => {
|
||||
setTemp(response)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{JSON.stringify(temp)}</div>
|
||||
<Button onClick={() => hello()}>
|
||||
Hello
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
export default function Main() {
|
||||
return ( <><h1>Main page</h1></>)
|
||||
return (
|
||||
<>
|
||||
Главная
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
export default function NotFound() {
|
||||
return ( <><h1>Page not found</h1></>)
|
||||
return (
|
||||
<>
|
||||
<h1>Page not found</h1>
|
||||
</>
|
||||
)
|
||||
}
|
@ -10,9 +10,9 @@ interface IRoleCard{
|
||||
interface Props {
|
||||
showModal: boolean;
|
||||
}
|
||||
function Users() {
|
||||
function Roles() {
|
||||
const [showModal, setShowModal] = useState<Props>({ showModal: false });
|
||||
const cards = useDataFetching<IRoleCard[]>("http://localhost:8000/auth/role/",[])
|
||||
const cards = useDataFetching<IRoleCard[]>(`${import.meta.env.VITE_API_URL}/auth/role/`, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -23,4 +23,4 @@ function Users() {
|
||||
)
|
||||
}
|
||||
|
||||
export default Users
|
||||
export default Roles
|
@ -7,7 +7,7 @@ interface ICard{
|
||||
}
|
||||
|
||||
function Users() {
|
||||
const cards= useDataFetching<ICard[]>("http://localhost:8000/auth/user/",[])
|
||||
const cards = useDataFetching<ICard[]>(`${import.meta.env.VITE_API_URL}/auth/user/`, [])
|
||||
return (
|
||||
<div>
|
||||
{cards.map((card, index) => <Card key={index} {...card} />)}
|
||||
|
9
frontend_reactjs/src/pages/auth/SignIn.tsx
Normal file
9
frontend_reactjs/src/pages/auth/SignIn.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const SignIn = () => {
|
||||
return (
|
||||
<div>SignIn</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignIn
|
0
frontend_reactjs/src/pages/auth/SignUp.tsx
Normal file
0
frontend_reactjs/src/pages/auth/SignUp.tsx
Normal file
7
frontend_reactjs/src/services/AuthService.ts
Normal file
7
frontend_reactjs/src/services/AuthService.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import axios from "axios";
|
||||
|
||||
export default class AuthService {
|
||||
static async hello() {
|
||||
return await axios.get(`${import.meta.env.VITE_API_AUTH_URL}/hello`)
|
||||
}
|
||||
}
|
29
frontend_reactjs/src/services/UserService.ts
Normal file
29
frontend_reactjs/src/services/UserService.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// Data mockup
|
||||
let users =
|
||||
[
|
||||
{
|
||||
"email": "string",
|
||||
"login": "string",
|
||||
"phone": "string",
|
||||
"name": "string",
|
||||
"surname": "string",
|
||||
"is_active": true,
|
||||
"id": 0,
|
||||
"role_id": 2
|
||||
}
|
||||
]
|
||||
|
||||
export default class UserService {
|
||||
static async getUsers() {
|
||||
new Promise((resolve, reject) => {
|
||||
if (!users) {
|
||||
return setTimeout(
|
||||
() => reject(new Error('Users not found')),
|
||||
250
|
||||
)
|
||||
}
|
||||
|
||||
setTimeout(() => resolve(users), 250)
|
||||
})
|
||||
}
|
||||
}
|
0
frontend_reactjs/src/store/auth-store.ts
Normal file
0
frontend_reactjs/src/store/auth-store.ts
Normal file
@ -1,5 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"vite-plugin-pwa/client"
|
||||
],
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
|
@ -1,7 +1,78 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [
|
||||
react(),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
workbox: {
|
||||
globPatterns: ["**/*"],
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: ({ request }) => request.mode === 'navigate',
|
||||
handler: 'NetworkFirst',
|
||||
options: {
|
||||
cacheName: 'html-cache',
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /\.(?:js|css)$/,
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'static-resources',
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'image-cache',
|
||||
expiration: {
|
||||
maxEntries: 50,
|
||||
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
includeAssets: [
|
||||
"**/*",
|
||||
],
|
||||
manifest: {
|
||||
"theme_color": "#f69435",
|
||||
"background_color": "#f69435",
|
||||
"display": "standalone",
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"short_name": "Vite PWA",
|
||||
"description": "Vite PWA Boilerplate",
|
||||
"name": "Vite PWA Boilerplate",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icon-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user