feat: add saved views — database table, CRUD API, migration

- New views table (id, name, filters jsonb, sort_key, is_public, creator_id)
- GET/POST/PATCH/DELETE /views endpoints
- Register views router in server

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Gjermund Høsøien Wiggen
2026-06-09 11:10:25 +02:00
parent 000e97e1bd
commit aa90b88991
10 changed files with 1615 additions and 154 deletions

View File

@@ -13,8 +13,8 @@ import {
PanelLeftIcon,
CommandIcon,
} from "lucide-react";
import { getTickets, getQueues } from "@/lib/api";
import type { Queue } from "@/lib/types";
import { getTickets, getQueues, getViews } from "@/lib/api";
import type { Queue, SavedView } from "@/lib/types";
import { CommandPalette } from "@/components/command-palette";
import { ThemeToggle } from "@/components/theme-toggle";
import { cn } from "@/lib/utils";
@@ -86,6 +86,7 @@ function SidebarNav() {
recent: 0,
});
const [queues, setQueues] = useState<(Queue & { count: number })[]>([]);
const [savedViews, setSavedViews] = useState<SavedView[]>([]);
useEffect(() => {
getTickets().then(({ data }) => {
@@ -115,6 +116,10 @@ function SidebarNav() {
).then(setQueues);
}
});
getViews().then(({ data }) => {
if (data) setSavedViews(data);
});
}, []);
const collapsed = useSidebarCollapsed();
@@ -198,6 +203,29 @@ function SidebarNav() {
})}
</div>
)}
{savedViews.length > 0 && (
<div className="mt-4">
{!collapsed && (
<div className="px-2 py-1.5 text-[11px] font-semibold text-sidebar-foreground/45 uppercase">
Saved views
</div>
)}
{savedViews.map((view) => {
const active =
pathname === "/" && searchParams.get("view_id") === view.id;
return (
<SidebarNavItem
key={view.id}
href={`/?view_id=${view.id}`}
icon={ClockIcon}
label={view.name}
active={active}
/>
);
})}
</div>
)}
</>
);
}