mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-21 13:40:31 -06:00
Compare commits
5 Commits
4.3.3
...
release/v3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cb3881cd0 | ||
|
|
ec78038caa | ||
|
|
908614b4e2 | ||
|
|
c584901337 | ||
|
|
087699fb57 |
@@ -5,6 +5,7 @@ import { ShareSurveyModal } from "@/app/(app)/environments/[environmentId]/surve
|
||||
import { SurveyStatusDropdown } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/SurveyStatusDropdown";
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { EditPublicSurveyAlertDialog } from "@/modules/survey/components/edit-public-survey-alert-dialog";
|
||||
import { useSingleUseId } from "@/modules/survey/hooks/useSingleUseId";
|
||||
import { copySurveyToOtherEnvironmentAction } from "@/modules/survey/list/actions";
|
||||
import { Badge } from "@/modules/ui/components/badge";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
@@ -12,7 +13,7 @@ import { IconBar } from "@/modules/ui/components/iconbar";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { BellRing, Eye, SquarePenIcon } from "lucide-react";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
@@ -48,17 +49,16 @@ export const SurveyAnalysisCTA = ({
|
||||
isFormbricksCloud,
|
||||
}: SurveyAnalysisCTAProps) => {
|
||||
const { t } = useTranslate();
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [modalState, setModalState] = useState<ModalState>({
|
||||
start: searchParams.get("share") === "true",
|
||||
share: false,
|
||||
});
|
||||
|
||||
const surveyUrl = useMemo(() => `${publicDomain}/s/${survey.id}`, [survey.id, publicDomain]);
|
||||
const { refreshSingleUseId } = useSingleUseId(survey);
|
||||
|
||||
const widgetSetupCompleted = survey.type === "app" && environment.appSetupCompleted;
|
||||
|
||||
@@ -102,9 +102,18 @@ export const SurveyAnalysisCTA = ({
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const getPreviewUrl = () => {
|
||||
const separator = surveyUrl.includes("?") ? "&" : "?";
|
||||
return `${surveyUrl}${separator}preview=true`;
|
||||
const getPreviewUrl = async () => {
|
||||
const surveyUrl = new URL(`${publicDomain}/s/${survey.id}`);
|
||||
|
||||
if (survey.singleUse?.enabled) {
|
||||
const newId = await refreshSingleUseId();
|
||||
if (newId) {
|
||||
surveyUrl.searchParams.set("suId", newId);
|
||||
}
|
||||
}
|
||||
|
||||
surveyUrl.searchParams.set("preview", "true");
|
||||
return surveyUrl.toString();
|
||||
};
|
||||
|
||||
const [isCautionDialogOpen, setIsCautionDialogOpen] = useState(false);
|
||||
@@ -119,7 +128,10 @@ export const SurveyAnalysisCTA = ({
|
||||
{
|
||||
icon: Eye,
|
||||
tooltip: t("common.preview"),
|
||||
onClick: () => window.open(getPreviewUrl(), "_blank"),
|
||||
onClick: async () => {
|
||||
const previewUrl = await getPreviewUrl();
|
||||
window.open(previewUrl, "_blank");
|
||||
},
|
||||
isVisible: survey.type === "link",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,14 +4,13 @@ import { updateSingleUseLinksAction } from "@/app/(app)/environments/[environmen
|
||||
import { DisableLinkModal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/disable-link-modal";
|
||||
import { DocumentationLinks } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/documentation-links";
|
||||
import { ShareSurveyLink } from "@/modules/analysis/components/ShareSurveyLink";
|
||||
import { getSurveyUrl } from "@/modules/analysis/utils";
|
||||
import { generateSingleUseIdsAction } from "@/modules/survey/list/actions";
|
||||
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/modules/ui/components/alert";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CirclePlayIcon } from "lucide-react";
|
||||
import { CirclePlayIcon, CopyIcon } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -33,6 +32,7 @@ export const AnonymousLinksTab = ({
|
||||
setSurveyUrl,
|
||||
locale,
|
||||
}: AnonymousLinksTabProps) => {
|
||||
const surveyUrlWithCustomSuid = `${surveyUrl}?suId=CUSTOM-ID`;
|
||||
const router = useRouter();
|
||||
const { t } = useTranslate();
|
||||
|
||||
@@ -173,11 +173,9 @@ export const AnonymousLinksTab = ({
|
||||
count,
|
||||
});
|
||||
|
||||
const baseSurveyUrl = getSurveyUrl(survey, publicDomain, "default");
|
||||
|
||||
if (!!response?.data?.length) {
|
||||
const singleUseIds = response.data;
|
||||
const surveyLinks = singleUseIds.map((singleUseId) => `${baseSurveyUrl}?suId=${singleUseId}`);
|
||||
const surveyLinks = singleUseIds.map((singleUseId) => `${surveyUrl}?suId=${singleUseId}`);
|
||||
|
||||
// Create content with just the links
|
||||
const csvContent = surveyLinks.join("\n");
|
||||
@@ -258,14 +256,36 @@ export const AnonymousLinksTab = ({
|
||||
/>
|
||||
|
||||
{!singleUseEncryption ? (
|
||||
<Alert variant="info" size="default">
|
||||
<AlertTitle>
|
||||
{t("environments.surveys.share.anonymous_links.custom_single_use_id_title")}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
{t("environments.surveys.share.anonymous_links.custom_single_use_id_description")}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<div className="flex w-full flex-col gap-4">
|
||||
<Alert variant="info" size="default">
|
||||
<AlertTitle>
|
||||
{t("environments.surveys.share.anonymous_links.custom_single_use_id_title")}
|
||||
</AlertTitle>
|
||||
|
||||
<AlertDescription>
|
||||
{t("environments.surveys.share.anonymous_links.custom_single_use_id_description")}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<div className="grid w-full grid-cols-6 items-center gap-2">
|
||||
<div className="col-span-5 truncate rounded-md border border-slate-200 px-2 py-1">
|
||||
<span className="truncate text-sm text-slate-900">{surveyUrlWithCustomSuid}</span>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(surveyUrlWithCustomSuid);
|
||||
|
||||
toast.success(t("common.copied_to_clipboard"));
|
||||
}}
|
||||
className="col-span-1 gap-1 text-sm">
|
||||
{t("common.copy")}
|
||||
|
||||
<CopyIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{singleUseEncryption && (
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { responses } from "@/app/lib/api/response";
|
||||
import { CRON_SECRET } from "@/lib/constants";
|
||||
import { CRON_SECRET, SMTP_HOST } from "@/lib/constants";
|
||||
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
||||
import { sendNoLiveSurveyNotificationEmail, sendWeeklySummaryNotificationEmail } from "@/modules/email";
|
||||
import { headers } from "next/headers";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { getNotificationResponse } from "./lib/notificationResponse";
|
||||
import { getOrganizationIds } from "./lib/organization";
|
||||
import { getProjectsByOrganizationId } from "./lib/project";
|
||||
@@ -16,6 +17,11 @@ export const POST = async (): Promise<Response> => {
|
||||
return responses.notAuthenticatedResponse();
|
||||
}
|
||||
|
||||
if (!SMTP_HOST) {
|
||||
logger.info("SMTP_HOST is not configured, skipping weekly summary email");
|
||||
return responses.successResponse({}, true);
|
||||
}
|
||||
|
||||
const emailSendingPromises: Promise<void>[] = [];
|
||||
|
||||
// Fetch all organization IDs
|
||||
|
||||
@@ -4,6 +4,7 @@ import { toast } from "react-hot-toast";
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { getSurveyUrl } from "../../utils";
|
||||
|
||||
vi.mock("react-hot-toast", () => ({
|
||||
toast: {
|
||||
@@ -11,6 +12,24 @@ vi.mock("react-hot-toast", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock the useSingleUseId hook
|
||||
vi.mock("@/modules/survey/hooks/useSingleUseId", () => ({
|
||||
useSingleUseId: vi.fn(() => ({
|
||||
singleUseId: "test-single-use-id",
|
||||
refreshSingleUseId: vi.fn().mockResolvedValue("test-single-use-id"),
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock the survey utils
|
||||
vi.mock("../../utils", () => ({
|
||||
getSurveyUrl: vi.fn((survey, publicDomain, language) => {
|
||||
if (language && language !== "en") {
|
||||
return `${publicDomain}/s/${survey.id}?lang=${language}`;
|
||||
}
|
||||
return `${publicDomain}/s/${survey.id}`;
|
||||
}),
|
||||
}));
|
||||
|
||||
const survey: TSurvey = {
|
||||
id: "survey-id",
|
||||
name: "Test Survey",
|
||||
@@ -161,7 +180,7 @@ describe("ShareSurveyLink", () => {
|
||||
expect(toast.success).toHaveBeenCalledWith("common.copied_to_clipboard");
|
||||
});
|
||||
|
||||
test("opens the preview link in a new tab when preview button is clicked (no query params)", () => {
|
||||
test("opens the preview link in a new tab when preview button is clicked (no query params)", async () => {
|
||||
render(
|
||||
<ShareSurveyLink
|
||||
survey={survey}
|
||||
@@ -175,10 +194,13 @@ describe("ShareSurveyLink", () => {
|
||||
const previewButton = screen.getByLabelText("environments.surveys.preview_survey_in_a_new_tab");
|
||||
fireEvent.click(previewButton);
|
||||
|
||||
// Wait for the async function to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(global.open).toHaveBeenCalledWith(`${surveyUrl}?preview=true`, "_blank");
|
||||
});
|
||||
|
||||
test("opens the preview link in a new tab when preview button is clicked (with query params)", () => {
|
||||
test("opens the preview link in a new tab when preview button is clicked (with query params)", async () => {
|
||||
const surveyWithParamsUrl = `${publicDomain}/s/survey-id?foo=bar`;
|
||||
render(
|
||||
<ShareSurveyLink
|
||||
@@ -193,6 +215,9 @@ describe("ShareSurveyLink", () => {
|
||||
const previewButton = screen.getByLabelText("environments.surveys.preview_survey_in_a_new_tab");
|
||||
fireEvent.click(previewButton);
|
||||
|
||||
// Wait for the async function to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(global.open).toHaveBeenCalledWith(`${surveyWithParamsUrl}&preview=true`, "_blank");
|
||||
});
|
||||
|
||||
@@ -215,7 +240,9 @@ describe("ShareSurveyLink", () => {
|
||||
});
|
||||
|
||||
test("updates the survey URL when the language is changed", () => {
|
||||
const { rerender } = render(
|
||||
const mockGetSurveyUrl = vi.mocked(getSurveyUrl);
|
||||
|
||||
render(
|
||||
<ShareSurveyLink
|
||||
survey={survey}
|
||||
publicDomain={publicDomain}
|
||||
@@ -231,16 +258,7 @@ describe("ShareSurveyLink", () => {
|
||||
const germanOption = screen.getByText("German");
|
||||
fireEvent.click(germanOption);
|
||||
|
||||
rerender(
|
||||
<ShareSurveyLink
|
||||
survey={survey}
|
||||
publicDomain={publicDomain}
|
||||
surveyUrl={surveyUrl}
|
||||
setSurveyUrl={setSurveyUrl}
|
||||
locale={locale}
|
||||
/>
|
||||
);
|
||||
expect(setSurveyUrl).toHaveBeenCalled();
|
||||
expect(surveyUrl).toContain("lang=de");
|
||||
expect(mockGetSurveyUrl).toHaveBeenCalledWith(survey, publicDomain, "de");
|
||||
expect(setSurveyUrl).toHaveBeenCalledWith(`${publicDomain}/s/${survey.id}?lang=de`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useSingleUseId } from "@/modules/survey/hooks/useSingleUseId";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Copy, SquareArrowOutUpRight } from "lucide-react";
|
||||
@@ -32,6 +33,22 @@ export const ShareSurveyLink = ({
|
||||
setSurveyUrl(url);
|
||||
};
|
||||
|
||||
const { refreshSingleUseId } = useSingleUseId(survey);
|
||||
|
||||
const getPreviewUrl = async () => {
|
||||
const previewUrl = new URL(surveyUrl);
|
||||
|
||||
if (survey.singleUse?.enabled) {
|
||||
const newId = await refreshSingleUseId();
|
||||
if (newId) {
|
||||
previewUrl.searchParams.set("suId", newId);
|
||||
}
|
||||
}
|
||||
|
||||
previewUrl.searchParams.set("preview", "true");
|
||||
return previewUrl.toString();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={"flex max-w-full flex-col items-center justify-center gap-2 md:flex-row"}>
|
||||
<SurveyLinkDisplay surveyUrl={surveyUrl} key={surveyUrl} />
|
||||
@@ -53,14 +70,9 @@ export const ShareSurveyLink = ({
|
||||
title={t("environments.surveys.preview_survey_in_a_new_tab")}
|
||||
aria-label={t("environments.surveys.preview_survey_in_a_new_tab")}
|
||||
disabled={!surveyUrl}
|
||||
onClick={() => {
|
||||
let previewUrl = surveyUrl;
|
||||
if (previewUrl.includes("?")) {
|
||||
previewUrl += "&preview=true";
|
||||
} else {
|
||||
previewUrl += "?preview=true";
|
||||
}
|
||||
window.open(previewUrl, "_blank");
|
||||
onClick={async () => {
|
||||
const url = await getPreviewUrl();
|
||||
window.open(url, "_blank");
|
||||
}}>
|
||||
{t("common.preview")}
|
||||
<SquareArrowOutUpRight />
|
||||
|
||||
@@ -7,12 +7,10 @@ import { DatePicker } from "@/modules/ui/components/date-picker";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { Slider } from "@/modules/ui/components/slider";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowUpRight, CheckIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { KeyboardEventHandler, useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
@@ -47,12 +45,6 @@ export const ResponseOptionsCard = ({
|
||||
subheading: t("environments.surveys.edit.survey_completed_subheading"),
|
||||
});
|
||||
|
||||
const [singleUseMessage, setSingleUseMessage] = useState({
|
||||
heading: t("environments.surveys.edit.survey_already_answered_heading"),
|
||||
subheading: t("environments.surveys.edit.survey_already_answered_subheading"),
|
||||
});
|
||||
|
||||
const [singleUseEncryption, setSingleUseEncryption] = useState(true);
|
||||
const [runOnDate, setRunOnDate] = useState<Date | null>(null);
|
||||
const [closeOnDate, setCloseOnDate] = useState<Date | null>(null);
|
||||
const [recaptchaThreshold, setRecaptchaThreshold] = useState<number>(localSurvey.recaptcha?.threshold ?? 0);
|
||||
@@ -163,53 +155,6 @@ export const ResponseOptionsCard = ({
|
||||
setLocalSurvey({ ...localSurvey, surveyClosedMessage: message });
|
||||
};
|
||||
|
||||
const handleSingleUseSurveyToggle = () => {
|
||||
if (!localSurvey.singleUse?.enabled) {
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
singleUse: { enabled: true, ...singleUseMessage, isEncrypted: singleUseEncryption },
|
||||
});
|
||||
} else {
|
||||
setLocalSurvey({ ...localSurvey, singleUse: { enabled: false, isEncrypted: false } });
|
||||
}
|
||||
};
|
||||
|
||||
const handleSingleUseSurveyMessageChange = ({
|
||||
heading,
|
||||
subheading,
|
||||
}: {
|
||||
heading?: string;
|
||||
subheading?: string;
|
||||
}) => {
|
||||
const message = {
|
||||
heading: heading ?? singleUseMessage.heading,
|
||||
subheading: subheading ?? singleUseMessage.subheading,
|
||||
};
|
||||
|
||||
const localSurveySingleUseEnabled = localSurvey.singleUse?.enabled ?? false;
|
||||
setSingleUseMessage(message);
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
singleUse: { enabled: localSurveySingleUseEnabled, ...message, isEncrypted: singleUseEncryption },
|
||||
});
|
||||
};
|
||||
|
||||
const hangleSingleUseEncryptionToggle = () => {
|
||||
if (!singleUseEncryption) {
|
||||
setSingleUseEncryption(true);
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
singleUse: { enabled: true, ...singleUseMessage, isEncrypted: true },
|
||||
});
|
||||
} else {
|
||||
setSingleUseEncryption(false);
|
||||
setLocalSurvey({
|
||||
...localSurvey,
|
||||
singleUse: { enabled: true, ...singleUseMessage, isEncrypted: false },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleHideBackButtonToggle = () => {
|
||||
setLocalSurvey({ ...localSurvey, isBackButtonHidden: !localSurvey.isBackButtonHidden });
|
||||
};
|
||||
@@ -223,14 +168,6 @@ export const ResponseOptionsCard = ({
|
||||
setSurveyClosedMessageToggle(true);
|
||||
}
|
||||
|
||||
if (localSurvey.singleUse?.enabled) {
|
||||
setSingleUseMessage({
|
||||
heading: localSurvey.singleUse.heading ?? singleUseMessage.heading,
|
||||
subheading: localSurvey.singleUse.subheading ?? singleUseMessage.subheading,
|
||||
});
|
||||
setSingleUseEncryption(localSurvey.singleUse.isEncrypted);
|
||||
}
|
||||
|
||||
if (localSurvey.runOnDate) {
|
||||
setRunOnDate(localSurvey.runOnDate);
|
||||
setRunOnDateToggle(true);
|
||||
@@ -240,13 +177,7 @@ export const ResponseOptionsCard = ({
|
||||
setCloseOnDate(localSurvey.closeOnDate);
|
||||
setCloseOnDateToggle(true);
|
||||
}
|
||||
}, [
|
||||
localSurvey,
|
||||
singleUseMessage.heading,
|
||||
singleUseMessage.subheading,
|
||||
surveyClosedMessage.heading,
|
||||
surveyClosedMessage.subheading,
|
||||
]);
|
||||
}, [localSurvey, surveyClosedMessage.heading, surveyClosedMessage.subheading]);
|
||||
|
||||
const toggleAutocomplete = () => {
|
||||
if (autoComplete) {
|
||||
@@ -471,80 +402,6 @@ export const ResponseOptionsCard = ({
|
||||
</div>
|
||||
</AdvancedOptionToggle>
|
||||
|
||||
{/* Single User Survey Options */}
|
||||
<AdvancedOptionToggle
|
||||
htmlId="singleUserSurveyOptions"
|
||||
isChecked={!!localSurvey.singleUse?.enabled}
|
||||
onToggle={handleSingleUseSurveyToggle}
|
||||
title={t("environments.surveys.edit.single_use_survey_links")}
|
||||
description={t("environments.surveys.edit.single_use_survey_links_description")}
|
||||
childBorder={true}>
|
||||
<div className="flex w-full items-center space-x-1 p-4 pb-4">
|
||||
<div className="w-full cursor-pointer items-center bg-slate-50">
|
||||
<div className="row mb-2 flex cursor-default items-center space-x-2">
|
||||
<Label htmlFor="howItWorks">{t("environments.surveys.edit.how_it_works")}</Label>
|
||||
</div>
|
||||
<ul className="mb-3 ml-4 cursor-default list-inside list-disc space-y-1">
|
||||
<li className="text-sm text-slate-600">
|
||||
{t(
|
||||
"environments.surveys.edit.blocks_survey_if_the_survey_url_has_no_single_use_id_suid"
|
||||
)}
|
||||
</li>
|
||||
<li className="text-sm text-slate-600">
|
||||
{t(
|
||||
"environments.surveys.edit.blocks_survey_if_a_submission_with_the_single_use_id_suid_exists_already"
|
||||
)}
|
||||
</li>
|
||||
<li className="text-sm text-slate-600">
|
||||
<Link
|
||||
href="https://formbricks.com/docs/link-surveys/single-use-links"
|
||||
target="_blank"
|
||||
className="underline">
|
||||
{t("common.read_docs")} <ArrowUpRight className="inline" size={16} />
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<Label htmlFor="headline">{t("environments.surveys.edit.link_used_message")}</Label>
|
||||
<Input
|
||||
autoFocus
|
||||
id="heading"
|
||||
className="mb-4 mt-2 bg-white"
|
||||
name="heading"
|
||||
value={singleUseMessage.heading}
|
||||
onChange={(e) => handleSingleUseSurveyMessageChange({ heading: e.target.value })}
|
||||
/>
|
||||
|
||||
<Label htmlFor="headline">{t("environments.surveys.edit.subheading")}</Label>
|
||||
<Input
|
||||
className="mb-4 mt-2 bg-white"
|
||||
id="subheading"
|
||||
name="subheading"
|
||||
value={singleUseMessage.subheading}
|
||||
onChange={(e) => handleSingleUseSurveyMessageChange({ subheading: e.target.value })}
|
||||
/>
|
||||
<Label htmlFor="headline">{t("environments.surveys.edit.url_encryption")}</Label>
|
||||
<div>
|
||||
<div className="mt-2 flex items-center space-x-1">
|
||||
<Switch
|
||||
id="encryption-switch"
|
||||
checked={singleUseEncryption}
|
||||
onCheckedChange={hangleSingleUseEncryptionToggle}
|
||||
/>
|
||||
<Label htmlFor="encryption-label">
|
||||
<div className="ml-2">
|
||||
<p className="text-sm font-normal text-slate-600">
|
||||
{t(
|
||||
"environments.surveys.edit.enable_encryption_of_single_use_id_suid_in_survey_url"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AdvancedOptionToggle>
|
||||
|
||||
{/* Verify Email Section */}
|
||||
<AdvancedOptionToggle
|
||||
htmlId="verifyEmailBeforeSubmission"
|
||||
|
||||
@@ -14,7 +14,7 @@ export const useSingleUseId = (survey: TSurvey | TSurveyList) => {
|
||||
if (survey.singleUse?.enabled) {
|
||||
const response = await generateSingleUseIdsAction({
|
||||
surveyId: survey.id,
|
||||
isEncrypted: !!survey.singleUse?.isEncrypted,
|
||||
isEncrypted: Boolean(survey.singleUse?.isEncrypted),
|
||||
count: 1,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user