Create auth page, add styling & prettier
This commit is contained in:
parent
8da9ef55ed
commit
4ad3290e04
16 changed files with 235 additions and 34 deletions
4
.prettierignore
Normal file
4
.prettierignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Package Managers
|
||||||
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
|
yarn.lock
|
18
.prettierrc
Normal file
18
.prettierrc
Normal 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
BIN
bun.lockb
Binary file not shown.
70
package.json
70
package.json
|
@ -1,34 +1,40 @@
|
||||||
{
|
{
|
||||||
"name": "talkomatic",
|
"name": "talkomatic",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"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 .",
|
||||||
"devDependencies": {
|
"lint": "prettier --check ."
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
},
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"devDependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@deno/kv": "^0.8.1",
|
||||||
"autoprefixer": "^10.4.20",
|
"@olli/kvdex": "npm:@jsr/olli__kvdex",
|
||||||
"daisyui": "^4.12.10",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"postcss": "^8.4.41",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"svelte": "^5.0.0-next.1",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"svelte-check": "^3.6.0",
|
"@ts-rex/argon2": "npm:@jsr/ts-rex__argon2",
|
||||||
"tailwindcss": "^3.4.10",
|
"autoprefixer": "^10.4.20",
|
||||||
"typescript": "^5.0.0",
|
"daisyui": "^4.12.10",
|
||||||
"vite": "^5.0.3"
|
"hono": "^4.5.5",
|
||||||
},
|
"lucia": "^3.2.0",
|
||||||
"type": "module",
|
"postcss": "^8.4.41",
|
||||||
"trustedDependencies": [
|
"prettier": "^3.3.2",
|
||||||
"svelte-preprocess"
|
"prettier-plugin-svelte": "^3.2.5",
|
||||||
],
|
"svelte": "^5.0.0-next.1",
|
||||||
"dependencies": {
|
"svelte-check": "^3.6.0",
|
||||||
"@olli/kvdex": "npm:@jsr/olli__kvdex",
|
"sveltekit-superforms": "^2.17.0",
|
||||||
"@ts-rex/argon2": "npm:@jsr/ts-rex__argon2",
|
"tailwindcss": "^3.4.10",
|
||||||
"lucia": "^3.2.0"
|
"typescript": "^5.0.0",
|
||||||
}
|
"vite": "^5.0.3",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"trustedDependencies": [
|
||||||
|
"svelte-preprocess"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
3
src/app.pcss
Normal file
3
src/app.pcss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
21
src/lib/Tab.svelte
Normal file
21
src/lib/Tab.svelte
Normal 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
10
src/lib/apiclient.ts
Normal 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',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,2 @@
|
||||||
|
import { } from "@olli/kvdex"
|
||||||
|
import { } from "@deno/kv"
|
|
@ -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)
|
6
src/routes/+layout.svelte
Normal file
6
src/routes/+layout.svelte
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<script>
|
||||||
|
import "../app.pcss"
|
||||||
|
const { children } = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{@render children()}
|
|
@ -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>
|
|
5
src/routes/api/[...path]/+server.ts
Normal file
5
src/routes/api/[...path]/+server.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { hono } from '$lib/hono.js';
|
||||||
|
|
||||||
|
export async function fallback({ request }) {
|
||||||
|
return hono.fetch(request)
|
||||||
|
}
|
33
src/routes/auth/+page.server.ts
Normal file
33
src/routes/auth/+page.server.ts
Normal 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');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
71
src/routes/auth/+page.svelte
Normal file
71
src/routes/auth/+page.svelte
Normal 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
11
tailwind.config.js
Normal 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],
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue