diff --git a/src/db/seed.ts b/src/db/seed.ts index a1d73e6..fe5571f 100644 --- a/src/db/seed.ts +++ b/src/db/seed.ts @@ -12,6 +12,9 @@ import { templates, tickets, transactions, + views, + dashboards, + dashboardWidgets, users, } from './schema.ts'; @@ -314,6 +317,9 @@ async function resetDatabase(db: Db) { await db.delete(customFieldValues); await db.delete(transactions); await db.delete(queueCustomFields); + await db.delete(dashboardWidgets); + await db.delete(dashboards); + await db.delete(views); await db.delete(scrips); await db.delete(templates); await db.delete(tickets); @@ -775,6 +781,56 @@ async function main() { }))); console.log(`${reset ? 'Reset and seeded' : 'Seeded'} ${demoTickets.length} demo tickets across 4 queues`); + + // ── Dashboard seeding ── + const dashboardViews = [ + { name: 'Open tickets', filters: [{ field: 'status', operator: 'is', value: 'open' }] }, + { name: 'My tickets', filters: [{ field: 'owner', operator: 'is', value: userIds.dispatcher }] }, + { name: 'Unassigned', filters: [{ field: 'owner', operator: 'is', value: 'unassigned' }] }, + { name: 'All tickets', filters: [] }, + ]; + + const viewRecords: Record = {}; + for (const v of dashboardViews) { + const [row] = await db.insert(views).values({ + name: v.name, + filters: v.filters, + is_public: true, + }).returning(); + if (row) viewRecords[v.name] = row.id; + } + + const [dashboard] = await db.insert(dashboards).values({ + name: 'Support overview', + description: 'Daily support team dashboard', + is_default: true, + }).returning(); + + if (dashboard) { + const widgetDefs = [ + { view: 'Open tickets', type: 'count', title: 'Open tickets', x: 0, y: 0, w: 3, h: 1 }, + { view: 'My tickets', type: 'count', title: 'My tickets', x: 3, y: 0, w: 3, h: 1 }, + { view: 'Unassigned', type: 'count', title: 'Unassigned', x: 6, y: 0, w: 3, h: 1 }, + { view: 'All tickets', type: 'count', title: 'Total tickets', x: 9, y: 0, w: 3, h: 1 }, + { view: 'Open tickets', type: 'status_chart', title: 'Status breakdown', x: 0, y: 1, w: 4, h: 2 }, + { view: 'Open tickets', type: 'ticket_list', title: 'Recent open', x: 4, y: 1, w: 5, h: 2, config: { limit: 5 } }, + { view: 'All tickets', type: 'grouped_counts', title: 'By queue', x: 9, y: 1, w: 3, h: 2, config: { group_by: 'queue' } }, + ]; + + for (const w of widgetDefs) { + await db.insert(dashboardWidgets).values({ + dashboard_id: dashboard.id, + view_id: viewRecords[w.view], + title: w.title, + widget_type: w.type, + position: { x: w.x, y: w.y, w: w.w, h: w.h }, + config: w.config ?? {}, + }); + } + + console.log(`Seeded dashboard "${dashboard.name}" with ${widgetDefs.length} widgets`); + } + console.log('Demo data ready'); } finally { await pool.end(); diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index 07fcce0..0b3846b 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -409,7 +409,11 @@ function TicketWorkbenchContent() { return tickets .filter((ticket) => { - if (view === "my" && !ticket.owner_id) return false; + if (view === "my") { + const myOwner = searchParams.get("owner"); + if (myOwner && ticket.owner_id !== myOwner) return false; + if (!myOwner && !ticket.owner_id) return false; + } if (view === "unassigned" && ticket.owner_id) return false; if (view === "recent") { const week = 7 * 24 * 60 * 60 * 1000; diff --git a/web/src/components/app-shell.tsx b/web/src/components/app-shell.tsx index 4499c13..c63793c 100644 --- a/web/src/components/app-shell.tsx +++ b/web/src/components/app-shell.tsx @@ -13,8 +13,8 @@ import { PanelLeftIcon, CommandIcon, } from "lucide-react"; -import { getTickets, getQueues, getViews, getDashboards } from "@/lib/api"; -import type { Dashboard, Queue, SavedView } from "@/lib/types"; +import { getTickets, getQueues, getViews, getDashboards, getUsers } from "@/lib/api"; +import type { Dashboard, Queue, SavedView, User } from "@/lib/types"; import { CommandPalette } from "@/components/command-palette"; import { ThemeToggle } from "@/components/theme-toggle"; import { cn } from "@/lib/utils"; @@ -88,15 +88,23 @@ function SidebarNav() { const [queues, setQueues] = useState<(Queue & { count: number })[]>([]); const [savedViews, setSavedViews] = useState([]); const [dashboards, setDashboards] = useState([]); + const [currentUserId, setCurrentUserId] = useState(null); useEffect(() => { - getTickets().then(({ data }) => { + // Find current user and compute view counts + Promise.all([getTickets(), getUsers()]).then(([ticketRes, userRes]) => { + const data = ticketRes.data; + const users = userRes.data ?? []; + const currentUser = users.find((u) => u.username !== 'system') ?? users[0] ?? null; + if (currentUser) setCurrentUserId(currentUser.id); + if (data) { + const myId = currentUser?.id; const now = Date.now(); const week = 7 * 24 * 60 * 60 * 1000; setCounts({ all: data.length, - my: data.filter((t) => t.owner_id).length, + my: myId ? data.filter((t) => t.owner_id === myId).length : 0, unassigned: data.filter((t) => !t.owner_id).length, recent: data.filter( (t) => new Date(t.updated_at).getTime() > now - week @@ -139,7 +147,7 @@ function SidebarNav() { }, { label: "My tickets", - href: "/?view=my", + href: currentUserId ? `/?view=my&owner=${currentUserId}` : "/?view=my", param: "my", count: counts.my, icon: UserIcon,