diff options
Diffstat (limited to 'apps/portal/src/app/pages')
| -rw-r--r-- | apps/portal/src/app/pages/_layout.svelte | 62 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/_layout@loggedin.svelte | 75 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/admin/index.svelte | 18 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/forgot.svelte | 102 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/home.svelte | 103 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/login.svelte | 142 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/profile/index.svelte | 167 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/reset-password.svelte | 138 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/sign-up.svelte | 131 |
9 files changed, 0 insertions, 938 deletions
diff --git a/apps/portal/src/app/pages/_layout.svelte b/apps/portal/src/app/pages/_layout.svelte deleted file mode 100644 index 8c75cb9..0000000 --- a/apps/portal/src/app/pages/_layout.svelte +++ /dev/null @@ -1,62 +0,0 @@ -<script> - import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte"; -</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> -<BlowoutToolbelt/> - -<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/> - </div> - - <figure id="decoration" - class="z-index-1" - 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/_layout@loggedin.svelte b/apps/portal/src/app/pages/_layout@loggedin.svelte deleted file mode 100644 index 44e2e4a..0000000 --- a/apps/portal/src/app/pages/_layout@loggedin.svelte +++ /dev/null @@ -1,75 +0,0 @@ -<script> - import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte"; - import {end_session, get_session_data} from "$shared/lib/session"; - import {replace} from "svelte-spa-router"; - - 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> -<BlowoutToolbelt/> -<main class="container max-width-xl 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 left-0 top-0 margin-md z-index-2"> - <span on:click={async () => { - if (confirm("Are you sure?")) await end_session(() => { - replace("/login"); - }) - }} class="btn btn--sm"> - Logout - </span> - </div> - - <figure id="decoration" - class="z-index-1" - 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/admin/index.svelte b/apps/portal/src/app/pages/admin/index.svelte deleted file mode 100644 index f9b91d2..0000000 --- a/apps/portal/src/app/pages/admin/index.svelte +++ /dev/null @@ -1,18 +0,0 @@ -<script> - import Layout from "../_layout@loggedin.svelte"; - import {Bread, Crumb} from "$shared/components/breadcrumb"; - import {push} from "svelte-spa-router"; - -</script> - -<Layout> - <Bread> - <Crumb name="Home" - withArrow="true" - isLink="true" - on:click={() => push("/")}/> - <Crumb name="Organisation"/> - </Bread> - - <main class="max-width-sm"></main> -</Layout> diff --git a/apps/portal/src/app/pages/forgot.svelte b/apps/portal/src/app/pages/forgot.svelte deleted file mode 100644 index 156deab..0000000 --- a/apps/portal/src/app/pages/forgot.svelte +++ /dev/null @@ -1,102 +0,0 @@ -<script> - import {onMount} from "svelte"; - import {link} from "svelte-spa-router"; - import {create_forgot_password_request} from "$shared/lib/api/user"; - import {is_email} from "$shared/lib/helpers"; - import Alert from "$shared/components/alert.svelte"; - import Button from "$shared/components/button.svelte"; - import Tile from "$shared/components/tile.svelte"; - import Layout from "./_layout.svelte"; - - let isLoading = false; - let username; - - const alert = { - title: "", - type: "", - message: "", - isVisible: false, - show(type, obj) { - alert.title = obj.title; - alert.message = obj.text; - alert.type = type; - alert.isVisible = true; - isLoading = false; - }, - hide() { - alert.isVisible = false; - alert.title = ""; - alert.message = ""; - alert.type = ""; - isLoading = false; - }, - }; - - function is_valid() { - return is_email(username); - } - - async function submit_form() { - if (isLoading) { - return; - } - if (is_valid()) { - isLoading = true; - const response = await create_forgot_password_request(username); - if (response.ok) { - alert.show("success", { - title: "Request is sent", - text: "If we find an account associated with this email address, you will receive an email with a reset link very soon.", - }); - } else { - console.error(response.data); - alert.show("error", { - title: response.data?.title ?? "An error occured", - text: response.data?.text ?? "Please try again soon", - }); - } - } - } - - onMount(() => { - document.addEventListener("DOMContentLoaded", () => { - document.getElementById("email-address").focus(); - }); - }); -</script> - -<Layout> - <Tile> - <form on:submit|preventDefault={submit_form} - class="max-width-xxs"> - <fieldset> - <legend class="form-legend"> - <span class="margin-bottom-xs text-xl">Send reset link</span> <br/> - <span class="text-sm">... or <a href="/login" - use:link>log in</a></span> - </legend> - <div class="margin-bottom-xs"> - <p>Provide your email address, and we'll send you a link to set your new password.</p> - </div> - <div class="margin-bottom-xxs max-width-xxs"> - <Alert visible={alert.isVisible} - title={alert.title} - message={alert.message} - type={alert.type}/> - </div> - <div class="margin-bottom-xs"> - <input type="email" - id="email-address" - placeholder="Email address" - class="form-control width-100%" - bind:value={username}/> - </div> - <div class="flex justify-end"> - <Button text="Send reset link" - type="primary" - loading={isLoading}/> - </div> - </fieldset> - </form> - </Tile> -</Layout> diff --git a/apps/portal/src/app/pages/home.svelte b/apps/portal/src/app/pages/home.svelte deleted file mode 100644 index 0e325ee..0000000 --- a/apps/portal/src/app/pages/home.svelte +++ /dev/null @@ -1,103 +0,0 @@ -<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 Alert from "$shared/components/alert.svelte"; - import {UserIcon, UsersIcon, WatchIcon, SendIcon, ListIcon} from "svelte-feather-icons"; - - let showUsers = true; - const session = get_session_data(); -</script> - -<Layout> - <div class="grid gap-md"> - <div class="row"> - <Alert closeable="true" - closeableCooldown="~" - id="welcome-note" - title="Hello {session.profile?.username}" - message="This is your portal to Greatoffice, here you will find all your great apps and management options."/> - </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 in a new tab" - title="Open Projects" - 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> - <LinkCard name="Tickets" - description="The home for your tickets" - class="c-disabled user-select-none" - text="Coming soon" - title="Open Tickets" - href="{projects_base()}"> - <figure slot="icon"> - <div class="bg-primary bg-opacity-10% padding-xs border-left border-primary border-2"> - <SendIcon size="42" - class="color-primary" - strokeWidth="1.2"/> - </div> - </figure> - </LinkCard> - <LinkCard name="Todo" - description="The home for your todos" - class="c-disabled user-select-none" - text="Coming soon" - title="Open Todo" - href="{projects_base()}"> - <figure slot="icon"> - <div class="bg-primary bg-opacity-10% padding-xs border-left border-primary border-2"> - <ListIcon 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" - 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="Organisation" - description="Manage your organisation" - title="Go to your organisations management page" - text="Open" - on:click={() => push("/admin")}> - <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 deleted file mode 100644 index 1ca6b61..0000000 --- a/apps/portal/src/app/pages/login.svelte +++ /dev/null @@ -1,142 +0,0 @@ -<script> - import {onMount} from "svelte"; - import {link, replace, querystring} from "svelte-spa-router"; - import {api_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"; - import {login} from "$shared/lib/api/user"; - import {is_email} from "$shared/lib/helpers"; - import Layout from "./_layout.svelte"; - - const loginForm = { - loading: false, - values: { - username: "", - password: "", - persist: true - }, - alert: { - title: "", - type: "", - message: "", - isVisible: false, - show(type, obj) { - loginForm.alert.title = obj.title; - loginForm.alert.message = obj.text; - loginForm.alert.type = type; - loginForm.alert.isVisible = true; - loginForm.loading = false; - }, - hide() { - loginForm.alert.isVisible = false; - loginForm.alert.title = ""; - loginForm.alert.message = ""; - loginForm.alert.type = ""; - }, - }, - is_valid() { - return is_email(loginForm.values.username) && loginForm.values.password.length > 0; - }, - async submit_form() { - if (loginForm.loading) { - return; - } - if (loginForm.is_valid()) { - loginForm.alert.hide(); - loginForm.loading = true; - try { - const response = await login(loginForm.values); - if (response.ok) { - await replace("#/home"); - } else { - if (response.data.title || response.data.text) { - loginForm.alert.show("error", { - title: response.data.title ?? "", - text: response.data.text ?? "", - }); - } else { - loginForm.alert.show("error", { - title: "An unknown error occured", - text: "Try again soon", - }); - } - } - } catch (e) { - loginForm.alert.show("error", { - title: "An error occured", - text: "Could not connect to server, please check your internet connection", - }); - } - } else { - loginForm.alert.show("error", { - title: "Invalid form", - }); - } - }, - }; - - onMount(() => { - if ($querystring === "deleted") { - loginForm.alert.show("info", { - title: "Account deleted", - text: "Your account and all its data was successfully deleted.", - }); - } - if ($querystring === "expired") { - loginForm.alert.show("info", { - title: "Session expired", - text: "Your session has expired, feel free to log in again.", - }); - } - }); -</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"> - <fieldset> - <legend class="form-legend"> - <span class="margin-bottom-xs text-xl">Log into your account</span> - <br/> - <span class="text-sm">... or <a href="/signup" - use:link>create a new one</a></span> - </legend> - <div class="margin-bottom-xxs max-width-xxs"> - <Alert visible={loginForm.alert.isVisible} - title={loginForm.alert.title} - message={loginForm.alert.message} - type={loginForm.alert.type}/> - </div> - <div class="margin-bottom-xxs"> - <input type="email" - placeholder="Email address" - class="form-control width-100%" - id="username" - bind:value={loginForm.values.username}/> - </div> - <div class="margin-bottom-xxs"> - <input type="password" - placeholder="Password" - id="password" - class="form-control width-100%" - bind:value={loginForm.values.password}/> - <div class="flex justify-end"> - <a tabindex="-1" - class="text-sm" - href="/forgot" - use:link>Reset password</a> - </div> - </div> - <div class="flex justify-between"> - <Button text="Login" - type="submit" - variant="primary" - loading={loginForm.loading}/> - </div> - </fieldset> - </form> - </Tile> -</Layout> diff --git a/apps/portal/src/app/pages/profile/index.svelte b/apps/portal/src/app/pages/profile/index.svelte deleted file mode 100644 index a7291d6..0000000 --- a/apps/portal/src/app/pages/profile/index.svelte +++ /dev/null @@ -1,167 +0,0 @@ -<script> - import {push} from "svelte-spa-router"; - import {Bread, Crumb} from "$shared/components/breadcrumb/index"; - import Layout from "$app/pages/_layout@loggedin.svelte"; - import {update_profile} from "$shared/lib/api/user"; - import Alert from "$shared/components/alert.svelte"; - import Button from "$shared/components/button.svelte"; - import {is_email} from "$shared/lib/helpers"; - import {api_base} from "$shared/lib/configuration"; - import {get_session_data} from "$shared/lib/session"; - - const archiveLink = api_base("_/account/archive"); - - let modal; - let understands = false; - - let formIsLoading = false; - let formError; - - let username = get_session_data()?.profile.username; - let usernameFieldMessage; - let usernameFieldMessageClass = "color-error"; - - let password; - let passwordFieldMessage; - let passwordFieldMessageClass = "color-error"; - - async function submit_form(e) { - e.preventDefault(); - if (!username && !password) { - console.error("Not submitting because both values is empty"); - return; - } - - usernameFieldMessage = ""; - passwordFieldMessage = ""; - - if (username && !is_email(username)) { - usernameFieldMessage = "Username has to be a valid email"; - return; - } - - if (password && password?.length < 6) { - passwordFieldMessage = "The new password must contain at least 6 characters"; - return; - } - - formIsLoading = true; - - const response = await update_profile({ - username, - password, - }); - - formIsLoading = false; - - if (response.ok) { - if (password) { - passwordFieldMessage = "Successfully updated"; - passwordFieldMessageClass = "color-success"; - password = ""; - } - if (username) { - usernameFieldMessage = "Successfully updated"; - usernameFieldMessageClass = "color-success"; - password = ""; - } - } else { - formError = response.data.title ?? "An unknown error occured"; - } - } - - async function handle_delete_account_button_click() { - alert("Not implemented"); - return; - if (understands && confirm("Are you absolutely sure that you want to delete your account?")) { - } - } - - export const functions = { - open() { - modal.open(); - }, - close() { - // modal.close(); - }, - }; -</script> - -<Layout> - <Bread> - <Crumb name="Home" - withArrow="true" - isLink="true" - on:click={() => push("/")}/> - <Crumb name="Profile"/> - </Bread> - - <main class="max-width-sm"> - <section class="margin-bottom-md"> - <p class="text-md margin-bottom-sm">Update your information</p> - <form on:submit={submit_form} - autocomplete="new-password"> - {#if formError} - <small class="color-danger">{formError}</small> - {/if} - <div class="margin-bottom-sm"> - <label for="email" - class="form-label margin-bottom-xxs">New username</label> - <input type="email" - class="form-control width-100%" - id="email" - placeholder={username} - bind:value={username}/> - {#if usernameFieldMessage} - <small class={usernameFieldMessageClass}>{usernameFieldMessage}</small> - {/if} - </div> - <div class="margin-bottom-sm"> - <label for="password" - class="form-label margin-bottom-xxs">New password</label> - <input type="password" - class="form-control width-100%" - id="password" - bind:value={password}/> - {#if passwordFieldMessage} - <small class={passwordFieldMessageClass}>{passwordFieldMessage}</small> - {/if} - </div> - <div class="flex justify-end"> - <Button text="Save" - on:click={submit_form} - variant="primary" - loading={formIsLoading}/> - </div> - </form> - </section> - <section class="margin-bottom-md"> - <p class="text-md margin-bottom-sm">Download your data</p> - <a class="btn btn--subtle" - href={archiveLink} - download>Click here to download your data</a> - </section> - <section> - <p class="text-md margin-bottom-sm">Delete account</p> - <div class="margin-bottom-sm"> - <Alert - message="Deleting your account and data means that all of your data (entries, categories, etc.) will be unrecoverable forever.<br>You should probably download your data before continuing." - type="info" - /> - </div> - <div class="form-check margin-bottom-sm"> - <input type="checkbox" - class="checkbox" - id="the-consequences" - bind:checked={understands}/> - <label for="the-consequences">I understand the consequences of deleting my account and data.</label> - </div> - <div class="flex justify-end"> - <Button text="Delete everything" - variant="accent" - disabled={!understands} - on:click={handle_delete_account_button_click}/> - </div> - </section> - </main> -</Layout> diff --git a/apps/portal/src/app/pages/reset-password.svelte b/apps/portal/src/app/pages/reset-password.svelte deleted file mode 100644 index dabf5c9..0000000 --- a/apps/portal/src/app/pages/reset-password.svelte +++ /dev/null @@ -1,138 +0,0 @@ -<script> - import {querystring, link} from "svelte-spa-router"; - import {check_forgot_password_request, fulfill_forgot_password_request} from "$shared/lib/api/user"; - import Alert from "$shared/components/alert.svelte"; - import Button from "$shared/components/button.svelte"; - import Tile from "$shared/components/tile.svelte"; - import Layout from "./_layout.svelte"; - - const requestId = new URLSearchParams($querystring).get("id"); - let isLoading = false; - let newPassword; - let newPasswordError; - let alert = { - title: "", - type: "", - message: "", - isVisible: false, - show(type, obj) { - alert.title = obj.title; - alert.message = obj.text; - alert.type = type; - alert.isVisible = true; - isLoading = false; - }, - hide() { - alert.isVisible = false; - alert.title = ""; - alert.message = ""; - alert.type = ""; - isLoading = false; - }, - }; - - function is_valid() { - let isValid = true; - if (!newPassword.length > 5) { - newPasswordError = "The new password must be at least 5 characters"; - isValid = false; - } - return isValid; - } - - async function submit() { - if (isLoading) { - return; - } - if (is_valid()) { - isLoading = true; - const response = await fulfill_forgot_password_request(requestId, newPassword); - if (response.ok) { - alert.show("success", { - title: "Your new password is set", - text: "<a href='/#/login'>Click here to log in</a>", - }); - } else { - console.error(response.data); - alert.show("error", { - title: response.data?.title ?? "An error occured", - text: response.data?.text ?? "Please try again soon", - }); - } - } - } - - async function is_valid_password_reset_request() { - const response = await check_forgot_password_request(requestId); - if (response.ok) { - return response.data === true; - } - return false; - } -</script> - -<Layout> - <Tile> - <form on:submit|preventDefault={submit} - class="max-width-xxs {isLoading ? 'c-disabled loading' : ''}"> - {#if requestId} - {#await is_valid_password_reset_request()} - <p>Checking your request...</p> - <a href="/login" - use:link>cancel</a> - {:then isActive} - {#if isActive === true} - <fieldset> - <legend class="form-legend"> - <span class="margin-bottom-xs text-xl">Set your new password</span> <br/> - <span class="text-sm"> - ... or - <a href="/login" - use:link> log in </a> - </span> - </legend> - <div class="margin-bottom-xxs max-width-xxs"> - <Alert visible={alert.isVisible} - title={alert.title} - message={alert.message} - type={alert.type}/> - </div> - <div class="margin-bottom-xs"> - <input - type="password" - id="new-password" - placeholder="New password" - class="form-control width-100%" - bind:value={newPassword} - /> - {#if newPasswordError} - <small class="color-danger">{newPasswordError}</small> - {/if} - </div> - <div class="flex justify-end"> - <Button text="Set new password" - type="primary" - loading={isLoading} - on:click={submit}/> - </div> - </fieldset> - {:else} - <Alert title="This request is expired" - message="Please submit the forgot password form again" - type="warning"/> - <div class="flex justify-between width-100% margin-y-sm"> - <a href="/forgot" - use:link>Go to forgot form</a> - <a href="/login" - use:link>Go to login form</a> - </div> - {/if} - {:catch _} - <Alert title="An error occured" - message="Please try again soon" - type="error"/> - {/await} - {/if} - </form> - </Tile> -</Layout> diff --git a/apps/portal/src/app/pages/sign-up.svelte b/apps/portal/src/app/pages/sign-up.svelte deleted file mode 100644 index 3bcab6d..0000000 --- a/apps/portal/src/app/pages/sign-up.svelte +++ /dev/null @@ -1,131 +0,0 @@ -<script> - import {create_account} from "$shared/lib/api/user"; - import {frontpage_base} from "$shared/lib/configuration"; - import {is_email} from "$shared/lib/helpers"; - import Alert from "$shared/components/alert.svelte"; - import Button from "$shared/components/button.svelte"; - import Tile from "$shared/components/tile.svelte"; - import {link} from "svelte-spa-router"; - import Layout from "./_layout.svelte"; - - const signupForm = { - loading: false, - values: { - username: "", - password: "", - }, - alert: { - title: "", - type: "", - message: "", - isVisible: false, - show(type, obj) { - signupForm.alert.title = obj.title; - signupForm.alert.message = obj.text; - signupForm.alert.type = type; - signupForm.alert.isVisible = true; - signupForm.loading = false; - }, - hide() { - signupForm.alert.isVisible = false; - signupForm.alert.title = ""; - signupForm.alert.message = ""; - signupForm.alert.type = ""; - }, - }, - is_valid() { - return ( - is_email(signupForm.values.username) && - signupForm.values.password.length > 0 - ); - }, - async submit_form() { - if (signupForm.loading) { - return; - } - if (signupForm.is_valid()) { - signupForm.alert.hide(); - signupForm.loading = true; - try { - const response = await create_account(signupForm.values); - if (response.ok) { - location.reload(); - } else { - if (response.data.title || response.data.text) { - signupForm.alert.show("error", { - title: response.data.title ?? "", - text: response.data.text ?? "", - }); - } else { - signupForm.alert.show("error", { - title: "An unknown error occured", - text: "Try again soon", - }); - } - } - } catch (e) { - console.error(e); - signupForm.alert.show("error", { - title: "An error occured", - text: "Could not connect to server, please check your internet connection", - }); - } - } else { - signupForm.alert.show("error", { - title: "Invalid form", - }); - } - }, - }; -</script> - -<Layout> - <a href="{frontpage_base()}" - class="block margin-bottom-xs">Go to {frontpage_base()}</a> - <Tile> - <form on:submit|preventDefault={signupForm.submit_form} - class="max-width-xxs"> - <fieldset> - <legend class="form-legend"> - <span class="margin-bottom-xs text-xl">Create your account</span> <br/> - <span class="text-sm" - >... or <a href="/login" - use:link>log in</a></span - > - </legend> - <div class="margin-bottom-xs"> - <p>Provide an email and password to get immediate access to your new environment (30 days full access, no billing details required, no promotion emails).</p> - </div> - <div class="margin-bottom-xxs max-width-xxs"> - <Alert visible={signupForm.alert.isVisible} - title={signupForm.alert.title} - message={signupForm.alert.message} - type={signupForm.alert.type} - /> - </div> - <div class="margin-bottom-xxs"> - <input type="email" - placeholder="Email address" - class="form-control width-100%" - id="email-address" - bind:value={signupForm.values.username} - /> - </div> - <div class="margin-bottom-xxs"> - <input type="password" - placeholder="Password" - class="form-control width-100%" - bind:value={signupForm.values.password} - /> - </div> - <div class="flex justify-end"> - <Button class="margin-bottom-xs" - text="Create account" - type="primary" - loading={signupForm.loading} - /> - </div> - </fieldset> - </form> - </Tile> -</Layout> |
