mirror of
https://github.com/Unkn0wnCat/matrix-veles.git
synced 2025-08-06 10:08:41 +02:00
webui: Add basic panel layout
This commit is contained in:
parent
45ca353b3f
commit
ac5faca859
5 changed files with 136 additions and 11 deletions
|
@ -1,5 +1,5 @@
|
||||||
import React, {useState} from 'react';
|
import React from 'react';
|
||||||
import { Routes, Route, Link } from "react-router-dom";
|
import { Routes, Route } from "react-router-dom";
|
||||||
import AuthLayout from "./layouts/AuthLayout";
|
import AuthLayout from "./layouts/AuthLayout";
|
||||||
import LoginView from "./components/auth/LoginView";
|
import LoginView from "./components/auth/LoginView";
|
||||||
import RegisterView from "./components/auth/RegisterView";
|
import RegisterView from "./components/auth/RegisterView";
|
||||||
|
@ -7,6 +7,7 @@ import RequireAuth from "./features/auth/RequireAuth";
|
||||||
import {useAppDispatch} from "./app/hooks";
|
import {useAppDispatch} from "./app/hooks";
|
||||||
import broadcastChannel from "./app/broadcastChannel";
|
import broadcastChannel from "./app/broadcastChannel";
|
||||||
import {logOut, receiveAuthUpdate} from "./features/auth/authSlice";
|
import {logOut, receiveAuthUpdate} from "./features/auth/authSlice";
|
||||||
|
import PanelLayout from "./layouts/PanelLayout";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
@ -23,10 +24,14 @@ function App() {
|
||||||
<Route path={"login"} element={<LoginView/>} />
|
<Route path={"login"} element={<LoginView/>} />
|
||||||
<Route path={"register"} element={<RegisterView/>} />
|
<Route path={"register"} element={<RegisterView/>} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={"/"} element={<RequireAuth><h1>hi</h1> <button onClick={() => {
|
<Route path={"/"} element={<PanelLayout/>}>
|
||||||
dispatch(logOut())
|
<Route path={""} element={<RequireAuth><h1>hi</h1> <button onClick={() => {
|
||||||
}
|
dispatch(logOut())
|
||||||
}>Log out</button></RequireAuth>}/>
|
}
|
||||||
|
}>Log out</button></RequireAuth>} />
|
||||||
|
<Route path={"hashing/lists"} element={<RequireAuth><h1>lists</h1></RequireAuth>} />
|
||||||
|
<Route path={"hashing/entries"} element={<RequireAuth><h1>entries</h1></RequireAuth>} />
|
||||||
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit';
|
import {createSlice, PayloadAction} from '@reduxjs/toolkit';
|
||||||
import {RootState, AppThunk} from '../../app/store';
|
import {RootState} from '../../app/store';
|
||||||
import broadcastChannel, { BroadcastMessage, sendMessage } from "../../app/broadcastChannel";
|
import { BroadcastMessage, sendMessage } from "../../app/broadcastChannel";
|
||||||
|
|
||||||
export interface AuthState {
|
export interface AuthState {
|
||||||
jwt: string|null;
|
jwt: string|null;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {Link, useLocation, useNavigate, useOutlet} from "react-router-dom";
|
||||||
import {UserPlus, User} from "lucide-react";
|
import {UserPlus, User} from "lucide-react";
|
||||||
|
|
||||||
import {ReactComponent as Logo} from "../logo.svg";
|
import {ReactComponent as Logo} from "../logo.svg";
|
||||||
import {useAppDispatch, useAppSelector} from "../app/hooks";
|
import {useAppSelector} from "../app/hooks";
|
||||||
import {selectAuth} from "../features/auth/authSlice";
|
import {selectAuth} from "../features/auth/authSlice";
|
||||||
|
|
||||||
export type AuthLocationState = {
|
export type AuthLocationState = {
|
||||||
|
@ -18,7 +18,6 @@ const AuthLayout = () => {
|
||||||
|
|
||||||
const locationState = location.state as AuthLocationState
|
const locationState = location.state as AuthLocationState
|
||||||
|
|
||||||
const dispatch = useAppDispatch()
|
|
||||||
const authState = useAppSelector(selectAuth)
|
const authState = useAppSelector(selectAuth)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
|
88
webui/src/layouts/PanelLayout.module.scss
Normal file
88
webui/src/layouts/PanelLayout.module.scss
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
@import "../globals";
|
||||||
|
|
||||||
|
.skipToContent {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
padding: var(--veles-layout-padding-slim);
|
||||||
|
background-color: var(--veles-color-background);
|
||||||
|
color: var(--veles-color-foreground);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin panelTopBarLink {
|
||||||
|
padding: var(--veles-layout-padding-slim) var(--veles-layout-padding);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
transition: color .25s;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&:hover, &.active, &:focus {
|
||||||
|
color: var(--veles-color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
margin-right: var(--veles-layout-padding);
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
stroke-width: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: var(--veles-color-background);
|
||||||
|
color: var(--veles-color-foreground);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.topBar {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: thin solid var(--veles-color-border);
|
||||||
|
|
||||||
|
a {
|
||||||
|
@include panelTopBarLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
> nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-right: thin solid var(--veles-color-border);
|
||||||
|
|
||||||
|
a {
|
||||||
|
@include panelTopBarLink;
|
||||||
|
padding: var(--veles-layout-padding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> main {
|
||||||
|
padding: var(--veles-layout-padding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
webui/src/layouts/PanelLayout.tsx
Normal file
33
webui/src/layouts/PanelLayout.tsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import {Link, NavLink, useOutlet} from "react-router-dom";
|
||||||
|
import {Home, List, ClipboardList, ExternalLink} from "lucide-react";
|
||||||
|
|
||||||
|
import {ReactComponent as Logo} from "../logo.svg";
|
||||||
|
|
||||||
|
import styles from "./PanelLayout.module.scss";
|
||||||
|
|
||||||
|
const PanelLayout = () => {
|
||||||
|
const outlet = useOutlet();
|
||||||
|
|
||||||
|
return <div className={styles.panel}>
|
||||||
|
<a href={"#main"} className={styles.skipToContent}>Jump to Content</a>
|
||||||
|
<a href={"#navigation"} className={styles.skipToContent}>Jump to Navigation</a>
|
||||||
|
<div className={styles.topBar}>
|
||||||
|
<Link to={"/"} className={styles.logo}><Logo/> <span>Matrix-Veles</span></Link>
|
||||||
|
<a href={"https://veles.1in1.net/docs/intro"} target={"_blank"} rel={"noreferrer"}><ExternalLink/> <span>Documentation</span></a>
|
||||||
|
</div>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<nav id={"navigation"}>
|
||||||
|
<NavLink to={"/"}><Home/><span>Dashboard</span></NavLink>
|
||||||
|
<NavLink to={"/hashing/lists"}><List/><span>Lists</span></NavLink>
|
||||||
|
<NavLink to={"/hashing/entries"}><ClipboardList/><span>Entries</span></NavLink>
|
||||||
|
</nav>
|
||||||
|
<main id={"main"}>
|
||||||
|
{outlet}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PanelLayout
|
Loading…
Add table
Add a link
Reference in a new issue