@@ -39,11 +65,14 @@ const SidebarMenu = ({menuItems = [], children}) => {
}
return
+
-
{elements}
{children}
+
;
};
diff --git a/src/css/App.css b/src/css/App.css
index 36aa0d1..adf4922 100644
--- a/src/css/App.css
+++ b/src/css/App.css
@@ -22,12 +22,27 @@ header {
width: 100%;
height: 100%;
background-color: var(--background-secondary);
+ padding-bottom: 1rem;
}
.page{
padding: 0 15px 0 245px;
}
+.footer{
+ padding: 15px 15px 15px 245px;
+ font-size: 14px;
+ display: flex;
+ justify-content: center;
+}
+
+@media screen and (max-width: 768px){
+ .footer{
+ padding: 15px;
+ font-size: inherit;
+ }
+}
+
.ld{
min-height: 140px;
position: relative;
diff --git a/src/css/components/Sidebar.css b/src/css/components/Sidebar.css
index d1d9cd7..27976be 100644
--- a/src/css/components/Sidebar.css
+++ b/src/css/components/Sidebar.css
@@ -3,22 +3,29 @@
position: fixed;
width: 230px;
padding: 1rem 0;
+ z-index: 1000;
+ border-radius: 0;
}
.sidebar-menu {
display: flex;
flex-direction: column;
}
+.sidebar-menu .parent-menu:first-of-type{
+ border-top: 1px solid #232323;
+}
+
.parent-menu{
display: flex;
flex-direction: column;
+ border-bottom: 1px solid #232323;
}
.sidebar-logo{
margin: auto;
- margin-top: 1rem;
- margin-bottom: 1rem;
- max-width: 170px;
+ margin-top: 2.5rem;
+ margin-bottom: 3.5rem;
+ max-height: 80px;
}
.sidebar-menu-item-carrot{
@@ -33,15 +40,16 @@
}
.sidebar-menu-item.has-children.open .sidebar-menu-item-carrot{
transform: rotate(-90deg);
- transition: transform ease-in-out 0.15s;
+ transition: transform ease-in-out 0.2s;
}
.sidebar-menu-item.has-children.active .sidebar-menu-item-carrot{
margin-right: 5px;
}
.sidebar-menu-item.has-children.open + .sidebar-menu-child-wrapper{
height: 100%;
- opacity: 1;
- transition: max-height 0.5s ease-in-out, opacity 0.25s ease-in-out;
+ /* opacity: 1; */
+ /* transition: max-height 0.5s ease-in-out, opacity 0.25s ease-in-out; */
+ transition: max-height ease-in-out 0.25s;
max-height: 600px;
}
.sidebar-menu-item.sidebar-menu-item.has-children{
@@ -58,8 +66,9 @@
z-index: 0;
overflow: hidden;
max-height: 0;
- opacity: 0;
- transition: max-height 0.25s ease-in-out, opacity 0.25s ease-in-out;
+ /* opacity: 0; */
+ /* transition: max-height 0.25s ease-in-out, opacity 0.25s ease-in-out; */
+ transition: max-height ease-in-out 0.25s;
}
.sidebar hr{
margin-left: 2rem;
@@ -67,7 +76,7 @@
}
.sidebar-menu-item {
margin: 0px 0px 0px -20px;
- padding: 14px 0px 14px calc(35px + 2rem);
+ padding: 15px 0px 15px calc(35px + 2rem);
background-color: var(--dark-blue);
color: var(--text-primary);
border-radius: 6px;
@@ -102,4 +111,60 @@
padding-left: 35px !important;
margin-top: 0px;
margin-bottom: 0px;
+}
+.hamburger{
+ display: none;
+ visibility: hidden;
+ position: absolute;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48px' height='48px' viewBox='0 0 48 48'%3E%3Ctitle%3E70 Basic icons by Xicons.co%3C/title%3E%3Cpath d='M41,14H7a2,2,0,0,1,0-4H41A2,2,0,0,1,41,14Z' fill='%23f5f5f5'/%3E%3Cpath d='M41,26H7a2,2,0,0,1,0-4H41A2,2,0,0,1,41,26Z' fill='%23f5f5f5'/%3E%3Cpath d='M41,38H7a2,2,0,0,1,0-4H41A2,2,0,0,1,41,38Z' fill='%23f5f5f5'/%3E%3C/svg%3E");
+ background-position: center;
+ width: 36px;
+ height: 36px;
+ right: 30px;
+ margin-top: calc(0.75rem + 12px);
+ margin-bottom: calc(1.75rem + 12px);
+ cursor: pointer;
+}
+.hamburger:hover{
+ opacity: 0.8;
+}
+.log-out-menu-item{
+ display: none;
+ visibility: hidden;
+}
+@media screen and (max-width: 768px){
+ .page{
+ padding-left: 15px;
+ }
+ .sidebar{
+ width: 100%;
+ max-height: 95px;
+ overflow: hidden;
+ transition: max-height ease-in-out 0.25s;
+ }
+ .sidebar.show-menu{
+ max-height: 100vh;
+ transition: max-height ease-in-out 0.25s;
+ }
+ .sidebar-logo{
+ max-height: 60px;
+ margin-top: 0.75rem;
+ margin-bottom: 1.75rem;
+ margin-left: 30px;
+ max-width: 82vw;
+ }
+ .hamburger{
+ display: block;
+ visibility: visible;
+ }
+ .main-content{
+ margin-top: 95px;
+ }
+ header{
+ display: none;
+ }
+ .log-out-menu-item{
+ display: flex;
+ visibility: visible;
+ }
}
\ No newline at end of file
diff --git a/src/css/index.css b/src/css/index.css
index 1c82b2a..765e0c4 100644
--- a/src/css/index.css
+++ b/src/css/index.css
@@ -34,7 +34,9 @@ a:hover:not(.button){
opacity: 1 !important;
color: #C4C4C4;
}
-
+a:hover{
+ cursor: pointer;
+}
details[open] summary ~ * {
animation: sweep .15s ease-in-out;
}
diff --git a/src/css/pages/Login.css b/src/css/pages/Login.css
index 6a466f3..ce679fc 100644
--- a/src/css/pages/Login.css
+++ b/src/css/pages/Login.css
@@ -25,4 +25,14 @@ input{
.registerText{
line-height: 1.6em;
padding: 18px 10px 4px 10px;
+}
+.footer.login{
+ padding: 15px !important;
+ position: absolute;
+ margin: auto;
+ width: 100%;
+ bottom: 0;
+}
+.main-content.login{
+ margin-top: 0;
}
\ No newline at end of file
diff --git a/src/pages/Home.js b/src/pages/Home.js
index ec87bf9..c1376be 100644
--- a/src/pages/Home.js
+++ b/src/pages/Home.js
@@ -35,7 +35,7 @@ const Profile = () => {
return
-
+
Profile
@@ -48,7 +48,7 @@ const Profile = () => {
-
+
Settings
ID: {user.id}
diff --git a/src/pages/Login.js b/src/pages/Login.js
index acc5199..695598f 100644
--- a/src/pages/Login.js
+++ b/src/pages/Login.js
@@ -1,8 +1,9 @@
-import React, { useState } from "react";
+import React, { useState, useRef } from "react";
import { Route, Routes, useNavigate } from "react-router";
import '../css/pages/Login.css';
import logoImg from '../img/logo.png';
import { useLoginContext } from "../structures/UserContext";
+import LoadingBar from 'react-top-loading-bar';
import { fetchUser, post } from "../util/Util";
const loginMethods = [
@@ -18,9 +19,10 @@ const CredentialFields = () => {
const [, setUser] = useLoginContext();
const [fail, setFail] = useState(false);
const navigate = useNavigate();
+ const ref = useRef(null);
const loginClick = async (event) => {
-
+ ref.current.continuousStart();
event.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
@@ -29,20 +31,23 @@ const CredentialFields = () => {
const result = await post('/api/login', { username, password });
if (![200, 302].includes(result.status)) {
+ ref.current.complete();
setFail(true);
return;
}
if (!result.data?.twoFactor) {
setUser(await fetchUser());
+ ref.current.complete();
return navigate('/home');
}
-
+ ref.current.complete();
return navigate('verify');
};
return
+
@@ -85,24 +90,34 @@ const TwoFactor = () => {
const [fail, setFail] = useState(false);
const [, setUser] = useLoginContext();
const navigate = useNavigate();
+ const ref = useRef(null);
const twoFactorClick = async () => {
const code = document.getElementById('2faCode').value;
if (!code) return;
-
+ ref.current.continuousStart();
const result = await post('/api/login/verify', { code });
if (result.status === 200) {
setUser(await fetchUser());
+ ref.current.complete();
return navigate('/home');
}
+ ref.current.complete();
setFail(true);
};
- return
- {fail ?
Invalid code
: null}
-
Verification code
-
-
+ return
+
+
+
+
+
+
+
Verification Code
+ {fail ?
Invalid code
: null}
+
+
+
;
};
diff --git a/src/pages/Register.js b/src/pages/Register.js
index 017741d..8a2e4d9 100644
--- a/src/pages/Register.js
+++ b/src/pages/Register.js
@@ -1,32 +1,41 @@
-import React, { useState } from "react";
+import React, { useState, useRef } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { post } from "../util/Util";
import '../css/pages/Register.css';
import logoImg from '../img/logo.png';
+import LoadingBar from 'react-top-loading-bar';
const Register = () => {
const [error, setError] = useState();
const [params] = useSearchParams();
const navigate = useNavigate();
+ const ref = useRef(null);
document.body.classList.add('bg-triangles');
const code = params.get('code');
const submit = async (event) => {
-
+ ref.current.continuousStart();
event.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
- if (!username.length || !password.length) return;
-
+ if (!username.length || !password.length) {
+ ref.current.complete();
+ return;
+ }
const response = await post('/api/register', { username, password, code });
- if (response.status !== 200) return setError(response.message);
+ if (response.status !== 200) {
+ ref.current.complete();
+ return setError(response.message);
+ }
+ ref.current.complete();
navigate('/login');
};
return
+
diff --git a/yarn.lock b/yarn.lock
index 5553adf..a998e3b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7496,6 +7496,11 @@ react-scripts@5.0.1:
optionalDependencies:
fsevents "^2.3.2"
+react-top-loading-bar@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/react-top-loading-bar/-/react-top-loading-bar-2.3.1.tgz#d727eb6aaa412eae52a990e5de9f33e9136ac714"
+ integrity sha512-rQk2Nm+TOBrM1C4E3e6KwT65iXyRSgBHjCkr2FNja1S51WaPulRA5nKj/xazuQ3x89wDDdGsrqkqy0RBIfd0xg==
+
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"