Rename db folder to server, remove levelup dependencies, add sveltekit flash message, add toast library, and use both on signup.

This commit is contained in:
Bradley Shellnut 2023-07-30 16:31:39 -07:00
parent a62bd44279
commit a32adc3ae8
18 changed files with 188 additions and 129 deletions

View file

@ -67,8 +67,6 @@
"@fontsource/fira-mono": "^4.5.10", "@fontsource/fira-mono": "^4.5.10",
"@iconify-icons/line-md": "^1.2.23", "@iconify-icons/line-md": "^1.2.23",
"@iconify-icons/mdi": "^1.2.47", "@iconify-icons/mdi": "^1.2.47",
"@leveluptuts/svelte-side-menu": "^1.0.5",
"@leveluptuts/svelte-toy": "^2.0.3",
"@lucia-auth/adapter-mysql": "^1.1.1", "@lucia-auth/adapter-mysql": "^1.1.1",
"@lucia-auth/adapter-prisma": "^3.0.0", "@lucia-auth/adapter-prisma": "^3.0.0",
"@lukeed/uuid": "^2.0.1", "@lukeed/uuid": "^2.0.1",
@ -85,6 +83,7 @@
"lucide-svelte": "^0.256.1", "lucide-svelte": "^0.256.1",
"open-props": "^1.5.10", "open-props": "^1.5.10",
"radix-svelte": "^0.8.0", "radix-svelte": "^0.8.0",
"svelte-french-toast": "^1.2.0",
"svelte-lazy": "^1.2.1", "svelte-lazy": "^1.2.1",
"svelte-lazy-loader": "^1.0.0", "svelte-lazy-loader": "^1.0.0",
"svelte-legos": "^0.2.1", "svelte-legos": "^0.2.1",

View file

@ -17,12 +17,6 @@ dependencies:
'@iconify-icons/mdi': '@iconify-icons/mdi':
specifier: ^1.2.47 specifier: ^1.2.47
version: 1.2.47 version: 1.2.47
'@leveluptuts/svelte-side-menu':
specifier: ^1.0.5
version: 1.0.5
'@leveluptuts/svelte-toy':
specifier: ^2.0.3
version: 2.0.3
'@lucia-auth/adapter-mysql': '@lucia-auth/adapter-mysql':
specifier: ^1.1.1 specifier: ^1.1.1
version: 1.1.1(lucia-auth@1.8.0) version: 1.1.1(lucia-auth@1.8.0)
@ -71,6 +65,9 @@ dependencies:
radix-svelte: radix-svelte:
specifier: ^0.8.0 specifier: ^0.8.0
version: 0.8.0(svelte@4.1.1) version: 0.8.0(svelte@4.1.1)
svelte-french-toast:
specifier: ^1.2.0
version: 1.2.0(svelte@4.1.1)
svelte-lazy: svelte-lazy:
specifier: ^1.2.1 specifier: ^1.2.1
version: 1.2.1(svelte@4.1.1) version: 1.2.1(svelte@4.1.1)
@ -1143,16 +1140,6 @@ packages:
'@jridgewell/resolve-uri': 3.1.0 '@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/sourcemap-codec': 1.4.15
/@leveluptuts/svelte-side-menu@1.0.5:
resolution: {integrity: sha512-czPmr0LEjVhr7qXYZtH4PrUrfHPYg9nS7ZHH+xDINKoajkERWlHlsBtdoJC5ZTMzGvdhLCLfF70q4xeMzJgS7w==}
dev: false
/@leveluptuts/svelte-toy@2.0.3:
resolution: {integrity: sha512-A2pjSG4UQbWLffD3r3cC8zaNwtTmBoNJJ2TIlNzVtdlUXk1xrYtscMopB+N2ztHnfjlKWyIee4TEnF3OBVwIfQ==}
dependencies:
lodash.set: 4.3.2
dev: false
/@lucia-auth/adapter-mysql@1.1.1(lucia-auth@1.8.0): /@lucia-auth/adapter-mysql@1.1.1(lucia-auth@1.8.0):
resolution: {integrity: sha512-br+/OBDNJ+eRc6RrZnnC20ef+2VEMrXFxNYvsbryPw64ito7vg40STblpENdjJF0o4R10mjWTO43wQ+56jyXLA==} resolution: {integrity: sha512-br+/OBDNJ+eRc6RrZnnC20ef+2VEMrXFxNYvsbryPw64ito7vg40STblpENdjJF0o4R10mjWTO43wQ+56jyXLA==}
peerDependencies: peerDependencies:
@ -2719,10 +2706,6 @@ packages:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true dev: true
/lodash.set@4.3.2:
resolution: {integrity: sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==}
dev: false
/logform@2.5.1: /logform@2.5.1:
resolution: {integrity: sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==} resolution: {integrity: sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==}
dependencies: dependencies:
@ -3857,6 +3840,15 @@ packages:
svelte: 4.1.1 svelte: 4.1.1
dev: true dev: true
/svelte-french-toast@1.2.0(svelte@4.1.1):
resolution: {integrity: sha512-5PW+6RFX3xQPbR44CngYAP1Sd9oCq9P2FOox4FZffzJuZI2mHOB7q5gJBVnOiLF5y3moVGZ7u2bYt7+yPAgcEQ==}
peerDependencies:
svelte: ^3.57.0 || ^4.0.0
dependencies:
svelte: 4.1.1
svelte-writable-derived: 3.1.0(svelte@4.1.1)
dev: false
/svelte-hmr@0.15.2(svelte@4.1.1): /svelte-hmr@0.15.2(svelte@4.1.1):
resolution: {integrity: sha512-q/bAruCvFLwvNbeE1x3n37TYFb3mTBJ6TrCq6p2CoFbSTNhDE9oAtEfpy+wmc9So8AG0Tja+X0/mJzX9tSfvIg==} resolution: {integrity: sha512-q/bAruCvFLwvNbeE1x3n37TYFb3mTBJ6TrCq6p2CoFbSTNhDE9oAtEfpy+wmc9So8AG0Tja+X0/mJzX9tSfvIg==}
engines: {node: ^12.20 || ^14.13.1 || >= 16} engines: {node: ^12.20 || ^14.13.1 || >= 16}
@ -3939,6 +3931,14 @@ packages:
typescript: 5.1.6 typescript: 5.1.6
dev: true dev: true
/svelte-writable-derived@3.1.0(svelte@4.1.1):
resolution: {integrity: sha512-cTvaVFNIJ036vSDIyPxJYivKC7ZLtcFOPm1Iq6qWBDo1fOHzfk6ZSbwaKrxhjgy52Rbl5IHzRcWgos6Zqn9/rg==}
peerDependencies:
svelte: ^3.2.1 || ^4.0.0-next.1
dependencies:
svelte: 4.1.1
dev: false
/svelte@4.1.1: /svelte@4.1.1:
resolution: {integrity: sha512-Enick5fPFISLoVy0MFK45cG+YlQt6upw8skEK9zzTpJnH1DqEv8xOZwizCGSo3Q6HZ7KrZTM0J18poF7aQg5zw==} resolution: {integrity: sha512-Enick5fPFISLoVy0MFK45cG+YlQt6upw8skEK9zzTpJnH1DqEv8xOZwizCGSo3Q6HZ7KrZTM0J18poF7aQg5zw==}
engines: {node: '>=16'} engines: {node: '>=16'}

2
src/app.d.ts vendored
View file

@ -10,7 +10,7 @@ type User = Omit<User, 'created_at' | 'updated_at'>;
declare global { declare global {
namespace App { namespace App {
interface PageData { interface PageData {
flash?: { type: 'success' | 'error'; message: string }; flash?: { type: 'success' | 'error' | 'info'; message: string };
} }
interface Locals { interface Locals {
auth: import('lucia').AuthRequest; auth: import('lucia').AuthRequest;

View file

@ -19,6 +19,9 @@
import RemoveCollectionDialog from '../../dialog/RemoveCollectionDialog.svelte'; import RemoveCollectionDialog from '../../dialog/RemoveCollectionDialog.svelte';
import RemoveWishlistDialog from '../../dialog/RemoveWishlistDialog.svelte'; import RemoveWishlistDialog from '../../dialog/RemoveWishlistDialog.svelte';
import type { ListGameSchema, SearchSchema } from '$lib/zodValidation'; import type { ListGameSchema, SearchSchema } from '$lib/zodValidation';
import { Label } from '$components/ui/label';
import { Input } from '$components/ui/input';
import { Button } from '$components/ui/button';
interface RemoveGameEvent extends Event { interface RemoveGameEvent extends Event {
detail: GameType | SavedGameType; detail: GameType | SavedGameType;
@ -147,22 +150,11 @@
<form id="search-form" action="/search" method="GET"> <form id="search-form" action="/search" method="GET">
<div class="search"> <div class="search">
<fieldset class="text-search" aria-busy={submitting} disabled={submitting}> <fieldset class="text-search" aria-busy={submitting} disabled={submitting}>
<label class="label" for="q"> <Label for="label">Search</Label>
<span>Search</span> <Input type="text" id="q" class={$errors.q && "outline outline-destructive"} name="search" placeholder="Search board games" data-invalid={$errors.q} bind:value={$form.q} />
<input {#if $errors.q}
id="q" <p class="text-sm text-destructive">{$errors.q}</p>
class="input" {/if}
name="q"
bind:value={$form.q}
data-invalid={$errors?.q}
{...$constraints.q}
type="search"
aria-label="Search board games"
placeholder="Search board games"
/>
</label>
{#if $errors?.q}<span class="invalid">{$errors?.q}</span>{/if}
<input id="skip" type="hidden" name="skip" bind:value={$form.skip} /> <input id="skip" type="hidden" name="skip" bind:value={$form.skip} />
<input id="limit" type="hidden" name="limit" bind:value={$form.limit} /> <input id="limit" type="hidden" name="limit" bind:value={$form.limit} />
</fieldset> </fieldset>
@ -196,15 +188,7 @@
{/if} {/if}
</div> </div>
{#if showButton} {#if showButton}
<button <Button type="submit">Submit</Button>
id="search-submit"
class="btn"
type="submit"
disabled={submitting}
bind:this={submitButton}
>
Submit
</button>
{/if} {/if}
</form> </form>

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { fly, fade } from 'svelte/transition'; import { fly, fade } from 'svelte/transition';
import { flip } from 'svelte/animate'; import { flip } from 'svelte/animate';
import Portal from '../../Portal.svelte'; import Portal from '$lib/Portal.svelte';
import ToastMessage from './ToastMessage.svelte'; import ToastMessage from './ToastMessage.svelte';
import { toast } from './toast'; import { toast } from './toast';
</script> </script>
@ -10,6 +10,8 @@
<div class="toast-wrapper"> <div class="toast-wrapper">
{#each $toast as toastData (toastData.id)} {#each $toast as toastData (toastData.id)}
<div <div
role="button"
tabindex="0"
aria-label={toastData.dismissible ? 'Click to dismiss' : `${toastData.message}`} aria-label={toastData.dismissible ? 'Click to dismiss' : `${toastData.message}`}
on:click={() => toastData.dismissible && toast.remove(toastData.id)} on:click={() => toastData.dismissible && toast.remove(toastData.id)}
on:keydown={() => toastData.dismissible && toast.remove(toastData.id)} on:keydown={() => toastData.dismissible && toast.remove(toastData.id)}

View file

@ -4,40 +4,40 @@ import { ToastType } from '$lib/types';
// Custom store // Custom store
const newToast = () => { const newToast = () => {
const { subscribe, update } = writable<ToastData[]>([]); const { subscribe, update } = writable<ToastData[]>([]);
function send( function send(
message: string, message: string,
{ {
duration = 2000, duration = 2000,
type = ToastType.INFO, type = ToastType.INFO,
autoDismiss = true, autoDismiss = true,
dismissible = false, dismissible = false,
showButton = false showButton = false
} = {} } = {}
) { ) {
const id = Math.floor(Math.random() * 1000); const id = Math.floor(Math.random() * 1000);
const newMessage: ToastData = { const newMessage: ToastData = {
id, id,
duration, duration,
autoDismiss, autoDismiss,
dismissible, dismissible,
showButton, showButton,
type, type,
message message
}; };
update((store) => [...store, newMessage]); update((store) => [...store, newMessage]);
} }
function remove(id: number) { function remove(id: number) {
update((store) => { update((store) => {
const newStore = store.filter((item: ToastData) => item.id !== id); const newStore = store.filter((item: ToastData) => item.id !== id);
return [...newStore]; return [...newStore];
}); });
} }
return { subscribe, send, remove }; return { subscribe, send, remove };
}; };
export const toast = newToast(); export const toast = newToast();

View file

@ -1,6 +1,8 @@
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client';
import type { SvelteComponent } from 'svelte'; import type { SvelteComponent } from 'svelte';
export type Message = { status: 'error' | 'success' | 'warning' | 'info'; text: string };
export const gameInclude = Prisma.validator<Prisma.CollectionItemInclude>()({ export const gameInclude = Prisma.validator<Prisma.CollectionItemInclude>()({
game: { game: {
select: { select: {

View file

@ -1,11 +1,9 @@
import { loadFlash } from 'sveltekit-flash-message/server'; import { loadFlash } from 'sveltekit-flash-message/server';
import type { LayoutServerLoad } from './$types'; import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ url, locals }) => { export const load: LayoutServerLoad = loadFlash(async ({ url, locals }) => {
return { return {
url: url.pathname, url: url.pathname,
user: locals.user user: locals.user
}; };
}; });
// loadFlash(

View file

@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import "../app.postcss"; import "../app.postcss";
import { onMount } from "svelte"; import { onMount } from "svelte";
// import { getFlash } from 'sveltekit-flash-message/client'; import { getFlash } from 'sveltekit-flash-message/client';
import toast, { Toaster } from 'svelte-french-toast';
import { navigating, page } from '$app/stores'; import { navigating, page } from '$app/stores';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import debounce from 'just-debounce-it'; import debounce from 'just-debounce-it';
// import { Toy } from '@leveluptuts/svelte-toy';
import 'iconify-icon'; import 'iconify-icon';
import Analytics from '$lib/components/analytics.svelte'; import Analytics from '$lib/components/analytics.svelte';
import Header from '$lib/components/header/index.svelte'; import Header from '$lib/components/header/index.svelte';
@ -16,9 +16,7 @@
import { boredState } from '$lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import { collectionStore } from '$lib/stores/collectionStore'; import { collectionStore } from '$lib/stores/collectionStore';
import { wishlistStore } from '$lib/stores/wishlistStore'; import { wishlistStore } from '$lib/stores/wishlistStore';
import Toast from '$lib/components/toast/Toast.svelte';
import { theme } from '$state/theme'; import { theme } from '$state/theme';
// import '$styles/styles.pcss';
import type { SavedGameType } from '$lib/types'; import type { SavedGameType } from '$lib/types';
$: { $: {
@ -33,7 +31,6 @@
} }
$: isOpen = $boredState?.dialog?.isOpen; $: isOpen = $boredState?.dialog?.isOpen;
// const flash = getFlash(page);
if (browser) { if (browser) {
const collator = new Intl.Collator('en'); const collator = new Intl.Collator('en');
@ -74,6 +71,27 @@
export let data; export let data;
$: ({ user } = data); $: ({ user } = data);
const flash = getFlash(page);
let flashType;
let flashMessage;
$: flashType = $flash?.type;
$: flashMessage = $flash?.message;
console.log('flashType', flashType);
console.log('flashMessage', flashMessage);
// if ($flash && flashType && flashMessage) {
// switch (flashType) {
// case 'success':
// toast.success(flashMessage);
// break;
// case 'error':
// toast.error(flashMessage);
// break;
// default:
// toast.error(flashMessage);
// }
// }
onMount(() => { onMount(() => {
// set the theme to the user's active theme // set the theme to the user's active theme
$theme = user?.theme || 'system'; $theme = user?.theme || 'system';
@ -85,27 +103,29 @@
<Analytics /> <Analytics />
{/if} {/if}
<!-- {#if dev}
<Toy
register={{
boredState,
collectionStore,
wishlistStore,
gameStore,
toast
}}
/>
{/if} -->
<div class="wrapper"> <div class="wrapper">
<Header user="{data.user}"></Header> <Header user={data.user} />
<main>
<Transition url={data.url} transition={{ type: 'page' }}> <main>
<slot></slot> <Transition url={data.url} transition={{ type: 'page' }}>
</Transition> <slot />
</main> </Transition>
</main>
<Footer /> <Footer />
</div> </div>
{#if $flash}
<div class="status"
class:error={$flash.type == 'error'}
class:success={$flash.type == 'success'}
>
{$flash.message}
</div>
{/if}
<Toaster />
{#if $boredState?.loading} {#if $boredState?.loading}
<Portal> <Portal>
<div class="loading"> <div class="loading">
@ -120,13 +140,16 @@
<svelte:component this={$boredState?.dialog?.content}></svelte:component> <svelte:component this={$boredState?.dialog?.content}></svelte:component>
</div> </div>
{/if} {/if}
<Toast></Toast>
<!-- {#if $flash}
{@const bg = $flash.type == 'success' ? '#3D9970' : '#FF4136'}
<div style:background-color={bg} class="flash">{$flash.message}</div>
{/if} -->
<style lang="postcss"> <style lang="postcss">
.flash {
display: inline-block;
position: absolute;
place-items: center;
padding: 0.5rem;
border-radius: 2px;
}
.loading { .loading {
position: fixed; position: fixed;
top: 50%; top: 50%;

View file

@ -1,5 +1,6 @@
import { fail, redirect } from '@sveltejs/kit'; import { fail } from '@sveltejs/kit';
import { setError, superValidate } from 'sveltekit-superforms/server'; import { setError, superValidate } from 'sveltekit-superforms/server';
import { redirect } from 'sveltekit-flash-message/server';
import { auth } from '$lib/server/lucia'; import { auth } from '$lib/server/lucia';
import prisma from '$lib/prisma.js'; import prisma from '$lib/prisma.js';
import { userSchema } from '$lib/config/zod-schemas'; import { userSchema } from '$lib/config/zod-schemas';
@ -13,7 +14,8 @@ export const load = async (event) => {
console.log('sign in load event', event); console.log('sign in load event', event);
const session = await event.locals.auth.validate(); const session = await event.locals.auth.validate();
if (session) { if (session) {
throw redirect(302, '/'); const message = { type: 'info', message: 'You are already signed in' };
throw redirect('/', message, event);
} }
const form = await superValidate(event, signInSchema); const form = await superValidate(event, signInSchema);
return { return {
@ -40,9 +42,9 @@ export const actions = {
}); });
event.locals.auth.setSession(session); event.locals.auth.setSession(session);
const user = await prisma.authUser.findUnique({ const user = await prisma.user.findUnique({
where: { where: {
id: session.userId id: session.user.userId
}, },
include: { include: {
roles: { roles: {
@ -86,6 +88,8 @@ export const actions = {
} }
form.data.username = ''; form.data.username = '';
form.data.password = ''; form.data.password = '';
return { form }; const message = { type: 'success', message: 'Signed In!' };
// return { form, message };
throw redirect('/', message, event);
} }
}; };

View file

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import * as flashModule from 'sveltekit-flash-message/client';
import { AlertCircle } from "lucide-svelte"; import { AlertCircle } from "lucide-svelte";
import { userSchema } from '$lib/config/zod-schemas.js'; import { userSchema } from '$lib/config/zod-schemas.js';
import Label from '$components/ui/label/Label.svelte'; import Label from '$components/ui/label/Label.svelte';
@ -9,6 +10,17 @@
export let data; export let data;
const { form, errors, enhance, delayed } = superForm(data.form, { const { form, errors, enhance, delayed } = superForm(data.form, {
flashMessage: {
module: flashModule,
onError: ({ result, message }) => {
// Error handling for the flash message:
// - result is the ActionResult
// - message is the flash store (not the status message store)
const errorMessage = result.error.message
message.set({ type: 'error', message: errorMessage });
}
},
syncFlashMessage: false,
taintedMessage: null, taintedMessage: null,
validationMethod: 'oninput', validationMethod: 'oninput',
delayMs: 0, delayMs: 0,

View file

@ -1,11 +1,13 @@
import { fail, redirect } from '@sveltejs/kit'; import { fail, redirect, error } from '@sveltejs/kit';
import { setError, superValidate } from 'sveltekit-superforms/server'; import { setError, superValidate } from 'sveltekit-superforms/server';
import { redirect as flashRedirect } from 'sveltekit-flash-message/server'; import { redirect as flashRedirect } from 'sveltekit-flash-message/server';
import { LuciaError } from 'lucia'; import { LuciaError } from 'lucia';
import { auth } from '$lib/server/lucia'; import { auth } from '$lib/server/lucia';
import { userSchema } from '$lib/config/zod-schemas'; import { userSchema } from '$lib/config/zod-schemas';
import { add_user_to_role } from '$db/roles'; import { add_user_to_role } from '$server/roles';
import prisma from '$lib/prisma.js'; import prisma from '$lib/prisma.js';
import { Schema } from 'zod';
import type { Message } from '$lib/types.js';
const signUpSchema = userSchema const signUpSchema = userSchema
.pick({ .pick({
@ -38,7 +40,7 @@ export const load = async (event) => {
if (session) { if (session) {
throw redirect(302, '/'); throw redirect(302, '/');
} }
const form = await superValidate(event, signUpSchema); const form = await superValidate<typeof signUpSchema, Message>(event, signUpSchema);
return { return {
form form
}; };
@ -46,7 +48,7 @@ export const load = async (event) => {
export const actions = { export const actions = {
default: async (event) => { default: async (event) => {
const form = await superValidate(event, signUpSchema); const form = await superValidate<typeof signUpSchema, Message>(event, signUpSchema);
if (!form.valid) { if (!form.valid) {
return fail(400, { return fail(400, {
@ -99,13 +101,18 @@ export const actions = {
event.locals.auth.setSession(session); event.locals.auth.setSession(session);
// const message = { type: 'success', message: 'Signed Up!' } as const; // const message = { type: 'success', message: 'Signed Up!' } as const;
// throw flashRedirect(message, event); // throw flashRedirect(message, event);
} catch (error) { } catch (e) {
if (error instanceof LuciaError && error.message === `DUPLICATE_KEY_ID`) { if (e instanceof LuciaError && e.message === `DUPLICATE_KEY_ID`) {
// key already exists // key already exists
console.error(error); console.error('Lucia Error: ', e);
} }
console.log(error); console.log(e);
return setError(form, '', 'Unable to create your account. Please try again.'); const message = {
type: 'error',
message: 'Unable to create your account. Please try again.'
};
throw error(500, message);
// return setError(form, '', message);
} }
} }
}; };

View file

@ -1,9 +1,12 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores';
import { superForm } from 'sveltekit-superforms/client';
import * as flashModule from 'sveltekit-flash-message/client';
import Button from '$components/ui/button/Button.svelte'; import Button from '$components/ui/button/Button.svelte';
import Input from '$components/ui/input/Input.svelte'; import Input from '$components/ui/input/Input.svelte';
import Label from '$components/ui/label/Label.svelte'; import Label from '$components/ui/label/Label.svelte';
import { userSchema } from '$lib/config/zod-schemas.js'; import { userSchema } from '$lib/config/zod-schemas.js';
import { superForm } from 'sveltekit-superforms/client'; import toast from 'svelte-french-toast';
export let data; export let data;
@ -17,12 +20,35 @@ import { userSchema } from '$lib/config/zod-schemas.js';
}); });
const { form, errors, constraints, enhance, delayed } = superForm(data.form, { const { form, errors, constraints, enhance, delayed } = superForm(data.form, {
flashMessage: {
module: flashModule,
onError: ({ result, message }) => {
const errorMessage = result.error.message;
message.set({ type: 'error', message: errorMessage });
}
},
taintedMessage: null, taintedMessage: null,
validators: signUpSchema, validators: signUpSchema,
delayMs: 0, delayMs: 0,
}); });
const flash = flashModule.getFlash(page);
$: {
if ($flash) {
toast.error($flash.message, {
duration: 5000
});
}
}
</script> </script>
<!-- {#if $flash}
{@const bg = $flash.type == 'success' ? '#3D9970' : '#FF4136'}
<div style:background-color={bg} class="flash">{$flash.message}</div>
{/if} -->
<div class="page"> <div class="page">
<form method="POST" action="/auth/signup" use:enhance> <form method="POST" action="/auth/signup" use:enhance>
<div class="grid w-full max-w-sm items-center gap-2.5"> <div class="grid w-full max-w-sm items-center gap-2.5">

View file

@ -5,6 +5,8 @@
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import type { SuperValidated } from 'sveltekit-superforms'; import type { SuperValidated } from 'sveltekit-superforms';
import type { ModifyListGame } from '$lib/config/zod-schemas.js'; import type { ModifyListGame } from '$lib/config/zod-schemas.js';
import { onMount } from 'svelte';
import toast from 'svelte-french-toast';
// import { collectionStore } from '$lib/stores/collectionStore'; // import { collectionStore } from '$lib/stores/collectionStore';
// import type { GameType, SavedGameType } from '$lib/types'; // import type { GameType, SavedGameType } from '$lib/types';
// import { boredState } from '$lib/stores/boredState'; // import { boredState } from '$lib/stores/boredState';

View file

@ -14,7 +14,7 @@ const config = {
$assets: './src/assets', $assets: './src/assets',
$components: './src/components', $components: './src/components',
'$components/*': 'src/lib/components/*', '$components/*': 'src/lib/components/*',
$db: './src/db', $server: './src/server',
$lib: './src/lib', $lib: './src/lib',
$state: './src/state', $state: './src/state',
$styles: './src/styles', $styles: './src/styles',