Merge branch 'main' of git.zomo.dev:/zomo/discord-retokenizer-web
This commit is contained in:
@@ -11,7 +11,7 @@ import { Tokens } from "./pages/Tokens";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div>
|
||||
<div className="bg-gray-900 text-gray-200 w-full h-full">
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/logout" element={<Login />} />
|
||||
|
||||
81
src/_index.scss
Normal file
81
src/_index.scss
Normal file
@@ -0,0 +1,81 @@
|
||||
#root {
|
||||
--bg-color-1: #262626;
|
||||
--bg-color-2: #363636;
|
||||
--bg-color-3-5: #3d3d3d;
|
||||
--bg-color-3: #464646;
|
||||
--text-color-1: #d6d6d6;
|
||||
--text-color-2: #a6a6a6;
|
||||
--color-error: #860000;
|
||||
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
height: 100%;
|
||||
|
||||
width: 200px;
|
||||
& + #content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
width: 50px;
|
||||
& + #content {
|
||||
margin-left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
background-color: var(--bg-color-2);
|
||||
color: var(--text-color-2);
|
||||
|
||||
#collapse {
|
||||
cursor: pointer;
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
font-weight: inherit;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
|
||||
font-size: 16px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--bg-color-3);
|
||||
color: var(--text-color-1);
|
||||
|
||||
&.important {
|
||||
background-color: var(--color-error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#content {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
3
src/index.css
Normal file
3
src/index.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
173
src/index.scss
173
src/index.scss
@@ -1,173 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#root {
|
||||
--bg-color-1: #262626;
|
||||
--bg-color-2: #363636;
|
||||
--bg-color-3-5: #3d3d3d;
|
||||
--bg-color-3: #464646;
|
||||
--text-color-1: #d6d6d6;
|
||||
--text-color-2: #a6a6a6;
|
||||
--color-error: #860000;
|
||||
|
||||
background-color: var(--bg-color-1);
|
||||
color: var(--text-color-1);
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
input {
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: var(--bg-color-2);
|
||||
color: var(--text-color-2);
|
||||
font-size: 16px;
|
||||
|
||||
&[type="text"],
|
||||
&[type="password"] {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
padding: 7px 10px;
|
||||
margin: 0 5px;
|
||||
|
||||
border-radius: 200px;
|
||||
|
||||
&:focus {
|
||||
background-color: var(--bg-color-3);
|
||||
color: var(--text-color-1);
|
||||
}
|
||||
|
||||
border-bottom: 1px solid transparent;
|
||||
&.error {
|
||||
border-bottom: 1px solid var(--color-error);
|
||||
}
|
||||
}
|
||||
|
||||
button, .button {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: var(--bg-color-2);
|
||||
color: var(--text-color-2);
|
||||
font-size: 16px;
|
||||
|
||||
&.svg svg {
|
||||
fill: var(--text-color-2);
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
padding: 7px 10px;
|
||||
margin: 0 5px;
|
||||
|
||||
&.pad {
|
||||
padding: 10px 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
border-radius: 200px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--bg-color-3);
|
||||
color: var(--text-color-1);
|
||||
|
||||
&.svg svg {
|
||||
fill: var(--text-color-1);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--bg-color-3-5);
|
||||
}
|
||||
}
|
||||
|
||||
#login {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.form {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
height: 100%;
|
||||
|
||||
width: 200px;
|
||||
& + #content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
width: 50px;
|
||||
& + #content {
|
||||
margin-left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
background-color: var(--bg-color-2);
|
||||
color: var(--text-color-2);
|
||||
|
||||
#collapse {
|
||||
cursor: pointer;
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
font-weight: inherit;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
|
||||
font-size: 16px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--bg-color-3);
|
||||
color: var(--text-color-1);
|
||||
|
||||
&.important {
|
||||
background-color: var(--color-error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#content {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.scss'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
|
||||
@@ -1,7 +1,27 @@
|
||||
import { Outlet, Navigate, NavLink } from 'react-router-dom'
|
||||
import { isLoggedIn, LoginState, usePersistentState } from '../authorization'
|
||||
|
||||
const activeClassName: ({ isActive }: { isActive: boolean }) => string = ({ isActive }) => isActive ? "active" : ""
|
||||
function SLink({ to, children, important }: {
|
||||
to: string,
|
||||
children: React.ReactNode,
|
||||
important?: boolean
|
||||
}) {
|
||||
return (
|
||||
<NavLink
|
||||
className={
|
||||
({ isActive }) =>
|
||||
"flex w-full h-10 justify-center items-center hover:text-gray-300 "
|
||||
+ (isActive ? "bg-gray-500 text-gray-300 " : "")
|
||||
+ (important ?
|
||||
"hover:bg-red-900 active:bg-red-800" :
|
||||
"hover:bg-gray-600 active:bg-gray-600"
|
||||
)
|
||||
}
|
||||
to={to}>
|
||||
{children}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
export function Layout() {
|
||||
const [collapsed, setCollapsed] = usePersistentState("collapsed", false)
|
||||
@@ -10,21 +30,36 @@ export function Layout() {
|
||||
{
|
||||
isLoggedIn() === LoginState.No && <Navigate to="/login" replace={true} />
|
||||
}
|
||||
<div id="sidebar" className={collapsed ? "collapse" : ""}>
|
||||
<label id="collapse">
|
||||
<input type="checkbox" checked={collapsed} onChange={e => setCollapsed(e.target.checked)} />
|
||||
<div className="button">yo</div>
|
||||
<div
|
||||
className={
|
||||
"fixed top-0 left-0 h-full overflow-hidden bg-gray-700 text-gray-400 "
|
||||
+ (collapsed ? "w-14" : "w-48")
|
||||
}>
|
||||
<label className="inline-block rounded-full outline-none focus:bg-gray-700 hover:bg-gray-700 active:bg-gray-800 fill-gray-400 focus:fill-gray-200 px-3 py-2 mx-2 shadow-glow shadow-transparent invalid:outline-red-600 invalid:shadow-red-600 focus:outline-blue-500 focus:shadow-blue-500 transition-all">
|
||||
{/* TODO I COPIED THE ABOVE FROM LOGIN */}
|
||||
<input className="hidden" type="checkbox" checked={collapsed} onChange={e => setCollapsed(e.target.checked)} />
|
||||
<svg height="24px" viewBox="0 0 3000 3000">
|
||||
<g transform="matrix(1.50981,0,0,1.2859,-338.198,86.7981)">
|
||||
<path d="M2211,435.5C2211,349.677 2151.66,280 2078.56,280L356.438,280C283.344,280 224,349.677 224,435.5C224,521.323 283.344,591 356.438,591L2078.56,591C2151.66,591 2211,521.323 2211,435.5Z" />
|
||||
</g>
|
||||
<g transform="matrix(1.50981,0,0,1.2859,-338.198,939.991)">
|
||||
<path d="M2211,435.5C2211,349.677 2151.66,280 2078.56,280L356.438,280C283.344,280 224,349.677 224,435.5C224,521.323 283.344,591 356.438,591L2078.56,591C2151.66,591 2211,521.323 2211,435.5Z" />
|
||||
</g>
|
||||
<g transform="matrix(-1.50981,1.84899e-16,-1.57477e-16,-1.2859,3338.2,2913.2)">
|
||||
<path d="M2211,435.5C2211,349.677 2151.66,280 2078.56,280L356.438,280C283.344,280 224,349.677 224,435.5C224,521.323 283.344,591 356.438,591L2078.56,591C2151.66,591 2211,521.323 2211,435.5Z" />
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
<div className="menu">
|
||||
<NavLink className={activeClassName} to="/"><span className="item">Dashboard</span></NavLink>
|
||||
<NavLink className={activeClassName} to="/bots"><span className="item">Bots</span></NavLink>
|
||||
<NavLink className={activeClassName} to="/tokens"><span className="item">Tokens</span></NavLink>
|
||||
<NavLink className={activeClassName} to="/settings"><span className="item">Settings</span></NavLink>
|
||||
<NavLink className={activeClassName} to="/logout"><span className="item important">Logout</span></NavLink>
|
||||
<NavLink className={activeClassName} to="/404"><span className="item">(temp) 404</span></NavLink>
|
||||
<SLink to="/">Dashboard</SLink>
|
||||
<SLink to="/bots">Bots</SLink>
|
||||
<SLink to="/tokens">Tokens</SLink>
|
||||
<SLink to="/settings">Settings</SLink>
|
||||
<SLink important to="/logout">Logout</SLink>
|
||||
<SLink to="/404">(temp) 404</SLink>
|
||||
</div>
|
||||
</div>
|
||||
<div id="content">
|
||||
<div className={"w-full overflow-x-hidden " + (collapsed ? "pl-14" : "pl-48")}>
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,43 +1,61 @@
|
||||
import { useMemo, useState } from "react"
|
||||
import { createRef, useEffect, useMemo, useState } from "react"
|
||||
import { Navigate } from "react-router-dom"
|
||||
import { authorizeLogin, isLoggedIn, LoginState, useClearToken } from "../authorization"
|
||||
|
||||
export function Login() {
|
||||
var [username, setUsername] = useState("")
|
||||
var [password, setPassword] = useState("")
|
||||
var [loggedIn, setLoggedIn] = useState(isLoggedIn())
|
||||
var [loginState, setLoginState] = useState(isLoggedIn())
|
||||
|
||||
var login = () => {
|
||||
var usernameRef = createRef<HTMLInputElement>()
|
||||
var passwordRef = createRef<HTMLInputElement>()
|
||||
|
||||
var login = (e: React.ChangeEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
if (username.length === 0 || password.length === 0) {
|
||||
setLoggedIn(LoginState.Invalid)
|
||||
setLoginState(LoginState.Invalid)
|
||||
return
|
||||
}
|
||||
authorizeLogin(username, password, setLoggedIn)
|
||||
authorizeLogin(username, password, setLoginState)
|
||||
}
|
||||
|
||||
useMemo(() => {
|
||||
if (loggedIn === LoginState.Invalid) {
|
||||
setLoggedIn(LoginState.No)
|
||||
if (loginState === LoginState.Invalid) {
|
||||
setLoginState(LoginState.No)
|
||||
}
|
||||
}, [username, password])
|
||||
|
||||
useEffect(() => {
|
||||
if (loginState === LoginState.Invalid) {
|
||||
usernameRef.current?.setCustomValidity("Invalid username or password")
|
||||
passwordRef.current?.setCustomValidity("Invalid username or password")
|
||||
} else {
|
||||
usernameRef.current?.setCustomValidity("")
|
||||
passwordRef.current?.setCustomValidity("")
|
||||
}
|
||||
}, [loginState])
|
||||
|
||||
return (
|
||||
<div id="login">
|
||||
<div className="form">
|
||||
<input type="text" className={loggedIn === LoginState.Invalid ? "error" : ""} onChange={e => setUsername(e.target.value)} />
|
||||
<input type="password" className={loggedIn === LoginState.Invalid ? "error" : ""} onChange={e => setPassword(e.target.value)} />
|
||||
</div>
|
||||
<div>
|
||||
<button className="pad svg" onClick={login}>
|
||||
<svg viewBox="0 0 1000 1000">
|
||||
<path d="M619.5,899.5l350-350c27.3-27.3,27.3-71.7,0-99l-350-350c-27.3-27.3-71.7-27.3-99,0c-27.3,27.3-27.3,71.7,0,99L751,430H80c-38.7,0-70,31.3-70,70c0,38.7,31.3,70,70,70h671L520.5,800.5C506.8,814.2,500,832.1,500,850c0,17.9,6.8,35.8,20.5,49.5C547.8,926.8,592.2,926.8,619.5,899.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex w-full h-full justify-center items-center align-middle">
|
||||
<form onSubmit={login}>
|
||||
<div className="flex justify-center items-center align-middle my-2">
|
||||
<input type="text" ref={usernameRef} onChange={e => setUsername(e.target.value)}
|
||||
className="rounded-full outline-none bg-gray-800 focus:bg-gray-700 text-gray-400 focus:text-gray-200 px-3 py-1 mx-2 shadow-glow shadow-transparent invalid:outline-red-600 invalid:shadow-red-600 focus:outline-blue-500 focus:shadow-blue-500 transition-all" />
|
||||
<input type="password" ref={passwordRef} onChange={e => setPassword(e.target.value)}
|
||||
className="rounded-full outline-none bg-gray-800 focus:bg-gray-700 text-gray-400 focus:text-gray-200 px-3 py-1 mx-2 shadow-glow shadow-transparent invalid:outline-red-600 invalid:shadow-red-600 focus:outline-blue-500 focus:shadow-blue-500 transition-all" />
|
||||
</div>
|
||||
<div className="flex justify-center items-center align-middle my-2">
|
||||
<button className="rounded-full outline-none bg-gray-800 focus:bg-gray-700 hover:bg-gray-700 active:bg-gray-800 fill-gray-400 focus:fill-gray-200 px-3 py-2 shadow-glow shadow-transparent invalid:outline-red-600 invalid:shadow-red-600 focus:outline-blue-500 focus:shadow-blue-500 transition-all">
|
||||
<svg viewBox="0 0 1000 1000" height="16px">
|
||||
<path d="M619.5,899.5l350-350c27.3-27.3,27.3-71.7,0-99l-350-350c-27.3-27.3-71.7-27.3-99,0c-27.3,27.3-27.3,71.7,0,99L751,430H80c-38.7,0-70,31.3-70,70c0,38.7,31.3,70,70,70h671L520.5,800.5C506.8,814.2,500,832.1,500,850c0,17.9,6.8,35.8,20.5,49.5C547.8,926.8,592.2,926.8,619.5,899.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{
|
||||
loggedIn === LoginState.Yes && <Navigate to="/" replace={true} />
|
||||
loginState === LoginState.Yes && <Navigate to="/" replace={true} />
|
||||
}
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user