feat: queues have default team, tickets inherit it

- team_id on queues table (optional, can be overridden per-ticket)
- Ticket creation auto-sets team_id from the queue's default
- Queue admin form has team selector (scrip flow node 03)
- Queue API (POST/PATCH) accepts team_id

No enforcement — just a helpful default. Teams and queues
are loosely coupled, not hierarchically locked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Gjermund Høsøien Wiggen
2026-06-09 14:47:20 +02:00
parent 3d7ba0d6a7
commit 4e285f8c4d
10 changed files with 1367 additions and 3 deletions

View File

@@ -13,6 +13,7 @@ export const queues = pgTable('queues', {
name: text('name').notNull().unique(),
description: text('description'),
lifecycle_id: uuid('lifecycle_id').references(() => lifecycles.id),
team_id: uuid('team_id').references(() => teams.id, { onDelete: 'set null' }),
created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
});

View File

@@ -8,4 +8,5 @@ export const CreateQueueSchema = z.object({
name: z.string().min(1),
description: z.string().optional(),
lifecycle_id: z.string().uuid().optional(),
team_id: z.string().uuid().nullable().optional(),
});

View File

@@ -23,6 +23,7 @@ export function createQueuesRouter(db: Db): Hono {
name: parsed.name,
description: parsed.description ?? null,
lifecycle_id: parsed.lifecycle_id ?? null,
team_id: parsed.team_id ?? null,
}).returning();
if (!queue) {
@@ -48,6 +49,7 @@ export function createQueuesRouter(db: Db): Hono {
if (body.name !== undefined) updateData.name = String(body.name);
if (body.description !== undefined) updateData.description = body.description ? String(body.description) : null;
if (body.lifecycle_id !== undefined) updateData.lifecycle_id = body.lifecycle_id || null;
if (body.team_id !== undefined) updateData.team_id = body.team_id || null;
const [updated] = await db.update(queues)
.set(updateData)

View File

@@ -183,6 +183,7 @@ export function createTicketsRouter(db: Db): Hono {
queue_id: parsed.queue_id,
status: initialStatus,
creator_id: creatorId,
team_id: (queue as any).team_id ?? null,
}).returning();
if (!ticket) {