diff options
Diffstat (limited to 'apps/portal')
| -rw-r--r-- | apps/portal/src/app/components/user-menu.svelte | 70 | ||||
| -rw-r--r-- | apps/portal/src/app/index.scss | 4 | ||||
| -rw-r--r-- | apps/portal/src/app/index.svelte | 35 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/_layout.svelte | 3 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/_layout@loggedin.svelte | 69 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/home.svelte | 72 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/login.svelte | 8 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/profile/index.svelte | 16 | ||||
| -rw-r--r-- | apps/portal/src/package.json | 2 | ||||
| -rw-r--r-- | apps/portal/src/pnpm-lock.yaml | 15 | ||||
| -rw-r--r-- | apps/portal/src/tsconfig.json | 3 |
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/*": [ |
