From f07092595ff23cb5be1cced33e1fd7450938cd2e Mon Sep 17 00:00:00 2001 From: Johannes <72809645+jobenjada@users.noreply.github.com> Date: Tue, 25 Nov 2025 01:49:59 -0800 Subject: [PATCH] feat: UI improvements to survey editor and summary cards (#6857) --- .../summary/components/AddressSummary.tsx | 73 ++++---- .../summary/components/ContactInfoSummary.tsx | 73 ++++---- .../components/DateQuestionSummary.tsx | 73 ++++---- .../summary/components/FileUploadSummary.tsx | 133 +++++++------- .../components/HiddenFieldsSummary.tsx | 73 ++++---- .../components/MultipleChoiceSummary.tsx | 172 +++++++++--------- .../summary/components/NPSSummary.tsx | 56 +++--- .../summary/components/OpenTextSummary.tsx | 111 +++++------ .../summary/components/RatingSummary.tsx | 7 +- .../summary/components/SummaryMetadata.tsx | 6 +- .../summary/components/SummaryPage.tsx | 1 + .../components/QuestionsComboBox.tsx | 2 +- apps/web/app/lib/surveys/surveys.test.ts | 127 +++++++++++++ apps/web/app/lib/surveys/surveys.ts | 4 +- apps/web/app/lib/templates.ts | 4 +- apps/web/i18n.lock | 5 +- apps/web/locales/de-DE.json | 5 +- apps/web/locales/en-US.json | 7 +- apps/web/locales/es-ES.json | 7 +- apps/web/locales/fr-FR.json | 7 +- apps/web/locales/ja-JP.json | 5 +- apps/web/locales/nl-NL.json | 7 +- apps/web/locales/pt-BR.json | 7 +- apps/web/locales/pt-PT.json | 7 +- apps/web/locales/ro-RO.json | 7 +- apps/web/locales/zh-Hans-CN.json | 5 +- apps/web/locales/zh-Hant-TW.json | 5 +- .../components/quota-condition-builder.tsx | 3 + .../ee/quotas/components/quota-modal.tsx | 24 ++- .../settings/(setup)/app-connection/page.tsx | 10 +- .../components/survey-variables-card-item.tsx | 2 +- .../editor/components/update-question-id.tsx | 2 +- .../follow-ups/components/follow-up-email.tsx | 6 +- .../follow-ups/components/follow-up-modal.tsx | 3 + .../ui/components/conditions-editor/index.tsx | 4 +- .../editor/styles-editor-frontend.css | 1 - .../ui/components/empty-state/index.tsx | 11 +- 37 files changed, 652 insertions(+), 403 deletions(-) diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/AddressSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/AddressSummary.tsx index fa1e6f0c89..5689c22999 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/AddressSummary.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/AddressSummary.tsx @@ -8,6 +8,7 @@ import { timeSince } from "@/lib/time"; import { getContactIdentifier } from "@/lib/utils/contact"; import { ArrayResponse } from "@/modules/ui/components/array-response"; import { PersonAvatar } from "@/modules/ui/components/avatars"; +import { EmptyState } from "@/modules/ui/components/empty-state"; import { QuestionSummaryHeader } from "./QuestionSummaryHeader"; interface AddressSummaryProps { @@ -29,42 +30,48 @@ export const AddressSummary = ({ questionSummary, environmentId, survey, locale
{t("common.time")}
- {questionSummary.samples.map((response) => { - return ( -
-
- {response.contact ? ( - -
- + {questionSummary.samples.length === 0 ? ( +
+ +
+ ) : ( + questionSummary.samples.map((response) => { + return ( +
+
+ {response.contact ? ( + +
+ +
+

+ {getContactIdentifier(response.contact, response.contactAttributes)} +

+ + ) : ( +
+
+ +
+

{t("common.anonymous")}

-

- {getContactIdentifier(response.contact, response.contactAttributes)} -

- - ) : ( -
-
- -
-

{t("common.anonymous")}

-
- )} -
-
- -
+ )} +
+
+ +
-
- {timeSince(new Date(response.updatedAt).toISOString(), locale)} +
+ {timeSince(new Date(response.updatedAt).toISOString(), locale)} +
-
- ); - })} + ); + }) + )}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ContactInfoSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ContactInfoSummary.tsx index 592df6e8c3..07ada774bf 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ContactInfoSummary.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ContactInfoSummary.tsx @@ -8,6 +8,7 @@ import { timeSince } from "@/lib/time"; import { getContactIdentifier } from "@/lib/utils/contact"; import { ArrayResponse } from "@/modules/ui/components/array-response"; import { PersonAvatar } from "@/modules/ui/components/avatars"; +import { EmptyState } from "@/modules/ui/components/empty-state"; import { QuestionSummaryHeader } from "./QuestionSummaryHeader"; interface ContactInfoSummaryProps { @@ -34,42 +35,48 @@ export const ContactInfoSummary = ({
{t("common.time")}
- {questionSummary.samples.map((response) => { - return ( -
-
- {response.contact ? ( - -
- + {questionSummary.samples.length === 0 ? ( +
+ +
+ ) : ( + questionSummary.samples.map((response) => { + return ( +
+
+ {response.contact ? ( + +
+ +
+

+ {getContactIdentifier(response.contact, response.contactAttributes)} +

+ + ) : ( +
+
+ +
+

{t("common.anonymous")}

-

- {getContactIdentifier(response.contact, response.contactAttributes)} -

- - ) : ( -
-
- -
-

{t("common.anonymous")}

-
- )} -
-
- -
+ )} +
+
+ +
-
- {timeSince(new Date(response.updatedAt).toISOString(), locale)} +
+ {timeSince(new Date(response.updatedAt).toISOString(), locale)} +
-
- ); - })} + ); + }) + )}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/DateQuestionSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/DateQuestionSummary.tsx index de2a2620f8..f9b56f74be 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/DateQuestionSummary.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/DateQuestionSummary.tsx @@ -10,6 +10,7 @@ import { getContactIdentifier } from "@/lib/utils/contact"; import { formatDateWithOrdinal } from "@/lib/utils/datetime"; import { PersonAvatar } from "@/modules/ui/components/avatars"; import { Button } from "@/modules/ui/components/button"; +import { EmptyState } from "@/modules/ui/components/empty-state"; import { QuestionSummaryHeader } from "./QuestionSummaryHeader"; interface DateQuestionSummary { @@ -55,41 +56,47 @@ export const DateQuestionSummary = ({
{t("common.time")}
- {questionSummary.samples.slice(0, visibleResponses).map((response) => ( -
-
- {response.contact ? ( - -
- -
-

- {getContactIdentifier(response.contact, response.contactAttributes)} -

- - ) : ( -
-
- -
-

{t("common.anonymous")}

-
- )} -
-
- {renderResponseValue(response.value)} -
-
- {timeSince(new Date(response.updatedAt).toISOString(), locale)} -
+ {questionSummary.samples.length === 0 ? ( +
+
- ))} + ) : ( + questionSummary.samples.slice(0, visibleResponses).map((response) => ( +
+
+ {response.contact ? ( + +
+ +
+

+ {getContactIdentifier(response.contact, response.contactAttributes)} +

+ + ) : ( +
+
+ +
+

{t("common.anonymous")}

+
+ )} +
+
+ {renderResponseValue(response.value)} +
+
+ {timeSince(new Date(response.updatedAt).toISOString(), locale)} +
+
+ )) + )}
- {visibleResponses < questionSummary.samples.length && ( + {questionSummary.samples.length > 0 && visibleResponses < questionSummary.samples.length && (
- {questionSummary.files.slice(0, visibleResponses).map((response) => ( -
-
- {response.contact ? ( - -
- -
-

- {getContactIdentifier(response.contact, response.contactAttributes)} -

- - ) : ( -
-
- -
-

{t("common.anonymous")}

-
- )} -
- -
- {Array.isArray(response.value) && - (response.value.length > 0 ? ( - response.value.map((fileUrl) => { - const fileName = getOriginalFileNameFromUrl(fileUrl); - - return ( -
- -
-
- -
-
-
- -
- -

{fileName}

-
-
- ); - }) - ) : ( -
-

- {t("common.skipped")} -

-
- ))} -
- -
- {timeSince(new Date(response.updatedAt).toISOString(), locale)} -
+ {questionSummary.files.length === 0 ? ( +
+
- ))} + ) : ( + questionSummary.files.slice(0, visibleResponses).map((response) => ( +
+
+ {response.contact ? ( + +
+ +
+

+ {getContactIdentifier(response.contact, response.contactAttributes)} +

+ + ) : ( +
+
+ +
+

{t("common.anonymous")}

+
+ )} +
+ +
+ {Array.isArray(response.value) && + (response.value.length > 0 ? ( + response.value.map((fileUrl) => { + const fileName = getOriginalFileNameFromUrl(fileUrl); + + return ( +
+ +
+
+ +
+
+
+ +
+ +

{fileName}

+
+
+ ); + }) + ) : ( +
+

+ {t("common.skipped")} +

+
+ ))} +
+ +
+ {timeSince(new Date(response.updatedAt).toISOString(), locale)} +
+
+ )) + )}
- {visibleResponses < questionSummary.files.length && ( + {questionSummary.files.length > 0 && visibleResponses < questionSummary.files.length && (
- {questionSummary.samples.slice(0, visibleResponses).map((response, idx) => ( -
-
- {response.contact ? ( - -
- -
-

- {getContactIdentifier(response.contact, response.contactAttributes)} -

- - ) : ( -
-
- -
-

{t("common.anonymous")}

-
- )} -
-
- {response.value} -
-
- {timeSince(new Date(response.updatedAt).toISOString(), locale)} -
+ {questionSummary.samples.length === 0 ? ( +
+
- ))} - {visibleResponses < questionSummary.samples.length && ( + ) : ( + questionSummary.samples.slice(0, visibleResponses).map((response, idx) => ( +
+
+ {response.contact ? ( + +
+ +
+

+ {getContactIdentifier(response.contact, response.contactAttributes)} +

+ + ) : ( +
+
+ +
+

{t("common.anonymous")}

+
+ )} +
+
+ {response.value} +
+
+ {timeSince(new Date(response.updatedAt).toISOString(), locale)} +
+
+ )) + )} + {questionSummary.samples.length > 0 && visibleResponses < questionSummary.samples.length && (
- {result.others && result.others.length > 0 && ( -
-
-
- {t("environments.surveys.summary.other_values_found")} +
+
+ {results.map((result) => { + const choiceId = getChoiceIdByValue(result.value, questionSummary.question); + return ( + + + {result.others && result.others.length > 0 && ( +
+
+
+ {t("environments.surveys.summary.other_values_found")} +
+
{surveyType === "app" && t("common.user")}
+
+ {result.others + .filter((otherValue) => otherValue.value !== "") + .slice(0, visibleOtherResponses) + .map((otherValue, idx) => ( +
+ {surveyType === "link" && ( +
{otherValue.value}
-
- {otherValue.contact.id && } - - {getContactIdentifier(otherValue.contact, otherValue.contactAttributes)} - -
- - )} + )} + {surveyType === "app" && otherValue.contact && ( + +
+ {otherValue.value} +
+
+ {otherValue.contact.id && } + + {getContactIdentifier(otherValue.contact, otherValue.contactAttributes)} + +
+ + )} +
+ ))} + {visibleOtherResponses < result.others.length && ( +
+
- ))} - {visibleOtherResponses < result.others.length && ( -
- -
- )} -
- )} -
- ); - })} + )} +
+ )} + + ); + })} +
); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/NPSSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/NPSSummary.tsx index d6462d1833..ec01c81412 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/NPSSummary.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/NPSSummary.tsx @@ -106,36 +106,38 @@ export const NPSSummary = ({ questionSummary, survey, setFilter }: NPSSummaryPro
-
- {["promoters", "passives", "detractors", "dismissed"].map((group) => ( -
- - - ))} + + + ))} +
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/OpenTextSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/OpenTextSummary.tsx index d4db26ed05..56b6278712 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/OpenTextSummary.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/OpenTextSummary.tsx @@ -10,6 +10,7 @@ import { getContactIdentifier } from "@/lib/utils/contact"; import { renderHyperlinkedContent } from "@/modules/analysis/utils"; import { PersonAvatar } from "@/modules/ui/components/avatars"; import { Button } from "@/modules/ui/components/button"; +import { EmptyState } from "@/modules/ui/components/empty-state"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/modules/ui/components/table"; import { QuestionSummaryHeader } from "./QuestionSummaryHeader"; @@ -35,59 +36,65 @@ export const OpenTextSummary = ({ questionSummary, environmentId, survey, locale
-
- - - - {t("common.user")} - {t("common.response")} - {t("common.time")} - - - - {questionSummary.samples.slice(0, visibleResponses).map((response) => ( - - - {response.contact ? ( - -
- -
-

- {getContactIdentifier(response.contact, response.contactAttributes)} -

- - ) : ( -
-
- -
-

{t("common.anonymous")}

-
- )} -
- - {typeof response.value === "string" - ? renderHyperlinkedContent(response.value) - : response.value} - - - {timeSince(new Date(response.updatedAt).toISOString(), locale)} - + {questionSummary.samples.length === 0 ? ( +
+ +
+ ) : ( +
+
+ + + {t("common.user")} + {t("common.response")} + {t("common.time")} - ))} - -
- {visibleResponses < questionSummary.samples.length && ( -
- -
- )} -
+ + + {questionSummary.samples.slice(0, visibleResponses).map((response) => ( + + + {response.contact ? ( + +
+ +
+

+ {getContactIdentifier(response.contact, response.contactAttributes)} +

+ + ) : ( +
+
+ +
+

{t("common.anonymous")}

+
+ )} +
+ + {typeof response.value === "string" + ? renderHyperlinkedContent(response.value) + : response.value} + + + {timeSince(new Date(response.updatedAt).toISOString(), locale)} + +
+ ))} +
+ + {visibleResponses < questionSummary.samples.length && ( +
+ +
+ )} +
+ )}
); }; diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/RatingSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/RatingSummary.tsx index 146f929575..24cd83e78e 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/RatingSummary.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/RatingSummary.tsx @@ -11,6 +11,7 @@ import { TSurveyQuestionTypeEnum, } from "@formbricks/types/surveys/types"; import { convertFloatToNDecimal } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/utils"; +import { EmptyState } from "@/modules/ui/components/empty-state"; import { ProgressBar } from "@/modules/ui/components/progress-bar"; import { RatingResponse } from "@/modules/ui/components/rating-response"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/modules/ui/components/tabs"; @@ -84,11 +85,7 @@ export const RatingSummary = ({ questionSummary, survey, setFilter }: RatingSumm
{questionSummary.responseCount === 0 ? ( <> -
-

- {t("environments.surveys.summary.no_responses_found")} -

-
+ >; @@ -31,6 +32,7 @@ const formatTime = (ttc) => { export const SummaryMetadata = ({ surveySummary, + quotasCount, isLoading, tab, setTab, @@ -61,7 +63,7 @@ export const SummaryMetadata = ({
0 && "2xl:grid-cols-6" )}> - {isQuotasAllowed && ( + {isQuotasAllowed && quotasCount > 0 && ( - + {t("common.no_result_found")} {options?.map((data) => ( diff --git a/apps/web/app/lib/surveys/surveys.test.ts b/apps/web/app/lib/surveys/surveys.test.ts index 6649e48724..da588beb87 100644 --- a/apps/web/app/lib/surveys/surveys.test.ts +++ b/apps/web/app/lib/surveys/surveys.test.ts @@ -268,6 +268,64 @@ describe("surveys", () => { expect(sourceFilterOption).toBeDefined(); expect(sourceFilterOption?.filterOptions).toEqual(["Equals", "Not equals"]); }); + + test("should include quota options in filter options when quotas are provided", () => { + const survey = { + id: "survey1", + name: "Test Survey", + questions: [], + createdAt: new Date(), + updatedAt: new Date(), + environmentId: "env1", + status: "draft", + } as unknown as TSurvey; + + const quotas = [{ id: "quota1" }]; + + const result = generateQuestionAndFilterOptions(survey, undefined, {}, {}, {}, quotas as any); + + const quotaFilterOption = result.questionFilterOptions.find((o) => o.id === "quota1"); + expect(quotaFilterOption).toBeDefined(); + expect(quotaFilterOption?.type).toBe("Quotas"); + expect(quotaFilterOption?.filterOptions).toEqual(["Status"]); + expect(quotaFilterOption?.filterComboBoxOptions).toEqual([ + "Screened in", + "Screened out (overquota)", + "Not in quota", + ]); + }); + + test("should include multiple quota options when multiple quotas are provided", () => { + const survey = { + id: "survey1", + name: "Test Survey", + questions: [], + createdAt: new Date(), + updatedAt: new Date(), + environmentId: "env1", + status: "draft", + } as unknown as TSurvey; + + const quotas = [{ id: "quota1" }, { id: "quota2" }]; + + const result = generateQuestionAndFilterOptions(survey, undefined, {}, {}, {}, quotas as any); + + const quota1 = result.questionFilterOptions.find((o) => o.id === "quota1"); + const quota2 = result.questionFilterOptions.find((o) => o.id === "quota2"); + + expect(quota1).toBeDefined(); + expect(quota2).toBeDefined(); + expect(quota1?.filterComboBoxOptions).toEqual([ + "Screened in", + "Screened out (overquota)", + "Not in quota", + ]); + expect(quota2?.filterComboBoxOptions).toEqual([ + "Screened in", + "Screened out (overquota)", + "Not in quota", + ]); + }); }); describe("getFormattedFilters", () => { @@ -867,6 +925,75 @@ describe("surveys", () => { expect(result.meta?.url).toEqual({ op: "contains", value: "formbricks.com" }); expect(result.meta?.source).toEqual({ op: "equals", value: "newsletter" }); }); + + test("should filter by quota with screened in status", () => { + const selectedFilter: SelectedFilterValue = { + responseStatus: "all", + filter: [ + { + questionType: { type: "Quotas", label: "Quota 1", id: "quota1" }, + filterType: { filterComboBoxValue: "Screened in" }, + }, + ], + } as any; + + const result = getFormattedFilters(survey, selectedFilter, {} as any); + + expect(result.quotas?.quota1).toEqual({ op: "screenedIn" }); + }); + + test("should filter by quota with screened out status", () => { + const selectedFilter: SelectedFilterValue = { + responseStatus: "all", + filter: [ + { + questionType: { type: "Quotas", label: "Quota 1", id: "quota1" }, + filterType: { filterComboBoxValue: "Screened out (overquota)" }, + }, + ], + } as any; + + const result = getFormattedFilters(survey, selectedFilter, {} as any); + + expect(result.quotas?.quota1).toEqual({ op: "screenedOut" }); + }); + + test("should filter by quota with not in quota status", () => { + const selectedFilter: SelectedFilterValue = { + responseStatus: "all", + filter: [ + { + questionType: { type: "Quotas", label: "Quota 1", id: "quota1" }, + filterType: { filterComboBoxValue: "Not in quota" }, + }, + ], + } as any; + + const result = getFormattedFilters(survey, selectedFilter, {} as any); + + expect(result.quotas?.quota1).toEqual({ op: "screenedOutNotInQuota" }); + }); + + test("should filter by multiple quotas with different statuses", () => { + const selectedFilter: SelectedFilterValue = { + responseStatus: "all", + filter: [ + { + questionType: { type: "Quotas", label: "Quota 1", id: "quota1" }, + filterType: { filterComboBoxValue: "Screened in" }, + }, + { + questionType: { type: "Quotas", label: "Quota 2", id: "quota2" }, + filterType: { filterComboBoxValue: "Not in quota" }, + }, + ], + } as any; + + const result = getFormattedFilters(survey, selectedFilter, {} as any); + + expect(result.quotas?.quota1).toEqual({ op: "screenedIn" }); + expect(result.quotas?.quota2).toEqual({ op: "screenedOutNotInQuota" }); + }); }); describe("getTodayDate", () => { diff --git a/apps/web/app/lib/surveys/surveys.ts b/apps/web/app/lib/surveys/surveys.ts index 013bdd5ef7..289c7e6cad 100644 --- a/apps/web/app/lib/surveys/surveys.ts +++ b/apps/web/app/lib/surveys/surveys.ts @@ -236,7 +236,7 @@ export const generateQuestionAndFilterOptions = ( questionFilterOptions.push({ type: "Quotas", filterOptions: ["Status"], - filterComboBoxOptions: ["Screened in", "Screened out (overquota)", "Screened out (not in quota)"], + filterComboBoxOptions: ["Screened in", "Screened out (overquota)", "Not in quota"], id: quota.id, }); }); @@ -549,7 +549,7 @@ export const getFormattedFilters = ( const statusMap: Record = { "Screened in": "screenedIn", "Screened out (overquota)": "screenedOut", - "Screened out (not in quota)": "screenedOutNotInQuota", + "Not in quota": "screenedOutNotInQuota", }; const op = statusMap[String(filterType.filterComboBoxValue)]; if (op) filters.quotas[quotaId] = { op }; diff --git a/apps/web/app/lib/templates.ts b/apps/web/app/lib/templates.ts index 9f69a5368b..0b917a17f1 100644 --- a/apps/web/app/lib/templates.ts +++ b/apps/web/app/lib/templates.ts @@ -2073,7 +2073,7 @@ const careerDevelopmentSurvey = (t: TFunction): TTemplate => { return buildSurvey( { name: t("templates.career_development_survey_name"), - role: "productManager", + role: "peopleManager", industries: ["saas", "eCommerce", "other"], channels: ["link"], description: t("templates.career_development_survey_description"), @@ -2160,7 +2160,7 @@ const professionalDevelopmentSurvey = (t: TFunction): TTemplate => { return buildSurvey( { name: t("templates.professional_development_survey_name"), - role: "productManager", + role: "peopleManager", industries: ["saas", "eCommerce", "other"], channels: ["link"], description: t("templates.professional_development_survey_description"), diff --git a/apps/web/i18n.lock b/apps/web/i18n.lock index 82337fef0e..b43547f28d 100644 --- a/apps/web/i18n.lock +++ b/apps/web/i18n.lock @@ -304,7 +304,7 @@ checksums: common/project_not_found: be3b516c02b05553acb4ae338511f645 common/project_permission_not_found: ace6b03f06bd14e884e4295c5022d61b common/projects: fe8af5cfb3c95cb35534872a325b225e - common/question: 0576462ce60d4263d7c482463fcc9547 + common/question: 2a47e06b62410b16003c4979dee0099f common/question_id: d0c3672976c281411bdccf749faf5ffd common/questions: 38d08215fd7a8026077c7b64eea6bb59 common/quota: edd33b180b463ee7a70a64a5c4ad7f02 @@ -748,8 +748,11 @@ checksums: environments/project/app-connection/how_to_setup_description: 2ae5cd9456a8acd3986e3d3678e70ed2 environments/project/app-connection/receiving_data: 9f2a48c0b0278861add70b526061264c environments/project/app-connection/recheck: f95f2bbe6990a123d60255c87bdd59f7 + environments/project/app-connection/sdk_connection_details: 89f2c169fd1604c1df5a834517f1eae1 + environments/project/app-connection/sdk_connection_details_description: d9b5d06776a139aef6fc8ed53d71bf0a environments/project/app-connection/setup_alert_description: 6d676044d01dc2147731ffab7df6c259 environments/project/app-connection/setup_alert_title: 9561cca2b391e0df81e8a982921ff2bb + environments/project/app-connection/webapp_url: d64d8cc3c4c4ecce780d94755f7e4de9 environments/project/general/cannot_delete_only_project: 24751701a42d8b4d2ba6112a5f642bad environments/project/general/delete_project: e4a2a227105c4ec71e561ab1f140eb26 environments/project/general/delete_project_confirmation: 54a4ee78867537e0244c7170453cdb3f diff --git a/apps/web/locales/de-DE.json b/apps/web/locales/de-DE.json index 5d1b9a15e1..c6ea45b1b3 100644 --- a/apps/web/locales/de-DE.json +++ b/apps/web/locales/de-DE.json @@ -801,8 +801,11 @@ "how_to_setup_description": "Befolge diese Schritte, um das Formbricks Widget in deiner App einzurichten.", "receiving_data": "Daten werden empfangen 💃🕺", "recheck": "Erneut prüfen", + "sdk_connection_details": "SDK-Verbindungsdetails", + "sdk_connection_details_description": "Deine eindeutige Umgebungs-ID und SDK-Verbindungs-URL zur Integration von Formbricks mit deiner Anwendung.", "setup_alert_description": "Befolge dieses Schritt-für-Schritt-Tutorial, um deine App oder Website in weniger als 5 Minuten zu verbinden.", - "setup_alert_title": "Wie man verbindet" + "setup_alert_title": "Wie man verbindet", + "webapp_url": "SDK-Verbindungs-URL" }, "general": { "cannot_delete_only_project": "Dies ist dein einziges Projekt, es kann nicht gelöscht werden. Erstelle zuerst ein neues Projekt.", diff --git a/apps/web/locales/en-US.json b/apps/web/locales/en-US.json index f682ede8b1..cfb21bd49c 100644 --- a/apps/web/locales/en-US.json +++ b/apps/web/locales/en-US.json @@ -331,7 +331,7 @@ "project_not_found": "Project not found", "project_permission_not_found": "Project permission not found", "projects": "Projects", - "question": "Question", + "question": "question", "question_id": "Question ID", "questions": "Questions", "quota": "Quota", @@ -794,6 +794,8 @@ "cache_update_delay_title": "Changes will be reflected after ~1 minute due to caching", "environment_id": "Your Environment ID", "environment_id_description": "This id uniquely identifies this Formbricks environment.", + "sdk_connection_details": "SDK Connection Details", + "sdk_connection_details_description": "Your unique environment ID and SDK connection URL for integrating Formbricks with your application.", "formbricks_sdk_connected": "Formbricks SDK is connected", "formbricks_sdk_not_connected": "Formbricks SDK is not yet connected.", "formbricks_sdk_not_connected_description": "Add the Formbricks SDK to your website or app to connect it with Formbricks", @@ -802,7 +804,8 @@ "receiving_data": "Receiving data \uD83D\uDC83\uD83D\uDD7A", "recheck": "Re-check", "setup_alert_description": "Follow this step-by-step tutorial to connect your app or website in under 5 minutes.", - "setup_alert_title": "How to connect" + "setup_alert_title": "How to connect", + "webapp_url": "SDK Connection URL" }, "general": { "cannot_delete_only_project": "This is your only project, it cannot be deleted. Create a new project first.", diff --git a/apps/web/locales/es-ES.json b/apps/web/locales/es-ES.json index db6ca1aa85..bf2d5e826e 100644 --- a/apps/web/locales/es-ES.json +++ b/apps/web/locales/es-ES.json @@ -331,7 +331,7 @@ "project_not_found": "Proyecto no encontrado", "project_permission_not_found": "Permiso de proyecto no encontrado", "projects": "Proyectos", - "question": "Pregunta", + "question": "pregunta", "question_id": "ID de pregunta", "questions": "Preguntas", "quota": "Cuota", @@ -801,8 +801,11 @@ "how_to_setup_description": "Sigue estos pasos para configurar el widget de Formbricks en tu aplicación.", "receiving_data": "Recibiendo datos 💃🕺", "recheck": "Volver a comprobar", + "sdk_connection_details": "Detalles de conexión del SDK", + "sdk_connection_details_description": "Tu ID de entorno único y URL de conexión del SDK para integrar Formbricks con tu aplicación.", "setup_alert_description": "Sigue este tutorial paso a paso para conectar tu aplicación o sitio web en menos de 5 minutos.", - "setup_alert_title": "Cómo conectar" + "setup_alert_title": "Cómo conectar", + "webapp_url": "URL de conexión del SDK" }, "general": { "cannot_delete_only_project": "Este es tu único proyecto, no se puede eliminar. Crea un proyecto nuevo primero.", diff --git a/apps/web/locales/fr-FR.json b/apps/web/locales/fr-FR.json index cfcb7befaa..62cdfdefd0 100644 --- a/apps/web/locales/fr-FR.json +++ b/apps/web/locales/fr-FR.json @@ -331,7 +331,7 @@ "project_not_found": "Projet non trouvé", "project_permission_not_found": "Autorisation de projet non trouvée", "projects": "Projets", - "question": "Question", + "question": "question", "question_id": "ID de la question", "questions": "Questions", "quota": "Quota", @@ -801,8 +801,11 @@ "how_to_setup_description": "Suivez ces étapes pour configurer le widget Formbricks dans votre application.", "receiving_data": "Réception des données 💃🕺", "recheck": "Réessayer", + "sdk_connection_details": "Détails de connexion SDK", + "sdk_connection_details_description": "Votre ID d'environnement unique et votre URL de connexion SDK pour intégrer Formbricks à votre application.", "setup_alert_description": "Suivez les indications de ce tutoriel pour connecter votre application ou votre site Web en moins de cinq minutes.", - "setup_alert_title": "Connexion" + "setup_alert_title": "Connexion", + "webapp_url": "URL de connexion SDK" }, "general": { "cannot_delete_only_project": "Comme il s'agit de votre seul projet, il ne peut pas être supprimé. Créez d'abord un nouveau projet.", diff --git a/apps/web/locales/ja-JP.json b/apps/web/locales/ja-JP.json index b48f33053b..3cff490ef7 100644 --- a/apps/web/locales/ja-JP.json +++ b/apps/web/locales/ja-JP.json @@ -801,8 +801,11 @@ "how_to_setup_description": "アプリ内でFormbricksウィジェットを設定する手順に従ってください。", "receiving_data": "データ受信中 💃🕺", "recheck": "再チェック", + "sdk_connection_details": "SDK接続詳細", + "sdk_connection_details_description": "FormbricksをアプリケーションとAPI統合するためのEnvironmentIdとSDK接続URL。", "setup_alert_description": "5 分以内でアプリまたはウェブサイト を 接続する手順をステップバイステップ の チュートリアルに従ってください。", - "setup_alert_title": "接続方法" + "setup_alert_title": "接続方法", + "webapp_url": "SDK接続URL" }, "general": { "cannot_delete_only_project": "これは唯一のプロジェクトのため削除できません。まず新しいプロジェクトを作成してください。", diff --git a/apps/web/locales/nl-NL.json b/apps/web/locales/nl-NL.json index 1954cad506..0d01c8d5b6 100644 --- a/apps/web/locales/nl-NL.json +++ b/apps/web/locales/nl-NL.json @@ -331,7 +331,7 @@ "project_not_found": "Project niet gevonden", "project_permission_not_found": "Projecttoestemming niet gevonden", "projects": "Projecten", - "question": "Vraag", + "question": "vraag", "question_id": "Vraag-ID", "questions": "Vragen", "quota": "Quotum", @@ -801,8 +801,11 @@ "how_to_setup_description": "Volg deze stappen om de Formbricks-widget in uw app in te stellen.", "receiving_data": "Gegevens ontvangen 💃🕺", "recheck": "Controleer opnieuw", + "sdk_connection_details": "SDK-verbindingsdetails", + "sdk_connection_details_description": "Uw unieke Environment ID en SDK-verbindings-URL voor integratie van Formbricks met uw applicatie.", "setup_alert_description": "Volg deze stapsgewijze handleiding om uw app of website in minder dan 5 minuten te verbinden.", - "setup_alert_title": "Hoe te verbinden" + "setup_alert_title": "Hoe te verbinden", + "webapp_url": "SDK-verbindings-URL" }, "general": { "cannot_delete_only_project": "Dit is uw enige project. Het kan niet worden verwijderd. Maak eerst een nieuw project aan.", diff --git a/apps/web/locales/pt-BR.json b/apps/web/locales/pt-BR.json index 72f42b4e4f..63f54bd9c2 100644 --- a/apps/web/locales/pt-BR.json +++ b/apps/web/locales/pt-BR.json @@ -331,7 +331,7 @@ "project_not_found": "Projeto não encontrado", "project_permission_not_found": "Permissão do projeto não encontrada", "projects": "Projetos", - "question": "Pergunta", + "question": "pergunta", "question_id": "ID da Pergunta", "questions": "Perguntas", "quota": "Cota", @@ -801,8 +801,11 @@ "how_to_setup_description": "Siga esses passos para configurar o widget do Formbricks no seu app.", "receiving_data": "Recebendo dados 💃🕺", "recheck": "Verificar novamente", + "sdk_connection_details": "Detalhes de Conexão do SDK", + "sdk_connection_details_description": "Seu ID de ambiente único e URL de conexão do SDK para integrar o Formbricks com seu aplicativo.", "setup_alert_description": "Siga este tutorial passo a passo para conectar seu app ou site em menos de 5 minutos.", - "setup_alert_title": "Como conectar" + "setup_alert_title": "Como conectar", + "webapp_url": "URL de conexão do SDK" }, "general": { "cannot_delete_only_project": "Esse é seu único projeto, não pode ser deletado. Crie um novo projeto primeiro.", diff --git a/apps/web/locales/pt-PT.json b/apps/web/locales/pt-PT.json index fbb2bf89fb..0f6109f173 100644 --- a/apps/web/locales/pt-PT.json +++ b/apps/web/locales/pt-PT.json @@ -331,7 +331,7 @@ "project_not_found": "Projeto não encontrado", "project_permission_not_found": "Permissão do projeto não encontrada", "projects": "Projetos", - "question": "Pergunta", + "question": "pergunta", "question_id": "ID da pergunta", "questions": "Perguntas", "quota": "Quota", @@ -801,8 +801,11 @@ "how_to_setup_description": "Siga estes passos para configurar o widget Formbricks na sua aplicação.", "receiving_data": "A receber dados 💃🕺", "recheck": "Verificar novamente", + "sdk_connection_details": "Detalhes de Conexão SDK", + "sdk_connection_details_description": "O seu ID de ambiente único e URL de conexão SDK para integrar o Formbricks com a sua aplicação.", "setup_alert_description": "Siga este tutorial passo a passo para conectar a sua app ou site em menos de 5 minutos", - "setup_alert_title": "Como conectar" + "setup_alert_title": "Como conectar", + "webapp_url": "URL de ligação do SDK" }, "general": { "cannot_delete_only_project": "Este é o seu único projeto, não pode ser eliminado. Crie um novo projeto primeiro.", diff --git a/apps/web/locales/ro-RO.json b/apps/web/locales/ro-RO.json index 6305db2c8b..4804374866 100644 --- a/apps/web/locales/ro-RO.json +++ b/apps/web/locales/ro-RO.json @@ -331,7 +331,7 @@ "project_not_found": "Proiectul nu a fost găsit", "project_permission_not_found": "Permisiunea proiectului nu a fost găsită", "projects": "Proiecte", - "question": "Întrebare", + "question": "întrebare", "question_id": "ID întrebare", "questions": "Întrebări", "quota": "Cotă", @@ -801,8 +801,11 @@ "how_to_setup_description": "Urmează acești pași pentru a configura widget-ul Formbricks în aplicația ta.", "receiving_data": "Recepționare date 💃🕺", "recheck": "Re-verifică", + "sdk_connection_details": "Detalii de conexiune SDK", + "sdk_connection_details_description": "ID-ul mediului tău unic și URL-ul de conexiune SDK pentru a integra Formbricks cu aplicația ta.", "setup_alert_description": "Urmează acest tutorial pas cu pas pentru a-ți conecta aplicația sau site-ul în mai puțin de 5 minute.", - "setup_alert_title": "Cum să conectezi" + "setup_alert_title": "Cum să conectezi", + "webapp_url": "URL de conexiune SDK" }, "general": { "cannot_delete_only_project": "Acesta este singurul tău proiect, nu poate fi șters. Creează mai întâi un proiect nou.", diff --git a/apps/web/locales/zh-Hans-CN.json b/apps/web/locales/zh-Hans-CN.json index 52f258b9da..799f8ad4b5 100644 --- a/apps/web/locales/zh-Hans-CN.json +++ b/apps/web/locales/zh-Hans-CN.json @@ -801,8 +801,11 @@ "how_to_setup_description": "遵循这些步骤在你的应用中设置 Formbricks 小部件。", "receiving_data": "接收 数据 💃🕺", "recheck": "重新检查", + "sdk_connection_details": "SDK 连接详情", + "sdk_connection_details_description": "您唯一的环境 ID 和 SDK 连接 URL,用于将 Formbricks 与您的应用程序集成。", "setup_alert_description": "按照 此 步骤教程 在 5 分钟 以内 连接 你的 应用 或 网站。", - "setup_alert_title": "如何 连接" + "setup_alert_title": "如何 连接", + "webapp_url": "SDK连接URL" }, "general": { "cannot_delete_only_project": "这是 您 唯一的 项目,不可 删除。请 先 创建一个新的 项目。", diff --git a/apps/web/locales/zh-Hant-TW.json b/apps/web/locales/zh-Hant-TW.json index f421c90abd..a4621907b1 100644 --- a/apps/web/locales/zh-Hant-TW.json +++ b/apps/web/locales/zh-Hant-TW.json @@ -801,8 +801,11 @@ "how_to_setup_description": "請按照這些步驟在您的應用程式中設定 Formbricks 小工具。", "receiving_data": "正在接收資料 💃🕺", "recheck": "重新檢查", + "sdk_connection_details": "SDK 連線詳細資訊", + "sdk_connection_details_description": "您的唯一環境 ID 和 SDK 連線 URL,用於將 Formbricks 與您的應用程式整合。", "setup_alert_description": "遵循 此 分步 教程 ,在 5 分鐘 內 將您的應用程式 或 網站 連線 。", - "setup_alert_title": "如何 連線" + "setup_alert_title": "如何 連線", + "webapp_url": "SDK 連接 URL" }, "general": { "cannot_delete_only_project": "這是您唯一的專案,無法刪除。請先建立新專案。", diff --git a/apps/web/modules/ee/quotas/components/quota-condition-builder.tsx b/apps/web/modules/ee/quotas/components/quota-condition-builder.tsx index 0ed50617c4..8b2928a66a 100644 --- a/apps/web/modules/ee/quotas/components/quota-condition-builder.tsx +++ b/apps/web/modules/ee/quotas/components/quota-condition-builder.tsx @@ -18,6 +18,7 @@ interface QuotaConditionBuilderProps { conditions: TSurveyQuotaLogic; onChange: (conditions: TSurveyQuotaLogic) => void; quotaErrors?: FieldErrors; + isSubmitted?: boolean; } export const QuotaConditionBuilder = ({ @@ -25,6 +26,7 @@ export const QuotaConditionBuilder = ({ conditions, onChange, quotaErrors, + isSubmitted, }: QuotaConditionBuilderProps) => { const { t } = useTranslation(); @@ -66,6 +68,7 @@ export const QuotaConditionBuilder = ({ config={config} callbacks={callbacks} quotaErrors={quotaErrors} + isSubmitted={isSubmitted} />
); diff --git a/apps/web/modules/ee/quotas/components/quota-modal.tsx b/apps/web/modules/ee/quotas/components/quota-modal.tsx index 630cb71aae..afbff3c738 100644 --- a/apps/web/modules/ee/quotas/components/quota-modal.tsx +++ b/apps/web/modules/ee/quotas/components/quota-modal.tsx @@ -18,9 +18,13 @@ import { } from "@formbricks/types/quota"; import { TSurvey } from "@formbricks/types/surveys/types"; import { getFormattedErrorMessage } from "@/lib/utils/helper"; +import { replaceHeadlineRecall } from "@/lib/utils/recall"; import { createQuotaAction, updateQuotaAction } from "@/modules/ee/quotas/actions"; import { EndingCardSelector } from "@/modules/ee/quotas/components/ending-card-selector"; -import { getDefaultOperatorForQuestion } from "@/modules/survey/editor/lib/utils"; +import { + getDefaultOperatorForQuestion, + replaceEndingCardHeadlineRecall, +} from "@/modules/survey/editor/lib/utils"; import { Button } from "@/modules/ui/components/button"; import { ConfirmationModal } from "@/modules/ui/components/confirmation-modal"; import { @@ -80,6 +84,15 @@ export const QuotaModal = ({ const { t } = useTranslation(); const [openConfirmationModal, setOpenConfirmationModal] = useState(false); const [openConfirmChangesInInclusionCriteria, setOpenConfirmChangesInInclusionCriteria] = useState(false); + + // Transform survey to replace recall: with actual question headlines + const transformedSurvey = useMemo(() => { + let modifiedSurvey = replaceHeadlineRecall(survey, "default"); + modifiedSurvey = replaceEndingCardHeadlineRecall(modifiedSurvey, "default"); + + return modifiedSurvey; + }, [survey]); + const defaultValues = useMemo(() => { return { name: quota?.name || "", @@ -124,7 +137,7 @@ export const QuotaModal = ({ reset, watch, control, - formState: { isSubmitting, isDirty, errors, isValid }, + formState: { isSubmitting, isDirty, errors, isValid, isSubmitted }, } = form; // Watch form values for conditional logic @@ -312,14 +325,17 @@ export const QuotaModal = ({ render={({ field }) => (
- {t("environments.surveys.edit.quotas.inclusion_criteria")} + {field.value && ( )} diff --git a/apps/web/modules/projects/settings/(setup)/app-connection/page.tsx b/apps/web/modules/projects/settings/(setup)/app-connection/page.tsx index 84f14fe05c..4610c85603 100644 --- a/apps/web/modules/projects/settings/(setup)/app-connection/page.tsx +++ b/apps/web/modules/projects/settings/(setup)/app-connection/page.tsx @@ -3,6 +3,7 @@ import Link from "next/link"; import { WidgetStatusIndicator } from "@/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator"; import { SettingsCard } from "@/app/(app)/environments/[environmentId]/settings/components/SettingsCard"; +import { WEBAPP_URL } from "@/lib/constants"; import { getActionClasses } from "@/lib/actionClass/service"; import { getEnvironments } from "@/lib/environment/service"; import { findMatchingLocale } from "@/lib/utils/locale"; @@ -40,9 +41,12 @@ export const AppConnectionPage = async ({ params }: { params: Promise<{ environm
- + title={t("environments.project.app-connection.sdk_connection_details")} + description={t("environments.project.app-connection.sdk_connection_details_description")}> +
+ + +
- + diff --git a/apps/web/modules/survey/editor/components/update-question-id.tsx b/apps/web/modules/survey/editor/components/update-question-id.tsx index 70d7ec43a8..f1a7faa00c 100644 --- a/apps/web/modules/survey/editor/components/update-question-id.tsx +++ b/apps/web/modules/survey/editor/components/update-question-id.tsx @@ -74,7 +74,7 @@ export const UpdateQuestionId = ({ disabled={localSurvey.status !== "draft" && !question.isDraft} className={`h-10 ${isInputInvalid ? "border-red-300 focus:border-red-300" : ""}`} /> -
diff --git a/apps/web/modules/survey/follow-ups/components/follow-up-email.tsx b/apps/web/modules/survey/follow-ups/components/follow-up-email.tsx index 2fadd55a18..1f4fb3a031 100644 --- a/apps/web/modules/survey/follow-ups/components/follow-up-email.tsx +++ b/apps/web/modules/survey/follow-ups/components/follow-up-email.tsx @@ -18,6 +18,7 @@ import { TResponse } from "@formbricks/types/responses"; import { TSurvey } from "@formbricks/types/surveys/types"; import { FB_LOGO_URL, IMPRINT_ADDRESS, IMPRINT_URL, PRIVACY_URL } from "@/lib/constants"; import { getQuestionResponseMapping } from "@/lib/responses"; +import { parseRecallInfo } from "@/lib/utils/recall"; import { getTranslate } from "@/lingodotdev/server"; import { renderEmailResponseValue } from "@/modules/email/emails/lib/utils"; @@ -34,7 +35,10 @@ interface FollowUpEmailProps { export async function FollowUpEmail(props: FollowUpEmailProps): Promise { const { properties } = props.followUp.action; - const { body } = properties; + let { body } = properties; + + // Parse recall tags and replace with actual response values + body = parseRecallInfo(body, props.response.data, props.response.variables); const questions = props.attachResponseData ? getQuestionResponseMapping(props.survey, props.response) : []; const t = await getTranslate(); diff --git a/apps/web/modules/survey/follow-ups/components/follow-up-modal.tsx b/apps/web/modules/survey/follow-ups/components/follow-up-modal.tsx index 93b78504ae..066a55434c 100644 --- a/apps/web/modules/survey/follow-ups/components/follow-up-modal.tsx +++ b/apps/web/modules/survey/follow-ups/components/follow-up-modal.tsx @@ -801,6 +801,9 @@ export const FollowUpModal = ({ } }} isInvalid={!!formErrors.body} + localSurvey={localSurvey} + questionId="follow-up" + selectedLanguageCode={selectedLanguageCode} /> diff --git a/apps/web/modules/ui/components/conditions-editor/index.tsx b/apps/web/modules/ui/components/conditions-editor/index.tsx index fb9f9d2f28..aecfb822fe 100644 --- a/apps/web/modules/ui/components/conditions-editor/index.tsx +++ b/apps/web/modules/ui/components/conditions-editor/index.tsx @@ -35,6 +35,7 @@ interface ConditionsEditorProps { callbacks: TConditionsEditorCallbacks; depth?: number; quotaErrors?: FieldErrors; + isSubmitted?: boolean; } export function ConditionsEditor({ @@ -43,6 +44,7 @@ export function ConditionsEditor({ callbacks, depth = 0, quotaErrors, + isSubmitted = false, }: Readonly) { const { t } = useTranslation(); const [parent] = useAutoAnimate(); @@ -256,7 +258,7 @@ export function ConditionsEditor({
- {quotaError &&

{quotaError}

} + {quotaError && isSubmitted &&

{quotaError}

}
); }; diff --git a/apps/web/modules/ui/components/editor/styles-editor-frontend.css b/apps/web/modules/ui/components/editor/styles-editor-frontend.css index 9e372b7950..8531c887c0 100644 --- a/apps/web/modules/ui/components/editor/styles-editor-frontend.css +++ b/apps/web/modules/ui/components/editor/styles-editor-frontend.css @@ -29,7 +29,6 @@ .fb-editor-heading-h1 { font-size: 25px !important; - font-weight: 400 !important; margin-bottom: 20px !important; font-weight: bold !important; } diff --git a/apps/web/modules/ui/components/empty-state/index.tsx b/apps/web/modules/ui/components/empty-state/index.tsx index e23dc6ed82..e0d3799b84 100644 --- a/apps/web/modules/ui/components/empty-state/index.tsx +++ b/apps/web/modules/ui/components/empty-state/index.tsx @@ -2,9 +2,18 @@ interface EmptyStateProps { text: string; + variant?: "default" | "simple"; } -export const EmptyState = ({ text }: EmptyStateProps) => { +export const EmptyState = ({ text, variant = "default" }: EmptyStateProps) => { + if (variant === "simple") { + return ( +
+

{text}

+
+ ); + } + return (