diff options
Diffstat (limited to 'apps')
35 files changed, 948 insertions, 58 deletions
diff --git a/apps/frontpage/package.json b/apps/frontpage/package.json index cde11de..6943b38 100644 --- a/apps/frontpage/package.json +++ b/apps/frontpage/package.json @@ -15,7 +15,9 @@ "@sveltejs/adapter-static": "1.0.0-next.34", "@sveltejs/kit": "next", "sass": "^1.52.2", - "svelte": "^3.44.0" + "svelte": "^3.44.0", + "svelte-preprocess": "^4.10.7", + "typescript": "^4.7.3" }, "type": "module" }
\ No newline at end of file diff --git a/apps/frontpage/pnpm-lock.yaml b/apps/frontpage/pnpm-lock.yaml index 5b1b562..a80546e 100644 --- a/apps/frontpage/pnpm-lock.yaml +++ b/apps/frontpage/pnpm-lock.yaml @@ -7,6 +7,8 @@ specifiers: '@sveltejs/kit': next sass: ^1.52.2 svelte: ^3.44.0 + svelte-preprocess: ^4.10.7 + typescript: ^4.7.3 devDependencies: '@playwright/test': 1.22.2 @@ -15,6 +17,8 @@ devDependencies: '@sveltejs/kit': 1.0.0-next.348_sass@1.52.2+svelte@3.48.0 sass: 1.52.2 svelte: 3.48.0 + svelte-preprocess: 4.10.7_qkexsrbgfwouorfsttztq6wl2m + typescript: 4.7.3 packages: @@ -147,6 +151,16 @@ packages: resolution: {integrity: sha512-UXdBxNGqTMtm7hCwh9HtncFVLrXoqA3oJW30j6XWp5BH/wu3mVeaxo7cq5benFdBw34HB3XDT2TRPI7rXZ+mDg==} dev: true + /@types/pug/2.0.6: + resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} + dev: true + + /@types/sass/1.43.1: + resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} + dependencies: + '@types/node': 17.0.40 + dev: true + /@vercel/nft/0.19.1: resolution: {integrity: sha512-klR5oN7S3WJsZz0r6Xsq7o8YlFEyU3/00VmlpZzIPVFzKfbcEjXo/sVR5lQBUqNKuOzhcbxaFtzW9aOyHjmPYA==} hasBin: true @@ -256,6 +270,10 @@ packages: fill-range: 7.0.1 dev: true + /buffer-crc32/0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -339,6 +357,11 @@ packages: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} dev: true + /detect-indent/6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + /detect-libc/1.0.3: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} engines: {node: '>=0.10'} @@ -354,6 +377,10 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true + /es6-promise/3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: true + /esbuild-android-64/0.14.42: resolution: {integrity: sha512-P4Y36VUtRhK/zivqGVMqhptSrFILAGlYp0Z8r9UQqHJ3iWztRCNWnlBzD9HRx0DbueXikzOiwyOri+ojAFfW6A==} engines: {node: '>=12'} @@ -779,6 +806,12 @@ packages: yallist: 4.0.0 dev: true + /magic-string/0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + /magic-string/0.26.2: resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==} engines: {node: '>=12'} @@ -801,6 +834,11 @@ packages: picomatch: 2.3.1 dev: true + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -1155,6 +1193,15 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /sander/0.5.1: + resolution: {integrity: sha1-dB4kXiMfB8r7b98PEzrfohalAq0=} + dependencies: + es6-promise: 3.3.1 + graceful-fs: 4.2.10 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + /sass/1.52.2: resolution: {integrity: sha512-mfHB2VSeFS7sZlPv9YohB9GB7yWIgQNTGniQwfQ04EoQN0wsQEv7SwpCwy/x48Af+Z3vDeFXz+iuXM3HK/phZQ==} engines: {node: '>=12.0.0'} @@ -1199,6 +1246,16 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /sorcery/0.10.0: + resolution: {integrity: sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=} + hasBin: true + dependencies: + buffer-crc32: 0.2.13 + minimist: 1.2.6 + sander: 0.5.1 + sourcemap-codec: 1.4.8 + dev: true + /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -1252,6 +1309,13 @@ packages: ansi-regex: 5.0.1 dev: true + /strip-indent/3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + /strip-json-comments/2.0.1: resolution: {integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo=} engines: {node: '>=0.10.0'} @@ -1271,6 +1335,58 @@ packages: svelte: 3.48.0 dev: true + /svelte-preprocess/4.10.7_qkexsrbgfwouorfsttztq6wl2m: + resolution: {integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==} + engines: {node: '>= 9.11.2'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + node-sass: '*' + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 + svelte: ^3.23.0 + typescript: ^3.9.5 || ^4.0.0 + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + node-sass: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.6 + '@types/sass': 1.43.1 + detect-indent: 6.1.0 + magic-string: 0.25.9 + sass: 1.52.2 + sorcery: 0.10.0 + strip-indent: 3.0.0 + svelte: 3.48.0 + typescript: 4.7.3 + dev: true + /svelte/3.48.0: resolution: {integrity: sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==} engines: {node: '>= 8'} @@ -1319,6 +1435,12 @@ packages: resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=} dev: true + /typescript/4.7.3: + resolution: {integrity: sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + /util-deprecate/1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} dev: true diff --git a/apps/frontpage/src/app.html b/apps/frontpage/src/app.html index 5893110..e46fbe3 100644 --- a/apps/frontpage/src/app.html +++ b/apps/frontpage/src/app.html @@ -4,6 +4,8 @@ <meta charset="utf-8"/> <link rel="icon" href="%sveltekit.assets%/favicon.png"/> + <link rel="stylesheet" + href="%sveltekit.assets%/pre.css"/> <script> const value = `; ${document.cookie}`; const parts = value.split(`; go_theme=`); diff --git a/apps/frontpage/src/hooks.ts b/apps/frontpage/src/hooks.ts new file mode 100644 index 0000000..ca80d40 --- /dev/null +++ b/apps/frontpage/src/hooks.ts @@ -0,0 +1,7 @@ +/** @type {import("@sveltejs/kit").Handle} */ +export async function handle({event, resolve}) { + const response = await resolve(event, { + ssr: false + }); + return response; +}
\ No newline at end of file diff --git a/apps/frontpage/src/routes/__layout-docs.svelte b/apps/frontpage/src/routes/__layout-docs.svelte index 6f06f52..d0a17ef 100644 --- a/apps/frontpage/src/routes/__layout-docs.svelte +++ b/apps/frontpage/src/routes/__layout-docs.svelte @@ -1,6 +1,13 @@ +<script> + import ThemeSwitcher from "$shared/components/theme-switcher.svelte"; +</script> <main class="padding-md"> <nav> <a href="/">Go back</a> </nav> <slot></slot> + <div class="position-fixed right-0 top-0 margin-md z-index-2"> + <ThemeSwitcher/> + </div> + </main> diff --git a/apps/frontpage/src/routes/__layout.svelte b/apps/frontpage/src/routes/__layout.svelte index f1a7eaa..ea37fe7 100644 --- a/apps/frontpage/src/routes/__layout.svelte +++ b/apps/frontpage/src/routes/__layout.svelte @@ -1,5 +1,6 @@ <script> import {portal_base} from "$shared/lib/configuration"; + import ThemeSwitcher from "$shared/components/theme-switcher.svelte"; import "./app.scss"; import {afterNavigate} from "$app/navigation"; import {page} from "$app/stores"; @@ -77,4 +78,8 @@ <main class="position-relative padding-sm z-index-1 flex-grow"> <slot></slot> </main> + + <div class="position-fixed right-0 top-0 margin-md z-index-2"> + <ThemeSwitcher/> + </div> </div> diff --git a/apps/frontpage/src/routes/app.scss b/apps/frontpage/src/routes/app.scss index 73a46ba..6ba6e97 100644 --- a/apps/frontpage/src/routes/app.scss +++ b/apps/frontpage/src/routes/app.scss @@ -5,3 +5,4 @@ @use '../../web-shared/src/styles/custom-style/typography'; @use '../../web-shared/src/styles/custom-style/util'; @use '../../web-shared/src/styles/components/responsive-sidebar'; +@use '../../web-shared/src/styles/components/light-dark-switch'; diff --git a/apps/frontpage/static/pre.css b/apps/frontpage/static/pre.css new file mode 100644 index 0000000..9c9446e --- /dev/null +++ b/apps/frontpage/static/pre.css @@ -0,0 +1,128 @@ +:root { + --loader-primary: hsl(250, 84%, 54%); + --loader-accent: hsl(342, 89%, 48%); + --loader-contrast: hsl(180, 1%, 84%); + --loader-easing: cubic-bezier(0.645, 0.045, 0.355, 1); +} + +[data-theme="dark"] :root { + --loader-primary: hsl(250, 93%, 65%); + --loader-accent: hsl(342, 92%, 54%); + --loader-contrast: hsl(208, 12%, 24%); + --loader-easing: cubic-bezier(0.645, 0.045, 0.355, 1); +} + +[data-theme="dark"] { + background-color: hsl(203, 24%, 13%); +} + +.fill-loader { + position: relative; + overflow: hidden; + display: inline-block; + margin: 3rem; +} + +.fill-loader__fill { + position: absolute; +} + +@supports (-webkit-animation-name: this) or (animation-name: this) { + .fill-loader__label { + position: absolute; + clip: rect(1px, 1px, 1px, 1px); + -webkit-clip-path: inset(50%); + clip-path: inset(50%); + } +} + +@supports (-webkit-animation-name: this) or (animation-name: this) { + .fill-loader--v4 { + width: 90%; + max-width: 300px; + } + + .fill-loader--v4 .fill-loader__base { + height: 4px; + background-color: var(--loader-contrast); + } + + .fill-loader--v4 .fill-loader__fill { + top: 0; + left: 0; + right: 0; + height: 100%; + background-color: var(--loader-primary); + -webkit-animation: fill-loader-4 1.6s infinite var(--loader-easing); + animation: fill-loader-4 1.6s infinite var(--loader-easing); + will-change: left, right; + } +} + +@-webkit-keyframes fill-loader-4 { + 0% { + left: 0; + right: 100%; + background-color: var(--loader-primary); + } + + 10%, + 60% { + left: 0; + } + + 40%, + 90% { + right: 0; + } + + 50% { + left: 100%; + background-color: var(--loader-primary); + } + + 51% { + left: 0; + right: 100%; + background-color: var(--loader-accent); + } + + 100% { + left: 100%; + background-color: var(--loader-accent); + } +} + +@keyframes fill-loader-4 { + 0% { + left: 0; + right: 100%; + background-color: var(--loader-primary); + } + + 10%, + 60% { + left: 0; + } + + 40%, + 90% { + right: 0; + } + + 50% { + left: 100%; + background-color: var(--loader-primary); + } + + 51% { + left: 0; + right: 100%; + background-color: var(--loader-accent); + } + + 100% { + left: 100%; + background-color: var(--loader-accent); + } +} diff --git a/apps/frontpage/svelte.config.js b/apps/frontpage/svelte.config.js index 60a9bcf..9646d6b 100644 --- a/apps/frontpage/svelte.config.js +++ b/apps/frontpage/svelte.config.js @@ -1,7 +1,9 @@ import adapter from "@sveltejs/adapter-static"; +import preprocess from "svelte-preprocess" /** @type {import("@sveltejs/kit").Config} */ const config = { + preprocess: preprocess(), kit: { prerender: { default: true, 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/*": [ diff --git a/apps/projects/src/app/index.svelte b/apps/projects/src/app/index.svelte index c217797..77d290d 100644 --- a/apps/projects/src/app/index.svelte +++ b/apps/projects/src/app/index.svelte @@ -24,7 +24,6 @@ } const queryClient = new QueryClient(); - const routes = { "/home": wrap({ @@ -47,7 +46,9 @@ "*": NotFound, }; </script> + <PreHeader show="{!online}">You seem to be offline, please check your internet connection.</PreHeader> + <QueryClientProvider client={queryClient}> <Router {routes} diff --git a/apps/projects/src/app/lib/services/user-service.ts b/apps/projects/src/app/lib/services/user-service.ts index 6e4a200..4155819 100644 --- a/apps/projects/src/app/lib/services/user-service.ts +++ b/apps/projects/src/app/lib/services/user-service.ts @@ -1,21 +1,14 @@ -import {delete_account, logout} from "$shared/lib/api/user"; import {portal_base} from "$shared/lib/configuration"; -import {clear_session_data} from "$shared/lib/session"; +import {end_session} from "$shared/lib/session"; import {clear_categories} from "$app/lib/stores/categories"; import {clear_entries} from "$app/lib/stores/entries"; import {clear_labels} from "$app/lib/stores/labels"; export async function logout_user(reason: string = "") { - await logout(); - clear_session_data(); - clear_categories(); - clear_labels(); - clear_entries(); - location.replace(portal_base("#/login" + (reason ? "?" + reason : ""))); -} - -export async function delete_user() { - await delete_account(); - clear_session_data(); - location.replace(portal_base("#/login?deleted")); + await end_session(() => { + clear_categories(); + clear_labels(); + clear_entries(); + location.replace(portal_base("#/login" + (reason ? "?" + reason : ""))); + }); } diff --git a/apps/web-shared/src/components/breadcrumb/bread.svelte b/apps/web-shared/src/components/breadcrumb/bread.svelte new file mode 100644 index 0000000..4cf0698 --- /dev/null +++ b/apps/web-shared/src/components/breadcrumb/bread.svelte @@ -0,0 +1,9 @@ +<script lang="ts"> + export type sizes = "big"|"small"; + export let size: sizes = "small"; +</script> +<nav class="breadcrumbs {size === 'small' ? 'text-sm' : 'text-lg'}"> + <ol class="flex flex-wrap gap-xxs"> + <slot></slot> + </ol> +</nav> diff --git a/apps/web-shared/src/components/breadcrumb/crumb.svelte b/apps/web-shared/src/components/breadcrumb/crumb.svelte new file mode 100644 index 0000000..7621de6 --- /dev/null +++ b/apps/web-shared/src/components/breadcrumb/crumb.svelte @@ -0,0 +1,22 @@ +<script> + export let name; + export let withArrow = false; + export let isLink = false; +</script> +<li class="breadcrumbs__item"> + <span class="color-inherit {isLink ? 'cursor-pointer color-primary-light' : ''}" + on:click>{name}</span> + {#if withArrow} + <svg class="icon margin-left-xxxs color-contrast-low" + aria-hidden="true" + viewBox="0 0 16 16"> + <polyline fill="none" + stroke="currentColor" + stroke-width="1" + stroke-linecap="round" + stroke-linejoin="round" + stroke-miterlimit="10" + points="6.5,3.5 11,8 6.5,12.5 "></polyline> + </svg> + {/if} +</li> diff --git a/apps/web-shared/src/components/breadcrumb/index.ts b/apps/web-shared/src/components/breadcrumb/index.ts new file mode 100644 index 0000000..485ed7b --- /dev/null +++ b/apps/web-shared/src/components/breadcrumb/index.ts @@ -0,0 +1,7 @@ +import Bread from "./bread.svelte"; +import Crumb from "./crumb.svelte"; + +export { + Bread, + Crumb +}; diff --git a/apps/web-shared/src/components/link-card.svelte b/apps/web-shared/src/components/link-card.svelte new file mode 100644 index 0000000..0c15a53 --- /dev/null +++ b/apps/web-shared/src/components/link-card.svelte @@ -0,0 +1,47 @@ +<script> + export let text = "View"; + export let name; + export let description = null; + export let href = null; + export let target; + export let title = null; +</script> + +<a class="link-card flex flex-column bg-light cursor-pointer radius-md {$$restProps.class??''}" + {href} + {target} + {title} + on:click + aria-label="Link label"> + <div class="padding-md"> + <div class="flex flex-wrap gap-xs items-center"> + <slot name="icon"></slot> + <div class="line-height-xs"> + <p class="text-lg font-semibold color-contrast-higher">{name}</p> + {#if description} + <p class="color-contrast-low margin-top-xxxs">{description}</p> + {/if} + </div> + </div> + </div> + <div class="link-card__footer margin-top-auto border-top border-contrast-lower"> + <p class="text-sm">{text}</p> + <div> + <svg class="icon icon--sm" + viewBox="0 0 24 24"> + <g fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"> + <line x1="3" + y1="12" + x2="21" + y2="12"/> + <polyline points="15 6 21 12 15 18"/> + </g> + </svg> + </div> + </div> +</a> + diff --git a/apps/web-shared/src/components/menu/menu.svelte b/apps/web-shared/src/components/menu/menu.svelte index 33b1160..33517ab 100644 --- a/apps/web-shared/src/components/menu/menu.svelte +++ b/apps/web-shared/src/components/menu/menu.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import {random_string} from "$shared/lib/helpers"; - export let id = "__menu_" + random_string(3); + export const id = "__menu_" + random_string(3); export let trigger: HTMLElement; export let show = false; diff --git a/apps/web-shared/src/components/theme-switcher.svelte b/apps/web-shared/src/components/theme-switcher.svelte index 26ae507..d0a43b3 100644 --- a/apps/web-shared/src/components/theme-switcher.svelte +++ b/apps/web-shared/src/components/theme-switcher.svelte @@ -42,7 +42,8 @@ } </script> -<div class="ld-switch" data-theme-switcher-element> +<div class="ld-switch" + data-theme-switcher-element> <button class="reset ld-switch-btn" on:click={() => show =!show}> <span class="sr-only">{selection}</span> @@ -64,39 +65,46 @@ </svg> {:else if selection === "light"} <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"> - <title>light-auto</title> + viewBox="0 0 20 20"><title>light</title> <g fill="currentColor"> - <path d="M10 14a4 4 0 1 1 3.465-6" - fill-opacity=".2" + <circle cx="10" + cy="10" + r="4" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></circle> + <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" - stroke-width="2"></path> + stroke-width="2" + d="M10 1v1.5"></path> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" - d="M12 18l2.5-7h1l2.5 7"></path> + d="M16.364 3.636l-1.061 1.061"></path> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" - d="M12.714 16h4.572"></path> + d="M19 10h-1.5"></path> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" - d="M10 1v1.5"></path> + d="M16.364 16.364l-1.061-1.061"></path> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" - d="M16.364 3.636l-1.061 1.061"></path> + d="M10 19v-1.5"></path> <path fill="none" stroke="currentColor" stroke-linecap="round" @@ -119,9 +127,14 @@ </svg> {:else } <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"> - <title>dark-auto</title> + viewBox="0 0 20 20"><title>light-auto</title> <g fill="currentColor"> + <path d="M10 14a4 4 0 1 1 3.465-6" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> <path fill="none" stroke="currentColor" stroke-linecap="round" @@ -134,15 +147,36 @@ stroke-linejoin="round" stroke-width="2" d="M12.714 16h4.572"></path> - <path d="M12.146 10.159A2.5 2.5 0 0 1 14.5 8.5h1a2.5 2.5 0 0 1 1.412.441 7 7 0 0 0-4.948-5.657c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a6.99 6.99 0 0 0 6.427 5.012z" - fill-opacity=".2"></path> - <path d="M16.71 8a7.015 7.015 0 0 0-4.746-4.716c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036A7.006 7.006 0 0 0 9 16.929" - fill="none" + <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" - stroke-width="2"></path> - <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + stroke-width="2" + d="M10 1v1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M16.364 3.636l-1.061 1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 16.364l1.061-1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M1 10h1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 3.636l1.061 1.061"></path> </g> </svg> {/if} @@ -371,7 +405,6 @@ <figure class="radius-md inner-glow" on:click={() => change("system")}> <svg id="Layer_1" - class="block radius-inherit" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="70" diff --git a/apps/web-shared/src/components/user-menu.svelte b/apps/web-shared/src/components/user-menu.svelte new file mode 100644 index 0000000..c0195f4 --- /dev/null +++ b/apps/web-shared/src/components/user-menu.svelte @@ -0,0 +1,99 @@ +<script> + import {onMount} from "svelte"; + import {Menu, MenuItem, MenuItemSeparator} from "./menu"; + + let userMenuTrigger; + let showUserMenu = false; + + export let avatar = ""; + export let name; + export let secondary = ""; + let userMenuId; + + 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="User picture"> + </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> + <svg class="icon menu__icon" + aria-hidden="true" + viewBox="0 0 16 16"> + <circle cx="8" + cy="3.5" + r="3.5"/> + <path d="M14.747,14.15a6.995,6.995,0,0,0-13.494,0A1.428,1.428,0,0,0,1.5,15.4a1.531,1.531,0,0,0,1.209.6H13.288a1.531,1.531,0,0,0,1.209-.6A1.428,1.428,0,0,0,14.747,14.15Z"/> + </svg> + <span>Profile</span> + + </MenuItem> + + <MenuItem> + <svg class="icon menu__icon" + aria-hidden="true" + viewBox="0 0 16 16"> + <g fill="currentColor"> + <path d="M15.135,6.784c-1.303-0.326-1.921-1.818-1.23-2.969c0.322-0.536,0.225-0.998-0.094-1.316l-0.31-0.31 c-0.318-0.318-0.78-0.415-1.316-0.094c-1.152,0.691-2.644,0.073-2.969-1.23C9.065,0.258,8.669,0,8.219,0H7.781 c-0.45,0-0.845,0.258-0.997,0.865c-0.326,1.303-1.818,1.921-2.969,1.23C3.279,1.773,2.816,1.87,2.498,2.188l-0.31,0.31 C1.87,2.816,1.773,3.279,2.095,3.815c0.691,1.152,0.073,2.644-1.23,2.969C0.26,6.935,0,7.33,0,7.781v0.438 c0,0.45,0.258,0.845,0.865,0.997c1.303,0.326,1.921,1.818,1.23,2.969c-0.322,0.536-0.225,0.998,0.094,1.316l0.31,0.31 c0.319,0.319,0.782,0.415,1.316,0.094c1.152-0.691,2.644-0.073,2.969,1.23C6.935,15.742,7.331,16,7.781,16h0.438 c0.45,0,0.845-0.258,0.997-0.865c0.326-1.303,1.818-1.921,2.969-1.23c0.535,0.321,0.997,0.225,1.316-0.094l0.31-0.31 c0.318-0.318,0.415-0.78,0.094-1.316c-0.691-1.152-0.073-2.644,1.23-2.969C15.742,9.065,16,8.669,16,8.219V7.781 C16,7.33,15.74,6.935,15.135,6.784z M8,11c-1.657,0-3-1.343-3-3s1.343-3,3-3s3,1.343,3,3S9.657,11,8,11z"></path> + </g> + </svg> + <span>Settings</span> + </MenuItem> + + <MenuItem> + <svg class="icon menu__icon" + aria-hidden="true" + viewBox="0 0 16 16"> + <circle cx="10.5" + cy="2.5" + r="2.5"/> + <circle cx="5.5" + cy="6.5" + r="2.5"/> + <path d="M15.826,10.124A5.455,5.455,0,0,0,9.46,6.107,3.932,3.932,0,0,1,9.5,6.5,3.97,3.97,0,0,1,8.452,9.176,6.963,6.963,0,0,1,11.539,12h2.829a1.5,1.5,0,0,0,1.458-1.876Z"/> + <path d="M10.826,14.124a5.5,5.5,0,0,0-10.652,0A1.5,1.5,0,0,0,1.632,16H9.368a1.5,1.5,0,0,0,1.458-1.876Z"/> + </svg> + <span>Team</span> + </MenuItem> + + <MenuItemSeparator/> + + <MenuItem danger="true" on:click={() => logout_user()}> + Logout + </MenuItem> + </div> +</Menu> diff --git a/apps/web-shared/src/lib/helpers.ts b/apps/web-shared/src/lib/helpers.ts index f2d0cca..4da8254 100644 --- a/apps/web-shared/src/lib/helpers.ts +++ b/apps/web-shared/src/lib/helpers.ts @@ -51,9 +51,7 @@ export function get_cookie(name) { } export function set_cookie(name, value, baseDomain = window.location.host) { - let asdf = name + "=" + encodeURIComponent(value) + (baseDomain ? ";domain=" + baseDomain : ""); - console.log(asdf); - document.cookie = asdf; + document.cookie = name + "=" + encodeURIComponent(value) + (baseDomain ? ";domain=" + baseDomain : ""); } export function unwrap_date_time_from_entry(entry: TimeEntryDto): UnwrappedEntryDateTime { diff --git a/apps/web-shared/src/lib/session.ts b/apps/web-shared/src/lib/session.ts index 4f40a17..f729687 100644 --- a/apps/web-shared/src/lib/session.ts +++ b/apps/web-shared/src/lib/session.ts @@ -1,5 +1,5 @@ import {Temporal} from "@js-temporal/polyfill"; -import {get_profile_for_active_check} from "./api/user"; +import {get_profile_for_active_check, logout} from "./api/user"; import {is_guid, session_storage_get_json, session_storage_set_json} from "./helpers"; import {SECONDS_BETWEEN_SESSION_CHECK, StorageKeys} from "./configuration"; import type {ISession} from "$shared/lib/models/ISession"; @@ -21,6 +21,12 @@ export async function is_active(forceRefresh: boolean = false): Promise<boolean> } } +export async function end_session(cb: Function): Promise<void> { + await logout(); + clear_session_data(); + cb(); +} + async function call_api(): Promise<boolean> { console.log("Getting profile data while checking session state"); try { diff --git a/apps/web-shared/src/styles/components/auto-sized-grid.scss b/apps/web-shared/src/styles/components/auto-sized-grid.scss new file mode 100644 index 0000000..a3b1be5 --- /dev/null +++ b/apps/web-shared/src/styles/components/auto-sized-grid.scss @@ -0,0 +1,56 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_auto-sized-grid +Title: Auto Sized Grid +Descr: A grid layout based on CSS Grid where the columns are automatically created according to a min-width value +Usage: codyhouse.co/license + +-------------------------------- */ + +.grid-auto-xs, .grid-auto-sm, .grid-auto-md, .grid-auto-lg, .grid-auto-xl { + display: grid; + gap: var(--gap-y, 0px) var(--gap-x, 0px); + grid-template-columns: repeat(auto-fit, minmax(var(--col-min-width), 1fr)); // auto add new cols +} + +.grid-auto-xs { + --col-min-width: 8rem; +} + +.grid-auto-sm { + --col-min-width: 10rem; +} + +.grid-auto-md { + --col-min-width: 15rem; +} + +.grid-auto-lg { + --col-min-width: 20rem; +} + +.grid-auto-xl { + --col-min-width: 25rem; +} + +@each $breakpoint, $value in $breakpoints { + @include breakpoint(#{$breakpoint}) { + .grid-auto-xs\@#{$breakpoint} { + --col-min-width: 8rem; + } + .grid-auto-sm\@#{$breakpoint} { + --col-min-width: 10rem; + } + .grid-auto-md\@#{$breakpoint} { + --col-min-width: 15rem; + } + .grid-auto-lg\@#{$breakpoint} { + --col-min-width: 20rem; + } + .grid-auto-xl\@#{$breakpoint} { + --col-min-width: 25rem; + } + } +} diff --git a/apps/web-shared/src/styles/components/breadcrumbs.scss b/apps/web-shared/src/styles/components/breadcrumbs.scss new file mode 100644 index 0000000..45bf7c6 --- /dev/null +++ b/apps/web-shared/src/styles/components/breadcrumbs.scss @@ -0,0 +1,18 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_breadcrumbs +Title: Breadcrumbs +Descr: List of links to help the user move within website structure +Usage: codyhouse.co/license + +-------------------------------- */ + +.breadcrumbs {} + +.breadcrumbs__item { + display: inline-block; // flex fallback + display: inline-flex; + align-items: center; +} diff --git a/apps/web-shared/src/styles/components/link-card.scss b/apps/web-shared/src/styles/components/link-card.scss new file mode 100644 index 0000000..dad4f98 --- /dev/null +++ b/apps/web-shared/src/styles/components/link-card.scss @@ -0,0 +1,56 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_link-card +Title: Link Card +Descr: Link card for app UI +Usage: codyhouse.co/license + +-------------------------------- */ + +.link-card { + text-decoration: none; + color: inherit; + box-shadow: var(--inner-glow), var(--shadow-xs); + + &:hover { + box-shadow: var(--inner-glow), var(--shadow-sm); + } +} + +.link-card__footer { + position: relative; + overflow: hidden; + height: 60px; + + > * { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + } + + > *:last-child { + transform: translateY(100%); + opacity: 0; + } +} + +.link-card:hover { + .link-card__footer { + > *:first-child { + transform: translateY(-100%); + opacity: 0; + } + + > *:last-child { + transform: translateY(0); + opacity: 1; + } + } +} diff --git a/apps/web-shared/src/styles/components/user-menu.scss b/apps/web-shared/src/styles/components/user-menu.scss index 1b5c1d5..416655f 100644 --- a/apps/web-shared/src/styles/components/user-menu.scss +++ b/apps/web-shared/src/styles/components/user-menu.scss @@ -42,7 +42,7 @@ Usage: codyhouse.co/license width: var(--profile-figure-size); height: var(--profile-figure-size); position: relative; - transition: opacity 0.2s; + //transition: opacity 0.2s; &::after { content: ''; @@ -61,7 +61,7 @@ Usage: codyhouse.co/license opacity: 0; transform: scale(0.8); - transition: all 0.2s; + //transition: all 0.2s; } } @@ -73,9 +73,9 @@ Usage: codyhouse.co/license } .user-menu__meta { - max-width: 100px; + //max-width: 100px; } .user-menu__meta-title { - transition: color 0.2s; + //transition: color 0.2s; } |
