Making every thing nicer
13
src/Icons/Account.svelte
Normal 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
@ -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
@ -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 |
12
src/Icons/Darktheme.svelte
Normal 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
@ -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
@ -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 |
1
src/Icons/Security.svelte
Normal 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 |
@ -1,3 +1,7 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import Theme from "../../components/theme";
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.main {
|
.main {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
@ -11,9 +15,14 @@
|
|||||||
li > a {
|
li > a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary: yellow;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="main">
|
<Theme dark={true}>
|
||||||
|
<div class="main">
|
||||||
<h1>Home Page</h1>
|
<h1>Home Page</h1>
|
||||||
|
|
||||||
<h2>About</h2>
|
<h2>About</h2>
|
||||||
@ -24,21 +33,17 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>QickLinks</h2>
|
<h2>QickLinks</h2>
|
||||||
<p>
|
<p>If you want to manage your Account, click <a href="/user">here</a></p>
|
||||||
If you want to manage your Account, click
|
|
||||||
<a href="user.html">here</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Applications using OpenAuth</h2>
|
<h2>Applications using OpenAuth</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li><a href="https://ebook.stamm.me">EBook Store and Reader</a></li>
|
||||||
<a href="https://ebook.stamm.me">EBook Store and Reader</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<a href="https://notes.hibas123.de">
|
<a href="https://notes.hibas123.de">
|
||||||
Secure and Simple Notes application
|
Secure and Simple Notes application
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</Theme>
|
||||||
|
@ -4,5 +4,3 @@ import App from "./App.svelte";
|
|||||||
new App({
|
new App({
|
||||||
target: document.body,
|
target: document.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default app;
|
|
||||||
|
@ -1,28 +1,77 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import HomePage from "./Pages/Home.svelte";
|
||||||
import AccountPage from "./Pages/Account.svelte";
|
import AccountPage from "./Pages/Account.svelte";
|
||||||
import SecurityPage from "./Pages/Security.svelte";
|
import SecurityPage from "./Pages/Security.svelte";
|
||||||
|
import AppsPage from "./Pages/Apps.svelte";
|
||||||
import { slide, fade } from "svelte/transition";
|
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",
|
id: "account",
|
||||||
title: "Account",
|
title: "Account",
|
||||||
icon:
|
icon: AccountIcon,
|
||||||
"data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyIgdmlld0JveD0iMCAwIDUwMCA1MDAiIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiPjx0aXRsZT4wMSBAZnVsbHdpZHRoPC90aXRsZT48ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz48ZyBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBza2V0Y2g6dHlwZT0iTVNQYWdlIj48ZyBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDU3LjUsMjUwIEM0NTcuNSwxMzUuOTUzMTk5IDM2NS4wNDY4MDEsNDMuNSAyNTEsNDMuNSBDMTM2Ljk1MzE5OSw0My41IDQ0LjUsMTM1Ljk1MzE5OSA0NC41LDI1MCBDNDQuNSwzNjQuMDQ2ODAxIDEzNi45NTMxOTksNDU2LjUgMjUxLDQ1Ni41IEMzNjUuMDQ2ODAxLDQ1Ni41IDQ1Ny41LDM2NC4wNDY4MDEgNDU3LjUsMjUwIFogTTU3LjUsMjUwIEM1Ny41LDE0My4xMzI5MDEgMTQ0LjEzMjkwMSw1Ni41IDI1MSw1Ni41IEMzNTcuODY3MDk5LDU2LjUgNDQ0LjUsMTQzLjEzMjkwMSA0NDQuNSwyNTAgQzQ0NC41LDM1Ni44NjcwOTkgMzU3Ljg2NzA5OSw0NDMuNSAyNTEsNDQzLjUgQzE0NC4xMzI5MDEsNDQzLjUgNTcuNSwzNTYuODY3MDk5IDU3LjUsMjUwIFoiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD48cGF0aCBkPSJNMjUxLjUsMjUyLjkzMzk2MiBDMTk2Ljg1NDE5LDI1Mi45MzM5NjIgMTUyLjUsMjk2LjM2MDgwOSAxNTIuNSwzNTAgQzE1Mi41LDM1My41ODk4NTEgMTU1LjQxMDE0OSwzNTYuNSAxNTksMzU2LjUgTDM0NCwzNTYuNSBDMzQ3LjU4OTg1MSwzNTYuNSAzNTAuNSwzNTMuNTg5ODUxIDM1MC41LDM1MCBDMzUwLjUsMjk2LjM2MDgwOSAzMDYuMTQ1ODEsMjUyLjkzMzk2MiAyNTEuNSwyNTIuOTMzOTYyIFogTTE2NS41LDM0My41MDAwMDEgQzE2NS41LDMwMy42MDI3MDggMjAzLjk3MzEzMSwyNjUuOTMzOTYyIDI1MS41LDI2NS45MzM5NjIgQzI5OS4wMjY4NjksMjY1LjkzMzk2MiAzMzcuNSwzMDMuNjAyNzA4IDMzNy40OTk5OTcsMzQzLjUwMDAwMSBMMTY1LjUsMzQzLjUwMDAwMSBaIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+PHBhdGggZD0iTTMwNC4yNSwxOTMuMzk2MjI2IEMzMDQuMjUsMTY1LjgwODIwMiAyODEuNDY1NDI0LDE0My41IDI1My40MjcwODMsMTQzLjUgQzIyNS4zODg3NDIsMTQzLjUgMjAyLjYwNDE2NywxNjUuODA4MjAyIDIwMi42MDQxNjcsMTkzLjM5NjIyNiBDMjAyLjYwNDE2NywyMjAuOTg0MjUgMjI1LjM4ODc0MiwyNDMuMjkyNDUzIDI1My40MjcwODMsMjQzLjI5MjQ1MyBDMjgxLjQ2NTQyNCwyNDMuMjkyNDUzIDMwNC4yNSwyMjAuOTg0MjUgMzA0LjI1LDE5My4zOTYyMjYgWiBNMjE1LjYwNDE2NywxOTMuMzk2MjI2IEMyMTUuNjA0MTY3LDE3My4wNTAxMDIgMjMyLjUwNzY4MywxNTYuNSAyNTMuNDI3MDgzLDE1Ni41IEMyNzQuMzQ2NDg0LDE1Ni41IDI5MS4yNSwxNzMuMDUwMTAyIDI5MS4yNSwxOTMuMzk2MjI2IEMyOTEuMjUsMjEzLjc0MjM1MSAyNzQuMzQ2NDg0LDIzMC4yOTI0NTMgMjUzLjQyNzA4MywyMzAuMjkyNDUzIEMyMzIuNTA3NjgzLDIzMC4yOTI0NTMgMjE1LjYwNDE2NywyMTMuNzQyMzUxIDIxNS42MDQxNjcsMTkzLjM5NjIyNiBaIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+PC9nPjwvZz48L3N2Zz4=",
|
component: AccountPage,
|
||||||
component: AccountPage
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "security",
|
id: "security",
|
||||||
title: "Security",
|
title: "Security",
|
||||||
icon:
|
icon: SecurityIcon,
|
||||||
"data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTEyIDUxMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGQ9Ik00NDUuOCwzNy4zYy0xLjItMC43LTIuNy0wLjgtMy45LTAuMWMtNC4zLDIuMi0xMS42LDMuNC0yMS45LDMuNGMtMzMuNiwwLTkwLjgtMTIuNC0xMjguNy0yMC41Yy0xNC0zLTI2LjEtNS42LTM0LjEtNyAgIGMtMC40LTAuMS0wLjktMC4xLTEuNCwwYy03LjIsMS4yLTE4LjUsMy42LTMxLjcsNi4zQzE4NC4zLDI3LjYsMTI0LjIsNDAsOTAuNiw0MGMtMTEuNiwwLTE3LTEuNS0xOS41LTIuOCAgIGMtMS4yLTAuNi0yLjctMC42LTMuOSwwLjFzLTEuOSwyLTEuOSwzLjRjMCw3My4xLDMuOCwxNjguNCwzMy45LDI1Ny43YzE0LjMsNDIuOCwzMy41LDgwLjcsNTcuMSwxMTIuNyAgIGMyNi42LDM2LDU5LjcsNjUuNyw5OC4zLDg4LjNjMC42LDAuNCwxLjMsMC41LDIsMC41czEuNC0wLjIsMi0wLjVjMzguNi0yMi42LDcxLjYtNTIuMyw5OC4yLTg4LjNjMjMuNi0zMiw0Mi45LTY5LjksNTcuMS0xMTIuNyAgIGMyOS41LTg3LjYsMzMuNy0xNzkuNCwzMy45LTI1Ny43QzQ0Ny43LDM5LjQsNDQ3LDM4LjEsNDQ1LjgsMzcuM3ogTTQwNi4zLDI5NS45Yy0yOS4zLDg4LjEtNzkuNywxNTMuOC0xNDkuOCwxOTUuNCAgIEMxODYuNCw0NDkuNywxMzYsMzg0LDEwNi43LDI5NS45Qzc3LjgsMjEwLDczLjQsMTE4LjEsNzMuMiw0Ni40YzQuNSwxLjEsMTAuMiwxLjYsMTcuMywxLjZjMCwwLDAsMCwwLDAgICBjMzQuNSwwLDk1LjEtMTIuNiwxMzUuMi0yMC45YzEyLjctMi42LDIzLjYtNC45LDMwLjctNi4xYzcuOCwxLjQsMTkuNSwzLjksMzMuMSw2LjhDMzMwLDM2LjYsMzg1LjUsNDguNiw0MjAsNDguNiAgIGM4LjEsMCwxNC43LTAuNywxOS43LTJDNDM5LjMsMTIyLjksNDM0LjcsMjExLjUsNDA2LjMsMjk1Ljl6Ij48L3BhdGg+PHBhdGggZD0iTTI1Ni41LDIxNy44YzQ1LDAsODEuNi0zNi42LDgxLjYtODEuNmMwLTQ1LTM2LjYtODEuNi04MS42LTgxLjZjLTQ1LDAtODEuNiwzNi42LTgxLjYsODEuNiAgIEMxNzQuOSwxODEuMiwyMTEuNSwyMTcuOCwyNTYuNSwyMTcuOHogTTI1Ni41LDYyLjZjNDAuNiwwLDczLjYsMzMsNzMuNiw3My42YzAsNDAuNi0zMyw3My42LTczLjYsNzMuNmMtNDAuNiwwLTczLjYtMzMtNzMuNi03My42ICAgQzE4Mi45LDk1LjYsMjE1LjksNjIuNiwyNTYuNSw2Mi42eiI+PC9wYXRoPjxwYXRoIGQ9Ik0zMDkuMiwyMjguOUgyMDMuOGMtMjYuNSwwLTQ4LDIxLjUtNDgsNDh2NzljMCwyLjIsMS44LDQsNCw0aDE5My40YzIuMiwwLDQtMS44LDQtNHYtNzkgICBDMzU3LjIsMjUwLjQsMzM1LjYsMjI4LjksMzA5LjIsMjI4Ljl6IE0zNDkuMiwzNTEuOUgxNjMuOHYtNzVjMC0yMiwxNy45LTQwLDQwLTQwaDEwNS40YzIyLjEsMCw0MCwxNy45LDQwLDQwTDM0OS4yLDM1MS45ICAgTDM0OS4yLDM1MS45eiI+PC9wYXRoPjwvZz48L3N2Zz4=",
|
component: SecurityPage,
|
||||||
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() {
|
function getPage() {
|
||||||
let pageid = window.location.hash.slice(1);
|
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();
|
let page = getPage();
|
||||||
@ -33,14 +82,14 @@
|
|||||||
|
|
||||||
const mq = window.matchMedia("(min-width: 45rem)");
|
const mq = window.matchMedia("(min-width: 45rem)");
|
||||||
let sidebar_button = !mq.matches;
|
let sidebar_button = !mq.matches;
|
||||||
mq.addEventListener("change", ev => {
|
mq.addEventListener("change", (ev) => {
|
||||||
sidebar_button = !ev.matches;
|
sidebar_button = !ev.matches;
|
||||||
});
|
});
|
||||||
|
|
||||||
let sidebar_active = false;
|
let sidebar_active = false;
|
||||||
|
|
||||||
function setPage(pageid) {
|
function setPage(pageid) {
|
||||||
let pg = pages.find(e => e.id === pageid);
|
let pg = pages.find((e) => e.id === pageid);
|
||||||
if (!pg) {
|
if (!pg) {
|
||||||
throw new Error("Invalid Page " + pageid);
|
throw new Error("Invalid Page " + pageid);
|
||||||
} else {
|
} else {
|
||||||
@ -56,6 +105,7 @@
|
|||||||
let loading = true;
|
let loading = true;
|
||||||
|
|
||||||
import NavigationBar from "./NavigationBar.svelte";
|
import NavigationBar from "./NavigationBar.svelte";
|
||||||
|
import Theme from "../../components/theme/Theme.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -72,9 +122,11 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.app-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
grid-template-columns: auto 100%;
|
grid-template-columns: auto 100%;
|
||||||
grid-template-rows: 60px auto 60px;
|
grid-template-rows: 60px auto 60px;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
@ -88,6 +140,9 @@
|
|||||||
background-color: var(--primary);
|
background-color: var(--primary);
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header > h1 {
|
.header > h1 {
|
||||||
@ -110,12 +165,14 @@
|
|||||||
background-color: rgba(255, 255, 255, 0.151);
|
background-color: rgba(255, 255, 255, 0.151);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header > .dark-theme-btn {
|
||||||
|
}
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 0;
|
width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
grid-area: sidebar;
|
grid-area: sidebar;
|
||||||
transition: width 0.2s;
|
transition: width 0.2s;
|
||||||
background-color: lightgrey;
|
/* background-color: lightgrey; */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +192,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 45rem) {
|
@media (min-width: 45rem) {
|
||||||
.container {
|
.app-container {
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: auto 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,32 +220,21 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class:loading class="root">
|
<Theme {dark}>
|
||||||
<div class="container">
|
<div class:loading class="root">
|
||||||
|
<div class="app-container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
{#if sidebar_button}
|
{#if sidebar_button}
|
||||||
<button on:click={() => (sidebar_active = !sidebar_active)}>
|
<button on:click={() => (sidebar_active = !sidebar_active)}>
|
||||||
<svg
|
<MenuIcon />
|
||||||
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>
|
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
<h1>{page.title}</h1>
|
<h1>{page.title}</h1>
|
||||||
|
<button on:click={() => (dark = !dark)} class="theme-btn">
|
||||||
|
<DarkthemeIcon />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar" class:sidebar-visible={sidebar_active}>
|
<div class="sidebar elv-16" class:sidebar-visible={sidebar_active}>
|
||||||
<NavigationBar open={setPage} {pages} active={page} />
|
<NavigationBar open={setPage} {pages} active={page} />
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -196,12 +242,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="footer" />
|
<div class="footer" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<div class="loader_container">
|
<div class="loader_container">
|
||||||
<div class="loader_box">
|
<div class="loader_box">
|
||||||
<div class="loader" />
|
<div class="loader" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
</Theme>
|
||||||
|
@ -18,12 +18,17 @@
|
|||||||
/* justify-content: center; */
|
/* justify-content: center; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subcomponent {
|
||||||
|
padding: 0 var(--rel-size);
|
||||||
|
margin: 0;
|
||||||
|
margin-left: calc(var(--rel-size) * 6);
|
||||||
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
background: rgba(0, 0, 0, 0.1);
|
background: rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
/* float: left; */
|
|
||||||
width: calc(var(--rel-size) * 3);
|
width: calc(var(--rel-size) * 3);
|
||||||
height: calc(var(--rel-size) * 3);
|
height: calc(var(--rel-size) * 3);
|
||||||
}
|
}
|
||||||
@ -33,20 +38,56 @@
|
|||||||
height: calc(var(--rel-size) * 3);
|
height: calc(var(--rel-size) * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon > :global(svg) {
|
||||||
|
width: calc(var(--rel-size) * 3);
|
||||||
|
height: calc(var(--rel-size) * 3);
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
/* margin: auto; */
|
/* margin: auto; */
|
||||||
margin-left: var(--rel-size);
|
margin-left: calc(var(--rel-size) * 1.5);
|
||||||
|
/* padding-left: var(--rel-size); */
|
||||||
/* height: 100%; */
|
/* 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>
|
</style>
|
||||||
|
|
||||||
{#each pages as page}
|
{#each pages as page}
|
||||||
|
<div class:divide={page.divide}>
|
||||||
<div
|
<div
|
||||||
class={'container' + (page === active ? ' active' : '')}
|
class="container"
|
||||||
|
class:active={page === active}
|
||||||
on:click={() => open(page.id)}>
|
on:click={() => open(page.id)}>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
|
{#if typeof page.icon === 'string'}
|
||||||
<img alt={page.title} src={page.icon} />
|
<img alt={page.title} src={page.icon} />
|
||||||
|
{:else}
|
||||||
|
<svelte:component this={page.icon} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<h3 class="title">{page.title}</h3>
|
<h3 class="title">{page.title}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
{#if page.subComponents}
|
||||||
|
{#each page.subComponents as sub}
|
||||||
|
<h3 class="subcomponent">{sub.title}</h3>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
114
src/pages/User/Pages/Apps.svelte
Normal 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}
|
@ -1,26 +1,23 @@
|
|||||||
<style>
|
<style>
|
||||||
.box {
|
.box {
|
||||||
border-radius: 4px;
|
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;
|
padding: 2rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
background-color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.box> :global(h1) {
|
.box > :global(h1) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
color: #444444;
|
/* color: #444444; */
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box> :global(div) {
|
.box > :global(div) {
|
||||||
padding: 16px;
|
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box> :global(div):first-of-type {
|
.box > :global(div):first-of-type {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +28,6 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="box">
|
<div class="box elv-4">
|
||||||
<slot></slot>
|
<slot />
|
||||||
</div>
|
</div>
|
@ -12,12 +12,13 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.root:hover {
|
.root:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.04);
|
background-color: rgba(128, 128, 128, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.boxitem-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.values {
|
.values {
|
||||||
@ -34,20 +35,22 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.values > div:nth-child(2) {
|
/* .values > div:nth-child(2) {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
} */
|
||||||
|
|
||||||
:global(svg) {
|
:global(.next-icon) {
|
||||||
margin: auto 8px auto 8px;
|
margin: auto 8px auto 8px;
|
||||||
height: var(--default-font-size);
|
height: var(--default-font-size);
|
||||||
min-width: var(--default-font-size);
|
min-width: var(--default-font-size);
|
||||||
|
fill: var(--on-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0.1px;
|
padding: 0.1px;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 45rem) {
|
@media (min-width: 45rem) {
|
||||||
@ -63,27 +66,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.highlight-element {
|
.highlight-element {
|
||||||
background-color: #7bff003b;
|
background-color: var(--success);
|
||||||
|
/* #7bff003b */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="root" class:highlight-element={highlight}>
|
<div class="root">
|
||||||
<div class="container" on:click={() => (open = !open)}>
|
<div
|
||||||
|
class="boxitem-container"
|
||||||
|
on:click={() => (open = !open)}
|
||||||
|
class:highlight-element={highlight}>
|
||||||
<div class="values">
|
<div class="values">
|
||||||
<div>{name}</div>
|
<div>{name}</div>
|
||||||
<div>
|
<div>
|
||||||
|
<slot name="value">
|
||||||
{#if Array.isArray(value)}
|
{#if Array.isArray(value)}
|
||||||
{#each value as v, i}
|
{#each value as v, i}
|
||||||
{v}
|
{v}
|
||||||
{#if i < value.length - 1}
|
{#if i < value.length - 1}<br />{/if}
|
||||||
<br />
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
{:else}{value}{/if}
|
{:else}{value}{/if}
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if !noOpen}
|
{#if !noOpen}
|
||||||
<NextIcon rotation={open ? -90 : 90} />
|
<NextIcon class="next-icon" rotation={open ? -90 : 90} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if open && !noOpen}
|
{#if open && !noOpen}
|
||||||
|
46
src/pages/User/Pages/Home.svelte
Normal 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>
|
@ -2,12 +2,21 @@
|
|||||||
export let rotation;
|
export let rotation;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svg style={`enable-background:new 0 0 35.414 35.414; transform: rotate(${rotation}deg); transition: all .4s;`}
|
<svg
|
||||||
version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
{...$$props}
|
||||||
y="0px" viewBox="0 0 35.414 35.414" xml:space="preserve">
|
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>
|
||||||
<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 " />
|
<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>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
@ -170,7 +170,7 @@
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<h1>Anmeldungen</h1>
|
<h1>Logins</h1>
|
||||||
|
|
||||||
{#each token as t}
|
{#each token as t}
|
||||||
<BoxItem name={t.browser} value={t.ip} highlight={t.isthis}>
|
<BoxItem name={t.browser} value={t.ip} highlight={t.isthis}>
|
||||||
|
0
src/pages/User/Pages/admin/Admin.svelte
Normal file
33
src/pages/User/api.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|