summaryrefslogtreecommitdiffstats
path: root/apps/portal/src
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-06-06 15:48:53 +0200
committerivarlovlie <git@ivarlovlie.no>2022-06-06 15:48:53 +0200
commit18c458d91ca5e7187ffb3615fca8970fc6e4ca65 (patch)
treefde7f844b4d212d3892da2f72a4cfcccc56186c8 /apps/portal/src
parent44a95927edb532f8982cf24c03d9fdd129016bd6 (diff)
downloadgreatoffice-18c458d91ca5e7187ffb3615fca8970fc6e4ca65.tar.xz
greatoffice-18c458d91ca5e7187ffb3615fca8970fc6e4ca65.zip
feat: More work on portal
Diffstat (limited to 'apps/portal/src')
-rw-r--r--apps/portal/src/app/components/user-menu.svelte70
-rw-r--r--apps/portal/src/app/index.scss4
-rw-r--r--apps/portal/src/app/index.svelte35
-rw-r--r--apps/portal/src/app/pages/_layout.svelte3
-rw-r--r--apps/portal/src/app/pages/_layout@loggedin.svelte69
-rw-r--r--apps/portal/src/app/pages/home.svelte72
-rw-r--r--apps/portal/src/app/pages/login.svelte8
-rw-r--r--apps/portal/src/app/pages/profile/index.svelte16
-rw-r--r--apps/portal/src/package.json2
-rw-r--r--apps/portal/src/pnpm-lock.yaml15
-rw-r--r--apps/portal/src/tsconfig.json3
11 files changed, 283 insertions, 14 deletions
diff --git a/apps/portal/src/app/components/user-menu.svelte b/apps/portal/src/app/components/user-menu.svelte
new file mode 100644
index 0000000..b0cfc8a
--- /dev/null
+++ b/apps/portal/src/app/components/user-menu.svelte
@@ -0,0 +1,70 @@
+<script>
+ import {end_session} from "$shared/lib/session";
+ import {onMount} from "svelte";
+ import {Menu, MenuItem, MenuItemSeparator} from "$shared/components/menu";
+ import {replace} from "svelte-spa-router";
+
+ let userMenuTrigger;
+ let showUserMenu = false;
+
+ export let avatar = "";
+ export let name;
+ export let secondary = "";
+ let userMenuId;
+
+ async function on_logout() {
+ await end_session(() => {
+ replace("/login");
+ });
+ }
+
+ onMount(() => {
+ userMenuTrigger = document.getElementById("open-user-menu");
+ });
+</script>
+
+<button class="reset user-menu-control"
+ id="open-user-menu"
+ aria-controls="{userMenuId}"
+ on:click={() => showUserMenu = true}>
+ {#if avatar}
+ <figure class="user-menu-control__img-wrapper radius-50%">
+ <img class="user-menu-control__img"
+ src="{avatar}"
+ alt="Avatar">
+ </figure>
+ {/if}
+
+ <div class="margin-x-xs user-menu__meta">
+ <p class="user-menu__meta-title text-sm line-height-1 padding-y-xxxxs font-semibold color-contrast-higher text-truncate">{name}</p>
+ {#if secondary}
+ <p class="text-xs color-contrast-medium line-height-1 padding-bottom-xxxxs">{secondary}</p>
+ {/if}
+ </div>
+
+ <svg class="icon icon--xxs"
+ aria-hidden="true"
+ viewBox="0 0 12 12">
+ <polyline points="1 4 6 9 11 4"
+ fill="none"
+ stroke="currentColor"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ stroke-width="2"/>
+ </svg>
+</button>
+
+<Menu trigger={userMenuTrigger}
+ bind:id={userMenuId}
+ bind:show="{showUserMenu}">
+ <div slot="options">
+ <MenuItem on:click={() => replace("/profile")}>
+ <span>Profile</span>
+ </MenuItem>
+ <MenuItemSeparator/>
+ <MenuItem danger="true"
+ on:click={() => on_logout()}>
+ Logout
+ </MenuItem>
+ </div>
+</Menu>
diff --git a/apps/portal/src/app/index.scss b/apps/portal/src/app/index.scss
index dd4ddb6..8633a7d 100644
--- a/apps/portal/src/app/index.scss
+++ b/apps/portal/src/app/index.scss
@@ -20,3 +20,7 @@
@use '../../web-shared/src/styles/components/alert';
@use '../../web-shared/src/styles/components/details';
@use '../../web-shared/src/styles/components/light-dark-switch';
+@use '../../web-shared/src/styles/components/link-card';
+@use '../../web-shared/src/styles/components/auto-sized-grid';
+@use '../../web-shared/src/styles/components/menu';
+@use '../../web-shared/src/styles/components/user-menu';
diff --git a/apps/portal/src/app/index.svelte b/apps/portal/src/app/index.svelte
index 0082aa2..fe143bc 100644
--- a/apps/portal/src/app/index.svelte
+++ b/apps/portal/src/app/index.svelte
@@ -2,32 +2,54 @@
<svelte:window bind:online={online}/>
<script>
- import {projects_base} from "$shared/lib/configuration";
- import Router from "svelte-spa-router";
+ import Router, {replace} from "svelte-spa-router";
import {wrap} from "svelte-spa-router/wrap";
import {is_active} from "$shared/lib/session";
import SignUp from "$app/pages/sign-up.svelte";
import Login from "$app/pages/login.svelte";
import Forgot from "$app/pages/forgot.svelte";
import Reset from "$app/pages/reset-password.svelte";
+ import Home from "$app/pages/home.svelte";
+ import ProfileHome from "$app/pages/profile/index.svelte";
import PreHeader from "$shared/components/pre-header.svelte";
let online = true;
- async function user_is_logged_in() {
- if (await is_active()) {
- location.replace(projects_base("#/home"));
+ const publicRoutes = ["/login", "/signup", "/reset-password", "/forgot"];
+ const guardedRoutes = ["/", "/home", "/profile"];
+
+ async function user_is_logged_in(event) {
+ const isActive = await is_active();
+ if (!isActive && !publicRoutes.includes(event.route)) {
+ return false;
+ }
+ if (isActive && !guardedRoutes.includes(event.route)) {
+ await replace("/");
}
return true;
}
+ function route_guarded(event) {
+ if (!publicRoutes.includes(event.detail.route)) {
+ replace("/login");
+ }
+ }
+
const routes = {
"/login": wrap({
component: Login,
conditions: [user_is_logged_in],
}),
+ "/home": wrap({
+ component: Home,
+ conditions: [user_is_logged_in],
+ }),
+ "/profile": wrap({
+ component: ProfileHome,
+ conditions: [user_is_logged_in],
+ }),
"/": wrap({
- component: Login,
+ component: Home,
conditions: [user_is_logged_in],
}),
"/signup": wrap({
@@ -50,6 +72,7 @@
<Router
{routes}
restoreScrollState={true}
+ on:conditionsFailed={route_guarded}
on:routeLoading={() => {
document.getElementById("loader").style.display = "inline-block";
}}
diff --git a/apps/portal/src/app/pages/_layout.svelte b/apps/portal/src/app/pages/_layout.svelte
index d5af444..04cbdd3 100644
--- a/apps/portal/src/app/pages/_layout.svelte
+++ b/apps/portal/src/app/pages/_layout.svelte
@@ -1,5 +1,6 @@
<script>
import ThemeSwitcher from "$shared/components/theme-switcher.svelte";
+ import {frontpage_base} from "$shared/lib/configuration";
</script>
<style>
@@ -26,7 +27,7 @@
height: auto;
}
</style>
-
+
<main class="container-fluid padding-x-xs padding-x-xxl@xs padding-y-md padding-y-lg@md max-width-sm">
<div class="z-index-2 position-relative">
<slot/>
diff --git a/apps/portal/src/app/pages/_layout@loggedin.svelte b/apps/portal/src/app/pages/_layout@loggedin.svelte
new file mode 100644
index 0000000..72515df
--- /dev/null
+++ b/apps/portal/src/app/pages/_layout@loggedin.svelte
@@ -0,0 +1,69 @@
+<script>
+ import ThemeSwitcher from "$shared/components/theme-switcher.svelte";
+ import UserMenu from "$app/components/user-menu.svelte";
+ import {get_session_data} from "$shared/lib/session";
+
+ const session = get_session_data();
+</script>
+
+<style>
+ #decoration {
+ position: absolute;
+ top: 0;
+ left: 0;
+ pointer-events: none;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ z-index: 1;
+ }
+
+ #decoration svg {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+ width: 134%;
+ min-width: 1280px;
+ max-width: 1920px;
+ height: auto;
+ }
+</style>
+
+<main class="container-fluid padding-x-xs padding-x-xxl@xs padding-y-md padding-y-lg@md">
+ <div class="z-index-2 position-relative">
+ <slot/>
+ </div>
+
+ <div class="flex flex-row gap-xs position-fixed right-0 top-0 margin-md z-index-2">
+ <UserMenu name="{session.profile?.username}"/>
+ <ThemeSwitcher/>
+ </div>
+
+ <figure id="decoration"
+ aria-hidden="true">
+ <svg class="color-contrast-higher opacity-10%"
+ viewBox="0 0 1920 450"
+ fill="none">
+ <g stroke="currentColor"
+ stroke-width="2"
+ stroke-linejoin="round"
+ stroke-linecap="round">
+ <path d="M1449 94.9993V3L1354 48.9995L1259 3V94.9993L1354 140.999L1449 94.9993Z"/>
+ <path d="M1639 94.9993V3L1544 48.9995L1449 3V94.9993L1544 140.999L1639 94.9993Z"/>
+ <path d="M1354 49.0002V141"/>
+ <path d="M1544 49.0002V141"/>
+ <path d="M1449 94.9995L1544 140.999L1449 186.999L1354 140.999L1449 94.9995Z"/>
+ <path d="M1544 141V232.999L1449 278.999L1354 232.999V141"/>
+ <path d="M1449 187V279"/>
+ <path d="M1544 264L1639 310L1544 355.999L1449 310L1544 264Z"/>
+ <path d="M1639 310V402L1544 447.999L1449 402V310"/>
+ <path d="M1544 356.001V448"/>
+ <path d="M1639 94.9995L1734 140.999L1639 186.999L1544 140.999L1639 94.9995Z"/>
+ <path d="M1734 141V232.999L1639 278.999L1544 232.999V141"/>
+ <path d="M1639 187V279"/>
+ </g>
+ </svg>
+ </figure>
+</main>
diff --git a/apps/portal/src/app/pages/home.svelte b/apps/portal/src/app/pages/home.svelte
new file mode 100644
index 0000000..b9b9829
--- /dev/null
+++ b/apps/portal/src/app/pages/home.svelte
@@ -0,0 +1,72 @@
+<script>
+ import {projects_base} from "$shared/lib/configuration";
+ import {get_session_data} from "$shared/lib/session";
+ import {push} from "svelte-spa-router";
+ import Layout from "./_layout@loggedin.svelte";
+ import LinkCard from "$shared/components/link-card.svelte";
+ import {UserIcon, UsersIcon, WatchIcon} from "svelte-feather-icons";
+
+ let showUsers = true;
+ const session = get_session_data();
+</script>
+
+<Layout>
+ <div class="grid gap-md">
+ <div class="row">
+ <h1 class="margin-bottom-xs">Hello {session.profile?.username}</h1>
+ <p>This is your portal to Greatoffice, here you will find all your great apps and management options.</p>
+ </div>
+ <div class="row">
+ <h2 class="margin-bottom-xs">Apps</h2>
+ <div class="grid-auto-md gap-sm">
+ <LinkCard name="Projects"
+ description="The home for your projects"
+ text="Open"
+ target="_blank"
+ title="Open Projects in a new tab"
+ href="{projects_base()}">
+ <figure slot="icon">
+ <div class="bg-primary bg-opacity-10% padding-xs border-left border-primary border-2">
+ <WatchIcon size="42"
+ class="color-primary"
+ strokeWidth="1.2"/>
+ </div>
+ </figure>
+ </LinkCard>
+ </div>
+ </div>
+ <div class="row">
+ <h2 class="margin-bottom-xs">Manage</h2>
+ <div class="grid-auto-md gap-sm">
+ <LinkCard name="Profile"
+ description="Manage your profile information"
+ text="Open"
+ title="Go to your profile management page"
+ on:click={() => push("/profile")}>
+ <figure slot="icon">
+ <div class="bg-primary bg-opacity-10% padding-xs border-left border-primary border-2">
+ <UserIcon size="42"
+ class="color-primary"
+ strokeWidth="1.2"/>
+ </div>
+ </figure>
+ </LinkCard>
+ {#if showUsers}
+ <LinkCard name="Users"
+ description="Manage your users"
+ title="Go to your users management page"
+ text="Open"
+ href="{projects_base()}">
+ <figure slot="icon">
+ <div class="bg-primary bg-opacity-10% padding-xs border-left border-primary border-2">
+ <UsersIcon size="42"
+ class="color-primary"
+ strokeWidth="1.2"/>
+ </div>
+ </figure>
+ </LinkCard>
+ {/if}
+ </div>
+ </div>
+ </div>
+</Layout>
diff --git a/apps/portal/src/app/pages/login.svelte b/apps/portal/src/app/pages/login.svelte
index 2822be0..db010d2 100644
--- a/apps/portal/src/app/pages/login.svelte
+++ b/apps/portal/src/app/pages/login.svelte
@@ -1,7 +1,7 @@
<script>
import {onMount} from "svelte";
- import {link, querystring} from "svelte-spa-router";
- import {api_base, projects_base, IconNames} from "$shared/lib/configuration";
+ import {link, replace, querystring} from "svelte-spa-router";
+ import {api_base, projects_base, IconNames, frontpage_base} from "$shared/lib/configuration";
import Button from "$shared/components/button.svelte";
import Alert from "$shared/components/alert.svelte";
import Tile from "$shared/components/tile.svelte";
@@ -47,7 +47,7 @@
try {
const response = await login(loginForm.values);
if (response.ok) {
- location.replace(projects_base("#/home"));
+ await replace("#/home");
} else {
if (response.data.title || response.data.text) {
loginForm.alert.show("error", {
@@ -62,7 +62,6 @@
}
}
} catch (e) {
- console.error(e);
loginForm.alert.show("error", {
title: "An error occured",
text: "Could not connect to server, please check your internet connection",
@@ -93,6 +92,7 @@
</script>
<Layout>
+ <a href="{frontpage_base()}" class="block margin-bottom-xs">Go to {frontpage_base()}</a>
<Tile>
<form on:submit|preventDefault={loginForm.submit_form}
class="max-width-xxs">
diff --git a/apps/portal/src/app/pages/profile/index.svelte b/apps/portal/src/app/pages/profile/index.svelte
new file mode 100644
index 0000000..0929c3c
--- /dev/null
+++ b/apps/portal/src/app/pages/profile/index.svelte
@@ -0,0 +1,16 @@
+<script>
+ import {push} from "svelte-spa-router";
+ import {Bread, Crumb} from "$shared/components/breadcrumb/index";
+ import Layout from "$app/pages/_layout@loggedin.svelte";
+</script>
+
+<Layout>
+ <Bread>
+ <Crumb name="Home"
+ withArrow="true"
+ isLink="true"
+ on:click={() => push("/")}/>
+ <Crumb name="Profile"/>
+ </Bread>
+ <h1>Profile</h1>
+</Layout>
diff --git a/apps/portal/src/package.json b/apps/portal/src/package.json
index e969c4e..64af86e 100644
--- a/apps/portal/src/package.json
+++ b/apps/portal/src/package.json
@@ -8,8 +8,10 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "1.0.0-next.43",
+ "install": "^0.13.0",
"sass": "^1.51.0",
"svelte": "^3.48.0",
+ "svelte-feather-icons": "^4.0.0",
"svelte-preprocess": "^4.10.6",
"svelte-spa-router": "^3.2.0",
"typescript": "4.6.4",
diff --git a/apps/portal/src/pnpm-lock.yaml b/apps/portal/src/pnpm-lock.yaml
index 3b56115..0c6d187 100644
--- a/apps/portal/src/pnpm-lock.yaml
+++ b/apps/portal/src/pnpm-lock.yaml
@@ -4,8 +4,10 @@ specifiers:
'@js-temporal/polyfill': ^0.4.1
'@sveltejs/vite-plugin-svelte': 1.0.0-next.43
fuzzysort: ^1.9.0
+ install: ^0.13.0
sass: ^1.51.0
svelte: ^3.48.0
+ svelte-feather-icons: ^4.0.0
svelte-preprocess: ^4.10.6
svelte-spa-router: ^3.2.0
typescript: 4.6.4
@@ -17,8 +19,10 @@ dependencies:
devDependencies:
'@sveltejs/vite-plugin-svelte': 1.0.0-next.43_svelte@3.48.0+vite@2.9.8
+ install: 0.13.0
sass: 1.51.0
svelte: 3.48.0
+ svelte-feather-icons: 4.0.0
svelte-preprocess: 4.10.6_24ezlekk4ocevlsjgs2qnqmjum
svelte-spa-router: 3.2.0
typescript: 4.6.4
@@ -442,6 +446,11 @@ packages:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
+ /install/0.13.0:
+ resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==}
+ engines: {node: '>= 0.10'}
+ dev: true
+
/is-binary-path/2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@@ -650,6 +659,12 @@ packages:
engines: {node: '>= 0.4'}
dev: true
+ /svelte-feather-icons/4.0.0:
+ resolution: {integrity: sha512-4ieUsjp+VYa1r6y80jDt9zRiRUZyJNbESpRdHdJJhiBubyuXX96A7f1UZSK4olxzP6Qsg5ZAuyZlnmvD+/swAA==}
+ dependencies:
+ svelte: 3.48.0
+ dev: true
+
/svelte-hmr/0.14.11_svelte@3.48.0:
resolution: {integrity: sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ==}
engines: {node: ^12.20 || ^14.13.1 || >= 16}
diff --git a/apps/portal/src/tsconfig.json b/apps/portal/src/tsconfig.json
index e00d638..c60fce6 100644
--- a/apps/portal/src/tsconfig.json
+++ b/apps/portal/src/tsconfig.json
@@ -17,9 +17,6 @@
"checkJs": false,
"paths": {
"$app/*": [
- "./_public/*"
- ],
- "$app/*": [
"./app/*"
],
"$shared/*": [