From 6e08a94da76d8931da74032177d536ef75bb560d Mon Sep 17 00:00:00 2001 From: Naitik Kapadia <88614335+KapadiaNaitik@users.noreply.github.com> Date: Mon, 4 Dec 2023 00:57:05 +0530 Subject: [PATCH] feat: Show the number of Responses to Respondents (#1720) Co-authored-by: Johannes --- .../components/dummyUI/templates.ts | 130 +++++------------- .../summary/components/SummaryMetadata.tsx | 2 - .../edit/components/EditWelcomeCard.tsx | 22 +++ .../surveys/components/PreviewSurvey.tsx | 6 +- .../surveys/templates/templates.ts | 2 + .../[environmentId]/in-app/sync/route.ts | 1 - .../s/[surveyId]/components/LinkSurvey.tsx | 3 + apps/web/app/s/[surveyId]/page.tsx | 4 +- .../surveys/src/components/general/Survey.tsx | 6 +- .../src/components/general/SurveyInline.tsx | 2 + .../src/components/general/SurveyModal.tsx | 2 + .../src/components/general/WelcomeCard.tsx | 51 ++++++- packages/surveys/src/types/props.ts | 1 + packages/types/js.ts | 4 +- packages/types/surveys.ts | 1 + packages/ui/Survey/index.tsx | 7 + 16 files changed, 130 insertions(+), 114 deletions(-) diff --git a/apps/formbricks-com/components/dummyUI/templates.ts b/apps/formbricks-com/components/dummyUI/templates.ts index 8c9521db4c..702a4e49a0 100644 --- a/apps/formbricks-com/components/dummyUI/templates.ts +++ b/apps/formbricks-com/components/dummyUI/templates.ts @@ -22,9 +22,9 @@ import { VideoTabletAdjustIcon, } from "@formbricks/ui/icons"; -import { createId } from "@paralleldrive/cuid2"; -import { TTemplate } from "@formbricks/types/templates"; import { TSurveyQuestionType } from "@formbricks/types/surveys"; +import { TTemplate } from "@formbricks/types/templates"; +import { createId } from "@paralleldrive/cuid2"; const thankYouCardDefault = { enabled: true, @@ -32,6 +32,12 @@ const thankYouCardDefault = { subheader: "We appreciate your feedback.", }; +const welcomeCardDefault = { + enabled: true, + timeToFinish: false, + showResponseCount: false, +}; + export const customSurvey: TTemplate = { name: "Start from scratch", description: "Create a survey without template.", @@ -51,10 +57,7 @@ export const customSurvey: TTemplate = { }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -150,10 +153,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -260,10 +260,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -340,10 +337,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -389,10 +383,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -447,10 +438,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -513,10 +501,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -582,10 +567,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -640,10 +622,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -685,10 +664,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -723,10 +699,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -752,10 +725,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -803,10 +773,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -848,10 +815,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -905,10 +869,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -961,10 +922,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1013,10 +971,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1043,10 +998,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1071,10 +1023,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1098,10 +1047,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1142,10 +1088,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1179,10 +1122,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1216,10 +1156,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, @@ -1281,10 +1218,7 @@ export const templates: TTemplate[] = [ }, ], thankYouCard: thankYouCardDefault, - welcomeCard: { - enabled: false, - timeToFinish: false, - }, + welcomeCard: welcomeCardDefault, hiddenFields: { enabled: false, }, diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryMetadata.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryMetadata.tsx index a384e18431..8c7be758c7 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryMetadata.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryMetadata.tsx @@ -73,8 +73,6 @@ export default function SummaryMetadata({ return ttc; }, [responses]); - console.log(ttc); - const totalResponses = responses.length; return ( diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx index f2e7083161..709db8f861 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx @@ -177,6 +177,28 @@ export default function EditWelcomeCard({ + {localSurvey?.type === "link" && ( +
+
+ + updateSurvey({ showResponseCount: !localSurvey.welcomeCard.showResponseCount }) + } + /> +
+
+ +
+ Display number of responses for survey +
+
+
+ )} diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/components/PreviewSurvey.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/components/PreviewSurvey.tsx index fcb5b3c5ea..c22dfb45ea 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/components/PreviewSurvey.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/components/PreviewSurvey.tsx @@ -3,11 +3,12 @@ import Modal from "@/app/(app)/environments/[environmentId]/surveys/components/Modal"; import TabOption from "@/app/(app)/environments/[environmentId]/surveys/components/TabOption"; -import { SurveyInline } from "@formbricks/ui/Survey"; import type { TEnvironment } from "@formbricks/types/environment"; import type { TProduct } from "@formbricks/types/product"; +import { TUploadFileConfig } from "@formbricks/types/storage"; import { TSurvey } from "@formbricks/types/surveys"; import { Button } from "@formbricks/ui/Button"; +import { SurveyInline } from "@formbricks/ui/Survey"; import { ArrowPathRoundedSquareIcon } from "@heroicons/react/24/outline"; import { ArrowsPointingInIcon, @@ -17,7 +18,6 @@ import { } from "@heroicons/react/24/solid"; import { Variants, motion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; -import { TUploadFileConfig } from "@formbricks/types/storage"; type TPreviewType = "modal" | "fullwidth" | "email"; @@ -226,6 +226,7 @@ export default function PreviewSurvey({ isBrandingEnabled={product.linkSurveyBranding} onActiveQuestionChange={setActiveQuestionId} onFileUpload={onFileUpload} + responseCount={42} /> @@ -297,6 +298,7 @@ export default function PreviewSurvey({ onActiveQuestionChange={setActiveQuestionId} isRedirectDisabled={true} onFileUpload={onFileUpload} + responseCount={42} /> diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/templates.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/templates.ts index 589e65b9d9..272826db12 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/templates/templates.ts +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/templates/templates.ts @@ -23,6 +23,7 @@ const welcomeCardDefault: TSurveyWelcomeCard = { headline: "Welcome!", html: "Thanks for providing your feedback - let's go!", timeToFinish: true, + showResponseCount: false, }; export const testTemplate: TTemplate = { @@ -320,6 +321,7 @@ export const testTemplate: TTemplate = { welcomeCard: { enabled: false, timeToFinish: false, + showResponseCount: false, }, hiddenFields: { enabled: false, diff --git a/apps/web/app/api/v1/client/[environmentId]/in-app/sync/route.ts b/apps/web/app/api/v1/client/[environmentId]/in-app/sync/route.ts index 4c8df57090..6df73b4e18 100644 --- a/apps/web/app/api/v1/client/[environmentId]/in-app/sync/route.ts +++ b/apps/web/app/api/v1/client/[environmentId]/in-app/sync/route.ts @@ -46,7 +46,6 @@ export async function GET( getActionClasses(environmentId), getProductByEnvironmentId(environmentId), ]); - if (!product) { throw new Error("Product not found"); } diff --git a/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx b/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx index 3afb3c97fa..2a2288e973 100644 --- a/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx +++ b/apps/web/app/s/[surveyId]/components/LinkSurvey.tsx @@ -25,6 +25,7 @@ interface LinkSurveyProps { singleUseId?: string; singleUseResponse?: TResponse; webAppUrl: string; + responseCount?: number; } export default function LinkSurvey({ @@ -36,6 +37,7 @@ export default function LinkSurvey({ singleUseId, singleUseResponse, webAppUrl, + responseCount, }: LinkSurveyProps) { const responseId = singleUseResponse?.id; const searchParams = useSearchParams(); @@ -187,6 +189,7 @@ export default function LinkSurvey({ activeQuestionId={activeQuestionId} autoFocus={autoFocus} prefillResponseData={prefillResponseData} + responseCount={responseCount} /> diff --git a/apps/web/app/s/[surveyId]/page.tsx b/apps/web/app/s/[surveyId]/page.tsx index 10fa4cca4c..6c9fb08e3e 100644 --- a/apps/web/app/s/[surveyId]/page.tsx +++ b/apps/web/app/s/[surveyId]/page.tsx @@ -15,6 +15,7 @@ import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { getEmailVerificationStatus } from "./lib/helpers"; import { ZId } from "@formbricks/types/environment"; +import { getResponseCountBySurveyId } from "@formbricks/lib/response/service"; interface LinkSurveyPageProps { params: { @@ -166,7 +167,7 @@ export default async function LinkSurveyPage({ params, searchParams }: LinkSurve } const isSurveyPinProtected = Boolean(!!survey && survey.pin); - + const responseCount = await getResponseCountBySurveyId(survey.id); if (isSurveyPinProtected) { return ( ); } diff --git a/packages/surveys/src/components/general/Survey.tsx b/packages/surveys/src/components/general/Survey.tsx index 6a64afbcc8..0fcf576ca6 100644 --- a/packages/surveys/src/components/general/Survey.tsx +++ b/packages/surveys/src/components/general/Survey.tsx @@ -1,4 +1,5 @@ import FormbricksBranding from "@/components/general/FormbricksBranding"; +import ProgressBar from "@/components/general/ProgressBar"; import { AutoCloseWrapper } from "@/components/wrappers/AutoCloseWrapper"; import { evaluateCondition } from "@/lib/logicEvaluator"; import { cn } from "@/lib/utils"; @@ -8,7 +9,6 @@ import { useEffect, useRef, useState } from "preact/hooks"; import QuestionConditional from "./QuestionConditional"; import ThankYouCard from "./ThankYouCard"; import WelcomeCard from "./WelcomeCard"; -import ProgressBar from "@/components/general/ProgressBar"; export function Survey({ survey, @@ -22,6 +22,7 @@ export function Survey({ isRedirectDisabled = false, prefillResponseData, onFileUpload, + responseCount, }: SurveyBaseProps) { const [questionId, setQuestionId] = useState( activeQuestionId || (survey.welcomeCard.enabled ? "start" : survey?.questions[0]?.id) @@ -33,7 +34,6 @@ export function Survey({ const currentQuestion = survey.questions[currentQuestionIndex]; const contentRef = useRef(null); const [ttc, setTtc] = useState({}); - useEffect(() => { if (activeQuestionId === "hidden") return; if (activeQuestionId === "start" && !survey.welcomeCard.enabled) { @@ -131,9 +131,9 @@ export function Survey({ html={survey.welcomeCard.html} fileUrl={survey.welcomeCard.fileUrl} buttonLabel={survey.welcomeCard.buttonLabel} - timeToFinish={survey.welcomeCard.timeToFinish} onSubmit={onSubmit} survey={survey} + responseCount={responseCount} /> ); } else if (questionId === "end" && survey.thankYouCard.enabled) { diff --git a/packages/surveys/src/components/general/SurveyInline.tsx b/packages/surveys/src/components/general/SurveyInline.tsx index e142c909e1..7d2f897c56 100644 --- a/packages/surveys/src/components/general/SurveyInline.tsx +++ b/packages/surveys/src/components/general/SurveyInline.tsx @@ -12,6 +12,7 @@ export function SurveyInline({ prefillResponseData, isRedirectDisabled = false, onFileUpload, + responseCount, }: SurveyBaseProps) { return (
@@ -26,6 +27,7 @@ export function SurveyInline({ prefillResponseData={prefillResponseData} isRedirectDisabled={isRedirectDisabled} onFileUpload={onFileUpload} + responseCount={responseCount} />
); diff --git a/packages/surveys/src/components/general/SurveyModal.tsx b/packages/surveys/src/components/general/SurveyModal.tsx index 93eddefa34..1f760e37e6 100644 --- a/packages/surveys/src/components/general/SurveyModal.tsx +++ b/packages/surveys/src/components/general/SurveyModal.tsx @@ -18,6 +18,7 @@ export function SurveyModal({ onFinished = () => {}, onFileUpload, isRedirectDisabled = false, + responseCount, }: SurveyModalProps) { const [isOpen, setIsOpen] = useState(true); @@ -55,6 +56,7 @@ export function SurveyModal({ }} onFileUpload={onFileUpload} isRedirectDisabled={isRedirectDisabled} + responseCount={responseCount} /> diff --git a/packages/surveys/src/components/general/WelcomeCard.tsx b/packages/surveys/src/components/general/WelcomeCard.tsx index 8eb6f26383..1095ee4dda 100644 --- a/packages/surveys/src/components/general/WelcomeCard.tsx +++ b/packages/surveys/src/components/general/WelcomeCard.tsx @@ -10,9 +10,9 @@ interface WelcomeCardProps { html?: string; fileUrl?: string; buttonLabel?: string; - timeToFinish?: boolean; onSubmit: (data: TResponseData, ttc: TResponseTtc) => void; survey: TSurvey; + responseCount?: number; } const TimerIcon = () => { @@ -32,14 +32,34 @@ const TimerIcon = () => { ); }; +const UsersIcon = () => { + return ( +
+ + + +
+ ); +}; + export default function WelcomeCard({ headline, html, fileUrl, buttonLabel, - timeToFinish, onSubmit, survey, + responseCount, }: WelcomeCardProps) { const calculateTimeToComplete = () => { let idx = calculateElementIdx(survey, 0); @@ -69,6 +89,9 @@ export default function WelcomeCard({ return `${minutes} minutes`; }; + const timeToFinish = survey.welcomeCard.timeToFinish; + const showResponseCount = survey.welcomeCard.showResponseCount; + return (
{fileUrl && ( @@ -93,12 +116,30 @@ export default function WelcomeCard({
Press Enter ↵
- {timeToFinish && ( + + {timeToFinish && !showResponseCount ? (
-

Takes {calculateTimeToComplete()}

+

+ Takes {calculateTimeToComplete()} +

- )} + ) : showResponseCount && !timeToFinish && responseCount && responseCount > 3 ? ( +
+ +

+ {`${responseCount} people responded`} +

+
+ ) : timeToFinish && showResponseCount ? ( +
+ +

+ Takes {calculateTimeToComplete()} + {responseCount && responseCount > 3 ? `⋅ ${responseCount} people responded` : ""} +

+
+ ) : null} ); } diff --git a/packages/surveys/src/types/props.ts b/packages/surveys/src/types/props.ts index e58c9439bf..d196025acf 100644 --- a/packages/surveys/src/types/props.ts +++ b/packages/surveys/src/types/props.ts @@ -15,6 +15,7 @@ export interface SurveyBaseProps { isRedirectDisabled?: boolean; prefillResponseData?: TResponseData; onFileUpload: (file: File, config?: TUploadFileConfig) => Promise; + responseCount?: number; } export interface SurveyInlineProps extends SurveyBaseProps { diff --git a/packages/types/js.ts b/packages/types/js.ts index 847a6f0fba..4049f94be4 100644 --- a/packages/types/js.ts +++ b/packages/types/js.ts @@ -1,8 +1,8 @@ import z from "zod"; -import { ZPerson, ZPersonAttributes, ZPersonClient } from "./people"; -import { ZSurvey } from "./surveys"; import { ZActionClass } from "./actionClasses"; +import { ZPerson, ZPersonAttributes, ZPersonClient } from "./people"; import { ZProduct } from "./product"; +import { ZSurvey } from "./surveys"; const ZSurveyWithTriggers = ZSurvey.extend({ triggers: z.array(ZActionClass).or(z.array(z.string())), diff --git a/packages/types/surveys.ts b/packages/types/surveys.ts index 796c196438..c0af2e707c 100644 --- a/packages/types/surveys.ts +++ b/packages/types/surveys.ts @@ -27,6 +27,7 @@ export const ZSurveyWelcomeCard = z.object({ fileUrl: z.string().optional(), buttonLabel: z.string().optional(), timeToFinish: z.boolean().default(true), + showResponseCount: z.boolean().default(false), }); export const ZSurveyHiddenFields = z.object({ diff --git a/packages/ui/Survey/index.tsx b/packages/ui/Survey/index.tsx index 69c29fadeb..f1db9f6d1c 100644 --- a/packages/ui/Survey/index.tsx +++ b/packages/ui/Survey/index.tsx @@ -20,6 +20,7 @@ interface SurveyProps { autoFocus?: boolean; prefillResponseData?: TResponseData; isRedirectDisabled?: boolean; + responseCount?: number; } interface SurveyModalProps extends SurveyProps { @@ -42,6 +43,7 @@ export const SurveyInline = ({ prefillResponseData, isRedirectDisabled, onFileUpload, + responseCount, }: SurveyProps) => { const containerId = useMemo(() => createContainerId(), []); useEffect(() => { @@ -59,6 +61,7 @@ export const SurveyInline = ({ prefillResponseData, isRedirectDisabled, onFileUpload, + responseCount, }); }, [ activeQuestionId, @@ -74,6 +77,7 @@ export const SurveyInline = ({ prefillResponseData, isRedirectDisabled, onFileUpload, + responseCount, ]); return
; }; @@ -94,6 +98,7 @@ export const SurveyModal = ({ autoFocus, isRedirectDisabled, onFileUpload, + responseCount, }: SurveyModalProps) => { useEffect(() => { renderSurveyModal({ @@ -112,6 +117,7 @@ export const SurveyModal = ({ autoFocus, isRedirectDisabled, onFileUpload, + responseCount, }); }, [ activeQuestionId, @@ -129,6 +135,7 @@ export const SurveyModal = ({ autoFocus, isRedirectDisabled, onFileUpload, + responseCount, ]); return
; };