diff --git a/src/App.css b/src/App.css
deleted file mode 100644
index 74b5e05..0000000
--- a/src/App.css
+++ /dev/null
@@ -1,38 +0,0 @@
-.App {
- text-align: center;
-}
-
-.App-logo {
- height: 40vmin;
- pointer-events: none;
-}
-
-@media (prefers-reduced-motion: no-preference) {
- .App-logo {
- animation: App-logo-spin infinite 20s linear;
- }
-}
-
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
-}
-
-.App-link {
- color: #61dafb;
-}
-
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/src/App.js b/src/App.js
index 3784575..08f8ea9 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,23 +1,62 @@
-import logo from './logo.svg';
-import './App.css';
+import React, { useEffect } from 'react';
+import { BrowserRouter, Route, Routes} from 'react-router-dom';
+
+import './css/App.css';
+import Home from './pages/Home';
+import ErrorBoundary from './util/ErrorBoundary';
+import Sidebar, { SidebarMenu } from './components/Sidebar';
+import UserControls from './components/UserControls';
+import { useLoginContext } from './structures/UserContext';
+import Login from './pages/Login';
+import { fetchUser } from './util/Util';
function App() {
+
+ const [user] = useLoginContext();
+
+ useEffect(() => {
+ fetchUser();
+ }, []);
+
+ const menuItems = [
+ { to: '/', label: 'Home' },
+ { to: '/users', label: 'Users' },
+ { to: '/admin', label: 'Admin' }
+];
+
return (
-
-
-
-
- Edit src/App.js
and save to reload.
-
-
- Learn React
-
+
+
+
+
+ {user ?
+
+
+
+
+
+ SIDEBAR
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+ :
}
+
);
}
diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js
new file mode 100644
index 0000000..b08e231
--- /dev/null
+++ b/src/components/Sidebar.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import '../css/components/Sidebar.css';
+import NavLink from '../structures/NavLink';
+
+const Sidebar = ({children}) => {
+
+ return
+ {children}
+
;
+
+};
+
+// Nav menu
+const SidebarMenu = ({menuItems, children}) => {
+
+ let key = 0;
+ return
+ {menuItems.map(link => {
+ const { label, ...rest } = link;
+ return { label};
+ })}
+
+ {children}
+
;
+
+};
+
+export {SidebarMenu, Sidebar};
+export default Sidebar;
\ No newline at end of file
diff --git a/src/components/UserControls.js b/src/components/UserControls.js
new file mode 100644
index 0000000..c39b62c
--- /dev/null
+++ b/src/components/UserControls.js
@@ -0,0 +1,18 @@
+import React from "react";
+import '../css/components/UserControls.css';
+import { useLoginContext } from "../structures/UserContext";
+
+const UserControls = () => {
+
+ const [user] = useLoginContext();
+ console.log(user);
+
+ if (!user) return;
+
+ return
+ Hello {user.displayName}
+
;
+
+};
+
+export default UserControls;
\ No newline at end of file
diff --git a/src/css/App.css b/src/css/App.css
new file mode 100644
index 0000000..838ced4
--- /dev/null
+++ b/src/css/App.css
@@ -0,0 +1,26 @@
+.app {
+ width: 100vw;
+ height: 100vh;
+ background-color: var(--background-primary);
+}
+
+header {
+ display: flex;
+ flex-direction: row;
+ text-align: center;
+ height: var(--header-height);
+ background-color: var(--background-tertiary);
+}
+
+.background {
+ display: flex;
+ height: 100%;
+ width: 100%;
+}
+
+.main-content {
+ width: 100%;
+ height: 91%;
+ margin: 20px;
+ background-color: var(--background-secondary);
+}
\ No newline at end of file
diff --git a/src/css/components/Sidebar.css b/src/css/components/Sidebar.css
new file mode 100644
index 0000000..0f1aa48
--- /dev/null
+++ b/src/css/components/Sidebar.css
@@ -0,0 +1,25 @@
+.sidebar {
+ min-width: 200px;
+ height: 100%;
+ background-color: var(--background-secondary);
+}
+
+.sidebar-menu {
+ display: flex;
+ flex-direction: column;
+}
+
+.sidebar-menu-item {
+ margin: 5px;
+ margin-right: 10px;
+ padding: 10px;
+ background-color: var(--dark-blue);
+ color: var(--text-primary);
+ border-radius: 6px;
+ text-decoration: none;
+ font-weight: bold;
+}
+
+.active {
+ background-color: var(--light-blue);
+}
\ No newline at end of file
diff --git a/src/css/components/UserControls.css b/src/css/components/UserControls.css
new file mode 100644
index 0000000..b7f5c2f
--- /dev/null
+++ b/src/css/components/UserControls.css
@@ -0,0 +1,3 @@
+.user-controls {
+
+}
\ No newline at end of file
diff --git a/src/css/index.css b/src/css/index.css
new file mode 100644
index 0000000..c3aa700
--- /dev/null
+++ b/src/css/index.css
@@ -0,0 +1,48 @@
+:root {
+ --background-primary: #1e1e1e;
+ --background-secondary: #323232;
+ --background-tertiary: #272727;
+ --lighter-blue: rgb(129, 196, 250);
+ --light-blue:rgb(88, 164, 226);
+ --dark-blue:rgb(27, 76, 117);
+ --darker-blue: rgb(25, 54, 78);
+ --text-primary: #ffffff;
+ --header-height: 40px
+}
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ color: rgb(223, 223, 223);
+ overflow: hidden;
+ text-align: center;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
+* {
+ outline: 1px dashed red;
+}
+
+button {
+ cursor: pointer;
+ background-color: var(--dark-blue);
+ border: 0;
+ border-radius: 10px;
+ padding: 5px;
+ padding-left: 10px;
+ padding-right: 10px;
+ font-weight: bold;
+ color: var(--lighter-blue)
+}
+
+button:hover {
+ background-color: var(--darker-blue);
+}
\ No newline at end of file
diff --git a/src/css/pages/Home.css b/src/css/pages/Home.css
new file mode 100644
index 0000000..e1f83fa
--- /dev/null
+++ b/src/css/pages/Home.css
@@ -0,0 +1,6 @@
+.home {
+ /* width: 100%; */
+ /* height: 100%; */
+ margin: 10px;
+ background-color: var(--background-secondary);
+}
\ No newline at end of file
diff --git a/src/css/pages/Login.css b/src/css/pages/Login.css
new file mode 100644
index 0000000..d421280
--- /dev/null
+++ b/src/css/pages/Login.css
@@ -0,0 +1,18 @@
+.login {
+ background-color: var(--background-secondary);
+ width: 20vw;
+ border-radius: 25px;
+ margin: auto;
+}
+
+.login > div > * {
+ margin: 5px;
+}
+
+.login > * {
+ margin: 25px;
+}
+
+.third-party-login:hover {
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index ec2585e..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,13 +0,0 @@
-body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
- sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
- monospace;
-}
diff --git a/src/index.js b/src/index.js
index d563c0f..df85416 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,17 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
-import './index.css';
+import './css/index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
+import { UserContext } from './structures/UserContext';
const root = ReactDOM.createRoot(document.getElementById('root'));
-root.render(
-
+root.render(
+
-
-);
+
+);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();
+reportWebVitals(console.log);
diff --git a/src/logo.svg b/src/logo.svg
deleted file mode 100644
index 9dfc1c0..0000000
--- a/src/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/pages/Home.js b/src/pages/Home.js
new file mode 100644
index 0000000..46499fc
--- /dev/null
+++ b/src/pages/Home.js
@@ -0,0 +1,12 @@
+import React from "react";
+import '../css/pages/Home.css';
+
+const Home = () => {
+
+ return
+ HOME
+
;
+
+};
+
+export default Home;
\ No newline at end of file
diff --git a/src/pages/Login.js b/src/pages/Login.js
new file mode 100644
index 0000000..dcbaf2c
--- /dev/null
+++ b/src/pages/Login.js
@@ -0,0 +1,50 @@
+import React from "react";
+import '../css/pages/Login.css';
+import { post } from "../util/Util";
+
+const loginMethods = [
+{
+ name: 'Discord',
+ img: 'https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a69f118df70ad7828d4_icon_clyde_blurple_RGB.svg'
+}
+];
+
+const Login = () => {
+
+ const loginClick = async () => {
+ const username = document.getElementById('username').value;
+ const password = document.getElementById('password').value;
+ if (!username.length || !password.length) return;
+
+ await post('/api/login', {username, password});
+
+ };
+
+ return
+
+
+
Log in
+
+
+
+
+
+
+
+
Alternate login methods
+ {loginMethods.map(method =>
{ window.location.pathname = `/api/login/${method.name}`; }}
+ />)}
+
+
+
+
;
+
+};
+
+export default Login;
\ No newline at end of file
diff --git a/src/setupProxy.js b/src/setupProxy.js
new file mode 100644
index 0000000..45365ed
--- /dev/null
+++ b/src/setupProxy.js
@@ -0,0 +1,11 @@
+const { createProxyMiddleware } = require('http-proxy-middleware');
+
+module.exports = (app) => {
+ app.use(
+ '/api',
+ createProxyMiddleware({
+ target: 'http://localhost:4000',
+ changeOrigin: true
+ })
+ );
+};
\ No newline at end of file
diff --git a/src/structures/NavLink.js b/src/structures/NavLink.js
new file mode 100644
index 0000000..83fdaf2
--- /dev/null
+++ b/src/structures/NavLink.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import { NavLink as BaseNavLink } from 'react-router-dom';
+
+// eslint-disable-next-line react/display-name
+const NavLink = React.forwardRef(({ activeClassName, activeStyle, ...props }, ref) => {
+ return [props.className, isActive ? activeClassName : null].filter(Boolean).join(' ')}
+ style={({ isActive }) => ({
+ ...props.style,
+ ...isActive ? activeStyle : null
+ })}>
+ {props?.children}
+ ;
+
+});
+
+export default NavLink;
\ No newline at end of file
diff --git a/src/structures/PrivateRoute.js b/src/structures/PrivateRoute.js
new file mode 100644
index 0000000..177d44b
--- /dev/null
+++ b/src/structures/PrivateRoute.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import { Navigate, useLocation } from "react-router-dom";
+import { getUser } from "../util/Util";
+
+const PrivateRoute = ({ children }) => {
+ const user = getUser();
+ const location = useLocation();
+ if (!user) return ;
+ return children;
+};
+
+export default PrivateRoute;
\ No newline at end of file
diff --git a/src/structures/UserContext.js b/src/structures/UserContext.js
new file mode 100644
index 0000000..2a41ef6
--- /dev/null
+++ b/src/structures/UserContext.js
@@ -0,0 +1,28 @@
+import React, { useContext, useState } from 'react';
+import { getUser } from '../util/Util';
+
+const LoginContext = React.createContext();
+const LoginUpdateContext = React.createContext();
+
+// Hook
+export const useLoginContext = () => {
+ return [useContext(LoginContext), useContext(LoginUpdateContext)];
+};
+
+// Component
+export const UserContext = ({ children }) => {
+
+ const [user, setLoginState] = useState(getUser());
+ const updateLoginState = () => {
+ setLoginState(getUser());
+ };
+
+ return (
+
+
+ {children}
+
+
+ );
+
+};
\ No newline at end of file
diff --git a/src/util/ErrorBoundary.js b/src/util/ErrorBoundary.js
new file mode 100644
index 0000000..c848ffe
--- /dev/null
+++ b/src/util/ErrorBoundary.js
@@ -0,0 +1,22 @@
+import React from "react";
+
+class ErrorBoundary extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = { error: null };
+ }
+
+ componentDidCatch(error, errorInfo) {
+ this.sate.error = true;
+ console.log(error, errorInfo);
+ }
+
+ render() {
+ if (this.state.error) return Something went wrong :(
;
+ return this.props.children;
+ }
+
+}
+
+export default ErrorBoundary;
\ No newline at end of file
diff --git a/src/util/Util.js b/src/util/Util.js
new file mode 100644
index 0000000..72ad7f1
--- /dev/null
+++ b/src/util/Util.js
@@ -0,0 +1,38 @@
+export const getUser = () => {
+ const data = sessionStorage.getItem('user');
+ if (data) return JSON.parse(data);
+ return null;
+};
+
+export const getToken = () => {
+ return getUser()?.token || null;
+};
+
+export const clearSession = () => {
+ sessionStorage.removeItem('user');
+};
+
+export const setSession = (user) => {
+ sessionStorage.setItem('user', JSON.stringify(user));
+};
+
+export const fetchUser = async () => {
+ const result = await fetch('/api/user');
+ if (result.status === 200) {
+ const data = await result.json();
+ setSession(data);
+ return data;
+ }
+};
+
+export const post = (url, body) => {
+ return fetch(url, {
+ method: 'post',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(body)
+ });
+
+
+};
\ No newline at end of file