feat(mvp): phase 5 - board, section & widget system

Add board/section/widget CRUD APIs with permission filtering, board view
page with collapsible sections and app widgets in responsive grid, form-based
board editor, and 9 Svelte components (Board, Section, Widget families).
This commit is contained in:
2026-03-24 21:05:00 +03:00
parent 4d941f566f
commit b0d77d3c29
23 changed files with 1564 additions and 27 deletions
+57
View File
@@ -0,0 +1,57 @@
<script lang="ts">
interface BoardSummary {
id: string;
name: string;
icon: string | null;
description: string | null;
isDefault: boolean;
isGuestAccessible: boolean;
_count?: { sections: number };
}
interface Props {
board: BoardSummary;
}
let { board }: Props = $props();
const sectionCount = $derived(board._count?.sections ?? 0);
</script>
<a
href="/boards/{board.id}"
class="group block rounded-lg border border-gray-700 bg-gray-800/50 p-5 transition-colors hover:border-indigo-500/50 hover:bg-gray-800"
>
<div class="flex items-start gap-3">
{#if board.icon}
<span class="text-xl">{board.icon}</span>
{:else}
<span class="flex h-8 w-8 items-center justify-center rounded-md bg-gray-700 text-sm text-gray-400">
B
</span>
{/if}
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<h3 class="truncate font-semibold text-white group-hover:text-indigo-300 transition-colors">
{board.name}
</h3>
{#if board.isDefault}
<span class="shrink-0 rounded bg-indigo-600/20 px-1.5 py-0.5 text-xs text-indigo-400">
Default
</span>
{/if}
{#if board.isGuestAccessible}
<span class="shrink-0 rounded bg-green-600/20 px-1.5 py-0.5 text-xs text-green-400">
Guest
</span>
{/if}
</div>
{#if board.description}
<p class="mt-1 line-clamp-2 text-sm text-gray-400">{board.description}</p>
{/if}
<p class="mt-2 text-xs text-gray-500">
{sectionCount} section{sectionCount === 1 ? '' : 's'}
</p>
</div>
</div>
</a>