fix: Tweaked thank you card (#1923)

Co-authored-by: Johannes <johannes@formbricks.com>
Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
This commit is contained in:
Dhruwang Jariwala
2024-01-23 20:56:16 +05:30
committed by GitHub
parent 1fd339e5a0
commit aa63c89a6a
10 changed files with 155 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
import nextMDX from "@next/mdx";
import { withPlausibleProxy } from "next-plausible";
import { recmaPlugins } from "./mdx/recma.mjs";
import { rehypePlugins } from "./mdx/rehype.mjs";
import { remarkPlugins } from "./mdx/remark.mjs";
@@ -170,6 +170,11 @@ const nextConfig = {
destination: "/community",
permanent: true,
},
{
source: "/signup",
destination: "https://app.formbricks.com/auth/signup",
permanent: true,
},
];
},
async rewrites() {

View File

@@ -1,9 +1,11 @@
"use client";
import * as Collapsible from "@radix-ui/react-collapsible";
import { useState } from "react";
import { cn } from "@formbricks/lib/cn";
import { TSurvey } from "@formbricks/types/surveys";
import { Input } from "@formbricks/ui/Input";
import { Label } from "@formbricks/ui/Label";
import QuestionFormInput from "@formbricks/ui/QuestionFormInput";
import { Switch } from "@formbricks/ui/Switch";
@@ -23,6 +25,9 @@ export default function EditThankYouCard({
}: EditThankYouCardProps) {
// const [open, setOpen] = useState(false);
let open = activeQuestionId == "end";
const [showThankYouCardCTA, setshowThankYouCardCTA] = useState<boolean>(
localSurvey.thankYouCard.buttonLabel || localSurvey.thankYouCard.buttonLink ? true : false
);
const setOpen = (e) => {
if (e) {
setActiveQuestionId("end");
@@ -114,6 +119,59 @@ export default function EditThankYouCard({
/>
</div>
</div>
<div className="mt-4">
<div className="flex items-center space-x-1">
<Switch
id="showButton"
checked={showThankYouCardCTA}
onCheckedChange={() => {
if (showThankYouCardCTA) {
updateSurvey({ buttonLabel: undefined, buttonLink: undefined });
} else {
updateSurvey({
buttonLabel: "Create your own Survey",
buttonLink: "https://formbricks.com/signup",
});
}
setshowThankYouCardCTA(!showThankYouCardCTA);
}}
/>
<Label htmlFor="showButton" className="cursor-pointer">
<div className="ml-2">
<h3 className="text-sm font-semibold text-slate-700">Show Button</h3>
<p className="text-xs font-normal text-slate-500">
Send your respondents to a page of your choice.
</p>
</div>
</Label>
</div>
{showThankYouCardCTA && (
<div className="border-1 mt-4 space-y-4 rounded-md border bg-slate-100 p-4">
<div className="space-y-2">
<Label>Button Label</Label>
<Input
id="buttonLabel"
name="buttonLabel"
className="bg-white"
placeholder="Create your own Survey"
value={localSurvey.thankYouCard.buttonLabel}
onChange={(e) => updateSurvey({ buttonLabel: e.target.value })}
/>
</div>
<div className="space-y-2">
<Label>Button Link</Label>
<Input
id="buttonLink"
name="buttonLink"
className="bg-white"
placeholder="https://formbricks.com/signup"
value={localSurvey.thankYouCard.buttonLink}
onChange={(e) => updateSurvey({ buttonLink: e.target.value })}
/>
</div>
</div>
)}
</div>
</form>
</Collapsible.CollapsibleContent>
</Collapsible.Root>

View File

@@ -18,7 +18,7 @@ import { Input } from "@formbricks/ui/Input";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
import { deleteSurveyAction, updateSurveyAction } from "../actions";
import { validateQuestion } from "./Validation";
import { isValidUrl, validateQuestion } from "./Validation";
interface SurveyMenuBarProps {
localSurvey: TSurvey;
@@ -122,6 +122,26 @@ export default function SurveyMenuBar({
return;
}
const { thankYouCard } = localSurvey;
if (thankYouCard.enabled) {
const { buttonLabel, buttonLink } = thankYouCard;
if (buttonLabel && !buttonLink) {
toast.error("Button Link missing on Thank you card.");
return;
}
if (!buttonLabel && buttonLink) {
toast.error("Button Label missing on Thank you card.");
return;
}
if (buttonLink && !isValidUrl(buttonLink)) {
toast.error("Invalid URL on Thank You card.");
return;
}
}
faultyQuestions = [];
for (let index = 0; index < survey.questions.length; index++) {
const question = survey.questions[index];

View File

@@ -37,3 +37,12 @@ const validateQuestion = (question) => {
};
export { validateQuestion };
export const isValidUrl = (string: string): boolean => {
try {
new URL(string);
return true;
} catch (e) {
return false;
}
};

View File

@@ -12,6 +12,8 @@ const thankYouCardDefault = {
enabled: true,
headline: "Thank you!",
subheader: "We appreciate your feedback.",
buttonLabel: "Create your own Survey",
buttonLink: "https://formbricks.com/signup",
};
const hiddenFieldsDefault: TSurveyHiddenFields = {

View File

@@ -488,13 +488,16 @@ export const createSurvey = async (environmentId: string, surveyBody: TSurveyInp
const actionClasses = await getActionClasses(environmentId);
revalidateSurveyByActionClassId(actionClasses, surveyBody.triggers);
}
// TODO: Create with triggers & attributeFilters
delete surveyBody.triggers;
delete surveyBody.attributeFilters;
const data: Omit<TSurveyInput, "triggers" | "attributeFilters"> = {
...surveyBody,
};
if (surveyBody.type === "web" && data.thankYouCard) {
data.thankYouCard.buttonLabel = "";
data.thankYouCard.buttonLink = "";
}
const survey = await prisma.survey.create({
data: {

View File

@@ -217,6 +217,9 @@ export function Survey({
? replaceRecallInfo(survey.thankYouCard.subheader)
: ""
}
buttonLabel={survey.thankYouCard.buttonLabel}
buttonLink={survey.thankYouCard.buttonLink}
imageUrl={survey.thankYouCard.imageUrl}
redirectUrl={survey.redirectUrl}
isRedirectDisabled={isRedirectDisabled}
/>

View File

@@ -1,12 +1,18 @@
import Button from "@/components/buttons/SubmitButton";
import Headline from "@/components/general/Headline";
import QuestionImage from "@/components/general/QuestionImage";
import RedirectCountDown from "@/components/general/RedirectCountdown";
import Subheader from "@/components/general/Subheader";
import { useEffect } from "preact/hooks";
interface ThankYouCardProps {
headline?: string;
subheader?: string;
redirectUrl: string | null;
isRedirectDisabled: boolean;
buttonLabel?: string;
buttonLink?: string;
imageUrl?: string;
}
export default function ThankYouCard({
@@ -14,9 +20,27 @@ export default function ThankYouCard({
subheader,
redirectUrl,
isRedirectDisabled,
buttonLabel,
buttonLink,
imageUrl,
}: ThankYouCardProps) {
useEffect(() => {
if (!buttonLink) return;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Enter") {
window.location.href = buttonLink;
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [buttonLink]);
return (
<div className="text-center">
{imageUrl && <QuestionImage imgUrl={imageUrl} />}
<div className="text-brand flex items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -39,6 +63,19 @@ export default function ThankYouCard({
<Headline alignTextCenter={true} headline={headline} questionId="thankYouCard" />
<Subheader subheader={subheader} questionId="thankYouCard" />
<RedirectCountDown redirectUrl={redirectUrl} isRedirectDisabled={isRedirectDisabled} />
{buttonLabel && (
<div className="mt-6 flex w-full flex-col items-center justify-center space-y-4">
<Button
buttonLabel={buttonLabel}
isLastQuestion={false}
onClick={() => {
if (!buttonLink) return;
window.location.href = buttonLink;
}}
/>
<p class="text-xs">Press Enter </p>
</div>
)}
</div>
</div>
);

View File

@@ -7,6 +7,9 @@ export const ZSurveyThankYouCard = z.object({
enabled: z.boolean(),
headline: z.optional(z.string()),
subheader: z.optional(z.string()),
buttonLabel: z.optional(z.string()),
buttonLink: z.optional(z.string()),
imageUrl: z.string().optional(),
});
export enum TSurveyQuestionType {

View File

@@ -63,7 +63,11 @@ const QuestionFormInput = ({
const fallbackInputRef = useRef<HTMLInputElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const [showImageUploader, setShowImageUploader] = useState<boolean>(
questionId === "end" ? false : !!(question as TSurveyQuestion).imageUrl && type === "headline"
questionId === "end"
? localSurvey.thankYouCard.imageUrl
? true
: false
: !!(question as TSurveyQuestion).imageUrl
);
const [showQuestionSelect, setShowQuestionSelect] = useState(false);
const [showFallbackInput, setShowFallbackInput] = useState(false);
@@ -244,17 +248,21 @@ const QuestionFormInput = ({
<div className="mt-3 w-full">
<Label htmlFor="headline">{type === "headline" ? "Question" : "Description"}</Label>
<div className="mt-2 flex flex-col gap-6 overflow-hidden">
{showImageUploader && (
{showImageUploader && type === "headline" && (
<FileInput
id="question-image"
allowedFileExtensions={["png", "jpeg", "jpg"]}
environmentId={environmentId}
onFileUpload={(url: string[] | undefined) => {
if (updateQuestion && url) {
if (isThankYouCard && updateSurvey && url) {
updateSurvey({ imageUrl: url[0] });
} else if (updateQuestion && url) {
updateQuestion(questionIdx, { imageUrl: url[0] });
}
}}
fileUrl={isThankYouCard ? "" : (question as TSurveyQuestion).imageUrl}
fileUrl={
isThankYouCard ? localSurvey.thankYouCard.imageUrl : (question as TSurveyQuestion).imageUrl
}
/>
)}
<div className="flex items-center space-x-2">