Compare commits

...

4 Commits

Author SHA1 Message Date
Matti Nannt
6a707c48d5 Merge branch 'main' into feat-redirect-url-param 2024-06-03 15:03:01 +02:00
pandeymangg
b48673a863 Merge branch 'main' into feat-redirect-url-param 2024-05-29 17:19:16 +05:30
Dhruwang
c0c35298a7 updated comment 2024-05-16 12:55:33 +05:30
Dhruwang
fa327736f2 feat: redirectUrl url parameter to allow dynamic redirects 2024-05-16 12:46:35 +05:30
9 changed files with 104 additions and 19 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,70 @@
import { MdxImage } from "@/components/MdxImage";
import StepOne from "../pin-protected-surveys/images/StepOne.webp";
import RedirectUrlExample from "./images/RedirectUrlExample.webp";
import StepTwo from "./images/StepTwo.webp";
export const metadata = {
title: "Redirect on survey completion",
description:
"Automatically redirect users to a specified URL upon completing a survey. Enhance user flow and engagement by seamlessly guiding respondents to the next step.",
};
# Redirect on survey completion
Automatically redirect users to a specified URL upon completing a survey. Enhance user flow and engagement by seamlessly guiding respondents to the next step. There are two ways of achieving it:
1. Static Url Redirection
2. Dynamic Url Redirection
## **Staic Url Redirection**
This is usefull when you want to redirect all the survey respondents to the same url.
### **Steps to Set Up Staic Url Redirection**
1. **Open Settings in Survey Editor**: Navigate to your survey in the survey editor where you wish to enable redirection (make sure its a link survey)
2. **Select Response Options**: Find and select **`Response Options`** to access settings related to survey responses.
<MdxImage
src={StepOne}
alt="Choose Response option"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl "
/>
3. **Enable Redirect on completion**: Find the option for **`Redirect on completion`** and activate it. You will be prompted
to enter a url to which respondents would be redirected to.
<MdxImage
src={StepTwo}
alt="Choose Redirect on Completion"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl "
/>
## **Dynamic Url Redirection**
This is usefull when you want to redirect different respondents to different Urls
### **Steps to Set Up Dynamic Url Redirection**
1. **Append redirectUrl param to survey link**: Before sending the survey link to the respondent, append **`redirectUrl=<you-redirect-url-here>`** to the survey link
<MdxImage
src={RedirectUrlExample}
alt="Redirect Url example"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl "
/>
<Note>
In cases where both static and dynamic redirect URLs are configured, dynamic redirect URL would be given a
higher priority
</Note>{" "}
### **Benefits of Redirect on completion**
- **Increased Engagement**: Direct users to relevant content or next actions, such as additional surveys, or promotional offers.
- **Customization and Personalization**: Use dynamic redirection to provide personalized experiences based on user responses, enhancing relevance and engagement.
## **Conclusion**
Implementing redirect on survey completion is a powerful feature that enhances user flow and engagement by guiding respondents to the next step seamlessly. Whether using static or dynamic URL redirection, this capability provides numerous benefits, including improved user experience, increased engagement, and enhanced tracking and analytics. By integrating redirection into your surveys, you can automate workflows, personalize user journeys, and ultimately achieve better outcomes from your survey initiatives.

View File

@@ -91,6 +91,7 @@ export const navigation: Array<NavGroup> = [
{ title: "Verify Email before Survey", href: "/link-surveys/verify-email-before-survey" },
{ title: "PIN Protected Surveys", href: "/link-surveys/pin-protected-surveys" },
{ title: "Partial Submissions", href: "/global/partial-submissions" },
{ title: "Redirect on Completion", href: "/link-surveys/redirect-on-completion" },
],
},
],

View File

@@ -4,7 +4,7 @@ import { useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import { MatchType, testURLmatch } from "@formbricks/lib/utils/testUrlMatch";
import { MatchType, testURLmatch } from "@formbricks/lib/utils/url";
import { TActionClass, TActionClassInput, TActionClassNoCodeConfig } from "@formbricks/types/actionClasses";
import { TSurvey } from "@formbricks/types/surveys";
import { CssSelector, InnerHtmlSelector, PageUrlSelector } from "@formbricks/ui/Actions";

View File

@@ -4,6 +4,7 @@ import { toast } from "react-hot-toast";
import { extractLanguageCodes, getLocalizedValue } from "@formbricks/lib/i18n/utils";
import { checkForEmptyFallBackValue } from "@formbricks/lib/utils/recall";
import { isValidUrl } from "@formbricks/lib/utils/url";
import { ZSegmentFilters } from "@formbricks/types/segment";
import {
TI18nString,
@@ -189,15 +190,6 @@ export const isCardValid = (
);
};
export const isValidUrl = (string: string): boolean => {
try {
new URL(string);
return true;
} catch (e) {
return false;
}
};
// Function to validate question ID and Hidden field Id
export const validateId = (
type: "Hidden field" | "Question",
@@ -227,6 +219,7 @@ export const validateId = (
"hidden",
"verifiedEmail",
"multiLanguage",
"redirectUrl",
];
if (forbiddenIds.includes(field)) {
toast.error(`${type} Id not allowed.`);
@@ -408,11 +401,7 @@ export const isSurveyValid = (
}
// Checking the validity of redirection URLs to ensure they are properly formatted.
if (
survey.redirectUrl &&
!survey.redirectUrl.includes("https://") &&
!survey.redirectUrl.includes("http://")
) {
if (survey.redirectUrl && !isValidUrl(survey.redirectUrl)) {
toast.error("Please enter a valid URL for redirecting respondents.");
return false;
}

View File

@@ -12,7 +12,7 @@ import { useForm } from "react-hook-form";
import { toast } from "react-hot-toast";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { testURLmatch } from "@formbricks/lib/utils/testUrlMatch";
import { testURLmatch } from "@formbricks/lib/utils/url";
import {
TActionClass,
TActionClassInput,

View File

@@ -15,6 +15,7 @@ import { createPerson, getPersonByUserId } from "@formbricks/lib/person/service"
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
import { getResponseBySingleUseId, getResponseCountBySurveyId } from "@formbricks/lib/response/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { isValidUrl } from "@formbricks/lib/utils/url";
import { ZId } from "@formbricks/types/environment";
import { TResponse } from "@formbricks/types/responses";
import { MediaBackground } from "@formbricks/ui/MediaBackground";
@@ -30,6 +31,7 @@ interface LinkSurveyPageProps {
userId?: string;
verify?: string;
lang?: string;
redirectUrl?: string;
};
}
@@ -47,16 +49,24 @@ const Page = async ({ params, searchParams }: LinkSurveyPageProps) => {
if (!validId.success) {
notFound();
}
const survey = await getSurvey(params.surveyId);
const surveyPrisma = await getSurvey(params.surveyId);
const suId = searchParams.suId;
const langParam = searchParams.lang; //can either be language code or alias
const isSingleUseSurvey = survey?.singleUse?.enabled;
const isSingleUseSurveyEncrypted = survey?.singleUse?.isEncrypted;
const redirectUrl =
searchParams.redirectUrl && isValidUrl(searchParams.redirectUrl) ? searchParams.redirectUrl : undefined;
const survey = structuredClone(surveyPrisma);
if (!survey || survey.type !== "link" || survey.status === "draft") {
notFound();
}
const isSingleUseSurveyEncrypted = survey.singleUse?.isEncrypted;
const isSingleUseSurvey = survey.singleUse?.enabled;
if (redirectUrl) {
// if redirectUrl exist in params and if its valid, we overwrite the redirect url of survey
survey.redirectUrl = redirectUrl;
}
const organization = await getOrganizationByEnvironmentId(survey?.environmentId);
if (!organization) {

View File

@@ -18,3 +18,18 @@ export const testURLmatch = (testUrl: string, pageUrlValue: string, pageUrlRule:
throw new Error("Invalid match type");
}
};
export const isValidUrl = (inputUrl: string): boolean => {
try {
// URL object will automatically URL-encode the input
const url = new URL(inputUrl);
// Check if the protocol is HTTPS
if (url.protocol !== "https:") {
return false;
}
return true;
} catch (error) {
return false;
}
};