From 3474fbd12d8b91ebf3a93360a644be2a1cddb487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Mon, 3 Nov 2025 22:14:10 +0100 Subject: [PATCH] feat: duplicate report --- ...d.$projectId_.dashboards_.$dashboardId.tsx | 25 +++++++++++ packages/trpc/src/routers/report.ts | 43 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/apps/start/src/routes/_app.$organizationId.$projectId_.dashboards_.$dashboardId.tsx b/apps/start/src/routes/_app.$organizationId.$projectId_.dashboards_.$dashboardId.tsx index e8dff57b..f53387af 100644 --- a/apps/start/src/routes/_app.$organizationId.$projectId_.dashboards_.$dashboardId.tsx +++ b/apps/start/src/routes/_app.$organizationId.$projectId_.dashboards_.$dashboardId.tsx @@ -12,6 +12,7 @@ import { import { cn } from '@/utils/cn'; import { createProjectTitle } from '@/utils/title'; import { + CopyIcon, LayoutPanelTopIcon, MoreHorizontal, PlusIcon, @@ -122,6 +123,7 @@ function ReportItem({ endDate, interval, onDelete, + onDuplicate, }: { report: any; organizationId: string; @@ -131,6 +133,7 @@ function ReportItem({ endDate: any; interval: any; onDelete: (reportId: string) => void; + onDuplicate: (reportId: string) => void; }) { const router = useRouter(); const chartRange = report.range; @@ -218,6 +221,15 @@ function ReportItem({ + { + event.stopPropagation(); + onDuplicate(report.id); + }} + > + + Duplicate + { reportDeletion.mutate({ reportId }); }} + onDuplicate={(reportId) => { + reportDuplicate.mutate({ reportId }); + }} /> ))} diff --git a/packages/trpc/src/routers/report.ts b/packages/trpc/src/routers/report.ts index 7d5f75b3..723803ea 100644 --- a/packages/trpc/src/routers/report.ts +++ b/packages/trpc/src/routers/report.ts @@ -135,6 +135,49 @@ export const reportRouter = createTRPCRouter({ }, }); }), + duplicate: protectedProcedure + .input( + z.object({ + reportId: z.string(), + }), + ) + .mutation(async ({ input: { reportId }, ctx }) => { + const report = await db.report.findUniqueOrThrow({ + where: { + id: reportId, + }, + }); + + const access = await getProjectAccess({ + userId: ctx.session.userId, + projectId: report.projectId, + }); + + if (!access) { + throw TRPCAccessError('You do not have access to this project'); + } + + return db.report.create({ + data: { + projectId: report.projectId, + dashboardId: report.dashboardId, + name: `Copy of ${report.name}`, + events: report.events!, + interval: report.interval, + breakdowns: report.breakdowns!, + chartType: report.chartType, + lineType: report.lineType, + range: report.range, + formula: report.formula, + previous: report.previous, + unit: report.unit, + criteria: report.criteria, + metric: report.metric, + funnelGroup: report.funnelGroup, + funnelWindow: report.funnelWindow, + }, + }); + }), get: protectedProcedure .input( z.object({