feat: add dashboard layout with navigation and auth guard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
29
web/src/lib/api/auth.remote.ts
Normal file
29
web/src/lib/api/auth.remote.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { form, getRequestEvent, query } from '$app/server';
|
||||
import { auth } from '$lib/server/auth';
|
||||
import { signupSchema, loginSchema } from '$lib/schema/auth';
|
||||
|
||||
export const signup = form(signupSchema, async (user) => {
|
||||
await auth.api.signUpEmail({ body: user });
|
||||
redirect(307, '/dashboard');
|
||||
});
|
||||
|
||||
export const login = form(loginSchema, async (user) => {
|
||||
const { request } = getRequestEvent();
|
||||
await auth.api.signInEmail({ body: user, headers: request.headers });
|
||||
redirect(303, '/dashboard');
|
||||
});
|
||||
|
||||
export const signout = form(async () => {
|
||||
const { request } = getRequestEvent();
|
||||
await auth.api.signOut({ headers: request.headers });
|
||||
redirect(303, '/login');
|
||||
});
|
||||
|
||||
export const getUser = query(async () => {
|
||||
const { locals } = getRequestEvent();
|
||||
if (!locals.user) {
|
||||
redirect(307, '/login');
|
||||
}
|
||||
return locals.user;
|
||||
});
|
||||
7
web/src/routes/+layout.server.ts
Normal file
7
web/src/routes/+layout.server.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
|
||||
export const load: LayoutServerLoad = async ({ locals }) => {
|
||||
return {
|
||||
user: locals.user ?? null
|
||||
};
|
||||
};
|
||||
9
web/src/routes/+page.server.ts
Normal file
9
web/src/routes/+page.server.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
if (locals.user) {
|
||||
redirect(307, '/dashboard');
|
||||
}
|
||||
redirect(307, '/login');
|
||||
};
|
||||
1
web/src/routes/+page.svelte
Normal file
1
web/src/routes/+page.svelte
Normal file
@@ -0,0 +1 @@
|
||||
<p>Redirecting...</p>
|
||||
11
web/src/routes/dashboard/+layout.server.ts
Normal file
11
web/src/routes/dashboard/+layout.server.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
|
||||
export const load: LayoutServerLoad = async ({ locals }) => {
|
||||
if (!locals.user) {
|
||||
redirect(307, '/login');
|
||||
}
|
||||
return {
|
||||
user: locals.user
|
||||
};
|
||||
};
|
||||
29
web/src/routes/dashboard/+layout.svelte
Normal file
29
web/src/routes/dashboard/+layout.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { signout } from '$lib/api/auth.remote';
|
||||
|
||||
let { children, data } = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex min-h-screen">
|
||||
<aside class="flex w-64 flex-col border-r border-neutral-200 bg-neutral-50 p-6">
|
||||
<h1 class="mb-8 text-xl font-bold">DroidClaw</h1>
|
||||
<nav class="flex flex-col gap-2">
|
||||
<a href="/dashboard" class="rounded px-3 py-2 hover:bg-neutral-200">Overview</a>
|
||||
<a href="/dashboard/devices" class="rounded px-3 py-2 hover:bg-neutral-200">Devices</a>
|
||||
<a href="/dashboard/api-keys" class="rounded px-3 py-2 hover:bg-neutral-200">API Keys</a>
|
||||
<a href="/dashboard/settings" class="rounded px-3 py-2 hover:bg-neutral-200">Settings</a>
|
||||
</nav>
|
||||
<div class="mt-auto pt-8">
|
||||
<p class="mb-2 text-sm text-neutral-500">{data.user.email}</p>
|
||||
<form {...signout}>
|
||||
<button type="submit" class="text-sm text-neutral-500 hover:text-neutral-800">
|
||||
Sign out
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="flex-1 p-8">
|
||||
{@render children?.()}
|
||||
</main>
|
||||
</div>
|
||||
30
web/src/routes/dashboard/+page.svelte
Normal file
30
web/src/routes/dashboard/+page.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<h2 class="mb-6 text-2xl font-bold">Dashboard</h2>
|
||||
<p class="text-neutral-600">Welcome back, {data.user.name}.</p>
|
||||
|
||||
<div class="mt-8 grid grid-cols-3 gap-6">
|
||||
<a
|
||||
href="/dashboard/devices"
|
||||
class="rounded-lg border border-neutral-200 p-6 hover:border-neutral-400"
|
||||
>
|
||||
<h3 class="font-semibold">Devices</h3>
|
||||
<p class="mt-1 text-sm text-neutral-500">Manage connected phones</p>
|
||||
</a>
|
||||
<a
|
||||
href="/dashboard/api-keys"
|
||||
class="rounded-lg border border-neutral-200 p-6 hover:border-neutral-400"
|
||||
>
|
||||
<h3 class="font-semibold">API Keys</h3>
|
||||
<p class="mt-1 text-sm text-neutral-500">Create keys for your devices</p>
|
||||
</a>
|
||||
<a
|
||||
href="/dashboard/settings"
|
||||
class="rounded-lg border border-neutral-200 p-6 hover:border-neutral-400"
|
||||
>
|
||||
<h3 class="font-semibold">Settings</h3>
|
||||
<p class="mt-1 text-sm text-neutral-500">Configure LLM provider</p>
|
||||
</a>
|
||||
</div>
|
||||
Reference in New Issue
Block a user