mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-21 00:58:29 -06:00
fix: applying fixes after doing self review
This commit is contained in:
@@ -42,14 +42,14 @@ export const createChartAction = authenticatedActionClient.schema(ZCreateChartAc
|
||||
"readWrite"
|
||||
);
|
||||
|
||||
const chart = await createChart(
|
||||
const chart = await createChart({
|
||||
projectId,
|
||||
parsedInput.name,
|
||||
parsedInput.type,
|
||||
parsedInput.query,
|
||||
parsedInput.config || {},
|
||||
ctx.user.id
|
||||
);
|
||||
name: parsedInput.name,
|
||||
type: parsedInput.type,
|
||||
query: parsedInput.query,
|
||||
config: parsedInput.config,
|
||||
createdBy: ctx.user.id,
|
||||
});
|
||||
|
||||
ctx.auditLoggingCtx.organizationId = organizationId;
|
||||
ctx.auditLoggingCtx.projectId = projectId;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { validateInputs } from "@/lib/utils/validate";
|
||||
import { TChartType } from "../../types/analysis";
|
||||
|
||||
const selectChart = {
|
||||
export const selectChart = {
|
||||
id: true,
|
||||
name: true,
|
||||
type: true,
|
||||
@@ -16,24 +16,24 @@ const selectChart = {
|
||||
updatedAt: true,
|
||||
} as const;
|
||||
|
||||
export const createChart = async (
|
||||
projectId: string,
|
||||
name: string,
|
||||
type: TChartType,
|
||||
query: TChartQuery,
|
||||
config: TChartConfig,
|
||||
createdBy: string
|
||||
) => {
|
||||
validateInputs([projectId, ZId], [createdBy, ZId]);
|
||||
export const createChart = async (data: {
|
||||
projectId: string;
|
||||
name: string;
|
||||
type: TChartType;
|
||||
query: TChartQuery;
|
||||
config: TChartConfig;
|
||||
createdBy: string;
|
||||
}) => {
|
||||
validateInputs([data.projectId, ZId], [data.createdBy, ZId]);
|
||||
|
||||
return prisma.chart.create({
|
||||
data: {
|
||||
name,
|
||||
type,
|
||||
projectId,
|
||||
query,
|
||||
config: config || {},
|
||||
createdBy,
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
projectId: data.projectId,
|
||||
query: data.query,
|
||||
config: data.config,
|
||||
createdBy: data.createdBy,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -62,10 +62,10 @@ export const updateChart = async (
|
||||
const updatedChart = await tx.chart.update({
|
||||
where: { id: chartId },
|
||||
data: {
|
||||
...(data.name !== undefined && { name: data.name }),
|
||||
...(data.type !== undefined && { type: data.type }),
|
||||
...(data.query !== undefined && { query: data.query }),
|
||||
...(data.config !== undefined && { config: data.config }),
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
query: data.query,
|
||||
config: data.config,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -74,7 +74,7 @@ export const updateChart = async (
|
||||
};
|
||||
|
||||
const getUniqueCopyName = async (baseName: string, projectId: string): Promise<string> => {
|
||||
const stripped = baseName.replace(/\s+\(copy(?:\s+\d+)?\)$/, "");
|
||||
const stripped = baseName.replace(/ \(copy(?: \d+)?\)$/, "");
|
||||
|
||||
const existing = await prisma.chart.findMany({
|
||||
where: {
|
||||
@@ -111,15 +111,13 @@ export const duplicateChart = async (chartId: string, projectId: string, created
|
||||
|
||||
const uniqueName = await getUniqueCopyName(sourceChart.name, projectId);
|
||||
|
||||
return prisma.chart.create({
|
||||
data: {
|
||||
name: uniqueName,
|
||||
type: sourceChart.type,
|
||||
projectId,
|
||||
query: sourceChart.query as object,
|
||||
config: (sourceChart.config as object) || {},
|
||||
createdBy,
|
||||
},
|
||||
return createChart({
|
||||
projectId,
|
||||
name: uniqueName,
|
||||
type: sourceChart.type as TChartType,
|
||||
query: sourceChart.query as TChartQuery,
|
||||
config: (sourceChart.config as TChartConfig) ?? {},
|
||||
createdBy,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
"use server";
|
||||
|
||||
import { z } from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { ZWidgetLayout } from "@formbricks/types/dashboard";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
||||
import { AuthenticatedActionClientCtx } from "@/lib/utils/action-client/types/context";
|
||||
import { checkProjectAccess } from "@/modules/ee/analysis/lib/access";
|
||||
import { withAuditLogging } from "@/modules/ee/audit-logs/lib/handler";
|
||||
import {
|
||||
addChartToDashboard,
|
||||
createDashboard,
|
||||
deleteDashboard,
|
||||
getDashboard,
|
||||
getDashboards,
|
||||
updateDashboard,
|
||||
} from "./lib/dashboards";
|
||||
|
||||
const ZCreateDashboardAction = z.object({
|
||||
environmentId: ZId,
|
||||
@@ -33,13 +39,11 @@ export const createDashboardAction = authenticatedActionClient.schema(ZCreateDas
|
||||
"readWrite"
|
||||
);
|
||||
|
||||
const dashboard = await prisma.dashboard.create({
|
||||
data: {
|
||||
name: parsedInput.name,
|
||||
description: parsedInput.description,
|
||||
projectId,
|
||||
createdBy: ctx.user.id,
|
||||
},
|
||||
const dashboard = await createDashboard({
|
||||
projectId,
|
||||
name: parsedInput.name,
|
||||
description: parsedInput.description,
|
||||
createdBy: ctx.user.id,
|
||||
});
|
||||
|
||||
ctx.auditLoggingCtx.organizationId = organizationId;
|
||||
@@ -75,20 +79,9 @@ export const updateDashboardAction = authenticatedActionClient.schema(ZUpdateDas
|
||||
"readWrite"
|
||||
);
|
||||
|
||||
const dashboard = await prisma.dashboard.findFirst({
|
||||
where: { id: parsedInput.dashboardId, projectId },
|
||||
});
|
||||
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", parsedInput.dashboardId);
|
||||
}
|
||||
|
||||
const updatedDashboard = await prisma.dashboard.update({
|
||||
where: { id: parsedInput.dashboardId },
|
||||
data: {
|
||||
...(parsedInput.name !== undefined && { name: parsedInput.name }),
|
||||
...(parsedInput.description !== undefined && { description: parsedInput.description }),
|
||||
},
|
||||
const { dashboard, updatedDashboard } = await updateDashboard(parsedInput.dashboardId, projectId, {
|
||||
name: parsedInput.name,
|
||||
description: parsedInput.description,
|
||||
});
|
||||
|
||||
ctx.auditLoggingCtx.organizationId = organizationId;
|
||||
@@ -123,17 +116,7 @@ export const deleteDashboardAction = authenticatedActionClient.schema(ZDeleteDas
|
||||
"readWrite"
|
||||
);
|
||||
|
||||
const dashboard = await prisma.dashboard.findFirst({
|
||||
where: { id: parsedInput.dashboardId, projectId },
|
||||
});
|
||||
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", parsedInput.dashboardId);
|
||||
}
|
||||
|
||||
await prisma.dashboard.delete({
|
||||
where: { id: parsedInput.dashboardId },
|
||||
});
|
||||
const dashboard = await deleteDashboard(parsedInput.dashboardId, projectId);
|
||||
|
||||
ctx.auditLoggingCtx.organizationId = organizationId;
|
||||
ctx.auditLoggingCtx.projectId = projectId;
|
||||
@@ -160,18 +143,7 @@ export const getDashboardsAction = authenticatedActionClient
|
||||
}) => {
|
||||
const { projectId } = await checkProjectAccess(ctx.user.id, parsedInput.environmentId, "read");
|
||||
|
||||
return prisma.dashboard.findMany({
|
||||
where: { projectId },
|
||||
orderBy: { createdAt: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
description: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
_count: { select: { widgets: true } },
|
||||
},
|
||||
});
|
||||
return getDashboards(projectId);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -192,31 +164,7 @@ export const getDashboardAction = authenticatedActionClient
|
||||
}) => {
|
||||
const { projectId } = await checkProjectAccess(ctx.user.id, parsedInput.environmentId, "read");
|
||||
|
||||
const dashboard = await prisma.dashboard.findFirst({
|
||||
where: { id: parsedInput.dashboardId, projectId },
|
||||
include: {
|
||||
widgets: {
|
||||
orderBy: { order: "asc" },
|
||||
include: {
|
||||
chart: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
type: true,
|
||||
query: true,
|
||||
config: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", parsedInput.dashboardId);
|
||||
}
|
||||
|
||||
return dashboard;
|
||||
return getDashboard(parsedInput.dashboardId, projectId);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -245,37 +193,13 @@ export const addChartToDashboardAction = authenticatedActionClient.schema(ZAddCh
|
||||
"readWrite"
|
||||
);
|
||||
|
||||
const [chart, dashboard] = await Promise.all([
|
||||
prisma.chart.findFirst({ where: { id: parsedInput.chartId, projectId } }),
|
||||
prisma.dashboard.findFirst({ where: { id: parsedInput.dashboardId, projectId } }),
|
||||
]);
|
||||
|
||||
if (!chart) {
|
||||
throw new ResourceNotFoundError("Chart", parsedInput.chartId);
|
||||
}
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", parsedInput.dashboardId);
|
||||
}
|
||||
|
||||
const widget = await prisma.$transaction(
|
||||
async (tx) => {
|
||||
const maxOrder = await tx.dashboardWidget.aggregate({
|
||||
where: { dashboardId: parsedInput.dashboardId },
|
||||
_max: { order: true },
|
||||
});
|
||||
|
||||
return tx.dashboardWidget.create({
|
||||
data: {
|
||||
dashboardId: parsedInput.dashboardId,
|
||||
chartId: parsedInput.chartId,
|
||||
title: parsedInput.title,
|
||||
layout: parsedInput.layout,
|
||||
order: (maxOrder._max.order ?? -1) + 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
{ isolationLevel: "Serializable" }
|
||||
);
|
||||
const widget = await addChartToDashboard({
|
||||
dashboardId: parsedInput.dashboardId,
|
||||
chartId: parsedInput.chartId,
|
||||
projectId,
|
||||
title: parsedInput.title,
|
||||
layout: parsedInput.layout,
|
||||
});
|
||||
|
||||
ctx.auditLoggingCtx.organizationId = organizationId;
|
||||
ctx.auditLoggingCtx.projectId = projectId;
|
||||
|
||||
163
apps/web/modules/ee/analysis/dashboards/lib/dashboards.ts
Normal file
163
apps/web/modules/ee/analysis/dashboards/lib/dashboards.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import "server-only";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZId } from "@formbricks/types/common";
|
||||
import { TWidgetLayout } from "@formbricks/types/dashboard";
|
||||
import { ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { validateInputs } from "@/lib/utils/validate";
|
||||
import { selectChart } from "@/modules/ee/analysis/charts/lib/charts";
|
||||
|
||||
const selectDashboard = {
|
||||
id: true,
|
||||
name: true,
|
||||
description: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
} as const;
|
||||
|
||||
export const createDashboard = async (data: {
|
||||
projectId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
createdBy: string;
|
||||
}) => {
|
||||
validateInputs([data.projectId, ZId], [data.createdBy, ZId]);
|
||||
|
||||
return prisma.dashboard.create({
|
||||
data: {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
projectId: data.projectId,
|
||||
createdBy: data.createdBy,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const updateDashboard = async (
|
||||
dashboardId: string,
|
||||
projectId: string,
|
||||
data: {
|
||||
name?: string;
|
||||
description?: string | null;
|
||||
}
|
||||
) => {
|
||||
validateInputs([dashboardId, ZId], [projectId, ZId]);
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const dashboard = await tx.dashboard.findFirst({
|
||||
where: { id: dashboardId, projectId },
|
||||
});
|
||||
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", dashboardId);
|
||||
}
|
||||
|
||||
const updatedDashboard = await tx.dashboard.update({
|
||||
where: { id: dashboardId },
|
||||
data: {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
},
|
||||
});
|
||||
|
||||
return { dashboard, updatedDashboard };
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteDashboard = async (dashboardId: string, projectId: string) => {
|
||||
validateInputs([dashboardId, ZId], [projectId, ZId]);
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const dashboard = await tx.dashboard.findFirst({
|
||||
where: { id: dashboardId, projectId },
|
||||
});
|
||||
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", dashboardId);
|
||||
}
|
||||
|
||||
await tx.dashboard.delete({
|
||||
where: { id: dashboardId },
|
||||
});
|
||||
|
||||
return dashboard;
|
||||
});
|
||||
};
|
||||
|
||||
export const getDashboard = async (dashboardId: string, projectId: string) => {
|
||||
validateInputs([dashboardId, ZId], [projectId, ZId]);
|
||||
|
||||
const dashboard = await prisma.dashboard.findFirst({
|
||||
where: { id: dashboardId, projectId },
|
||||
include: {
|
||||
widgets: {
|
||||
orderBy: { order: "asc" },
|
||||
include: {
|
||||
chart: {
|
||||
select: selectChart,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", dashboardId);
|
||||
}
|
||||
|
||||
return dashboard;
|
||||
};
|
||||
|
||||
export const getDashboards = async (projectId: string) => {
|
||||
validateInputs([projectId, ZId]);
|
||||
|
||||
return prisma.dashboard.findMany({
|
||||
where: { projectId },
|
||||
orderBy: { createdAt: "desc" },
|
||||
select: {
|
||||
...selectDashboard,
|
||||
_count: { select: { widgets: true } },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const addChartToDashboard = async (data: {
|
||||
dashboardId: string;
|
||||
chartId: string;
|
||||
projectId: string;
|
||||
title?: string;
|
||||
layout: TWidgetLayout;
|
||||
}) => {
|
||||
validateInputs([data.dashboardId, ZId], [data.chartId, ZId], [data.projectId, ZId]);
|
||||
|
||||
return prisma.$transaction(
|
||||
async (tx) => {
|
||||
const [chart, dashboard] = await Promise.all([
|
||||
tx.chart.findFirst({ where: { id: data.chartId, projectId: data.projectId } }),
|
||||
tx.dashboard.findFirst({ where: { id: data.dashboardId, projectId: data.projectId } }),
|
||||
]);
|
||||
|
||||
if (!chart) {
|
||||
throw new ResourceNotFoundError("Chart", data.chartId);
|
||||
}
|
||||
if (!dashboard) {
|
||||
throw new ResourceNotFoundError("Dashboard", data.dashboardId);
|
||||
}
|
||||
|
||||
const maxOrder = await tx.dashboardWidget.aggregate({
|
||||
where: { dashboardId: data.dashboardId },
|
||||
_max: { order: true },
|
||||
});
|
||||
|
||||
return tx.dashboardWidget.create({
|
||||
data: {
|
||||
dashboardId: data.dashboardId,
|
||||
chartId: data.chartId,
|
||||
title: data.title,
|
||||
layout: data.layout,
|
||||
order: (maxOrder._max.order ?? -1) + 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
{ isolationLevel: "Serializable" }
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user