feat: add dashboards — tables, CRUD API, widget data endpoint
- New dashboards table (name, description, layout, is_default) - New dashboard_widgets table (view_id, title, widget_type, position, config) - GET/POST/PATCH/DELETE /dashboards - GET/POST/PATCH/DELETE /dashboards/:id/widgets - GET /dashboards/:id/widgets/:id/data — runs saved view filters, returns pre-aggregated data for count/ticket_list/status_chart/grouped_counts - is_default uniqueness enforced on PATCH Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,8 @@ import {
|
||||
PanelLeftIcon,
|
||||
CommandIcon,
|
||||
} from "lucide-react";
|
||||
import { getTickets, getQueues, getViews } from "@/lib/api";
|
||||
import type { Queue, SavedView } from "@/lib/types";
|
||||
import { getTickets, getQueues, getViews, getDashboards } from "@/lib/api";
|
||||
import type { Dashboard, Queue, SavedView } from "@/lib/types";
|
||||
import { CommandPalette } from "@/components/command-palette";
|
||||
import { ThemeToggle } from "@/components/theme-toggle";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -87,6 +87,7 @@ function SidebarNav() {
|
||||
});
|
||||
const [queues, setQueues] = useState<(Queue & { count: number })[]>([]);
|
||||
const [savedViews, setSavedViews] = useState<SavedView[]>([]);
|
||||
const [dashboards, setDashboards] = useState<Dashboard[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getTickets().then(({ data }) => {
|
||||
@@ -120,6 +121,10 @@ function SidebarNav() {
|
||||
getViews().then(({ data }) => {
|
||||
if (data) setSavedViews(data);
|
||||
});
|
||||
|
||||
getDashboards().then(({ data }) => {
|
||||
if (data) setDashboards(data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const collapsed = useSidebarCollapsed();
|
||||
@@ -204,6 +209,29 @@ function SidebarNav() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{dashboards.length > 0 && (
|
||||
<div className="mt-4">
|
||||
{!collapsed && (
|
||||
<div className="px-2 py-1.5 text-[11px] font-semibold text-sidebar-foreground/45 uppercase">
|
||||
Dashboards
|
||||
</div>
|
||||
)}
|
||||
{dashboards.map((dash) => {
|
||||
const active =
|
||||
pathname.startsWith("/dashboards/") && pathname.endsWith(dash.id);
|
||||
return (
|
||||
<SidebarNavItem
|
||||
key={dash.id}
|
||||
href={`/dashboards/${dash.id}`}
|
||||
icon={LayoutGridIcon}
|
||||
label={dash.name}
|
||||
active={active}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{savedViews.length > 0 && (
|
||||
<div className="mt-4">
|
||||
{!collapsed && (
|
||||
|
||||
Reference in New Issue
Block a user