feat: Ttc on welcome card (#1461)

Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
This commit is contained in:
Dhruwang Jariwala
2023-11-10 22:44:33 +05:30
committed by GitHub
parent 11ede2e517
commit ac8cf987d3
8 changed files with 89 additions and 27 deletions

View File

@@ -157,7 +157,7 @@ export default function EditWelcomeCard({
</div>
</div>
</div>
{/* <div className="mt-8 flex items-center">
<div className="mt-8 flex items-center">
<div className="mr-2">
<Switch
id="timeToFinish"
@@ -176,7 +176,7 @@ export default function EditWelcomeCard({
Display an estimate of completion time for survey
</div>
</div>
</div> */}
</div>
</form>
</Collapsible.CollapsibleContent>
</Collapsible.Root>

View File

@@ -18,7 +18,7 @@ const welcomeCardDefault: TSurveyWelcomeCard = {
enabled: false,
headline: "Welcome!",
html: "Thanks for providing your feedback - let's go!",
timeToFinish: false,
timeToFinish: true,
};
export const templates: TTemplate[] = [

View File

@@ -1,6 +1,7 @@
import { TSurveyWithTriggers } from "@formbricks/types/js";
import { useEffect, useState } from "preact/hooks";
import Progress from "./Progress";
import { calculateElementIdx } from "../lib/utils";
interface ProgressBarProps {
survey: TSurveyWithTriggers;
@@ -13,6 +14,7 @@ const PROGRESS_INCREMENT = 0.1;
export default function ProgressBar({ survey, questionId, brandColor }: ProgressBarProps) {
const [progress, setProgress] = useState(0); // [0, 1]
const [prevQuestionIdx, setPrevQuestionIdx] = useState(0); // [0, survey.questions.length
const [prevQuestionId, setPrevQuestionId] = useState(""); // [0, survey.questions.length
useEffect(() => {
// calculate progress
@@ -20,28 +22,10 @@ export default function ProgressBar({ survey, questionId, brandColor }: Progress
function calculateProgress(questionId: string, survey: TSurveyWithTriggers, progress: number) {
if (survey.questions.length === 0) return 0;
if (questionId === "end") return 1;
let currentQustionIdx = survey.questions.findIndex((e) => e.id === questionId);
if (progress > 0 && currentQustionIdx === prevQuestionIdx) return progress;
if (progress > 0 && questionId === prevQuestionId) return progress;
if (currentQustionIdx === -1) currentQustionIdx = 0;
const currentQuestion = survey.questions[currentQustionIdx];
const surveyLength = survey.questions.length;
const middleIdx = Math.floor(surveyLength / 2);
const possibleNextQuestions = currentQuestion.logic?.map((l) => l.destination) || [];
const getLastQuestionIndex = () => {
const lastQuestion = survey.questions
.filter((q) => possibleNextQuestions.includes(q.id))
.sort((a, b) => survey.questions.indexOf(a) - survey.questions.indexOf(b))
.pop();
return survey.questions.findIndex((e) => e.id === lastQuestion?.id);
};
let elementIdx = currentQustionIdx || 0.5;
const lastprevQuestionIdx = getLastQuestionIndex();
if (lastprevQuestionIdx > 0) elementIdx = Math.min(middleIdx, lastprevQuestionIdx - 1);
if (possibleNextQuestions.includes("end")) elementIdx = middleIdx;
const elementIdx = calculateElementIdx(survey, currentQustionIdx);
const newProgress = elementIdx / survey.questions.length;
@@ -57,7 +41,7 @@ export default function ProgressBar({ survey, questionId, brandColor }: Progress
} else if (newProgress <= progress && progress + PROGRESS_INCREMENT <= 1) {
updatedProgress = progress + PROGRESS_INCREMENT;
}
setPrevQuestionId(questionId);
setPrevQuestionIdx(currentQustionIdx);
return updatedProgress;
}

View File

@@ -131,6 +131,7 @@ export function Survey({
timeToFinish={survey.welcomeCard.timeToFinish}
brandColor={brandColor}
onSubmit={onSubmit}
survey={survey}
/>
);
} else if (questionId === "end" && survey.thankYouCard.enabled) {

View File

@@ -1,6 +1,8 @@
import Headline from "./Headline";
import HtmlBody from "./HtmlBody";
import SubmitButton from "./SubmitButton";
import { calculateElementIdx } from "../lib/utils";
import { TSurveyWithTriggers } from "@formbricks/types/js";
interface WelcomeCardProps {
headline?: string;
@@ -10,8 +12,26 @@ interface WelcomeCardProps {
timeToFinish?: boolean;
brandColor: string;
onSubmit: (data: { [x: string]: any }) => void;
survey: TSurveyWithTriggers;
}
const TimerIcon = () => {
return (
<div className="mr-1">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="bi bi-stopwatch"
viewBox="0 0 16 16">
<path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5V5.6z" />
<path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64a.715.715 0 0 1 .012-.013l.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354a.512.512 0 0 1-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5zM8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3z" />
</svg>
</div>
);
};
export default function WelcomeCard({
headline,
html,
@@ -20,7 +40,36 @@ export default function WelcomeCard({
timeToFinish,
brandColor,
onSubmit,
survey,
}: WelcomeCardProps) {
const calculateTimeToComplete = () => {
let idx = calculateElementIdx(survey, 0);
if (idx === 0.5) {
idx = 1;
}
const timeInSeconds = (survey.questions.length / idx) * 15; //15 seconds per question.
if (timeInSeconds > 360) {
// If it's more than 6 minutes
return "6+ minutes";
}
// Calculate minutes, if there are any seconds left, add a minute
const minutes = Math.floor(timeInSeconds / 60);
const remainingSeconds = timeInSeconds % 60;
if (remainingSeconds > 0) {
// If there are any seconds left, we'll need to round up to the next minute
if (minutes === 0) {
// If less than 1 minute, return 'less than 1 minute'
return "less than 1 minute";
} else {
// If more than 1 minute, return 'less than X minutes', where X is minutes + 1
return `less than ${minutes + 1} minutes`;
}
}
// If there are no remaining seconds, just return the number of minutes
return `${minutes} minutes`;
};
return (
<div>
{fileUrl && (
@@ -46,7 +95,12 @@ export default function WelcomeCard({
<div className="flex items-center text-xs text-slate-600">Press Enter </div>
</div>
</div>
{timeToFinish && <></>}
{timeToFinish && (
<div className="item-center mt-4 flex text-slate-500">
<TimerIcon />
<p className="text-xs">Takes {calculateTimeToComplete()}</p>
</div>
)}
</div>
);
}

View File

@@ -47,7 +47,6 @@ export function evaluateCondition(logic: TSurveyLogic, responseValue: any): bool
(Array.isArray(responseValue) && responseValue.length === 0) ||
responseValue === "" ||
responseValue === null ||
responseValue === undefined ||
responseValue === "dismissed"
);
default:

View File

@@ -1,3 +1,5 @@
import { TSurveyWithTriggers } from "@formbricks/types/js";
export const cn = (...classes: string[]) => {
return classes.filter(Boolean).join(" ");
};
@@ -45,3 +47,25 @@ export const shuffleQuestions = (array: any[], shuffleOption: string) => {
return arrayCopy;
};
export const calculateElementIdx = (survey: TSurveyWithTriggers, currentQustionIdx: number): number => {
const currentQuestion = survey.questions[currentQustionIdx];
const surveyLength = survey.questions.length;
const middleIdx = Math.floor(surveyLength / 2);
const possibleNextQuestions = currentQuestion?.logic?.map((l) => l.destination) || [];
const getLastQuestionIndex = () => {
const lastQuestion = survey.questions
.filter((q) => possibleNextQuestions.includes(q.id))
.sort((a, b) => survey.questions.indexOf(a) - survey.questions.indexOf(b))
.pop();
return survey.questions.findIndex((e) => e.id === lastQuestion?.id);
};
let elementIdx = currentQustionIdx || 0.5;
const lastprevQuestionIdx = getLastQuestionIndex();
if (lastprevQuestionIdx > 0) elementIdx = Math.min(middleIdx, lastprevQuestionIdx - 1);
if (possibleNextQuestions.includes("end")) elementIdx = middleIdx;
return elementIdx;
};

View File

@@ -25,7 +25,7 @@ export const ZSurveyWelcomeCard = z.object({
html: z.string().optional(),
fileUrl: z.string().optional(),
buttonLabel: z.string().optional(),
timeToFinish: z.boolean().default(false),
timeToFinish: z.boolean().default(true),
});
export const ZSurveyHiddenFields = z.object({