Moving validations to separate folder, upgrading superforms v2, and shadcn components.

This commit is contained in:
Bradley Shellnut 2024-02-25 22:59:29 -08:00
parent becf1d8349
commit 8f9db3fea5
53 changed files with 997 additions and 583 deletions

View file

@ -26,7 +26,7 @@
}, },
"devDependencies": { "devDependencies": {
"@melt-ui/pp": "^0.3.0", "@melt-ui/pp": "^0.3.0",
"@melt-ui/svelte": "^0.74.1", "@melt-ui/svelte": "^0.74.2",
"@playwright/test": "^1.41.2", "@playwright/test": "^1.41.2",
"@resvg/resvg-js": "^2.6.0", "@resvg/resvg-js": "^2.6.0",
"@sveltejs/adapter-auto": "^3.1.1", "@sveltejs/adapter-auto": "^3.1.1",
@ -34,14 +34,14 @@
"@sveltejs/kit": "^2.5.1", "@sveltejs/kit": "^2.5.1",
"@sveltejs/vite-plugin-svelte": "^3.0.2", "@sveltejs/vite-plugin-svelte": "^3.0.2",
"@types/cookie": "^0.6.0", "@types/cookie": "^0.6.0",
"@types/node": "^20.11.19", "@types/node": "^20.11.20",
"@types/pg": "^8.11.0", "@types/pg": "^8.11.1",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",
"autoprefixer": "^10.4.17", "autoprefixer": "^10.4.17",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"drizzle-kit": "^0.20.14", "drizzle-kit": "^0.20.14",
"eslint": "^8.56.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.35.1", "eslint-plugin-svelte": "^2.35.1",
"just-clone": "^6.2.0", "just-clone": "^6.2.0",
@ -53,17 +53,17 @@
"prettier": "^3.2.5", "prettier": "^3.2.5",
"prettier-plugin-svelte": "^3.2.1", "prettier-plugin-svelte": "^3.2.1",
"prisma": "^5.9.1", "prisma": "^5.9.1",
"sass": "^1.71.0", "sass": "^1.71.1",
"satori": "^0.10.13", "satori": "^0.10.13",
"satori-html": "^0.3.2", "satori-html": "^0.3.2",
"svelte": "^4.2.11", "svelte": "^4.2.12",
"svelte-check": "^3.6.4", "svelte-check": "^3.6.4",
"svelte-meta-tags": "^3.1.0", "svelte-meta-tags": "^3.1.0",
"svelte-preprocess": "^5.1.3", "svelte-preprocess": "^5.1.3",
"svelte-sequential-preprocessor": "^2.0.1", "svelte-sequential-preprocessor": "^2.0.1",
"sveltekit-flash-message": "^2.4.2", "sveltekit-flash-message": "^2.4.2",
"sveltekit-rate-limiter": "^0.4.3", "sveltekit-rate-limiter": "^0.4.3",
"sveltekit-superforms": "^1.13.4", "sveltekit-superforms": "^2.6.2",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tslib": "^2.6.1", "tslib": "^2.6.1",
@ -93,31 +93,31 @@
"@sveltejs/adapter-vercel": "^5.1.0", "@sveltejs/adapter-vercel": "^5.1.0",
"@types/feather-icons": "^4.29.4", "@types/feather-icons": "^4.29.4",
"@vercel/og": "^0.5.20", "@vercel/og": "^0.5.20",
"bits-ui": "^0.18.1", "bits-ui": "^0.18.2",
"boardgamegeekclient": "^1.9.1", "boardgamegeekclient": "^1.9.1",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"cookie": "^0.6.0", "cookie": "^0.6.0",
"drizzle-orm": "^0.29.4", "drizzle-orm": "^0.29.4",
"feather-icons": "^4.29.1", "feather-icons": "^4.29.1",
"formsnap": "^0.4.4", "formsnap": "^0.5.1",
"html-entities": "^2.4.0", "html-entities": "^2.4.0",
"iconify-icon": "^2.0.0", "iconify-icon": "^2.0.0",
"just-kebab-case": "^4.2.0", "just-kebab-case": "^4.2.0",
"loader": "^2.1.1", "loader": "^2.1.1",
"lucia": "3.0.1", "lucia": "3.0.1",
"lucide-svelte": "^0.335.0", "lucide-svelte": "^0.340.0",
"mysql2": "^3.9.1", "mysql2": "^3.9.1",
"nanoid": "^5.0.6", "nanoid": "^5.0.6",
"open-props": "^1.6.19", "open-props": "^1.6.19",
"oslo": "^1.1.2", "oslo": "^1.1.3",
"pg": "^8.11.3", "pg": "^8.11.3",
"postgres": "^3.4.3", "postgres": "^3.4.3",
"radix-svelte": "^0.9.0", "radix-svelte": "^0.9.0",
"svelte-french-toast": "^1.2.0", "svelte-french-toast": "^1.2.0",
"svelte-lazy-loader": "^1.0.0", "svelte-lazy-loader": "^1.0.0",
"tailwind-merge": "^2.2.1", "tailwind-merge": "^2.2.1",
"tailwind-variants": "^0.1.20", "tailwind-variants": "^0.2.0",
"tailwindcss-animate": "^1.0.6", "tailwindcss-animate": "^1.0.6",
"zod-to-json-schema": "^3.22.4" "zod-to-json-schema": "^3.22.4"
} }

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,18 @@
<script lang="ts"> <script lang="ts">
import { zodClient } from 'sveltekit-superforms/adapters';
import { ConicGradient } from '@skeletonlabs/skeleton'; import { ConicGradient } from '@skeletonlabs/skeleton';
import type { ConicStop } from '@skeletonlabs/skeleton'; import type { ConicStop } from '@skeletonlabs/skeleton';
import { i } from "@inlang/sdk-js";
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
//import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte'; //import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
import { userSchema } from '$lib/config/zod-schemas'; import { userSchema } from '$lib/validations/zod-schemas';
import { AlertTriangle } from 'lucide-svelte'; import { AlertTriangle } from 'lucide-svelte';
import { i } from "@inlang/sdk-js"; import { signInSchema } from '$lib/validations/auth';
export let data; export let data;
const signInSchema = userSchema.pick({ email: true, password: true });
const { form, errors, enhance, delayed } = superForm(data.form, { const { form, errors, enhance, delayed } = superForm(data.form, {
taintedMessage: null, taintedMessage: null,
validators: signInSchema, validators: zodClient(signInSchema),
delayMs: 0 delayMs: 0
}); });
const conicStops: ConicStop[] = [ const conicStops: ConicStop[] = [

View file

@ -1,11 +1,13 @@
<script lang="ts"> <script lang="ts">
import { zodClient } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import { signUpSchema } from '$lib/config/zod-schemas'; import { signUpSchema } from '$lib/validations/auth';
export let data; export let data;
const { form, errors, enhance } = superForm(data.form, { const { form, errors, enhance } = superForm(data.form, {
taintedMessage: null, taintedMessage: null,
validators: signUpSchema, validators: zodClient(signUpSchema),
delayMs: 0 delayMs: 0
}); });
// $: termsValue = $form.terms as Writable<boolean>; // $: termsValue = $form.terms as Writable<boolean>;

View file

@ -9,10 +9,7 @@
</script> </script>
<AvatarPrimitive.Fallback <AvatarPrimitive.Fallback
class={cn( class={cn("flex h-full w-full items-center justify-center rounded-full bg-muted", className)}
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
{...$$restProps} {...$$restProps}
> >
<slot /> <slot />

View file

@ -11,10 +11,7 @@
<AvatarPrimitive.Root <AvatarPrimitive.Root
{delayMs} {delayMs}
class={cn( class={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)}
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
{...$$restProps} {...$$restProps}
> >
<slot /> <slot />

View file

@ -9,5 +9,5 @@ export {
// //
Root as Avatar, Root as Avatar,
Image as AvatarImage, Image as AvatarImage,
Fallback as AvatarFallback Fallback as AvatarFallback,
}; };

View file

@ -12,19 +12,19 @@ const buttonVariants = tv({
"border border-input bg-background hover:bg-accent hover:text-accent-foreground", "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground", ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline" link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {
default: "h-10 px-4 py-2", default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3", sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8", lg: "h-11 rounded-md px-8",
icon: "h-10 w-10" icon: "h-10 w-10",
} },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
size: "default" size: "default",
} },
}); });
type Variant = VariantProps<typeof buttonVariants>["variant"]; type Variant = VariantProps<typeof buttonVariants>["variant"];
@ -45,5 +45,5 @@ export {
Root as Button, Root as Button,
type Props as ButtonProps, type Props as ButtonProps,
type Events as ButtonEvents, type Events as ButtonEvents,
buttonVariants buttonVariants,
}; };

View file

@ -9,10 +9,7 @@
</script> </script>
<div <div
class={cn( class={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...$$restProps} {...$$restProps}
> >
<slot /> <slot />

View file

@ -18,7 +18,7 @@ export {
Description as CardDescription, Description as CardDescription,
Footer as CardFooter, Footer as CardFooter,
Header as CardHeader, Header as CardHeader,
Title as CardTitle Title as CardTitle,
}; };
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";

View file

@ -6,7 +6,7 @@
export let transition: $$Props["transition"] = slide; export let transition: $$Props["transition"] = slide;
export let transitionConfig: $$Props["transitionConfig"] = { export let transitionConfig: $$Props["transitionConfig"] = {
duration: 150 duration: 150,
}; };
</script> </script>

View file

@ -11,5 +11,5 @@ export {
// //
Root as Collapsible, Root as Collapsible,
Content as CollapsibleContent, Content as CollapsibleContent,
Trigger as CollapsibleTrigger Trigger as CollapsibleTrigger,
}; };

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import Check from "lucide-svelte/icons/check";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { Check } from "lucide-svelte";
type $$Props = DropdownMenuPrimitive.CheckboxItemProps; type $$Props = DropdownMenuPrimitive.CheckboxItemProps;
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents; type $$Events = DropdownMenuPrimitive.CheckboxItemEvents;
@ -14,7 +14,7 @@
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
bind:checked bind:checked
class={cn( class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
className className
)} )}
{...$$restProps} {...$$restProps}

View file

@ -6,6 +6,7 @@
type $$Events = DropdownMenuPrimitive.ContentEvents; type $$Events = DropdownMenuPrimitive.ContentEvents;
let className: $$Props["class"] = undefined; let className: $$Props["class"] = undefined;
export let sideOffset: $$Props["sideOffset"] = 4;
export let transition: $$Props["transition"] = flyAndScale; export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = undefined; export let transitionConfig: $$Props["transitionConfig"] = undefined;
export { className as class }; export { className as class };
@ -14,6 +15,7 @@
<DropdownMenuPrimitive.Content <DropdownMenuPrimitive.Content
{transition} {transition}
{transitionConfig} {transitionConfig}
{sideOffset}
class={cn( class={cn(
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none", "z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
className className

View file

@ -14,7 +14,7 @@
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
class={cn( class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
inset && "pl-8", inset && "pl-8",
className className
)} )}

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import Circle from "lucide-svelte/icons/circle";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { Circle } from "lucide-svelte";
type $$Props = DropdownMenuPrimitive.RadioItemProps; type $$Props = DropdownMenuPrimitive.RadioItemProps;
type $$Events = DropdownMenuPrimitive.RadioItemEvents; type $$Events = DropdownMenuPrimitive.RadioItemEvents;
@ -13,7 +13,7 @@
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
class={cn( class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
className className
)} )}
{value} {value}

View file

@ -8,9 +8,6 @@
export { className as class }; export { className as class };
</script> </script>
<span <span class={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...$$restProps}>
class={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...$$restProps}
>
<slot /> <slot />
</span> </span>

View file

@ -9,7 +9,7 @@
export let transition: $$Props["transition"] = flyAndScale; export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = { export let transitionConfig: $$Props["transitionConfig"] = {
x: -10, x: -10,
y: 0 y: 0,
}; };
export { className as class }; export { className as class };
</script> </script>

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import ChevronRight from "lucide-svelte/icons/chevron-right";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { ChevronRight } from "lucide-svelte";
type $$Props = DropdownMenuPrimitive.SubTriggerProps & { type $$Props = DropdownMenuPrimitive.SubTriggerProps & {
inset?: boolean; inset?: boolean;
@ -15,7 +15,7 @@
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
class={cn( class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent", "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground",
inset && "pl-8", inset && "pl-8",
className className
)} )}

View file

@ -44,5 +44,5 @@ export {
RadioGroup as DropdownMenuRadioGroup, RadioGroup as DropdownMenuRadioGroup,
SubContent as DropdownMenuSubContent, SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger, SubTrigger as DropdownMenuSubTrigger,
CheckboxItem as DropdownMenuCheckboxItem CheckboxItem as DropdownMenuCheckboxItem,
}; };

View file

@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import * as Button from "$lib/components/ui/button"; import * as Button from "$lib/components/ui/button";
type $$Props = Button.Props; type $$Props = Button.Props;
type $$Events = Button.Events;
</script> </script>
<Button.Root type="submit" {...$$restProps} on:click on:keydown> <Button.Root type="submit" {...$$restProps}>
<slot /> <slot />
</Button.Root> </Button.Root>

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Form as FormPrimitive } from "formsnap"; import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
@ -8,6 +8,10 @@
export { className as class }; export { className as class };
</script> </script>
<FormPrimitive.Description class={cn("text-sm text-muted-foreground", className)} {...$$restProps}> <FormPrimitive.Description
<slot /> class={cn("text-sm text-muted-foreground", className)}
{...$$restProps}
let:descriptionAttrs
>
<slot {descriptionAttrs} />
</FormPrimitive.Description> </FormPrimitive.Description>

View file

@ -0,0 +1,26 @@
<script lang="ts" context="module">
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { FormPathLeaves, SuperForm } from "sveltekit-superforms";
type T = Record<string, unknown>;
type U = unknown;
</script>
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPathLeaves<T>">
import type { HTMLAttributes } from "svelte/elements";
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils";
type $$Props = FormPrimitive.ElementFieldProps<T, U> & HTMLAttributes<HTMLElement>;
export let form: SuperForm<T>;
export let name: U;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<FormPrimitive.ElementField {form} {name} let:constraints let:errors let:tainted let:value>
<div class={cn("space-y-2", className)}>
<slot {constraints} {errors} {tainted} {value} />
</div>
</FormPrimitive.ElementField>

View file

@ -0,0 +1,26 @@
<script lang="ts">
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils";
type $$Props = FormPrimitive.FieldErrorsProps & {
errorClasses?: string | undefined | null;
};
let className: $$Props["class"] = undefined;
export { className as class };
export let errorClasses: $$Props["class"] = undefined;
</script>
<FormPrimitive.FieldErrors
class={cn("text-sm font-medium text-destructive", className)}
{...$$restProps}
let:errors
let:fieldErrorsAttrs
let:errorAttrs
>
<slot {errors} {fieldErrorsAttrs} {errorAttrs}>
{#each errors as error}
<div {...errorAttrs} class={cn(errorClasses)}>{error}</div>
{/each}
</slot>
</FormPrimitive.FieldErrors>

View file

@ -0,0 +1,26 @@
<script lang="ts" context="module">
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { FormPath, SuperForm } from "sveltekit-superforms";
type T = Record<string, unknown>;
type U = unknown;
</script>
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
import type { HTMLAttributes } from "svelte/elements";
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils";
type $$Props = FormPrimitive.FieldProps<T, U> & HTMLAttributes<HTMLElement>;
export let form: SuperForm<T>;
export let name: U;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<FormPrimitive.Field {form} {name} let:constraints let:errors let:tainted let:value>
<div class={cn("space-y-2", className)}>
<slot {constraints} {errors} {tainted} {value} />
</div>
</FormPrimitive.Field>

View file

@ -0,0 +1,31 @@
<script lang="ts" context="module">
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { FormPath, SuperForm } from "sveltekit-superforms";
type T = Record<string, unknown>;
type U = unknown;
</script>
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils";
type $$Props = FormPrimitive.FieldsetProps<T, U>;
export let form: SuperForm<T>;
export let name: U;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<FormPrimitive.Fieldset
{form}
{name}
let:constraints
let:errors
let:tainted
let:value
class={cn("space-y-2", className)}
>
<slot {constraints} {errors} {tainted} {value} />
</FormPrimitive.Fieldset>

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { Label as LabelPrimitive } from "bits-ui"; import type { Label as LabelPrimitive } from "bits-ui";
import { getFormField } from "formsnap"; import { getFormControl } from "formsnap";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { Label } from "$lib/components/ui/label"; import { Label } from "$lib/components/ui/label";
@ -9,9 +9,9 @@
let className: $$Props["class"] = undefined; let className: $$Props["class"] = undefined;
export { className as class }; export { className as class };
const { errors, ids } = getFormField(); const { labelAttrs } = getFormControl();
</script> </script>
<Label for={$ids.input} class={cn($errors && "text-destructive", className)} {...$$restProps}> <Label {...$labelAttrs} class={cn("data-[fs-error]:text-destructive", className)} {...$$restProps}>
<slot /> <slot {labelAttrs} />
</Label> </Label>

View file

@ -0,0 +1,17 @@
<script lang="ts">
import * as FormPrimitive from "formsnap";
import { cn } from "$lib/utils";
type $$Props = FormPrimitive.LegendProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<FormPrimitive.Legend
{...$$restProps}
class={cn("text-sm font-medium leading-none data-[fs-error]:text-destructive", className)}
let:legendAttrs
>
<slot {legendAttrs} />
</FormPrimitive.Legend>

View file

@ -1,82 +1,33 @@
import { Form as FormPrimitive, getFormField } from "formsnap"; import * as FormPrimitive from "formsnap";
import * as RadioGroupComp from "$lib/components/ui/radio-group";
import * as SelectComp from "$lib/components/ui/select";
import type { Writable } from "svelte/store";
import Item from "./form-item.svelte";
import Input from "./form-input.svelte";
import Textarea from "./form-textarea.svelte";
import Description from "./form-description.svelte"; import Description from "./form-description.svelte";
import Label from "./form-label.svelte"; import Label from "./form-label.svelte";
import Validation from "./form-validation.svelte"; import FieldErrors from "./form-field-errors.svelte";
import Checkbox from "./form-checkbox.svelte"; import Field from "./form-field.svelte";
import Switch from "./form-switch.svelte"; import Fieldset from "./form-fieldset.svelte";
import NativeSelect from "./form-native-select.svelte"; import Legend from "./form-legend.svelte";
import RadioGroup from "./form-radio-group.svelte"; import ElementField from "./form-element-field.svelte";
import Select from "./form-select.svelte";
import SelectTrigger from "./form-select-trigger.svelte";
import Button from "./form-button.svelte"; import Button from "./form-button.svelte";
const Root = FormPrimitive.Root;
const Field = FormPrimitive.Field;
const Control = FormPrimitive.Control; const Control = FormPrimitive.Control;
const RadioItem = RadioGroupComp.Item;
const NativeRadio = FormPrimitive.Radio;
const SelectContent = SelectComp.Content;
const SelectLabel = SelectComp.Label;
const SelectGroup = SelectComp.Group;
const SelectItem = SelectComp.Item;
const SelectSeparator = SelectComp.Separator;
export type TextareaGetFormField = Omit<ReturnType<typeof getFormField>, "value"> & {
value: Writable<string>;
};
export { export {
Root,
Field, Field,
Control, Control,
Item,
Input,
Label, Label,
Button, Button,
Switch, FieldErrors,
Select,
Checkbox,
Textarea,
Validation,
RadioGroup,
RadioItem,
Description, Description,
SelectContent, Fieldset,
SelectLabel, Legend,
SelectGroup, ElementField,
SelectItem,
SelectSeparator,
SelectTrigger,
NativeSelect,
NativeRadio,
// //
Root as Form,
Field as FormField, Field as FormField,
Control as FormControl, Control as FormControl,
Item as FormItem,
Input as FormInput,
Textarea as FormTextarea,
Description as FormDescription, Description as FormDescription,
Label as FormLabel, Label as FormLabel,
Validation as FormValidation, FieldErrors as FormFieldErrors,
NativeSelect as FormNativeSelect, Fieldset as FormFieldset,
NativeRadio as FormNativeRadio, Legend as FormLegend,
Checkbox as FormCheckbox, ElementField as FormElementField,
Switch as FormSwitch, Button as FormButton,
RadioGroup as FormRadioGroup,
RadioItem as FormRadioItem,
Select as FormSelect,
SelectContent as FormSelectContent,
SelectLabel as FormSelectLabel,
SelectGroup as FormSelectGroup,
SelectItem as FormSelectItem,
SelectSeparator as FormSelectSeparator,
SelectTrigger as FormSelectTrigger,
Button as FormButton
}; };

View file

@ -3,5 +3,5 @@ import Root from "./label.svelte";
export { export {
Root, Root,
// //
Root as Label Root as Label,
}; };

View file

@ -20,5 +20,5 @@ export {
Link as PaginationLink, Link as PaginationLink,
PrevButton as PaginationPrevButton, PrevButton as PaginationPrevButton,
NextButton as PaginationNextButton, NextButton as PaginationNextButton,
Ellipsis as PaginationEllipsis Ellipsis as PaginationEllipsis,
}; };

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import MoreHorizontal from "lucide-svelte/icons/more-horizontal";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { MoreHorizontal } from "lucide-svelte";
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLSpanElement>; type $$Props = HTMLAttributes<HTMLSpanElement>;

View file

@ -23,7 +23,7 @@
class={cn( class={cn(
buttonVariants({ buttonVariants({
variant: isActive ? "outline" : "ghost", variant: isActive ? "outline" : "ghost",
size size,
}), }),
className className
)} )}

View file

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui"; import { Pagination as PaginationPrimitive } from "bits-ui";
import ChevronRight from "lucide-svelte/icons/chevron-right";
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { ChevronRight } from "lucide-svelte";
type $$Props = PaginationPrimitive.NextButtonProps; type $$Props = PaginationPrimitive.NextButtonProps;
type $$Events = PaginationPrimitive.NextButtonEvents; type $$Events = PaginationPrimitive.NextButtonEvents;

View file

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui"; import { Pagination as PaginationPrimitive } from "bits-ui";
import ChevronLeft from "lucide-svelte/icons/chevron-left";
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { ChevronLeft } from "lucide-svelte";
type $$Props = PaginationPrimitive.PrevButtonProps; type $$Props = PaginationPrimitive.PrevButtonProps;
type $$Events = PaginationPrimitive.PrevButtonEvents; type $$Events = PaginationPrimitive.PrevButtonEvents;

View file

@ -28,7 +28,7 @@
asChild asChild
{...$$restProps} {...$$restProps}
> >
<nav {...builder} class={cn("mx-auto flex flex-col w-full items-center", className)}> <nav {...builder} class={cn("mx-auto flex w-full flex-col items-center", className)}>
<slot {pages} {range} {currentPage} /> <slot {pages} {range} {currentPage} />
</nav> </nav>
</PaginationPrimitive.Root> </PaginationPrimitive.Root>

View file

@ -1,90 +1,43 @@
import { z } from 'zod'; import { z } from 'zod';
import { userSchema } from './zod-schemas';
export type ListGame = { export const profileSchema = userSchema.pick({
id: string; firstName: true,
game_name: string; lastName: true,
game_id: string; email: true,
collection_id: string; username: true
wishlist_id: string;
times_played: number;
thumb_url: string | null;
in_collection: boolean;
in_wishlist: boolean;
};
export const modifyListGameSchema = z.object({
id: z.string()
}); });
export type ModifyListGame = typeof modifyListGameSchema; export const changeUserPasswordSchema = z
.object({
export const userSchema = z.object({ current_password: z.string({ required_error: 'Current Password is required' }),
firstName: z.string().trim().optional(), password: z.string({ required_error: 'Password is required' }).trim(),
lastName: z.string().trim().optional(), confirm_password: z.string({ required_error: 'Confirm Password is required' }).trim()
email: z.string().email({ message: 'Please enter a valid email address' }).optional(),
username: z
.string()
.trim()
.min(3, { message: 'Username must be at least 3 characters' })
.max(50, { message: 'Username must be less than 50 characters' }),
password: z
.string({ required_error: 'Password is required' })
.trim(),
confirm_password: z
.string({ required_error: 'Confirm Password is required' })
.trim(),
verified: z.boolean().default(false),
token: z.string().optional(),
receiveEmail: z.boolean().default(false),
createdAt: z.date().optional(),
updatedAt: z.date().optional()
});
export const signUpSchema = userSchema
.pick({
firstName: true,
lastName: true,
email: true,
username: true,
password: true,
confirm_password: true,
terms: true
}) })
.superRefine(({ confirm_password, password }, ctx) => { .superRefine(({ confirm_password, password }, ctx) => {
refinePasswords(confirm_password, password, ctx); refinePasswords(confirm_password, password, ctx);
}); });
export const signInSchema = userSchema.pick({
username: true,
password: true
});
export const updateUserPasswordSchema = userSchema export const updateUserPasswordSchema = userSchema
.pick({ password: true, confirm_password: true }) .pick({ password: true, confirm_password: true })
.superRefine(({ confirm_password, password }, ctx) => { .superRefine(({ confirm_password, password }, ctx) => {
refinePasswords(confirm_password, password, ctx); refinePasswords(confirm_password, password, ctx);
}); });
export const changeUserPasswordSchema = z export const refinePasswords = async function (
.object({ confirm_password: string,
current_password: z.string({ required_error: 'Current Password is required' }), password: string,
password: z ctx: z.RefinementCtx
.string({ required_error: 'Password is required' }) ) {
.trim(),
confirm_password: z
.string({ required_error: 'Confirm Password is required' })
.trim()
})
.superRefine(({ confirm_password, password }, ctx) => {
refinePasswords(confirm_password, password, ctx);
});
const refinePasswords = async function(confirm_password: string, password: string, ctx: z.RefinementCtx) {
comparePasswords(confirm_password, password, ctx); comparePasswords(confirm_password, password, ctx);
checkPasswordStrength(password, ctx); checkPasswordStrength(password, ctx);
} };
const comparePasswords = async function(confirm_password: string, password: string, ctx: z.RefinementCtx) { const comparePasswords = async function (
confirm_password: string,
password: string,
ctx: z.RefinementCtx
) {
if (confirm_password !== password) { if (confirm_password !== password) {
ctx.addIssue({ ctx.addIssue({
code: 'custom', code: 'custom',
@ -92,15 +45,14 @@ const comparePasswords = async function(confirm_password: string, password: stri
path: ['confirm_password'] path: ['confirm_password']
}); });
} }
} };
const checkPasswordStrength = async function (password: string, ctx: z.RefinementCtx) { const checkPasswordStrength = async function (password: string, ctx: z.RefinementCtx) {
const minimumLength = password.length < 8; const minimumLength = password.length < 8;
const maximumLength = password.length > 128; const maximumLength = password.length > 128;
const containsUppercase = (ch: string) => /[A-Z]/.test(ch); const containsUppercase = (ch: string) => /[A-Z]/.test(ch);
const containsLowercase = (ch: string) => /[a-z]/.test(ch); const containsLowercase = (ch: string) => /[a-z]/.test(ch);
const containsSpecialChar = (ch: string) => const containsSpecialChar = (ch: string) => /[`!@#$%^&*()_\-+=\[\]{};':"\\|,.<>\/?~ ]/.test(ch);
/[`!@#$%^&*()_\-+=\[\]{};':"\\|,.<>\/?~ ]/.test(ch);
let countOfUpperCase = 0, let countOfUpperCase = 0,
countOfLowerCase = 0, countOfLowerCase = 0,
countOfNumbers = 0, countOfNumbers = 0,
@ -139,7 +91,6 @@ const checkPasswordStrength = async function (password: string, ctx: z.Refinemen
errorMessage += ' Be less than 128 characters long.'; errorMessage += ' Be less than 128 characters long.';
} }
if (errorMessage.length > 'Your password:'.length) { if (errorMessage.length > 'Your password:'.length) {
ctx.addIssue({ ctx.addIssue({
code: 'custom', code: 'custom',
@ -147,4 +98,4 @@ const checkPasswordStrength = async function (password: string, ctx: z.Refinemen
path: ['password'] path: ['password']
}); });
} }
} };

View file

@ -0,0 +1,21 @@
import { refinePasswords } from "./account";
import { userSchema } from "./zod-schemas";
export const signUpSchema = userSchema
.pick({
firstName: true,
lastName: true,
email: true,
username: true,
password: true,
confirm_password: true,
terms: true
})
.superRefine(({ confirm_password, password }, ctx) => {
refinePasswords(confirm_password, password, ctx);
});
export const signInSchema = userSchema.pick({
username: true,
password: true
});

View file

@ -0,0 +1,45 @@
import { z } from 'zod';
export type ListGame = {
id: string;
game_name: string;
game_id: string;
collection_id: string;
wishlist_id: string;
times_played: number;
thumb_url: string | null;
in_collection: boolean;
in_wishlist: boolean;
};
export const modifyListGameSchema = z.object({
id: z.string()
});
export type ModifyListGame = typeof modifyListGameSchema;
export const userSchema = z.object({
firstName: z.string().trim().optional(),
lastName: z.string().trim().optional(),
email: z.string()
.trim()
.max(64, { message: 'Email must be less than 64 characters' })
.email({ message: 'Please enter a valid email address' })
.optional(),
username: z
.string()
.trim()
.min(3, { message: 'Username must be at least 3 characters' })
.max(50, { message: 'Username must be less than 50 characters' }),
password: z
.string({ required_error: 'Password is required' })
.trim(),
confirm_password: z
.string({ required_error: 'Confirm Password is required' })
.trim(),
verified: z.boolean().default(false),
token: z.string().optional(),
receiveEmail: z.boolean().default(false),
createdAt: z.date().optional(),
updatedAt: z.date().optional()
});

View file

@ -4,11 +4,11 @@ import { drizzle } from 'drizzle-orm/postgres-js';
import { migrate } from 'drizzle-orm/postgres-js/migrator'; import { migrate } from 'drizzle-orm/postgres-js/migrator';
const connection = postgres({ const connection = postgres({
host: process.env.DATABASE_HOST, host: process.env.DATABASE_HOST || 'localhost',
port: 3306, port: 3306,
user: process.env.DATABASE_USER, user: process.env.DATABASE_USER || 'root',
password: process.env.DATABASE_PASSWORD, password: process.env.DATABASE_PASSWORD || '',
database: process.env.DATABASE_DB, database: process.env.DATABASE_DB || 'boredgame',
ssl: 'require', ssl: 'require',
max: 1 max: 1
}); });

View file

@ -1,10 +1,11 @@
import { type Actions, error, fail, redirect } from '@sveltejs/kit'; import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import { and, eq } from 'drizzle-orm';
import { superValidate } from 'sveltekit-superforms/server'; import { superValidate } from 'sveltekit-superforms/server';
import { zod } from 'sveltekit-superforms/adapters';
import { modifyListGameSchema, type ListGame } from '$lib/config/zod-schemas.js'; import { modifyListGameSchema, type ListGame } from '$lib/config/zod-schemas.js';
import { search_schema } from '$lib/zodValidation.js'; import { search_schema } from '$lib/zodValidation.js';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import db from '$lib/drizzle'; import db from '$lib/drizzle';
import { and, eq } from 'drizzle-orm';
import { collection_items, collections, games } from '../../../../schema'; import { collection_items, collections, games } from '../../../../schema';
export const load: PageServerLoad = async ({ fetch, url, locals }) => { export const load: PageServerLoad = async ({ fetch, url, locals }) => {
@ -26,8 +27,8 @@ export const load: PageServerLoad = async ({ fetch, url, locals }) => {
skip skip
}; };
const searchForm = await superValidate(searchData, search_schema); const searchForm = await superValidate(searchData, zod(search_schema));
const listManageForm = await superValidate(modifyListGameSchema); const listManageForm = await superValidate(zod(modifyListGameSchema));
try { try {
const collection = await db.query.collections.findFirst({ const collection = await db.query.collections.findFirst({
@ -98,7 +99,7 @@ export const load: PageServerLoad = async ({ fetch, url, locals }) => {
export const actions: Actions = { export const actions: Actions = {
// Add game to a wishlist // Add game to a wishlist
add: async (event) => { add: async (event) => {
const form = await superValidate(event, modifyListGameSchema); const form = await superValidate(event, zod(modifyListGameSchema));
if (!event.locals.user) { if (!event.locals.user) {
throw fail(401); throw fail(401);
@ -160,7 +161,7 @@ export const actions: Actions = {
// Remove game from a wishlist // Remove game from a wishlist
remove: async (event) => { remove: async (event) => {
const { locals } = event; const { locals } = event;
const form = await superValidate(event, modifyListGameSchema); const form = await superValidate(event, zod(modifyListGameSchema));
if (!locals.user) { if (!locals.user) {
throw fail(401); throw fail(401);

View file

@ -1,7 +1,8 @@
import { redirect } from "@sveltejs/kit"; import { redirect } from "@sveltejs/kit";
import { superValidate } from "sveltekit-superforms/server";
import { zod } from 'sveltekit-superforms/adapters';
import type { PageServerLoad } from "../$types"; import type { PageServerLoad } from "../$types";
import { BggForm } from "$lib/zodValidation"; import { BggForm } from "$lib/zodValidation";
import { superValidate } from "sveltekit-superforms/server";
export const load: PageServerLoad = async ({ locals, fetch }) => { export const load: PageServerLoad = async ({ locals, fetch }) => {
const user = locals.user; const user = locals.user;
@ -9,7 +10,7 @@ export const load: PageServerLoad = async ({ locals, fetch }) => {
redirect(302, '/login'); redirect(302, '/login');
} }
const form = await superValidate({}, BggForm); const form = await superValidate({}, zod(BggForm));
return { form }; return { form };
} }

View file

@ -44,7 +44,7 @@ export const actions: Actions = {
// Add game to a wishlist // Add game to a wishlist
add: async (event) => { add: async (event) => {
const { params, locals, request } = event; const { params, locals, request } = event;
const form = await superValidate(event, modifyListGameSchema); const form = await superValidate(event, zod(modifyListGameSchema));
if (!locals.user) { if (!locals.user) {
throw fail(401); throw fail(401);

View file

@ -1,5 +1,6 @@
import { fail, redirect, type Actions } from "@sveltejs/kit"; import { fail, redirect, type Actions } from "@sveltejs/kit";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { zod } from 'sveltekit-superforms/adapters';
import { setError, superValidate } from 'sveltekit-superforms/server'; import { setError, superValidate } from 'sveltekit-superforms/server';
import { Argon2id } from "oslo/password"; import { Argon2id } from "oslo/password";
import db from "$lib/drizzle"; import db from "$lib/drizzle";
@ -9,7 +10,7 @@ import type { PageServerLoad } from "./$types";
import { users } from "../../../../../schema"; import { users } from "../../../../../schema";
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
const form = await superValidate(event, changeUserPasswordSchema); const form = await superValidate(event, zod(changeUserPasswordSchema));
const user = event.locals.user; const user = event.locals.user;
if (!user) { if (!user) {
@ -28,7 +29,7 @@ export const load: PageServerLoad = async (event) => {
export const actions: Actions = { export const actions: Actions = {
default: async (event) => { default: async (event) => {
const form = await superValidate(event, changeUserPasswordSchema); const form = await superValidate(event, zod(changeUserPasswordSchema));
if (!form.valid) { if (!form.valid) {
return fail(400, { return fail(400, {

View file

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { zodClient } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import { changeUserPasswordSchema, userSchema } from '$lib/config/zod-schemas'; import { changeUserPasswordSchema, userSchema } from '$lib/validations/zod-schemas.js';
import { Label } from '$components/ui/label'; import { Label } from '$components/ui/label';
import { Input } from '$components/ui/input'; import { Input } from '$components/ui/input';
import { Button } from '$components/ui/button'; import { Button } from '$components/ui/button';
@ -9,7 +10,7 @@
const { form, errors, enhance, delayed, message } = superForm(data.form, { const { form, errors, enhance, delayed, message } = superForm(data.form, {
taintedMessage: null, taintedMessage: null,
validators: changeUserPasswordSchema, validators: zodClient(changeUserPasswordSchema),
delayMs: 0 delayMs: 0
}); });
</script> </script>

View file

@ -1,8 +1,9 @@
import { fail, type Actions } from '@sveltejs/kit'; import { fail, type Actions } from '@sveltejs/kit';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { zod } from 'sveltekit-superforms/adapters';
import { message, setError, superValidate } from 'sveltekit-superforms/server'; import { message, setError, superValidate } from 'sveltekit-superforms/server';
import { redirect } from 'sveltekit-flash-message/server'; import { redirect } from 'sveltekit-flash-message/server';
import { userSchema } from '$lib/config/zod-schemas'; import { userSchema } from '$lib/validations/zod-schemas';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import { users } from '../../../../schema'; import { users } from '../../../../schema';
import db from '$lib/drizzle'; import db from '$lib/drizzle';
@ -15,7 +16,7 @@ const profileSchema = userSchema.pick({
}); });
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
const form = await superValidate(event, profileSchema); const form = await superValidate(event, zod(profileSchema));
if (!event.locals.user) { if (!event.locals.user) {
const message = { type: 'error', message: 'You are not signed in' } as const; const message = { type: 'error', message: 'You are not signed in' } as const;
@ -37,7 +38,7 @@ export const load: PageServerLoad = async (event) => {
export const actions: Actions = { export const actions: Actions = {
default: async (event) => { default: async (event) => {
const form = await superValidate(event, profileSchema); const form = await superValidate(event, zod(profileSchema));
if (!form.valid) { if (!form.valid) {
return fail(400, { return fail(400, {

View file

@ -1,7 +1,8 @@
<script lang="ts"> <script lang="ts">
import { zodClient } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import { AlertTriangle, KeyRound } from 'lucide-svelte'; import { AlertTriangle, KeyRound } from 'lucide-svelte';
import { userSchema } from '$lib/config/zod-schemas'; import { profileSchema } from '$lib/validations/account';
import * as Alert from "$lib/components/ui/alert"; import * as Alert from "$lib/components/ui/alert";
import { Label } from '$lib/components/ui/label'; import { Label } from '$lib/components/ui/label';
import { Input } from '$components/ui/input'; import { Input } from '$components/ui/input';
@ -9,16 +10,9 @@
export let data; export let data;
const profileSchema = userSchema.pick({
firstName: true,
lastName: true,
email: true,
username: true
});
const { form, errors, enhance, message } = superForm(data.form, { const { form, errors, enhance, message } = superForm(data.form, {
taintedMessage: null, taintedMessage: null,
validators: profileSchema, validators: zodClient(profileSchema),
delayMs: 0 delayMs: 0
}); });
</script> </script>

View file

@ -1,4 +1,5 @@
import { error, redirect, type Actions } from '@sveltejs/kit'; import { error, redirect, type Actions } from '@sveltejs/kit';
import { zod } from 'sveltekit-superforms/adapters';
import { superValidate } from 'sveltekit-superforms/server'; import { superValidate } from 'sveltekit-superforms/server';
import { modifyListGameSchema } from '$lib/config/zod-schemas.js'; import { modifyListGameSchema } from '$lib/config/zod-schemas.js';
import db from '$lib/drizzle.js'; import db from '$lib/drizzle.js';
@ -49,7 +50,7 @@ export const actions: Actions = {
// Add game to a wishlist // Add game to a wishlist
add: async (event) => { add: async (event) => {
const { locals } = event; const { locals } = event;
const form = await superValidate(event, modifyListGameSchema); const form = await superValidate(event, zod(modifyListGameSchema));
try { try {
if (!locals.user) { if (!locals.user) {
@ -111,7 +112,7 @@ export const actions: Actions = {
// Remove game from a wishlist // Remove game from a wishlist
remove: async (event) => { remove: async (event) => {
const { locals } = event; const { locals } = event;
const form = await superValidate(event, modifyListGameSchema); const form = await superValidate(event, zod(modifyListGameSchema));
try { try {
if (!locals.user) { if (!locals.user) {

View file

@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { fade, fly } from 'svelte/transition';
import { Image } from 'svelte-lazy-loader'; import { Image } from 'svelte-lazy-loader';
import { Dices, ExternalLinkIcon, MinusIcon, PlusIcon } from 'lucide-svelte'; import { Dices, ExternalLinkIcon, MinusIcon, PlusIcon } from 'lucide-svelte';
import type { SavedGameType } from '$lib/types'; import type { SavedGameType } from '$lib/types';

View file

@ -1,4 +1,5 @@
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { zod } from 'sveltekit-superforms/adapters';
import { superValidate } from 'sveltekit-superforms/server'; import { superValidate } from 'sveltekit-superforms/server';
import kebabCase from 'just-kebab-case'; import kebabCase from 'just-kebab-case';
import type { GameType, SearchQuery } from '$lib/types'; import type { GameType, SearchQuery } from '$lib/types';
@ -16,7 +17,7 @@ async function searchForGames(
try { try {
console.log('urlQueryParams search games', urlQueryParams); console.log('urlQueryParams search games', urlQueryParams);
const headers: HeadersInit = new Headers(); const headers = new Headers();
headers.set('Content-Type', 'application/json'); headers.set('Content-Type', 'application/json');
const requestInit: RequestInit = { const requestInit: RequestInit = {
method: 'GET', method: 'GET',
@ -112,7 +113,7 @@ export const load: PageServerLoad = async ({ locals, fetch, url }) => {
skip: Number(searchParams.skip || defaults.skip), skip: Number(searchParams.skip || defaults.skip),
limit: Number(searchParams.limit || defaults.limit), limit: Number(searchParams.limit || defaults.limit),
exact: searchParams.exact ? searchParams.exact === 'true' : defaults.exact exact: searchParams.exact ? searchParams.exact === 'true' : defaults.exact
}, search_schema); }, zod(search_schema));
const queryParams: SearchQuery = { const queryParams: SearchQuery = {
limit: form.data?.limit, limit: form.data?.limit,
@ -189,7 +190,7 @@ export const load: PageServerLoad = async ({ locals, fetch, url }) => {
export const actions = { export const actions = {
random: async ({ request, locals, fetch }): Promise<any> => { random: async ({ request, locals, fetch }): Promise<any> => {
const form = await superValidate(request, search_schema); const form = await superValidate(request, zod(search_schema));
const queryParams: SearchQuery = { const queryParams: SearchQuery = {
order_by: 'rank', order_by: 'rank',
ascending: false, ascending: false,

View file

@ -1,21 +1,17 @@
import { fail, type Actions } from '@sveltejs/kit'; import { fail, type Actions } from '@sveltejs/kit';
import { eq, sql } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { zod } from 'sveltekit-superforms/adapters';
import { setError, superValidate } from 'sveltekit-superforms/server'; import { setError, superValidate } from 'sveltekit-superforms/server';
import { redirect } from 'sveltekit-flash-message/server'; import { redirect } from 'sveltekit-flash-message/server';
import { lucia } from '$lib/server/auth'; import { lucia } from '$lib/server/auth';
import { Argon2id } from 'oslo/password'; import { Argon2id } from 'oslo/password';
import { userSchema } from '$lib/config/zod-schemas';
import db from '$lib/drizzle'; import db from '$lib/drizzle';
import { signInSchema } from '$lib/validations/auth'
import { collections, users, wishlists } from '../../../schema'; import { collections, users, wishlists } from '../../../schema';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
const signInSchema = userSchema.pick({
username: true,
password: true
});
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
const form = await superValidate(event, signInSchema); const form = await superValidate(event, zod(signInSchema));
console.log('login load event', event); console.log('login load event', event);
if (event.locals.user) { if (event.locals.user) {
@ -31,7 +27,7 @@ export const load: PageServerLoad = async (event) => {
export const actions: Actions = { export const actions: Actions = {
default: async (event) => { default: async (event) => {
const { locals } = event; const { locals } = event;
const form = await superValidate(event, signInSchema); const form = await superValidate(event, zod(signInSchema));
if (!form.valid) { if (!form.valid) {
form.data.password = ''; form.data.password = '';

View file

@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { zodClient } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import * as flashModule from 'sveltekit-flash-message/client'; import * as flashModule from 'sveltekit-flash-message/client';
import { AlertCircle } from "lucide-svelte"; import { AlertCircle } from "lucide-svelte";
import { signInSchema } from '$lib/config/zod-schemas.js'; import { signInSchema } from '$lib/validations/zod-schemas.js';
import { Label } from '$components/ui/label'; import { Label } from '$components/ui/label';
import { Input } from '$components/ui/input'; import { Input } from '$components/ui/input';
import { Button } from '$components/ui/button'; import { Button } from '$components/ui/button';
@ -26,7 +26,7 @@
}, },
syncFlashMessage: false, syncFlashMessage: false,
taintedMessage: null, taintedMessage: null,
validators: signInSchema, validators: zodClient(signInSchema),
validationMethod: 'oninput', validationMethod: 'oninput',
delayMs: 0, delayMs: 0,
}); });

View file

@ -1,13 +1,14 @@
<script lang="ts"> <script lang="ts">
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import { quintIn } from 'svelte/easing'; import { quintIn } from 'svelte/easing';
import { zodClient } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import * as flashModule from 'sveltekit-flash-message/client'; import * as flashModule from 'sveltekit-flash-message/client';
import { ChevronsUpDown } from "lucide-svelte"; import { ChevronsUpDown } from "lucide-svelte";
import { Button } from '$components/ui/button'; import { Button } from '$components/ui/button';
import { Label } from '$components/ui/label'; import { Label } from '$components/ui/label';
import { Input } from '$components/ui/input'; import { Input } from '$components/ui/input';
import { signUpSchema } from '$lib/config/zod-schemas.js'; import { signUpSchema } from '$lib/validations/zod-schemas.js';
import * as Collapsible from '$lib/components/ui/collapsible'; import * as Collapsible from '$lib/components/ui/collapsible';
import * as Alert from '$lib/components/ui/alert'; import * as Alert from '$lib/components/ui/alert';
import { boredState } from '$lib/stores/boredState.js'; import { boredState } from '$lib/stores/boredState.js';
@ -25,7 +26,7 @@
}, },
}, },
taintedMessage: null, taintedMessage: null,
validators: signUpSchema, validators: zodClient(signUpSchema),
delayMs: 0, delayMs: 0,
}); });