diff --git a/.eslintrc b/.eslintrc index 21c32ce..bb6e01b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,9 +1,9 @@ { - "extends": ["@bravo68web"], - "rules": { - "sonarjs/cognitive-complexity": "off", - "unicorn/no-array-reduce": "off", - "unicorn/filename-case": "off", - "@typescript-eslint/no-empty-interface": "off" - } -} \ No newline at end of file + "extends": ["@bravo68web"], + "rules": { + "sonarjs/cognitive-complexity": "off", + "unicorn/no-array-reduce": "off", + "unicorn/filename-case": "off", + "@typescript-eslint/no-empty-interface": "off" + } +} diff --git a/apps/web/jsconfig.json b/apps/web/jsconfig.json index 4120155..a224293 100644 --- a/apps/web/jsconfig.json +++ b/apps/web/jsconfig.json @@ -1,5 +1,5 @@ { - "compilerOptions": { - "jsx": "react-jsx" - } -} \ No newline at end of file + "compilerOptions": { + "jsx": "react-jsx" + } +} diff --git a/apps/web/src/App.css b/apps/web/src/App.css deleted file mode 100644 index d669090..0000000 --- a/apps/web/src/App.css +++ /dev/null @@ -1,440 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; - --r: 10px; /* control the radius of the circles */ - padding: calc(2 * var(--r)); - filter: grayscale(.4); - background: - radial-gradient(var(--r),#0000 98%,#fff) round - calc(-1.5 * var(--r)) calc(-1.5 * var(--r)) / calc(3 * var(--r)) calc(3 * var(--r)), - linear-gradient(#fff 0 0) no-repeat - 50% / calc(100% - 3 * var(--r)) calc(100% - 3 * var(--r)); - -} - -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} - -.login-card { - max-width: 30em; - margin: 0 auto; - -} - - -.form-control { - display: block; - width: 100%; - padding: 0.5em; - font-size: 1em; - line-height: 1.5; - color: #495057; - background-color: #cec4c4; - background-clip: padding-box; - border: 1px solid #ced4da; - border-radius: 0.25em; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - font-family: ; - margin: 20px; - - /* add pixlated border */ - clip-path: polygon( - 0px calc(100% - 9px), - 3px calc(100% - 9px), - 3px calc(100% - 6px), - 6px calc(100% - 3px), - 9px calc(100% - 3px), - 9px 100%, - calc(100% - 9px) 100%, - calc(100% - 9px) calc(100% - 3px), - calc(100% - 6px) calc(100% - 3px), - calc(100% - 3px) calc(100% - 6px), - calc(100% - 3px) calc(100% - 9px), - 100% calc(100% - 9px), - 100% 9px, - calc(100% - 3px) 9px, - calc(100% - 3px) 6px, - calc(100% - 6px) 3px, - calc(100% - 9px) 3px, - calc(100% - 9px) 0px, - 9px 0px, - 9px 3px, - 6px 3px, - 3px 6px, - 3px 9px, - 0px 9px - ); - -} - -.player-component { - margin: auto; - border: 10px solid #ccc; - height: 700px; - width: 1200px; - /* width: 100%; - height: 100%; */ - display: flex; -} - -.player-machine-section { - width: 100%; - height: 100%; - max-width: 300px; - display: flex; - border: 1px solid #ccc; - flex-direction: column; - overflow-y: scroll; -} - -.player-challenge-detail-section { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - -} - -.actions { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 1em; - -} - -@keyframes slide-in-bck-center{0%{transform:translateZ(600px);opacity:0}100%{transform:translateZ(0);opacity:1}} - - - - -.action-title { - font-size: 1.5em; - font-weight: bold; -} - -.action-card { - width: 100%; - height: 100%; - display: flex; - flex-direction: row; - gap: 1em; - align-items: center; - justify-content: center; - -} -@media (max-width: 600px) { - .action-card { - flex-direction: column; - - } -} - -.player-mlists { -padding: 2em; -margin: 10%; -} - -.player-machine-title { - position: sticky; - top: 0; -} - -.leaderboard-wrapper { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - gap: 1em; - align-items: center; - justify-content: center; -} - -.btn { - border: #646cff 5px solid ; - color: #0000; - --g: no-repeat linear-gradient(#5793f2 0 0) 0 0; - background: var(--g), var(--g); - background-size: 0 80%; - -webkit-background-clip: padding-box,text; - background-clip: padding-box,text; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; - animation: - t 1.2s .5s both, - b 1.2s 1.3s both; - /* This adds pixilated border */ - clip-path: polygon( - 0px calc(100% - 9px), - 3px calc(100% - 9px), - 3px calc(100% - 6px), - 6px calc(100% - 3px), - 9px calc(100% - 3px), - 9px 100%, - calc(100% - 9px) 100%, - calc(100% - 9px) calc(100% - 3px), - calc(100% - 6px) calc(100% - 3px), - calc(100% - 3px) calc(100% - 6px), - calc(100% - 3px) calc(100% - 9px), - 100% calc(100% - 9px), - 100% 9px, - calc(100% - 3px) 9px, - calc(100% - 3px) 6px, - calc(100% - 6px) 3px, - calc(100% - 9px) 3px, - calc(100% - 9px) 0px, - 9px 0px, - 9px 3px, - 6px 3px, - 3px 6px, - 3px 9px, - 0px 9px - ); -} -@keyframes t{ - to {background-size:150% 100%} -} -@keyframes b { - to {background-position:-200% 0,0 0} -} - -.forminput{ -width: 90%; -border-radius:0.5em; -padding: 0.5em; -font-family: Minecraft; -font-size: 0.7em; -} - - - -.home-head{ - animation: slide-in-bck-center 1000ms normal; -} - -/* This is challenge section components */ - -/* This gives pixilated borders to website */ -.player-component{ - - clip-path: polygon( - 0px calc(100% - 15px), - 5px calc(100% - 15px), - 5px calc(100% - 10px), - 10px calc(100% - 5px), - 15px calc(100% - 5px), - 15px 100%, - calc(100% - 15px) 100%, - calc(100% - 15px) calc(100% - 5px), - calc(100% - 10px) calc(100% - 5px), - calc(100% - 5px) calc(100% - 10px), - calc(100% - 5px) calc(100% - 15px), - 100% calc(100% - 15px), - 100% 15px, - calc(100% - 5px) 15px, - calc(100% - 5px) 10px, - calc(100% - 10px) 5px, - calc(100% - 15px) 5px, - calc(100% - 15px) 0px, - 15px 0px, - 15px 5px, - 10px 5px, - 5px 10px, - 5px 15px, - 0px 15px - ); -} - -.player-machine-detail{ - padding: 1em; -} -.player-challenge-button{ -margin: 10px; -clip-path: polygon( - 0px calc(100% - 6px), - 2px calc(100% - 6px), - 2px calc(100% - 4px), - 4px calc(100% - 2px), - 6px calc(100% - 2px), - 6px 100%, - calc(100% - 6px) 100%, - calc(100% - 6px) calc(100% - 2px), - calc(100% - 4px) calc(100% - 2px), - calc(100% - 2px) calc(100% - 4px), - calc(100% - 2px) calc(100% - 6px), - 100% calc(100% - 6px), - 100% 6px, - calc(100% - 2px) 6px, - calc(100% - 2px) 4px, - calc(100% - 4px) 2px, - calc(100% - 6px) 2px, - calc(100% - 6px) 0px, - 6px 0px, - 6px 2px, - 4px 2px, - 2px 4px, - 2px 6px, - 0px 6px -); -} -.player-challenge-compl-button{ - margin: 5px; - border: #646cff 1px solid; - clip-path: polygon( - 0px calc(100% - 4px), - 2px calc(100% - 4px), - 2px calc(100% - 2px), - 4px calc(100% - 2px), - 4px 100%, - calc(100% - 4px) 100%, - calc(100% - 4px) calc(100% - 2px), - calc(100% - 2px) calc(100% - 2px), - calc(100% - 2px) calc(100% - 4px), - 100% calc(100% - 4px), - 100% 4px, - calc(100% - 2px) 4px, - calc(100% - 2px) 2px, - calc(100% - 4px) 2px, - calc(100% - 4px) 0px, - 4px 0px, - 4px 2px, - 2px 2px, - 2px 4px, - 0px 4px - ); -} - -.who-card{ - padding: 10px; - display: flex; - flex-direction: column; - row-gap: 2em; -} -.user-wrapper{ - padding: 1em; - border: #ccc 5px solid; - clip-path: polygon( - 0px calc(100% - 9px), - 3px calc(100% - 9px), - 3px calc(100% - 6px), - 6px calc(100% - 3px), - 9px calc(100% - 3px), - 9px 100%, - calc(100% - 9px) 100%, - calc(100% - 9px) calc(100% - 3px), - calc(100% - 6px) calc(100% - 3px), - calc(100% - 3px) calc(100% - 6px), - calc(100% - 3px) calc(100% - 9px), - 100% calc(100% - 9px), - 100% 9px, - calc(100% - 3px) 9px, - calc(100% - 3px) 6px, - calc(100% - 6px) 3px, - calc(100% - 9px) 3px, - calc(100% - 9px) 0px, - 9px 0px, - 9px 3px, - 6px 3px, - 3px 6px, - 3px 9px, - 0px 9px - ); - ); -} -.team-wrapper{ - padding: 1em; - border: #ccc 5px solid; - clip-path: polygon( - 0px calc(100% - 9px), - 3px calc(100% - 9px), - 3px calc(100% - 6px), - 6px calc(100% - 3px), - 9px calc(100% - 3px), - 9px 100%, - calc(100% - 9px) 100%, - calc(100% - 9px) calc(100% - 3px), - calc(100% - 6px) calc(100% - 3px), - calc(100% - 3px) calc(100% - 6px), - calc(100% - 3px) calc(100% - 9px), - 100% calc(100% - 9px), - 100% 9px, - calc(100% - 3px) 9px, - calc(100% - 3px) 6px, - calc(100% - 6px) 3px, - calc(100% - 9px) 3px, - calc(100% - 9px) 0px, - 9px 0px, - 9px 3px, - 6px 3px, - 3px 6px, - 3px 9px, - 0px 9px - ); -} -.stats-wrapper{ - padding: 1em; - border: #ccc 5px solid; - clip-path: polygon( - 0px calc(100% - 9px), - 3px calc(100% - 9px), - 3px calc(100% - 6px), - 6px calc(100% - 3px), - 9px calc(100% - 3px), - 9px 100%, - calc(100% - 9px) 100%, - calc(100% - 9px) calc(100% - 3px), - calc(100% - 6px) calc(100% - 3px), - calc(100% - 3px) calc(100% - 6px), - calc(100% - 3px) calc(100% - 9px), - 100% calc(100% - 9px), - 100% 9px, - calc(100% - 3px) 9px, - calc(100% - 3px) 6px, - calc(100% - 6px) 3px, - calc(100% - 9px) 3px, - calc(100% - 9px) 0px, - 9px 0px, - 9px 3px, - 6px 3px, - 3px 6px, - 3px 9px, - 0px 9px - ); -} \ No newline at end of file diff --git a/apps/web/src/App.jsx b/apps/web/src/App.jsx index 16826fe..f6c2116 100644 --- a/apps/web/src/App.jsx +++ b/apps/web/src/App.jsx @@ -1,27 +1,29 @@ // App.js -import { Routes, Route } from 'react-router-dom'; -import Home from './pages/home'; -import Player from './pages/app'; -import Login from './pages/login'; -import Register from './pages/register'; -import Verify from './pages/verify'; -import About from './pages/about'; -import Leaderboard from './pages/leaderboard'; -import WhoAmI from './pages/whoami'; +import { Routes, Route } from "react-router-dom"; +import Home from "./pages/home"; +import Player from "./pages/app"; +import Login from "./pages/login"; +import Register from "./pages/register"; +import Verify from "./pages/verify"; +import About from "./pages/about"; +import Leaderboard from "./pages/leaderboard"; +import WhoAmI from "./pages/whoami"; +import News from "./pages/news"; const App = () => { - return ( + return ( - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - ); + ); }; -export default App; \ No newline at end of file +export default App; diff --git a/apps/web/src/components/cards/login-card.tsx b/apps/web/src/components/cards/login-card.tsx new file mode 100644 index 0000000..f0a260c --- /dev/null +++ b/apps/web/src/components/cards/login-card.tsx @@ -0,0 +1,108 @@ +import { useState } from "react"; +import apiClient from "../../libs/api.client"; +import { toast } from "react-toastify"; +import { Link } from "react-router-dom"; + +export const LoginCard = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + let token = localStorage.getItem("token"); + + const handleSubmit = (event) => { + event.preventDefault(); + + apiClient + .post( + "/user/login", + { + email, + password, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ) + .then((response) => { + if (response.data.message === "Login successful") { + token = response.data.token; + localStorage.setItem("token", token); + toast.success("Login Successful!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + setTimeout(() => { + window.location.href = "/app"; + }, 3000); + } else { + toast.error("Login Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + } + }) + .catch(() => { + toast.error("Login Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + }); + }; + return ( +
+
+

Sign In

+
+
+
+ +
+
+ setEmail(event.target.value)} + placeholder='Enter your email' + /> +
+
+
+
+ +
+
+ setPassword(event.target.value)} + placeholder='Enter your password' + /> +
+
+ +
+
+

+ Don't have a account, register from{" "} + here. +

+
+ ); +}; diff --git a/apps/web/src/components/cards/register-card.tsx b/apps/web/src/components/cards/register-card.tsx new file mode 100644 index 0000000..a59268b --- /dev/null +++ b/apps/web/src/components/cards/register-card.tsx @@ -0,0 +1,140 @@ +import { useState } from "react"; +import { ToastContainer, toast } from "react-toastify"; +import { Link } from "react-router-dom"; + +import "react-toastify/dist/ReactToastify.css"; +import apiClient from "../../libs/api.client"; + +export const RegisterCard = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [firstName, setFirstName] = useState(""); + const [lastName, setLastName] = useState(""); + + const handleSubmit = (event) => { + event.preventDefault(); + + apiClient + .post( + "/user/register", + { + email, + password, + firstName, + lastName, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ) + .then((response) => { + if (response.data.email === email) { + toast.success("Register Successful!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + setTimeout(() => { + window.location.href = "/verify"; + }, 3000); + } else { + toast.error("Register Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + } + }) + .catch(() => { + toast.error("Register Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + }); + }; + + return ( +
+
+

Sign In

+
+
+
+ +
+
+ setEmail(event.target.value)} + placeholder='Enter your email' + /> +
+
+
+
+ +
+
+ setPassword(event.target.value)} + placeholder='Enter your password' + /> +
+
+
+
+ +
+
+ setFirstName(event.target.value)} + placeholder='Enter your firstName' + /> +
+
+
+
+ +
+
+ setLastName(event.target.value)} + placeholder='Enter your lastName' + /> +
+
+ +
+
+

+ Already have a account? Login from here. +

+
+ ); +}; diff --git a/apps/web/src/components/navbar.jsx b/apps/web/src/components/navbar.jsx index e2ef2cb..c6a6adb 100644 --- a/apps/web/src/components/navbar.jsx +++ b/apps/web/src/components/navbar.jsx @@ -1,23 +1,31 @@ import { Link } from "react-router-dom"; -import { Component } from 'react' -export default class Navbar extends Component { - render() { - return ( -
-
-
- Home - About - App - Login - Register - Verify Account - Leaderboard - Whoami +import "../styles/navbar.styles.css"; + +export default function Navbar(props) { + return ( +
+
+
+
+ About + News + Whoami +
+ + ee logo + +
+ App + Leaderboard
+ {/* Login + Register */} + {/* Verify Account */}
+ +

{props.pageTitle}

- ) - } -} \ No newline at end of file +
+ ); +} diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 56e4f04..24b4493 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -8,7 +8,6 @@ src: url("/MinecraftBold.woff2"); } - :root { font-family: "MinecraftBold", sans-serif; line-height: 1.5; @@ -16,7 +15,8 @@ color-scheme: light dark; color: rgba(255, 255, 255, 0.87); - background-color: #242424; + /* background-color: #242424; */ + background-color: #0e0e0e; font-synthesis: none; text-rendering: optimizeLegibility; @@ -35,10 +35,10 @@ a:hover { body { margin: 0; - display: flex; + /* display: flex; place-items: center; min-width: 320px; - min-height: 100vh; + min-height: 100vh; */ } h1 { diff --git a/apps/web/src/main.jsx b/apps/web/src/main.jsx index 62ed430..69eadfc 100644 --- a/apps/web/src/main.jsx +++ b/apps/web/src/main.jsx @@ -1,14 +1,15 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.jsx' -import { BrowserRouter } from 'react-router-dom'; -import './index.css' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; +import { BrowserRouter } from "react-router-dom"; +import "./index.css"; +import "./styles/logo.styles.css"; -const root = ReactDOM.createRoot(document.getElementById('root')); +const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - - - + + + -); \ No newline at end of file +); diff --git a/apps/web/src/pages/about.jsx b/apps/web/src/pages/about.jsx index 6e50321..c51690e 100644 --- a/apps/web/src/pages/about.jsx +++ b/apps/web/src/pages/about.jsx @@ -1,18 +1,14 @@ -import '../App.css' -import 'react-toastify/dist/ReactToastify.css'; -import Navbar from '../components/navbar'; + +import "react-toastify/dist/ReactToastify.css"; +import Navbar from "../components/navbar"; import { url } from '../libs/api.client'; + function Home() { - return ( - <> - -
- ee logo -
-

rcsCTF24

- -
+ return ( + <> + +

Guides

How to use the scoreboard @@ -23,8 +19,8 @@ function Home() { {url}
- - ) + + ); } -export default Home +export default Home; diff --git a/apps/web/src/pages/app.jsx b/apps/web/src/pages/app.jsx index 86da67e..ed66f12 100644 --- a/apps/web/src/pages/app.jsx +++ b/apps/web/src/pages/app.jsx @@ -1,84 +1,111 @@ -import '../App.css' -import 'react-toastify/dist/ReactToastify.css'; -import Navbar from '../components/navbar'; -import { useEffect, useState } from 'react'; -import Machines from "../components/machine" -import Challenge from '../components/challenge'; -import { ToastContainer } from 'react-toastify'; -import apiClient from '../libs/api.client'; +import "react-toastify/dist/ReactToastify.css"; +import Navbar from "../components/navbar"; +import { useEffect, useState } from "react"; +import Machines from "../components/machine"; +import Challenge from "../components/challenge"; +import { ToastContainer } from "react-toastify"; +import apiClient from "../libs/api.client"; function Player() { - const token = localStorage.getItem('token'); - const [machines, setMachines] = useState([]); - const [currentExpandedMachine, setCurrentExpandedMachine] = useState(1); - const [currentChallenge, setCurrentChallenge] = useState([currentExpandedMachine,-1]); + const token = localStorage.getItem("token"); + const [machines, setMachines] = useState([]); + const [currentExpandedMachine, setCurrentExpandedMachine] = useState(1); + const [currentChallenge, setCurrentChallenge] = useState([ + currentExpandedMachine, + -1, + ]); - useEffect(() => { - if(!token) return window.location.href = '/login'; + useEffect(() => { + if (!token) return (window.location.href = "/"); - apiClient.get("/challenge/progress", { - headers: { - "Authorization": "Bearer " + token, - } - }).then(res => { - if(!Array.isArray(res.data)) return window.location.href = '/login' - if(res.data.message === 'Invalid token') return window.location.href = '/login'; - setMachines(res.data); - }) - - },[token]) + apiClient + .get("/challenge/progress", { + headers: { + Authorization: "Bearer " + token, + }, + }) + .then((res) => { + if (!Array.isArray(res.data)) return (window.location.href = "/"); + if (res.data.message === "Invalid token") + return (window.location.href = "/"); + setMachines(res.data); + }); + }, [token]); - return ( - <> - -
-
-
-

Machines

-
- {machines.map((machine, index) => ( - - ))} -
-
-
-
-
- {currentChallenge[0] > -1 && currentChallenge[1] > -1 && } -
-
+ return ( + <> + {/* */} +
+
+
+

Machines

+
+ {machines.map((machine, index) => ( + + ))}
- - - ) +
+
+
+
+ {currentChallenge[0] > -1 && currentChallenge[1] > -1 && ( + + )} +
+
+
+ + + ); } -export default Player +export default Player; diff --git a/apps/web/src/pages/home.jsx b/apps/web/src/pages/home.jsx index bad0465..55d0d6b 100644 --- a/apps/web/src/pages/home.jsx +++ b/apps/web/src/pages/home.jsx @@ -1,31 +1,58 @@ -import '../App.css' -import 'react-toastify/dist/ReactToastify.css'; -import { Link } from "react-router-dom"; +import "../styles/home.styles.css"; +import "react-toastify/dist/ReactToastify.css"; +import apiClient from "../libs/api.client"; +import Navbar from "../components/navbar"; +import { useEffect, useState } from "react"; +import { LoginCard } from "../components/cards/login-card"; +import { RegisterCard } from "../components/cards/register-card"; function Home() { - return ( - <> -
- ee logo -
-

Welcome to eeCTF

- -
-

- Actions -

-
- About - App - Login - Register - Verify Account - Leaderboard - Whoami + const [user, setUser] = useState(null); + useEffect(() => { + const token = localStorage.getItem("token"); + + if (!token) return; + + apiClient + .get("/user/whoami", { + headers: { + Authorization: "Bearer " + token, + }, + }) + .then((res) => { + if (res.data.error) return; + + if (res.data.message === "Invalid token") return; + setUser(res.data); + }); + }, []); + + return ( +
+ +
+ {user === null ? ( +
+ + +
+ ) : ( +
+
+ {/*

+ You are currently in {user.team_name} team. Your team has{" "} + {user.team_score} points. +

*/}
-
- - ) +
+ )} +
+
+ ); } -export default Home +export default Home; diff --git a/apps/web/src/pages/leaderboard.jsx b/apps/web/src/pages/leaderboard.jsx index 8847fda..1e9dd03 100644 --- a/apps/web/src/pages/leaderboard.jsx +++ b/apps/web/src/pages/leaderboard.jsx @@ -1,7 +1,6 @@ -import "../App.css"; import "react-toastify/dist/ReactToastify.css"; import { useEffect, useState } from "react"; -import apiClient from '../libs/api.client'; +import apiClient from "../libs/api.client"; import Navbar from "../components/navbar"; function Leaderboard() { @@ -10,16 +9,16 @@ function Leaderboard() { useEffect(() => { const token = localStorage.getItem("token"); - if (!token) return (window.location.href = "/login"); + if (!token) return (window.location.href = "/"); apiClient .get("/stats/leaderboard", { headers: { Authorization: "Bearer " + token, - } + }, }) .then((response) => { - if(!Array.isArray(response.data)) return window.location.href = '/login' + if (!Array.isArray(response.data)) return (window.location.href = "/"); setLeaderboard(response.data); }) .catch(() => { @@ -29,12 +28,8 @@ function Leaderboard() { return ( <> - -
- ee logo -
-

Leaderboard

-
+ +
diff --git a/apps/web/src/pages/login.jsx b/apps/web/src/pages/login.jsx index 56e5644..c9fc918 100644 --- a/apps/web/src/pages/login.jsx +++ b/apps/web/src/pages/login.jsx @@ -1,126 +1,127 @@ -import { useState } from 'react' -import apiClient from '../libs/api.client'; -import { ToastContainer, toast } from 'react-toastify'; +import { useState } from "react"; +import apiClient from "../libs/api.client"; +import { ToastContainer, toast } from "react-toastify"; import { Link } from "react-router-dom"; -import '../App.css' -import 'react-toastify/dist/ReactToastify.css'; -import Navbar from '../components/navbar'; +import "react-toastify/dist/ReactToastify.css"; +import Navbar from "../components/navbar"; function Home() { - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); - let token = localStorage.getItem('token'); + let token = localStorage.getItem("token"); - const handleSubmit = (event) => { - event.preventDefault(); - - apiClient.post("/user/login", { - email, - password - }, { - headers: { - "Content-Type": "application/json" - } - }) - .then((response) => { - if (response.data.message === 'Login successful') { - token = response.data.token; - localStorage.setItem('token', token); - toast.success('Login Successful!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - setTimeout(() => { - window.location.href = '/app'; - }, 3000); - } else { - toast.error('Login Failed!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - } - }) - .catch(() => { - toast.error('Login Failed!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - }); - } + const handleSubmit = (event) => { + event.preventDefault(); - return ( - <> - -
- ee logo -
-

Login Portal

-
-

Sign In

-
-
-
- -
-
- setEmail(event.target.value)} - placeholder="Enter your email" - /> -
+ apiClient + .post( + "/user/login", + { + email, + password, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ) + .then((response) => { + if (response.data.message === "Login successful") { + token = response.data.token; + localStorage.setItem("token", token); + toast.success("Login Successful!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + setTimeout(() => { + window.location.href = "/app"; + }, 3000); + } else { + toast.error("Login Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + } + }) + .catch(() => { + toast.error("Login Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + }); + }; + + return ( + <> + +
+

Sign In

+ +
+
+ +
+
+ setEmail(event.target.value)} + placeholder='Enter your email' + /> +
+
+
+
+
-
-
- -
-
- + setPassword(event.target.value)} - placeholder="Enter your password" - /> -
+ type='password' + id='password' + value={password} + onChange={(event) => setPassword(event.target.value)} + placeholder='Enter your password' + />
- - -
-

- Don't have a account, register from here. -

- - - ) +
+ + +
+

+ Don't have a account, register from{" "} + here. +

+ + + ); } -export default Home +export default Home; diff --git a/apps/web/src/pages/news.jsx b/apps/web/src/pages/news.jsx new file mode 100644 index 0000000..4f781e7 --- /dev/null +++ b/apps/web/src/pages/news.jsx @@ -0,0 +1,20 @@ +import "react-toastify/dist/ReactToastify.css"; +import Navbar from "../components/navbar"; + +function News() { + return ( + <> + + {/* */} + + ); +} + +export default News; diff --git a/apps/web/src/pages/register.jsx b/apps/web/src/pages/register.jsx index 45c6cbf..f7811f3 100644 --- a/apps/web/src/pages/register.jsx +++ b/apps/web/src/pages/register.jsx @@ -1,154 +1,158 @@ -import { useState } from 'react' -import apiClient from '../libs/api.client'; -import { ToastContainer, toast } from 'react-toastify'; +import { useState } from "react"; +import apiClient from "../libs/api.client"; +import { ToastContainer, toast } from "react-toastify"; import { Link } from "react-router-dom"; -import '../App.css' -import 'react-toastify/dist/ReactToastify.css'; +import "react-toastify/dist/ReactToastify.css"; function Home() { - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') - const [firstName, setFirstName] = useState('') - const [lastName, setLastName] = useState('') + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [firstName, setFirstName] = useState(""); + const [lastName, setLastName] = useState(""); - const handleSubmit = (event) => { - event.preventDefault() - - apiClient.post("/user/register", { - email, - password, - firstName, - lastName - }, { - headers: { - "Content-Type": "application/json" - } - }) - .then((response) => { - if (response.data.email === email) { - toast.success('Register Successful!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - setTimeout(() => { - window.location.href = '/verify'; - }, 3000); - } else { - toast.error('Register Failed!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - } - }) - .catch(() => { - toast.error('Register Failed!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - }); - } + const handleSubmit = (event) => { + event.preventDefault(); - return ( - <> -
- React logo + apiClient + .post( + "/user/register", + { + email, + password, + firstName, + lastName, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ) + .then((response) => { + if (response.data.email === email) { + toast.success("Register Successful!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + setTimeout(() => { + window.location.href = "/verify"; + }, 3000); + } else { + toast.error("Register Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + } + }) + .catch(() => { + toast.error("Register Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + }); + }; + + return ( + <> +
+ React logo +
+

Login Portal

+
+

Sign In

+
+
+
+ +
+
+ setEmail(event.target.value)} + placeholder='Enter your email' + /> +
+
+
+
+ +
+
+ setPassword(event.target.value)} + placeholder='Enter your password' + /> +
+
+
+
+ +
+
+ setFirstName(event.target.value)} + placeholder='Enter your firstName' + /> +
+
+
+
+
-

Login Portal

-
-

Sign In

- -
-
- -
-
- setEmail(event.target.value)} - placeholder="Enter your email" - /> -
-
-
-
- -
-
- setPassword(event.target.value)} - placeholder="Enter your password" - /> -
-
-
-
- -
-
- setFirstName(event.target.value)} - placeholder="Enter your firstName" - /> -
-
-
-
- -
-
- setLastName(event.target.value)} - placeholder="Enter your lastName" - /> -
-
- - +
+ setLastName(event.target.value)} + placeholder='Enter your lastName' + />
-

- Already have a account? Login from here. -

- - - ) +
+ + +
+

+ Already have a account? Login from here. +

+ + + ); } -export default Home +export default Home; diff --git a/apps/web/src/pages/verify.jsx b/apps/web/src/pages/verify.jsx index ee2d168..ad2d342 100644 --- a/apps/web/src/pages/verify.jsx +++ b/apps/web/src/pages/verify.jsx @@ -1,98 +1,102 @@ -import { useState } from 'react' -import apiClient from '../libs/api.client'; -import { ToastContainer, toast } from 'react-toastify'; +import { useState } from "react"; +import apiClient from "../libs/api.client"; +import { ToastContainer, toast } from "react-toastify"; -import '../App.css' -import 'react-toastify/dist/ReactToastify.css'; +import "react-toastify/dist/ReactToastify.css"; function Home() { - const [otp, setOtp] = useState('') + const [otp, setOtp] = useState(""); - const handleSubmit = (event) => { - event.preventDefault() - - apiClient.post("/user/verify", { - otp - }, { - headers: { - "Content-Type": "application/json" - } - }) - .then((response) => { - if (response.data.message === 'User verified successfully') { - toast.success('Email Verified!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - setTimeout(() => { - window.location.href = '/login'; - }, 3000); - } else { - toast.error('Email Verification Failed!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - } - }) - .catch(() => { - toast.error('Email Verification Failed!', { - position: "bottom-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - draggable: true, - }); - }); - } + const handleSubmit = (event) => { + event.preventDefault(); - return ( - <> -
- ee logo -
-

Auth Portal

-
-

Verify Email

-
-
-
- -
-
- setOtp(event.target.value)} - placeholder="EC-XXXXXX" - /> -
+ apiClient + .post( + "/user/verify", + { + otp, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ) + .then((response) => { + if (response.data.message === "User verified successfully") { + toast.success("Email Verified!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + setTimeout(() => { + window.location.href = "/"; + }, 3000); + } else { + toast.error("Email Verification Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + } + }) + .catch(() => { + toast.error("Email Verification Failed!", { + position: "bottom-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + draggable: true, + }); + }); + }; + + return ( + <> +
+ ee logo +
+

Auth Portal

+
+

Verify Email

+ +
+
+ +
+
+ setOtp(event.target.value)} + placeholder='EC-XXXXXX' + />
- - -
- - - ) +
+ + +
+ + + ); } -export default Home +export default Home; diff --git a/apps/web/src/pages/whoami.jsx b/apps/web/src/pages/whoami.jsx index cee0b18..0c5c367 100644 --- a/apps/web/src/pages/whoami.jsx +++ b/apps/web/src/pages/whoami.jsx @@ -1,41 +1,48 @@ -import '../App.css' -import 'react-toastify/dist/ReactToastify.css'; -import { useState, useEffect } from 'react'; -import apiClient from '../libs/api.client'; +import "../styles/App.css"; +import "react-toastify/dist/ReactToastify.css"; +import { useState, useEffect } from "react"; +import apiClient from "../libs/api.client"; import Navbar from "../components/navbar"; +import "../styles/whoami.styles.css"; + function WhoAmI() { - const [userWhoami, setUserWhoami] = useState(null); - const [teamWhoami, setTeamWhoami] = useState(null); - const [teamStats, setTeamStats] = useState(null); + const [userWhoami, setUserWhoami] = useState(null); + const [teamWhoami, setTeamWhoami] = useState(null); + const [teamStats, setTeamStats] = useState(null); - useEffect(() => { - const token = localStorage.getItem('token'); + useEffect(() => { + const token = localStorage.getItem("token"); - if(!token) return window.location.href = '/login'; + if (!token) return (window.location.href = "/"); - apiClient.get("/user/whoami", { - headers: { - "Authorization": "Bearer " + token, - } - }).then(res => { - if(res.data.error) return window.location.href = '/login' + apiClient + .get("/user/whoami", { + headers: { + Authorization: "Bearer " + token, + }, + }) + .then((res) => { + if (res.data.error) return (window.location.href = "/"); - if(res.data.message === 'Invalid token') return window.location.href = '/login'; - setUserWhoami(res.data); - }) + if (res.data.message === "Invalid token") + return (window.location.href = "/"); + setUserWhoami(res.data); + }); - apiClient.get("/team/whoami", { - headers: { - "Authorization": "Bearer " + token, - } - }).then(res => { - if(res.data.error) return window.location.href = '/login' - - if(res.data.message === 'Invalid token') return window.location.href = '/login'; - setTeamWhoami(res.data); - }) + apiClient + .get("/team/whoami", { + headers: { + Authorization: "Bearer " + token, + }, + }) + .then((res) => { + if (res.data.error) return (window.location.href = "/"); + if (res.data.message === "Invalid token") + return (window.location.href = "/"); + setTeamWhoami(res.data); + }); apiClient.get("/stats/team", { headers: { "Authorization": "Bearer " + token, @@ -47,48 +54,53 @@ function WhoAmI() { }) }, []) - return ( - <> - -
- ee logo -
-

Who Am I ?

-
-
-

$ User

- ID: {userWhoami?.id}
- Name: {userWhoami?.first_name} {userWhoami?.last_name}
-
-
-

$ Team

- ID: {teamWhoami?.id}
- Name: {teamWhoami?.name}
-
-
-

$ Stats

-
- - - - - - - - - {teamStats?.submissions?.map((stat, index) => ( - - - - - - ))} - -
MachineChallengeFlag
{stat.machine_name}{stat.challenge_name}{stat.submited_flag}
+ return ( + <> + +
+
+
+

$ User

+ ID: {userWhoami?.id} +
+ Name:{" "} + + {userWhoami?.first_name} {userWhoami?.last_name} + +
+
+
+

$ Team

+ ID: {teamWhoami?.id} +
+ Name: {teamWhoami?.name} +
+
+
+
+

$ Stats

+ + + + + + + + + + {teamStats?.submissions?.map((stat, index) => ( + + + + + + ))} + +
MachineChallengeFlag
{stat.machine_name}{stat.challenge_name}{stat.submited_flag}
-
- - ) +
+ + ); } -export default WhoAmI +export default WhoAmI; diff --git a/apps/web/src/styles/App.css b/apps/web/src/styles/App.css new file mode 100644 index 0000000..a2828bb --- /dev/null +++ b/apps/web/src/styles/App.css @@ -0,0 +1,301 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.action-card { + width: 100%; + display: flex; + flex-direction: row; + align-items: center; +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} + +.login-card { + max-width: 30em; + margin: 0 auto; +} + +.form-control { + display: block; + width: 100%; + padding: 0.5em; + font-size: 1em; + line-height: 1.5; + color: #495057; + background-color: #cec4c4; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25em; + transition: + border-color 0.15s ease-in-out, + box-shadow 0.15s ease-in-out; + margin: 20px; + + /* add pixlated border */ + clip-path: polygon( + 0px calc(100% - 9px), + 3px calc(100% - 9px), + 3px calc(100% - 6px), + 6px calc(100% - 3px), + 9px calc(100% - 3px), + 9px 100%, + calc(100% - 9px) 100%, + calc(100% - 9px) calc(100% - 3px), + calc(100% - 6px) calc(100% - 3px), + calc(100% - 3px) calc(100% - 6px), + calc(100% - 3px) calc(100% - 9px), + 100% calc(100% - 9px), + 100% 9px, + calc(100% - 3px) 9px, + calc(100% - 3px) 6px, + calc(100% - 6px) 3px, + calc(100% - 9px) 3px, + calc(100% - 9px) 0px, + 9px 0px, + 9px 3px, + 6px 3px, + 3px 6px, + 3px 9px, + 0px 9px + ); +} + +.player-component { + margin: auto; + border: 10px solid #ccc; + height: 700px; + width: 1200px; + /* width: 100%; + height: 100%; */ + display: flex; +} + +.player-machine-section { + width: 100%; + height: 100%; + max-width: 300px; + display: flex; + border: 1px solid #ccc; + flex-direction: column; + overflow-y: scroll; +} + +.player-challenge-detail-section { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} + +@keyframes slide-in-bck-center { + 0% { + transform: translateZ(600px); + opacity: 0; + } + 100% { + transform: translateZ(0); + opacity: 1; + } +} + +.player-mlists { + padding: 2em; + margin: 10%; +} + +.player-machine-title { + position: sticky; + top: 0; +} + +.leaderboard-wrapper { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: 1em; + align-items: center; + justify-content: center; +} + +.btn { + border: #646cff 5px solid; + color: #0000; + --g: no-repeat linear-gradient(#5793f2 0 0) 0 0; + background: var(--g), var(--g); + background-size: 0 80%; + -webkit-background-clip: padding-box, text; + background-clip: padding-box, text; + -webkit-box-decoration-break: clone; + box-decoration-break: clone; + animation: + t 1.2s 0.5s both, + b 1.2s 1.3s both; + /* This adds pixilated border */ + clip-path: polygon( + 0px calc(100% - 9px), + 3px calc(100% - 9px), + 3px calc(100% - 6px), + 6px calc(100% - 3px), + 9px calc(100% - 3px), + 9px 100%, + calc(100% - 9px) 100%, + calc(100% - 9px) calc(100% - 3px), + calc(100% - 6px) calc(100% - 3px), + calc(100% - 3px) calc(100% - 6px), + calc(100% - 3px) calc(100% - 9px), + 100% calc(100% - 9px), + 100% 9px, + calc(100% - 3px) 9px, + calc(100% - 3px) 6px, + calc(100% - 6px) 3px, + calc(100% - 9px) 3px, + calc(100% - 9px) 0px, + 9px 0px, + 9px 3px, + 6px 3px, + 3px 6px, + 3px 9px, + 0px 9px + ); +} +@keyframes t { + to { + background-size: 150% 100%; + } +} +@keyframes b { + to { + background-position: + -200% 0, + 0 0; + } +} + +.forminput { + width: 90%; + border-radius: 0.5em; + padding: 0.5em; + font-family: Minecraft; + font-size: 0.7em; +} + +.home-head { + animation: slide-in-bck-center 1000ms normal; +} + +/* This is challenge section components */ + +/* This gives pixilated borders to website */ +.player-component { + clip-path: polygon( + 0px calc(100% - 15px), + 5px calc(100% - 15px), + 5px calc(100% - 10px), + 10px calc(100% - 5px), + 15px calc(100% - 5px), + 15px 100%, + calc(100% - 15px) 100%, + calc(100% - 15px) calc(100% - 5px), + calc(100% - 10px) calc(100% - 5px), + calc(100% - 5px) calc(100% - 10px), + calc(100% - 5px) calc(100% - 15px), + 100% calc(100% - 15px), + 100% 15px, + calc(100% - 5px) 15px, + calc(100% - 5px) 10px, + calc(100% - 10px) 5px, + calc(100% - 15px) 5px, + calc(100% - 15px) 0px, + 15px 0px, + 15px 5px, + 10px 5px, + 5px 10px, + 5px 15px, + 0px 15px + ); +} + +.player-machine-detail { + padding: 1em; +} +.player-challenge-button { + margin: 10px; + clip-path: polygon( + 0px calc(100% - 6px), + 2px calc(100% - 6px), + 2px calc(100% - 4px), + 4px calc(100% - 2px), + 6px calc(100% - 2px), + 6px 100%, + calc(100% - 6px) 100%, + calc(100% - 6px) calc(100% - 2px), + calc(100% - 4px) calc(100% - 2px), + calc(100% - 2px) calc(100% - 4px), + calc(100% - 2px) calc(100% - 6px), + 100% calc(100% - 6px), + 100% 6px, + calc(100% - 2px) 6px, + calc(100% - 2px) 4px, + calc(100% - 4px) 2px, + calc(100% - 6px) 2px, + calc(100% - 6px) 0px, + 6px 0px, + 6px 2px, + 4px 2px, + 2px 4px, + 2px 6px, + 0px 6px + ); +} +.player-challenge-compl-button { + margin: 5px; + border: #646cff 1px solid; + clip-path: polygon( + 0px calc(100% - 4px), + 2px calc(100% - 4px), + 2px calc(100% - 2px), + 4px calc(100% - 2px), + 4px 100%, + calc(100% - 4px) 100%, + calc(100% - 4px) calc(100% - 2px), + calc(100% - 2px) calc(100% - 2px), + calc(100% - 2px) calc(100% - 4px), + 100% calc(100% - 4px), + 100% 4px, + calc(100% - 2px) 4px, + calc(100% - 2px) 2px, + calc(100% - 4px) 2px, + calc(100% - 4px) 0px, + 4px 0px, + 4px 2px, + 2px 2px, + 2px 4px, + 0px 4px + ); +} diff --git a/apps/web/src/styles/home.styles.css b/apps/web/src/styles/home.styles.css new file mode 100644 index 0000000..f857508 --- /dev/null +++ b/apps/web/src/styles/home.styles.css @@ -0,0 +1,9 @@ +.auht-grid { + display: flex; + flex-direction: row; + gap: 3rem; + align-items: center; + justify-content: center; + width: 100%; + margin: auto; +} diff --git a/apps/web/src/styles/logo.styles.css b/apps/web/src/styles/logo.styles.css new file mode 100644 index 0000000..0deca91 --- /dev/null +++ b/apps/web/src/styles/logo.styles.css @@ -0,0 +1,25 @@ +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; + --r: 10px; /* control the radius of the circles */ + padding: calc(2 * var(--r)); + filter: grayscale(0.4); + border-radius: 100%; + cursor: pointer; + object-position: -1px 2px; + font-size: 0.7em; + /* background: + radial-gradient(var(--r), #0000 98%, #fff) round calc(-1.5 * var(--r)) + calc(-1.5 * var(--r)) / calc(3 * var(--r)) calc(3 * var(--r)), + linear-gradient(#fff 0 0) no-repeat 50% / calc(100% - 3 * var(--r)) + calc(100% - 3 * var(--r)); */ +} + +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo .react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} diff --git a/apps/web/src/styles/navbar.styles.css b/apps/web/src/styles/navbar.styles.css new file mode 100644 index 0000000..8d2ea9b --- /dev/null +++ b/apps/web/src/styles/navbar.styles.css @@ -0,0 +1,40 @@ +.actions { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 1em; +} + +.actions a { + font-size: 1.3em; + font-weight: bold; +} + +.action-card .right { + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + flex: 1; + gap: 1em; + align-items: center; + justify-content: space-evenly; +} + +.action-card .left { + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + flex: 1; + gap: 1em; + align-items: center; + justify-content: space-evenly; +} + +@media (max-width: 600px) { + .action-card { + flex-direction: column; + } +} diff --git a/apps/web/src/styles/whoami.styles.css b/apps/web/src/styles/whoami.styles.css new file mode 100644 index 0000000..b906c07 --- /dev/null +++ b/apps/web/src/styles/whoami.styles.css @@ -0,0 +1,108 @@ +.who-card { + padding: 10px; + display: flex; + flex-direction: column; + gap: 2em; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); +} + +.who-wrapper { + display: grid; + gap: 2em; +} + +.user-wrapper { + padding: 1em; + border: #ccc 5px solid; + clip-path: polygon( + 0px calc(100% - 9px), + 3px calc(100% - 9px), + 3px calc(100% - 6px), + 6px calc(100% - 3px), + 9px calc(100% - 3px), + 9px 100%, + calc(100% - 9px) 100%, + calc(100% - 9px) calc(100% - 3px), + calc(100% - 6px) calc(100% - 3px), + calc(100% - 3px) calc(100% - 6px), + calc(100% - 3px) calc(100% - 9px), + 100% calc(100% - 9px), + 100% 9px, + calc(100% - 3px) 9px, + calc(100% - 3px) 6px, + calc(100% - 6px) 3px, + calc(100% - 9px) 3px, + calc(100% - 9px) 0px, + 9px 0px, + 9px 3px, + 6px 3px, + 3px 6px, + 3px 9px, + 0px 9px + ); +} + +.team-wrapper { + padding: 1em; + border: #ccc 5px solid; + clip-path: polygon( + 0px calc(100% - 9px), + 3px calc(100% - 9px), + 3px calc(100% - 6px), + 6px calc(100% - 3px), + 9px calc(100% - 3px), + 9px 100%, + calc(100% - 9px) 100%, + calc(100% - 9px) calc(100% - 3px), + calc(100% - 6px) calc(100% - 3px), + calc(100% - 3px) calc(100% - 6px), + calc(100% - 3px) calc(100% - 9px), + 100% calc(100% - 9px), + 100% 9px, + calc(100% - 3px) 9px, + calc(100% - 3px) 6px, + calc(100% - 6px) 3px, + calc(100% - 9px) 3px, + calc(100% - 9px) 0px, + 9px 0px, + 9px 3px, + 6px 3px, + 3px 6px, + 3px 9px, + 0px 9px + ); +} + +.stats-wrapper { + display: flex; + flex-direction: column; + padding: 1em; + border: #ccc 5px solid; + clip-path: polygon( + 0px calc(100% - 9px), + 3px calc(100% - 9px), + 3px calc(100% - 6px), + 6px calc(100% - 3px), + 9px calc(100% - 3px), + 9px 100%, + calc(100% - 9px) 100%, + calc(100% - 9px) calc(100% - 3px), + calc(100% - 6px) calc(100% - 3px), + calc(100% - 3px) calc(100% - 6px), + calc(100% - 3px) calc(100% - 9px), + 100% calc(100% - 9px), + 100% 9px, + calc(100% - 3px) 9px, + calc(100% - 3px) 6px, + calc(100% - 6px) 3px, + calc(100% - 9px) 3px, + calc(100% - 9px) 0px, + 9px 0px, + 9px 3px, + 6px 3px, + 3px 6px, + 3px 9px, + 0px 9px + ); +}