Updating shadcn files, moving routes around.

This commit is contained in:
Bradley Shellnut 2023-09-08 16:30:32 -07:00
parent ea9a0f60a8
commit 13162f8270
84 changed files with 1553 additions and 770 deletions

View file

@ -26,7 +26,7 @@
"@playwright/test": "^1.37.0", "@playwright/test": "^1.37.0",
"@sveltejs/adapter-auto": "^1.0.3", "@sveltejs/adapter-auto": "^1.0.3",
"@sveltejs/adapter-vercel": "^1.0.6", "@sveltejs/adapter-vercel": "^1.0.6",
"@sveltejs/kit": "^1.22.6", "@sveltejs/kit": "^1.24.1",
"@types/cookie": "^0.5.1", "@types/cookie": "^0.5.1",
"@types/node": "^18.17.5", "@types/node": "^18.17.5",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
@ -37,27 +37,27 @@
"eslint-plugin-svelte": "^2.32.4", "eslint-plugin-svelte": "^2.32.4",
"just-clone": "^6.2.0", "just-clone": "^6.2.0",
"just-debounce-it": "^3.2.0", "just-debounce-it": "^3.2.0",
"postcss": "^8.4.27", "postcss": "^8.4.29",
"postcss-import": "^15.1.0", "postcss-import": "^15.1.0",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"postcss-preset-env": "^8.5.1", "postcss-preset-env": "^8.5.1",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.1", "prettier-plugin-svelte": "^2.10.1",
"prisma": "^5.1.1", "prisma": "^5.2.0",
"sass": "^1.65.1", "sass": "^1.65.1",
"svelte": "^4.2.0", "svelte": "^4.2.0",
"svelte-check": "^3.5.0", "svelte-check": "^3.5.0",
"svelte-preprocess": "^5.0.4", "svelte-preprocess": "^5.0.4",
"svelte-sequential-preprocessor": "^2.0.1", "svelte-sequential-preprocessor": "^2.0.1",
"sveltekit-flash-message": "^2.2.0", "sveltekit-flash-message": "^2.2.0",
"sveltekit-superforms": "^1.6.0", "sveltekit-superforms": "^1.6.1",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tslib": "^2.6.1", "tslib": "^2.6.1",
"typescript": "^5.1.6", "typescript": "^5.1.6",
"vite": "^4.4.9", "vite": "^4.4.9",
"vitest": "^0.25.3", "vitest": "^0.25.3",
"zod": "^3.21.4" "zod": "^3.22.2"
}, },
"type": "module", "type": "module",
"engines": { "engines": {
@ -67,12 +67,12 @@
"dependencies": { "dependencies": {
"@axiomhq/axiom-node": "^0.12.0", "@axiomhq/axiom-node": "^0.12.0",
"@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.26",
"@iconify-icons/mdi": "^1.2.47", "@iconify-icons/mdi": "^1.2.47",
"@lucia-auth/adapter-mysql": "^2.0.0", "@lucia-auth/adapter-mysql": "^2.0.0",
"@lucia-auth/adapter-prisma": "^3.0.1", "@lucia-auth/adapter-prisma": "^3.0.1",
"@lukeed/uuid": "^2.0.1", "@lukeed/uuid": "^2.0.1",
"@melt-ui/svelte": "^0.37.5", "@melt-ui/svelte": "^0.37.6",
"@prisma/client": "5.1.1", "@prisma/client": "5.1.1",
"@types/feather-icons": "^4.29.1", "@types/feather-icons": "^4.29.1",
"bits-ui": "^0.0.27", "bits-ui": "^0.0.27",
@ -80,12 +80,13 @@
"clsx": "^1.2.1", "clsx": "^1.2.1",
"cookie": "^0.5.0", "cookie": "^0.5.0",
"feather-icons": "^4.29.1", "feather-icons": "^4.29.1",
"formsnap": "^0.0.9",
"iconify-icon": "^1.0.8", "iconify-icon": "^1.0.8",
"just-kebab-case": "^4.2.0", "just-kebab-case": "^4.2.0",
"loader": "^2.1.1", "loader": "^2.1.1",
"lucia": "^2.4.0", "lucia": "^2.4.2",
"lucide-svelte": "^0.256.1", "lucide-svelte": "^0.256.1",
"open-props": "^1.5.11", "open-props": "^1.5.13",
"radix-svelte": "^0.9.0", "radix-svelte": "^0.9.0",
"svelte-french-toast": "^1.2.0", "svelte-french-toast": "^1.2.0",
"svelte-lazy": "^1.2.1", "svelte-lazy": "^1.2.1",

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
const tailwindcss = require("tailwindcss"); const tailwindcss = require("tailwindcss");
const tailwindNesting = require('tailwindcss/nesting');
const autoprefixer = require('autoprefixer'); const autoprefixer = require('autoprefixer');
const postcssPresetEnv = require('postcss-preset-env'); const postcssPresetEnv = require('postcss-preset-env');
const atImport = require('postcss-import'); const atImport = require('postcss-import');
@ -6,7 +7,7 @@ const atImport = require('postcss-import');
const config = { const config = {
plugins: [ plugins: [
atImport(), atImport(),
// 'tailwindcss/nesting'(), tailwindNesting(),
tailwindcss(), tailwindcss(),
postcssPresetEnv({ postcssPresetEnv({
stage: 2, stage: 2,
@ -16,8 +17,6 @@ const config = {
'media-query-ranges': true 'media-query-ranges': true
} }
}), }),
] //Some plugins, like tailwindcss/nesting, need to run before Tailwind, tailwindcss(), //But others, like autoprefixer, need to run after, autoprefixer] ] //Some plugins, like tailwindcss/nesting, need to run before Tailwind, tailwindcss(), //But others, like autoprefixer, need to run after, autoprefixer]
}; };

View file

@ -5,46 +5,46 @@
@layer base { @layer base {
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 224 71.4% 4.1%; --foreground: 20 14.3% 4.1%;
--card: 0 0% 100%; --card: 0 0% 100%;
--card-foreground: 224 71.4% 4.1%; --card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%; --popover: 0 0% 100%;
--popover-foreground: 224 71.4% 4.1%; --popover-foreground: 20 14.3% 4.1%;
--primary: 262.1 83.3% 57.8%; --primary: 47.9 95.8% 53.1%;
--primary-foreground: 210 20% 98%; --primary-foreground: 26 83.3% 14.1%;
--secondary: 220 14.3% 95.9%; --secondary: 60 4.8% 95.9%;
--secondary-foreground: 220.9 39.3% 11%; --secondary-foreground: 24 9.8% 10%;
--muted: 220 14.3% 95.9%; --muted: 60 4.8% 95.9%;
--muted-foreground: 220 8.9% 46.1%; --muted-foreground: 25 5.3% 44.7%;
--accent: 220 14.3% 95.9%; --accent: 60 4.8% 95.9%;
--accent-foreground: 220.9 39.3% 11%; --accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%; --destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 20% 98%; --destructive-foreground: 60 9.1% 97.8%;
--border: 220 13% 91%; --border: 20 5.9% 90%;
--input: 220 13% 91%; --input: 20 5.9% 90%;
--ring: 262.1 83.3% 57.8%; --ring: 20 14.3% 4.1%;
--radius: 0.5rem; --radius: 0.5rem;
} }
.dark { .dark {
--background: 224 71.4% 4.1%; --background: 20 14.3% 4.1%;
--foreground: 210 20% 98%; --foreground: 60 9.1% 97.8%;
--card: 224 71.4% 4.1%; --card: 20 14.3% 4.1%;
--card-foreground: 210 20% 98%; --card-foreground: 60 9.1% 97.8%;
--popover: 224 71.4% 4.1%; --popover: 20 14.3% 4.1%;
--popover-foreground: 210 20% 98%; --popover-foreground: 60 9.1% 97.8%;
--primary: 263.4 70% 50.4%; --primary: 47.9 95.8% 53.1%;
--primary-foreground: 210 20% 98%; --primary-foreground: 26 83.3% 14.1%;
--secondary: 215 27.9% 16.9%; --secondary: 12 6.5% 15.1%;
--secondary-foreground: 210 20% 98%; --secondary-foreground: 60 9.1% 97.8%;
--muted: 215 27.9% 16.9%; --muted: 12 6.5% 15.1%;
--muted-foreground: 217.9 10.6% 64.9%; --muted-foreground: 24 5.4% 63.9%;
--accent: 215 27.9% 16.9%; --accent: 12 6.5% 15.1%;
--accent-foreground: 210 20% 98%; --accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%; --destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 20% 98%; --destructive-foreground: 60 9.1% 97.8%;
--border: 215 27.9% 16.9%; --border: 12 6.5% 15.1%;
--input: 215 27.9% 16.9%; --input: 12 6.5% 15.1%;
--ring: 263.4 70% 50.4%; --ring: 35.5 91.7% 32.9%;
} }
} }

View file

@ -42,8 +42,8 @@ export const authentication: Handle = async function ({ event, resolve }) {
console.log('user', session?.user); console.log('user', session?.user);
event.locals.user = session?.user; event.locals.user = session?.user;
// if (event.route.id?.startsWith('/(protected)')) { // if (event.route.id?.startsWith('/(protected)')) {
// if (!user) throw redirect(302, '/auth/sign-in'); // if (!user) throw redirect(302, '/sign-in');
// if (!user.verified) throw redirect(302, '/auth/verify/email'); // if (!user.verified) throw redirect(302, '/verify/email');
// } // }
} catch (error) { } catch (error) {
console.error('Error validating user', error); console.error('Error validating user', error);

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -3,7 +3,7 @@
import { fly } from "svelte/transition"; import { fly } from "svelte/transition";
import { createSelect, melt } from "@melt-ui/svelte"; import { createSelect, melt } from "@melt-ui/svelte";
import { Check, ChevronDown, MinusCircle, PlusCircle } from "lucide-svelte"; import { Check, ChevronDown, MinusCircle, PlusCircle } from "lucide-svelte";
import Button from "./ui/button/Button.svelte"; import { Button } from '$components/ui/button';
import type { Collection, Wishlist } from "@prisma/client"; import type { Collection, Wishlist } from "@prisma/client";
export let game_id: string; export let game_id: string;

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import Button from "./ui/button/Button.svelte"; import { Button } from '$components/ui/button';
</script> </script>
<Button type="submit">Add to wishlist</Button> <Button type="submit">Add to wishlist</Button>

View file

@ -1,27 +1,21 @@
<script lang="ts"> <script lang="ts">
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import { LogOut } from 'lucide-svelte'; import { LogOut } from 'lucide-svelte';
import { Button } from '$components/ui/button'; import { Button } from '$lib/components/ui/button';
import { Separator } from "$components/ui/separator"; import { Toggle } from "$lib/components/ui/toggle";
import logo from './bored-game.png'; import * as Avatar from "$lib/components/ui/avatar";
import { Avatar, AvatarFallback } from '$components/ui/avatar'; import * as Sheet from "$lib/components/ui/sheet";
import { import Logo from '$components/logo.svelte';
Sheet,
SheetClose,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger
} from "$components/ui/sheet";
export let user: any; export let user: any;
let avatar = user?.username.slice(0, 1).toUpperCase() || '?';
</script> </script>
<header> <header>
<div class="corner"> <div class="corner">
<a href="/" title="Home"> <a href="/" title="Home">
<img src={logo} alt="Bored Game Home" /> <Logo />
</a> </a>
</div> </div>
<!-- <TextSearch /> --> <!-- <TextSearch /> -->
@ -29,39 +23,42 @@
{#if user} {#if user}
<a href="/collection" title="Go to your collection" data-sveltekit-preload-data>Collection</a> <a href="/collection" title="Go to your collection" data-sveltekit-preload-data>Collection</a>
<a href="/wishlist" title="Go to your wishlist" data-sveltekit-preload-data>Wishlist</a> <a href="/wishlist" title="Go to your wishlist" data-sveltekit-preload-data>Wishlist</a>
<Sheet> <Sheet.Root>
<SheetTrigger> <Sheet.Trigger>
<Avatar class="h-16 w-16 bg-neutral-100"> <Avatar.Root asChild>
<AvatarFallback class="text-3xl font-medium text-magnum-700"> <Avatar.Fallback class="text-3xl font-medium text-magnum-700 h-16 w-16 bg-neutral-100">
{user?.username.slice(0, 1).toUpperCase() || '?'} {avatar}
</AvatarFallback> </Avatar.Fallback>
</Avatar> </Avatar.Root>
</SheetTrigger> </Sheet.Trigger>
<SheetContent position="right" size="lg"> <Sheet.Content side="right">
<SheetHeader> <Sheet.Header>
<SheetTitle>Menu</SheetTitle> <Sheet.Title>Menu</Sheet.Title>
</SheetHeader> <Toggle aria-label="toggle bold">
</Toggle>
</Sheet.Header>
<div class="menu"> <div class="menu">
<div class="item"> <Sheet.Close asChild let:builder>
<SheetClose> <div class="item">
<Button variant="link" href="/profile">View Profile</Button> <Button builders={[builder]} variant="link" class="text-secondary-foreground" href="/profile">View Profile</Button>
</SheetClose> </div>
</div> </Sheet.Close>
<div class="item"> <Sheet.Close asChild let:builder>
<SheetClose> <div class="item">
<Button variant="link" href="/collection">Your Collection</Button> <Button builders={[builder]} variant="link" class="text-secondary-foreground" href="/collection">Your Collection</Button>
</SheetClose> </div>
</div> </Sheet.Close>
<div class="item"> <Sheet.Close asChild let:builder>
<SheetClose> <div class="item">
<Button variant="link" href="/wishlist">Your Wishlist</Button> <Button builders={[builder]} variant="link" class="text-secondary-foreground" href="/wishlist">Your Wishlist</Button>
</SheetClose> </div>
</div> </Sheet.Close>
<div class="separator" /> <div class="separator" />
<div class="item"> <div class="item">
<form <form
use:enhance use:enhance
action="/auth/signout" action="/logout"
method="POST" method="POST"
> >
<Button type="submit"> <Button type="submit">
@ -71,33 +68,22 @@
</form> </form>
</div> </div>
</div> </div>
<SheetFooter> <Sheet.Footer>
<SheetClose> <Sheet.Close asChild let:builder>
<Button type="button">Close</Button> <Button builders={[builder]} type="button">Close</Button>
</SheetClose> </Sheet.Close>
</SheetFooter> </Sheet.Footer>
</SheetContent> </Sheet.Content>
</Sheet> </Sheet.Root>
<!-- <form
use:enhance
action="/auth/signout"
method="POST"
>
<Button type="submit">
<LogOut class="mr-2 h-4 w-4"/>
Sign out
</Button>
</form> -->
{/if} {/if}
{#if !user} {#if !user}
<a href="/auth/signin"> <a href="/login">
<span class="flex-auto">Sign In</span></a <span class="flex-auto">Login</span></a
> >
<a href="/auth/signup"> <a href="/sign-up">
<span class="flex-auto">Sign Up</span></a <span class="flex-auto">Sign Up</span></a
> >
{/if} {/if}
<!-- <Profile /> -->
</nav> </nav>
</header> </header>
@ -119,12 +105,13 @@
} }
.item { .item {
margin-bottom: 0.2rem; /* margin: 0.2rem 0; */
} }
.corner { .corner {
width: 3em; width: 3em;
height: 3em; height: 3em;
margin-left: 1rem;
} }
.corner a { .corner a {

View file

@ -0,0 +1,5 @@
<script lang="ts">
import logo from '$lib/assets/bored-game.png';
</script>
<img src={logo} alt="Bored Game Home" />

View file

@ -6,7 +6,7 @@
import { ToastType } from '$lib/types'; import { ToastType } from '$lib/types';
import { superForm } from 'sveltekit-superforms/client'; import { superForm } from 'sveltekit-superforms/client';
import { toast } from '../../toast/toast'; import { toast } from '../../toast/toast';
import Button from '$components/ui/button/Button.svelte'; import { Button } from '$components/ui/button';
export let data: SuperValidated<SearchSchema>; export let data: SuperValidated<SearchSchema>;
const { enhance } = superForm(data, { const { enhance } = superForm(data, {

View file

@ -19,7 +19,7 @@
]; ];
</script> </script>
<form method="POST" action="/auth/sign-in" use:enhance> <form method="POST" action="/sign-in" use:enhance>
<!--<SuperDebug data={$form} />--> <!--<SuperDebug data={$form} />-->
{#if $errors._errors} {#if $errors._errors}
<aside class="alert variant-filled-error mt-6"> <aside class="alert variant-filled-error mt-6">
@ -77,6 +77,6 @@
> >
</div> </div>
<div class="flex flex-row justify-center items-center mt-10"> <div class="flex flex-row justify-center items-center mt-10">
<a href="/auth/password/reset" class="font-semibold">{i("forgotPassword")}</a> <a href="/password/reset" class="font-semibold">{i("forgotPassword")}</a>
</div> </div>
</form> </form>

View file

@ -19,7 +19,7 @@
// $: termsValue = $form.terms as Writable<boolean>; // $: termsValue = $form.terms as Writable<boolean>;
</script> </script>
<form method="POST" action="/auth/signup" use:enhance> <form method="POST" action="/sign-up" use:enhance>
<h1>Signup user</h1> <h1>Signup user</h1>
<label class="label"> <label class="label">
<span class="sr-only">First Name</span> <span class="sr-only">First Name</span>
@ -105,7 +105,7 @@
<small>{$errors.password}</small> <small>{$errors.password}</small>
{/if} {/if}
</label> </label>
<button type="submit">Signup</button> <button type="submit">Signup</button>
<a class="back" href="/"> or Cancel </a> <a class="back" href="/"> or Cancel </a>

View file

@ -1,30 +0,0 @@
<script lang="ts">
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import { cn } from "$lib/utils";
const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive"
}
},
defaultVariants: {
variant: "default"
}
}
);
let className: string | undefined | null = undefined;
export { className as class };
export let variant: VariantProps<typeof alertVariants>["variant"] =
"default";
</script>
<div class={cn(alertVariants({ variant }), className)} {...$$restProps}>
<slot />
</div>

View file

@ -1,15 +0,0 @@
<script lang="ts">
import { cn } from "$lib/utils";
let className: string | undefined | null = undefined;
export { className as class };
export let level: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" = "h5";
</script>
<svelte:element
this={level}
class={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</svelte:element>

View file

@ -1,7 +1,10 @@
<script lang="ts"> <script lang="ts">
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
let className: string | undefined | null = undefined; type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class }; export { className as class };
</script> </script>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
import type { HeadingLevel } from ".";
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
level?: HeadingLevel;
};
let className: $$Props["class"] = undefined;
export let level: $$Props["level"] = "h5";
export { className as class };
</script>
<svelte:element
this={level}
class={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</svelte:element>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
import { alertVariants, type Variant } from ".";
type $$Props = HTMLAttributes<HTMLDivElement> & {
variant?: Variant;
};
let className: $$Props["class"] = undefined;
export let variant: $$Props["variant"] = "default";
export { className as class };
</script>
<div
class={cn(alertVariants({ variant }), className)}
{...$$restProps}
role="alert"
>
<slot />
</div>

View file

@ -1,3 +1,33 @@
export { default as Alert } from "./Alert.svelte"; import { tv, type VariantProps } from "tailwind-variants";
export { default as AlertDescription } from "./AlertDescription.svelte";
export { default as AlertTitle } from "./AlertTitle.svelte"; import Root from "./alert.svelte";
import Description from "./alert-description.svelte";
import Title from "./alert-title.svelte";
export const alertVariants = tv({
base: "relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11",
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive"
}
},
defaultVariants: {
variant: "default"
}
});
export type Variant = VariantProps<typeof alertVariants>["variant"];
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
export {
Root,
Description,
Title,
//
Root as Alert,
Description as AlertDescription,
Title as AlertTitle
};

View file

@ -1,51 +0,0 @@
<script lang="ts">
import type { VariantProps } from "class-variance-authority";
import type {
HTMLAnchorAttributes,
HTMLButtonAttributes
} from "svelte/elements";
import { cn } from "$lib/utils";
import { buttonVariants } from ".";
let className: string | undefined | null = undefined;
export { className as class };
export let href: HTMLAnchorAttributes["href"] = undefined;
export let type: HTMLButtonAttributes["type"] = undefined;
export let variant: VariantProps<typeof buttonVariants>["variant"] =
"default";
export let size: VariantProps<typeof buttonVariants>["size"] = "default";
type Props = {
class?: string | null;
variant?: VariantProps<typeof buttonVariants>["variant"];
size?: VariantProps<typeof buttonVariants>["size"];
};
interface AnchorElement extends Props, HTMLAnchorAttributes {
href?: HTMLAnchorAttributes["href"];
type?: never;
}
interface ButtonElement extends Props, HTMLButtonAttributes {
type?: HTMLButtonAttributes["type"];
href?: never;
}
type $$Props = AnchorElement | ButtonElement;
</script>
<svelte:element
this={href ? "a" : "button"}
type={href ? undefined : type}
{href}
class={cn(buttonVariants({ variant, size, className }))}
{...$$restProps}
on:click
on:change
on:keydown
on:keyup
on:mouseenter
on:mouseleave
>
<slot />
</svelte:element>

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { Button as ButtonPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { buttonVariants, type Size, type Variant } from ".";
type $$Props = ButtonPrimitive.Props & {
variant?: Variant;
size?: Size;
};
type $$Events = ButtonPrimitive.Events;
let className: $$Props["class"] = undefined;
export let variant: $$Props["variant"] = "default";
export let size: $$Props["size"] = "default";
export let builders: $$Props["builders"] = [];
export { className as class };
</script>
<ButtonPrimitive.Root
{builders}
class={cn(buttonVariants({ variant, size, className }))}
{...$$restProps}
on:click
on:keydown
>
<slot />
</ButtonPrimitive.Root>

View file

@ -1,32 +1,35 @@
import { cva } from "class-variance-authority"; import Root from './button.svelte';
import { tv, type VariantProps } from 'tailwind-variants';
export { default as Button } from "./Button.svelte"; export const buttonVariants = tv({
base: 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
export const buttonVariants = cva( variants: {
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", variant: {
{ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
variants: { destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
variant: { outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
default: secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
"bg-primary text-primary-foreground hover:bg-primary/90", ghost: 'hover:bg-accent hover:text-accent-foreground',
destructive: link: 'text-primary underline-offset-4 hover:underline'
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "underline-offset-4 hover:underline text-primary"
},
size: {
default: "h-10 py-2 px-4",
sm: "h-9 px-3 rounded-md",
lg: "h-11 px-8 rounded-md"
}
}, },
defaultVariants: { size: {
variant: "default", default: 'h-10 px-4 py-2',
size: "default" sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10'
} }
},
defaultVariants: {
variant: 'default',
size: 'default'
} }
); });
export type Variant = VariantProps<typeof buttonVariants>['variant'];
export type Size = VariantProps<typeof buttonVariants>['size'];
export {
Root,
//
Root as Button
};

View file

@ -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
bind:checked
class={cn(
"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",
className
)}
{...$$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-4 w-4" />
{:else if isIndeterminate}
<Minus class="h-4 w-4" />
{/if}
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>

View file

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

View file

@ -0,0 +1,15 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import { slide } from "svelte/transition";
type $$Props = CollapsiblePrimitive.ContentProps;
export let transition: $$Props["transition"] = slide;
export let transitionConfig: $$Props["transitionConfig"] = {
duration: 150
};
</script>
<CollapsiblePrimitive.Content {transition} {transitionConfig} {...$$restProps}>
<slot />
</CollapsiblePrimitive.Content>

View file

@ -1,7 +1,15 @@
import { Collapsible as CollapsiblePrimitive } from "radix-svelte"; import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import Content from "./collapsible-content.svelte";
export const Collapsible = CollapsiblePrimitive.Root; const Root = CollapsiblePrimitive.Root;
const Trigger = CollapsiblePrimitive.Trigger;
export const CollapsibleTrigger = CollapsiblePrimitive.Trigger; export {
Root,
export const CollapsibleContent = CollapsiblePrimitive.Content; Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger
};

View file

@ -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>

View file

@ -0,0 +1,25 @@
<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();
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} />

View file

@ -0,0 +1,16 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { cn } from "@/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>

View file

@ -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
/>

View file

@ -0,0 +1,12 @@
<script lang="ts">
import { cn } from "@/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>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import type { Label as LabelPrimitive } from "bits-ui";
import { getFormField } from "formsnap";
import { cn } from "@/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>

View file

@ -0,0 +1,24 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { buttonVariants } from "$lib/components/ui/button";
import { cn } from "@/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" />

View file

@ -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>

View file

@ -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>

View file

@ -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"];
</script>
<Select.Root
onSelectedChange={(v) => {
onSelectedChange?.(v);
setValue(v ? v.value : undefined);
}}
{...$$restProps}
>
<slot />
<input hidden {name} value={$value} />
</Select.Root>

View file

@ -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} />

View file

@ -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
/>

View file

@ -0,0 +1,14 @@
<script lang="ts">
import { Form as FormPrimitive } from "formsnap";
import { cn } from "@/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}
/>

View file

@ -0,0 +1,82 @@
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 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,
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,
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
};

View file

@ -1 +1,25 @@
export { default as Input } from "./Input.svelte"; import Root from "./input.svelte";
type FormInputEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLInputElement;
};
export type InputEvents = {
blur: FormInputEvent<FocusEvent>;
change: FormInputEvent<Event>;
click: FormInputEvent<MouseEvent>;
focus: FormInputEvent<FocusEvent>;
keydown: FormInputEvent<KeyboardEvent>;
keypress: FormInputEvent<KeyboardEvent>;
keyup: FormInputEvent<KeyboardEvent>;
mouseover: FormInputEvent<MouseEvent>;
mouseenter: FormInputEvent<MouseEvent>;
mouseleave: FormInputEvent<MouseEvent>;
paste: FormInputEvent<ClipboardEvent>;
input: FormInputEvent<InputEvent>;
};
export {
Root,
//
Root as Input
};

View file

@ -0,0 +1,33 @@
<script lang="ts">
import type { HTMLInputAttributes } from "svelte/elements";
import { cn } from "$lib/utils";
import type { InputEvents } from ".";
type $$Props = HTMLInputAttributes;
type $$Events = InputEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
</script>
<input
class={cn(
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium 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}
/>

View file

@ -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
};

View file

@ -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>

View file

@ -0,0 +1,38 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { Check } from "lucide-svelte";
import { cn } from "$lib/utils";
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 focus:bg-accent focus: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>

View file

@ -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>

View file

@ -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}
/>

View file

@ -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>

View file

@ -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>

View file

@ -1,15 +0,0 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "radix-svelte";
import { cn } from "$lib/utils";
let className: string | undefined | null = undefined;
export { className as class };
</script>
<SheetPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
className
)}
{...$$restProps}
/>

View file

@ -1,29 +0,0 @@
<script lang="ts">
import type { VariantProps } from "class-variance-authority";
import { cva } from "class-variance-authority";
import { Dialog as SheetPrimitive } from "radix-svelte";
import { cn } from "$lib/utils";
const portalVariants = cva("fixed inset-0 z-50 flex", {
variants: {
position: {
top: "items-start",
bottom: "items-end",
left: "justify-start",
right: "justify-end"
}
},
defaultVariants: { position: "right" }
});
let className: string | undefined | null = undefined;
export { className as class };
export let position: VariantProps<typeof portalVariants>["position"] =
"right";
</script>
<SheetPrimitive.Portal class={cn(className)} {...$$restProps}>
<div class={portalVariants({ position })}>
<slot />
</div>
</SheetPrimitive.Portal>

View file

@ -1,102 +1,55 @@
import { cva } from "class-variance-authority"; import { Dialog as SheetPrimitive } from "bits-ui";
import { Dialog as SheetPrimitive } from "radix-svelte"; import { tv, type VariantProps } from "tailwind-variants";
export { default as SheetContent } from "./SheetContent.svelte"; import Portal from "./sheet-portal.svelte";
export { default as SheetDescription } from "./SheetDescription.svelte"; import Overlay from "./sheet-overlay.svelte";
export { default as SheetFooter } from "./SheetFooter.svelte"; import Content from "./sheet-content.svelte";
export { default as SheetHeader } from "./SheetHeader.svelte"; import Header from "./sheet-header.svelte";
export { default as SheetOverlay } from "./SheetOverlay.svelte"; import Footer from "./sheet-footer.svelte";
export { default as SheetPortal } from "./SheetPortal.svelte"; import Title from "./sheet-title.svelte";
export { default as SheetTitle } from "./SheetTitle.svelte"; import Description from "./sheet-description.svelte";
export const Sheet = SheetPrimitive.Root; const Root = SheetPrimitive.Root;
export const SheetTrigger = SheetPrimitive.Trigger; const Close = SheetPrimitive.Close;
export const SheetClose = SheetPrimitive.Close; const Trigger = SheetPrimitive.Trigger;
export const sheetVariants = cva( export {
"fixed z-50 scale-100 gap-4 bg-background p-6 opacity-100 shadow-lg border", Root,
{ Close,
variants: { Trigger,
position: { Portal,
top: "animate-in slide-in-from-top w-full duration-300", Overlay,
bottom: "animate-in slide-in-from-bottom w-full duration-300", Content,
left: "animate-in slide-in-from-left h-full duration-300", Header,
right: "animate-in slide-in-from-right h-full duration-300" Footer,
}, Title,
size: { Description,
content: "", //
default: "", Root as Sheet,
sm: "", Close as SheetClose,
lg: "", Trigger as SheetTrigger,
xl: "", Portal as SheetPortal,
full: "" Overlay as SheetOverlay,
} Content as SheetContent,
}, Header as SheetHeader,
compoundVariants: [ Footer as SheetFooter,
{ Title as SheetTitle,
position: ["top", "bottom"], Description as SheetDescription
size: "content", };
class: "max-h-screen"
}, export const sheetVariants = tv({
{ base: "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
position: ["top", "bottom"], variants: {
size: "default", side: {
class: "h-1/3" top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
}, bottom: "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
{ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
position: ["top", "bottom"], right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm"
size: "sm",
class: "h-1/4"
},
{
position: ["top", "bottom"],
size: "lg",
class: "h-1/2"
},
{
position: ["top", "bottom"],
size: "xl",
class: "h-5/6"
},
{
position: ["top", "bottom"],
size: "full",
class: "h-screen"
},
{
position: ["right", "left"],
size: "content",
class: "max-w-screen"
},
{
position: ["right", "left"],
size: "default",
class: "w-1/3"
},
{
position: ["right", "left"],
size: "sm",
class: "w-1/4"
},
{
position: ["right", "left"],
size: "lg",
class: "w-1/2"
},
{
position: ["right", "left"],
size: "xl",
class: "w-5/6"
},
{
position: ["right", "left"],
size: "full",
class: "w-screen"
}
],
defaultVariants: {
position: "right",
size: "default"
} }
},
defaultVariants: {
side: "right"
} }
); });
export type Side = VariantProps<typeof sheetVariants>["side"];

View file

@ -1,23 +1,22 @@
<script lang="ts"> <script lang="ts">
import type { VariantProps } from "class-variance-authority"; import { Dialog as SheetPrimitive } from "bits-ui";
import { SheetOverlay, SheetPortal, sheetVariants, type Side } from ".";
import { X } from "lucide-svelte"; import { X } from "lucide-svelte";
import { Dialog as SheetPrimitive } from "radix-svelte";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import { sheetVariants } from ".";
import SheetOverlay from "./SheetOverlay.svelte";
import SheetPortal from "./SheetPortal.svelte";
let className: string | undefined | null = undefined; type $$Props = SheetPrimitive.ContentProps & {
side?: Side;
};
let className: $$Props["class"] = undefined;
export let side: $$Props["side"] = "right";
export { className as class }; export { className as class };
export let position: VariantProps<typeof sheetVariants>["position"] =
"right";
export let size: VariantProps<typeof sheetVariants>["size"] = "default";
</script> </script>
<SheetPortal {position}> <SheetPortal>
<SheetOverlay /> <SheetOverlay />
<SheetPrimitive.Content <SheetPrimitive.Content
class={cn(sheetVariants({ position, size }), className)} class={cn(sheetVariants({ side }), className)}
{...$$restProps} {...$$restProps}
> >
<slot /> <slot />

View file

@ -1,8 +1,10 @@
<script lang="ts"> <script lang="ts">
import { Dialog as SheetPrimitive } from "radix-svelte"; import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
let className: string | undefined | null = undefined; type $$Props = SheetPrimitive.DescriptionProps;
let className: $$Props["class"] = undefined;
export { className as class }; export { className as class };
</script> </script>

View file

@ -1,7 +1,10 @@
<script lang="ts"> <script lang="ts">
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
let className: string | undefined | null = undefined; type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class }; export { className as class };
</script> </script>

View file

@ -1,7 +1,10 @@
<script lang="ts"> <script lang="ts">
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
let className: string | undefined | null = undefined; type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class }; export { className as class };
</script> </script>

View file

@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SheetPrimitive.OverlayProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SheetPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...$$restProps}
/>

View file

@ -0,0 +1,13 @@
<script lang="ts">
import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SheetPrimitive.PortalProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SheetPrimitive.Portal class={cn(className)} {...$$restProps}>
<slot />
</SheetPrimitive.Portal>

View file

@ -1,8 +1,10 @@
<script lang="ts"> <script lang="ts">
import { Dialog as SheetPrimitive } from "radix-svelte"; import { Dialog as SheetPrimitive } from "bits-ui";
import { cn } from "$lib/utils"; import { cn } from "$lib/utils";
let className: string | undefined | null = undefined; type $$Props = SheetPrimitive.TitleProps;
let className: $$Props["class"] = undefined;
export { className as class }; export { className as class };
</script> </script>

View file

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

View file

@ -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>

View file

@ -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
};

View file

@ -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}
/>

View file

@ -0,0 +1,31 @@
import Root from "./toggle.svelte";
import { tv, type VariantProps } from "tailwind-variants";
export const toggleVariants = tv({
base: "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors data-[state=on]:bg-accent data-[state=on]:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background hover:bg-muted hover:text-muted-foreground",
variants: {
variant: {
default: "bg-transparent",
outline:
"bg-transparent border border-input hover:bg-accent hover:text-accent-foreground"
},
size: {
default: "h-10 px-3",
sm: "h-9 px-2.5",
lg: "h-11 px-5"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
});
export type Variant = VariantProps<typeof toggleVariants>["variant"];
export type Size = VariantProps<typeof toggleVariants>["size"];
export {
Root,
//
Root as Toggle
};

View file

@ -0,0 +1,26 @@
<script lang="ts">
import { Toggle as TogglePrimitive } from "bits-ui";
import { toggleVariants, type Variant, type Size } from ".";
import { cn } from "$lib/utils";
type $$Props = TogglePrimitive.Props & {
variant?: Variant;
size?: Size;
};
type $$Events = TogglePrimitive.Events;
let className: $$Props["class"] = undefined;
export let variant: $$Props["variant"] = "default";
export let size: $$Props["size"] = "default";
export let pressed: $$Props["pressed"] = undefined;
export { className as class };
</script>
<TogglePrimitive.Root
bind:pressed
class={cn(toggleVariants({ variant, size, className }))}
{...$$restProps}
on:m-click
>
<slot />
</TogglePrimitive.Root>

View file

@ -69,6 +69,11 @@ export const signUpSchema = userSchema
} }
}); });
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) => {

View file

@ -11,7 +11,7 @@ const signInSchema = userSchema.pick({
}); });
export const load = async (event) => { export const load = async (event) => {
console.log('sign in load event', event); console.log('login load event', event);
const session = await event.locals.auth.validate(); const session = await event.locals.auth.validate();
if (session) { if (session) {
const message = { type: 'info', message: 'You are already signed in' }; const message = { type: 'info', message: 'You are already signed in' };

View file

@ -4,11 +4,11 @@
import * as flashModule from 'sveltekit-flash-message/client'; import * as flashModule from 'sveltekit-flash-message/client';
import toast from 'svelte-french-toast'; import toast from 'svelte-french-toast';
import { AlertCircle } from "lucide-svelte"; import { AlertCircle } from "lucide-svelte";
import { userSchema } from '$lib/config/zod-schemas.js'; import { signInSchema } from '$lib/config/zod-schemas.js';
import Label from '$components/ui/label/Label.svelte'; import { Label } from '$components/ui/label';
import Input from '$components/ui/input/Input.svelte'; import { Input } from '$components/ui/input';
import Button from '$components/ui/button/Button.svelte'; import { Button } from '$components/ui/button';
import { Alert, AlertDescription, AlertTitle } from "$components/ui/alert"; import * as Alert from "$components/ui/alert";
export let data; export let data;
const { form, errors, enhance, delayed } = superForm(data.form, { const { form, errors, enhance, delayed } = superForm(data.form, {
@ -24,6 +24,7 @@
}, },
syncFlashMessage: false, syncFlashMessage: false,
taintedMessage: null, taintedMessage: null,
validators: signInSchema,
validationMethod: 'oninput', validationMethod: 'oninput',
delayMs: 0, delayMs: 0,
}); });
@ -39,34 +40,38 @@
} }
</script> </script>
<div class="signin"> <svelte:head>
<title>Bored Game | Login</title>
</svelte:head>
<div class="login">
<form method="POST" use:enhance> <form method="POST" use:enhance>
<div class="grid w-full max-w-sm items-center gap-2"> <div class="grid w-full max-w-sm items-center gap-2">
<h2 <h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0" class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
> >
Sign into your account Log into your account
</h2> </h2>
<Label for="username">Username</Label> <Label for="username">Username</Label>
<Input type="text" id="username" name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} /> <Input type="text" id="username" name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} required />
<Label for="password">Password</Label> <Label for="password">Password</Label>
<Input type="password" id="password" name="password" placeholder="Password" autocomplete="password" data-invalid={$errors.password} bind:value={$form.password} /> <Input type="password" id="password" name="password" placeholder="Password" autocomplete="password" data-invalid={$errors.password} bind:value={$form.password} required />
<Button type="submit">Sign In</Button> <Button type="submit">Sign In</Button>
</div> </div>
</form> </form>
{#if $errors._errors} {#if $errors._errors}
<Alert variant="destructive"> <Alert.Root variant="destructive">
<AlertCircle class="h-4 w-4" /> <AlertCircle class="h-4 w-4" />
<AlertTitle>Error</AlertTitle> <Alert.Title>Error</Alert.Title>
<AlertDescription> <Alert.Description>
{$errors._errors} {$errors._errors}
</AlertDescription> </Alert.Description>
</Alert> </Alert.Root>
{/if} {/if}
</div> </div>
<style lang="postcss"> <style lang="postcss">
.signin { .login {
display: grid; display: grid;
gap: 2rem; gap: 2rem;
} }

View file

@ -6,10 +6,10 @@ export const actions = {
console.log('Signing out user'); console.log('Signing out user');
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
await auth.invalidateSession(session.sessionId); // invalidate session await auth.invalidateSession(session.sessionId); // invalidate session
locals.auth.setSession(null); // remove cookie locals.auth.setSession(null); // remove cookie
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
}; };

View file

@ -40,9 +40,8 @@ export const load = async (event) => {
if (session) { if (session) {
throw redirect(302, '/'); throw redirect(302, '/');
} }
const form = await superValidate<typeof signUpSchema, Message>(event, signUpSchema);
return { return {
form form: await superValidate<typeof signUpSchema, Message>(event, signUpSchema)
}; };
}; };

View file

@ -4,21 +4,16 @@
import * as flashModule from 'sveltekit-flash-message/client'; import * as flashModule from 'sveltekit-flash-message/client';
import toast from 'svelte-french-toast'; import toast from 'svelte-french-toast';
import { ChevronsUpDown } from "lucide-svelte"; import { ChevronsUpDown } from "lucide-svelte";
import Button from '$components/ui/button/Button.svelte'; import { Button } from '$components/ui/button';
import Input from '$components/ui/input/Input.svelte'; import { Input } from '$components/ui/input';
import Label from '$components/ui/label/Label.svelte'; import Label from '$components/ui/label/Label.svelte';
import { signUpSchema } from '$lib/config/zod-schemas.js'; import { signUpSchema } from '$lib/config/zod-schemas.js';
import { import * as Collapsible from '$lib/components/ui/collapsible';
Collapsible, import * as Alert from '$lib/components/ui/alert';
CollapsibleContent, import { slide } from 'svelte/transition';
CollapsibleTrigger import { quintIn } from 'svelte/easing';
} from "$components/ui/collapsible";
import Alert from '$components/ui/alert/Alert.svelte';
import AlertTitle from '$components/ui/alert/AlertTitle.svelte';
import AlertDescription from '$components/ui/alert/AlertDescription.svelte';
export let data; export let data;
let isOpen = false;
const { form, errors, constraints, enhance, delayed } = superForm(data.form, { const { form, errors, constraints, enhance, delayed } = superForm(data.form, {
flashMessage: { flashMessage: {
@ -44,72 +39,82 @@
} }
</script> </script>
<svelte:head>
<title>Bored Game | Sign Up</title>
</svelte:head>
<div class="page"> <div class="page">
<form method="POST" action="/auth/signup" use:enhance> <form method="POST" action="/sign-up" 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">
<h2 <h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0" class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
> >
Signup for an account Signup for an account
</h2> </h2>
<Label for="username">Username <small class="text-destructive">(Required)</small></Label> <Label for="username">Username</Label>
<Input type="text" id="username" class={$errors.username && "outline outline-destructive"} name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} /> <Input type="text" id="username" class={$errors.username && "outline outline-destructive"} name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} />
{#if $errors.username} {#if $errors.username}
<p class="text-sm text-destructive">{$errors.username}</p> <p class="text-sm text-destructive">{$errors.username}</p>
{/if} {/if}
<Label for="password">Password <small class="text-destructive">(Required)</small></Label> <Label for="password">Password</Label>
<Input type="password" id="password" class={$errors.password && "outline outline-destructive"} name="password" placeholder="Password" autocomplete="new-password" data-invalid={$errors.password} bind:value={$form.password} /> <Input type="password" id="password" class={$errors.password && "outline outline-destructive"} name="password" placeholder="Password" autocomplete="new-password" data-invalid={$errors.password} bind:value={$form.password} />
{#if $errors.password} {#if $errors.password}
<p class="text-sm text-destructive">{$errors.password}</p> <p class="text-sm text-destructive">{$errors.password}</p>
{/if} {/if}
<Label for="confirm_password">Confirm Password <small class="text-destructive">(Required)</small></Label> <Label for="confirm_password">Confirm Password</Label>
<Input type="password" id="confirm_password" class={$errors.confirm_password && "outline outline-destructive"} name="confirm_password" placeholder="Confirm Password" autocomplete="new-password" data-invalid={$errors.confirm_password} bind:value={$form.confirm_password} /> <Input type="password" id="confirm_password" class={$errors.confirm_password && "outline outline-destructive"} name="confirm_password" placeholder="Confirm Password" autocomplete="new-password" data-invalid={$errors.confirm_password} bind:value={$form.confirm_password} />
{#if $errors.confirm_password} {#if $errors.confirm_password}
<p class="text-sm text-destructive">{$errors.confirm_password}</p> <p class="text-sm text-destructive">{$errors.confirm_password}</p>
{/if} {/if}
<Collapsible open={isOpen} class="grid w-full max-w-sm items-center gap-2.5"> <Collapsible.Root class="grid w-full max-w-sm items-center gap-2.5">
<div> <div>
Optional Fields: Optional Fields:
<CollapsibleTrigger> <Collapsible.Trigger asChild let:builder>
<Button variant="ghost" size="sm" type="button" class="w-9 p-0"> <Button builders={[builder]} variant="ghost" size="sm" type="button" class="w-9 p-0">
<ChevronsUpDown class="h-4 w-4" /> <ChevronsUpDown class="h-4 w-4" />
<span class="sr-only">Toggle</span> <span class="sr-only">Toggle</span>
</Button> </Button>
</CollapsibleTrigger> </Collapsible.Trigger>
</div> </div>
<CollapsibleContent> <Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="email">Email</Label> <Label for="email">Email</Label>
<Input type="email" id="email" class={$errors.email && "outline outline-destructive"} name="email" placeholder="Email" autocomplete="email" data-invalid={$errors.email} bind:value={$form.email} /> <Input type="email" id="email" class={$errors.email && "outline outline-destructive"} name="email" placeholder="Email" autocomplete="email" data-invalid={$errors.email} bind:value={$form.email} />
{#if $errors.email} {#if $errors.email}
<p class="text-sm text-destructive">{$errors.email}</p> <p class="text-sm text-destructive">{$errors.email}</p>
{/if} {/if}
</CollapsibleContent> </div>
<CollapsibleContent> </Collapsible.Content>
<Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="firstName">First Name</Label> <Label for="firstName">First Name</Label>
<Input type="text" id="firstName" class={$errors.firstName && "outline outline-destructive"} name="firstName" placeholder="First Name" autocomplete="given-name" data-invalid={$errors.firstName} bind:value={$form.firstName} /> <Input type="text" id="firstName" class={$errors.firstName && "outline outline-destructive"} name="firstName" placeholder="First Name" autocomplete="given-name" data-invalid={$errors.firstName} bind:value={$form.firstName} />
{#if $errors.firstName} {#if $errors.firstName}
<p class="text-sm text-destructive">{$errors.firstName}</p> <p class="text-sm text-destructive">{$errors.firstName}</p>
{/if} {/if}
</CollapsibleContent> </div>
<CollapsibleContent> </Collapsible.Content>
<Collapsible.Content>
<div transition:slide|global={{ delay: 10, duration: 150, easing: quintIn }}>
<Label for="firstName">Last Name</Label> <Label for="firstName">Last Name</Label>
<Input type="text" id="lastName" class={$errors.firstName && "outline outline-destructive"} name="lastName" placeholder="Last Name" autocomplete="family-name" data-invalid={$errors.lastName} bind:value={$form.lastName} /> <Input type="text" id="lastName" class={$errors.firstName && "outline outline-destructive"} name="lastName" placeholder="Last Name" autocomplete="family-name" data-invalid={$errors.lastName} bind:value={$form.lastName} />
{#if $errors.lastName} {#if $errors.lastName}
<p class="text-sm text-destructive">{$errors.lastName}</p> <p class="text-sm text-destructive">{$errors.lastName}</p>
{/if} {/if}
</CollapsibleContent> </div>
</Collapsible> </Collapsible.Content>
</Collapsible.Root>
<div class="grid grid-cols-2"> <div class="grid grid-cols-2">
<Button type="submit">Signup</Button> <Button type="submit">Signup</Button>
<Button variant="link" href="/">or Cancel</Button> <Button variant="link" class="text-secondary-foreground" href="/">or Cancel</Button>
</div> </div>
{#if !$form.email} {#if !$form.email}
<Alert> <Alert.Root>
<AlertTitle>Heads up!</AlertTitle> <Alert.Title level="h3">Heads up!</Alert.Title>
<AlertDescription> <Alert.Description>
Without an email address, you won't be able to reset your password. Submit only if you are sure. You can always add this later. Without an email address, you won't be able to reset your password. Submit only if you are sure. You can always add this later.
</AlertDescription> </Alert.Description>
</Alert> </Alert.Root>
{/if} {/if}
</div> </div>
</form> </form>

View file

@ -9,7 +9,7 @@ import { search_schema } from '$lib/zodValidation.js';
export const load: PageServerLoad = async ({ fetch, url, locals }) => { export const load: PageServerLoad = async ({ fetch, url, locals }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
// console.log('locals load', locals); // console.log('locals load', locals);
@ -107,7 +107,7 @@ export const actions = {
try { try {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
let game = await prisma.game.findUnique({ let game = await prisma.game.findUnique({
@ -159,7 +159,7 @@ export const actions = {
create: async ({ params, locals, request }) => { create: async ({ params, locals, request }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
@ -167,7 +167,7 @@ export const actions = {
delete: async ({ params, locals, request }) => { delete: async ({ params, locals, request }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
@ -179,7 +179,7 @@ export const actions = {
try { try {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
let game = await prisma.game.findUnique({ let game = await prisma.game.findUnique({

View file

@ -4,7 +4,7 @@ import { redirect } from '@sveltejs/kit';
export async function load({ params, locals }) { export async function load({ params, locals }) {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
try { try {

View file

@ -6,7 +6,7 @@ import { list_game_request_schema } from '$lib/zodValidation';
export async function load({ params, locals }) { export async function load({ params, locals }) {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
try { try {
@ -49,7 +49,7 @@ export const actions = {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
let game = await prisma.game.findUnique({ let game = await prisma.game.findUnique({
@ -106,21 +106,21 @@ export const actions = {
create: async ({ params, locals, request }) => { create: async ({ params, locals, request }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
}, },
// Delete a wishlist // Delete a wishlist
delete: async ({ params, locals, request }) => { delete: async ({ params, locals, request }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
}, },
// Remove game from a wishlist // Remove game from a wishlist
remove: async ({ params, locals, request }) => { remove: async ({ params, locals, request }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
} }
}; };

View file

@ -17,7 +17,7 @@ export const load = async (event) => {
const session = await event.locals.auth.validate(); const session = await event.locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
const { user } = session; const { user } = session;
@ -48,7 +48,7 @@ export const actions = {
const session = await event.locals.auth.validate(); const session = await event.locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
const user = session.user; const user = session.user;

View file

@ -5,7 +5,7 @@
import { AlertTriangle } from 'lucide-svelte'; import { AlertTriangle } from 'lucide-svelte';
import Label from '$components/ui/label/Label.svelte'; import Label from '$components/ui/label/Label.svelte';
import Input from '$components/ui/input/Input.svelte'; import Input from '$components/ui/input/Input.svelte';
import Button from '$components/ui/button/Button.svelte'; import { Button } from '$components/ui/button';
export let data; export let data;
const profileSchema = userSchema.pick({ const profileSchema = userSchema.pick({
@ -63,8 +63,8 @@
{/if} {/if}
</div> </div>
<div class="mt-6"> <div class="mt-6">
<Button variant="link" href="/auth/password/reset">Change Password</Button> <Button variant="link" href="/password/reset">Change Password</Button>
<!-- <a href="/auth/password/reset">Change Password</a> --> <!-- <a href="/password/reset">Change Password</a> -->
</div> </div>
<div class="mt-6"> <div class="mt-6">

View file

@ -7,7 +7,7 @@ import { modifyListGameSchema } from '$lib/config/zod-schemas.js';
export async function load({ params, locals }) { export async function load({ params, locals }) {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
console.log('Wishlist load User id', session.user); console.log('Wishlist load User id', session.user);
@ -56,7 +56,7 @@ export const actions = {
try { try {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
let game = await prisma.game.findUnique({ let game = await prisma.game.findUnique({
@ -107,7 +107,7 @@ export const actions = {
create: async ({ params, locals, request }) => { create: async ({ params, locals, request }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
@ -115,7 +115,7 @@ export const actions = {
delete: async ({ params, locals, request }) => { delete: async ({ params, locals, request }) => {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
return error(405, 'Method not allowed'); return error(405, 'Method not allowed');
}, },
@ -127,7 +127,7 @@ export const actions = {
try { try {
const session = await locals.auth.validate(); const session = await locals.auth.validate();
if (!session) { if (!session) {
throw redirect(302, '/auth/signin'); throw redirect(302, '/login');
} }
let game = await prisma.game.findUnique({ let game = await prisma.game.findUnique({

View file

@ -66,7 +66,7 @@
<AddToList {in_collection} {in_wishlist} game_id={game.id} {wishlist} {collection} /> <AddToList {in_collection} {in_wishlist} game_id={game.id} {wishlist} {collection} />
{:else} {:else}
<span> <span>
<Button href="/auth/signup">Sign Up</Button> or <Button href="/auth/signin">Sign In</Button> to add to a list. <Button href="/sign-up">Sign Up</Button> or <Button href="/login">Sign In</Button> to add to a list.
</span> </span>
{/if} {/if}
</div> </div>

View file

@ -5,11 +5,6 @@
export let data; export let data;
console.log("search page data", data); console.log("search page data", data);
// $: if (data?.searchData?.games) {
// gameStore.removeAll();
// gameStore.addAll(data?.searchData?.games);
// }
</script> </script>
<div class="game-search"> <div class="game-search">