Making every thing nicer

This commit is contained in:
Paul Scherer 2020-12-20 00:53:13 +01:00
parent 8ed18a9695
commit 6b0c75e5d2
19 changed files with 498 additions and 144 deletions

13
src/Icons/Account.svelte Normal file
View File

@ -0,0 +1,13 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-user"><path
d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" /></svg>

After

Width:  |  Height:  |  Size: 329 B

1
src/Icons/Admin.svelte Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-tool"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>

After

Width:  |  Height:  |  Size: 386 B

1
src/Icons/Apps.svelte Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-grid"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>

After

Width:  |  Height:  |  Size: 404 B

View File

@ -0,0 +1,12 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-moon"><path
d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" /></svg>

After

Width:  |  Height:  |  Size: 301 B

13
src/Icons/Home.svelte Normal file
View File

@ -0,0 +1,13 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-home"><path
d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" /></svg>

After

Width:  |  Height:  |  Size: 346 B

16
src/Icons/Menu.svelte Normal file
View File

@ -0,0 +1,16 @@
<svg
id="Layer_1"
style="enable-background:new 0 0 32 32;"
version="1.1"
viewBox="0 0 32 32"
width="32px"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z
M28,14H4c-1.104,0-2,0.896-2,2
s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z
M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2
S29.104,22,28,22z" />
</svg>

After

Width:  |  Height:  |  Size: 494 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shield"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>

After

Width:  |  Height:  |  Size: 279 B

View File

@ -1,3 +1,7 @@
<script lang="typescript">
import Theme from "../../components/theme";
</script>
<style>
.main {
padding: 2rem;
@ -11,34 +15,35 @@
li > a {
text-decoration: none;
}
:root {
--primary: yellow;
}
</style>
<div class="main">
<h1>Home Page</h1>
<Theme dark={true}>
<div class="main">
<h1>Home Page</h1>
<h2>About</h2>
<p>
OpenAuth is a Service to provide simple Authentication to a veriaty of
Applications. With a simple to use API and different Strategies, it can be
easily integrated into most Applications.
</p>
<h2>About</h2>
<p>
OpenAuth is a Service to provide simple Authentication to a veriaty of
Applications. With a simple to use API and different Strategies, it can be
easily integrated into most Applications.
</p>
<h2>QickLinks</h2>
<p>
If you want to manage your Account, click
<a href="user.html">here</a>
</p>
<h2>QickLinks</h2>
<p>If you want to manage your Account, click <a href="/user">here</a></p>
<h2>Applications using OpenAuth</h2>
<h2>Applications using OpenAuth</h2>
<ul>
<li>
<a href="https://ebook.stamm.me">EBook Store and Reader</a>
</li>
<li>
<a href="https://notes.hibas123.de">
Secure and Simple Notes application
</a>
</li>
</ul>
</div>
<ul>
<li><a href="https://ebook.stamm.me">EBook Store and Reader</a></li>
<li>
<a href="https://notes.hibas123.de">
Secure and Simple Notes application
</a>
</li>
</ul>
</div>
</Theme>

View File

@ -4,5 +4,3 @@ import App from "./App.svelte";
new App({
target: document.body,
});
export default app;

View File

@ -1,28 +1,77 @@
<script>
import HomePage from "./Pages/Home.svelte";
import AccountPage from "./Pages/Account.svelte";
import SecurityPage from "./Pages/Security.svelte";
import AppsPage from "./Pages/Apps.svelte";
import { slide, fade } from "svelte/transition";
import HomeIcon from "../../Icons/Home.svelte";
import AccountIcon from "../../Icons/Account.svelte";
import SecurityIcon from "../../Icons/Security.svelte";
import AppsIcon from "../../Icons/Apps.svelte";
import AdminIcon from "../../Icons/Admin.svelte";
import MenuIcon from "../../Icons/Menu.svelte";
import DarkthemeIcon from "../../Icons/Darktheme.svelte";
const pages = [
import { isAdmin } from "./api";
let dark = localStorage.getItem("theme") === "dark";
$: {
localStorage.setItem("theme", dark ? "dark" : "light");
}
let pages = [
{
id: "home",
title: "Home",
icon: HomeIcon,
component: HomePage,
},
{
id: "account",
title: "Account",
icon:
"data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyIgdmlld0JveD0iMCAwIDUwMCA1MDAiIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiPjx0aXRsZT4wMSBAZnVsbHdpZHRoPC90aXRsZT48ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz48ZyBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBza2V0Y2g6dHlwZT0iTVNQYWdlIj48ZyBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDU3LjUsMjUwIEM0NTcuNSwxMzUuOTUzMTk5IDM2NS4wNDY4MDEsNDMuNSAyNTEsNDMuNSBDMTM2Ljk1MzE5OSw0My41IDQ0LjUsMTM1Ljk1MzE5OSA0NC41LDI1MCBDNDQuNSwzNjQuMDQ2ODAxIDEzNi45NTMxOTksNDU2LjUgMjUxLDQ1Ni41IEMzNjUuMDQ2ODAxLDQ1Ni41IDQ1Ny41LDM2NC4wNDY4MDEgNDU3LjUsMjUwIFogTTU3LjUsMjUwIEM1Ny41LDE0My4xMzI5MDEgMTQ0LjEzMjkwMSw1Ni41IDI1MSw1Ni41IEMzNTcuODY3MDk5LDU2LjUgNDQ0LjUsMTQzLjEzMjkwMSA0NDQuNSwyNTAgQzQ0NC41LDM1Ni44NjcwOTkgMzU3Ljg2NzA5OSw0NDMuNSAyNTEsNDQzLjUgQzE0NC4xMzI5MDEsNDQzLjUgNTcuNSwzNTYuODY3MDk5IDU3LjUsMjUwIFoiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD48cGF0aCBkPSJNMjUxLjUsMjUyLjkzMzk2MiBDMTk2Ljg1NDE5LDI1Mi45MzM5NjIgMTUyLjUsMjk2LjM2MDgwOSAxNTIuNSwzNTAgQzE1Mi41LDM1My41ODk4NTEgMTU1LjQxMDE0OSwzNTYuNSAxNTksMzU2LjUgTDM0NCwzNTYuNSBDMzQ3LjU4OTg1MSwzNTYuNSAzNTAuNSwzNTMuNTg5ODUxIDM1MC41LDM1MCBDMzUwLjUsMjk2LjM2MDgwOSAzMDYuMTQ1ODEsMjUyLjkzMzk2MiAyNTEuNSwyNTIuOTMzOTYyIFogTTE2NS41LDM0My41MDAwMDEgQzE2NS41LDMwMy42MDI3MDggMjAzLjk3MzEzMSwyNjUuOTMzOTYyIDI1MS41LDI2NS45MzM5NjIgQzI5OS4wMjY4NjksMjY1LjkzMzk2MiAzMzcuNSwzMDMuNjAyNzA4IDMzNy40OTk5OTcsMzQzLjUwMDAwMSBMMTY1LjUsMzQzLjUwMDAwMSBaIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+PHBhdGggZD0iTTMwNC4yNSwxOTMuMzk2MjI2IEMzMDQuMjUsMTY1LjgwODIwMiAyODEuNDY1NDI0LDE0My41IDI1My40MjcwODMsMTQzLjUgQzIyNS4zODg3NDIsMTQzLjUgMjAyLjYwNDE2NywxNjUuODA4MjAyIDIwMi42MDQxNjcsMTkzLjM5NjIyNiBDMjAyLjYwNDE2NywyMjAuOTg0MjUgMjI1LjM4ODc0MiwyNDMuMjkyNDUzIDI1My40MjcwODMsMjQzLjI5MjQ1MyBDMjgxLjQ2NTQyNCwyNDMuMjkyNDUzIDMwNC4yNSwyMjAuOTg0MjUgMzA0LjI1LDE5My4zOTYyMjYgWiBNMjE1LjYwNDE2NywxOTMuMzk2MjI2IEMyMTUuNjA0MTY3LDE3My4wNTAxMDIgMjMyLjUwNzY4MywxNTYuNSAyNTMuNDI3MDgzLDE1Ni41IEMyNzQuMzQ2NDg0LDE1Ni41IDI5MS4yNSwxNzMuMDUwMTAyIDI5MS4yNSwxOTMuMzk2MjI2IEMyOTEuMjUsMjEzLjc0MjM1MSAyNzQuMzQ2NDg0LDIzMC4yOTI0NTMgMjUzLjQyNzA4MywyMzAuMjkyNDUzIEMyMzIuNTA3NjgzLDIzMC4yOTI0NTMgMjE1LjYwNDE2NywyMTMuNzQyMzUxIDIxNS42MDQxNjcsMTkzLjM5NjIyNiBaIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+PC9nPjwvZz48L3N2Zz4=",
component: AccountPage
icon: AccountIcon,
component: AccountPage,
},
{
id: "security",
title: "Security",
icon:
"data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTEyIDUxMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGQ9Ik00NDUuOCwzNy4zYy0xLjItMC43LTIuNy0wLjgtMy45LTAuMWMtNC4zLDIuMi0xMS42LDMuNC0yMS45LDMuNGMtMzMuNiwwLTkwLjgtMTIuNC0xMjguNy0yMC41Yy0xNC0zLTI2LjEtNS42LTM0LjEtNyAgIGMtMC40LTAuMS0wLjktMC4xLTEuNCwwYy03LjIsMS4yLTE4LjUsMy42LTMxLjcsNi4zQzE4NC4zLDI3LjYsMTI0LjIsNDAsOTAuNiw0MGMtMTEuNiwwLTE3LTEuNS0xOS41LTIuOCAgIGMtMS4yLTAuNi0yLjctMC42LTMuOSwwLjFzLTEuOSwyLTEuOSwzLjRjMCw3My4xLDMuOCwxNjguNCwzMy45LDI1Ny43YzE0LjMsNDIuOCwzMy41LDgwLjcsNTcuMSwxMTIuNyAgIGMyNi42LDM2LDU5LjcsNjUuNyw5OC4zLDg4LjNjMC42LDAuNCwxLjMsMC41LDIsMC41czEuNC0wLjIsMi0wLjVjMzguNi0yMi42LDcxLjYtNTIuMyw5OC4yLTg4LjNjMjMuNi0zMiw0Mi45LTY5LjksNTcuMS0xMTIuNyAgIGMyOS41LTg3LjYsMzMuNy0xNzkuNCwzMy45LTI1Ny43QzQ0Ny43LDM5LjQsNDQ3LDM4LjEsNDQ1LjgsMzcuM3ogTTQwNi4zLDI5NS45Yy0yOS4zLDg4LjEtNzkuNywxNTMuOC0xNDkuOCwxOTUuNCAgIEMxODYuNCw0NDkuNywxMzYsMzg0LDEwNi43LDI5NS45Qzc3LjgsMjEwLDczLjQsMTE4LjEsNzMuMiw0Ni40YzQuNSwxLjEsMTAuMiwxLjYsMTcuMywxLjZjMCwwLDAsMCwwLDAgICBjMzQuNSwwLDk1LjEtMTIuNiwxMzUuMi0yMC45YzEyLjctMi42LDIzLjYtNC45LDMwLjctNi4xYzcuOCwxLjQsMTkuNSwzLjksMzMuMSw2LjhDMzMwLDM2LjYsMzg1LjUsNDguNiw0MjAsNDguNiAgIGM4LjEsMCwxNC43LTAuNywxOS43LTJDNDM5LjMsMTIyLjksNDM0LjcsMjExLjUsNDA2LjMsMjk1Ljl6Ij48L3BhdGg+PHBhdGggZD0iTTI1Ni41LDIxNy44YzQ1LDAsODEuNi0zNi42LDgxLjYtODEuNmMwLTQ1LTM2LjYtODEuNi04MS42LTgxLjZjLTQ1LDAtODEuNiwzNi42LTgxLjYsODEuNiAgIEMxNzQuOSwxODEuMiwyMTEuNSwyMTcuOCwyNTYuNSwyMTcuOHogTTI1Ni41LDYyLjZjNDAuNiwwLDczLjYsMzMsNzMuNiw3My42YzAsNDAuNi0zMyw3My42LTczLjYsNzMuNmMtNDAuNiwwLTczLjYtMzMtNzMuNi03My42ICAgQzE4Mi45LDk1LjYsMjE1LjksNjIuNiwyNTYuNSw2Mi42eiI+PC9wYXRoPjxwYXRoIGQ9Ik0zMDkuMiwyMjguOUgyMDMuOGMtMjYuNSwwLTQ4LDIxLjUtNDgsNDh2NzljMCwyLjIsMS44LDQsNCw0aDE5My40YzIuMiwwLDQtMS44LDQtNHYtNzkgICBDMzU3LjIsMjUwLjQsMzM1LjYsMjI4LjksMzA5LjIsMjI4Ljl6IE0zNDkuMiwzNTEuOUgxNjMuOHYtNzVjMC0yMiwxNy45LTQwLDQwLTQwaDEwNS40YzIyLjEsMCw0MCwxNy45LDQwLDQwTDM0OS4yLDM1MS45ICAgTDM0OS4yLDM1MS45eiI+PC9wYXRoPjwvZz48L3N2Zz4=",
component: SecurityPage
}
icon: SecurityIcon,
component: SecurityPage,
},
{
id: "apps",
title: "Applications",
icon: AppsIcon,
component: AppsPage,
},
];
isAdmin().then((admin) => {
if (admin) {
pages = [
...pages,
{
id: "admin",
title: "Admin",
icon: AdminIcon,
component: HomePage,
divide: true,
subComponents: [
{
id: "admin_user",
title: "User",
component: HomePage,
},
],
},
];
}
});
function getPage() {
let pageid = window.location.hash.slice(1);
return pages.find(e => e.id === pageid) || pages[0];
return pages.find((e) => e.id === pageid) || pages[0];
}
let page = getPage();
@ -33,14 +82,14 @@
const mq = window.matchMedia("(min-width: 45rem)");
let sidebar_button = !mq.matches;
mq.addEventListener("change", ev => {
mq.addEventListener("change", (ev) => {
sidebar_button = !ev.matches;
});
let sidebar_active = false;
function setPage(pageid) {
let pg = pages.find(e => e.id === pageid);
let pg = pages.find((e) => e.id === pageid);
if (!pg) {
throw new Error("Invalid Page " + pageid);
} else {
@ -56,6 +105,7 @@
let loading = true;
import NavigationBar from "./NavigationBar.svelte";
import Theme from "../../components/theme/Theme.svelte";
</script>
<style>
@ -72,9 +122,11 @@
height: 100%;
}
.container {
.app-container {
display: grid;
height: 100%;
height: 100vh;
width: 100vw;
overflow-x: hidden;
grid-template-columns: auto 100%;
grid-template-rows: 60px auto 60px;
grid-template-areas:
@ -88,6 +140,9 @@
background-color: var(--primary);
padding: 12px;
display: flex;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
}
.header > h1 {
@ -110,12 +165,14 @@
background-color: rgba(255, 255, 255, 0.151);
}
.header > .dark-theme-btn {
}
.sidebar {
width: 0;
overflow: hidden;
grid-area: sidebar;
transition: width 0.2s;
background-color: lightgrey;
/* background-color: lightgrey; */
height: 100%;
}
@ -135,7 +192,7 @@
}
@media (min-width: 45rem) {
.container {
.app-container {
grid-template-columns: auto 1fr;
}
@ -163,45 +220,35 @@
}
</style>
<div class:loading class="root">
<div class="container">
<div class="header">
{#if sidebar_button}
<button on:click={() => (sidebar_active = !sidebar_active)}>
<svg
id="Layer_1"
style="enable-background:new 0 0 32 32;"
version="1.1"
viewBox="0 0 32 32"
width="32px"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z
M28,14H4c-1.104,0-2,0.896-2,2
s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z
M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2
S29.104,22,28,22z" />
</svg>
<Theme {dark}>
<div class:loading class="root">
<div class="app-container">
<div class="header">
{#if sidebar_button}
<button on:click={() => (sidebar_active = !sidebar_active)}>
<MenuIcon />
</button>
{/if}
<h1>{page.title}</h1>
<button on:click={() => (dark = !dark)} class="theme-btn">
<DarkthemeIcon />
</button>
{/if}
<h1>{page.title}</h1>
</div>
<div class="sidebar elv-16" class:sidebar-visible={sidebar_active}>
<NavigationBar open={setPage} {pages} active={page} />
</div>
<div class="content">
<svelte:component this={page.component} bind:loading />
</div>
<div class="footer" />
</div>
<div class="sidebar" class:sidebar-visible={sidebar_active}>
<NavigationBar open={setPage} {pages} active={page} />
</div>
<div class="content">
<svelte:component this={page.component} bind:loading />
</div>
<div class="footer" />
</div>
</div>
{#if loading}
<div class="loader_container">
<div class="loader_box">
<div class="loader" />
{#if loading}
<div class="loader_container">
<div class="loader_box">
<div class="loader" />
</div>
</div>
</div>
{/if}
{/if}
</Theme>

View File

@ -18,12 +18,17 @@
/* justify-content: center; */
}
.subcomponent {
padding: 0 var(--rel-size);
margin: 0;
margin-left: calc(var(--rel-size) * 6);
}
.active {
background: rgba(0, 0, 0, 0.1);
}
.icon {
/* float: left; */
width: calc(var(--rel-size) * 3);
height: calc(var(--rel-size) * 3);
}
@ -33,20 +38,56 @@
height: calc(var(--rel-size) * 3);
}
.icon > :global(svg) {
width: calc(var(--rel-size) * 3);
height: calc(var(--rel-size) * 3);
}
.title {
/* margin: auto; */
margin-left: var(--rel-size);
margin-left: calc(var(--rel-size) * 1.5);
/* padding-left: var(--rel-size); */
/* height: 100%; */
}
.divide {
position: relative;
margin-top: var(--rel-size);
padding-top: var(--rel-size);
}
.divide::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: 0;
height: 1px;
width: 100%; /* or 100px */
transform: scaleX(0.8);
border-top: 1px solid var(--on-background);
}
</style>
{#each pages as page}
<div
class={'container' + (page === active ? ' active' : '')}
on:click={() => open(page.id)}>
<div class="icon">
<img alt={page.title} src={page.icon} />
<div class:divide={page.divide}>
<div
class="container"
class:active={page === active}
on:click={() => open(page.id)}>
<div class="icon">
{#if typeof page.icon === 'string'}
<img alt={page.title} src={page.icon} />
{:else}
<svelte:component this={page.icon} />
{/if}
</div>
<h3 class="title">{page.title}</h3>
</div>
<h3 class="title">{page.title}</h3>
{#if page.subComponents}
{#each page.subComponents as sub}
<h3 class="subcomponent">{sub.title}</h3>
{/each}
{/if}
</div>
{/each}

View File

@ -0,0 +1,114 @@
<script lang="typescript">
import { onMount } from "svelte";
import Box from "./Box.svelte";
import BoxItem from "./BoxItem.svelte";
export let loading = false;
let modalState = false;
onMount(() => {
loading = false;
});
function modal(call: string, apiCall?: string) {
if (call == "close") {
closeModal();
}
if (call == "open") {
openModal();
}
if (apiCall) {
console.log(apiCall);
}
}
function closeModal() {
modalState = false;
}
function openModal() {
modalState = true;
}
</script>
<style>
.btn {
background-color: var(--primary);
margin: auto 0;
margin-left: 1rem;
font-size: 1rem;
padding: 0 0.5rem;
}
.btn-delete {
margin-top: 0.5rem;
}
.btn-delete > .btn {
margin: 0;
}
</style>
<Box>
<h1>Authorized Apps</h1>
<BoxItem name={'Hedgedoc'} value={'https://md.elijas.de'} highlight={false}>
<table class="table">
<tbody>
<tr>
<th>Permission</th>
<th>Description</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
</tr>
<tr>
<td>Sanna</td>
<td>Castillo</td>
</tr>
<tr>
<td>Petra</td>
<td>Serrano</td>
</tr>
</tbody>
</table>
<div class="btn-delete">
<button
on:click={() => {
modal('open');
}}
class="btn btn-secondary">
delete permissions
</button>
</div>
</BoxItem>
</Box>
{#if modalState}
<div id="modal1" class="modal">
<div class="modal-title">Delte Hedgedocs permissions</div>
<div id="modal1_content" class="modal-content">
By pressing the "delete" button you will remove all set permissions for
"Hedgedocs". This could mean an irreversible loss of all data of this
service.
</div>
<div class="modal-action">
<button
on:click={() => {
modal('close');
}}
class="btn btn-success">Close</button>
<button
on:click={() => {
modal('close', 'delete');
}}
class="btn btn-error">Delete</button>
</div>
</div>
{/if}

View File

@ -1,36 +1,33 @@
<style>
.box {
border-radius: 4px;
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.30), 0 5px 4px rgba(0, 0, 0, 0.22);
padding: 2rem;
margin-bottom: 1rem;
background-color: white;
}
.box {
border-radius: 4px;
padding: 2rem;
margin-bottom: 1rem;
}
.box> :global(h1) {
margin: 0;
margin-bottom: 1rem;
color: #444444;
font-size: 1.3rem;
}
.box > :global(h1) {
margin: 0;
margin-bottom: 1rem;
/* color: #444444; */
font-size: 1.3rem;
}
.box> :global(div) {
padding: 16px;
border-top: 1px solid var(--border-color);
word-wrap: break-word;
}
.box > :global(div) {
border-top: 1px solid var(--border-color);
word-wrap: break-word;
}
.box> :global(div):first-of-type {
border-top: none;
}
.box > :global(div):first-of-type {
border-top: none;
}
@media (min-width: 45rem) {
.box {
margin-bottom: 2rem;
}
}
@media (min-width: 45rem) {
.box {
margin-bottom: 2rem;
}
}
</style>
<div class="box">
<slot></slot>
</div>
<div class="box elv-4">
<slot />
</div>

View File

@ -12,12 +12,13 @@
<style>
.root:hover {
background-color: rgba(0, 0, 0, 0.04);
background-color: rgba(128, 128, 128, 0.1);
}
.container {
.boxitem-container {
display: flex;
flex-direction: row;
padding: 1rem;
}
.values {
@ -34,20 +35,22 @@
font-weight: 500;
}
.values > div:nth-child(2) {
/* .values > div:nth-child(2) {
color: black;
}
} */
:global(svg) {
:global(.next-icon) {
margin: auto 8px auto 8px;
height: var(--default-font-size);
min-width: var(--default-font-size);
fill: var(--on-background);
}
.body {
box-sizing: border-box;
padding: 0.1px;
margin-top: 2rem;
padding-bottom: 1rem;
}
@media (min-width: 45rem) {
@ -63,27 +66,31 @@
}
.highlight-element {
background-color: #7bff003b;
background-color: var(--success);
/* #7bff003b */
}
</style>
<div class="root" class:highlight-element={highlight}>
<div class="container" on:click={() => (open = !open)}>
<div class="root">
<div
class="boxitem-container"
on:click={() => (open = !open)}
class:highlight-element={highlight}>
<div class="values">
<div>{name}</div>
<div>
{#if Array.isArray(value)}
{#each value as v, i}
{v}
{#if i < value.length - 1}
<br />
{/if}
{/each}
{:else}{value}{/if}
<slot name="value">
{#if Array.isArray(value)}
{#each value as v, i}
{v}
{#if i < value.length - 1}<br />{/if}
{/each}
{:else}{value}{/if}
</slot>
</div>
</div>
{#if !noOpen}
<NextIcon rotation={open ? -90 : 90} />
<NextIcon class="next-icon" rotation={open ? -90 : 90} />
{/if}
</div>
{#if open && !noOpen}

View File

@ -0,0 +1,46 @@
<script lang="typescript">
import { onMount } from "svelte";
import Box from "./Box.svelte";
import BoxItem from "./BoxItem.svelte";
import { getFeatured, getName } from "../api";
export let loading = false;
const featured = getFeatured();
let name = "LOADING...";
onMount(() => {
loading = true;
getName().then((n) => {
loading = false;
name = n;
});
});
</script>
<style>
a {
color: var(--on-surface);
}
</style>
<Box>
<h1>{name}</h1>
<p>
{name}
is a
<a href="/user">OAuth2.0</a>
Service powered by
<a href="/user">OpenAuth</a>. It's goal is to bundle multiple services
logins under one central address.
</p>
<p>Services currently featured by {name}:</p>
{#await featured then clients}
{#each clients as client}
<BoxItem name={client.name} value={client.website} highlight={false}>
<a slot="value" href={client.website}>{client.website}</a>
<p>{client.description}</p>
</BoxItem>
{/each}
{/await}
</Box>

View File

@ -1,13 +1,22 @@
<script>
export let rotation;
export let rotation;
</script>
<svg style={`enable-background:new 0 0 35.414 35.414; transform: rotate(${rotation}deg); transition: all .4s;`}
version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 35.414 35.414" xml:space="preserve">
<g>
<g>
<polygon points="27.051,17 9.905,0 8.417,1.414 24.674,17.707 8.363,34 9.914,35.414 27.051,18.414 " />
</g>
</g>
</svg>
<svg
{...$$props}
style={`enable-background:new 0 0 35.414 35.414; transform: rotate(${rotation}deg); transition: all .4s;`}
version="1.1"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 35.414 35.414"
xml:space="preserve">
<g>
<g>
<polygon
points="27.051,17 9.905,0 8.417,1.414 24.674,17.707 8.363,34 9.914,35.414 27.051,18.414 " />
</g>
</g>
</svg>

View File

@ -170,7 +170,7 @@
</Box>
<Box>
<h1>Anmeldungen</h1>
<h1>Logins</h1>
{#each token as t}
<BoxItem name={t.browser} value={t.ip} highlight={t.isthis}>

View File

33
src/pages/User/api.ts Normal file
View File

@ -0,0 +1,33 @@
export async function isAdmin() {
return Promise.resolve(true);
}
export async function getName() {
if (cache.has("name")) {
return cache.get("name");
} else {
const { name } = await fetch("/api/config.json").then((res) =>
res.json()
);
cache.set("name", name);
return name;
}
}
const cache = new Map<string, any>();
export async function getFeatured() {
if (cache.has("featured")) {
return cache.get("featured");
} else {
const { clients } = await fetch("/api/client/featured").then((res) =>
res.json()
);
cache.set("featured", clients);
return clients;
}
}