Alert on login failure and starting light/dark mode change.

This commit is contained in:
Bradley Shellnut 2023-07-01 16:12:17 -07:00
parent f80b8f5391
commit 9c71c3664f
10 changed files with 130 additions and 67 deletions

View file

@ -5,16 +5,19 @@ const atImport = require('postcss-import');
const config = {
plugins: [
tailwindcss(),
atImport(),
// 'tailwindcss/nesting'(),
tailwindcss(),
postcssPresetEnv({
stage: 2,
features: {
'nesting-rules': true,
'nesting-rules': false,
'custom-media-queries': 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]
};

View file

@ -6,34 +6,34 @@
<link rel="icon" href="%sveltekit.assets%/favicon-bored.png" />
<meta name="viewport" content="width=device-width" />
<script>
const htmlElement = document.documentElement;
const userTheme = localStorage.theme;
const userFont = localStorage.font;
// const htmlElement = document.documentElement;
// const userTheme = localStorage.theme;
// const userFont = localStorage.font;
const prefersDarkMode = window.matchMedia('prefers-color-scheme: dark').matches;
const prefersLightMode = window.matchMedia('prefers-color-scheme: light').matches;
// const prefersDarkMode = window.matchMedia('prefers-color-scheme: dark').matches;
// const prefersLightMode = window.matchMedia('prefers-color-scheme: light').matches;
// check if the user set a theme
if (userTheme) {
htmlElement.dataset.theme = userTheme;
}
// // check if the user set a theme
// if (userTheme) {
// htmlElement.dataset.theme = userTheme;
// }
// otherwise check for user preference
if (!userTheme && prefersDarkMode) {
htmlElement.dataset.theme = '🌛 Night';
localStorage.theme = '🌛 Night';
}
// // otherwise check for user preference
// if (!userTheme && prefersDarkMode) {
// htmlElement.dataset.theme = '🌛 Night';
// localStorage.theme = '🌛 Night';
// }
if (!userTheme && prefersLightMode) {
htmlElement.dataset.theme = '☀️ Daylight';
localStorage.theme = '☀️ Daylight';
}
// if (!userTheme && prefersLightMode) {
// htmlElement.dataset.theme = '☀️ Daylight';
// localStorage.theme = '☀️ Daylight';
// }
// if nothing is set default to dark mode
if (!userTheme && !prefersDarkMode && !prefersLightMode) {
htmlElement.dataset.theme = '🌛 Night';
localStorage.theme = '🌛 Night';
}
// // if nothing is set default to dark mode
// if (!userTheme && !prefersDarkMode && !prefersLightMode) {
// htmlElement.dataset.theme = '🌛 Night';
// localStorage.theme = '🌛 Night';
// }
</script>
%sveltekit.head%
</head>

View file

@ -1,5 +1,7 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { LogOut } from 'lucide-svelte';
import Button from '$components/ui/button/Button.svelte';
// import Profile from '../preferences/profile.svelte';
import logo from './bored-game.png';
@ -23,9 +25,10 @@
action="/auth/signout"
method="POST"
>
<button type="submit" class="btn"
><span>Sign out</span></button
>
<Button type="submit">
<LogOut class="mr-2 h-4 w-4"/>
Sign out
</Button>
</form>
{/if}
{#if !user}

View file

@ -0,0 +1,30 @@
<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

@ -0,0 +1,10 @@
<script lang="ts">
import { cn } from "$lib/utils";
let className: string | undefined | null = undefined;
export { className as class };
</script>
<div class={cn("text-sm [&_p]:leading-relaxed", className)} {...$$restProps}>
<slot />
</div>

View file

@ -0,0 +1,15 @@
<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

@ -0,0 +1,3 @@
export { default as Alert } from "./Alert.svelte";
export { default as AlertDescription } from "./AlertDescription.svelte";
export { default as AlertTitle } from "./AlertTitle.svelte";

View file

@ -52,7 +52,7 @@ export const actions = {
// TODO: need to return error message to the client
console.error(e);
form.data.password = '';
return setError(form, '', 'The username or password is incorrect.');
return setError(form, '', 'Your username or password is incorrect.');
}
form.data.username = '';
form.data.password = '';

View file

@ -1,46 +1,43 @@
<script lang="ts">
import { superForm } from 'sveltekit-superforms/client';
import { AlertCircle } from "lucide-svelte";
import { userSchema } from '$lib/config/zod-schemas.js';
import Label from '$components/ui/label/Label.svelte';
import Input from '$components/ui/input/Input.svelte';
import Button from '$components/ui/button/Button.svelte';
import { Alert, AlertDescription, AlertTitle } from "$components/ui/alert";
export let data;
const signInSchema = userSchema.pick({ username: true, password: true });
const { form, errors, enhance, delayed } = superForm(data.form, {
taintedMessage: null,
validators: signInSchema,
validationMethod: 'oninput',
delayMs: 0,
});
console.log($errors);
</script>
<form method="POST" use:enhance>
<div>
{#if $errors._errors}
<aside class="alert">
<div class="alert-message">
<h3>There was an error signing in</h3>
<p>{$errors._errors}</p>
</div>
</aside>
<Alert variant="destructive">
<AlertCircle class="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>
{$errors._errors}
</AlertDescription>
</Alert>
{/if}
<div class="grid w-full max-w-sm items-center gap-2">
<h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
>
Sign into your account
</h2>
<Label for="username">Username</Label>
<Input type="text" id="username" name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} />
{#if $errors.username}
<p class="text-sm text-muted-foreground">{$errors.username}</p>
{/if}
<Label for="password">Password</Label>
<Input type="password" id="password" name="password" placeholder="Password" autocomplete="new-password" data-invalid={$errors.password} bind:value={$form.password} />
{#if $errors.password}
<p class="text-sm text-muted-foreground">{$errors.password}</p>
{/if}
<Button type="submit">Sign In</Button>
</div>
</form>
<form method="POST" use:enhance>
<div class="grid w-full max-w-sm items-center gap-2">
<h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
>
Sign into your account
</h2>
<Label for="username">Username</Label>
<Input type="text" id="username" name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} />
<Label for="password">Password</Label>
<Input type="password" id="password" name="password" placeholder="Password" autocomplete="new-password" data-invalid={$errors.password} bind:value={$form.password} />
<Button type="submit">Sign In</Button>
</div>
</form>
</div>

View file

@ -25,43 +25,45 @@ import { userSchema } from '$lib/config/zod-schemas.js';
<div class="page">
<form method="POST" action="/auth/signup" 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.5">
<h2
class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0"
>
Signup for an account
</h2>
<Label for="firstName">First Name</Label>
<Input type="text" id="firstName" 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}
<p class="text-sm text-destructive">{$errors.firstName}</p>
{/if}
<Label for="firstName">Last Name</Label>
<Input type="text" id="lastName" 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}
<p class="text-sm text-destructive">{$errors.lastName}</p>
{/if}
<Label for="email">Email</Label>
<Input type="email" id="email" 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}
<p class="text-sm text-destructive">{$errors.email}</p>
{/if}
<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" class={$errors.username && "outline outline-destructive"} name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} />
{#if $errors.username}
<p class="text-sm text-destructive">{$errors.username}</p>
{/if}
<Label for="password">Password</Label>
<Input type="password" id="password" 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}
<p class="text-sm text-destructive">{$errors.password}</p>
{/if}
<Label for="confirm_password">Confirm Password</Label>
<Input type="password" id="confirm_password" 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}
<p class="text-sm text-destructive">{$errors.confirm_password}</p>
{/if}
<div class="flex place-content-">
<div class="grid grid-cols-2">
<Button type="submit">Signup</Button>
<Button variant="link" href="/">or Cancel</Button>
</div>