Feature/duplicate surveys (#268)

* add duplicate to menu

* add duplicate survey API endpoint

---------

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Johannes
2023-05-02 15:33:45 +02:00
committed by GitHub
parent a0acc945b2
commit 478d4e16f8
4 changed files with 117 additions and 4 deletions

View File

@@ -10,11 +10,12 @@ import {
} from "@/components/shared/DropdownMenu";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
import SurveyStatusIndicator from "@/components/shared/SurveyStatusIndicator";
import { deleteSurvey, useSurveys } from "@/lib/surveys/surveys";
import { deleteSurvey, duplicateSurvey, useSurveys } from "@/lib/surveys/surveys";
import { Badge, ErrorComponent } from "@formbricks/ui";
import { PlusIcon } from "@heroicons/react/24/outline";
import {
ComputerDesktopIcon,
DocumentDuplicateIcon,
EllipsisHorizontalIcon,
LinkIcon,
PencilSquareIcon,
@@ -52,6 +53,16 @@ export default function SurveysList({ environmentId }) {
}
};
const duplicateSurveyAndRefresh = async (surveyId) => {
try {
await duplicateSurvey(environmentId, surveyId);
mutateSurveys();
toast.success("Survey duplicated successfully.");
} catch (error) {
toast.error("Failed to duplicate the survey.");
}
};
if (isLoadingSurveys) {
return <LoadingSpinner />;
}
@@ -136,6 +147,24 @@ export default function SurveysList({ environmentId }) {
Edit
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<button
className="flex w-full items-center"
onClick={async () => {
duplicateSurveyAndRefresh(survey.id);
}}>
<DocumentDuplicateIcon className="mr-2 h-4 w-4" />
Duplicate
</button>
</DropdownMenuItem>
{/* <DropdownMenuItem>
<Link
className="flex w-full items-center"
href={`/environments/${environmentId}/surveys/${survey.id}/edit`}>
<ArrowUturnUpIcon className="mr-2 h-4 w-4" />
Copy to Production
</Link>
</DropdownMenuItem> */}
<DropdownMenuItem>
<button
className="flex w-full items-center"

View File

@@ -7,10 +7,10 @@ import { useSurvey } from "@/lib/surveys/surveys";
import type { QuestionSummary } from "@formbricks/types/responses";
import { ErrorComponent } from "@formbricks/ui";
import { useMemo } from "react";
import MultipleChoiceSummary from "./MultipleChoiceSummary";
import OpenTextSummary from "./OpenTextSummary";
import NPSSummary from "./NPSSummary";
import CTASummary from "./CTASummary";
import MultipleChoiceSummary from "./MultipleChoiceSummary";
import NPSSummary from "./NPSSummary";
import OpenTextSummary from "./OpenTextSummary";
import RatingSummary from "./RatingSummary";
export default function SummaryList({ environmentId, surveyId }) {

View File

@@ -103,3 +103,16 @@ export const getSurveyPage = (survey, pageId) => {
}
return page;
};
export const duplicateSurvey = async (environmentId: string, surveyId: string) => {
try {
const res = await fetch(`/api/v1/environments/${environmentId}/surveys/${surveyId}/duplicate`, {
method: "POST",
headers: { "Content-Type": "application/json" },
});
return await res.json();
} catch (error) {
console.error(error);
throw Error(`duplicateSurvey: unable to duplicate survey: ${error.message}`);
}
};

View File

@@ -0,0 +1,71 @@
import { hasEnvironmentAccess } from "@/lib/api/apiHelper";
import { prisma } from "@formbricks/database";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
const surveyId = req.query.surveyId?.toString();
if (environmentId === undefined) {
return res.status(400).json({ message: "Missing environmentId" });
}
if (surveyId === undefined) {
return res.status(400).json({ message: "Missing surveyId" });
}
const hasAccess = await hasEnvironmentAccess(req, res, environmentId);
if (!hasAccess) {
return res.status(403).json({ message: "Not authorized" });
}
// POST
else if (req.method === "POST") {
// duplicate current survey including its triggers
const existingSurvey = await prisma.survey.findFirst({
where: {
id: surveyId,
environmentId,
},
include: {
triggers: true,
},
});
if (!existingSurvey) {
return res.status(404).json({ message: "Survey not found" });
}
// create new survey with the data of the existing survey
const newSurvey = await prisma.survey.create({
data: {
...existingSurvey,
id: undefined, // id is auto-generated
environmentId: undefined, // environmentId is set below
name: `${existingSurvey.name} (copy)`,
createdAt: new Date(),
updatedAt: new Date(),
status: "draft",
questions: JSON.parse(JSON.stringify(existingSurvey.questions)),
thankYouCard: JSON.parse(JSON.stringify(existingSurvey.thankYouCard)),
triggers: {
create: existingSurvey.triggers.map((trigger) => ({
eventClassId: trigger.eventClassId,
})),
},
environment: {
connect: {
id: environmentId,
},
},
},
});
return res.json(newSurvey);
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}