From b6c0dbf5d38598ad5bc401d536fc30d6f7b6abaa Mon Sep 17 00:00:00 2001 From: Pradumn Kumar Date: Mon, 7 Aug 2023 18:52:37 +0530 Subject: [PATCH] Close survey after x responses now needs to be set to a higher number than the number of current responses (#606) * fix: fixes close survey on x response issue * feat: updates * chore: don't update _count * chore: optimizations * fix: fixes issue with not being able to enter a lower value at all * update toast message * add response count to toast * only count completed responses --------- Co-authored-by: Johannes Co-authored-by: Matthias Nannt --- .../[surveyId]/edit/ResponseOptionsCard.tsx | 34 +++++++++++++++---- .../surveys/[surveyId]/edit/SurveyEditor.tsx | 2 +- .../surveys/[surveyId]/edit/SurveyMenuBar.tsx | 11 ++++++ apps/web/lib/surveys/surveys.ts | 4 +-- .../surveys/[surveyId]/index.ts | 5 +++ packages/types/surveys.ts | 1 + 6 files changed, 47 insertions(+), 10 deletions(-) diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/ResponseOptionsCard.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/ResponseOptionsCard.tsx index 5d54f3142d..586e4d8b6a 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/ResponseOptionsCard.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/ResponseOptionsCard.tsx @@ -5,6 +5,7 @@ import { DatePicker, Input, Label, Switch } from "@formbricks/ui"; import { CheckCircleIcon } from "@heroicons/react/24/solid"; import * as Collapsible from "@radix-ui/react-collapsible"; import { useEffect, useState } from "react"; +import toast from "react-hot-toast"; interface ResponseOptionsCardProps { localSurvey: Survey; @@ -117,13 +118,27 @@ export default function ResponseOptionsCard({ localSurvey, setLocalSurvey }: Res } }; - const handleInputResponse = (e: any) => { - let value = parseInt(e.target.value); - if (value < 1) value = 1; - const updatedSurvey: Survey = { ...localSurvey, autoComplete: value }; + const handleInputResponse = (e) => { + const updatedSurvey: Survey = { ...localSurvey, autoComplete: parseInt(e.target.value) }; setLocalSurvey(updatedSurvey); }; + const handleInputResponseBlur = (e) => { + if (parseInt(e.target.value) === 0) { + toast.error("Response limit can't be set to 0"); + return; + } + + const inputResponses = localSurvey?._count?.responses || 0; + + if (parseInt(e.target.value) <= inputResponses) { + toast.error( + `Response limit needs to exceed number of received responses (${localSurvey?._count?.responses}).` + ); + return; + } + }; + return ( handleInputResponse(e)} - className="ml-2 mr-2 inline w-16 bg-white text-center text-sm" + onChange={handleInputResponse} + onBlur={handleInputResponseBlur} + className="ml-2 mr-2 inline w-20 bg-white text-center text-sm" /> completed responses.

diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyEditor.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyEditor.tsx index cb23359749..1777c2ff04 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyEditor.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyEditor.tsx @@ -23,7 +23,7 @@ export default function SurveyEditor({ environmentId, surveyId }: SurveyEditorPr const [activeQuestionId, setActiveQuestionId] = useState(null); const [localSurvey, setLocalSurvey] = useState(); const [invalidQuestions, setInvalidQuestions] = useState(null); - const { survey, isLoadingSurvey, isErrorSurvey } = useSurvey(environmentId, surveyId); + const { survey, isLoadingSurvey, isErrorSurvey } = useSurvey(environmentId, surveyId, true); const { product, isLoadingProduct, isErrorProduct } = useProduct(environmentId); const { environment, isLoadingEnvironment, isErrorEnvironment } = useEnvironment(environmentId); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyMenuBar.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyMenuBar.tsx index c3d98cb065..8b924f8a32 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyMenuBar.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/SurveyMenuBar.tsx @@ -106,6 +106,17 @@ export default function SurveyMenuBar({ return false; } + /* + Check whether the count for autocomplete responses is not less + than the current count of accepted response and also it is not set to 0 + */ + if ( + (survey.autoComplete && survey._count?.responses && survey._count.responses >= survey.autoComplete) || + survey?.autoComplete === 0 + ) { + return false; + } + return true; }; diff --git a/apps/web/lib/surveys/surveys.ts b/apps/web/lib/surveys/surveys.ts index c6b01195e6..b04bf024b0 100644 --- a/apps/web/lib/surveys/surveys.ts +++ b/apps/web/lib/surveys/surveys.ts @@ -27,9 +27,9 @@ export const useSurveys = (environmentId: string) => { }; }; -export const useSurvey = (environmentId: string, id: string) => { +export const useSurvey = (environmentId: string, id: string, analytics?: boolean) => { const { data, error, mutate, isLoading } = useSWR( - `/api/v1/environments/${environmentId}/surveys/${id}`, + `/api/v1/environments/${environmentId}/surveys/${id}${analytics ? "?analytics=true" : ""}`, fetcher ); diff --git a/apps/web/pages/api/v1/environments/[environmentId]/surveys/[surveyId]/index.ts b/apps/web/pages/api/v1/environments/[environmentId]/surveys/[surveyId]/index.ts index 3f8fc301a6..aeb36a6ae8 100644 --- a/apps/web/pages/api/v1/environments/[environmentId]/surveys/[surveyId]/index.ts +++ b/apps/web/pages/api/v1/environments/[environmentId]/surveys/[surveyId]/index.ts @@ -9,6 +9,8 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) const surveyId = req.query.surveyId?.toString(); + const analytics = req.query.analytics?.toString() === "true"; + if (environmentId === undefined) { return res.status(400).json({ message: "Missing environmentId" }); } @@ -31,6 +33,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) include: { triggers: true, attributeFilters: true, + _count: analytics ? { select: { responses: { where: { finished: true } } } } : false, }, }); @@ -83,6 +86,8 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse) const body = { ...req.body }; delete body.updatedAt; + // preventing issue with unknowingly updating analytics + delete body._count; // delete unused fields for link surveys if (body.type === "link") { diff --git a/packages/types/surveys.ts b/packages/types/surveys.ts index 826da7138b..7150973a52 100644 --- a/packages/types/surveys.ts +++ b/packages/types/surveys.ts @@ -33,6 +33,7 @@ export interface Survey { autoComplete: number | null; surveyClosedMessage: SurveyClosedMessage | null; closeOnDate: Date | null; + _count: { responses: number | null } | null; } export interface AttributeFilter {