aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-10-01 10:44:31 +0200
committerivarlovlie <git@ivarlovlie.no>2022-10-01 10:44:31 +0200
commit7a5ba5ea4aec0704070cfe8d63ba504a07d88cc6 (patch)
tree683a6a121e02b4413774dcc36522c5d3817c9867
parent33b5c5a72974af5bd8745298772fe7cc71b87b76 (diff)
downloadgreatoffice-7a5ba5ea4aec0704070cfe8d63ba504a07d88cc6.tar.xz
greatoffice-7a5ba5ea4aec0704070cfe8d63ba504a07d88cc6.zip
feat: Functionality complete public sites
-rw-r--r--apps/kit/src/app.d.ts2
-rw-r--r--apps/kit/src/lib/components/alert.svelte4
-rw-r--r--apps/kit/src/lib/components/button.svelte12
-rw-r--r--apps/kit/src/lib/components/checkbox.svelte2
-rw-r--r--apps/kit/src/lib/i18n/en/index.ts41
-rw-r--r--apps/kit/src/lib/i18n/i18n-types.ts252
-rw-r--r--apps/kit/src/lib/models/LoginPayload.ts3
-rw-r--r--apps/kit/src/routes/(main)/(public)/+layout.svelte18
-rw-r--r--apps/kit/src/routes/(main)/(public)/reset-password/+page.svelte16
-rw-r--r--apps/kit/src/routes/(main)/(public)/reset-password/[id]/+page.svelte79
-rw-r--r--apps/kit/src/routes/(main)/(public)/sign-in/+page.svelte79
-rw-r--r--apps/kit/src/routes/(main)/(public)/sign-up/+page.svelte61
-rw-r--r--apps/kit/src/routes/(main)/+layout.svelte7
13 files changed, 425 insertions, 151 deletions
diff --git a/apps/kit/src/app.d.ts b/apps/kit/src/app.d.ts
index 4ab4e43..220ddc1 100644
--- a/apps/kit/src/app.d.ts
+++ b/apps/kit/src/app.d.ts
@@ -6,4 +6,4 @@ declare namespace App {
interface Platform {}
interface PrivateEnv {}
interface PublicEnv {}
-}
+} \ No newline at end of file
diff --git a/apps/kit/src/lib/components/alert.svelte b/apps/kit/src/lib/components/alert.svelte
index 5bcb3ae..fa36a63 100644
--- a/apps/kit/src/lib/components/alert.svelte
+++ b/apps/kit/src/lib/components/alert.svelte
@@ -193,7 +193,9 @@
</script>
{#if visible}
- <div class="rounded-md bg-{colorClassPart}-50 p-4 ">
+ <div
+ class="rounded-md bg-{colorClassPart}-50 p-4 {$$restProps.class ?? ''}"
+ >
<div class="flex">
<div class="flex-shrink-0">
<svelte:component
diff --git a/apps/kit/src/lib/components/button.svelte b/apps/kit/src/lib/components/button.svelte
index 95dba5c..323c7ad 100644
--- a/apps/kit/src/lib/components/button.svelte
+++ b/apps/kit/src/lib/components/button.svelte
@@ -13,7 +13,7 @@
export let tabindex: string | undefined = undefined;
export let style: string | undefined = undefined;
export let title: string | undefined = undefined;
- export let disabled = false;
+ export let disabled: boolean | null = false;
export let href: string | undefined = undefined;
export let text: string;
export let loading = false;
@@ -78,8 +78,9 @@
on:click
class="{sizeClasses} {kindClasses} {loading
? 'disabled:'
- : ''} {$$restProps.class ??
- ''} {fullWidth ? 'w-full justify-center': ''} inline-flex items-center border font-medium rounded shadow-sm focus:outline-none focus:ring-2"
+ : ''} {$$restProps.class ?? ''} {fullWidth
+ ? 'w-full justify-center'
+ : ''} inline-flex items-center border font-medium rounded shadow-sm focus:outline-none focus:ring-2"
>
{#if loading}
<Spinner class={spinnerTextClasses + " " + spinnerMarginClasses} />
@@ -89,8 +90,9 @@
{:else}
<button
{...shared_props}
- class="{sizeClasses} {kindClasses} {$$restProps.class ??
- ''} {fullWidth ? 'w-full justify-center': ''} inline-flex items-center border font-medium rounded shadow-sm focus:outline-none focus:ring-2"
+ class="{sizeClasses} {kindClasses} {$$restProps.class ?? ''} {fullWidth
+ ? 'w-full justify-center'
+ : ''} inline-flex items-center border font-medium rounded shadow-sm focus:outline-none focus:ring-2"
>
{#if loading}
<Spinner class={spinnerTextClasses + " " + spinnerMarginClasses} />
diff --git a/apps/kit/src/lib/components/checkbox.svelte b/apps/kit/src/lib/components/checkbox.svelte
index bb22215..311d154 100644
--- a/apps/kit/src/lib/components/checkbox.svelte
+++ b/apps/kit/src/lib/components/checkbox.svelte
@@ -5,6 +5,7 @@
export let id: string | undefined = "input__" + random_string(4);
export let name: string | undefined = undefined;
export let disabled: boolean | null = null;
+ export let checked: boolean;
</script>
<div class="flex items-center">
@@ -13,6 +14,7 @@
{disabled}
{id}
type="checkbox"
+ bind:checked
class="h-4 w-4 text-teal-600 focus:ring-teal-500 border-gray-300 rounded"
/>
<label for={id} class="ml-2 block text-sm text-gray-900">{label}</label>
diff --git a/apps/kit/src/lib/i18n/en/index.ts b/apps/kit/src/lib/i18n/en/index.ts
index 003de0d..e084a6c 100644
--- a/apps/kit/src/lib/i18n/en/index.ts
+++ b/apps/kit/src/lib/i18n/en/index.ts
@@ -12,16 +12,39 @@ const en: BaseTranslation = {
submit: "Submit",
success: "Success",
tryAgainSoon: "Try again soon",
+ createANewAccount: "Create a new account",
unexpectedError: "An unexpected error occured",
- requestAPasswordReset: "Request a password reset",
- signIntoYourAccount: "sign into your account",
- requestSentMessage: "If we find your email address in our systems, you will receive an email with instructions on how to set a new password for your account.",
- signIn: "Sign In",
- createANewAccount: "create a new account",
- notMyComputer: "This is not my computer",
- resetPassword: "Reset password",
- createYourNewAccount: "Create your new account",
- newPassword: "New password"
+ notFound: "Not found",
+ documentation: "Documentation",
+ tos: "Terms of service",
+ privacyPolicy: "Privacy policy",
+ signIntoYourAccount: "Sign into your account",
+ signInPage: {
+ notMyComputer: "This is not my computer",
+ resetPassword: "Reset password",
+ yourPasswordIsUpdated: "Your password is updated",
+ signIn: "Sign In",
+ yourNewPasswordIsApplied: "Your new password is applied",
+ signInBelow: "Sign in below",
+ yourAccountIsDisabled: "Your account is disabled",
+ contactYourAdminIfDisabled: "Contact your administrator if this feels wrong",
+ youHaveReachedInactivityLimit: "You've reached the hidden inactivity limit",
+ feelFreeToSignInAgain: "Feel free to sign in again"
+ },
+ signUpPage: {
+ createYourNewAccount: "Create your new account",
+ },
+ resetPasswordPage: {
+ setANewPassword: "Set a new password",
+ expired: "Expired",
+ requestHasExpired: "Your request has expired",
+ requestANewReset: "Request a new reset",
+ newPassword: "New password",
+ requestSentMessage: "If we find your email address in our systems, you will receive an email with instructions on how to set a new password for your account.",
+ requestAPasswordReset: "Request a password reset",
+ requestNotFound: "Your request was not found",
+ submitANewRequestBelow: "Submit a new reset request below"
+ }
};
export default en;
diff --git a/apps/kit/src/lib/i18n/i18n-types.ts b/apps/kit/src/lib/i18n/i18n-types.ts
index aedf48b..2d76cef 100644
--- a/apps/kit/src/lib/i18n/i18n-types.ts
+++ b/apps/kit/src/lib/i18n/i18n-types.ts
@@ -64,45 +64,119 @@ type RootTranslation = {
*/
tryAgainSoon: string
/**
- * An unexpected error occured
- */
- unexpectedError: string
- /**
- * Request a password reset
+ * Create a new account
*/
- requestAPasswordReset: string
- /**
- * sign into your account
- */
- signIntoYourAccount: string
- /**
- * If we find your email address in our systems, you will receive an email with instructions on how to set a new password for your account.
- */
- requestSentMessage: string
+ createANewAccount: string
/**
- * Sign In
+ * An unexpected error occured
*/
- signIn: string
+ unexpectedError: string
/**
- * create a new account
+ * Not found
*/
- createANewAccount: string
+ notFound: string
/**
- * This is not my computer
+ * Documentation
*/
- notMyComputer: string
+ documentation: string
/**
- * Reset password
+ * Terms of service
*/
- resetPassword: string
+ tos: string
/**
- * Create your new account
+ * Privacy policy
*/
- createYourNewAccount: string
+ privacyPolicy: string
/**
- * New password
+ * Sign into your account
*/
- newPassword: string
+ signIntoYourAccount: string
+ signInPage: {
+ /**
+ * This is not my computer
+ */
+ notMyComputer: string
+ /**
+ * Reset password
+ */
+ resetPassword: string
+ /**
+ * Your password is updated
+ */
+ yourPasswordIsUpdated: string
+ /**
+ * Sign In
+ */
+ signIn: string
+ /**
+ * Your new password is applied
+ */
+ yourNewPasswordIsApplied: string
+ /**
+ * Sign in below
+ */
+ signInBelow: string
+ /**
+ * Your account is disabled
+ */
+ yourAccountIsDisabled: string
+ /**
+ * Contact your administrator if this feels wrong
+ */
+ contactYourAdminIfDisabled: string
+ /**
+ * You've reached the hidden inactivity limit
+ */
+ youHaveReachedInactivityLimit: string
+ /**
+ * Feel free to sign in again
+ */
+ feelFreeToSignInAgain: string
+ }
+ signUpPage: {
+ /**
+ * Create your new account
+ */
+ createYourNewAccount: string
+ }
+ resetPasswordPage: {
+ /**
+ * Set a new password
+ */
+ setANewPassword: string
+ /**
+ * Expired
+ */
+ expired: string
+ /**
+ * Your request has expired
+ */
+ requestHasExpired: string
+ /**
+ * Request a new reset
+ */
+ requestANewReset: string
+ /**
+ * New password
+ */
+ newPassword: string
+ /**
+ * If we find your email address in our systems, you will receive an email with instructions on how to set a new password for your account.
+ */
+ requestSentMessage: string
+ /**
+ * Request a password reset
+ */
+ requestAPasswordReset: string
+ /**
+ * Your request was not found
+ */
+ requestNotFound: string
+ /**
+ * Submit a new reset request below
+ */
+ submitANewRequestBelow: string
+ }
}
export type NamespaceAppTranslation = {
@@ -473,45 +547,119 @@ export type TranslationFunctions = {
*/
tryAgainSoon: () => LocalizedString
/**
- * An unexpected error occured
- */
- unexpectedError: () => LocalizedString
- /**
- * Request a password reset
+ * Create a new account
*/
- requestAPasswordReset: () => LocalizedString
- /**
- * sign into your account
- */
- signIntoYourAccount: () => LocalizedString
- /**
- * If we find your email address in our systems, you will receive an email with instructions on how to set a new password for your account.
- */
- requestSentMessage: () => LocalizedString
+ createANewAccount: () => LocalizedString
/**
- * Sign In
+ * An unexpected error occured
*/
- signIn: () => LocalizedString
+ unexpectedError: () => LocalizedString
/**
- * create a new account
+ * Not found
*/
- createANewAccount: () => LocalizedString
+ notFound: () => LocalizedString
/**
- * This is not my computer
+ * Documentation
*/
- notMyComputer: () => LocalizedString
+ documentation: () => LocalizedString
/**
- * Reset password
+ * Terms of service
*/
- resetPassword: () => LocalizedString
+ tos: () => LocalizedString
/**
- * Create your new account
+ * Privacy policy
*/
- createYourNewAccount: () => LocalizedString
+ privacyPolicy: () => LocalizedString
/**
- * New password
+ * Sign into your account
*/
- newPassword: () => LocalizedString
+ signIntoYourAccount: () => LocalizedString
+ signInPage: {
+ /**
+ * This is not my computer
+ */
+ notMyComputer: () => LocalizedString
+ /**
+ * Reset password
+ */
+ resetPassword: () => LocalizedString
+ /**
+ * Your password is updated
+ */
+ yourPasswordIsUpdated: () => LocalizedString
+ /**
+ * Sign In
+ */
+ signIn: () => LocalizedString
+ /**
+ * Your new password is applied
+ */
+ yourNewPasswordIsApplied: () => LocalizedString
+ /**
+ * Sign in below
+ */
+ signInBelow: () => LocalizedString
+ /**
+ * Your account is disabled
+ */
+ yourAccountIsDisabled: () => LocalizedString
+ /**
+ * Contact your administrator if this feels wrong
+ */
+ contactYourAdminIfDisabled: () => LocalizedString
+ /**
+ * You've reached the hidden inactivity limit
+ */
+ youHaveReachedInactivityLimit: () => LocalizedString
+ /**
+ * Feel free to sign in again
+ */
+ feelFreeToSignInAgain: () => LocalizedString
+ }
+ signUpPage: {
+ /**
+ * Create your new account
+ */
+ createYourNewAccount: () => LocalizedString
+ }
+ resetPasswordPage: {
+ /**
+ * Set a new password
+ */
+ setANewPassword: () => LocalizedString
+ /**
+ * Expired
+ */
+ expired: () => LocalizedString
+ /**
+ * Your request has expired
+ */
+ requestHasExpired: () => LocalizedString
+ /**
+ * Request a new reset
+ */
+ requestANewReset: () => LocalizedString
+ /**
+ * New password
+ */
+ newPassword: () => LocalizedString
+ /**
+ * If we find your email address in our systems, you will receive an email with instructions on how to set a new password for your account.
+ */
+ requestSentMessage: () => LocalizedString
+ /**
+ * Request a password reset
+ */
+ requestAPasswordReset: () => LocalizedString
+ /**
+ * Your request was not found
+ */
+ requestNotFound: () => LocalizedString
+ /**
+ * Submit a new reset request below
+ */
+ submitANewRequestBelow: () => LocalizedString
+ }
app: {
nav: {
/**
diff --git a/apps/kit/src/lib/models/LoginPayload.ts b/apps/kit/src/lib/models/LoginPayload.ts
index ccd9bed..beb96cf 100644
--- a/apps/kit/src/lib/models/LoginPayload.ts
+++ b/apps/kit/src/lib/models/LoginPayload.ts
@@ -1,4 +1,5 @@
export interface LoginPayload {
username: string,
- password: string
+ password: string,
+ persist: boolean
}
diff --git a/apps/kit/src/routes/(main)/(public)/+layout.svelte b/apps/kit/src/routes/(main)/(public)/+layout.svelte
new file mode 100644
index 0000000..69c29c5
--- /dev/null
+++ b/apps/kit/src/routes/(main)/(public)/+layout.svelte
@@ -0,0 +1,18 @@
+<script>
+ import LL from "$lib/i18n/i18n-svelte";
+</script>
+
+<slot />
+<footer
+ class="grid sm:gap-5 grid-flow-row sm:justify-center px-2 sm:grid-flow-col"
+>
+ <a href="https://greatoffice.life/privacy" class="link">
+ {$LL.privacyPolicy()}
+ </a>
+ <a href="https://greatoffice.life/tos" class="link">
+ {$LL.tos()}
+ </a>
+ <a href="https://greatoffice.life/documentation" class="link">
+ {$LL.documentation()}
+ </a>
+</footer>
diff --git a/apps/kit/src/routes/(main)/(public)/reset-password/+page.svelte b/apps/kit/src/routes/(main)/(public)/reset-password/+page.svelte
index 9ee4a83..aa26892 100644
--- a/apps/kit/src/routes/(main)/(public)/reset-password/+page.svelte
+++ b/apps/kit/src/routes/(main)/(public)/reset-password/+page.svelte
@@ -37,16 +37,14 @@
</script>
<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 sm:max-w-md">
- <h2
- class="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900"
- >
- {$LL.requestAPasswordReset()}
+ <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">
+ {$LL.resetPasswordPage.requestAPasswordReset()}
</h2>
- <p class="mt-2 text-center text-sm text-gray-600">
+ <p class="mt-2 text-sm text-gray-600">
{$LL.or().toLowerCase()}
<a href="/sign-in" class="link">
- {$LL.signIntoYourAccount()}
+ {$LL.signIntoYourAccount().toLowerCase()}
</a>
</p>
</div>
@@ -64,7 +62,7 @@
<Alert
type="success"
title={$LL.success()}
- message={$LL.requestSentMessage()}
+ message={$LL.resetPasswordPage.requestSentMessage()}
visible={showSuccessAlert}
/>
@@ -77,7 +75,7 @@
bind:value={formData.email}
label={$LL.emailAddress()}
/>
- <Button text={$LL.submit()} type="submit" fullWidth />
+ <Button text={$LL.submit()} type="submit" {loading} fullWidth />
</form>
</div>
</div>
diff --git a/apps/kit/src/routes/(main)/(public)/reset-password/[id]/+page.svelte b/apps/kit/src/routes/(main)/(public)/reset-password/[id]/+page.svelte
index 7b46d2d..562d902 100644
--- a/apps/kit/src/routes/(main)/(public)/reset-password/[id]/+page.svelte
+++ b/apps/kit/src/routes/(main)/(public)/reset-password/[id]/+page.svelte
@@ -9,6 +9,7 @@
import type { PageServerData } from "./$types";
import type { ErrorResult } from "$lib/models/ErrorResult";
import { goto } from "$app/navigation";
+ import { Message, messageQueryKey } from "../../sign-in/+page.svelte";
export let data: PageServerData;
@@ -16,18 +17,15 @@
newPassword: "",
};
- $: showErrorAlert =
- (errorData?.text.length ?? 0 + errorData?.title.length ?? 0) > 0 &&
- !showSuccessAlert;
-
const errorData = {
text: "",
title: "",
} as ErrorResult;
+ let errorState: undefined | "expired" | "404" | "unknown";
+
let finishedPreliminaryLoading = false;
let loading = false;
- let showSuccessAlert = false;
let canSubmit = true;
async function submitFormAsync() {
@@ -38,39 +36,48 @@
formData.newPassword
);
if (request.ok) {
- goto("/sign-in?m=1");
+ goto(
+ "/sign-in?" +
+ messageQueryKey +
+ "=" +
+ Message.AFTER_PASSWORD_RESET
+ );
}
loading = false;
}
onMount(async () => {
+ errorState = undefined;
const isValidRequest = await check_forgot_password_request(
data.resetRequestId
);
- if (!isValidRequest.ok) {
- errorData.text = isValidRequest.data?.text ?? $LL.tryAgainSoon();
- errorData.title =
- isValidRequest.data?.title ?? $LL.unexpectedError();
+ if (!isValidRequest.ok && isValidRequest.status !== 404) {
+ errorState = "unknown";
+ canSubmit = false;
+ }
+ if (isValidRequest.status === 404) {
+ errorState = "404";
+ canSubmit = false;
+ }
+ if (isValidRequest.ok && isValidRequest.data !== true) {
+ errorState = "expired";
canSubmit = false;
}
- if (isValidRequest.status === 404) goto("/reset-password");
finishedPreliminaryLoading = true;
});
</script>
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
{#if finishedPreliminaryLoading}
- <div class="sm:mx-auto sm:w-full sm:max-w-md">
- <h2
- class="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900"
- >
- {$LL.requestAPasswordReset()}
+ <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">
+ {$LL.resetPasswordPage.setANewPassword()}
</h2>
- <p class="mt-2 text-center text-sm text-gray-600">
+ <p class="mt-2 text-sm text-gray-600">
{$LL.or().toLowerCase()}
<a href="/sign-in" class="link">
- {$LL.signIntoYourAccount()}
+ {$LL.signIntoYourAccount().toLowerCase()}
</a>
</p>
</div>
@@ -81,21 +88,24 @@
class="space-y-6"
on:submit|preventDefault={submitFormAsync}
>
- <Alert
- title={errorData.title}
- message={errorData.text}
- type="error"
- visible={showErrorAlert}
- />
-
- <Alert
- type="success"
- title={$LL.success()}
- message={$LL.requestSentMessage()}
- rightLinkHref="/sign-in"
- rightLinkText="Sign in"
- visible={showSuccessAlert}
- />
+ {#if errorState === "404"}
+ <Alert
+ title={$LL.notFound()}
+ message={$LL.resetPasswordPage.requestNotFound()}
+ />
+ {:else if errorState === "expired"}
+ <Alert
+ title={$LL.resetPasswordPage.expired()}
+ message={$LL.resetPasswordPage.requestHasExpired()}
+ rightLinkHref="/reset-password"
+ rightLinkText={$LL.resetPasswordPage.requestANewReset()}
+ />
+ {:else if errorState === "unknown"}
+ <Alert
+ title={$LL.unexpectedError()}
+ message={$LL.tryAgainSoon()}
+ />
+ {/if}
<Input
id="password"
@@ -104,8 +114,9 @@
autocomplete="new-password"
required
bind:value={formData.newPassword}
- label={$LL.newPassword()}
+ label={$LL.resetPasswordPage.newPassword()}
/>
+
<Button
text={$LL.submit()}
type="submit"
diff --git a/apps/kit/src/routes/(main)/(public)/sign-in/+page.svelte b/apps/kit/src/routes/(main)/(public)/sign-in/+page.svelte
index c431e0d..07ed0e0 100644
--- a/apps/kit/src/routes/(main)/(public)/sign-in/+page.svelte
+++ b/apps/kit/src/routes/(main)/(public)/sign-in/+page.svelte
@@ -1,25 +1,28 @@
<script context="module" lang="ts">
- export type Message =
- | "after-password-reset"
- | "user-inactivity"
- | "user-disabled";
+ export enum Message {
+ AFTER_PASSWORD_RESET = "after-password-reset",
+ USER_INACTIVITY = "user-inactivity",
+ USER_DISABLED = "user-disabled",
+ }
+ export const messageQueryKey = "m";
</script>
<script lang="ts">
import { goto } from "$app/navigation";
import { login } from "$lib/api/user";
- import Button from "$lib/components/button.svelte";
- import Checkbox from "$lib/components/checkbox.svelte";
- import Input from "$lib/components/input.svelte";
+ import { Button, Checkbox, Input, Alert } from "$lib/components";
import LL from "$lib/i18n/i18n-svelte";
import type { ErrorResult } from "$lib/models/ErrorResult";
import type { LoginPayload } from "$lib/models/LoginPayload";
+ import { onMount } from "svelte";
let loading = false;
+ let messageType: Message | undefined = undefined;
const data = {
username: "",
password: "",
+ persist: true,
} as LoginPayload;
let error = {
@@ -27,9 +30,23 @@
title: "",
} as ErrorResult;
+ onMount(() => {
+ const searcher = new URLSearchParams(window.location.search);
+ if (searcher.get(messageQueryKey)) {
+ messageType = searcher.get(messageQueryKey) as Message;
+ searcher.delete(messageQueryKey);
+ history.replaceState(
+ null,
+ "",
+ window.location.origin + window.location.pathname
+ );
+ }
+ });
+
async function submitFormAsync() {
error = { text: "", title: "" };
loading = true;
+ data.persist = !data.persist;
const loginResponse = await login(data);
if (loginResponse.ok) {
await goto("/home");
@@ -42,15 +59,38 @@
</script>
<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 sm:max-w-md">
- <h2
- class="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900"
- >
- {$LL.signIn()}
+ {#if messageType}
+ <div class="sm:max-w-md sm:mx-auto sm:w-full">
+ {#if messageType === "after-password-reset"}
+ <Alert
+ title={$LL.signInPage.yourNewPasswordIsApplied()}
+ message={$LL.signInPage.signInBelow()}
+ closeable
+ />
+ {:else if messageType === "user-disabled"}
+ <Alert
+ title={$LL.signInPage.yourAccountIsDisabled()}
+ message={$LL.signInPage.contactYourAdminIfDisabled()}
+ closeable
+ />
+ {:else if messageType === "user-inactivity"}
+ <Alert
+ title={$LL.signInPage.youHaveReachedInactivityLimit()}
+ message={$LL.signInPage.feelFreeToSignInAgain()}
+ closeable
+ />
+ {/if}
+ </div>
+ {/if}
+ <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">
+ {$LL.signInPage.signIn()}
</h2>
- <p class="mt-2 text-center text-sm text-gray-600">
+ <p class="mt-2 text-sm text-gray-600">
{$LL.or().toLowerCase()}
- <a href="/sign-up" class="link">{$LL.createANewAccount()}</a>
+ <a href="/sign-up" class="link"
+ >{$LL.createANewAccount().toLowerCase()}</a
+ >
</p>
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
@@ -76,7 +116,7 @@
type="email"
label={$LL.emailAddress()}
required
- value={data.username}
+ bind:value={data.username}
/>
<Input
@@ -86,23 +126,24 @@
label={$LL.password()}
autocomplete="current-password"
required
- value={data.password}
+ bind:value={data.password}
/>
<div class="flex items-center justify-between">
<Checkbox
id="remember-me"
name="remember-me"
- label={$LL.notMyComputer()}
+ bind:checked={data.persist}
+ label={$LL.signInPage.notMyComputer()}
/>
<div class="text-sm">
<a href="/reset-password" class="link">
- {$LL.resetPassword()}
+ {$LL.signInPage.resetPassword()}
</a>
</div>
</div>
- <Button text={$LL.signIn()} fullWidth type="submit" {loading} />
+ <Button text={$LL.submit()} fullWidth type="submit" {loading} />
</form>
</div>
</div>
diff --git a/apps/kit/src/routes/(main)/(public)/sign-up/+page.svelte b/apps/kit/src/routes/(main)/(public)/sign-up/+page.svelte
index 8b89190..0dfa41a 100644
--- a/apps/kit/src/routes/(main)/(public)/sign-up/+page.svelte
+++ b/apps/kit/src/routes/(main)/(public)/sign-up/+page.svelte
@@ -1,29 +1,62 @@
-<script>
- import { Button, Input } from "$lib/components";
+<script lang="ts">
+ import { goto } from "$app/navigation";
+ import { create_account } from "$lib/api/user";
+ import { Button, Input, Alert } from "$lib/components";
import LL from "$lib/i18n/i18n-svelte";
+ import type { CreateAccountPayload } from "$lib/models/CreateAccountPayload";
+ import type { ErrorResult } from "$lib/models/ErrorResult";
const formData = {
- emailAddress: "",
+ username: "",
password: "",
- };
+ } as CreateAccountPayload;
+
+ const errorData = {
+ text: "",
+ title: "",
+ } as ErrorResult;
+ let loading = false;
+ $: showErrorAlert =
+ (errorData?.text.length ?? 0 + errorData?.title.length ?? 0) > 0;
+
+ async function submitFormAsync() {
+ loading = true;
+ errorData.text = "";
+ errorData.title = "";
+ const response = await create_account(formData);
+ loading = false;
+ if (response.ok) {
+ await goto("/home");
+ return;
+ }
+ errorData.title = response.data?.title ?? $LL.unexpectedError();
+ errorData.text = response.data?.text ?? $LL.tryAgainSoon();
+ }
</script>
<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 sm:max-w-md">
- <h2
- class="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900"
- >
- {$LL.createYourNewAccount()}
+ <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">
+ {$LL.signUpPage.createYourNewAccount()}
</h2>
- <p class="mt-2 text-center text-sm text-gray-600">
+ <p class="mt-2 text-sm text-gray-600">
{$LL.or().toLowerCase()}
- <a href="/sign-in" class="link">{$LL.signIntoYourAccount()}</a>
+ <a href="/sign-in" class="link">
+ {$LL.signIntoYourAccount().toLowerCase()}
+ </a>
</p>
</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">
- <form class="space-y-6" action="#">
+ <Alert
+ title={errorData.title}
+ message={errorData.text}
+ type="error"
+ class="mb-2"
+ visible={showErrorAlert}
+ />
+ <form class="space-y-6" on:submit|preventDefault={submitFormAsync}>
<Input
label={$LL.emailAddress()}
id="email"
@@ -31,7 +64,7 @@
autocomplete="email"
required
type="email"
- bind:value={formData.emailAddress}
+ bind:value={formData.username}
/>
<Input
@@ -42,7 +75,7 @@
type="password"
bind:value={formData.password}
/>
- <Button type="submit" text={$LL.submit()} fullWidth />
+ <Button type="submit" text={$LL.submit()} {loading} fullWidth />
</form>
</div>
</div>
diff --git a/apps/kit/src/routes/(main)/+layout.svelte b/apps/kit/src/routes/(main)/+layout.svelte
index 3107861..9787e17 100644
--- a/apps/kit/src/routes/(main)/+layout.svelte
+++ b/apps/kit/src/routes/(main)/+layout.svelte
@@ -1,16 +1,11 @@
<script lang="ts">
import "../../app.pcss";
import { setLocale } from "$lib/i18n/i18n-svelte";
- import { onMount } from "svelte";
import type { LayoutData } from "./$types";
- import LocaleSwitcher from "$lib/components/locale-switcher.svelte";
export let data: LayoutData;
- onMount(async () => {
- setLocale(data.locale);
- });
+ setLocale(data.locale);
</script>
-<LocaleSwitcher />
<slot />