feat: dashboard auto-refresh, collapsible sidebar, error retry

- Dashboard: auto-refresh toggle (30s interval, spins when live)
- Dashboard: responsive grid (6 cols mobile, 12 cols desktop)
- Sidebar: Dashboards, Saved views, Queues sections now collapsible
  with chevron toggle
- Error banner: added Retry button next to error message

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Gjermund Høsøien Wiggen
2026-06-09 13:14:47 +02:00
parent 6263ce1332
commit f7e34f1690
3 changed files with 93 additions and 36 deletions

View File

@@ -54,6 +54,7 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string
const [views, setViews] = useState<SavedView[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [autoRefresh, setAutoRefresh] = useState(false);
// Add widget dialog
const [addOpen, setAddOpen] = useState(false);
@@ -94,6 +95,23 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string
});
}, [fetchDashboard]);
// Auto-refresh: only refresh widget data, not structure
useEffect(() => {
if (!autoRefresh || !dashboard) return;
const interval = setInterval(() => {
for (const widget of widgets) {
getWidgetData(dashboard.id, widget.id).then(({ data: wData }) => {
if (wData) {
setWidgets((prev) =>
prev.map((w) => (w.id === widget.id ? { ...w, data: wData } : w))
);
}
});
}
}, 30_000);
return () => clearInterval(interval);
}, [autoRefresh, dashboard?.id]);
const handleAddWidget = async () => {
if (!addViewId || !addTitle.trim()) return;
setAdding(true);
@@ -194,6 +212,15 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string
)}
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setAutoRefresh((v) => !v)}
className={cn("h-8 border-border/80", autoRefresh ? "bg-primary/20 text-primary" : "bg-card/70")}
>
<RefreshCwIcon className={cn("h-4 w-4", autoRefresh && "animate-spin")} />
{autoRefresh ? "Live" : "Auto"}
</Button>
<Button
variant="outline"
size="sm"
@@ -222,7 +249,7 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string
</Button>
</div>
) : (
<div className="grid auto-rows-[minmax(120px,auto)] grid-cols-12 gap-4">
<div className="grid auto-rows-[minmax(100px,auto)] grid-cols-6 gap-3 md:grid-cols-12 md:gap-4">
{widgets.map((widget) => (
<div
key={widget.id}