diff options
| author | ivarlovlie <git@ivarlovlie.no> | 2022-12-13 14:48:11 +0100 |
|---|---|---|
| committer | ivarlovlie <git@ivarlovlie.no> | 2022-12-13 14:48:21 +0100 |
| commit | a8219611cbebbd27501d9f30c804979048b98107 (patch) | |
| tree | 632dbab8b724f0148b06525771d85ad6ddb55e35 /code/app/src/routes/(main)/(public) | |
| parent | db2d937a38d5c309e3c6aa14f2eaf3cfe58f415e (diff) | |
| download | greatoffice-a8219611cbebbd27501d9f30c804979048b98107.tar.xz greatoffice-a8219611cbebbd27501d9f30c804979048b98107.zip | |
feat: A whole slew of things
- Use a md5 hash of the session cookie value as key for session validity check
- Introduce global state
- Introduce a common interface for form logic, and implement it on the sign-in form
- Introduce static resolve() on all services instead of new-upping all over.
- Implement /portal on the frontend to support giving the frontend a inital context from server or anywhere.
- Show a notification when users sign in for the first time after validating their email
Diffstat (limited to 'code/app/src/routes/(main)/(public)')
5 files changed, 115 insertions, 79 deletions
diff --git a/code/app/src/routes/(main)/(public)/portal/+page.svelte b/code/app/src/routes/(main)/(public)/portal/+page.svelte new file mode 100644 index 0000000..bd6aa15 --- /dev/null +++ b/code/app/src/routes/(main)/(public)/portal/+page.svelte @@ -0,0 +1,26 @@ +<script lang="ts"> + import { onMount } from "svelte"; + import type { PageData } from "./$types"; + import type { PortalMessage } from "$configuration"; + import { goto } from "$app/navigation"; + import { sgs } from "$help/global-state"; + + export let data: PageData; + + onMount(async () => { + switch (data.message as PortalMessage) { + case "emailValidated": { + sgs("showEmailValidatedAlertWhenLoggedIn", true); + await goto("/home"); + break; + } + default: { + await goto("/home"); + } + } + }); +</script> + +<div class="p-3"> + <h1>Warping...</h1> +</div> diff --git a/code/app/src/routes/(main)/(public)/portal/+page.ts b/code/app/src/routes/(main)/(public)/portal/+page.ts new file mode 100644 index 0000000..49bf3db --- /dev/null +++ b/code/app/src/routes/(main)/(public)/portal/+page.ts @@ -0,0 +1,10 @@ +import type { PortalMessage } from '$configuration'; +import { redirect } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = async ({ url }) => { + const queryParams = new URLSearchParams(url.search); + const message = queryParams.get("msg") as PortalMessage; + if (!message) throw redirect(302, "/"); + return { message }; +};
\ No newline at end of file diff --git a/code/app/src/routes/(main)/(public)/reset-password/+page.svelte b/code/app/src/routes/(main)/(public)/reset-password/+page.svelte index 55859f6..a45ccdd 100644 --- a/code/app/src/routes/(main)/(public)/reset-password/+page.svelte +++ b/code/app/src/routes/(main)/(public)/reset-password/+page.svelte @@ -12,7 +12,7 @@ }; const formError = new FormError(); - const resetRequests = new PasswordResetService(); + const passwordResetService = PasswordResetService.resolve(); let loading = false; let showSuccessAlert = false; @@ -23,7 +23,7 @@ showSuccessAlert = false; showErrorAlert = false; loading = true; - const response = await resetRequests.create_request_async(formData.email.value); + const response = await passwordResetService.create_request_async(formData.email.value); loading = false; if (response.isCreated) { showSuccessAlert = true; @@ -37,17 +37,12 @@ } } } else { - formError.title = $LL.unexpectedError(); - formError.subtitle = $LL.tryAgainSoon(); + formError.set($LL.unexpectedError(), $LL.tryAgainSoon()); } showErrorAlert = formError.has_error() && !showSuccessAlert; } </script> -<svlete:head> - <title>Reset password - Greatoffice</title> -</svlete:head> - <div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8"> <div class="sm:mx-auto sm:w-full p-2 sm:p-0 sm:max-w-md"> <h2 class="mt-6 text-3xl tracking-tight font-bold text-gray-900"> diff --git a/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte b/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte index 8f817bf..27a1af5 100644 --- a/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte +++ b/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte @@ -1,14 +1,15 @@ <script lang="ts"> - import {onMount} from "svelte"; + import { onMount } from "svelte"; import LL from "$i18n/i18n-svelte"; - import {Alert, Input, Button} from "$components"; - import type {PageServerData} from "./$types"; - import {goto} from "$app/navigation"; - import {SignInPageMessage, signInPageMessageQueryKey} from "$routes/(main)/(public)/sign-in"; - import {PasswordResetService} from "$services/password-reset-service"; + import { Alert, Input, Button } from "$components"; + import type { PageServerData } from "./$types"; + import { goto } from "$app/navigation"; + import { SignInPageMessage, signInPageMessageQueryKey } from "$routes/(main)/(public)/sign-in"; + import { PasswordResetService } from "$services/password-reset-service"; export let data: PageServerData; - const passwordResets = new PasswordResetService(); + const passwordResetService = PasswordResetService.resolve(); + const formData = { newPassword: { value: "", @@ -24,7 +25,7 @@ async function submitFormAsync() { if (!canSubmit) return; loading = true; - const request = await passwordResets.fulfill_request_async(data.resetRequestId, formData.newPassword.value); + const request = await passwordResetService.fulfill_request_async(data.resetRequestId, formData.newPassword.value); if (request.isFulfilled) { goto("/sign-in?" + signInPageMessageQueryKey + "=" + SignInPageMessage.AFTER_PASSWORD_RESET); } else if (request.knownProblem) { @@ -33,7 +34,7 @@ } onMount(async () => { - const response = await passwordResets.request_is_valid_async(data.resetRequestId); + const response = await passwordResetService.request_is_valid_async(data.resetRequestId); requestIsInvalid = !response.isValid; finishedPreliminaryLoading = true; }); @@ -57,19 +58,21 @@ <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10"> <form class="space-y-6" on:submit|preventDefault={submitFormAsync}> {#if requestIsInvalid} - <Alert title={$LL.resetPasswordPage.invalidRequestTitle()} - message={$LL.resetPasswordPage.invalidRequestMessage()}/> + <Alert + title={$LL.resetPasswordPage.invalidRequestTitle()} + message={$LL.resetPasswordPage.invalidRequestMessage()} + /> {/if} <Input - id="password" - name="password" - type="password" - autocomplete="new-password" - required - bind:value={formData.newPassword.value} - label={$LL.resetPasswordPage.newPassword()} + id="password" + name="password" + type="password" + autocomplete="new-password" + required + bind:value={formData.newPassword.value} + label={$LL.resetPasswordPage.newPassword()} /> - <Button text={$LL.submit()} type="submit" {loading} fullWidth/> + <Button text={$LL.submit()} type="submit" {loading} fullWidth /> </form> </div> </div> diff --git a/code/app/src/routes/(main)/(public)/sign-in/+page.svelte b/code/app/src/routes/(main)/(public)/sign-in/+page.svelte index c4ecb1a..66d4575 100644 --- a/code/app/src/routes/(main)/(public)/sign-in/+page.svelte +++ b/code/app/src/routes/(main)/(public)/sign-in/+page.svelte @@ -8,62 +8,62 @@ import { AccountService } from "$services/account-service"; import type { LoginPayload } from "$services/abstractions/IAccountService"; import { FormError } from "$models/internal/FormError"; + import type { IForm } from "$models/internal/IForm"; - let loading = false; - let showErrorAlert = false; let messageType: SignInPageMessage | undefined = undefined; - const accountService = new AccountService(); - - const formData = { - username: { - value: "", - errors: [], - }, - password: { - value: "", - errors: [], + const accountService = AccountService.resolve(); + const form = { + fields: { + username: { + value: "", + errors: [], + }, + password: { + value: "", + errors: [], + }, + persist: { + value: false, + errors: [], + }, }, - persist: { - value: false, - errors: [], - }, - as_payload(): LoginPayload { + error: new FormError(), + isLoading: false, + showError: false, + get_payload(): LoginPayload { return { - password: formData.password.value, - username: formData.username.value, - persist: !formData.persist.value, + password: form.fields.password.value, + username: form.fields.username.value, + persist: !form.fields.persist.value, }; }, - }; - - const formError = new FormError(); + async submit_async() { + console.log("sadf"); + form.error.set(); + form.showError = form.error.has_error(); + form.isLoading = true; + const loginResponse = await accountService.login_async(form.get_payload()); + if (loginResponse.isLoggedIn) { + await goto("/home"); + } else if (loginResponse.knownProblem) { + form.error.set_from_known_problem(loginResponse.knownProblem); + } else { + form.error.set($LL.unexpectedError(), $LL.tryAgainSoon()); + } + form.isLoading = false; + form.showError = form.error.has_error(); + }, + } as IForm; onMount(() => { - const searcher = new URLSearchParams(window.location.search); - if (searcher.get(signInPageMessageQueryKey)) { - messageType = searcher.get(signInPageMessageQueryKey) as SignInPageMessage; - searcher.delete(signInPageMessageQueryKey); - history.replaceState(null, "", window.location.origin + window.location.pathname); + const queryParams = new URLSearchParams(window.location.search); + if (queryParams.get(signInPageMessageQueryKey)) { + messageType = queryParams.get(signInPageMessageQueryKey) as SignInPageMessage; + queryParams.delete(signInPageMessageQueryKey); + window.history.replaceState(null, "", window.location.origin + window.location.pathname); } }); - - async function submit_form_async() { - formError.set(); - showErrorAlert = formError.has_error(); - loading = true; - const loginResponse = await accountService.login_async(formData.as_payload()); - if (loginResponse.isLoggedIn) { - await goto("/home"); - } else if (loginResponse.knownProblem) { - formError.set_from_known_problem(loginResponse.knownProblem); - } else { - formError.title = $LL.unexpectedError(); - formError.subtitle = $LL.tryAgainSoon(); - } - loading = false; - showErrorAlert = formError.has_error(); - } </script> <div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8"> @@ -106,10 +106,10 @@ </div> <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10"> - {#if showErrorAlert} - <Alert title={formError.title} message={formError.subtitle} type="error" _pwKey={signInPageTestKeys.formErrorAlert} /> + {#if form.showError} + <Alert title={form.error.title} message={form.error.subtitle} type="error" _pwKey={signInPageTestKeys.formErrorAlert} /> {/if} - <form class="space-y-6 mt-2" use:pwKey={signInPageTestKeys.signInForm} on:submit|preventDefault={submit_form_async}> + <form class="space-y-6 mt-2" use:pwKey={signInPageTestKeys.signInForm} on:submit|preventDefault={() => form.submit_async()}> <Input id="username" _pwKey={signInPageTestKeys.usernameInput} @@ -117,7 +117,8 @@ type="email" label={$LL.emailAddress()} required - bind:value={formData.username.value} + errors={form.fields.username.errors} + bind:value={form.fields.username.value} /> <Input @@ -128,7 +129,8 @@ _pwKey={signInPageTestKeys.passwordInput} autocomplete="current-password" required - bind:value={formData.password.value} + errors={form.fields.password.errors} + bind:value={form.fields.password.value} /> <div class="flex items-center justify-between"> @@ -136,7 +138,7 @@ id="remember-me" _pwKey={signInPageTestKeys.rememberMeCheckbox} name="remember-me" - bind:checked={formData.persist.value} + bind:checked={form.fields.persist.value} label={$LL.signInPage.notMyComputer()} /> <div class="text-sm"> @@ -146,7 +148,7 @@ </div> </div> - <Button text={$LL.submit()} fullWidth type="submit" {loading} /> + <Button text={$LL.submit()} fullWidth type="submit" loading={form.isLoading} /> </form> </div> </div> |
