feat: duplicate report

This commit is contained in:
Carl-Gerhard Lindesvärd
2025-11-03 22:14:10 +01:00
parent 74754cf65b
commit 3474fbd12d
2 changed files with 68 additions and 0 deletions

View File

@@ -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({
<MoreHorizontal size={16} />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[200px]">
<DropdownMenuItem
onClick={(event) => {
event.stopPropagation();
onDuplicate(report.id);
}}
>
<CopyIcon size={16} className="mr-2" />
Duplicate
</DropdownMenuItem>
<DropdownMenuGroup>
<DropdownMenuItem
className="text-destructive"
@@ -320,6 +332,16 @@ function Component() {
}),
);
const reportDuplicate = useMutation(
trpc.report.duplicate.mutationOptions({
onError: handleErrorToastOptions({}),
onSuccess() {
reportsQuery.refetch();
toast('Report duplicated');
},
}),
);
const updateLayout = useMutation(
trpc.report.updateLayout.mutationOptions({
onError: handleErrorToastOptions({}),
@@ -565,6 +587,9 @@ function Component() {
onDelete={(reportId) => {
reportDeletion.mutate({ reportId });
}}
onDuplicate={(reportId) => {
reportDuplicate.mutate({ reportId });
}}
/>
</div>
))}

View File

@@ -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({