feat: add teams/groups with dashboard scoping

Schema:
- teams table (name unique, description)
- team_members table (team_id, user_id, unique constraint)
- team_id column on dashboards

API:
- GET/POST/PATCH/DELETE /teams
- POST /teams/:id/members (add user)
- DELETE /teams/:id/members/:userId (remove user)
- dashboards support team_id on create/update

Frontend:
- Teams tab in admin: CRUD + member management with add/remove
- Sidebar: dashboards filtered to user's teams
  (unassigned dashboards visible to all)
- Compact dashboard picker dropdown in sidebar

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Gjermund Høsøien Wiggen
2026-06-09 13:32:39 +02:00
parent c79cd183d4
commit 3616046b78
11 changed files with 1713 additions and 25 deletions

View File

@@ -42,6 +42,7 @@ export function createDashboardsRouter(db: Db): Hono {
const [dashboard] = await db.insert(dashboards).values({
name,
description: body.description ?? null,
team_id: body.team_id || null,
layout: body.layout ?? [],
is_default: body.is_default ?? false,
}).returning();
@@ -87,9 +88,9 @@ export function createDashboardsRouter(db: Db): Hono {
if (body.name !== undefined) updateData.name = String(body.name).trim();
if (body.description !== undefined) updateData.description = body.description ?? null;
if (body.layout !== undefined) updateData.layout = body.layout;
if (body.team_id !== undefined) updateData.team_id = body.team_id || null;
if (body.is_default !== undefined) {
updateData.is_default = body.is_default;
// If setting this as default, unset others
if (body.is_default) {
await db.update(dashboards)
.set({ is_default: false })