From 3c01fed439be41a66807ce810c5f035ecea3d14b Mon Sep 17 00:00:00 2001 From: Type-Style Date: Wed, 3 Jul 2024 00:34:05 +0200 Subject: [PATCH] [Task] #77, introduced linearBuffer Bar for login --- httpdocs/css/base.css | 12 ++-- src/client/components/LinearBuffer.tsx | 41 +++++++++++++ src/client/css/start.css | 79 ++++++++++++++++++-------- src/client/pages/Login.tsx | 24 ++++++-- src/client/pages/Start.tsx | 31 +++++++--- 5 files changed, 143 insertions(+), 44 deletions(-) create mode 100644 src/client/components/LinearBuffer.tsx diff --git a/httpdocs/css/base.css b/httpdocs/css/base.css index d1cab3a..1b8335b 100644 --- a/httpdocs/css/base.css +++ b/httpdocs/css/base.css @@ -377,12 +377,12 @@ Neutral: #131211 /* --mui-palette-FilledInput-bg:rgba(0, 0, 0, 0.06); --mui-palette-FilledInput-hoverBg:rgba(0, 0, 0, 0.09); --mui-palette-FilledInput-disabledBg:rgba(0, 0, 0, 0.12); */ - --mui-palette-LinearProgress-primaryBg: color-mix(in oklch, var(--mui-palette-primary-main) 85%, transparent); - --mui-palette-LinearProgress-secondaryBg: color-mix(in oklch, var(--mui-palette-secondary-main) 85%, transparent); - --mui-palette-LinearProgress-errorBg: color-mix(in oklch, var(--mui-error-primary-main) 85%, transparent); - --mui-palette-LinearProgress-infoBg: color-mix(in oklch, var(--mui-palette-info-main) 85%, transparent); - --mui-palette-LinearProgress-successBg: color-mix(in oklch, var(--mui-palette-success-main) 85%, transparent); - --mui-palette-LinearProgress-warningBg: color-mix(in oklch, var(--mui-palette-warning-main) 85%, transparent); + --mui-palette-LinearProgress-primaryBg: color-mix(in oklch, var(--mui-palette-primary-main) 50%, transparent); + --mui-palette-LinearProgress-secondaryBg: color-mix(in oklch, var(--mui-palette-secondary-main) 50%, transparent); + --mui-palette-LinearProgress-errorBg: color-mix(in oklch, var(--mui-error-primary-main) 50%, transparent); + --mui-palette-LinearProgress-infoBg: color-mix(in oklch, var(--mui-palette-info-main) 50%, transparent); + --mui-palette-LinearProgress-successBg: color-mix(in oklch, var(--mui-palette-success-main) 50%, transparent); + --mui-palette-LinearProgress-warningBg: color-mix(in oklch, var(--mui-palette-warning-main) 50%, transparent); --mui-palette-Skeleton-bg: rgba(var(--mui-palette-text-primaryChannel) / 0.11); --mui-palette-Slider-primaryTrack: var(--mui-palette-primary-main); --mui-palette-Slider-secondaryTrack: var(--mui-palette-secondary-main); diff --git a/src/client/components/LinearBuffer.tsx b/src/client/components/LinearBuffer.tsx new file mode 100644 index 0000000..59e5316 --- /dev/null +++ b/src/client/components/LinearBuffer.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import LinearProgress from '@mui/material/LinearProgress'; + +export default function LinearBuffer({ msStart, msFinish }: { msStart: number, msFinish: number }) { + const [progress, setProgress] = React.useState(0); + const [buffer, setBuffer] = React.useState(10); + + const progressRef = React.useRef(() => { }); + React.useEffect(() => { + progressRef.current = () => { + const duration = msFinish - msStart; // duration based on input props + const secondPhase = duration == 1000; + const date = new Date(); + const now = date.getTime(); + + const bufferValue = secondPhase ? 100 : 90; + const progressCalcValue = ((now - msStart) / duration) * 100; + const progressValue = secondPhase ? 100 : Math.min(progressCalcValue, bufferValue); + + setProgress(progressValue); + setBuffer(bufferValue); + }; + }); + + React.useEffect(() => { + const timer = setInterval(() => { + progressRef.current(); + }, 300); + + return () => { + clearInterval(timer); + }; + }, []); + + return ( + + + + ); +} \ No newline at end of file diff --git a/src/client/css/start.css b/src/client/css/start.css index 8198196..f3b10ff 100644 --- a/src/client/css/start.css +++ b/src/client/css/start.css @@ -7,49 +7,80 @@ .start { /* theming */ --text: color-mix(in oklch, var(--neutral) 50%, black); + [data-mui-color-scheme="dark"] & { --text: var(--main); } + color: var(--text); /* grid layout */ height: 100%; display: grid; - grid-template-columns: 1fr 40vmin; + grid-template-columns: 1fr 40vmin; grid-template-rows: minmax(3em, auto) 1fr 1fr 1fr minmax(3em, auto); -} -.grid-item { + .grid-item { - &.info { - + &.info { + display: flex; + width: 100%; + justify-content: space-between; + padding: 0.7em 2em; - } + } - &.map { - grid-column: 1; - grid-row: 2 / span 3; - background-color: darkkhaki; - } + &.map { + grid-column: 1; + grid-row: 2 / span 3; + background-color: darkkhaki; + } - &.status { - grid-column: 2; - grid-row: 1 / span 2; - background-color: gold; - } + &.status { + grid-column: 2; + grid-row: 1 / span 2; + background-color: gold; + } + + &.image { + grid-column: 2; + background-color: moccasin; + } + + &.image+.image { + background-color: lightgoldenrodyellow; + } - &.image { - grid-column: 2; - background-color: moccasin; + &.subinfo { + grid-column: 1 / -1; + background-color: peachpuff; + } } - &.image+.image { - background-color: lightgoldenrodyellow; + .error { + display: inline-flex; + flex-wrap: wrap; + align-content: center; + justify-content: center; + font-size: 1.3em; + + .statusCode { + width: 100%; + text-align: center; + } } - &.subinfo { - grid-column: 1 / -1; - background-color: peachpuff; + .loginButton { + color: var(--bg); + margin-left: auto; + cursor: pointer; + + &.loginButton--loggedIn { + svg { + position: relative; + top: -0.1em; right: 0.1em; + } + } } } \ No newline at end of file diff --git a/src/client/pages/Login.tsx b/src/client/pages/Login.tsx index cf77510..a6da38e 100644 --- a/src/client/pages/Login.tsx +++ b/src/client/pages/Login.tsx @@ -7,8 +7,11 @@ import axios from 'axios'; import qs from 'qs'; import { LoginContext } from '../components/App'; import { useNavigate } from 'react-router-dom'; +import LinearBuffer from '../components/LinearBuffer'; function Login() { + const [finish, setFinish] = useState(1); + const [start, setStart] = useState(1); const navigate = useNavigate(); const [isLoggedIn, setLogin] = useContext(LoginContext); const [formInfo, updateFormInfo] = useState({ @@ -47,6 +50,11 @@ function Login() { async function submit(e) { e.preventDefault(); + const date = new Date(); + setStart(date.getTime()); + const milliseconds = 9 * 1000; // Estimated bcrypt Time + setFinish(new Date(date.getTime() + milliseconds).getTime()); + setLoading(true); setMessageObj({ isError: null, status: null, message: null }); @@ -63,8 +71,8 @@ function Login() { }) updateFormInfo({ ...formInfo, token: token.data }); } catch (error) { - setMessageObj({ isError: true, status: error.response.data.status || error.response.status, message: error.response.data.message || error.message }) console.log(error); + setMessageObj({ isError: true, status: error.response.data.status || error.response.status, message: error.response.data.message || error.message }) } if (!token) { setLoading(false); return; } // skip when the first request has an error @@ -81,14 +89,19 @@ function Login() { const token = response.data.token; sessionStorage.setItem("jwt", token); setLogin(true); - setTimeout(() => { navigate("/") }, 300); + setMessageObj({ isError: false, status: , message: "Success!" }) + // update linearBar for delay until redirect + const date = new Date(); + setStart(date.getTime()); + setFinish(new Date(date.getTime() + 1000).getTime()); + + // redirect back to main page + setTimeout(() => { setLoading(false); navigate("/") }, 1000); - setMessageObj({ isError: false, status: , message: "Success!" }) } catch (error) { - setMessageObj({ isError: true, status: error.response.data.status || error.response.status, message: error.response.data.message || error.message }) console.log(error); - } finally { + setMessageObj({ isError: true, status: error.response.data.status || error.response.status, message: error.response.data.message || error.message }) setLoading(false); // Reset loading after request is complete } } @@ -180,6 +193,7 @@ function Login() { Login + {isLoading && } diff --git a/src/client/pages/Start.tsx b/src/client/pages/Start.tsx index 996fa5a..cf9812f 100644 --- a/src/client/pages/Start.tsx +++ b/src/client/pages/Start.tsx @@ -5,27 +5,35 @@ import { LoginContext } from "../components/App"; import { HighlightOff, Check } from '@mui/icons-material'; import { Button } from '@mui/material'; - function Start() { - const [isLoggedIn] = useContext(LoginContext); + const [isLoggedIn, setLogin] = useContext(LoginContext); const [entries, setEntries] = useState([]); + const [errorObj, setMessageObj] = React.useState({ isError: null, status: null, message: null }); + useEffect(() => { const token = sessionStorage.getItem("jwt"); let response; const getData = async () => { + if (!token) { + setLogin(false); + setMessageObj({ isError: true, status: "403", message: "No token / logged out" }) + return false; + } + try { response = await axios({ method: 'get', url: "/read?index=0", headers: { - 'Authorization': `Bearer ${token}`, + 'Authorization': `Bearer ${token}` } }); setEntries(response.data.entries); + setMessageObj({ isError: null, status: null, message: null }); } catch (error) { - console.log(error) + setMessageObj({ isError: true, status: error.response.data.status || error.response.status, message: error.response.data.message || error.message }); } }; @@ -41,20 +49,25 @@ function Start() { return (
+ {errorObj.isError && +
+ {errorObj.status} {errorObj.message} +
+ } +
- - {isLoggedIn ? "yes" : "no"} info: {JSON.stringify(entries)}
map
status
image1