Add Login
This commit is contained in:
@@ -1 +1,5 @@
|
||||
REACT_APP_API_URL='http://localhost:4000'
|
||||
REACT_APP_MODE=prod
|
||||
REACT_APP_API_URL='http://localhost:4000'
|
||||
REACT_APP_ID=vanillameta
|
||||
REACT_APP_PWD=vanillameta
|
||||
REACT_APP_TOKEN=TOKEN1VB2GS3SWJYDS
|
||||
@@ -1 +1,5 @@
|
||||
REACT_APP_API_URL='https://dev-api.vanillameta.net/v1'
|
||||
REACT_APP_MODE=dev
|
||||
REACT_APP_API_URL='https://dev-api.vanillameta.net/v1'
|
||||
REACT_APP_ID=vanillameta
|
||||
REACT_APP_PWD=vanillameta
|
||||
REACT_APP_TOKEN=TOKEN1VB2GS3SWJYDS
|
||||
@@ -1 +1,5 @@
|
||||
REACT_APP_API_URL='http://localhost:4000'
|
||||
REACT_APP_MODE=local
|
||||
REACT_APP_API_URL='http://localhost:4000'
|
||||
REACT_APP_ID=vanillameta
|
||||
REACT_APP_PWD=vanillameta
|
||||
REACT_APP_TOKEN=TOKEN1VB2GS3SWJYDS
|
||||
@@ -42,7 +42,7 @@
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
"start": "env-cmd -f .env.local craco start",
|
||||
"start:dev": "env-cmd -f .env.development craco start",
|
||||
"start:local": "env-cmd -f .env.local craco start",
|
||||
"build": "craco build",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { CssBaseline } from '@mui/material';
|
||||
import Layout from './layouts/Layout';
|
||||
import Router from './router';
|
||||
import 'tui-grid/dist/tui-grid.css';
|
||||
import Grid from 'tui-grid';
|
||||
@@ -59,9 +58,7 @@ function App() {
|
||||
return (
|
||||
<>
|
||||
<CssBaseline />
|
||||
<Layout>
|
||||
<Router />
|
||||
</Layout>
|
||||
<Router />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
34
frontend-web/src/contexts/AuthContext.tsx
Normal file
34
frontend-web/src/contexts/AuthContext.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
const AuthContext = createContext(null);
|
||||
|
||||
export const AuthProvider = ({ children }) => {
|
||||
const [token, setToken] = useState(null);
|
||||
|
||||
const handleLogin = async (id, pwd) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (id === process.env.REACT_APP_ID && pwd === process.env.REACT_APP_PWD) {
|
||||
setToken(process.env.REACT_APP_TOKEN);
|
||||
setTimeout(() => resolve(process.env.REACT_APP_TOKEN), 1000);
|
||||
} else {
|
||||
reject(new Error('User ID or password incorrect.'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
setToken(null);
|
||||
};
|
||||
|
||||
const value = {
|
||||
token,
|
||||
onLogin: handleLogin,
|
||||
onLogout: handleLogout,
|
||||
};
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
};
|
||||
|
||||
export const useAuth = () => {
|
||||
return useContext(AuthContext);
|
||||
};
|
||||
@@ -10,6 +10,7 @@ import App from './App';
|
||||
import './index.css';
|
||||
import { LayoutProvider } from '@/contexts/LayoutContext';
|
||||
import { LoadingProvider } from '@/contexts/LoadingContext';
|
||||
import { AuthProvider } from '@/contexts/AuthContext';
|
||||
|
||||
// alert optional configuration
|
||||
const options = {
|
||||
@@ -25,11 +26,13 @@ root.render(
|
||||
<LayoutProvider>
|
||||
<LoadingProvider>
|
||||
<BrowserRouter>
|
||||
<ThemeProvider theme={theme}>
|
||||
<AlertProvider template={AlertTemplate} {...options}>
|
||||
<App />
|
||||
</AlertProvider>
|
||||
</ThemeProvider>
|
||||
<AuthProvider>
|
||||
<ThemeProvider theme={theme}>
|
||||
<AlertProvider template={AlertTemplate} {...options}>
|
||||
<App />
|
||||
</AlertProvider>
|
||||
</ThemeProvider>
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</LoadingProvider>
|
||||
</LayoutProvider>,
|
||||
|
||||
@@ -4,8 +4,10 @@ import { Box, Stack } from '@mui/material';
|
||||
import Header from './Header/Header';
|
||||
import Footer from './Footer/Footer';
|
||||
import { LayoutContext } from '@/contexts/LayoutContext';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
function Layout(props) {
|
||||
const Layout = props => {
|
||||
const { children } = props;
|
||||
const headerHeight = 65;
|
||||
const footerHeight = 50;
|
||||
|
||||
@@ -33,11 +35,11 @@ function Layout(props) {
|
||||
minHeight: `calc(100% - ${footerHeight}px)`,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
{children || <Outlet />}
|
||||
</Stack>
|
||||
<Footer height={footerHeight} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
|
||||
83
frontend-web/src/pages/Login/index.tsx
Normal file
83
frontend-web/src/pages/Login/index.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Box, Stack, Button, Checkbox, Container, FormControlLabel, Link, TextField, Typography } from '@mui/material';
|
||||
import { useAlert } from 'react-alert';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { LoadingContext } from '@/contexts/LoadingContext';
|
||||
|
||||
function Copyright(props: any) {
|
||||
return (
|
||||
<Typography variant="body2" color="text.secondary" align="center" {...props}>
|
||||
<Link color="inherit" href="https://vanillabrain.com/" sx={{ textDecoration: 'none' }}>
|
||||
@Vanilla Meta
|
||||
</Link>{' '}
|
||||
{new Date().getFullYear()}
|
||||
{'.'}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
const Login = () => {
|
||||
const { onLogin } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const alert = useAlert();
|
||||
const { showLoading, hideLoading } = useContext(LoadingContext);
|
||||
const [userInfo] = useState({
|
||||
userId: process.env.REACT_APP_MODE === 'local' ? process.env.REACT_APP_ID : '',
|
||||
userPwd: process.env.REACT_APP_MODE === 'local' ? process.env.REACT_APP_PWD : '',
|
||||
});
|
||||
|
||||
const handleLogin = async event => {
|
||||
event.preventDefault();
|
||||
showLoading();
|
||||
await onLogin(event.target.userId.value, event.target.userPwd.value)
|
||||
.then(res => {
|
||||
if (res) {
|
||||
navigate('/dashboard');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert.info(`${error}`);
|
||||
console.log('error', error);
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoading();
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Container component="main" maxWidth="xs">
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography component="h1" variant="h5">
|
||||
Sign in
|
||||
</Typography>
|
||||
<Stack component="form" onSubmit={handleLogin} noValidate sx={{ mt: 1 }} spacing="20px">
|
||||
<TextField margin="normal" required fullWidth id="userId" label="ID" name="email" defaultValue={userInfo.userId} />
|
||||
<TextField
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
name="userPwd"
|
||||
defaultValue={userInfo.userPwd}
|
||||
label="Password"
|
||||
type="password"
|
||||
id="password"
|
||||
/>
|
||||
<FormControlLabel control={<Checkbox value="remember" color="primary" />} label="Remember me" />
|
||||
<Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }}>
|
||||
Sign In
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Copyright sx={{ mt: 8, mb: 4 }} />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
12
frontend-web/src/router/ProtectedRoute.tsx
Normal file
12
frontend-web/src/router/ProtectedRoute.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
|
||||
export const ProtectedRoute = ({ children }) => {
|
||||
const { token } = useAuth();
|
||||
console.log('ProtectedRoute', token);
|
||||
if (!token) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
@@ -13,38 +13,50 @@ import WidgetModify from '@/pages/Widget/WidgetModify';
|
||||
import DashboardView from '@/pages/Dashboard/DashboardView';
|
||||
import DashboardCreate from '@/pages/Dashboard/DashboardCreate';
|
||||
import DashboardModify from '@/pages/Dashboard/DashboardModify';
|
||||
import Login from '@/pages/Login';
|
||||
import { ProtectedRoute } from '@/router/ProtectedRoute';
|
||||
import Layout from '@/layouts/Layout';
|
||||
|
||||
function Router() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/dashboard" />} />
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/dashboard/:dashboardId" element={<DashboardView />} />
|
||||
<Route path="/dashboard/create" element={<DashboardCreate />}>
|
||||
<Route path=":createType" element={<DashboardCreate />} />
|
||||
</Route>
|
||||
<Route path="/dashboard/modify" element={<DashboardModify />}>
|
||||
<Route path=":dashboardId" element={<DashboardModify />} />
|
||||
</Route>
|
||||
<Route path="/widget" element={<Widget />} />
|
||||
<Route path="/widget/:widgetId" element={<WidgetView />} />
|
||||
<Route path="/widget/create" element={<WidgetCreate />} />
|
||||
<Route path="/widget/modify" element={<WidgetModify />}>
|
||||
<Route path=":widgetId" element={<WidgetModify />} />
|
||||
</Route>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Layout />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
>
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/dashboard/:dashboardId" element={<DashboardView />} />
|
||||
<Route path="/dashboard/create" element={<DashboardCreate />}>
|
||||
<Route path=":createType" element={<DashboardCreate />} />
|
||||
</Route>
|
||||
<Route path="/dashboard/modify" element={<DashboardModify />}>
|
||||
<Route path=":dashboardId" element={<DashboardModify />} />
|
||||
</Route>
|
||||
<Route path="/widget" element={<Widget />} />
|
||||
<Route path="/widget/:widgetId" element={<WidgetView />} />
|
||||
<Route path="/widget/create" element={<WidgetCreate />} />
|
||||
<Route path="/widget/modify" element={<WidgetModify />}>
|
||||
<Route path=":widgetId" element={<WidgetModify />} />
|
||||
</Route>
|
||||
|
||||
<Route path="/data" element={<Data />} />
|
||||
<Route path="/data/source/create" element={<DataSource />} />
|
||||
<Route path="/data/source/modify" element={<DataSource />}>
|
||||
<Route path=":sourceId" element={<DataSource />} />
|
||||
</Route>
|
||||
<Route path="/data" element={<Data />} />
|
||||
<Route path="/data/source/create" element={<DataSource />} />
|
||||
<Route path="/data/source/modify" element={<DataSource />}>
|
||||
<Route path=":sourceId" element={<DataSource />} />
|
||||
</Route>
|
||||
|
||||
<Route path="/data/set/create" element={<DataSet />}>
|
||||
<Route path=":sourceId" element={<DataSet />} />
|
||||
</Route>
|
||||
<Route path="/data/set/modify" element={<DataSet />}>
|
||||
<Route path=":setId" element={<DataSet />} />
|
||||
<Route path="/data/set/create" element={<DataSet />}>
|
||||
<Route path=":sourceId" element={<DataSet />} />
|
||||
</Route>
|
||||
<Route path="/data/set/modify" element={<DataSet />}>
|
||||
<Route path=":setId" element={<DataSet />} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/*" element={<Status404 />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user