Style frontend
This commit is contained in:
parent
a11524516c
commit
220c9b917e
@ -1,14 +1,32 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from './Layout';
|
import Layout from './Layout';
|
||||||
import MainView from './MainView';
|
import { TokenProvider, TokenContext } from './tokenStorage';
|
||||||
import { TokenProvider } from './tokenStorage';
|
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||||
|
import Login from './Login';
|
||||||
|
import Index from './pages/Index';
|
||||||
|
import Page404 from './pages/Page404';
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
return <Layout>
|
return <TokenProvider>
|
||||||
<TokenProvider>
|
<Router>
|
||||||
<MainView />
|
<Layout>
|
||||||
</TokenProvider>
|
<Content />
|
||||||
</Layout>
|
</Layout>
|
||||||
|
</Router>
|
||||||
|
</TokenProvider>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Content: React.FC = () => {
|
||||||
|
const tokenStorage = React.useContext(TokenContext);
|
||||||
|
|
||||||
|
if (!tokenStorage.token) {
|
||||||
|
return <Login />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Routes>
|
||||||
|
<Route index element={<Index />} />
|
||||||
|
<Route path="*" element={<Page404 />} />
|
||||||
|
</Routes>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -1,21 +1,85 @@
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { fab } from "fontawesome.macro";
|
import { fas, fab } from "fontawesome.macro";
|
||||||
|
import React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { TokenContext } from "./tokenStorage";
|
||||||
|
|
||||||
export const Layout: React.FC = (props) => {
|
export const Layout: React.FC = (props) => {
|
||||||
return <div className="max-w-screen-lg px-6 py-4 mx-auto lg:mx-auto md:py-8 min-h-screen flex flex-col justify-between">
|
return <div className="min-h-screen flex flex-col justify-between">
|
||||||
<main className="mb-auto">{props.children}</main>
|
<Header />
|
||||||
<footer className="text-xs md:flex justify-between">
|
<div className="mb-auto flex flex-row justify-center">
|
||||||
<div>
|
<div className="w-full max-w-screen-lg py-2">
|
||||||
Frontend: <a className="portal ~neutral active" href="https://reactjs.org/"><FontAwesomeIcon icon={fab`react`} /> React</a>, <a className="portal ~neutral active" href="https://tailwindcss.com"><FontAwesomeIcon icon={fab`css3-alt`} /> tailwind</a> and <a className="portal ~neutral active" href="https://a17t.miles.land/"><div className="select-none font-black">∴</div> a17t</a>
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<a className="portal ~neutral active" href="https://git.leafbla.de/turtles-1.18.1/controlpanel"><FontAwesomeIcon icon={fab`git-alt`} /> Source</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<Footer />
|
||||||
Backend: <a className="portal ~neutral active" href="https://fastapi.tiangolo.com/"><FontAwesomeIcon icon={fab`python`} /> FastAPI</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Header: React.FC = () => {
|
||||||
|
|
||||||
|
const tokenStorage = React.useContext(TokenContext);
|
||||||
|
|
||||||
|
return <header className="px-3 py-3 flex flex-row justify-between">
|
||||||
|
<Link to="/">
|
||||||
|
<span className="shield ~info shadow">
|
||||||
|
<FontAwesomeIcon icon={fas`satellite-dish`} />
|
||||||
|
</span>
|
||||||
|
<span className="heading ml-2 inline-block h-full align-middle leading-normal">Panel of Controlness</span>
|
||||||
|
</Link>
|
||||||
|
<span>
|
||||||
|
<a className="button ~neutral mr-1" href="docs">
|
||||||
|
<FontAwesomeIcon icon={fas`book-skull`} />
|
||||||
|
<span className="ml-1">API Docs</span>
|
||||||
|
</a>
|
||||||
|
<button className="button ~neutral @high" onClick={tokenStorage.reset} disabled={tokenStorage.token ? false : true}>
|
||||||
|
<FontAwesomeIcon icon={fas`arrow-right-from-bracket`} />
|
||||||
|
<span className="ml-1">Logout</span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</header>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Footer: React.FC = () => {
|
||||||
|
return <footer className="text-xs px-6 py-6 sm:grid grid-cols-3">
|
||||||
|
<div className="text-left">
|
||||||
|
<span className="mr-1">Frontend:</span>
|
||||||
|
<FooterBadge href="https://reactjs.org" text="React">
|
||||||
|
<FontAwesomeIcon icon={fab`react`} />
|
||||||
|
</FooterBadge>
|
||||||
|
<span className="mr-1">,</span>
|
||||||
|
<FooterBadge href="https://tailwindcss.com" text="tailwind">
|
||||||
|
<FontAwesomeIcon icon={fab`css3-alt`} />
|
||||||
|
</FooterBadge>
|
||||||
|
<span className="mx-1">and</span>
|
||||||
|
<FooterBadge href="https://a17t.miles.land" text="a17t">
|
||||||
|
<span className="select-none font-black">∴</span>
|
||||||
|
</FooterBadge>
|
||||||
|
</div>
|
||||||
|
<div className="sm:text-center">
|
||||||
|
<FooterBadge href="https://git.leafbla.de/turtles-1.18.1/controlpanel" text="Source">
|
||||||
|
<FontAwesomeIcon icon={fab`git-alt`} />
|
||||||
|
</FooterBadge>
|
||||||
|
</div>
|
||||||
|
<div className="sm:text-right">
|
||||||
|
<span className="mr-1">Backend:</span>
|
||||||
|
<FooterBadge href="https://fastapi.tiangolo.com" text="FastAPI">
|
||||||
|
<FontAwesomeIcon icon={fab`python`} />
|
||||||
|
</FooterBadge>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
}
|
||||||
|
|
||||||
|
const FooterBadge: React.FC<{
|
||||||
|
text: string,
|
||||||
|
href: string,
|
||||||
|
}> = ({ text, href, children }) => {
|
||||||
|
return <a className="portal ~neutral active" href={href}>
|
||||||
|
<span className="inline-block align-middle mr-1">
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
@ -1,14 +0,0 @@
|
|||||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
|
||||||
import Index from "./pages/Index";
|
|
||||||
import Page404 from './pages/Page404';
|
|
||||||
|
|
||||||
export const MainView: React.FC = () => {
|
|
||||||
return <Router>
|
|
||||||
<Routes>
|
|
||||||
<Route index element={<Index />} />
|
|
||||||
<Route path="*" element={<Page404 />} />
|
|
||||||
</Routes>
|
|
||||||
</Router >
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MainView;
|
|
@ -1,5 +1,8 @@
|
|||||||
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { fas } from "fontawesome.macro";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Computer, State } from "../proto";
|
import { Computer, ComputerType, State } from "../proto";
|
||||||
import { TokenContext } from "../tokenStorage";
|
import { TokenContext } from "../tokenStorage";
|
||||||
|
|
||||||
export const Index: React.FC = () => {
|
export const Index: React.FC = () => {
|
||||||
@ -24,8 +27,6 @@ export const Index: React.FC = () => {
|
|||||||
}, [tokenStorage.token]);
|
}, [tokenStorage.token]);
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<button onClick={tokenStorage.reset}>Logout</button>
|
|
||||||
<p>State:</p>
|
|
||||||
{
|
{
|
||||||
state
|
state
|
||||||
? <Groups computers={state.computers} />
|
? <Groups computers={state.computers} />
|
||||||
@ -46,17 +47,17 @@ const Groups: React.FC<{ computers: Array<Computer> }> = ({ computers }) => {
|
|||||||
groupMap.get(computer.group)!.push(computer);
|
groupMap.get(computer.group)!.push(computer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>
|
return <div>
|
||||||
{
|
{
|
||||||
Array.from(groupMap.entries()).map((entry) => {
|
Array.from(groupMap.entries()).map((entry) => {
|
||||||
const [group, computers] = entry;
|
const [group, computers] = entry;
|
||||||
return <div key={group}>
|
return <div key={group} className="mt-5 first:mt-0">
|
||||||
<p>{group}:</p>
|
<h2 className="supra">{group}:</h2>
|
||||||
<CardList computers={computers} />
|
<CardList computers={computers} />
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +70,33 @@ const CardList: React.FC<{ computers: Array<Computer> }> = ({ computers }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ComputerCard: React.FC<{ computer: Computer }> = ({ computer }) => {
|
const ComputerCard: React.FC<{ computer: Computer }> = ({ computer }) => {
|
||||||
return <div>
|
|
||||||
{computer.is_advanced ? "advanced " : ""}{computer.type} {computer.label || "(no label)"}
|
const typeIcon: Map<ComputerType, IconProp> = new Map([
|
||||||
|
["computer", fas`desktop`],
|
||||||
|
["turtle", fas`pager`],
|
||||||
|
["pocket", fas`mobile-screen-button`],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return <div className="card mt-2">
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span className="inline-block align-middle">
|
||||||
|
<FontAwesomeIcon icon={typeIcon.get(computer.type) || fas`question`} />
|
||||||
|
</span>
|
||||||
|
<span className={`heading mx-2 text-lg ${computer.label ? "" : "font-extralight"}`}>
|
||||||
|
{computer.label || "(no label)"}
|
||||||
|
</span>
|
||||||
|
{computer.is_advanced && <span className="badge ~yellow @low">advanced</span>}
|
||||||
|
</p>
|
||||||
|
<p className="support">
|
||||||
|
{computer.uuid}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Offline
|
||||||
|
<span className="icon m-1 text-red-900"><FontAwesomeIcon icon={fas`ban`} /></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import Login from "./Login";
|
|
||||||
|
|
||||||
const LOCAL_STORAGE_KEY = "token";
|
const LOCAL_STORAGE_KEY = "token";
|
||||||
|
|
||||||
@ -48,10 +47,6 @@ export const TokenProvider: React.FC = (props) => {
|
|||||||
}, [tokenStorage]);
|
}, [tokenStorage]);
|
||||||
|
|
||||||
return <TokenContext.Provider value={tokenStorage}>
|
return <TokenContext.Provider value={tokenStorage}>
|
||||||
{
|
{ props.children }
|
||||||
tokenStorage.token
|
|
||||||
? props.children
|
|
||||||
: <Login />
|
|
||||||
}
|
|
||||||
</TokenContext.Provider>
|
</TokenContext.Provider>
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user