From 1c26c047a175041c3c347f169807cccb5cef3122 Mon Sep 17 00:00:00 2001 From: alfredorubin96 Date: Tue, 31 Mar 2026 13:52:55 +0200 Subject: [PATCH 1/3] feat(app): add duplicate template and test query for Widget Lab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add "Duplicate" button (Copy icon) on template cards — creates a copy with "(copy)" suffix via existing createTemplate mutation - Add "Test Query" button (Play icon) on templates that have a query and connection — executes the query via /api/query and shows success/error result - Test query button disabled while running, shows loading state Closes #270 Co-Authored-By: Claude Opus 4.6 (1M context) --- app/src/app/(dashboard)/widget-lab/page.tsx | 142 +++++++++++++++++--- 1 file changed, 126 insertions(+), 16 deletions(-) diff --git a/app/src/app/(dashboard)/widget-lab/page.tsx b/app/src/app/(dashboard)/widget-lab/page.tsx index 16782861..7b6ad0f4 100644 --- a/app/src/app/(dashboard)/widget-lab/page.tsx +++ b/app/src/app/(dashboard)/widget-lab/page.tsx @@ -2,9 +2,21 @@ import { useState, useMemo } from "react"; import { useRouter } from "next/navigation"; -import { FlaskConical, Trash2, Pencil, Plus, LayoutDashboard } from "lucide-react"; +import { + FlaskConical, + Trash2, + Pencil, + Plus, + LayoutDashboard, + Copy, + Play, +} from "lucide-react"; import { useSession } from "next-auth/react"; -import { useWidgetTemplates, useDeleteWidgetTemplate } from "@/hooks/use-widget-templates"; +import { + useWidgetTemplates, + useDeleteWidgetTemplate, + useCreateWidgetTemplate, +} from "@/hooks/use-widget-templates"; import { useConnections } from "@/hooks/use-connections"; import { getChartConfig } from "@/lib/chart-registry"; import { DashboardPickerDialog } from "@/components/dashboard-picker-dialog"; @@ -32,6 +44,9 @@ function TemplateCard({ canDelete, onEdit, onDelete, + onDuplicate, + onTestQuery, + testQueryLoading, onUseInDashboard, }: { readonly template: WidgetTemplate; @@ -39,13 +54,19 @@ function TemplateCard({ readonly canDelete: boolean; readonly onEdit: () => void; readonly onDelete: () => void; + readonly onDuplicate: () => void; + readonly onTestQuery: () => void; + readonly testQueryLoading: boolean; readonly onUseInDashboard: () => void; }) { const chartLabel = getChartConfig(template.chartType)?.label ?? template.chartType; return ( -
+
@@ -66,6 +87,27 @@ function TemplateCard({ > + + {template.query && template.connectionId && ( + + )} {canEdit && (