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 ( -
-
- logo -

- 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 => {method.name} { 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