From d5d6a209bde9ac71200e81ccb120e9ecefc33e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gjermund=20H=C3=B8s=C3=B8ien=20Wiggen?= Date: Tue, 9 Jun 2026 14:51:51 +0200 Subject: [PATCH] fix: All tickets link, redesign side panel into triage panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix sidebar All tickets link: /?view=all instead of / (avoids dashboard redirect) - Replace useless side panel with triage command center: - Quick status change buttons (click to transition inline) - Assign to me button (appears when unassigned) - Mini activity feed showing last 8 transactions with type labels, status badges, old→new values, and comment previews - Relative timestamps - Open full view button - Fetches ticket transactions on selection Co-Authored-By: Claude Opus 4.8 --- web/src/app/page.tsx | 175 ++++++++++++++++++++++--------- web/src/components/app-shell.tsx | 4 +- 2 files changed, 129 insertions(+), 50 deletions(-) diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index c9de44f..f698e7c 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -17,8 +17,8 @@ import { XIcon, } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; -import { createTicket, getCustomFields, getLifecycles, getQueueCustomFields, getQueues, getTickets, getUsers, getViews, createView, deleteView, getDashboards } from "@/lib/api"; -import type { CustomField, Lifecycle, Queue, QueueCustomField, SavedView, Ticket, User } from "@/lib/types"; +import { createTicket, getCustomFields, getLifecycles, getQueueCustomFields, getQueues, getTickets, getUsers, getViews, createView, deleteView, getDashboards, getTicketTransactions, updateTicket } from "@/lib/api"; +import type { CustomField, Lifecycle, Queue, QueueCustomField, SavedView, Ticket, Transaction, User } from "@/lib/types"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -148,6 +148,7 @@ function TicketWorkbenchContent() { const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [selectedId, setSelectedId] = useState(null); + const [selectedTxs, setSelectedTxs] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [filters, setFilters] = useState([]); @@ -442,7 +443,28 @@ function TicketWorkbenchContent() { }, [clock, filters, queues, routeQueue, searchQuery, sortKey, tickets, view]); const selectedTicket = - filteredTickets.find((ticket) => ticket.id === selectedId) ?? filteredTickets[0] ?? null; + filteredTickets.find((ticket) => ticket.id === selectedId) ?? null; + + // Fetch transactions when selection changes + useEffect(() => { + if (!selectedTicket) { setSelectedTxs([]); return; } + getTicketTransactions(selectedTicket.id).then(({ data }) => setSelectedTxs(data ?? [])); + }, [selectedTicket?.id]); + + const handleQuickStatus = async (ticketId: number, newStatus: string) => { + const { data } = await updateTicket(ticketId, { status: newStatus }); + if (data) { + setTickets((prev) => prev.map((t) => (t.id === ticketId ? data.ticket : t))); + getTicketTransactions(ticketId).then(({ data: txs }) => setSelectedTxs(txs ?? [])); + } + }; + + const handleQuickAssign = async (ticketId: number) => { + const { data } = await updateTicket(ticketId, { owner_id: users[0]?.id ?? null }); + if (data) { + setTickets((prev) => prev.map((t) => (t.id === ticketId ? data.ticket : t))); + } + }; const visibleTitle = routeQueue ? queueName(queues, routeQueue) : VIEW_LABELS[view] ?? "All tickets"; @@ -776,66 +798,123 @@ function TicketWorkbenchContent() { diff --git a/web/src/components/app-shell.tsx b/web/src/components/app-shell.tsx index bb4932b..7777e06 100644 --- a/web/src/components/app-shell.tsx +++ b/web/src/components/app-shell.tsx @@ -162,8 +162,8 @@ function SidebarNav() { const views = [ { label: "All tickets", - href: "/", - param: null, + href: "/?view=all", + param: "all", count: counts.all, icon: LayoutGridIcon, },