feat: team assignment on tickets + My team's tickets view

Backend:
- team_id column on tickets table
- team_id filter in GET /tickets (resolves team members)
- team_id in UpdateTicketSchema + PATCH handler
- SetTeam transaction type

Frontend:
- Team selector in ticket detail properties sidebar
- My team's tickets in sidebar (when user belongs to a team)
- team_id passed through to API from ticket list page

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

View File

@@ -8,6 +8,7 @@ import {
LayoutGridIcon,
PlusIcon,
UserIcon,
UsersIcon,
InboxIcon,
ClockIcon,
SettingsIcon,
@@ -91,6 +92,7 @@ function SidebarNav() {
const [savedViews, setSavedViews] = useState<SavedView[]>([]);
const [dashboards, setDashboards] = useState<Dashboard[]>([]);
const [currentUserId, setCurrentUserId] = useState<string | null>(null);
const [myTeamId, setMyTeamId] = useState<string | null>(null);
const [expanded, setExpanded] = useState<Record<string, boolean>>({
dashboards: true,
queues: true,
@@ -145,6 +147,7 @@ function SidebarNav() {
const userTeams = allTeams.filter((t) =>
(t.members ?? []).some((m) => m.id === myId)
);
setMyTeamId(userTeams[0]?.id ?? null);
const teamIds = new Set(userTeams.map((t) => t.id));
const visible = allDashboards.filter((d) =>
!d.team_id || teamIds.has(d.team_id)
@@ -171,6 +174,13 @@ function SidebarNav() {
count: counts.my,
icon: UserIcon,
},
...(myTeamId ? [{
label: "My team's tickets",
href: `/?view=team&team_id=${myTeamId}`,
param: "team",
count: undefined as number | undefined,
icon: UsersIcon,
}] : []),
{
label: "Unassigned",
href: "/?view=unassigned",