Private
Public Access
1
0

Phase 7: Search, filters, templates, and archive UI

Add full-text card search (PostgreSQL tsvector + GIN index) with navbar
SearchBar, client-side board filters (label/assignee/due date), board
templates (3 system templates + save-as-template API), and archive
toggle for both board listing and board view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-26 00:42:20 -05:00
parent e5f2c64e14
commit 6ed0349507
14 changed files with 727 additions and 27 deletions
+36 -2
View File
@@ -3,6 +3,8 @@ import type { RequestHandler } from './$types.js';
import { boardSchema } from '$lib/server/validation.js';
import { withTenant } from '$lib/server/rls.js';
import { requireAuth } from '$lib/server/guards.js';
import { prisma } from '$lib/server/db.js';
import { generatePositions } from '$lib/utils/fractional-index.js';
// List boards for current tenant
export const GET: RequestHandler = async ({ locals }) => {
@@ -50,10 +52,26 @@ export const POST: RequestHandler = async ({ request, locals }) => {
return json({ error: result.error.issues[0].message }, { status: 400 });
}
const { title, description, visibility, categoryId } = result.data;
const { title, description, visibility, categoryId, templateId } = result.data;
// Load template columns if templateId provided
let templateColumns: { title: string }[] = [];
if (templateId) {
// System templates have null tenantId, so query without RLS
const template = await prisma.boardTemplate.findFirst({
where: {
id: templateId,
OR: [{ tenantId: null }, { tenantId: tenant.id }]
}
});
if (template) {
const tpl = template.template as { columns?: { title: string }[] };
templateColumns = tpl.columns || [];
}
}
const board = await withTenant(tenant.id, async (tx) => {
return tx.board.create({
const newBoard = await tx.board.create({
data: {
tenantId: tenant.id,
title,
@@ -73,6 +91,22 @@ export const POST: RequestHandler = async ({ request, locals }) => {
}
}
});
// Create columns from template
if (templateColumns.length > 0) {
const positions = generatePositions(templateColumns.length);
for (let i = 0; i < templateColumns.length; i++) {
await tx.column.create({
data: {
boardId: newBoard.id,
title: templateColumns[i].title,
position: positions[i]
}
});
}
}
return newBoard;
});
return json({ board }, { status: 201 });