fix: resolve runtime errors and missing routes
- Fix $effect orphan error: move $effect calls from store constructors to initEffects() methods called from component context - Fix icon rendering: create DynamicIcon component to render Lucide icons from name strings instead of displaying raw text - Add /boards/new route for board creation - Fix seed emails (admin@launcher.local / user@launcher.local) to pass Zod email validation
This commit is contained in:
@@ -5,9 +5,17 @@
|
||||
import MainLayout from '$lib/components/layout/MainLayout.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { theme } from '$lib/stores/theme.svelte';
|
||||
import { ui } from '$lib/stores/ui.svelte';
|
||||
import { search } from '$lib/stores/search.svelte';
|
||||
|
||||
let { data, children }: { data: LayoutData; children: Snippet } = $props();
|
||||
|
||||
// Initialize store effects within component context
|
||||
theme.initEffects();
|
||||
ui.initEffects();
|
||||
search.initEffects();
|
||||
|
||||
// Pages that should NOT have the main layout (login, register)
|
||||
const noLayoutPaths = ['/login', '/register'];
|
||||
const showLayout = $derived(!noLayoutPaths.includes($page.url.pathname));
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { PageServerLoad, Actions } from './$types.js';
|
||||
import { redirect, fail } from '@sveltejs/kit';
|
||||
import { superValidate, message } from 'sveltekit-superforms';
|
||||
import { zod } from '$lib/utils/zod-adapter.js';
|
||||
import { createBoardSchema } from '$lib/utils/validators.js';
|
||||
import * as boardService from '$lib/server/services/boardService.js';
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
if (!locals.user || locals.user.role !== 'admin') {
|
||||
throw redirect(302, '/boards');
|
||||
}
|
||||
|
||||
const form = await superValidate(zod(createBoardSchema));
|
||||
return { form };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request, locals }) => {
|
||||
if (!locals.user || locals.user.role !== 'admin') {
|
||||
return fail(403, { error: 'Forbidden' });
|
||||
}
|
||||
|
||||
const form = await superValidate(request, zod(createBoardSchema));
|
||||
if (!form.valid) {
|
||||
return fail(400, { form });
|
||||
}
|
||||
|
||||
try {
|
||||
const board = await boardService.createBoard({
|
||||
...form.data,
|
||||
createdById: locals.user.id
|
||||
});
|
||||
throw redirect(302, `/boards/${board.id}`);
|
||||
} catch (err) {
|
||||
if (err && typeof err === 'object' && 'status' in err && err.status === 302) {
|
||||
throw err;
|
||||
}
|
||||
return message(form, 'Failed to create board', { status: 500 });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types.js';
|
||||
import { superForm } from 'sveltekit-superforms';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
const { form, errors, enhance, submitting } = superForm(data.form);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>New Board — Web App Launcher</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="p-6">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<div class="mb-6">
|
||||
<a href="/boards" class="text-sm text-muted-foreground hover:text-foreground">
|
||||
← Back to Boards
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h1 class="mb-6 text-3xl font-bold text-foreground">New Board</h1>
|
||||
|
||||
<form method="POST" use:enhance class="space-y-4 rounded-xl border border-border bg-card p-6">
|
||||
<div>
|
||||
<label for="name" class="mb-1 block text-sm font-medium text-foreground">Name</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
bind:value={$form.name}
|
||||
class="w-full rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
placeholder="My Dashboard"
|
||||
required
|
||||
/>
|
||||
{#if $errors.name}<p class="mt-1 text-xs text-destructive">{$errors.name}</p>{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="description" class="mb-1 block text-sm font-medium text-foreground">Description</label>
|
||||
<input
|
||||
id="description"
|
||||
name="description"
|
||||
type="text"
|
||||
bind:value={$form.description}
|
||||
class="w-full rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
placeholder="Optional description"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="icon" class="mb-1 block text-sm font-medium text-foreground">Icon (Lucide name)</label>
|
||||
<input
|
||||
id="icon"
|
||||
name="icon"
|
||||
type="text"
|
||||
bind:value={$form.icon}
|
||||
class="w-full rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
placeholder="layout-dashboard"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="flex items-center gap-2 text-sm text-foreground">
|
||||
<input type="checkbox" name="isDefault" bind:checked={$form.isDefault} class="rounded" />
|
||||
Default board
|
||||
</label>
|
||||
|
||||
<label class="flex items-center gap-2 text-sm text-foreground">
|
||||
<input type="checkbox" name="isGuestAccessible" bind:checked={$form.isGuestAccessible} class="rounded" />
|
||||
Guest accessible
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-3 pt-2">
|
||||
<a href="/boards" class="rounded-lg border border-border px-4 py-2 text-sm text-foreground hover:bg-accent">
|
||||
Cancel
|
||||
</a>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={$submitting}
|
||||
class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||
>
|
||||
{$submitting ? 'Creating...' : 'Create Board'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user