Compare commits

...

1 Commits

Author SHA1 Message Date
Jakob Schott
1475b7429d copied changes from PR 6341, commit 8f8c5cf509 2025-08-07 13:58:31 +02:00
5 changed files with 3197 additions and 314 deletions

2864
apps/web/locales/ro-RO.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,9 @@ import { ResponseOptionsCard } from "@/modules/survey/editor/components/response
import { SurveyPlacementCard } from "@/modules/survey/editor/components/survey-placement-card";
import { TargetingLockedCard } from "@/modules/survey/editor/components/targeting-locked-card";
import { WhenToSendCard } from "@/modules/survey/editor/components/when-to-send-card";
import { PreviewSurveyRef } from "@/modules/ui/components/preview-survey";
import { ActionClass, Environment, OrganizationRole } from "@prisma/client";
import { RefObject } from "react";
import { TContactAttributeKey } from "@formbricks/types/contact-attribute-key";
import { TSegment } from "@formbricks/types/segment";
import { TSurvey } from "@formbricks/types/surveys/types";
@@ -24,6 +26,7 @@ interface SettingsViewProps {
isSpamProtectionAllowed: boolean;
projectPermission: TTeamPermission | null;
isFormbricksCloud: boolean;
previewSurveyRef: RefObject<PreviewSurveyRef | null>;
}
export const SettingsView = ({
@@ -39,6 +42,7 @@ export const SettingsView = ({
isSpamProtectionAllowed,
projectPermission,
isFormbricksCloud,
previewSurveyRef,
}: SettingsViewProps) => {
const isAppSurvey = localSurvey.type === "app";
@@ -75,6 +79,7 @@ export const SettingsView = ({
propActionClasses={actionClasses}
membershipRole={membershipRole}
projectPermission={projectPermission}
previewSurveyRef={previewSurveyRef}
/>
<ResponseOptionsCard

View File

@@ -13,7 +13,7 @@ import { SurveyEditorTabs } from "@/modules/survey/editor/components/survey-edit
import { SurveyMenuBar } from "@/modules/survey/editor/components/survey-menu-bar";
import { TFollowUpEmailToUser } from "@/modules/survey/editor/types/survey-follow-up";
import { FollowUpsView } from "@/modules/survey/follow-ups/components/follow-ups-view";
import { PreviewSurvey } from "@/modules/ui/components/preview-survey";
import { PreviewSurvey, PreviewSurveyRef } from "@/modules/ui/components/preview-survey";
import { ActionClass, Environment, Language, OrganizationRole, Project } from "@prisma/client";
import { useCallback, useEffect, useRef, useState } from "react";
import { TContactAttributeKey } from "@formbricks/types/contact-attribute-key";
@@ -84,6 +84,7 @@ export const SurveyEditor = ({
const [styling, setStyling] = useState(localSurvey?.styling);
const [localStylingChanges, setLocalStylingChanges] = useState<TSurveyStyling | null>(null);
const previewSurveyRef = useRef<PreviewSurveyRef | null>(null);
const fetchLatestProject = useCallback(async () => {
const refetchProjectResponse = await refetchProjectAction({ projectId: localProject.id });
@@ -231,6 +232,7 @@ export const SurveyEditor = ({
isSpamProtectionAllowed={isSpamProtectionAllowed}
projectPermission={projectPermission}
isFormbricksCloud={isFormbricksCloud}
previewSurveyRef={previewSurveyRef}
/>
)}
@@ -250,6 +252,7 @@ export const SurveyEditor = ({
<aside className="group hidden flex-1 flex-shrink-0 items-center justify-center overflow-hidden border-l border-slate-200 bg-slate-100 shadow-inner md:flex md:flex-col">
<PreviewSurvey
ref={previewSurveyRef}
survey={localSurvey}
questionId={activeQuestionId}
project={localProject}

View File

@@ -9,12 +9,13 @@ import { ActionClassInfo } from "@/modules/ui/components/action-class-info";
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
import { Button } from "@/modules/ui/components/button";
import { Input } from "@/modules/ui/components/input";
import { PreviewSurveyRef } from "@/modules/ui/components/preview-survey";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { ActionClass, OrganizationRole } from "@prisma/client";
import * as Collapsible from "@radix-ui/react-collapsible";
import { useTranslate } from "@tolgee/react";
import { CheckIcon, PlusIcon, Trash2Icon } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { RefObject, useEffect, useMemo, useState } from "react";
import { TSurvey } from "@formbricks/types/surveys/types";
interface WhenToSendCardProps {
@@ -24,6 +25,7 @@ interface WhenToSendCardProps {
propActionClasses: ActionClass[];
membershipRole?: OrganizationRole;
projectPermission: TTeamPermission | null;
previewSurveyRef: RefObject<PreviewSurveyRef>;
}
export const WhenToSendCard = ({
@@ -33,6 +35,7 @@ export const WhenToSendCard = ({
propActionClasses,
membershipRole,
projectPermission,
previewSurveyRef,
}: WhenToSendCardProps) => {
const { t } = useTranslate();
const [open, setOpen] = useState(localSurvey.type === "app" ? true : false);
@@ -62,6 +65,9 @@ export const WhenToSendCard = ({
const updatedSurvey = { ...localSurvey, autoClose: 10 };
setLocalSurvey(updatedSurvey);
}
if (previewSurveyRef.current) {
previewSurveyRef.current.resetProgress();
}
};
const handleDelayToggle = () => {

View File

@@ -8,7 +8,7 @@ import { Environment, Project } from "@prisma/client";
import { useTranslate } from "@tolgee/react";
import { Variants, motion } from "framer-motion";
import { ExpandIcon, MonitorIcon, ShrinkIcon, SmartphoneIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
import { TProjectStyling } from "@formbricks/types/project";
import { TSurvey, TSurveyQuestionId, TSurveyStyling } from "@formbricks/types/surveys/types";
import { Modal } from "./components/modal";
@@ -26,6 +26,10 @@ interface PreviewSurveyProps {
isSpamProtectionAllowed: boolean;
}
export interface PreviewSurveyRef {
resetProgress: () => void;
}
let surveyNameTemp: string;
const previewParentContainerVariant: Variants = {
@@ -57,215 +61,317 @@ const previewParentContainerVariant: Variants = {
let setQuestionId = (_: string) => {};
export const PreviewSurvey = ({
questionId,
survey,
previewType,
project,
environment,
languageCode,
isSpamProtectionAllowed,
}: PreviewSurveyProps) => {
const [isModalOpen, setIsModalOpen] = useState(true);
const [isFullScreenPreview, setIsFullScreenPreview] = useState(false);
const { t } = useTranslate();
const [appSetupCompleted, setAppSetupCompleted] = useState(false);
export const PreviewSurvey = forwardRef<PreviewSurveyRef, PreviewSurveyProps>(
({ questionId, survey, previewType, project, environment, languageCode, isSpamProtectionAllowed }, ref) => {
const [isModalOpen, setIsModalOpen] = useState(true);
const [isFullScreenPreview, setIsFullScreenPreview] = useState(false);
const { t } = useTranslate();
const [appSetupCompleted, setAppSetupCompleted] = useState(false);
const [previewMode, setPreviewMode] = useState("desktop");
const [previewPosition, setPreviewPosition] = useState("relative");
const ContentRef = useRef<HTMLDivElement | null>(null);
const [shrink, setShrink] = useState(false);
const { projectOverwrites } = survey || {};
const previewScreenVariants: Variants = {
expanded: {
right: "5%",
bottom: "10%",
top: "12%",
width: "40%",
position: "fixed",
height: "80%",
zIndex: 1050,
boxShadow: "0px 4px 5px 4px rgba(169, 169, 169, 0.25)",
transition: {
ease: "easeInOut",
duration: shrink ? 0.3 : 0,
const [previewMode, setPreviewMode] = useState("desktop");
const [previewPosition, setPreviewPosition] = useState("relative");
const ContentRef = useRef<HTMLDivElement | null>(null);
const [shrink, setShrink] = useState(false);
const { projectOverwrites } = survey || {};
const previewScreenVariants: Variants = {
expanded: {
right: "5%",
bottom: "10%",
top: "12%",
width: "40%",
position: "fixed",
height: "80%",
zIndex: 1050,
boxShadow: "0px 4px 5px 4px rgba(169, 169, 169, 0.25)",
transition: {
ease: "easeInOut",
duration: shrink ? 0.3 : 0,
},
},
},
expanded_with_fixed_positioning: {
zIndex: 1050,
position: "fixed",
top: "5%",
right: "5%",
bottom: "10%",
width: "90%",
height: "90%",
transition: {
ease: "easeOut",
duration: 0.4,
expanded_with_fixed_positioning: {
zIndex: 1050,
position: "fixed",
top: "5%",
right: "5%",
bottom: "10%",
width: "90%",
height: "90%",
transition: {
ease: "easeOut",
duration: 0.4,
},
},
},
shrink: {
display: "relative",
width: ["95%"],
height: ["95%"],
},
};
shrink: {
display: "relative",
width: ["95%"],
height: ["95%"],
},
};
const { placement: surveyPlacement } = projectOverwrites || {};
const { darkOverlay: surveyDarkOverlay } = projectOverwrites || {};
const { clickOutsideClose: surveyClickOutsideClose } = projectOverwrites || {};
const { placement: surveyPlacement } = projectOverwrites || {};
const { darkOverlay: surveyDarkOverlay } = projectOverwrites || {};
const { clickOutsideClose: surveyClickOutsideClose } = projectOverwrites || {};
const placement = surveyPlacement || project.placement;
const darkOverlay = surveyDarkOverlay ?? project.darkOverlay;
const clickOutsideClose = surveyClickOutsideClose ?? project.clickOutsideClose;
const placement = surveyPlacement || project.placement;
const darkOverlay = surveyDarkOverlay ?? project.darkOverlay;
const clickOutsideClose = surveyClickOutsideClose ?? project.clickOutsideClose;
const widgetSetupCompleted = appSetupCompleted;
const widgetSetupCompleted = appSetupCompleted;
const styling: TSurveyStyling | TProjectStyling = useMemo(() => {
// allow style overwrite is disabled from the project
if (!project.styling.allowStyleOverwrite) {
return project.styling;
}
// allow style overwrite is enabled from the project
if (project.styling.allowStyleOverwrite) {
// survey style overwrite is disabled
if (!survey.styling?.overwriteThemeStyling) {
const styling: TSurveyStyling | TProjectStyling = useMemo(() => {
// allow style overwrite is disabled from the project
if (!project.styling.allowStyleOverwrite) {
return project.styling;
}
// survey style overwrite is enabled
return survey.styling;
}
// allow style overwrite is enabled from the project
if (project.styling.allowStyleOverwrite) {
// survey style overwrite is disabled
if (!survey.styling?.overwriteThemeStyling) {
return project.styling;
}
return project.styling;
}, [project.styling, survey.styling]);
// survey style overwrite is enabled
return survey.styling;
}
const updateQuestionId = useCallback(
(newQuestionId: TSurveyQuestionId) => {
if (
!newQuestionId ||
newQuestionId === "hidden" ||
newQuestionId === "multiLanguage" ||
newQuestionId.includes("fb-variables-")
)
return;
if (newQuestionId === "start" && !survey.welcomeCard.enabled) return;
setQuestionId(newQuestionId);
},
[survey.welcomeCard.enabled]
);
return project.styling;
}, [project.styling, survey.styling]);
useEffect(() => {
if (questionId) {
updateQuestionId(questionId);
}
}, [questionId, updateQuestionId]);
const updateQuestionId = useCallback(
(newQuestionId: TSurveyQuestionId) => {
if (
!newQuestionId ||
newQuestionId === "hidden" ||
newQuestionId === "multiLanguage" ||
newQuestionId.includes("fb-variables-")
)
return;
if (newQuestionId === "start" && !survey.welcomeCard.enabled) return;
setQuestionId(newQuestionId);
},
[survey.welcomeCard.enabled]
);
const onFinished = () => {
// close modal if there are no questions left
if (survey.type === "app" && survey.endings.length === 0) {
useEffect(() => {
if (questionId) {
updateQuestionId(questionId);
}
}, [questionId, updateQuestionId]);
const onFinished = () => {
// close modal if there are no questions left
if (survey.type === "app" && survey.endings.length === 0) {
setIsModalOpen(false);
setTimeout(() => {
setQuestionId(survey.questions[0]?.id);
setIsModalOpen(true);
}, 500);
}
};
// this useEffect is for refreshing the survey preview only if user is switching between templates on survey templates page and hence we are checking for survey.id === "someUniqeId1" which is a common Id for all templates
useEffect(() => {
if (survey.name !== surveyNameTemp && survey.id === "someUniqueId1") {
resetQuestionProgress();
surveyNameTemp = survey.name;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [survey]);
const resetQuestionProgress = () => {
let storePreviewMode = previewMode;
setPreviewMode("null");
setTimeout(() => {
setPreviewMode(storePreviewMode);
}, 10);
setQuestionId(survey.welcomeCard.enabled ? "start" : survey?.questions[0]?.id);
};
// Expose the reset function via ref
useImperativeHandle(ref, () => ({
resetProgress: resetQuestionProgress,
}));
useEffect(() => {
if (environment) {
setAppSetupCompleted(environment.appSetupCompleted);
}
}, [environment]);
const isSpamProtectionEnabled = useMemo(() => {
return isSpamProtectionAllowed && survey.recaptcha?.enabled;
}, [survey.recaptcha?.enabled, isSpamProtectionAllowed]);
const handlePreviewModalClose = () => {
setIsModalOpen(false);
setTimeout(() => {
setQuestionId(survey.questions[0]?.id);
setIsModalOpen(true);
}, 500);
}
};
resetQuestionProgress();
}, 1000);
};
// this useEffect is for refreshing the survey preview only if user is switching between templates on survey templates page and hence we are checking for survey.id === "someUniqeId1" which is a common Id for all templates
useEffect(() => {
if (survey.name !== surveyNameTemp && survey.id === "someUniqueId1") {
resetQuestionProgress();
surveyNameTemp = survey.name;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [survey]);
if (!previewType) {
previewType = widgetSetupCompleted ? "modal" : "fullwidth";
const resetQuestionProgress = () => {
let storePreviewMode = previewMode;
setPreviewMode("null");
setTimeout(() => {
setPreviewMode(storePreviewMode);
}, 10);
setQuestionId(survey.welcomeCard.enabled ? "start" : survey?.questions[0]?.id);
};
useEffect(() => {
if (environment) {
setAppSetupCompleted(environment.appSetupCompleted);
}
}, [environment]);
const isSpamProtectionEnabled = useMemo(() => {
return isSpamProtectionAllowed && survey.recaptcha?.enabled;
}, [survey.recaptcha?.enabled, isSpamProtectionAllowed]);
const handlePreviewModalClose = () => {
setIsModalOpen(false);
setTimeout(() => {
setIsModalOpen(true);
resetQuestionProgress();
}, 1000);
};
if (!previewType) {
previewType = widgetSetupCompleted ? "modal" : "fullwidth";
if (!questionId) {
return <></>;
}
}
const handlePreviewModeChange = (mode: "mobile" | "desktop") => {
setPreviewMode(mode);
requestAnimationFrame(() => {
if (questionId) {
setQuestionId(questionId);
if (!questionId) {
return <></>;
}
});
};
}
return (
<div className="flex h-full w-full flex-col items-center justify-items-center py-4" id="survey-preview">
<motion.div
variants={previewParentContainerVariant}
animate={isFullScreenPreview ? "expanded" : "shrink"}
/>
<motion.div
layout
variants={previewScreenVariants}
animate={
isFullScreenPreview
? previewPosition === "relative"
? "expanded"
: "expanded_with_fixed_positioning"
: "shrink"
const handlePreviewModeChange = (mode: "mobile" | "desktop") => {
setPreviewMode(mode);
requestAnimationFrame(() => {
if (questionId) {
setQuestionId(questionId);
}
className="relative flex h-full w-[95%] items-center justify-center rounded-lg border border-slate-300 bg-slate-200">
{previewMode === "mobile" && (
<>
<p className="absolute left-0 top-0 m-2 rounded bg-slate-100 px-2 py-1 text-xs text-slate-400">
Preview
</p>
<div className="absolute right-0 top-0 m-2">
<ResetProgressButton onClick={resetQuestionProgress} />
</div>
<MediaBackground
surveyType={survey.type}
styling={styling}
ContentRef={ContentRef as React.RefObject<HTMLDivElement>}
isMobilePreview>
});
};
return (
<div className="flex h-full w-full flex-col items-center justify-items-center py-4" id="survey-preview">
<motion.div
variants={previewParentContainerVariant}
animate={isFullScreenPreview ? "expanded" : "shrink"}
/>
<motion.div
layout
variants={previewScreenVariants}
animate={
isFullScreenPreview
? previewPosition === "relative"
? "expanded"
: "expanded_with_fixed_positioning"
: "shrink"
}
className="relative flex h-full w-[95%] items-center justify-center rounded-lg border border-slate-300 bg-slate-200">
{previewMode === "mobile" && (
<>
<p className="absolute left-0 top-0 m-2 rounded bg-slate-100 px-2 py-1 text-xs text-slate-400">
Preview
</p>
<div className="absolute right-0 top-0 m-2">
<ResetProgressButton onClick={resetQuestionProgress} />
</div>
<MediaBackground
surveyType={survey.type}
styling={styling}
ContentRef={ContentRef as React.RefObject<HTMLDivElement>}
isMobilePreview>
{previewType === "modal" ? (
<Modal
isOpen={isModalOpen}
placement={placement}
previewMode="mobile"
darkOverlay={darkOverlay}
clickOutsideClose={clickOutsideClose}
borderRadius={styling?.roundness ?? 8}
background={styling?.cardBackgroundColor?.light}>
<SurveyInline
isPreviewMode={true}
survey={survey}
isBrandingEnabled={project.inAppSurveyBranding}
isRedirectDisabled={true}
languageCode={languageCode}
styling={styling}
isCardBorderVisible={!styling.highlightBorderColor?.light}
onClose={handlePreviewModalClose}
getSetQuestionId={(f: (value: string) => void) => {
setQuestionId = f;
}}
onFinished={onFinished}
isSpamProtectionEnabled={isSpamProtectionEnabled}
/>
</Modal>
) : (
<div className="flex h-full w-full flex-col justify-center px-1">
<div className="absolute left-5 top-5">
{!styling.isLogoHidden && (
<ClientLogo environmentId={environment.id} projectLogo={project.logo} previewSurvey />
)}
</div>
<div className="z-10 w-full max-w-md rounded-lg border border-transparent">
<SurveyInline
isPreviewMode={true}
survey={{ ...survey, type: "link" }}
isBrandingEnabled={project.linkSurveyBranding}
languageCode={languageCode}
responseCount={42}
styling={styling}
getSetQuestionId={(f: (value: string) => void) => {
setQuestionId = f;
}}
isSpamProtectionEnabled={isSpamProtectionEnabled}
/>
</div>
</div>
)}
</MediaBackground>
</>
)}
{previewMode === "desktop" && (
<div className="flex h-full flex-1 flex-col">
<div className="flex h-8 w-full items-center rounded-t-lg bg-slate-100">
<div className="ml-6 flex space-x-2">
<div className="h-3 w-3 rounded-full bg-red-500"></div>
<div className="h-3 w-3 rounded-full bg-amber-500"></div>
<button
className="h-3 w-3 cursor-pointer rounded-full bg-emerald-500"
onClick={() => {
if (isFullScreenPreview) {
setShrink(true);
setPreviewPosition("relative");
setTimeout(() => setIsFullScreenPreview(false), 300);
} else {
setShrink(false);
setIsFullScreenPreview(true);
setTimeout(() => setPreviewPosition("fixed"), 300);
}
}}
aria-label={isFullScreenPreview ? "Shrink Preview" : "Expand Preview"}></button>
</div>
<div className="ml-4 flex w-full justify-between font-mono text-sm text-slate-400">
<p>
{previewType === "modal"
? t("environments.surveys.edit.your_web_app")
: t("common.preview")}
</p>
<div className="flex items-center">
{isFullScreenPreview ? (
<ShrinkIcon
className="mr-1 h-[22px] w-[22px] cursor-pointer rounded-md bg-white p-1 text-slate-500 hover:text-slate-700"
onClick={() => {
setShrink(true);
setPreviewPosition("relative");
setTimeout(() => setIsFullScreenPreview(false), 300);
}}
/>
) : (
<ExpandIcon
className="mr-1 h-[22px] w-[22px] cursor-pointer rounded-md bg-white p-1 text-slate-500 hover:text-slate-700"
onClick={() => {
setShrink(false);
setIsFullScreenPreview(true);
setTimeout(() => setPreviewPosition("fixed"), 300);
}}
/>
)}
<ResetProgressButton onClick={resetQuestionProgress} />
</div>
</div>
</div>
{previewType === "modal" ? (
<Modal
isOpen={isModalOpen}
placement={placement}
previewMode="mobile"
darkOverlay={darkOverlay}
clickOutsideClose={clickOutsideClose}
borderRadius={styling?.roundness ?? 8}
background={styling?.cardBackgroundColor?.light}>
darkOverlay={darkOverlay}
previewMode="desktop"
borderRadius={styling.roundness ?? 8}
background={styling.cardBackgroundColor?.light}>
<SurveyInline
isPreviewMode={true}
survey={survey}
@@ -283,17 +389,22 @@ export const PreviewSurvey = ({
/>
</Modal>
) : (
<div className="flex h-full w-full flex-col justify-center px-1">
<MediaBackground
surveyType={survey.type}
styling={styling}
ContentRef={ContentRef as React.RefObject<HTMLDivElement>}
isEditorView>
<div className="absolute left-5 top-5">
{!styling.isLogoHidden && (
<ClientLogo environmentId={environment.id} projectLogo={project.logo} previewSurvey />
)}
</div>
<div className="z-10 w-full max-w-md rounded-lg border border-transparent">
<div className="z-0 w-full max-w-4xl rounded-lg border-transparent">
<SurveyInline
isPreviewMode={true}
survey={{ ...survey, type: "link" }}
isBrandingEnabled={project.linkSurveyBranding}
isRedirectDisabled={true}
languageCode={languageCode}
responseCount={42}
styling={styling}
@@ -303,136 +414,30 @@ export const PreviewSurvey = ({
isSpamProtectionEnabled={isSpamProtectionEnabled}
/>
</div>
</div>
</MediaBackground>
)}
</MediaBackground>
</>
)}
{previewMode === "desktop" && (
<div className="flex h-full flex-1 flex-col">
<div className="flex h-8 w-full items-center rounded-t-lg bg-slate-100">
<div className="ml-6 flex space-x-2">
<div className="h-3 w-3 rounded-full bg-red-500"></div>
<div className="h-3 w-3 rounded-full bg-amber-500"></div>
<button
className="h-3 w-3 cursor-pointer rounded-full bg-emerald-500"
onClick={() => {
if (isFullScreenPreview) {
setShrink(true);
setPreviewPosition("relative");
setTimeout(() => setIsFullScreenPreview(false), 300);
} else {
setShrink(false);
setIsFullScreenPreview(true);
setTimeout(() => setPreviewPosition("fixed"), 300);
}
}}
aria-label={isFullScreenPreview ? "Shrink Preview" : "Expand Preview"}></button>
</div>
<div className="ml-4 flex w-full justify-between font-mono text-sm text-slate-400">
<p>
{previewType === "modal"
? t("environments.surveys.edit.your_web_app")
: t("common.preview")}
</p>
<div className="flex items-center">
{isFullScreenPreview ? (
<ShrinkIcon
className="mr-1 h-[22px] w-[22px] cursor-pointer rounded-md bg-white p-1 text-slate-500 hover:text-slate-700"
onClick={() => {
setShrink(true);
setPreviewPosition("relative");
setTimeout(() => setIsFullScreenPreview(false), 300);
}}
/>
) : (
<ExpandIcon
className="mr-1 h-[22px] w-[22px] cursor-pointer rounded-md bg-white p-1 text-slate-500 hover:text-slate-700"
onClick={() => {
setShrink(false);
setIsFullScreenPreview(true);
setTimeout(() => setPreviewPosition("fixed"), 300);
}}
/>
)}
<ResetProgressButton onClick={resetQuestionProgress} />
</div>
</div>
</div>
)}
</motion.div>
{previewType === "modal" ? (
<Modal
isOpen={isModalOpen}
placement={placement}
clickOutsideClose={clickOutsideClose}
darkOverlay={darkOverlay}
previewMode="desktop"
borderRadius={styling.roundness ?? 8}
background={styling.cardBackgroundColor?.light}>
<SurveyInline
isPreviewMode={true}
survey={survey}
isBrandingEnabled={project.inAppSurveyBranding}
isRedirectDisabled={true}
languageCode={languageCode}
styling={styling}
isCardBorderVisible={!styling.highlightBorderColor?.light}
onClose={handlePreviewModalClose}
getSetQuestionId={(f: (value: string) => void) => {
setQuestionId = f;
}}
onFinished={onFinished}
isSpamProtectionEnabled={isSpamProtectionEnabled}
/>
</Modal>
) : (
<MediaBackground
surveyType={survey.type}
styling={styling}
ContentRef={ContentRef as React.RefObject<HTMLDivElement>}
isEditorView>
<div className="absolute left-5 top-5">
{!styling.isLogoHidden && (
<ClientLogo environmentId={environment.id} projectLogo={project.logo} previewSurvey />
)}
</div>
<div className="z-0 w-full max-w-4xl rounded-lg border-transparent">
<SurveyInline
isPreviewMode={true}
survey={{ ...survey, type: "link" }}
isBrandingEnabled={project.linkSurveyBranding}
isRedirectDisabled={true}
languageCode={languageCode}
responseCount={42}
styling={styling}
getSetQuestionId={(f: (value: string) => void) => {
setQuestionId = f;
}}
isSpamProtectionEnabled={isSpamProtectionEnabled}
/>
</div>
</MediaBackground>
)}
</div>
)}
</motion.div>
{/* for toggling between mobile and desktop mode */}
<div className="mt-2 flex rounded-full border-2 border-slate-300 p-1">
<TabOption
active={previewMode === "mobile"}
icon={<SmartphoneIcon className="mx-4 my-2 h-4 w-4 text-slate-700" />}
onClick={() => handlePreviewModeChange("mobile")}
/>
<TabOption
active={previewMode === "desktop"}
icon={<MonitorIcon className="mx-4 my-2 h-4 w-4 text-slate-700" />}
onClick={() => handlePreviewModeChange("desktop")}
/>
{/* for toggling between mobile and desktop mode */}
<div className="mt-2 flex rounded-full border-2 border-slate-300 p-1">
<TabOption
active={previewMode === "mobile"}
icon={<SmartphoneIcon className="mx-4 my-2 h-4 w-4 text-slate-700" />}
onClick={() => handlePreviewModeChange("mobile")}
/>
<TabOption
active={previewMode === "desktop"}
icon={<MonitorIcon className="mx-4 my-2 h-4 w-4 text-slate-700" />}
onClick={() => handlePreviewModeChange("desktop")}
/>
</div>
</div>
</div>
);
};
);
}
);
PreviewSurvey.displayName = "PreviewSurvey";
export { getPlacementStyle } from "./lib/utils";