mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-05 00:49:57 -06:00
feat: Added recall highlighting to summary header (#2672)
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
committed by
GitHub
parent
d53ceaaaac
commit
291f628415
@@ -2,7 +2,8 @@ import Link from "next/link";
|
||||
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TSurveyQuestionSummaryAddress } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryAddress } from "@formbricks/types/surveys";
|
||||
import { AddressResponse } from "@formbricks/ui/AddressResponse";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
|
||||
@@ -11,12 +12,23 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
interface AddressSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryAddress;
|
||||
environmentId: string;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const AddressSummary = ({ questionSummary, environmentId }: AddressSummaryProps) => {
|
||||
export const AddressSummary = ({
|
||||
questionSummary,
|
||||
environmentId,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: AddressSummaryProps) => {
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="">
|
||||
<div className="grid h-10 grid-cols-4 items-center border-y border-slate-200 bg-slate-100 text-sm font-bold text-slate-600">
|
||||
<div className="pl-4 md:pl-6">User</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { InboxIcon } from "lucide-react";
|
||||
|
||||
import { TSurveyQuestionSummaryCta } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryCta } from "@formbricks/types/surveys";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
@@ -8,14 +9,18 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
interface CTASummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryCta;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const CTASummary = ({ questionSummary }: CTASummaryProps) => {
|
||||
export const CTASummary = ({ questionSummary, survey, attributeClasses }: CTASummaryProps) => {
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader
|
||||
survey={survey}
|
||||
questionSummary={questionSummary}
|
||||
showResponses={false}
|
||||
attributeClasses={attributeClasses}
|
||||
insights={
|
||||
<>
|
||||
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
|
||||
|
||||
import { TSurveyQuestionSummaryCal } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryCal } from "@formbricks/types/surveys";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
@@ -8,12 +9,18 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
interface CalSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryCal;
|
||||
environmentId: string;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const CalSummary = ({ questionSummary }: CalSummaryProps) => {
|
||||
export const CalSummary = ({ questionSummary, survey, attributeClasses }: CalSummaryProps) => {
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="space-y-5 px-4 pb-6 pt-4 text-sm md:px-6 md:text-base">
|
||||
<div>
|
||||
<div className="text flex justify-between px-2 pb-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { TSurveyQuestionSummaryConsent } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryConsent } from "@formbricks/types/surveys";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
@@ -6,12 +7,18 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
interface ConsentSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryConsent;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const ConsentSummary = ({ questionSummary }: ConsentSummaryProps) => {
|
||||
export const ConsentSummary = ({ questionSummary, survey, attributeClasses }: ConsentSummaryProps) => {
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="space-y-5 px-4 pb-6 pt-4 text-sm md:px-6 md:text-base">
|
||||
<div>
|
||||
<div className="text flex justify-between px-2 pb-2">
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useState } from "react";
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { formatDateWithOrdinal } from "@formbricks/lib/utils/datetime";
|
||||
import { TSurveyQuestionSummaryDate } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryDate } from "@formbricks/types/surveys";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
|
||||
@@ -13,9 +14,16 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
interface DateQuestionSummary {
|
||||
questionSummary: TSurveyQuestionSummaryDate;
|
||||
environmentId: string;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const DateQuestionSummary = ({ questionSummary, environmentId }: DateQuestionSummary) => {
|
||||
export const DateQuestionSummary = ({
|
||||
questionSummary,
|
||||
environmentId,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: DateQuestionSummary) => {
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
@@ -27,7 +35,11 @@ export const DateQuestionSummary = ({ questionSummary, environmentId }: DateQues
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="">
|
||||
<div className="grid h-10 grid-cols-4 items-center border-y border-slate-200 bg-slate-100 text-sm font-bold text-slate-600">
|
||||
<div className="pl-4 md:pl-6">User</div>
|
||||
|
||||
@@ -5,7 +5,8 @@ import { useState } from "react";
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { getOriginalFileNameFromUrl } from "@formbricks/lib/storage/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TSurveyQuestionSummaryFileUpload } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryFileUpload } from "@formbricks/types/surveys";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
|
||||
@@ -14,9 +15,16 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
interface FileUploadSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryFileUpload;
|
||||
environmentId: string;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const FileUploadSummary = ({ questionSummary, environmentId }: FileUploadSummaryProps) => {
|
||||
export const FileUploadSummary = ({
|
||||
questionSummary,
|
||||
environmentId,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: FileUploadSummaryProps) => {
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
@@ -28,7 +36,11 @@ export const FileUploadSummary = ({ questionSummary, environmentId }: FileUpload
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="">
|
||||
<div className="grid h-10 grid-cols-4 items-center border-y border-slate-200 bg-slate-100 text-sm font-bold text-slate-600">
|
||||
<div className="pl-4 md:pl-6">User</div>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import { TSurveyQuestionSummaryMatrix } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryMatrix } from "@formbricks/types/surveys";
|
||||
import { TooltipRenderer } from "@formbricks/ui/Tooltip";
|
||||
|
||||
import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
interface MatrixQuestionSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryMatrix;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const MatrixQuestionSummary = ({ questionSummary }: MatrixQuestionSummaryProps) => {
|
||||
export const MatrixQuestionSummary = ({
|
||||
questionSummary,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: MatrixQuestionSummaryProps) => {
|
||||
const getOpacityLevel = (percentage: number): string => {
|
||||
const parsedPercentage = percentage;
|
||||
const opacity = parsedPercentage * 0.75 + 15;
|
||||
@@ -27,7 +34,11 @@ export const MatrixQuestionSummary = ({ questionSummary }: MatrixQuestionSummary
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="overflow-x-auto p-6">
|
||||
{/* Summary Table */}
|
||||
<table className="mx-auto border-collapse cursor-default text-left">
|
||||
|
||||
@@ -2,7 +2,8 @@ import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { TSurveyQuestionSummaryMultipleChoice, TSurveyType } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryMultipleChoice, TSurveyType } from "@formbricks/types/surveys";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
@@ -14,12 +15,16 @@ interface MultipleChoiceSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryMultipleChoice;
|
||||
environmentId: string;
|
||||
surveyType: TSurveyType;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const MultipleChoiceSummary = ({
|
||||
questionSummary,
|
||||
environmentId,
|
||||
surveyType,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: MultipleChoiceSummaryProps) => {
|
||||
const [visibleOtherResponses, setVisibleOtherResponses] = useState(10);
|
||||
|
||||
@@ -45,7 +50,11 @@ export const MultipleChoiceSummary = ({
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="space-y-5 px-4 pb-6 pt-4 text-sm md:px-6 md:text-base">
|
||||
{results.map((result, resultsIdx) => (
|
||||
<div key={result.value}>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { TSurveyQuestionSummaryNps } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryNps } from "@formbricks/types/surveys";
|
||||
import { HalfCircle, ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
@@ -6,12 +7,18 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
interface NPSSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryNps;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const NPSSummary = ({ questionSummary }: NPSSummaryProps) => {
|
||||
export const NPSSummary = ({ questionSummary, survey, attributeClasses }: NPSSummaryProps) => {
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="space-y-5 px-4 pb-6 pt-4 text-sm md:px-6 md:text-base">
|
||||
{["promoters", "passives", "detractors"].map((group) => (
|
||||
<div key={group}>
|
||||
|
||||
@@ -3,7 +3,8 @@ import { useState } from "react";
|
||||
|
||||
import { getPersonIdentifier } from "@formbricks/lib/person/utils";
|
||||
import { timeSince } from "@formbricks/lib/time";
|
||||
import { TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys";
|
||||
import { PersonAvatar } from "@formbricks/ui/Avatars";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
|
||||
@@ -12,9 +13,16 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
interface OpenTextSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryOpenText;
|
||||
environmentId: string;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const OpenTextSummary = ({ questionSummary, environmentId }: OpenTextSummaryProps) => {
|
||||
export const OpenTextSummary = ({
|
||||
questionSummary,
|
||||
environmentId,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: OpenTextSummaryProps) => {
|
||||
const [visibleResponses, setVisibleResponses] = useState(10);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
@@ -26,7 +34,11 @@ export const OpenTextSummary = ({ questionSummary, environmentId }: OpenTextSumm
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="">
|
||||
<div className="grid h-10 grid-cols-4 items-center border-y border-slate-200 bg-slate-100 text-sm font-bold text-slate-600">
|
||||
<div className="pl-4 md:pl-6">User</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Image from "next/image";
|
||||
|
||||
import { TSurveyQuestionSummaryPictureSelection } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryPictureSelection } from "@formbricks/types/surveys";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
|
||||
import { convertFloatToNDecimal } from "../lib/utils";
|
||||
@@ -8,14 +9,24 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
interface PictureChoiceSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryPictureSelection;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const PictureChoiceSummary = ({ questionSummary }: PictureChoiceSummaryProps) => {
|
||||
export const PictureChoiceSummary = ({
|
||||
questionSummary,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: PictureChoiceSummaryProps) => {
|
||||
const results = questionSummary.choices.sort((a, b) => b.count - a.count);
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="space-y-5 px-4 pb-6 pt-4 text-sm md:px-6 md:text-base">
|
||||
{results.map((result) => (
|
||||
<div key={result.id}>
|
||||
|
||||
@@ -1,23 +1,55 @@
|
||||
import { questionTypes } from "@/app/lib/questions";
|
||||
import { InboxIcon } from "lucide-react";
|
||||
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { TSurveyQuestionSummary } from "@formbricks/types/surveys";
|
||||
import { recallToHeadline } from "@formbricks/lib/utils/recall";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummary } from "@formbricks/types/surveys";
|
||||
|
||||
interface HeadProps {
|
||||
questionSummary: TSurveyQuestionSummary;
|
||||
showResponses?: boolean;
|
||||
insights?: JSX.Element;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const QuestionSummaryHeader = ({ questionSummary, insights, showResponses = true }: HeadProps) => {
|
||||
export const QuestionSummaryHeader = ({
|
||||
questionSummary,
|
||||
insights,
|
||||
showResponses = true,
|
||||
survey,
|
||||
attributeClasses,
|
||||
}: HeadProps) => {
|
||||
const questionType = questionTypes.find((type) => type.id === questionSummary.question.type);
|
||||
|
||||
// formats the text to highlight specific parts of the text with slashes
|
||||
const formatTextWithSlashes = (text: string): (string | JSX.Element)[] => {
|
||||
const regex = /\/(.*?)\\/g;
|
||||
const parts = text.split(regex);
|
||||
|
||||
return parts.map((part, index) => {
|
||||
// Check if the part was inside slashes
|
||||
if (index % 2 !== 0) {
|
||||
return (
|
||||
<span key={index} className="mx-1 rounded-md bg-slate-100 p-1 px-2 text-lg">
|
||||
@{part}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return part;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2 px-4 pb-5 pt-6 md:px-6">
|
||||
<div className={"align-center flex justify-between gap-4 "}>
|
||||
<h3 className="pb-1 text-lg font-semibold text-slate-900 md:text-xl">
|
||||
{getLocalizedValue(questionSummary.question.headline, "default")}
|
||||
{formatTextWithSlashes(
|
||||
recallToHeadline(questionSummary.question.headline, survey, true, "default", attributeClasses)[
|
||||
"default"
|
||||
]
|
||||
)}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex space-x-2 text-xs font-semibold text-slate-600 md:text-sm">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils";
|
||||
|
||||
import { TSurveyQuestionSummaryRating } from "@formbricks/types/surveys";
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TSurvey, TSurveyQuestionSummaryRating } from "@formbricks/types/surveys";
|
||||
import { ProgressBar } from "@formbricks/ui/ProgressBar";
|
||||
import { RatingResponse } from "@formbricks/ui/RatingResponse";
|
||||
|
||||
@@ -8,12 +9,18 @@ import { QuestionSummaryHeader } from "./QuestionSummaryHeader";
|
||||
|
||||
interface RatingSummaryProps {
|
||||
questionSummary: TSurveyQuestionSummaryRating;
|
||||
survey: TSurvey;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const RatingSummary = ({ questionSummary }: RatingSummaryProps) => {
|
||||
export const RatingSummary = ({ questionSummary, survey, attributeClasses }: RatingSummaryProps) => {
|
||||
return (
|
||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<QuestionSummaryHeader questionSummary={questionSummary} />
|
||||
<QuestionSummaryHeader
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
<div className="space-y-5 px-4 pb-6 pt-4 text-sm md:px-6 md:text-base">
|
||||
{questionSummary.choices.map((result) => (
|
||||
<div key={result.rating}>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { OpenTextSummary } from "@/app/(app)/environments/[environmentId]/survey
|
||||
import { PictureChoiceSummary } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/PictureChoiceSummary";
|
||||
import { RatingSummary } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/RatingSummary";
|
||||
|
||||
import { TAttributeClass } from "@formbricks/types/attributeClasses";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TSurveyQuestionType } from "@formbricks/types/surveys";
|
||||
@@ -28,6 +29,7 @@ interface SummaryListProps {
|
||||
survey: TSurvey;
|
||||
fetchingSummary: boolean;
|
||||
totalResponseCount: number;
|
||||
attributeClasses: TAttributeClass[];
|
||||
}
|
||||
|
||||
export const SummaryList = ({
|
||||
@@ -37,6 +39,7 @@ export const SummaryList = ({
|
||||
survey,
|
||||
fetchingSummary,
|
||||
totalResponseCount,
|
||||
attributeClasses,
|
||||
}: SummaryListProps) => {
|
||||
return (
|
||||
<div className="mt-10 space-y-8">
|
||||
@@ -61,6 +64,8 @@ export const SummaryList = ({
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
environmentId={environment.id}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -74,24 +79,59 @@ export const SummaryList = ({
|
||||
questionSummary={questionSummary}
|
||||
environmentId={environment.id}
|
||||
surveyType={survey.type}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.NPS) {
|
||||
return <NPSSummary key={questionSummary.question.id} questionSummary={questionSummary} />;
|
||||
return (
|
||||
<NPSSummary
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.CTA) {
|
||||
return <CTASummary key={questionSummary.question.id} questionSummary={questionSummary} />;
|
||||
return (
|
||||
<CTASummary
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.Rating) {
|
||||
return <RatingSummary key={questionSummary.question.id} questionSummary={questionSummary} />;
|
||||
return (
|
||||
<RatingSummary
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.Consent) {
|
||||
return <ConsentSummary key={questionSummary.question.id} questionSummary={questionSummary} />;
|
||||
return (
|
||||
<ConsentSummary
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.PictureSelection) {
|
||||
return (
|
||||
<PictureChoiceSummary key={questionSummary.question.id} questionSummary={questionSummary} />
|
||||
<PictureChoiceSummary
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.Date) {
|
||||
@@ -100,6 +140,8 @@ export const SummaryList = ({
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
environmentId={environment.id}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -109,6 +151,8 @@ export const SummaryList = ({
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
environmentId={environment.id}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -118,12 +162,19 @@ export const SummaryList = ({
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
environmentId={environment.id}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.Matrix) {
|
||||
return (
|
||||
<MatrixQuestionSummary key={questionSummary.question.id} questionSummary={questionSummary} />
|
||||
<MatrixQuestionSummary
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (questionSummary.type === TSurveyQuestionType.Address) {
|
||||
@@ -132,6 +183,8 @@ export const SummaryList = ({
|
||||
key={questionSummary.question.id}
|
||||
questionSummary={questionSummary}
|
||||
environmentId={environment.id}
|
||||
survey={survey}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ export const SummaryPage = ({
|
||||
environment={environment}
|
||||
fetchingSummary={isFetchingSummary}
|
||||
totalResponseCount={totalResponseCount}
|
||||
attributeClasses={attributeClasses}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -4,14 +4,12 @@ import { headers } from "next/headers";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { sendResponseFinishedEmail } from "@formbricks/email";
|
||||
import { getAttributeClasses } from "@formbricks/lib/attributeClass/service";
|
||||
import { INTERNAL_SECRET } from "@formbricks/lib/constants";
|
||||
import { getIntegrations } from "@formbricks/lib/integration/service";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getResponseCountBySurveyId } from "@formbricks/lib/response/service";
|
||||
import { getSurvey, updateSurvey } from "@formbricks/lib/survey/service";
|
||||
import { convertDatesInObject } from "@formbricks/lib/time";
|
||||
import { replaceHeadlineRecall } from "@formbricks/lib/utils/recall";
|
||||
import { ZPipelineInput } from "@formbricks/types/pipelines";
|
||||
import { TUserNotificationSettings } from "@formbricks/types/user";
|
||||
|
||||
@@ -39,7 +37,6 @@ export const POST = async (request: Request) => {
|
||||
|
||||
const { environmentId, surveyId, event, response } = inputValidation.data;
|
||||
const product = await getProductByEnvironmentId(environmentId);
|
||||
const attributeClasses = await getAttributeClasses(environmentId);
|
||||
if (!product) return;
|
||||
|
||||
// get all webhooks of this environment where event in triggers
|
||||
@@ -108,7 +105,7 @@ export const POST = async (request: Request) => {
|
||||
getIntegrations(environmentId),
|
||||
getSurvey(surveyId),
|
||||
]);
|
||||
const survey = surveyData ? replaceHeadlineRecall(surveyData, "default", attributeClasses) : undefined;
|
||||
const survey = surveyData ?? undefined;
|
||||
|
||||
if (integrations.length > 0 && survey) {
|
||||
handleIntegrations(integrations, inputValidation.data, survey);
|
||||
|
||||
@@ -25,7 +25,6 @@ import { TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
|
||||
import { getAttributes } from "../attribute/service";
|
||||
import { getAttributeClasses } from "../attributeClass/service";
|
||||
import { cache } from "../cache";
|
||||
import { ITEMS_PER_PAGE, WEBAPP_URL } from "../constants";
|
||||
import { displayCache } from "../display/cache";
|
||||
@@ -37,7 +36,6 @@ import { putFile } from "../storage/service";
|
||||
import { getSurvey } from "../survey/service";
|
||||
import { captureTelemetry } from "../telemetry";
|
||||
import { convertToCsv, convertToXlsxBuffer } from "../utils/fileConversion";
|
||||
import { replaceHeadlineRecall } from "../utils/recall";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { responseCache } from "./cache";
|
||||
import {
|
||||
@@ -563,7 +561,6 @@ export const getSurveySummary = (
|
||||
if (!survey) {
|
||||
throw new ResourceNotFoundError("Survey", surveyId);
|
||||
}
|
||||
const attributeClasses = await getAttributeClasses(survey.environmentId);
|
||||
|
||||
const batchSize = 3000;
|
||||
const responseCount = await getResponseCountBySurveyId(surveyId, filterCriteria);
|
||||
@@ -582,11 +579,7 @@ export const getSurveySummary = (
|
||||
|
||||
const dropOff = getSurveySummaryDropOff(survey, responses, displayCount);
|
||||
const meta = getSurveySummaryMeta(responses, displayCount);
|
||||
const questionWiseSummary = getQuestionWiseSummary(
|
||||
replaceHeadlineRecall(survey, "default", attributeClasses),
|
||||
responses,
|
||||
dropOff
|
||||
);
|
||||
const questionWiseSummary = getQuestionWiseSummary(survey, responses, dropOff);
|
||||
|
||||
return { meta, dropOff, summary: questionWiseSummary };
|
||||
} catch (error) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { TResponse } from "@formbricks/types/responses";
|
||||
import { TSurvey, TSurveyQuestion, TSurveyQuestionType } from "@formbricks/types/surveys";
|
||||
|
||||
import { getLocalizedValue } from "./i18n/utils";
|
||||
import { parseRecallInfo } from "./utils/recall";
|
||||
|
||||
// function to convert response value of type string | number | string[] or Record<string, string> to string | string[]
|
||||
export const convertResponseValue = (
|
||||
@@ -41,12 +42,11 @@ export const getQuestionResponseMapping = (
|
||||
response: string | string[];
|
||||
type: TSurveyQuestionType;
|
||||
}[] = [];
|
||||
|
||||
for (const question of survey.questions) {
|
||||
const answer = response.data[question.id];
|
||||
|
||||
questionResponseMapping.push({
|
||||
question: getLocalizedValue(question.headline, "default"),
|
||||
question: parseRecallInfo(getLocalizedValue(question.headline, "default"), {}, response.data),
|
||||
response: convertResponseValue(answer, question),
|
||||
type: question.type,
|
||||
});
|
||||
|
||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@@ -21109,4 +21109,4 @@ packages:
|
||||
|
||||
/zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
dev: false
|
||||
dev: false
|
||||
Reference in New Issue
Block a user