added form action to login

pull/3/head
TZGyn 2 years ago
parent 92fd02b06f
commit f83a24ad68
Signed by: TZGyn
GPG Key ID: 122EAF77AE81FD4A

Binary file not shown.

@ -14,6 +14,7 @@
"devDependencies": { "devDependencies": {
"@sveltejs/kit": "^1.20.4", "@sveltejs/kit": "^1.20.4",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"bun-types": "^1.0.11",
"drizzle-kit": "^0.20.1", "drizzle-kit": "^0.20.1",
"postcss": "^8.4.24", "postcss": "^8.4.24",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
@ -29,13 +30,16 @@
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"bits-ui": "^0.9.1", "bits-ui": "^0.9.4",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"drizzle-orm": "^0.29.0", "drizzle-orm": "^0.29.0",
"formsnap": "^0.4.1",
"lucide-svelte": "^0.292.0", "lucide-svelte": "^0.292.0",
"mode-watcher": "^0.0.7", "mode-watcher": "^0.0.7",
"postgres": "^3.4.3", "postgres": "^3.4.3",
"sveltekit-superforms": "^1.10.1",
"tailwind-merge": "^2.0.0", "tailwind-merge": "^2.0.0",
"tailwind-variants": "^0.1.18" "tailwind-variants": "^0.1.18",
"zod": "^3.22.4"
} }
} }

@ -61,7 +61,7 @@
--accent: 240 3.7% 15.9%; --accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%; --accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%; --destructive: #f31260;
--destructive-foreground: 0 0% 98%; --destructive-foreground: 0 0% 98%;
--ring: 240 4.9% 83.9%; --ring: 240 4.9% 83.9%;

@ -0,0 +1,34 @@
<script lang="ts">
import { Checkbox as CheckboxPrimitive } from "bits-ui";
import { Check, Minus } from "lucide-svelte";
import { cn } from "$lib/utils";
type $$Props = CheckboxPrimitive.Props;
type $$Events = CheckboxPrimitive.Events;
let className: $$Props["class"] = undefined;
export let checked: $$Props["checked"] = false;
export { className as class };
</script>
<CheckboxPrimitive.Root
class={cn(
"box-content peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50",
className
)}
bind:checked
{...$$restProps}
on:click
>
<CheckboxPrimitive.Indicator
class={cn("flex items-center justify-center text-current h-4 w-4")}
let:isChecked
let:isIndeterminate
>
{#if isChecked}
<Check class="h-3.5 w-3.5" />
{:else if isIndeterminate}
<Minus class="h-3.5 w-3.5" />
{/if}
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>

@ -0,0 +1,6 @@
import Root from "./checkbox.svelte";
export {
Root,
//
Root as Checkbox
};

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

@ -0,0 +1,26 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { Checkbox as CheckboxPrimitive } from "bits-ui";
import { Checkbox } from "$lib/components/ui/checkbox";
type $$Props = CheckboxPrimitive.Props;
type $$Events = CheckboxPrimitive.Events;
export let onCheckedChange: $$Props["onCheckedChange"] = undefined;
const { name, setValue, attrStore, value } = getFormField();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { name: nameAttr, value: valueAttr, ...rest } = $attrStore;
</script>
<Checkbox
{...rest}
checked={typeof $value === "boolean" ? $value : false}
onCheckedChange={(v) => {
onCheckedChange?.(v);
setValue(v);
}}
{...$$restProps}
on:click
on:keydown
/>
<input hidden {name} value={$value} />

@ -0,0 +1,16 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLSpanElement>;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<FormPrimitive.Description
class={cn("text-sm text-muted-foreground", className)}
{...$$restProps}
>
<slot />
</FormPrimitive.Description>

@ -0,0 +1,28 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { HTMLInputAttributes } from "svelte/elements";
import { Input, type InputEvents } from "$lib/components/ui/input";
type $$Props = HTMLInputAttributes;
type $$Events = InputEvents;
const { attrStore, value } = getFormField();
</script>
<Input
{...$attrStore}
bind:value={$value}
{...$$restProps}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
/>

@ -0,0 +1,12 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<div class={cn("space-y-2", className)} {...$$restProps}>
<slot />
</div>

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

@ -0,0 +1,24 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { buttonVariants } from "$lib/components/ui/button";
import { cn } from "$lib/utils";
import { ChevronDown } from "lucide-svelte";
import type { HTMLSelectAttributes } from "svelte/elements";
type $$Props = HTMLSelectAttributes;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<FormPrimitive.Select
class={cn(
buttonVariants({ variant: "outline" }),
"appearance-none bg-transparent font-normal",
className
)}
{...$$restProps}
>
<slot />
</FormPrimitive.Select>
<ChevronDown class="absolute right-3 top-2.5 h-4 w-4 opacity-50" />

@ -0,0 +1,22 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { RadioGroup as RadioGroupPrimitive } from "bits-ui";
import * as RadioGroup from "$lib/components/ui/radio-group";
type $$Props = RadioGroupPrimitive.Props;
const { attrStore, setValue, name, value } = getFormField();
export let onValueChange: $$Props["onValueChange"] = undefined;
</script>
<RadioGroup.Root
{...$attrStore}
onValueChange={(v) => {
onValueChange?.(v);
setValue(v);
}}
{...$$restProps}
>
<slot />
<input hidden {name} value={$value} />
</RadioGroup.Root>

@ -0,0 +1,17 @@
<script lang="ts">
import * as Select from "$lib/components/ui/select";
import type { Select as SelectPrimitive } from "bits-ui";
import { getFormField } from "formsnap";
type $$Props = SelectPrimitive.TriggerProps & {
placeholder?: string;
};
type $$Events = SelectPrimitive.TriggerEvents;
const { attrStore } = getFormField();
export let placeholder = "";
</script>
<Select.Trigger {...$$restProps} {...$attrStore} on:click on:keydown>
<Select.Value {placeholder} />
<slot />
</Select.Trigger>

@ -0,0 +1,20 @@
<script lang="ts">
import * as Select from "$lib/components/ui/select";
import { getFormField } from "formsnap";
import type { Select as SelectPrimitive } from "bits-ui";
type $$Props = SelectPrimitive.Props;
const { setValue, name, value } = getFormField();
export let onSelectedChange: $$Props["onSelectedChange"] = undefined;
</script>
<Select.Root
onSelectedChange={(v) => {
onSelectedChange?.(v);
setValue(v ? v.value : undefined);
}}
{...$$restProps}
>
<slot />
<input hidden {name} value={$value} />
</Select.Root>

@ -0,0 +1,24 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { Switch as SwitchPrimitive } from "bits-ui";
import { Switch } from "$lib/components/ui/switch";
type $$Props = SwitchPrimitive.Props;
type $$Events = SwitchPrimitive.Events;
export let onCheckedChange: $$Props["onCheckedChange"] = undefined;
const { name, setValue, attrStore, value } = getFormField();
</script>
<Switch
{...$attrStore}
checked={typeof $value === "boolean" ? $value : false}
onCheckedChange={(v) => {
onCheckedChange?.(v);
setValue(v);
}}
{...$$restProps}
on:click
on:keydown
/>
<input hidden {name} value={$value} />

@ -0,0 +1,32 @@
<script lang="ts">
import { getFormField } from "formsnap";
import type { HTMLTextareaAttributes } from "svelte/elements";
import type { TextareaGetFormField } from ".";
import {
Textarea,
type TextareaEvents
} from "$lib/components/ui/textarea";
type $$Props = HTMLTextareaAttributes;
type $$Events = TextareaEvents;
const { attrStore, value } = getFormField() as TextareaGetFormField;
</script>
<Textarea
{...$attrStore}
bind:value={$value}
{...$$restProps}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
/>

@ -0,0 +1,14 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLParagraphElement>;
let className: string | undefined | null = undefined;
export { className as class };
</script>
<FormPrimitive.Validation
class={cn("text-sm font-medium text-destructive", className)}
{...$$restProps}
/>

@ -0,0 +1,85 @@
import { Form as FormPrimitive, getFormField } 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 Label from "./form-label.svelte";
import Validation from "./form-validation.svelte";
import Checkbox from "./form-checkbox.svelte";
import Switch from "./form-switch.svelte";
import NativeSelect from "./form-native-select.svelte";
import RadioGroup from "./form-radio-group.svelte";
import Select from "./form-select.svelte";
import SelectTrigger from "./form-select-trigger.svelte";
import Button from "./form-button.svelte";
const Root = FormPrimitive.Root;
const Field = FormPrimitive.Field;
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 {
Root,
Field,
Control,
Item,
Input,
Label,
Button,
Switch,
Select,
Checkbox,
Textarea,
Validation,
RadioGroup,
RadioItem,
Description,
SelectContent,
SelectLabel,
SelectGroup,
SelectItem,
SelectSeparator,
SelectTrigger,
NativeSelect,
NativeRadio,
//
Root as Form,
Field as FormField,
Control as FormControl,
Item as FormItem,
Input as FormInput,
Textarea as FormTextarea,
Description as FormDescription,
Label as FormLabel,
Validation as FormValidation,
NativeSelect as FormNativeSelect,
NativeRadio as FormNativeRadio,
Checkbox as FormCheckbox,
Switch as FormSwitch,
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
};

@ -0,0 +1,15 @@
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
import Root from "./radio-group.svelte";
import Item from "./radio-group-item.svelte";
const Input = RadioGroupPrimitive.Input;
export {
Root,
Input,
Item,
//
Root as RadioGroup,
Input as RadioGroupInput,
Item as RadioGroupItem
};

@ -0,0 +1,28 @@
<script lang="ts">
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
import { Circle } from "lucide-svelte";
import { cn } from "$lib/utils";
type $$Props = RadioGroupPrimitive.ItemProps;
type $$Events = RadioGroupPrimitive.ItemEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export { className as class };
</script>
<RadioGroupPrimitive.Item
{value}
class={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...$$restProps}
on:click
>
<div class="flex items-center justify-center">
<RadioGroupPrimitive.ItemIndicator>
<Circle class="h-2.5 w-2.5 fill-current text-current" />
</RadioGroupPrimitive.ItemIndicator>
</div>
</RadioGroupPrimitive.Item>

@ -0,0 +1,18 @@
<script lang="ts">
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = RadioGroupPrimitive.Props;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
</script>
<RadioGroupPrimitive.Root
bind:value
class={cn("grid gap-2", className)}
{...$$restProps}
>
<slot />
</RadioGroupPrimitive.Root>

@ -0,0 +1,33 @@
import { Select as SelectPrimitive } from "bits-ui";
import Root from "./select.svelte";
import Label from "./select-label.svelte";
import Item from "./select-item.svelte";
import Content from "./select-content.svelte";
import Trigger from "./select-trigger.svelte";
import Separator from "./select-separator.svelte";
const Group = SelectPrimitive.Group;
const Input = SelectPrimitive.Input;
const Value = SelectPrimitive.Value;
export {
Root,
Group,
Input,
Label,
Item,
Value,
Content,
Trigger,
Separator,
//
Root as Select,
Group as SelectGroup,
Input as SelectInput,
Label as SelectLabel,
Item as SelectItem,
Value as SelectValue,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator
};

@ -0,0 +1,36 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
import { scale } from "svelte/transition";
type $$Props = SelectPrimitive.ContentProps;
type $$Events = SelectPrimitive.ContentEvents;
export let inTransition: $$Props["inTransition"] = flyAndScale;
export let inTransitionConfig: $$Props["inTransitionConfig"] = undefined;
export let outTransition: $$Props["outTransition"] = scale;
export let outTransitionConfig: $$Props["outTransitionConfig"] = {
start: 0.95,
opacity: 0,
duration: 50
};
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md outline-none",
className
)}
{...$$restProps}
on:keydown
>
<div class="w-full p-1">
<slot />
</div>
</SelectPrimitive.Content>

@ -0,0 +1,38 @@
<script lang="ts">
import { cn } from "$lib/utils";
import { Select as SelectPrimitive } from "bits-ui";
import { Check } from "lucide-svelte";
type $$Props = SelectPrimitive.ItemProps;
type $$Events = SelectPrimitive.ItemEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export let label: $$Props["label"] = undefined;
export let disabled: $$Props["disabled"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Item
{value}
{disabled}
{label}
class={cn(
"relative flex w-full 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",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check class="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<slot />
</SelectPrimitive.Item>

@ -0,0 +1,16 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.LabelProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Label
class={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...$$restProps}
>
<slot />
</SelectPrimitive.Label>

@ -0,0 +1,14 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.SeparatorProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", className)}
{...$$restProps}
/>

@ -0,0 +1,27 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { ChevronDown } from "lucide-svelte";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.TriggerProps;
type $$Events = SelectPrimitive.TriggerEvents;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Trigger
class={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...$$restProps}
let:builder
on:click
on:keydown
>
<slot {builder} />
<div>
<ChevronDown class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger>

@ -0,0 +1,12 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
type $$Props = SelectPrimitive.Props;
export let selected: $$Props["selected"] = undefined;
export let open: $$Props["open"] = undefined;
</script>
<SelectPrimitive.Root bind:selected bind:open {...$$restProps}>
<slot />
</SelectPrimitive.Root>

@ -0,0 +1,7 @@
import Root from "./switch.svelte";
export {
Root,
//
Root as Switch
};

@ -0,0 +1,25 @@
<script lang="ts">
import { Switch as SwitchPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SwitchPrimitive.Props;
let className: $$Props["class"] = undefined;
export let checked: $$Props["checked"] = undefined;
export { className as class };
</script>
<SwitchPrimitive.Root
bind:checked
class={cn(
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...$$restProps}
>
<SwitchPrimitive.Thumb
class={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitive.Root>

@ -0,0 +1,28 @@
import Root from "./textarea.svelte";
type FormTextareaEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLTextAreaElement;
};
type TextareaEvents = {
blur: FormTextareaEvent<FocusEvent>;
change: FormTextareaEvent<Event>;
click: FormTextareaEvent<MouseEvent>;
focus: FormTextareaEvent<FocusEvent>;
keydown: FormTextareaEvent<KeyboardEvent>;
keypress: FormTextareaEvent<KeyboardEvent>;
keyup: FormTextareaEvent<KeyboardEvent>;
mouseover: FormTextareaEvent<MouseEvent>;
mouseenter: FormTextareaEvent<MouseEvent>;
mouseleave: FormTextareaEvent<MouseEvent>;
paste: FormTextareaEvent<ClipboardEvent>;
input: FormTextareaEvent<InputEvent>;
};
export {
Root,
//
Root as Textarea,
type TextareaEvents,
type FormTextareaEvent
};

@ -0,0 +1,31 @@
<script lang="ts">
import type { HTMLTextareaAttributes } from "svelte/elements";
import { cn } from "$lib/utils";
type $$Props = HTMLTextareaAttributes;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
</script>
<textarea
class={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
{...$$restProps}
/>

@ -1,53 +1,25 @@
<script lang="ts"> <script lang="ts">
import { Button } from '$lib/components/ui/button'; import * as Form from '$lib/components/ui/form';
import { Input } from '$lib/components/ui/input'; import { formSchema, type FormSchema } from '../schema';
import { Label } from '$lib/components/ui/label'; import type { SuperValidated } from 'sveltekit-superforms';
import { Loader2, LoaderIcon } from 'lucide-svelte';
import { cn } from '$lib/utils';
let className: string | undefined | null = undefined; export let form: SuperValidated<FormSchema>;
export { className as class };
let isLoading = false;
async function onSubmit() {
isLoading = true;
setTimeout(() => {
isLoading = false;
}, 3000);
}
</script> </script>
<div class={cn('grid gap-6', className)} {...$$restProps}> <Form.Root method="POST" {form} schema={formSchema} let:config>
<form on:submit|preventDefault={onSubmit}> <Form.Field {config} name="email">
<div class="grid gap-4"> <Form.Item>
<div class="grid gap-1"> <Form.Label>Email</Form.Label>
<Label for="email">Email</Label> <Form.Input placeholder="name@example.com" />
<Input <Form.Validation />
id="email" </Form.Item>
placeholder="name@example.com" </Form.Field>
type="email" <Form.Field {config} name="password">
autocapitalize="none" <Form.Item>
autocomplete="email" <Form.Label>Password</Form.Label>
autocorrect="off" <Form.Input type="password" placeholder="••••••••" />
disabled={isLoading} <Form.Validation />
/> </Form.Item>
</div> </Form.Field>
<div class="grid gap-1"> <Form.Button class="w-full">Sign In</Form.Button>
<Label for="password">Password</Label> </Form.Root>
<Input
id="password"
placeholder="••••••••"
type="password"
disabled={isLoading}
/>
</div>
<Button disabled={isLoading} type="submit" class="flex gap-2">
{#if isLoading}
<Loader2 class="animate-spin" />
{/if}
Sign In
</Button>
</div>
</form>
</div>

@ -0,0 +1,58 @@
import type { PageServerLoad, Actions } from './$types';
import { fail } from '@sveltejs/kit';
import { superValidate } from 'sveltekit-superforms/server';
import { formSchema } from './schema';
import { db } from '$lib/db';
import { user as userSchema } from '$lib/db/schema';
import { eq } from 'drizzle-orm';
export const load = (async () => {
return {
form: superValidate(formSchema),
};
}) satisfies PageServerLoad;
export const actions: Actions = {
default: async (event) => {
const form = await superValidate(event, formSchema);
console.log(form);
if (!form.valid) {
return fail(400, {
form,
});
}
try {
const users = await db
.select()
.from(userSchema)
.where(eq(userSchema.email, form.data.email));
const user = users[0];
const matchPassword =
user && (await Bun.password.verify(form.data.password, user.password));
if (user && matchPassword) {
return {
form,
};
} else {
return fail(400, {
form,
});
}
} catch (error) {
if (error instanceof SyntaxError) {
return fail(400, {
form,
});
} else {
console.log(error);
return fail(400, {
form,
});
}
}
},
};

@ -1,6 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types';
import ThemeToggle from '$lib/components/theme-toggle.svelte'; import ThemeToggle from '$lib/components/theme-toggle.svelte';
import UserAuthForm from './(components)/user-auth-form.svelte'; import UserAuthForm from './(components)/user-auth-form.svelte';
export let data: PageData;
</script> </script>
<div <div
@ -30,7 +33,7 @@
Enter your email below to login to your account Enter your email below to login to your account
</p> </p>
</div> </div>
<UserAuthForm /> <UserAuthForm form={data.form} />
<p class="px-8 text-center text-sm text-muted-foreground"> <p class="px-8 text-center text-sm text-muted-foreground">
Don't Have An Account? Signup{' '} Don't Have An Account? Signup{' '}
<a <a

@ -0,0 +1,8 @@
import { z } from 'zod';
export const formSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
export type FormSchema = typeof formSchema;

@ -1,63 +1,63 @@
import { fontFamily } from "tailwindcss/defaultTheme"; import { fontFamily } from 'tailwindcss/defaultTheme';
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
const config = { const config = {
darkMode: ["class"], darkMode: ['class'],
content: ["./src/**/*.{html,js,svelte,ts}"], content: ['./src/**/*.{html,js,svelte,ts}'],
safelist: ["dark"], safelist: ['dark'],
theme: { theme: {
container: { container: {
center: true, center: true,
padding: "2rem", padding: '2rem',
screens: { screens: {
"2xl": "1400px" '2xl': '1400px',
} },
}, },
extend: { extend: {
colors: { colors: {
border: "hsl(var(--border) / <alpha-value>)", border: 'hsl(var(--border) / <alpha-value>)',
input: "hsl(var(--input) / <alpha-value>)", input: 'hsl(var(--input) / <alpha-value>)',
ring: "hsl(var(--ring) / <alpha-value>)", ring: 'hsl(var(--ring) / <alpha-value>)',
background: "hsl(var(--background) / <alpha-value>)", background: 'hsl(var(--background) / <alpha-value>)',
foreground: "hsl(var(--foreground) / <alpha-value>)", foreground: 'hsl(var(--foreground) / <alpha-value>)',
primary: { primary: {
DEFAULT: "hsl(var(--primary) / <alpha-value>)", DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
foreground: "hsl(var(--primary-foreground) / <alpha-value>)" foreground: 'hsl(var(--primary-foreground) / <alpha-value>)',
}, },
secondary: { secondary: {
DEFAULT: "hsl(var(--secondary) / <alpha-value>)", DEFAULT: 'hsl(var(--secondary) / <alpha-value>)',
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)" foreground: 'hsl(var(--secondary-foreground) / <alpha-value>)',
}, },
destructive: { destructive: {
DEFAULT: "hsl(var(--destructive) / <alpha-value>)", DEFAULT: 'var(--destructive)',
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)" foreground: 'hsl(var(--destructive-foreground) / <alpha-value>)',
}, },
muted: { muted: {
DEFAULT: "hsl(var(--muted) / <alpha-value>)", DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
foreground: "hsl(var(--muted-foreground) / <alpha-value>)" foreground: 'hsl(var(--muted-foreground) / <alpha-value>)',
}, },
accent: { accent: {
DEFAULT: "hsl(var(--accent) / <alpha-value>)", DEFAULT: 'hsl(var(--accent) / <alpha-value>)',
foreground: "hsl(var(--accent-foreground) / <alpha-value>)" foreground: 'hsl(var(--accent-foreground) / <alpha-value>)',
}, },
popover: { popover: {
DEFAULT: "hsl(var(--popover) / <alpha-value>)", DEFAULT: 'hsl(var(--popover) / <alpha-value>)',
foreground: "hsl(var(--popover-foreground) / <alpha-value>)" foreground: 'hsl(var(--popover-foreground) / <alpha-value>)',
}, },
card: { card: {
DEFAULT: "hsl(var(--card) / <alpha-value>)", DEFAULT: 'hsl(var(--card) / <alpha-value>)',
foreground: "hsl(var(--card-foreground) / <alpha-value>)" foreground: 'hsl(var(--card-foreground) / <alpha-value>)',
} },
}, },
borderRadius: { borderRadius: {
lg: "var(--radius)", lg: 'var(--radius)',
md: "calc(var(--radius) - 2px)", md: 'calc(var(--radius) - 2px)',
sm: "calc(var(--radius) - 4px)" sm: 'calc(var(--radius) - 4px)',
}, },
fontFamily: { fontFamily: {
sans: [...fontFamily.sans] sans: [...fontFamily.sans],
} },
} },
}, },
}; };

@ -8,7 +8,8 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": true, "sourceMap": true,
"strict": true "strict": true,
"types": ["bun-types"]
} }
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// //

Loading…
Cancel
Save