@@ -179,20 +28,18 @@ export default function SummaryDropOffs({ responses, survey, displayCount }: Sum
Views
Drop Offs
- {survey.questions.map((question, i) => (
+ {dropOff.map((quesDropOff) => (
-
{question.headline}
+
{quesDropOff.headline}
- {avgTtc[question.id] !== undefined ? (avgTtc[question.id] / 1000).toFixed(2) + "s" : "N/A"}
-
-
- {dropoffMetrics.viewsCount[i]}
+ {quesDropOff.ttc > 0 ? (quesDropOff.ttc / 1000).toFixed(2) + "s" : "N/A"}
+
{quesDropOff.views}
- {dropoffMetrics.dropoffCount[i]}
- ({Math.round(dropoffMetrics.dropoffPercentage[i])}%)
+ {quesDropOff.dropOffCount}
+ ({Math.round(quesDropOff.dropOffPercentage)}%)
))}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryList.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryList.tsx
index a0bce72259..510467d953 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryList.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryList.tsx
@@ -4,26 +4,9 @@ import ConsentSummary from "@/app/(app)/environments/[environmentId]/surveys/[su
import HiddenFieldsSummary from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/HiddenFieldsSummary";
import { TEnvironment } from "@formbricks/types/environment";
-import { TResponse } from "@formbricks/types/responses";
+import { TSurveySummary } from "@formbricks/types/responses";
import { TSurveyQuestionType } from "@formbricks/types/surveys";
-import type {
- TSurveyCalQuestion,
- TSurveyDateQuestion,
- TSurveyFileUploadQuestion,
- TSurveyPictureSelectionQuestion,
- TSurveyQuestionSummary,
-} from "@formbricks/types/surveys";
-import {
- TSurvey,
- TSurveyCTAQuestion,
- TSurveyConsentQuestion,
- TSurveyMultipleChoiceMultiQuestion,
- TSurveyMultipleChoiceSingleQuestion,
- TSurveyNPSQuestion,
- TSurveyOpenTextQuestion,
- TSurveyQuestion,
- TSurveyRatingQuestion,
-} from "@formbricks/types/surveys";
+import { TSurvey } from "@formbricks/types/surveys";
import EmptySpaceFiller from "@formbricks/ui/EmptySpaceFiller";
import CTASummary from "./CTASummary";
@@ -36,157 +19,103 @@ import PictureChoiceSummary from "./PictureChoiceSummary";
import RatingSummary from "./RatingSummary";
interface SummaryListProps {
+ summary: TSurveySummary["summary"];
+ responseCount: number;
environment: TEnvironment;
survey: TSurvey;
- responses: TResponse[];
- responsesPerPage: number;
}
-export default function SummaryList({ environment, survey, responses, responsesPerPage }: SummaryListProps) {
- const getSummaryData = (): TSurveyQuestionSummary
[] =>
- survey.questions.map((question) => {
- const questionResponses = responses
- .filter((response) => question.id in response.data)
- .map((r) => ({
- id: r.id,
- value: r.data[question.id],
- updatedAt: r.updatedAt,
- person: r.person,
- }));
-
- return {
- question,
- responses: questionResponses,
- };
- });
-
+export default function SummaryList({ summary, environment, responseCount, survey }: SummaryListProps) {
return (
- {survey.type === "web" && responses.length === 0 && !environment.widgetSetupCompleted ? (
+ {survey.type === "web" && responseCount === 0 && !environment.widgetSetupCompleted ? (
- ) : responses.length === 0 ? (
+ ) : responseCount === 0 ? (
) : (
- <>
- {getSummaryData().map((questionSummary) => {
- if (questionSummary.question.type === TSurveyQuestionType.OpenText) {
- return (
-
}
- environmentId={environment.id}
- responsesPerPage={responsesPerPage}
- />
- );
- }
- if (
- questionSummary.question.type === TSurveyQuestionType.MultipleChoiceSingle ||
- questionSummary.question.type === TSurveyQuestionType.MultipleChoiceMulti
- ) {
- return (
-
- }
- environmentId={environment.id}
- surveyType={survey.type}
- responsesPerPage={responsesPerPage}
- />
- );
- }
- if (questionSummary.question.type === TSurveyQuestionType.NPS) {
- return (
- }
- />
- );
- }
- if (questionSummary.question.type === TSurveyQuestionType.CTA) {
- return (
- }
- />
- );
- }
- if (questionSummary.question.type === TSurveyQuestionType.Rating) {
- return (
- }
- />
- );
- }
- if (questionSummary.question.type === TSurveyQuestionType.Consent) {
- return (
- }
- />
- );
- }
- if (questionSummary.question.type === TSurveyQuestionType.PictureSelection) {
- return (
- }
- />
- );
- }
- if (questionSummary.question.type === TSurveyQuestionType.Date) {
- return (
- }
- environmentId={environment.id}
- responsesPerPage={responsesPerPage}
- />
- );
- }
- if (questionSummary.question.type === TSurveyQuestionType.FileUpload) {
- return (
- }
- environmentId={environment.id}
- />
- );
- }
+ summary.map((questionSummary) => {
+ if (questionSummary.type === TSurveyQuestionType.OpenText) {
+ return (
+
+ );
+ }
+ if (
+ questionSummary.type === TSurveyQuestionType.MultipleChoiceSingle ||
+ questionSummary.type === TSurveyQuestionType.MultipleChoiceMulti
+ ) {
+ return (
+
+ );
+ }
+ if (questionSummary.type === TSurveyQuestionType.NPS) {
+ return ;
+ }
+ if (questionSummary.type === TSurveyQuestionType.CTA) {
+ return ;
+ }
+ if (questionSummary.type === TSurveyQuestionType.Rating) {
+ return ;
+ }
+ if (questionSummary.type === TSurveyQuestionType.Consent) {
+ return ;
+ }
+ if (questionSummary.type === TSurveyQuestionType.PictureSelection) {
+ return (
+
+ );
+ }
+ if (questionSummary.type === TSurveyQuestionType.Date) {
+ return (
+
+ );
+ }
+ if (questionSummary.type === TSurveyQuestionType.FileUpload) {
+ return (
+
+ );
+ }
+ if (questionSummary.type === TSurveyQuestionType.Cal) {
+ return (
+
+ );
+ }
+ if (questionSummary.type === "hiddenField") {
+ return (
+
+ );
+ }
- if (questionSummary.question.type === TSurveyQuestionType.Cal) {
- return (
- }
- environmentId={environment.id}
- />
- );
- }
-
- return null;
- })}
-
- {survey.hiddenFields?.enabled &&
- survey.hiddenFields.fieldIds?.map((question) => {
- return (
-
- );
- })}
- >
+ return null;
+ })
)}
);
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 8e1f591eb8..8960c7b5d1 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
@@ -1,18 +1,16 @@
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/solid";
-import { useMemo, useState } from "react";
import { timeSinceConditionally } from "@formbricks/lib/time";
-import { TResponse } from "@formbricks/types/responses";
+import { TSurveySummary } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys";
import { Button } from "@formbricks/ui/Button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
interface SummaryMetadataProps {
- responses: TResponse[];
- showDropOffs: boolean;
- setShowDropOffs: React.Dispatch>;
survey: TSurvey;
- displayCount: number;
+ setShowDropOffs: React.Dispatch>;
+ showDropOffs: boolean;
+ surveySummary: TSurveySummary["meta"];
}
const StatCard = ({ label, percentage, value, tooltipText }) => (
@@ -36,8 +34,8 @@ const StatCard = ({ label, percentage, value, tooltipText }) => (
);
-function formatTime(ttc, totalResponses) {
- const seconds = ttc / (1000 * totalResponses);
+function formatTime(ttc) {
+ const seconds = ttc / 1000;
let formattedValue;
if (seconds >= 60) {
@@ -52,29 +50,21 @@ function formatTime(ttc, totalResponses) {
}
export default function SummaryMetadata({
- responses,
survey,
- displayCount,
setShowDropOffs,
showDropOffs,
+ surveySummary,
}: SummaryMetadataProps) {
- const completedResponsesCount = useMemo(() => responses.filter((r) => r.finished).length, [responses]);
- const [validTtcResponsesCount, setValidResponsesCount] = useState(0);
-
- const ttc = useMemo(() => {
- let validTtcResponsesCountAcc = 0; //stores the count of responses that contains a _total value
- const ttc = responses.reduce((acc, response) => {
- if (response.ttc?._total) {
- validTtcResponsesCountAcc++;
- return acc + response.ttc._total;
- }
- return acc;
- }, 0);
- setValidResponsesCount(validTtcResponsesCountAcc);
- return ttc;
- }, [responses]);
-
- const totalResponses = responses.length;
+ const {
+ completedPercentage,
+ completedResponses,
+ displayCount,
+ dropOffPercentage,
+ dropOffCount,
+ startsPercentage,
+ totalResponses,
+ ttcAverage,
+ } = surveySummary;
return (
@@ -88,28 +78,26 @@ export default function SummaryMetadata({
/>
- : totalResponses}
tooltipText="Number of times the survey has been started."
/>
- : completedResponsesCount}
+ percentage={`${Math.round(completedPercentage)}%`}
+ value={completedResponses === 0 ? - : completedResponses}
tooltipText="Number of times the survey has been completed."
/>
- : totalResponses - completedResponsesCount}
+ percentage={`${Math.round(dropOffPercentage)}%`}
+ value={dropOffCount === 0 ? - : dropOffCount}
tooltipText="Number of times the survey has been started but not completed."
/>
- : `${formatTime(ttc, validTtcResponsesCount)}`
- }
+ value={ttcAverage === 0 ? - : `${formatTime(ttcAverage)}`}
tooltipText="Average time to complete the survey."
/>
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx
index 41279d6f8f..da2963d16f 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryPage.tsx
@@ -1,13 +1,14 @@
"use client";
import { useResponseFilter } from "@/app/(app)/environments/[environmentId]/components/ResponseFilterContext";
+import { getSurveySummaryAction } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
import SurveyResultsTabs from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/SurveyResultsTabs";
import SummaryDropOffs from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs";
import SummaryList from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryList";
import SummaryMetadata from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryMetadata";
import CustomFilter from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/CustomFilter";
import SummaryHeader from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/SummaryHeader";
-import { getFilterResponses } from "@/app/lib/surveys/surveys";
+import { getFormattedFilters } from "@/app/lib/surveys/surveys";
import { useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
@@ -15,7 +16,7 @@ import { checkForRecallInHeadline } from "@formbricks/lib/utils/recall";
import { TEnvironment } from "@formbricks/types/environment";
import { TMembershipRole } from "@formbricks/types/memberships";
import { TProduct } from "@formbricks/types/product";
-import { TResponse, TSurveyPersonAttributes } from "@formbricks/types/responses";
+import { TSurveyPersonAttributes, TSurveySummary } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys";
import { TTag } from "@formbricks/types/tags";
import { TUser } from "@formbricks/types/user";
@@ -27,47 +28,69 @@ interface SummaryPageProps {
environment: TEnvironment;
survey: TSurvey;
surveyId: string;
- responses: TResponse[];
webAppUrl: string;
product: TProduct;
user: TUser;
environmentTags: TTag[];
attributes: TSurveyPersonAttributes;
- displayCount: number;
- responsesPerPage: number;
membershipRole?: TMembershipRole;
+ responseCount: number;
}
const SummaryPage = ({
environment,
survey,
surveyId,
- responses,
webAppUrl,
product,
user,
environmentTags,
attributes,
- displayCount,
- responsesPerPage,
membershipRole,
+ responseCount,
}: SummaryPageProps) => {
const { selectedFilter, dateRange, resetState } = useResponseFilter();
+ const [surveySummary, setSurveySummary] = useState({
+ meta: {
+ completedPercentage: 0,
+ completedResponses: 0,
+ displayCount: 0,
+ dropOffPercentage: 0,
+ dropOffCount: 0,
+ startsPercentage: 0,
+ totalResponses: 0,
+ ttcAverage: 0,
+ },
+ dropOff: [],
+ summary: [],
+ });
const [showDropOffs, setShowDropOffs] = useState(false);
+
+ const filters = useMemo(
+ () => getFormattedFilters(survey, selectedFilter, dateRange),
+ [survey, selectedFilter, dateRange]
+ );
+
+ useEffect(() => {
+ const fetchSurveySummary = async () => {
+ const response = await getSurveySummaryAction(surveyId, filters);
+ setSurveySummary(response);
+ };
+ fetchSurveySummary();
+ }, [filters, surveyId]);
+
const searchParams = useSearchParams();
survey = useMemo(() => {
return checkForRecallInHeadline(survey);
}, [survey]);
+
useEffect(() => {
if (!searchParams?.get("referer")) {
resetState();
}
}, [searchParams, resetState]);
- // get the filtered array when the selected filter value changes
- const filterResponses: TResponse[] = useMemo(() => {
- return getFilterResponses(responses, selectedFilter, survey, dateRange);
- }, [selectedFilter, responses, survey, dateRange]);
+ console.log({ surveySummary });
return (
@@ -86,18 +109,17 @@ const SummaryPage = ({