Files
stats/packages/trpc/src/routers/report.ts
Carl-Gerhard Lindesvärd 84fd5ce22f fix: metric chart total count
2025-11-12 23:15:20 +01:00

305 lines
7.9 KiB
TypeScript

import { z } from 'zod';
import { db, getReportById, getReportsByDashboardId } from '@openpanel/db';
import { zReportInput } from '@openpanel/validation';
import { getProjectAccess } from '../access';
import { TRPCAccessError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
export const reportRouter = createTRPCRouter({
list: protectedProcedure
.input(
z.object({
dashboardId: z.string(),
projectId: z.string(),
}),
)
.query(async ({ input: { dashboardId, projectId }, ctx }) => {
return getReportsByDashboardId(dashboardId);
}),
create: protectedProcedure
.input(
z.object({
report: zReportInput.omit({ projectId: true }),
dashboardId: z.string(),
}),
)
.mutation(async ({ input: { report, dashboardId }, ctx }) => {
const dashboard = await db.dashboard.findUniqueOrThrow({
where: {
id: dashboardId,
},
});
const access = await getProjectAccess({
userId: ctx.session.userId,
projectId: dashboard.projectId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this project');
}
return db.report.create({
data: {
projectId: dashboard.projectId,
dashboardId,
name: report.name,
events: report.events,
interval: report.interval,
breakdowns: report.breakdowns,
chartType: report.chartType,
lineType: report.lineType,
range: report.range === 'custom' ? '30d' : report.range,
formula: report.formula,
previous: report.previous ?? false,
unit: report.unit,
criteria: report.criteria,
metric: report.metric === 'count' ? 'sum' : report.metric,
funnelGroup: report.funnelGroup,
funnelWindow: report.funnelWindow,
},
});
}),
update: protectedProcedure
.input(
z.object({
reportId: z.string(),
report: zReportInput.omit({ projectId: true }),
}),
)
.mutation(async ({ input: { report, reportId }, ctx }) => {
const dbReport = await db.report.findUniqueOrThrow({
where: {
id: reportId,
},
});
const access = await getProjectAccess({
userId: ctx.session.userId,
projectId: dbReport.projectId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this project');
}
return db.report.update({
where: {
id: reportId,
},
data: {
name: report.name,
events: report.events,
interval: report.interval,
breakdowns: report.breakdowns,
chartType: report.chartType,
lineType: report.lineType,
range: report.range === 'custom' ? '30d' : report.range,
formula: report.formula,
previous: report.previous ?? false,
unit: report.unit,
criteria: report.criteria,
metric: report.metric === 'count' ? 'sum' : report.metric,
funnelGroup: report.funnelGroup,
funnelWindow: report.funnelWindow,
},
});
}),
delete: 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.delete({
where: {
id: reportId,
},
});
}),
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({
reportId: z.string(),
}),
)
.query(async ({ input: { reportId }, ctx }) => {
return getReportById(reportId);
}),
updateLayout: protectedProcedure
.input(
z.object({
reportId: z.string(),
layout: z.object({
x: z.number(),
y: z.number(),
w: z.number(),
h: z.number(),
minW: z.number().optional(),
minH: z.number().optional(),
maxW: z.number().optional(),
maxH: z.number().optional(),
}),
}),
)
.mutation(async ({ input: { reportId, layout }, 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');
}
// Upsert the layout (create if doesn't exist, update if it does)
return db.reportLayout.upsert({
where: {
reportId: reportId,
},
create: {
reportId: reportId,
x: layout.x,
y: layout.y,
w: layout.w,
h: layout.h,
minW: layout.minW,
minH: layout.minH,
maxW: layout.maxW,
maxH: layout.maxH,
},
update: {
x: layout.x,
y: layout.y,
w: layout.w,
h: layout.h,
minW: layout.minW,
minH: layout.minH,
maxW: layout.maxW,
maxH: layout.maxH,
},
});
}),
getLayouts: protectedProcedure
.input(
z.object({
dashboardId: z.string(),
projectId: z.string(),
}),
)
.query(async ({ input: { dashboardId, projectId }, ctx }) => {
const access = await getProjectAccess({
userId: ctx.session.userId,
projectId: projectId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this project');
}
return db.reportLayout.findMany({
where: {
report: {
dashboardId: dashboardId,
},
},
include: {
report: true,
},
});
}),
resetLayout: protectedProcedure
.input(
z.object({
dashboardId: z.string(),
projectId: z.string(),
}),
)
.mutation(async ({ input: { dashboardId, projectId }, ctx }) => {
const access = await getProjectAccess({
userId: ctx.session.userId,
projectId: projectId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this project');
}
// Delete all layout data for reports in this dashboard
return db.reportLayout.deleteMany({
where: {
report: {
dashboardId: dashboardId,
},
},
});
}),
});