diff --git a/frontend-web/.env b/frontend-web/.env
index 6214749..e6549ca 100644
--- a/frontend-web/.env
+++ b/frontend-web/.env
@@ -1 +1,5 @@
-REACT_APP_API_URL='http://localhost:4000'
\ No newline at end of file
+REACT_APP_MODE=prod
+REACT_APP_API_URL='http://localhost:4000'
+REACT_APP_ID=vanillameta
+REACT_APP_PWD=vanillameta
+REACT_APP_TOKEN=TOKEN1VB2GS3SWJYDS
\ No newline at end of file
diff --git a/frontend-web/.env.development b/frontend-web/.env.development
index 53018c8..f5823ad 100644
--- a/frontend-web/.env.development
+++ b/frontend-web/.env.development
@@ -1 +1,5 @@
-REACT_APP_API_URL='https://dev-api.vanillameta.net/v1'
\ No newline at end of file
+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
\ No newline at end of file
diff --git a/frontend-web/.env.local b/frontend-web/.env.local
index 6214749..9281d41 100644
--- a/frontend-web/.env.local
+++ b/frontend-web/.env.local
@@ -1 +1,5 @@
-REACT_APP_API_URL='http://localhost:4000'
\ No newline at end of file
+REACT_APP_MODE=local
+REACT_APP_API_URL='http://localhost:4000'
+REACT_APP_ID=vanillameta
+REACT_APP_PWD=vanillameta
+REACT_APP_TOKEN=TOKEN1VB2GS3SWJYDS
\ No newline at end of file
diff --git a/frontend-web/package.json b/frontend-web/package.json
index 05a2156..9fa89de 100644
--- a/frontend-web/package.json
+++ b/frontend-web/package.json
@@ -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",
diff --git a/frontend-web/src/App.tsx b/frontend-web/src/App.tsx
index 7451f2f..1390fe7 100644
--- a/frontend-web/src/App.tsx
+++ b/frontend-web/src/App.tsx
@@ -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 (
<>
-
-
-
+
>
);
}
diff --git a/frontend-web/src/contexts/AuthContext.tsx b/frontend-web/src/contexts/AuthContext.tsx
new file mode 100644
index 0000000..b05505c
--- /dev/null
+++ b/frontend-web/src/contexts/AuthContext.tsx
@@ -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 {children};
+};
+
+export const useAuth = () => {
+ return useContext(AuthContext);
+};
diff --git a/frontend-web/src/index.tsx b/frontend-web/src/index.tsx
index 1cc1404..18aa83d 100644
--- a/frontend-web/src/index.tsx
+++ b/frontend-web/src/index.tsx
@@ -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(
-
-
-
-
-
+
+
+
+
+
+
+
,
diff --git a/frontend-web/src/layouts/Layout.tsx b/frontend-web/src/layouts/Layout.tsx
index 1e5eb3a..d5c793b 100644
--- a/frontend-web/src/layouts/Layout.tsx
+++ b/frontend-web/src/layouts/Layout.tsx
@@ -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 || }
);
-}
+};
export default Layout;
diff --git a/frontend-web/src/pages/Login/index.tsx b/frontend-web/src/pages/Login/index.tsx
new file mode 100644
index 0000000..8bd57b9
--- /dev/null
+++ b/frontend-web/src/pages/Login/index.tsx
@@ -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 (
+
+
+ @Vanilla Meta
+ {' '}
+ {new Date().getFullYear()}
+ {'.'}
+
+ );
+}
+
+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 (
+
+
+
+ Sign in
+
+
+
+
+ } label="Remember me" />
+
+
+
+
+
+ );
+};
+
+export default Login;
diff --git a/frontend-web/src/router/ProtectedRoute.tsx b/frontend-web/src/router/ProtectedRoute.tsx
new file mode 100644
index 0000000..afad647
--- /dev/null
+++ b/frontend-web/src/router/ProtectedRoute.tsx
@@ -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 ;
+ }
+
+ return children;
+};
diff --git a/frontend-web/src/router/index.tsx b/frontend-web/src/router/index.tsx
index 400d0b9..2df5174 100644
--- a/frontend-web/src/router/index.tsx
+++ b/frontend-web/src/router/index.tsx
@@ -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 (
- } />
- } />
- } />
- }>
- } />
-
- }>
- } />
-
- } />
- } />
- } />
- }>
- } />
-
+
+
+
+ }
+ >
+ } />
+ } />
+ }>
+ } />
+
+ }>
+ } />
+
+ } />
+ } />
+ } />
+ }>
+ } />
+
- } />
- } />
- }>
- } />
-
+ } />
+ } />
+ }>
+ } />
+
- }>
- } />
-
- }>
- } />
+ }>
+ } />
+
+ }>
+ } />
+
+ } />
} />
);