fix: survey preview stuck in sending (#4941)

This commit is contained in:
Matti Nannt
2025-03-13 20:34:45 +01:00
committed by GitHub
parent 9cd7a25343
commit 7103ec9877
8 changed files with 88 additions and 36 deletions

View File

@@ -231,6 +231,7 @@ export const ProjectSettings = ({
<p className="text-sm text-slate-400">{t("common.preview")}</p>
<div className="z-0 h-3/4 w-3/4">
<SurveyInline
isPreviewMode={true}
survey={previewSurvey(projectName || "my Product", t)}
styling={{ brandColor: { light: brandColor } }}
isBrandingEnabled={false}

View File

@@ -243,7 +243,6 @@ export const SurveyEditor = ({
environment={environment}
previewType={localSurvey.type === "app" ? "modal" : "fullwidth"}
languageCode={selectedLanguageCode}
onFileUpload={async (file) => file.name}
/>
</aside>
</div>

View File

@@ -170,14 +170,14 @@ export const LinkSurvey = ({
PRIVACY_URL={PRIVACY_URL}
isBrandingEnabled={project.linkSurveyBranding}>
<SurveyInline
apiHost={!isPreview ? webAppUrl : undefined}
environmentId={!isPreview ? survey.environmentId : undefined}
apiHost={webAppUrl}
environmentId={survey.environmentId}
isPreviewMode={isPreview}
survey={survey}
styling={determineStyling()}
languageCode={languageCode}
isBrandingEnabled={project.linkSurveyBranding}
shouldResetQuestionId={false}
onFileUpload={isPreview ? async (file) => `https://formbricks.com/${file.name}` : undefined}
// eslint-disable-next-line jsx-a11y/no-autofocus -- need it as focus behaviour is different in normal surveys and survey preview
autoFocus={autoFocus}
prefillResponseData={prefillValue}

View File

@@ -84,7 +84,6 @@ export const TemplateContainerWithPreview = ({
project={project}
environment={environment}
languageCode={"default"}
onFileUpload={async (file) => file.name}
/>
)}
</aside>

View File

@@ -9,9 +9,7 @@ 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 { TJsFileUploadParams } from "@formbricks/types/js";
import { TProjectStyling } from "@formbricks/types/project";
import { TUploadFileConfig } from "@formbricks/types/storage";
import { TSurvey, TSurveyQuestionId, TSurveyStyling } from "@formbricks/types/surveys/types";
import { Modal } from "./components/modal";
import { TabOption } from "./components/tab-option";
@@ -25,7 +23,6 @@ interface PreviewSurveyProps {
project: Project;
environment: Pick<Environment, "id" | "appSetupCompleted">;
languageCode: string;
onFileUpload: (file: TJsFileUploadParams["file"], config?: TUploadFileConfig) => Promise<string>;
}
let surveyNameTemp: string;
@@ -66,7 +63,6 @@ export const PreviewSurvey = ({
project,
environment,
languageCode,
onFileUpload,
}: PreviewSurveyProps) => {
const [isModalOpen, setIsModalOpen] = useState(true);
const [isFullScreenPreview, setIsFullScreenPreview] = useState(false);
@@ -265,11 +261,11 @@ export const PreviewSurvey = ({
borderRadius={styling?.roundness ?? 8}
background={styling?.cardBackgroundColor?.light}>
<SurveyInline
isPreviewMode={true}
survey={survey}
isBrandingEnabled={project.inAppSurveyBranding}
isRedirectDisabled={true}
languageCode={languageCode}
onFileUpload={onFileUpload}
styling={styling}
isCardBorderVisible={!styling.highlightBorderColor?.light}
onClose={handlePreviewModalClose}
@@ -288,9 +284,9 @@ export const 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}
onFileUpload={onFileUpload}
languageCode={languageCode}
responseCount={42}
styling={styling}
@@ -367,11 +363,11 @@ export const PreviewSurvey = ({
borderRadius={styling.roundness ?? 8}
background={styling.cardBackgroundColor?.light}>
<SurveyInline
isPreviewMode={true}
survey={survey}
isBrandingEnabled={project.inAppSurveyBranding}
isRedirectDisabled={true}
languageCode={languageCode}
onFileUpload={onFileUpload}
styling={styling}
isCardBorderVisible={!styling.highlightBorderColor?.light}
onClose={handlePreviewModalClose}
@@ -394,10 +390,10 @@ export const 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}
onFileUpload={onFileUpload}
languageCode={languageCode}
responseCount={42}
styling={styling}

View File

@@ -162,6 +162,7 @@ export const ThemeStylingPreviewSurvey = ({
borderRadius={project.styling.roundness ?? 8}>
<Fragment key={surveyFormKey}>
<SurveyInline
isPreviewMode={true}
survey={{ ...survey, type: "app" }}
isBrandingEnabled={project.inAppSurveyBranding}
isRedirectDisabled={true}
@@ -187,6 +188,7 @@ export const ThemeStylingPreviewSurvey = ({
key={surveyFormKey}
className={`${project.logo?.url && !project.styling.isLogoHidden && !isFullScreenPreview ? "mt-12" : ""} z-0 w-full max-w-md rounded-lg p-4`}>
<SurveyInline
isPreviewMode={true}
survey={{ ...survey, type: "link" }}
isBrandingEnabled={project.linkSurveyBranding}
isRedirectDisabled={true}

View File

@@ -36,6 +36,7 @@ interface VariableStackEntry {
export function Survey({
apiHost,
environmentId,
isPreviewMode = false,
userId,
contactId,
mode,
@@ -150,7 +151,6 @@ export function Survey({
return localSurvey.questions[0]?.id;
});
const [showError, setShowError] = useState(false);
// flag state to store whether response processing has been completed or not, we ignore this check for survey editor preview and link survey preview where getSetIsResponseSendingFinished is undefined
const [isResponseSendingFinished, setIsResponseSendingFinished] = useState(
!getSetIsResponseSendingFinished
);
@@ -182,6 +182,11 @@ export function Survey({
};
const onFileUploadApi = async (file: TJsFileUploadParams["file"], params?: TUploadFileConfig) => {
if (isPreviewMode) {
// return mock url since an url is required for the preview
return `https://example.com/${file.name}`;
}
if (!apiClient) {
throw new Error("apiClient not initialized");
}
@@ -206,6 +211,17 @@ export function Survey({
}, [questionId]);
const createDisplay = useCallback(async () => {
// Skip display creation in preview mode but still trigger the onDisplayCreated callback
if (isPreviewMode) {
if (onDisplayCreated) {
onDisplayCreated();
}
if (onDisplay) {
onDisplay();
}
return;
}
if (apiClient && surveyState && responseQueue) {
try {
const display = await apiClient.createDisplay({
@@ -229,7 +245,17 @@ export function Survey({
console.error("error creating display: ", err);
}
}
}, [apiClient, surveyState, responseQueue, survey.id, userId, contactId, onDisplayCreated]);
}, [
apiClient,
surveyState,
responseQueue,
survey.id,
userId,
contactId,
onDisplayCreated,
isPreviewMode,
onDisplay,
]);
useEffect(() => {
// call onDisplay when component is mounted
@@ -385,6 +411,32 @@ export function Survey({
const onResponseCreateOrUpdate = useCallback(
(responseUpdate: TResponseUpdate) => {
// Always trigger the onResponse callback even in preview mode
if (!apiHost || !environmentId) {
onResponse?.({
data: responseUpdate.data,
ttc: responseUpdate.ttc,
finished: responseUpdate.finished,
variables: responseUpdate.variables,
language: responseUpdate.language,
endingId: responseUpdate.endingId,
});
return;
}
// Skip response creation in preview mode but still trigger the onResponseCreated callback
if (isPreviewMode) {
if (onResponseCreated) {
onResponseCreated();
}
// When in preview mode, set isResponseSendingFinished to true if the response is finished
if (responseUpdate.finished) {
setIsResponseSendingFinished(true);
}
return;
}
if (surveyState && responseQueue) {
if (contactId) {
surveyState.updateContactId(contactId);
@@ -415,7 +467,20 @@ export function Survey({
}
}
},
[surveyState, responseQueue, contactId, userId, survey, action, hiddenFieldsRecord, onResponseCreated]
[
apiHost,
environmentId,
isPreviewMode,
surveyState,
responseQueue,
contactId,
userId,
survey,
action,
hiddenFieldsRecord,
onResponseCreated,
onResponse,
]
);
useEffect(() => {
@@ -446,25 +511,14 @@ export function Survey({
onChange(surveyResponseData);
onChangeVariables(calculatedVariables);
if (apiHost && environmentId) {
onResponseCreateOrUpdate({
data: surveyResponseData,
ttc: responsettc,
finished,
variables: calculatedVariables,
language: selectedLanguage,
endingId,
});
} else {
onResponse?.({
data: surveyResponseData,
ttc: responsettc,
finished,
variables: calculatedVariables,
language: selectedLanguage,
endingId,
});
}
onResponseCreateOrUpdate({
data: surveyResponseData,
ttc: responsettc,
finished,
variables: calculatedVariables,
language: selectedLanguage,
endingId,
});
if (nextQuestionId) {
setQuestionId(nextQuestionId);
@@ -573,7 +627,7 @@ export function Survey({
onBack={onBack}
ttc={ttc}
setTtc={setTtc}
onFileUpload={apiHost && environmentId ? onFileUploadApi : onFileUpload!}
onFileUpload={onFileUpload ?? onFileUploadApi}
isFirstQuestion={question.id === localSurvey.questions[0]?.id}
skipPrefilled={skipPrefilled}
prefilledQuestionValue={getQuestionPrefillData(question.id, offset)}

View File

@@ -45,6 +45,7 @@ export interface SurveyModalProps extends SurveyBaseProps {
export interface SurveyContainerProps extends Omit<SurveyBaseProps, "onFileUpload"> {
apiHost?: string;
environmentId?: string;
isPreviewMode?: boolean;
userId?: string;
contactId?: string;
onDisplayCreated?: () => void | Promise<void>;