1
0
Fork 0

rewrote react frontend with svelte

This commit is contained in:
Kim, Jimin 2022-02-21 10:44:50 +09:00
parent c24f93c078
commit 57e2fe4ff9
50 changed files with 1202 additions and 12493 deletions

137
.gitignore vendored
View file

@ -1,136 +1,5 @@
_/
/_/
# Unmodified
# Created by https://www.toptal.com/developers/gitignore/api/firebase,node
# Edit at https://www.toptal.com/developers/gitignore?templates=firebase,node
### Firebase ###
.idea
**/node_modules/*
**/.firebaserc
### Firebase Patch ###
.runtimeconfig.json
.firebase/
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# End of https://www.toptal.com/developers/gitignore/api/firebase,node
.firebaserc
ui-debug.log

3
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"recommendations": ["svelte.svelte-vscode", "esbenp.prettier-vscode"]
}

View file

@ -1,6 +1,3 @@
# llama-bot-web-interface
![License: MIT](https://img.shields.io/github/license/llama-bot/llama-bot-web-api?style=flat-square)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
[Documentation](https://llama-bot.github.io/llama-bot-docs/docs/web-interface/overview)
## [Documentation](https://llama-bot.github.io/llama-bot-docs/docs/web-interface/overview)

24
frontend/.eslintrc.cjs Normal file
View file

@ -0,0 +1,24 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
],
plugins: ["svelte3", "@typescript-eslint"],
ignorePatterns: ["*.cjs"],
overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
settings: {
"svelte3/typescript": () => require("typescript"),
},
parserOptions: {
sourceType: "module",
ecmaVersion: 2020,
},
env: {
browser: true,
es2017: true,
node: true,
},
}

29
frontend/.gitignore vendored
View file

@ -1,26 +1,15 @@
# dependencies
/node_modules
/.pnp
.pnp.js
/build/
/.svelte-kit/
/package/
/node_modules/
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
.vscode
.idea
.env
.env.*
!.env.example
.vercel
.output
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Optional eslint cache
.eslintcache

View file

@ -1,10 +0,0 @@
import { CapacitorConfig } from "@capacitor/cli"
const config: CapacitorConfig = {
appId: "io.ionic.starter",
appName: "llama-bot-web-interface",
webDir: "build",
bundledWebRuntime: false,
}
export default config

View file

@ -1,7 +0,0 @@
{
"name": "llama-bot-web-interface",
"integrations": {
"capacitor": {}
},
"type": "react"
}

View file

@ -1,71 +1,38 @@
{
"name": "llama-bot-web-interface",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"start": "react-scripts start",
"serve": "npm run build && firebase serve",
"build": "react-scripts build",
"test": "react-scripts test",
"analyze": "source-map-explorer 'build/static/js/*.js'"
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"preview": "svelte-kit preview",
"check": "svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.15",
"evergreen-ui": "^6.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router": "^5.2.1",
"react-router-dom": "^5.3.0",
"react-spinners": "^0.11.0",
"styled-components": "^5.3.1",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
"workbox-broadcast-update": "^6.3.0",
"workbox-cacheable-response": "^6.3.0",
"workbox-core": "^6.3.0",
"workbox-expiration": "^6.3.0",
"workbox-google-analytics": "^6.3.0",
"workbox-navigation-preload": "^6.3.0",
"workbox-precaching": "^6.3.0",
"workbox-range-requests": "^6.3.0",
"workbox-routing": "^6.3.0",
"workbox-strategies": "^6.3.0",
"workbox-streams": "^6.3.0"
"@fontsource/noto-sans": "^4.5.4",
"@lukeed/uuid": "^2.0.0",
"cookie": "^0.4.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.3.0",
"@types/jest": "^27.0.2",
"@types/node": "^16.10.4",
"@types/react": "^17.0.29",
"@types/react-dom": "^17.0.9",
"@types/react-router": "^5.1.17",
"@types/react-router-dom": "^5.3.1",
"@types/styled-components": "^5.1.15",
"react-scripts": "^4.0.3",
"source-map-explorer": "^2.5.2",
"typescript": "^4.4.4"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
"@sveltejs/adapter-auto": "^1.0.0-next.17",
"@sveltejs/adapter-static": "^1.0.0-next.28",
"@sveltejs/kit": "^1.0.0-next.278",
"@types/cookie": "^0.4.1",
"@typescript-eslint/eslint-plugin": "^5.12.0",
"@typescript-eslint/parser": "^5.12.0",
"eslint": "^8.9.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-svelte3": "^3.4.0",
"prettier": "^2.5.1",
"prettier-plugin-svelte": "^2.6.0",
"sass": "^1.49.8",
"svelte": "^3.46.4",
"svelte-check": "^2.4.5",
"svelte-icons": "^2.1.0",
"svelte-loading-spinners": "^0.1.7",
"svelte-preprocess": "^4.10.3",
"tslib": "^2.3.1",
"typescript": "^4.5.5"
}
}

View file

@ -1,53 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Llama Bot</title>
<meta name="description" content="Llama bot web interface" />
<meta name="theme-color" content="#5DADEC" />
<link
rel="shortcut icon"
type="image/png"
href="%PUBLIC_URL%/assets/icon/llama-color.png"
/>
<link
rel="icon"
type="image/png"
href="%PUBLIC_URL%/assets/icon/llama-color.png"
/>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR&family=Source+Code+Pro&display=swap"
/>
<!-- IOS stuff -->
<link
rel="apple-touch-icon"
href="%PUBLIC_URL%/assets/icon/llama-color.png"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Llama Bot" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
</head>
<body>
<noscript>
Please enable JavaScript / 자바스크립트를 활성화해주세요
</noscript>
<div id="root"></div>
</body>
</html>

View file

@ -1,21 +0,0 @@
{
"short_name": "Ionic App",
"name": "My Ionic App",
"icons": [
{
"src": "assets/icon/favicon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "assets/icon/icon.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "maskable"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#ffffff",
"background_color": "#ffffff"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

View file

@ -1,8 +0,0 @@
import React from "react"
import { render } from "@testing-library/react"
import App from "./App"
test("renders without crashing", () => {
const { baseElement } = render(<App />)
expect(baseElement).toBeDefined()
})

View file

@ -1,93 +0,0 @@
import { lazy, Suspense } from "react"
import { ThemeProvider } from "evergreen-ui"
import { BrowserRouter as Router, Route } from "react-router-dom"
import styled, { createGlobalStyle, css } from "styled-components"
import Loader from "react-spinners/CircleLoader"
import Navbar from "./components/Navbar"
import Footer from "./components/Footer"
import darkTheme from "./theme/dark"
const Home = lazy(() => import("./pages/Home"))
const Servers = lazy(() => import("./pages/Servers"))
// const Dashboard = lazy(() => import("./pages/Dashboard"))
// wrapping it using css because prettier extension does not work well with styled-components
// https://github.com/styled-components/vscode-styled-components/issues/175
const _globalStyle = css`
html,
body,
#root {
font-family: "Noto Sans KR", sans-serif;
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
}
`
const GlobalStyle = createGlobalStyle`
${_globalStyle}
`
const StyledSpinContainer = styled.div`
width: 100%;
opacity: 0;
/* center elements */
display: flex;
align-content: center;
justify-content: center;
/* fade in */
animation-delay: 0.2s;
animation-duration: 2s;
animation-name: fadein;
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`
const App: React.FC = () => {
return (
<>
<GlobalStyle />
<ThemeProvider value={darkTheme}>
<Suspense
fallback={
<StyledSpinContainer>
<Loader size={150} />
</StyledSpinContainer>
}
>
<Navbar />
<Router>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/servers">
<Servers />
</Route>
{/* <Route exact path="/server/:server/dashboard">
<Dashboard />
</Route> */}
</Router>
<Footer />
</Suspense>
</ThemeProvider>
</>
)
}
export default App

15
frontend/src/app.d.ts vendored Normal file
View file

@ -0,0 +1,15 @@
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/typescript
// for information about these interfaces
declare namespace App {
interface Locals {
userid: string
}
interface Platform {}
interface Session {}
interface Stuff {}
}

15
frontend/src/app.html Normal file
View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description" content="Llama bot web interface" />
<link rel="icon" href="/assets/icon/llama-color.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%svelte.head%
</head>
<body>
%svelte.body%
</body>
</html>

View file

@ -1,50 +0,0 @@
import styled from "styled-components"
const StyledExploreContainer = styled.div`
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
strong {
font-size: 20px;
line-height: 26px;
}
p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
a {
text-decoration: none;
}
`
interface ContainerProps {
name: string
}
const ExploreContainer: React.FC<ContainerProps> = ({ name }) => {
return (
<StyledExploreContainer className="container">
<strong>{name}</strong>
<p>
Explore{" "}
<a
target="_blank"
rel="noopener noreferrer"
href="https://ionicframework.com/docs/components"
>
UI Components
</a>
</p>
</StyledExploreContainer>
)
}
export default ExploreContainer

View file

@ -1,58 +0,0 @@
import React from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faGithub } from "@fortawesome/free-brands-svg-icons"
import styled from "styled-components"
const StyledFooter = styled.div`
min-height: 5rem;
margin-top: auto;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
color: black;
background-color: rgb(0, 21, 41);
`
const StyledFooterContainer = styled.div`
width: 1500px;
color: grey;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1rem 0 1rem;
`
const GithubLink = styled.a`
font-size: 2rem;
color: grey;
&:hover {
color: white;
}
`
const Footer: React.FC = () => {
return (
<StyledFooter>
<StyledFooterContainer>
<div>
Created by <b>developomp</b>
</div>
<GithubLink
href="https://github.com/llama-bot/llama-bot-web-interface"
target="_blank"
>
<FontAwesomeIcon icon={faGithub} />
</GithubLink>
</StyledFooterContainer>
</StyledFooter>
)
}
export default Footer

View file

@ -1,123 +0,0 @@
import { useEffect, useState } from "react"
import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"
import { Button, LogOutIcon } from "evergreen-ui"
const StyledLlamaBotText = styled.b`
font-size: 1.5rem;
`
const StyledHeader = styled.div`
height: 3.75rem;
color: white;
line-height: 100%;
display: flex;
justify-items: space-between;
padding: 0 0.5rem 0 0.5rem;
div {
display: flex;
align-items: center;
}
div:nth-child(1) {
flex: 1 auto;
}
a {
color: hsla(0, 0%, 100%, 0.65);
text-decoration: none;
padding: 0 1rem;
:hover {
color: white;
}
svg {
margin-top: 0.4rem;
font-size: x-small;
}
}
`
const StyledLoginButton = styled(Button)`
color: white;
padding: 8 12 8 12;
border-radius: 5;
background-color: indianred;
:hover {
background-color: firebrick !important;
}
:active {
background-color: darkred !important;
}
`
const Navbar: React.FC = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [userName, setUserName] = useState("")
useEffect(() => {
window
.fetch("/api/user-data", { credentials: "same-origin" })
.then((data) => data.json())
.then((data) => {
setIsLoggedIn(true)
setUserName(`${data.username}#${data.discriminator}`)
})
.catch()
}, [])
return (
<StyledHeader>
<div>
<img
src={"assets/icon/llama.png"}
alt="llama logo"
style={{ width: "60px" }}
/>
<StyledLlamaBotText>Llama Bot</StyledLlamaBotText>
<a
href="https://llama-bot.github.io/llama-bot-docs/docs/web-interface/overview"
target="_"
>
Docs&nbsp;
<FontAwesomeIcon icon={faExternalLinkAlt} />
</a>
<a href="https://discord.gg/aQqamSCUcS" target="_">
Discord&nbsp;
<FontAwesomeIcon icon={faExternalLinkAlt} />
</a>
<a href="https://status.llama.developomp.com" target="_">
Status&nbsp;
<FontAwesomeIcon icon={faExternalLinkAlt} />
</a>
</div>
<div>
{isLoggedIn && `Logged in as ${userName}`}
<StyledLoginButton
appearance="mini1mal"
onClick={() => {
window.location.href = isLoggedIn
? "/api/logout"
: "/api/login"
}}
>
{isLoggedIn ? `${(<LogOutIcon />)} Logout` : "Login"}
</StyledLoginButton>
</div>
</StyledHeader>
)
}
export default Navbar

View file

@ -0,0 +1 @@
export const titlePrefix = "Llama Bot | "

View file

@ -1,8 +0,0 @@
import { createContext, Dispatch, SetStateAction } from "react"
const SidebarCollapsedContext = createContext({
isSidebarCollapsed: false,
setSidebarCollapsed: (() => {}) as Dispatch<SetStateAction<boolean>>,
})
export { SidebarCollapsedContext }

24
frontend/src/hooks.ts Normal file
View file

@ -0,0 +1,24 @@
import cookie from "cookie"
import { v4 as uuid } from "@lukeed/uuid"
import type { Handle } from "@sveltejs/kit"
export const handle: Handle = async ({ event, resolve }) => {
const cookies = cookie.parse(event.request.headers.get("cookie") || "")
event.locals.userid = cookies.userid || uuid()
const response = await resolve(event)
if (!cookies.userid) {
// if this is the first time the user has visited this app,
// set a cookie so that we recognise them when they return
response.headers.set(
"set-cookie",
cookie.serialize("userid", event.locals.userid, {
path: "/",
httpOnly: true,
})
)
}
return response
}

View file

@ -1,22 +0,0 @@
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
import * as serviceWorkerRegistration from "./serviceWorkerRegistration"
import reportWebVitals from "./reportWebVitals"
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.unregister()
// 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()

View file

@ -0,0 +1,96 @@
<script lang="ts">
import Github from "svelte-icons/fa/FaGithub.svelte"
import Discord from "svelte-icons/fa/FaDiscord.svelte"
import Heartbeat from "svelte-icons/fa/FaHeartbeat.svelte"
import Book from "svelte-icons/fa/FaBook.svelte"
</script>
<footer>
<div class="container">
<div class="created-by">
Created by <b>developomp</b>
</div>
<!-- right links -->
<div class="icons">
<a
class="icon"
alt="Documentation"
href="https://llama-bot.github.io/llama-bot-docs/docs/web-interface/overview"
target="_"
>
<Book />
</a>
<a
class="icon"
alt="Server status"
href="https://status.llama.developomp.com"
target="_"
>
<Heartbeat />
</a>
<a
class="icon"
alt="Discord server"
href="https://discord.gg/aQqamSCUcS"
target="_"
>
<Discord />
</a>
<a
class="icon"
alt="Github repository"
href="https://github.com/llama-bot"
target="_"
>
<Github />
</a>
</div>
</div>
</footer>
<style lang="scss">
footer {
--height: 8rem;
background-color: var(--dark);
height: var(--height);
display: flex;
justify-content: center;
padding: 0 var(--h-padding);
.container {
width: min(100%, var(--max-width));
height: var(--height);
display: flex;
justify-content: space-between; // spread content
align-items: center; // vertically center elements
.created-by {
color: darkgrey;
}
.icons {
height: 24px;
display: flex;
gap: 1rem;
.icon {
color: lightgrey;
&:hover {
color: white;
}
}
}
}
}
</style>

View file

@ -0,0 +1,139 @@
<script lang="ts">
import { page } from "$app/stores"
import { onMount } from "svelte"
let isLoggedIn = false
let userName = ""
onMount(() => {
// window
// .fetch("/api/user-data", { credentials: "same-origin" })
// .then((data) => data.json())
// .then((data) => {
// setIsLoggedIn(true)
// setUserName(`${data.username}#${data.discriminator}`)
// })
// .catch()
})
</script>
<header>
<nav>
<div class="left-content">
<img class="icon" src="assets/icon/llama.png" alt="llama logo" />
<b>Llama Bot</b>
<div class="links">
<a
class:active={$page.url.pathname === "/"}
sveltekit:prefetch
href="/"
>
Home
</a>
<a
class:active={$page.url.pathname === "/about"}
sveltekit:prefetch
href="/about"
>
About
</a>
</div>
</div>
<div class="login">
{#if isLoggedIn}
Logged in as {userName}
{/if}
<div class="login-button">
<a href={isLoggedIn ? "/api/logout" : "/api/login"}>
{isLoggedIn ? "Logout" : "Login"}
</a>
</div>
</div>
</nav>
</header>
<style lang="scss">
header {
--height: 4rem;
display: flex;
justify-content: center;
height: var(--height);
padding: 0 var(--h-padding);
background-color: var(--dark);
color: var(--light);
nav {
width: min(100%, var(--max-width)); // cap width
display: flex;
justify-content: space-between;
height: var(--height);
.left-content {
display: flex;
align-items: center;
img.icon {
width: 48px;
}
b {
font-size: 1.5rem;
}
.links {
display: flex;
gap: 0.8rem;
margin-left: 2rem;
a {
color: white;
text-decoration: none;
&.active {
border-bottom: 3px solid white;
}
}
}
}
.login {
display: flex;
align-items: center;
justify-content: right;
.login-button {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 0 1rem;
background-color: indianred;
&:hover {
background-color: firebrick !important;
}
&:active {
background-color: darkred !important;
}
a {
color: white;
text-decoration: none;
}
}
}
}
}
</style>

View file

@ -0,0 +1,29 @@
<script lang="ts">
import Heart from "svelte-icons/fa/FaHeart.svelte"
import List from "svelte-icons/fa/FaList.svelte"
import History from "svelte-icons/fa/FaHistory.svelte"
import Issue from "svelte-icons/fa/FaExclamationCircle.svelte"
const entries = [
{
title: "Favorites",
url: "/favorites",
icon: Heart,
},
{
title: "Modules",
url: "/modules",
icon: List,
},
{
title: "Logs",
url: "/logs",
icon: History,
},
{
title: "Incidents",
url: "/incidents",
icon: Issue,
},
]
</script>

View file

@ -1,16 +0,0 @@
import styled from "styled-components"
const StyledHome = styled.div`
text-align: center;
`
const Home: React.FC = () => {
return (
<StyledHome>
<h3>Home</h3>
<h4>Log in to list servers</h4>
</StyledHome>
)
}
export default Home

View file

@ -1,9 +0,0 @@
const Incidents: React.FC = () => {
return (
<>
<h3>Incidents</h3>
</>
)
}
export default Incidents

View file

@ -1,9 +0,0 @@
const Logs: React.FC = () => {
return (
<>
<h3>Logs</h3>
</>
)
}
export default Logs

View file

@ -1,5 +0,0 @@
const Modules: React.FC = () => {
return <h3>Modules</h3>
}
export default Modules

View file

@ -1,15 +0,0 @@
import styled from "styled-components"
const StyledServers = styled.div`
text-align: center;
`
const Servers: React.FC = () => {
return (
<StyledServers>
<h3>Available servers</h3>
</StyledServers>
)
}
export default Servers

View file

@ -1 +0,0 @@
/// <reference types="react-scripts" />

View file

@ -1,17 +0,0 @@
import { ReportHandler } from "web-vitals"
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import("web-vitals").then(
({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry)
getFID(onPerfEntry)
getFCP(onPerfEntry)
getLCP(onPerfEntry)
getTTFB(onPerfEntry)
}
)
}
}
export default reportWebVitals

View file

@ -0,0 +1,49 @@
<script lang="ts">
import Header from "$lib/Header.svelte"
import Footer from "$lib/Footer.svelte"
import { Circle2 } from "svelte-loading-spinners"
</script>
<Header />
<main>
<slot />
This loader does nothing
<Circle2 />
</main>
<Footer />
<style lang="scss" global>
@import "@fontsource/noto-sans/index.css";
:root {
/* global CSS variables */
--dark: #001529;
--light: #f0f2f5;
--max-width: 1500px;
--h-padding: 2rem;
}
html,
body {
font-family: "Noto Sans";
height: 100vh;
margin: 0;
display: flex;
flex-direction: column;
background-color: var(--light);
}
main {
display: flex;
flex-direction: column;
flex: 1;
align-items: center;
align-self: center;
width: min(100%, var(--max-width)); // cap width
}
</style>

View file

@ -0,0 +1,12 @@
<script lang="ts">
import { titlePrefix } from "../constants"
</script>
<svelte:head>
<title>{titlePrefix}About</title>
</svelte:head>
<h1>About</h1>
<style>
</style>

View file

@ -0,0 +1,12 @@
<script lang="ts">
import { titlePrefix } from "../constants"
</script>
<svelte:head>
<title>{titlePrefix}Home</title>
</svelte:head>
<h1>Home</h1>
<style>
</style>

View file

@ -1,81 +0,0 @@
/// <reference lib="webworker" />
/* eslint-disable no-restricted-globals */
// This service worker can be customized!
// See https://developers.google.com/web/tools/workbox/modules
// for the list of available Workbox modules, or add any other
// code you'd like.
// You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.
import { clientsClaim } from "workbox-core"
import { ExpirationPlugin } from "workbox-expiration"
import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching"
import { registerRoute } from "workbox-routing"
import { StaleWhileRevalidate } from "workbox-strategies"
declare const self: ServiceWorkerGlobalScope
clientsClaim()
// Precache all of the assets generated by your build process.
// Their URLs are injected into the manifest variable below.
// This variable must be present somewhere in your service worker file,
// even if you decide not to use precaching. See https://cra.link/PWA
precacheAndRoute(self.__WB_MANIFEST)
// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
const fileExtensionRegexp = new RegExp("/[^/?]+\\.[^/]+$")
registerRoute(
// Return false to exempt requests from being fulfilled by index.html.
({ request, url }: { request: Request; url: URL }) => {
// If this isn't a navigation, skip.
if (request.mode !== "navigate") {
return false
}
// If this is a URL that starts with /_, skip.
if (url.pathname.startsWith("/_")) {
return false
}
// If this looks like a URL for a resource, because it contains
// a file extension, skip.
if (url.pathname.match(fileExtensionRegexp)) {
return false
}
// Return true to signal that we want to use the handler.
return true
},
createHandlerBoundToURL(process.env.PUBLIC_URL + "/index.html")
)
// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
registerRoute(
// Add in any other file extensions or routing criteria as needed.
({ url }) =>
url.origin === self.location.origin && url.pathname.endsWith(".png"),
// Customize this strategy as needed, e.g., by changing to CacheFirst.
new StaleWhileRevalidate({
cacheName: "images",
plugins: [
// Ensure that once this runtime cache reaches a maximum size the
// least-recently used images are removed.
new ExpirationPlugin({ maxEntries: 50 }),
],
})
)
// This allows the web app to trigger skipWaiting via
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener("message", (event) => {
if (event.data && event.data.type === "SKIP_WAITING") {
self.skipWaiting()
}
})
// Any other custom service worker logic can go here.

View file

@ -1,147 +0,0 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://cra.link/PWA
const isLocalhost = Boolean(
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === "[::1]" ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
)
type Config = {
onSuccess?: (registration: ServiceWorkerRegistration) => void
onUpdate?: (registration: ServiceWorkerRegistration) => void
}
export function register(config?: Config) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href)
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return
}
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config)
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
"This web app is being served cache-first by a service " +
"worker. To learn more, visit https://cra.link/PWA"
)
})
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config)
}
})
}
}
function registerValidSW(swUrl: string, config?: Config) {
navigator.serviceWorker
.register(swUrl)
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing
if (installingWorker == null) {
return
}
installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
"New content is available and will be used when all " +
"tabs for this page are closed. See https://cra.link/PWA."
)
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration)
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log("Content is cached for offline use.")
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration)
}
}
}
}
}
})
.catch((error) => {
console.error("Error during service worker registration:", error)
})
}
function checkValidServiceWorker(swUrl: string, config?: Config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { "Service-Worker": "script" },
})
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get("content-type")
if (
response.status === 404 ||
(contentType != null &&
contentType.indexOf("javascript") === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload()
})
})
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config)
}
})
.catch(() => {
console.log(
"No internet connection found. App is running in offline mode."
)
})
}
export function unregister() {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready
.then((registration) => {
registration.unregister()
})
.catch((error) => {
console.error(error.message)
})
}
}

View file

@ -1,16 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom/extend-expect"
// Mock matchmedia
window.matchMedia =
window.matchMedia ||
function () {
return {
matches: false,
addListener: function () {},
removeListener: function () {},
}
}

View file

@ -1,7 +0,0 @@
import { defaultTheme, Theme } from "evergreen-ui"
const theme: Theme = {
...defaultTheme,
}
export default theme

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

BIN
frontend/static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

18
frontend/svelte.config.js Normal file
View file

@ -0,0 +1,18 @@
import adapter from "@sveltejs/adapter-static"
import preprocess from "svelte-preprocess"
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: preprocess(),
kit: {
adapter: adapter({
pages: "build",
assets: "build",
fallback: "index.html",
precompress: true,
}),
},
}
export default config

View file

@ -1,20 +1,41 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"module": "es2020",
"lib": ["es2020", "DOM"],
"target": "es2020",
/**
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
to enforce using \`import type\` instead of \`import\` for Types.
*/
"importsNotUsedAsValues": "error",
/**
TypeScript doesn't know about import usages in the template because it only sees the
script of a Svelte file. Therefore preserve all value imports. Requires TS 4.5 or higher.
*/
"preserveValueImports": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
"resolveJsonModule": true,
/**
To have warnings/errors of the Svelte compiler at the correct position,
enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"allowJs": true,
"checkJs": true,
"paths": {
"$lib": ["src/lib"],
"$lib/*": ["src/lib/*"]
}
},
"include": [
"src/**/*.d.ts",
"src/**/*.js",
"src/**/*.ts",
"src/**/*.svelte"
]
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,8 @@
# Compiled JavaScript files
/lib
/node_modules/
# sevrets
# Compiled JavaScript files
/lib/
# secrets
/src/secret.json
/src/firebase-adminsdk.json