diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx
index 476f6f794d..7407833959 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/emailTemplate.tsx
@@ -1,10 +1,10 @@
-import { getPreviewEmailTemplateHtml } from "@formbricks/email/components/survey/PreviewEmailTemplste";
+import { getPreviewEmailTemplateHtml } from "@formbricks/email/components/survey/PreviewEmailTemplate";
import { WEBAPP_URL } from "@formbricks/lib/constants";
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
-import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
import { getSurvey } from "@formbricks/lib/survey/service";
+import { getStyling } from "@formbricks/lib/utils/styling";
-export const getEmailTemplateHtml = async (surveyId) => {
+export const getEmailTemplateHtml = async (surveyId: string) => {
const survey = await getSurvey(surveyId);
if (!survey) {
throw new Error("Survey not found");
@@ -13,9 +13,10 @@ export const getEmailTemplateHtml = async (surveyId) => {
if (!product) {
throw new Error("Product not found");
}
- const brandColor = product.styling.brandColor?.light || COLOR_DEFAULTS.brandColor;
+
+ const styling = getStyling(product, survey);
const surveyUrl = WEBAPP_URL + "/s/" + survey.id;
- const html = getPreviewEmailTemplateHtml(survey, surveyUrl, brandColor);
+ const html = getPreviewEmailTemplateHtml(survey, surveyUrl, styling);
const doctype =
'';
const htmlCleaned = html.toString().replace(doctype, "");
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx
index db2e732fbb..5bb877e998 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx
@@ -118,10 +118,31 @@ export const QuestionsView = ({
};
if ("backButtonLabel" in updatedAttributes) {
- updatedSurvey.questions.forEach((question) => {
- question.backButtonLabel = updatedAttributes.backButtonLabel;
- });
- setbackButtonLabel(updatedAttributes.backButtonLabel);
+ const backButtonLabel = updatedSurvey.questions[questionIdx].backButtonLabel;
+ // If the value of backbuttonLabel is equal to {default:""}, then delete backButtonLabel key
+ if (
+ backButtonLabel &&
+ Object.keys(backButtonLabel).length === 1 &&
+ backButtonLabel["default"].trim() === ""
+ ) {
+ delete updatedSurvey.questions[questionIdx].backButtonLabel;
+ } else {
+ updatedSurvey.questions.forEach((question) => {
+ question.backButtonLabel = updatedAttributes.backButtonLabel;
+ });
+ setbackButtonLabel(updatedAttributes.backButtonLabel);
+ }
+ }
+ // If the value of buttonLabel is equal to {default:""}, then delete buttonLabel key
+ if ("buttonLabel" in updatedAttributes) {
+ const currentButtonLabel = updatedSurvey.questions[questionIdx].buttonLabel;
+ if (
+ currentButtonLabel &&
+ Object.keys(currentButtonLabel).length === 1 &&
+ currentButtonLabel["default"].trim() === ""
+ ) {
+ delete updatedSurvey.questions[questionIdx].buttonLabel;
+ }
}
setLocalSurvey(updatedSurvey);
validateSurveyQuestion(updatedSurvey.questions[questionIdx]);
diff --git a/packages/email/components/survey/PreviewEmailTemplste.tsx b/packages/email/components/survey/PreviewEmailTemplate.tsx
similarity index 60%
rename from packages/email/components/survey/PreviewEmailTemplste.tsx
rename to packages/email/components/survey/PreviewEmailTemplate.tsx
index 71de199e01..848e86b3dc 100644
--- a/packages/email/components/survey/PreviewEmailTemplste.tsx
+++ b/packages/email/components/survey/PreviewEmailTemplate.tsx
@@ -15,48 +15,52 @@ import React from "react";
import { cn } from "@formbricks/lib/cn";
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
-import { isLight } from "@formbricks/lib/utils";
-import { TSurvey, TSurveyQuestionType } from "@formbricks/types/surveys";
+import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants";
+import { isLight, mixColor } from "@formbricks/lib/utils";
+import { TSurvey, TSurveyQuestionType, TSurveyStyling } from "@formbricks/types/surveys";
import { RatingSmiley } from "@formbricks/ui/RatingSmiley";
interface PreviewEmailTemplateProps {
survey: TSurvey;
surveyUrl: string;
- brandColor: string;
+ styling: TSurveyStyling;
}
-export const getPreviewEmailTemplateHtml = (survey: TSurvey, surveyUrl: string, brandColor: string) => {
- return render(, {
+export const getPreviewEmailTemplateHtml = (survey: TSurvey, surveyUrl: string, styling: TSurveyStyling) => {
+ return render(, {
pretty: true,
});
};
-export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewEmailTemplateProps) => {
+export const PreviewEmailTemplate = ({ survey, surveyUrl, styling }: PreviewEmailTemplateProps) => {
const url = `${surveyUrl}?preview=true`;
const urlWithPrefilling = `${surveyUrl}?preview=true&`;
const defaultLanguageCode = "default";
const firstQuestion = survey.questions[0];
+
+ const brandColor = styling?.brandColor?.light || COLOR_DEFAULTS.brandColor;
+
switch (firstQuestion.type) {
case TSurveyQuestionType.OpenText:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
-
+
);
case TSurveyQuestionType.Consent:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
-
-
+
+
{getLocalizedValue(firstQuestion.label, defaultLanguageCode)}
@@ -73,14 +77,14 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
{!firstQuestion.required && (
+ className="rounded-custom inline-flex cursor-pointer appearance-none px-6 py-3 text-sm font-medium text-black">
Reject
)}
Accept
@@ -91,26 +95,26 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.NPS:
return (
-
+
-
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
-
+
{Array.from({ length: 11 }, (_, i) => (
+ className="border-input-border-color m-0 inline-flex h-10 w-10 items-center justify-center border p-0 text-slate-800">
{i}
))}
-
+
@@ -131,11 +135,11 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.CTA:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
+ className="rounded-custom inline-flex cursor-pointer appearance-none px-6 py-3 text-sm font-medium text-black">
{getLocalizedValue(firstQuestion.dismissButtonLabel, defaultLanguageCode) || "Skip"}
)}
{getLocalizedValue(firstQuestion.buttonLabel, defaultLanguageCode)}
@@ -165,17 +169,17 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.Rating:
return (
-
-
-
+
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
@@ -184,7 +188,7 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
key={i}
href={`${urlWithPrefilling}${firstQuestion.id}=${i + 1}`}
className={cn(
- " m-0 h-10 w-full p-0 text-center align-middle leading-10 text-slate-800",
+ "m-0 h-10 w-full p-0 text-center align-middle leading-10 text-slate-800",
{
["border border-solid border-gray-200"]: firstQuestion.scale === "number",
}
@@ -200,7 +204,7 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
))}
-
+
@@ -221,17 +225,17 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.MultipleChoiceMulti:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
{firstQuestion.choices.map((choice) => (
{getLocalizedValue(choice.label, defaultLanguageCode)}
@@ -242,18 +246,18 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.MultipleChoiceSingle:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
{firstQuestion.choices.map((choice) => (
{getLocalizedValue(choice.label, defaultLanguageCode)}
@@ -264,11 +268,11 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.PictureSelection:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
@@ -276,14 +280,14 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
firstQuestion.allowMulti ? (
) : (
-
+ className="rounded-custom mb-1 mr-1 inline-block h-[110px] w-[220px]">
+
)
)}
@@ -293,17 +297,17 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.Cal:
return (
-
+
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
-
+
You have been invited to schedule a meet via cal.com.
Schedule your meeting
@@ -314,27 +318,27 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.Date:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
-
);
case TSurveyQuestionType.Matrix:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, "default")}
-
+
{getLocalizedValue(firstQuestion.subheader, "default")}
@@ -345,7 +349,7 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
return (
+ className="text-question-color max-w-40 break-words px-4 py-2 text-center">
{getLocalizedValue(column, "default")}
);
@@ -353,14 +357,16 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
{firstQuestion.rows.map((row, rowIndex) => {
return (
-
+
{getLocalizedValue(row, "default")}
{firstQuestion.columns.map(() => {
return (
-
-
+
+
);
})}
@@ -374,17 +380,17 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
);
case TSurveyQuestionType.Address:
return (
-
-
+
+
{getLocalizedValue(firstQuestion.headline, defaultLanguageCode)}
-
+
{getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)}
{Array.from({ length: 6 }).map((_, index) => (
))}
@@ -393,14 +399,42 @@ export const PreviewEmailTemplate = ({ survey, surveyUrl, brandColor }: PreviewE
}
};
-const EmailTemplateWrapper = ({ children, surveyUrl, brandColor }) => {
+const EmailTemplateWrapper = ({
+ children,
+ surveyUrl,
+ styling,
+}: {
+ children: React.ReactNode;
+ surveyUrl: string;
+ styling: TSurveyStyling;
+}) => {
+ let signatureColor = "";
+ const colors = {
+ "brand-color": styling.brandColor?.light ?? COLOR_DEFAULTS.brandColor,
+ "card-bg-color": styling.cardBackgroundColor?.light ?? COLOR_DEFAULTS.cardBackgroundColor,
+ "input-color": styling.inputColor?.light ?? COLOR_DEFAULTS.inputColor,
+ "input-border-color": styling.inputBorderColor?.light ?? COLOR_DEFAULTS.inputBorderColor,
+ "card-border-color": styling.cardBorderColor?.light ?? COLOR_DEFAULTS.cardBorderColor,
+ "question-color": styling.questionColor?.light ?? COLOR_DEFAULTS.questionColor,
+ };
+
+ if (isLight(colors["question-color"])) {
+ signatureColor = mixColor(colors["question-color"], "#000000", 0.2);
+ } else {
+ signatureColor = mixColor(colors["question-color"], "#ffffff", 0.2);
+ }
+
return (
{
+ className="bg-card-bg-color border-card-border-color rounded-custom mx-0 my-2 block overflow-auto border border-solid p-8 font-sans text-inherit">
{children}
@@ -417,8 +451,8 @@ const EmailTemplateWrapper = ({ children, surveyUrl, brandColor }) => {
const EmailFooter = () => {
return (
-
-
+
+
Powered by Formbricks
diff --git a/packages/js-core/src/app/lib/widget.ts b/packages/js-core/src/app/lib/widget.ts
index f4b662137a..28b9485969 100644
--- a/packages/js-core/src/app/lib/widget.ts
+++ b/packages/js-core/src/app/lib/widget.ts
@@ -1,6 +1,7 @@
import { FormbricksAPI } from "@formbricks/api";
import { ResponseQueue } from "@formbricks/lib/responseQueue";
import SurveyState from "@formbricks/lib/surveyState";
+import { getStyling } from "@formbricks/lib/utils/styling";
import { TResponseUpdate } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys";
@@ -92,26 +93,6 @@ const renderWidget = async (survey: TSurvey, action?: string) => {
const isBrandingEnabled = product.inAppSurveyBranding;
const formbricksSurveys = await loadFormbricksSurveysExternally();
- const getStyling = () => {
- // allow style overwrite is disabled from the product
- if (!product.styling.allowStyleOverwrite) {
- return product.styling;
- }
-
- // allow style overwrite is enabled from the product
- if (product.styling.allowStyleOverwrite) {
- // survey style overwrite is disabled
- if (!survey.styling?.overwriteThemeStyling) {
- return product.styling;
- }
-
- // survey style overwrite is enabled
- return survey.styling;
- }
-
- return product.styling;
- };
-
setTimeout(() => {
formbricksSurveys.renderSurveyModal({
survey: survey,
@@ -120,7 +101,7 @@ const renderWidget = async (survey: TSurvey, action?: string) => {
darkOverlay,
languageCode,
placement,
- styling: getStyling(),
+ styling: getStyling(product, survey),
getSetIsError: (f: (value: boolean) => void) => {
setIsError = f;
},
diff --git a/packages/js-core/src/website/lib/widget.ts b/packages/js-core/src/website/lib/widget.ts
index 95bb22ca60..1a24b377b5 100644
--- a/packages/js-core/src/website/lib/widget.ts
+++ b/packages/js-core/src/website/lib/widget.ts
@@ -1,6 +1,7 @@
import { FormbricksAPI } from "@formbricks/api";
import { ResponseQueue } from "@formbricks/lib/responseQueue";
import SurveyState from "@formbricks/lib/surveyState";
+import { getStyling } from "@formbricks/lib/utils/styling";
import { TJSWebsiteStateDisplay } from "@formbricks/types/js";
import { TResponseUpdate } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys";
@@ -91,26 +92,6 @@ const renderWidget = async (survey: TSurvey, action?: string) => {
const isBrandingEnabled = product.inAppSurveyBranding;
const formbricksSurveys = await loadFormbricksSurveysExternally();
- const getStyling = () => {
- // allow style overwrite is disabled from the product
- if (!product.styling.allowStyleOverwrite) {
- return product.styling;
- }
-
- // allow style overwrite is enabled from the product
- if (product.styling.allowStyleOverwrite) {
- // survey style overwrite is disabled
- if (!survey.styling?.overwriteThemeStyling) {
- return product.styling;
- }
-
- // survey style overwrite is enabled
- return survey.styling;
- }
-
- return product.styling;
- };
-
setTimeout(() => {
formbricksSurveys.renderSurveyModal({
survey: survey,
@@ -119,7 +100,7 @@ const renderWidget = async (survey: TSurvey, action?: string) => {
darkOverlay,
languageCode,
placement,
- styling: getStyling(),
+ styling: getStyling(product, survey),
getSetIsError: (f: (value: boolean) => void) => {
setIsError = f;
},
diff --git a/packages/lib/utils/styling.ts b/packages/lib/utils/styling.ts
new file mode 100644
index 0000000000..93cdcc8cb8
--- /dev/null
+++ b/packages/lib/utils/styling.ts
@@ -0,0 +1,22 @@
+import { TProduct } from "@formbricks/types/product";
+import { TSurvey } from "@formbricks/types/surveys";
+
+export const getStyling = (product: TProduct, survey: TSurvey) => {
+ // allow style overwrite is disabled from the product
+ if (!product.styling.allowStyleOverwrite) {
+ return product.styling;
+ }
+
+ // allow style overwrite is enabled from the product
+ if (product.styling.allowStyleOverwrite) {
+ // survey style overwrite is disabled
+ if (!survey.styling?.overwriteThemeStyling) {
+ return product.styling;
+ }
+
+ // survey style overwrite is enabled
+ return survey.styling;
+ }
+
+ return product.styling;
+};