Create auth page, add styling & prettier

This commit is contained in:
Chad Freeman 2024-08-16 23:42:18 -04:00
parent 8da9ef55ed
commit 4ad3290e04
16 changed files with 235 additions and 34 deletions

4
.prettierignore Normal file
View file

@ -0,0 +1,4 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock

18
.prettierrc Normal file
View file

@ -0,0 +1,18 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte",
"svelteStrictMode": true,
"svelteBracketNewLine": false,
"svelteIndentScriptAndStyle": true
}
}
]
}

BIN
bun.lockb

Binary file not shown.

View file

@ -7,28 +7,34 @@
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check ."
}, },
"devDependencies": { "devDependencies": {
"@deno/kv": "^0.8.1",
"@olli/kvdex": "npm:@jsr/olli__kvdex",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@ts-rex/argon2": "npm:@jsr/ts-rex__argon2",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"daisyui": "^4.12.10", "daisyui": "^4.12.10",
"hono": "^4.5.5",
"lucia": "^3.2.0",
"postcss": "^8.4.41", "postcss": "^8.4.41",
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.5",
"svelte": "^5.0.0-next.1", "svelte": "^5.0.0-next.1",
"svelte-check": "^3.6.0", "svelte-check": "^3.6.0",
"sveltekit-superforms": "^2.17.0",
"tailwindcss": "^3.4.10", "tailwindcss": "^3.4.10",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vite": "^5.0.3" "vite": "^5.0.3",
"zod": "^3.23.8"
}, },
"type": "module", "type": "module",
"trustedDependencies": [ "trustedDependencies": [
"svelte-preprocess" "svelte-preprocess"
], ]
"dependencies": {
"@olli/kvdex": "npm:@jsr/olli__kvdex",
"@ts-rex/argon2": "npm:@jsr/ts-rex__argon2",
"lucia": "^3.2.0"
}
} }

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

3
src/app.pcss Normal file
View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

21
src/lib/Tab.svelte Normal file
View file

@ -0,0 +1,21 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let { tab = $bindable(), disabled = $bindable(false), map, ...props }: {
tab: string | undefined,
disabled?: boolean,
map?: Record<string, string>,
class?: string,
[x: `_${string}`]: Snippet<[]>;
} = $props();
const tabs = Object.fromEntries(Object.entries(props).filter((v) => v[0].startsWith('_')).map(([name, snippet]): [string, Snippet<[]>] => [name.replace('_', ''), snippet]));
</script>
<div role="tablist" class="tabs tabs-boxed {props.class}">
{#each Object.entries(tabs) as [tabID]}
<!-- svelte-ignore a11y_interactive_supports_focus -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_missing_attribute -->
<a class:cursor-not-allowed={disabled} class:pointer-events-none={disabled} class:tab-disabled={disabled} role="tab" class="tab uppercase" onclick={() => tab = tabID} class:tab-active={tabID == tab}>{@render tabs[tabID]()}</a>
{/each}
</div>

10
src/lib/apiclient.ts Normal file
View file

@ -0,0 +1,10 @@
import { type api } from "./hono"
import { hc } from "hono/client"
export const client = hc<api>('/api', {
async fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
return await fetch(input, {
...init,
credentials: 'include',
})
}
})

View file

@ -0,0 +1,2 @@
import { } from "@olli/kvdex"
import { } from "@deno/kv"

View file

@ -0,0 +1,6 @@
import { Hono } from "hono"
const api = new Hono()
export type api = typeof api
export const hono = new Hono().route('/api', api)

View file

@ -0,0 +1,6 @@
<script>
import "../app.pcss"
const { children } = $props()
</script>
{@render children()}

View file

@ -1,2 +1 @@
<h1>Welcome to SvelteKit</h1> <script></script>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>

View file

@ -0,0 +1,5 @@
import { hono } from '$lib/hono.js';
export async function fallback({ request }) {
return hono.fetch(request)
}

View file

@ -0,0 +1,33 @@
import { fail, message, superValidate } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import { z } from 'zod';
const schema = z.object({
username: z.string().min(4, "must be atleast 4 characters").max(32, "must be less than 32 characters").regex(/^[a-z0-9_\-]{4,32}$/i, `must be alphanumeric, with the exception of "_" and "-"`),
password: z.string().min(8, "must be atleast 8 characters")
});
export async function load() {
const form = await superValidate(zod(schema));
return { form };
};
export const actions = {
login: async ({ request }) => {
const form = await superValidate(request, zod(schema));
if (!form.valid) return fail(400, { form });
// TODO: Login user
return message(form, 'Login form submitted');
},
signup: async ({ request }) => {
const form = await superValidate(request, zod(schema));
if (!form.valid) return fail(400, { form });
// TODO: Login user
return message(form, 'Signup form submitted');
}
}

View file

@ -0,0 +1,71 @@
<script lang="ts">
import { superForm } from 'sveltekit-superforms';
const { data } = $props();
const { enhance, message, constraints, errors, form } = superForm(data.form);
</script>
<div class="h-[100vh] flex items-center justify-center">
<div class="card bg-base-100 w-96 shadow-xl">
<form class="m-2 flex-col flex gap-y-4" method="post" use:enhance>
<label class="input input-bordered flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-4">
<path
fill-rule="evenodd"
d="M11.89 4.111a5.5 5.5 0 1 0 0 7.778.75.75 0 1 1 1.06 1.061A7 7 0 1 1 15 8a2.5 2.5 0 0 1-4.083 1.935A3.5 3.5 0 1 1 11.5 8a1 1 0 0 0 2 0 5.48 5.48 0 0 0-1.61-3.889ZM10 8a2 2 0 1 0-4 0 2 2 0 0 0 4 0Z"
clip-rule="evenodd"></path>
</svg>
<input
bind:value={$form.username}
aria-invalid={$errors.username ? 'true' : undefined}
type="text"
class="grow placeholder:text-base-content/20"
placeholder="kaii" />
</label>
<span
class="opacity-0 hidden transition-opacity duration-1000 text-error"
class:showerror={$errors.username}>^ {$errors.username?.join(" & ")}</span>
<label class="input input-bordered flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-4">
<path
fill-rule="evenodd"
d="M8 1a3.5 3.5 0 0 0-3.5 3.5V7A1.5 1.5 0 0 0 3 8.5v5A1.5 1.5 0 0 0 4.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 7V4.5A3.5 3.5 0 0 0 8 1Zm2 6V4.5a2 2 0 1 0-4 0V7h4Z"
clip-rule="evenodd"></path>
</svg>
<input
bind:value={$form.password}
aria-invalid={$errors.password ? 'true' : undefined}
type="password"
class="grow placeholder:text-base-content/20"
placeholder="verygoodpassword" />
</label>
<span
class="opacity-0 hidden transition-opacity duration-1000 text-error"
class:showerror={$errors.password}>^ {$errors.password}</span>
<div class="flex flex-row gap-x-4">
<button formaction="?/signup" type="submit" class="btn btn-secondary flex-grow">
Signup
</button>
<button formaction="?/login" type="submit" class="btn btn-secondary btn-outline flex-grow">
Login
</button>
</div>
{#if $message}<span class="text-xs">{$message}</span>{/if}
</form>
</div>
</div>
<style lang="postcss">
.showerror {
@apply block opacity-100;
}
</style>

11
tailwind.config.js Normal file
View file

@ -0,0 +1,11 @@
import daisyui from "daisyui"
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {},
},
plugins: [daisyui],
}