diff --git a/.husky/post-checkout b/.husky/post-checkout deleted file mode 100644 index f8efd47397..0000000000 --- a/.husky/post-checkout +++ /dev/null @@ -1,2 +0,0 @@ -echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ./branch.json -prettier --write ./branch.json diff --git a/apps/storybook/package.json b/apps/storybook/package.json index 4e9a524282..0dca3377ff 100644 --- a/apps/storybook/package.json +++ b/apps/storybook/package.json @@ -10,25 +10,20 @@ "build-storybook": "storybook build", "clean": "rimraf .turbo node_modules dist storybook-static" }, - "dependencies": { - "@formbricks/survey-ui": "workspace:*" - }, "devDependencies": { - "@chromatic-com/storybook": "^5.0.0", - "@storybook/addon-a11y": "10.1.11", - "@storybook/addon-links": "10.1.11", - "@storybook/addon-onboarding": "10.1.11", - "@storybook/react-vite": "10.1.11", - "@typescript-eslint/eslint-plugin": "8.53.0", - "@tailwindcss/vite": "4.1.18", - "@typescript-eslint/parser": "8.53.0", - "@vitejs/plugin-react": "5.1.2", - "esbuild": "0.25.12", + "@chromatic-com/storybook": "^5.0.1", + "@storybook/addon-a11y": "10.2.14", + "@storybook/addon-links": "10.2.14", + "@storybook/addon-onboarding": "10.2.14", + "@storybook/react-vite": "10.2.14", + "@typescript-eslint/eslint-plugin": "8.56.1", + "@tailwindcss/vite": "4.2.1", + "@typescript-eslint/parser": "8.56.1", + "@vitejs/plugin-react": "5.1.4", "eslint-plugin-react-refresh": "0.4.26", - "eslint-plugin-storybook": "10.1.11", - "prop-types": "15.8.1", - "storybook": "10.1.11", + "eslint-plugin-storybook": "10.2.14", + "storybook": "10.2.14", "vite": "7.3.1", - "@storybook/addon-docs": "10.1.11" + "@storybook/addon-docs": "10.2.14" } } diff --git a/apps/web/.prettierrc.js b/apps/web/.prettierrc.js new file mode 100644 index 0000000000..21886d1fe2 --- /dev/null +++ b/apps/web/.prettierrc.js @@ -0,0 +1,6 @@ +const baseConfig = require("../../.prettierrc.js"); + +module.exports = { + ...baseConfig, + tailwindConfig: "./tailwind.config.js", +}; diff --git a/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/components/ConnectWithFormbricks.tsx b/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/components/ConnectWithFormbricks.tsx index 293224138e..d5b528b4b3 100644 --- a/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/components/ConnectWithFormbricks.tsx +++ b/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/components/ConnectWithFormbricks.tsx @@ -69,7 +69,7 @@ export const ConnectWithFormbricks = ({ ) : (
- +

diff --git a/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/page.tsx b/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/page.tsx index 32b9550ed1..46e5652be5 100644 --- a/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/page.tsx +++ b/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/page.tsx @@ -46,7 +46,7 @@ const Page = async (props: ConnectPageProps) => { channel={channel} />

-
+
{logoUrl && ( )}

{t("common.preview")}

-
- file.name} - autoFocus={false} - /> -
+ file.name} + autoFocus={false} + />
{ /> {projects.length >= 1 && (

{currentStatus.title}

-

{currentStatus.subtitle}

+

{currentStatus.subtitle}

{status === "notImplemented" && ( )} @@ -51,7 +51,7 @@ const Loading = () => {
-
+
diff --git a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/loading.tsx b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/loading.tsx index c1b8d97891..68c46c0573 100644 --- a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/loading.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/loading.tsx @@ -10,7 +10,7 @@ const Loading = () => {
-
@@ -48,7 +48,7 @@ const Loading = () => {
-
+
diff --git a/apps/web/app/api/v1/client/og/route.tsx b/apps/web/app/api/v1/client/og/route.tsx index ab585f2e34..d812f3c659 100644 --- a/apps/web/app/api/v1/client/og/route.tsx +++ b/apps/web/app/api/v1/client/og/route.tsx @@ -6,140 +6,138 @@ export const GET = async (req: NextRequest) => { let brandColor = req.nextUrl.searchParams.get("brandColor"); return new ImageResponse( - ( +
+
+
-
-
-
-
+
+
+

+ {name} +

+
+
+
+
+
+
+
-

- {name} -

-
-
-
-
-
-
-
-
- Begin! -
+ Begin!
- ), +
, { width: 800, height: 400, diff --git a/apps/web/app/lib/api/with-api-logging.test.ts b/apps/web/app/lib/api/with-api-logging.test.ts index 6aa388f9b7..4fdf24e4b8 100644 --- a/apps/web/app/lib/api/with-api-logging.test.ts +++ b/apps/web/app/lib/api/with-api-logging.test.ts @@ -131,13 +131,11 @@ describe("withV1ApiWrapper", () => { }); test("logs and audits on error response with API key authentication", async () => { - const { queueAuditEvent: mockedQueueAuditEvent } = (await import( - "@/modules/ee/audit-logs/lib/handler" - )) as unknown as { queueAuditEvent: Mock }; + const { queueAuditEvent: mockedQueueAuditEvent } = + (await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; const { authenticateRequest } = await import("@/app/api/v1/auth"); - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); @@ -185,13 +183,11 @@ describe("withV1ApiWrapper", () => { }); test("does not log Sentry if not 500", async () => { - const { queueAuditEvent: mockedQueueAuditEvent } = (await import( - "@/modules/ee/audit-logs/lib/handler" - )) as unknown as { queueAuditEvent: Mock }; + const { queueAuditEvent: mockedQueueAuditEvent } = + (await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; const { authenticateRequest } = await import("@/app/api/v1/auth"); - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); @@ -233,13 +229,11 @@ describe("withV1ApiWrapper", () => { }); test("logs and audits on thrown error", async () => { - const { queueAuditEvent: mockedQueueAuditEvent } = (await import( - "@/modules/ee/audit-logs/lib/handler" - )) as unknown as { queueAuditEvent: Mock }; + const { queueAuditEvent: mockedQueueAuditEvent } = + (await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; const { authenticateRequest } = await import("@/app/api/v1/auth"); - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); @@ -291,13 +285,11 @@ describe("withV1ApiWrapper", () => { }); test("does not log on success response but still audits", async () => { - const { queueAuditEvent: mockedQueueAuditEvent } = (await import( - "@/modules/ee/audit-logs/lib/handler" - )) as unknown as { queueAuditEvent: Mock }; + const { queueAuditEvent: mockedQueueAuditEvent } = + (await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; const { authenticateRequest } = await import("@/app/api/v1/auth"); - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); @@ -347,13 +339,11 @@ describe("withV1ApiWrapper", () => { REDIS_URL: "redis://localhost:6379", })); - const { queueAuditEvent: mockedQueueAuditEvent } = (await import( - "@/modules/ee/audit-logs/lib/handler" - )) as unknown as { queueAuditEvent: Mock }; + const { queueAuditEvent: mockedQueueAuditEvent } = + (await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; const { authenticateRequest } = await import("@/app/api/v1/auth"); - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); const { withV1ApiWrapper } = await import("./with-api-logging"); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); @@ -376,9 +366,8 @@ describe("withV1ApiWrapper", () => { }); test("handles client-side API routes without authentication", async () => { - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); const { authenticateRequest } = await import("@/app/api/v1/auth"); const { applyIPRateLimit } = await import("@/modules/core/rate-limit/helpers"); @@ -410,9 +399,8 @@ describe("withV1ApiWrapper", () => { }); test("returns authentication error for non-client routes without auth", async () => { - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); const { authenticateRequest } = await import("@/app/api/v1/auth"); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); @@ -435,9 +423,8 @@ describe("withV1ApiWrapper", () => { test("handles rate limiting errors", async () => { const { applyRateLimit } = await import("@/modules/core/rate-limit/helpers"); - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); const { authenticateRequest } = await import("@/app/api/v1/auth"); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); @@ -462,13 +449,11 @@ describe("withV1ApiWrapper", () => { }); test("skips audit log creation when no action/targetType provided", async () => { - const { queueAuditEvent: mockedQueueAuditEvent } = (await import( - "@/modules/ee/audit-logs/lib/handler" - )) as unknown as { queueAuditEvent: Mock }; + const { queueAuditEvent: mockedQueueAuditEvent } = + (await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; const { authenticateRequest } = await import("@/app/api/v1/auth"); - const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import( - "@/app/middleware/endpoint-validator" - ); + const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = + await import("@/app/middleware/endpoint-validator"); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); diff --git a/apps/web/app/lib/templates.ts b/apps/web/app/lib/templates.ts index 4e70295f74..3e0629ae99 100644 --- a/apps/web/app/lib/templates.ts +++ b/apps/web/app/lib/templates.ts @@ -4854,6 +4854,17 @@ export const previewSurvey = (projectName: string, t: TFunction): TSurvey => { }), isDraft: true, }, + { + ...buildOpenTextElement({ + id: "preview-open-text-01", + headline: t("templates.preview_survey_question_open_text_headline"), + subheader: t("templates.preview_survey_question_open_text_subheader"), + placeholder: t("templates.preview_survey_question_open_text_placeholder"), + inputType: "text", + required: false, + }), + isDraft: true, + }, ], buttonLabel: createI18nString(t("templates.next"), []), backButtonLabel: createI18nString(t("templates.preview_survey_question_2_back_button_label"), []), diff --git a/apps/web/app/middleware/endpoint-validator.test.ts b/apps/web/app/middleware/endpoint-validator.test.ts index 7e032e1a03..4ef7df3e78 100644 --- a/apps/web/app/middleware/endpoint-validator.test.ts +++ b/apps/web/app/middleware/endpoint-validator.test.ts @@ -313,7 +313,7 @@ describe("endpoint-validator", () => { expect(isPublicDomainRoute("/c")).toBe(false); expect(isPublicDomainRoute("/contact/token")).toBe(false); }); - + test("should return true for pretty URL survey routes", () => { expect(isPublicDomainRoute("/p/pretty123")).toBe(true); expect(isPublicDomainRoute("/p/pretty-name-with-dashes")).toBe(true); diff --git a/apps/web/i18n.lock b/apps/web/i18n.lock index 3ddf835c85..a8503ddd77 100644 --- a/apps/web/i18n.lock +++ b/apps/web/i18n.lock @@ -164,6 +164,7 @@ checksums: common/days: c95fe8aedde21a0b5653dbd0b3c58b48 common/default: d9c6dc5c412fe94143dfd1d332ec81d4 common/delete: 8bcf303dd10a645b5baacb02b47d72c9 + common/delete_what: 718ddfcc1dec7f3e8b67856fba838267 common/description: e17686a22ffad04cc7bb70524ed4478b common/dev_env: e650911d5e19ba256358e0cda154c005 common/development: 85211dbb918bda7a6e87649dcfc1b17a @@ -260,6 +261,7 @@ checksums: common/move_down: 4f4de55743043355ad4a839aff2c48ff common/move_up: 69f25b205c677abdb26cbb69d97cd10b common/multiple_languages: 7d8ddd4b40d32fcd7bd6f7bac6485b1f + common/my_product: ad022177062f9ef6e9acf33b13e889aa common/name: 9368b5a047572b6051f334af5aa76819 common/new: 126d036fae5fb6b629728ecb97e6195b common/new_version_available: 399ddfc4232712e18ddab2587356b3dc @@ -1022,7 +1024,7 @@ checksums: environments/settings/general/email_customization_preview_email_heading: 8b798cb8438b3dd356c02dab33b4c897 environments/settings/general/email_customization_preview_email_text: fa6ae92403cc8f3c35c03e6c94cbde51 environments/settings/general/error_deleting_organization_please_try_again: 7f0fe257d4a0b40bff025408a7766706 - environments/settings/general/from_your_organization: 4b7970431edb3d0f13c394dbd755a055 + environments/settings/general/from_your_organization: 9ebd6dcd79f7bfad3fea46ed2e3133d2 environments/settings/general/invitation_sent_once_more: e6e5ea066810f9dcb65788aa4f05d6e2 environments/settings/general/invite_deleted_successfully: 1c7dca6d0f6870d945288e38cfd2f943 environments/settings/general/invite_expires_on: 6fd2356ad91a5f189070c43855904bb4 @@ -1177,6 +1179,7 @@ checksums: environments/surveys/edit/add_fallback_placeholder: 0e77ea487ddd7bc7fc2f1574b018dc08 environments/surveys/edit/add_hidden_field_id: a8f55b51b790cf5f4d898af7770ad1ed environments/surveys/edit/add_highlight_border: 66f52b21fbb9aa6561c98a090abaaf8f + environments/surveys/edit/add_highlight_border_description: fe548fe03ea10ef5cd9e553d6812b3c2 environments/surveys/edit/add_logic: f234c9f1393a9ed4792dfbd15838c951 environments/surveys/edit/add_none_of_the_above: dbe1ada4512d6c3f80c54c8fac107ec6 environments/surveys/edit/add_option: 143c54f0b201067fe5159284d6daeca2 @@ -1375,7 +1378,6 @@ checksums: environments/surveys/edit/follow_ups_modal_updated_successfull_toast: 61204fada3231f4f1fe3866e87e1130a environments/surveys/edit/follow_ups_new: 224c779d252b3e75086e4ed456ba2548 environments/surveys/edit/follow_ups_upgrade_button_text: 4cd167527fc6cdb5b0bfc9b486b142a8 - environments/surveys/edit/form_styling: 1278a2db4257b5500474161133acc857 environments/surveys/edit/formbricks_sdk_is_not_connected: 35165b0cac182a98408007a378cc677e environments/surveys/edit/four_points: b289628a6b8a6cd0f7d17a14ca6cd7bf environments/surveys/edit/heading: 79e9dfa461f38a239d34b9833ca103f1 @@ -1546,7 +1548,7 @@ checksums: environments/surveys/edit/response_limits_redirections_and_more: e4f1cf94e56ad0e1b08701158d688802 environments/surveys/edit/response_options: 2988136d5248d7726583108992dcbaee environments/surveys/edit/roundness: 5a161c8f5f258defb57ed1d551737cc4 - environments/surveys/edit/roundness_description: bde131aa5674836416dcdf2ff517d899 + environments/surveys/edit/roundness_description: 03940a6871ae43efa4810cba7cadb74b environments/surveys/edit/row_used_in_logic_error: f89453ff1b6db77ad84af840fedd9813 environments/surveys/edit/rows: 8f41f34e6ca28221cf1ebd948af4c151 environments/surveys/edit/save_and_close: 6ede705b3f82f30269ff3054a5049e34 @@ -1592,6 +1594,7 @@ checksums: environments/surveys/edit/survey_completed_subheading: db537c356c3ab6564d24de0d11a0fee2 environments/surveys/edit/survey_display_settings: 8ed19e6a8e1376f7a1ba037d82c4ae11 environments/surveys/edit/survey_placement: 083c10f257337f9648bf9d435b18ec2c + environments/surveys/edit/survey_styling: 7f96d6563e934e65687b74374a33b1dc environments/surveys/edit/survey_trigger: f0c7014a684ca566698b87074fad5579 environments/surveys/edit/switch_multi_language_on_to_get_started: cca0ef91ee49095da30cd1e3f26c406f environments/surveys/edit/target_block_not_found: 0a0c401017ab32364fec2fcbf815d832 @@ -1680,7 +1683,6 @@ checksums: environments/surveys/edit/welcome_message: 986a434e3895c8ee0b267df95cc40051 environments/surveys/edit/without_a_filter_all_of_your_users_can_be_surveyed: 451990569c61f25d01044cc45b1ce122 environments/surveys/edit/you_have_not_created_a_segment_yet: c6658bd1cee9c5c957c675db044708dd - environments/surveys/edit/you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations: 04241177ba989ef4c1d8c01e1a7b8541 environments/surveys/edit/your_description_here_recall_information_with: 60f73a3cc9bdb9afea2166a7db8fd618 environments/surveys/edit/your_question_here_recall_information_with: 6395bd54f5167830c9d662ba403da167 environments/surveys/edit/your_web_app: 07234bed03a33330dc50ae9fcf0174f3 @@ -2045,7 +2047,7 @@ checksums: environments/workspace/look/advanced_styling_field_description_size: a0d51c3ab7dc56320ecedc2b27917842 environments/workspace/look/advanced_styling_field_description_size_description: ff880ea1beddd1b1ec7416d0b8a69cf3 environments/workspace/look/advanced_styling_field_description_weight: 514680cc7202ad29835c1cbcde3def1c - environments/workspace/look/advanced_styling_field_description_weight_description: 441ac8db1a32557813eb68fbfd759061 + environments/workspace/look/advanced_styling_field_description_weight_description: aa95bc81b5336a548e256bce49350683 environments/workspace/look/advanced_styling_field_font_size: ca44d14429b2175a1b194793b4ab8f6b environments/workspace/look/advanced_styling_field_font_weight: bfef83778146cf40550df9650d8a07da environments/workspace/look/advanced_styling_field_headline_color: 4ccf3935ad90c88ad4add24f498673ce @@ -2059,7 +2061,7 @@ checksums: environments/workspace/look/advanced_styling_field_indicator_bg_description: 7eb3b54a8b331354ec95c0dc1545c620 environments/workspace/look/advanced_styling_field_input_border_radius_description: 0007f1bb572b35d9a3720daeb7a55617 environments/workspace/look/advanced_styling_field_input_font_size_description: 5311f95dcbd083623e35c98ea5374c3b - environments/workspace/look/advanced_styling_field_input_height_description: e19ec0dc432478def0fd1199ad765e38 + environments/workspace/look/advanced_styling_field_input_height_description: bb7439d42ec3848a8fa9edb8b001b69a environments/workspace/look/advanced_styling_field_input_padding_x_description: 10e14296468321c13fda77fd1ba58dfd environments/workspace/look/advanced_styling_field_input_padding_y_description: 98b4aeff2940516d05ea61bdc1211d0d environments/workspace/look/advanced_styling_field_input_placeholder_opacity_description: f55a6700884d24014404e58876121ddf @@ -2068,6 +2070,8 @@ checksums: environments/workspace/look/advanced_styling_field_input_text_description: 460450df24ea0cc902710118a5000feb environments/workspace/look/advanced_styling_field_option_bg: 0ceaed10d99ed4ad83cb0934ab970174 environments/workspace/look/advanced_styling_field_option_bg_description: 6cd6ccecbbb9f2f19439d7c682eb67c1 + environments/workspace/look/advanced_styling_field_option_border: aa478eb148515b6a2637fb144ff72028 + environments/workspace/look/advanced_styling_field_option_border_description: 8f75b740e8dcb7f6cfeff2e5d5ca7c92 environments/workspace/look/advanced_styling_field_option_border_radius_description: 23f81c25b2681a7c9e2c4f2e7d2e0656 environments/workspace/look/advanced_styling_field_option_font_size_description: 5430fd9b08819972f0a613bf3fa659da environments/workspace/look/advanced_styling_field_option_label: 2767a5db32742073a01aac16488e93dc @@ -2849,6 +2853,9 @@ checksums: templates/preview_survey_question_2_choice_2_label: 1af148222f327f28cf0db6513de5989e templates/preview_survey_question_2_headline: 5cfb173d156555227fbc2c97ad921e72 templates/preview_survey_question_2_subheader: 2e652d8acd68d072e5a0ae686c4011c0 + templates/preview_survey_question_open_text_headline: a9509a47e0456ae98ec3ddac3d6fad2c + templates/preview_survey_question_open_text_placeholder: 37ee9c84f3777b9220d4faec1e1c78ee + templates/preview_survey_question_open_text_subheader: 3c7bf09f3f17b02bc2fbbbdb347a5830 templates/preview_survey_welcome_card_headline: 8778dc41547a2778d0f9482da989fc00 templates/prioritize_features_description: 1eae41fad0e3947f803d8539081e59ec templates/prioritize_features_name: 4ca59ff1f9c319aaa68c3106d820fd6a diff --git a/apps/web/lib/getPublicUrl.test.ts b/apps/web/lib/getPublicUrl.test.ts index 435441bb0e..ebe8bde220 100644 --- a/apps/web/lib/getPublicUrl.test.ts +++ b/apps/web/lib/getPublicUrl.test.ts @@ -1,44 +1,66 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; -// Mock constants module const envMock = { - env: { - WEBAPP_URL: "http://localhost:3000", - PUBLIC_URL: undefined as string | undefined, - }, + WEBAPP_URL: undefined as string | undefined, + VERCEL_URL: undefined as string | undefined, + PUBLIC_URL: undefined as string | undefined, }; -vi.mock("@/lib/env", () => envMock); +vi.mock("./env", () => ({ + env: envMock, +})); + +const loadGetPublicDomain = async () => { + vi.resetModules(); + const { getPublicDomain } = await import("./getPublicUrl"); + return getPublicDomain; +}; describe("getPublicDomain", () => { beforeEach(() => { - vi.resetModules(); + envMock.WEBAPP_URL = undefined; + envMock.VERCEL_URL = undefined; + envMock.PUBLIC_URL = undefined; }); - test("should return WEBAPP_URL when PUBLIC_URL is not set", async () => { - const { getPublicDomain } = await import("./getPublicUrl"); - const domain = getPublicDomain(); - expect(domain).toBe("http://localhost:3000"); + test("returns trimmed WEBAPP_URL when configured", async () => { + envMock.WEBAPP_URL = " https://app.formbricks.com "; + + const getPublicDomain = await loadGetPublicDomain(); + + expect(getPublicDomain()).toBe("https://app.formbricks.com"); }); - test("should return PUBLIC_URL when it is set", async () => { - envMock.env.PUBLIC_URL = "https://surveys.example.com"; - const { getPublicDomain } = await import("./getPublicUrl"); - const domain = getPublicDomain(); - expect(domain).toBe("https://surveys.example.com"); + test("falls back to VERCEL_URL when WEBAPP_URL is empty", async () => { + envMock.WEBAPP_URL = " "; + envMock.VERCEL_URL = "preview.formbricks.com"; + + const getPublicDomain = await loadGetPublicDomain(); + + expect(getPublicDomain()).toBe("https://preview.formbricks.com"); }); - test("should handle empty string PUBLIC_URL by returning WEBAPP_URL", async () => { - envMock.env.PUBLIC_URL = ""; - const { getPublicDomain } = await import("./getPublicUrl"); - const domain = getPublicDomain(); - expect(domain).toBe("http://localhost:3000"); + test("falls back to localhost when WEBAPP_URL and VERCEL_URL are not set", async () => { + const getPublicDomain = await loadGetPublicDomain(); + + expect(getPublicDomain()).toBe("http://localhost:3000"); }); - test("should handle undefined PUBLIC_URL by returning WEBAPP_URL", async () => { - envMock.env.PUBLIC_URL = undefined; - const { getPublicDomain } = await import("./getPublicUrl"); - const domain = getPublicDomain(); - expect(domain).toBe("http://localhost:3000"); + test("returns PUBLIC_URL when set", async () => { + envMock.WEBAPP_URL = "https://app.formbricks.com"; + envMock.PUBLIC_URL = "https://surveys.formbricks.com"; + + const getPublicDomain = await loadGetPublicDomain(); + + expect(getPublicDomain()).toBe("https://surveys.formbricks.com"); + }); + + test("falls back to WEBAPP_URL when PUBLIC_URL is empty", async () => { + envMock.WEBAPP_URL = "https://app.formbricks.com"; + envMock.PUBLIC_URL = " "; + + const getPublicDomain = await loadGetPublicDomain(); + + expect(getPublicDomain()).toBe("https://app.formbricks.com"); }); }); diff --git a/apps/web/lib/getPublicUrl.ts b/apps/web/lib/getPublicUrl.ts index aa2270c26a..de64e5f4e5 100644 --- a/apps/web/lib/getPublicUrl.ts +++ b/apps/web/lib/getPublicUrl.ts @@ -1,8 +1,18 @@ import "server-only"; import { env } from "./env"; -const WEBAPP_URL = - env.WEBAPP_URL ?? (env.VERCEL_URL ? `https://${env.VERCEL_URL}` : "") ?? "http://localhost:3000"; +const configuredWebappUrl = env.WEBAPP_URL?.trim() ?? ""; +const WEBAPP_URL = (() => { + if (configuredWebappUrl !== "") { + return configuredWebappUrl; + } + + if (env.VERCEL_URL) { + return `https://${env.VERCEL_URL}`; + } + + return "http://localhost:3000"; +})(); /** * Returns the public domain URL diff --git a/apps/web/lib/response/service.ts b/apps/web/lib/response/service.ts index a4b1d82fdc..badf46d715 100644 --- a/apps/web/lib/response/service.ts +++ b/apps/web/lib/response/service.ts @@ -397,7 +397,6 @@ export const getResponseDownloadFile = async ( "Survey ID", "Formbricks ID (internal)", "User ID", - "Notes", "Tags", ...metaDataFields, ...elements.flat(), diff --git a/apps/web/lib/styling/constants.ts b/apps/web/lib/styling/constants.ts index ca3f2769bc..1ac8c1b495 100644 --- a/apps/web/lib/styling/constants.ts +++ b/apps/web/lib/styling/constants.ts @@ -60,6 +60,7 @@ export const getSuggestedColors = (brandColor: string = DEFAULT_BRAND_COLOR) => // Options (Radio / Checkbox) "optionBgColor.light": inputBg, "optionLabelColor.light": questionColor, + "optionBorderColor.light": inputBorder, // Card "cardBackgroundColor.light": cardBg, @@ -138,6 +139,7 @@ export const STYLE_DEFAULTS: TProjectStyling = { // Options optionBgColor: { light: _colors["optionBgColor.light"] }, optionLabelColor: { light: _colors["optionLabelColor.light"] }, + optionBorderColor: { light: _colors["optionBorderColor.light"] }, optionBorderRadius: 8, optionPaddingX: 16, optionPaddingY: 16, @@ -169,6 +171,7 @@ export const deriveNewFieldsFromLegacy = (saved: Record): Recor const q = light("questionColor"); const b = light("brandColor"); const i = light("inputColor"); + const inputBorder = light("inputBorderColor"); return { ...(q && !saved.elementHeadlineColor && { elementHeadlineColor: { light: q } }), @@ -179,9 +182,9 @@ export const deriveNewFieldsFromLegacy = (saved: Record): Recor ...(b && !saved.buttonBgColor && { buttonBgColor: { light: b } }), ...(b && !saved.buttonTextColor && { buttonTextColor: { light: isLight(b) ? "#0f172a" : "#ffffff" } }), ...(i && !saved.optionBgColor && { optionBgColor: { light: i } }), + ...(inputBorder && !saved.optionBorderColor && { optionBorderColor: { light: inputBorder } }), ...(b && !saved.progressIndicatorBgColor && { progressIndicatorBgColor: { light: b } }), - ...(b && - !saved.progressTrackBgColor && { progressTrackBgColor: { light: mixColor(b, "#ffffff", 0.8) } }), + ...(b && !saved.progressTrackBgColor && { progressTrackBgColor: { light: mixColor(b, "#ffffff", 0.8) } }), }; }; @@ -211,6 +214,7 @@ export const buildStylingFromBrandColor = (brandColor: string = DEFAULT_BRAND_CO inputTextColor: { light: colors["inputTextColor.light"] }, optionBgColor: { light: colors["optionBgColor.light"] }, optionLabelColor: { light: colors["optionLabelColor.light"] }, + optionBorderColor: { light: colors["optionBorderColor.light"] }, cardBackgroundColor: { light: colors["cardBackgroundColor.light"] }, cardBorderColor: { light: colors["cardBorderColor.light"] }, highlightBorderColor: { light: colors["highlightBorderColor.light"] }, diff --git a/apps/web/lib/survey/service.ts b/apps/web/lib/survey/service.ts index 7801d400c2..5b52ce9799 100644 --- a/apps/web/lib/survey/service.ts +++ b/apps/web/lib/survey/service.ts @@ -508,6 +508,7 @@ export const updateSurveyInternal = async ( newFollowUps.length > 0 ? { data: newFollowUps.map((followUp) => ({ + id: followUp.id, name: followUp.name, trigger: followUp.trigger, action: followUp.action, diff --git a/apps/web/locales/de-DE.json b/apps/web/locales/de-DE.json index cd9522cd11..11c2bbf0f8 100644 --- a/apps/web/locales/de-DE.json +++ b/apps/web/locales/de-DE.json @@ -191,6 +191,7 @@ "days": "Tage", "default": "Standard", "delete": "Löschen", + "delete_what": "{deleteWhat} löschen", "description": "Beschreibung", "dev_env": "Entwicklungsumgebung", "development": "Entwicklung", @@ -287,6 +288,7 @@ "move_down": "Nach unten bewegen", "move_up": "Nach oben bewegen", "multiple_languages": "Mehrsprachigkeit", + "my_product": "mein Produkt", "name": "Name", "new": "Neu", "new_version_available": "Formbricks {version} ist da. Jetzt aktualisieren!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Hey {userName}", "email_customization_preview_email_text": "Dies ist eine E-Mail-Vorschau, um dir zu zeigen, welches Logo in den E-Mails gerendert wird.", "error_deleting_organization_please_try_again": "Fehler beim Löschen der Organisation. Bitte versuche es erneut.", - "from_your_organization": "von deiner Organisation", + "from_your_organization": "{memberName} aus Ihrer Organisation", "invitation_sent_once_more": "Einladung nochmal gesendet.", "invite_deleted_successfully": "Einladung erfolgreich gelöscht", "invite_expires_on": "Einladung läuft ab am {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Platzhalter hinzufügen, falls kein Wert zur Verfügung steht.", "add_hidden_field_id": "Verstecktes Feld ID hinzufügen", "add_highlight_border": "Rahmen hinzufügen", + "add_highlight_border_description": "Gilt nur für In-Product-Umfragen.", "add_logic": "Logik hinzufügen", "add_none_of_the_above": "Füge \"Keine der oben genannten Optionen\" hinzu", "add_option": "Option hinzufügen", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Nachverfolgung aktualisiert und wird gespeichert, sobald du die Umfrage speicherst.", "follow_ups_new": "Neues Follow-up", "follow_ups_upgrade_button_text": "Upgrade, um Follow-ups zu aktivieren", - "form_styling": "Umfrage Styling", "formbricks_sdk_is_not_connected": "Formbricks SDK ist nicht verbunden", "four_points": "4 Punkte", "heading": "Überschrift", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Antwort Limits, Weiterleitungen und mehr.", "response_options": "Antwortoptionen", "roundness": "Rundheit", - "roundness_description": "Steuert, wie abgerundet die Kartenecken sind.", + "roundness_description": "Steuert, wie abgerundet die Ecken sind.", "row_used_in_logic_error": "Diese Zeile wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.", "rows": "Zeilen", "save_and_close": "Speichern & Schließen", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Diese kostenlose und quelloffene Umfrage wurde geschlossen", "survey_display_settings": "Einstellungen zur Anzeige der Umfrage", "survey_placement": "Platzierung der Umfrage", + "survey_styling": "Umfrage Styling", "survey_trigger": "Auslöser der Umfrage", "switch_multi_language_on_to_get_started": "Aktiviere Mehrsprachigkeit, um loszulegen 👉", "target_block_not_found": "Zielblock nicht gefunden", @@ -1755,7 +1758,6 @@ "welcome_message": "Willkommensnachricht", "without_a_filter_all_of_your_users_can_be_surveyed": "Ohne Filter können alle deine Nutzer befragt werden.", "you_have_not_created_a_segment_yet": "Du hast noch keinen Segment erstellt.", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Sie müssen zwei oder mehr Sprachen in Ihrem Workspace eingerichtet haben, um mit Übersetzungen zu arbeiten.", "your_description_here_recall_information_with": "Deine Beschreibung hier. Informationen abrufen mit @", "your_question_here_recall_information_with": "Deine Frage hier. Informationen abrufen mit @", "your_web_app": "Deine Web-App", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Färbt den gefüllten Teil des Balkens.", "advanced_styling_field_input_border_radius_description": "Rundet die Eingabeecken ab.", "advanced_styling_field_input_font_size_description": "Skaliert den eingegebenen Text in Eingabefeldern.", - "advanced_styling_field_input_height_description": "Legt die Mindesthöhe des Eingabefelds fest.", + "advanced_styling_field_input_height_description": "Steuert die Mindesthöhe der Eingabe.", "advanced_styling_field_input_padding_x_description": "Fügt links und rechts Abstand hinzu.", "advanced_styling_field_input_padding_y_description": "Fügt oben und unten Abstand hinzu.", "advanced_styling_field_input_placeholder_opacity_description": "Blendet den Platzhaltertext aus.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Färbt den eingegebenen Text in Eingabefeldern.", "advanced_styling_field_option_bg": "Hintergrund", "advanced_styling_field_option_bg_description": "Füllt die Optionselemente.", + "advanced_styling_field_option_border": "Rahmenfarbe", + "advanced_styling_field_option_border_description": "Umrandet Radio- und Checkbox-Optionen.", "advanced_styling_field_option_border_radius_description": "Rundet die Ecken der Optionen ab.", "advanced_styling_field_option_font_size_description": "Skaliert den Text der Optionsbeschriftung.", "advanced_styling_field_option_label": "Label-Farbe", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Nein, danke!", "preview_survey_question_2_headline": "Möchtest Du auf dem Laufenden bleiben?", "preview_survey_question_2_subheader": "Dies ist eine Beispielbeschreibung.", + "preview_survey_question_open_text_headline": "Möchtest Du noch etwas teilen?", + "preview_survey_question_open_text_placeholder": "Tippe deine Antwort hier...", + "preview_survey_question_open_text_subheader": "Dein Feedback hilft uns, besser zu werden.", "preview_survey_welcome_card_headline": "Willkommen!", "prioritize_features_description": "Identifiziere die Funktionen, die deine Nutzer am meisten und am wenigsten brauchen.", "prioritize_features_name": "Funktionen priorisieren", diff --git a/apps/web/locales/en-US.json b/apps/web/locales/en-US.json index 9117c59ad6..0693d542a5 100644 --- a/apps/web/locales/en-US.json +++ b/apps/web/locales/en-US.json @@ -191,6 +191,7 @@ "days": "days", "default": "Default", "delete": "Delete", + "delete_what": "Delete {deleteWhat}", "description": "Description", "dev_env": "Dev Environment", "development": "Development", @@ -287,6 +288,7 @@ "move_down": "Move down", "move_up": "Move up", "multiple_languages": "Multiple languages", + "my_product": "my Product", "name": "Name", "new": "New", "new_version_available": "Formbricks {version} is here. Upgrade now!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Hey {userName}", "email_customization_preview_email_text": "This is an email preview to show you which logo will be rendered in the emails.", "error_deleting_organization_please_try_again": "Error deleting organization. Please try again.", - "from_your_organization": "from your organization", + "from_your_organization": "{memberName} from your organization", "invitation_sent_once_more": "Invitation sent once more.", "invite_deleted_successfully": "Invite deleted successfully", "invite_expires_on": "Invite expires on {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Add a placeholder to show if there is no value to recall.", "add_hidden_field_id": "Add hidden field ID", "add_highlight_border": "Add highlight border", + "add_highlight_border_description": "Only applies to in-product surveys.", "add_logic": "Add logic", "add_none_of_the_above": "Add “None of the Above”", "add_option": "Add option", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Follow-up updated and will be saved once you save the survey.", "follow_ups_new": "New follow-up", "follow_ups_upgrade_button_text": "Upgrade to enable follow-ups", - "form_styling": "Form styling", "formbricks_sdk_is_not_connected": "Formbricks SDK is not connected", "four_points": "4 points", "heading": "Heading", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Response limits, redirections and more.", "response_options": "Response Options", "roundness": "Roundness", - "roundness_description": "Controls how rounded the card corners are.", + "roundness_description": "Controls how rounded corners are.", "row_used_in_logic_error": "This row is used in logic of question {questionIndex}. Please remove it from logic first.", "rows": "Rows", "save_and_close": "Save & Close", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "This free & open-source survey has been closed", "survey_display_settings": "Survey Display Settings", "survey_placement": "Survey Placement", + "survey_styling": "Survey styling", "survey_trigger": "Survey Trigger", "switch_multi_language_on_to_get_started": "Switch multi-language on to get started 👉", "target_block_not_found": "Target block not found", @@ -1755,7 +1758,6 @@ "welcome_message": "Welcome message", "without_a_filter_all_of_your_users_can_be_surveyed": "Without a filter, all of your users can be surveyed.", "you_have_not_created_a_segment_yet": "You have not created a segment yet", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "You need to have two or more languages set up in your workspace to work with translations.", "your_description_here_recall_information_with": "Your description here. Recall information with @", "your_question_here_recall_information_with": "Your question here. Recall information with @", "your_web_app": "Your web app", @@ -2162,7 +2164,7 @@ "advanced_styling_field_description_size": "Description Font Size", "advanced_styling_field_description_size_description": "Scales the description text.", "advanced_styling_field_description_weight": "Description Font Weight", - "advanced_styling_field_description_weight_description": "Makes description text lighter or bolder.", + "advanced_styling_field_description_weight_description": "Makes descr. text lighter or bolder.", "advanced_styling_field_font_size": "Font Size", "advanced_styling_field_font_weight": "Font Weight", "advanced_styling_field_headline_color": "Headline Color", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Colors the filled portion of the bar.", "advanced_styling_field_input_border_radius_description": "Rounds the input corners.", "advanced_styling_field_input_font_size_description": "Scales the typed text in inputs.", - "advanced_styling_field_input_height_description": "Controls the minimum height of the input field.", + "advanced_styling_field_input_height_description": "Controls the min. height of the input.", "advanced_styling_field_input_padding_x_description": "Adds space on the left and right.", "advanced_styling_field_input_padding_y_description": "Adds space on the top and bottom.", "advanced_styling_field_input_placeholder_opacity_description": "Fades the placeholder hint text.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Colors the typed text in inputs.", "advanced_styling_field_option_bg": "Background", "advanced_styling_field_option_bg_description": "Fills the option items.", + "advanced_styling_field_option_border": "Border Color", + "advanced_styling_field_option_border_description": "Outlines radio and checkbox options.", "advanced_styling_field_option_border_radius_description": "Rounds the option corners.", "advanced_styling_field_option_font_size_description": "Scales the option label text.", "advanced_styling_field_option_label": "Label Color", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "No, thank you!", "preview_survey_question_2_headline": "Want to stay in the loop?", "preview_survey_question_2_subheader": "This is an example description.", + "preview_survey_question_open_text_headline": "Anything else you'd like to share?", + "preview_survey_question_open_text_placeholder": "Type your answer here…", + "preview_survey_question_open_text_subheader": "Your feedback helps us improve.", "preview_survey_welcome_card_headline": "Welcome!", "prioritize_features_description": "Identify features your users need most and least.", "prioritize_features_name": "Prioritize Features", diff --git a/apps/web/locales/es-ES.json b/apps/web/locales/es-ES.json index 2db90838d9..abb1e162e0 100644 --- a/apps/web/locales/es-ES.json +++ b/apps/web/locales/es-ES.json @@ -191,6 +191,7 @@ "days": "días", "default": "Predeterminado", "delete": "Eliminar", + "delete_what": "Eliminar {deleteWhat}", "description": "Descripción", "dev_env": "Entorno de desarrollo", "development": "Desarrollo", @@ -287,6 +288,7 @@ "move_down": "Mover hacia abajo", "move_up": "Mover hacia arriba", "multiple_languages": "Múltiples idiomas", + "my_product": "mi producto", "name": "Nombre", "new": "Nuevo", "new_version_available": "Formbricks {version} está aquí. ¡Actualiza ahora!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Hola {userName}", "email_customization_preview_email_text": "Este es un correo electrónico de vista previa para mostrarte qué logotipo se mostrará en los correos electrónicos.", "error_deleting_organization_please_try_again": "Error al eliminar la organización. Por favor, inténtalo de nuevo.", - "from_your_organization": "de tu organización", + "from_your_organization": "{memberName} de tu organización", "invitation_sent_once_more": "Invitación enviada una vez más.", "invite_deleted_successfully": "Invitación eliminada correctamente", "invite_expires_on": "La invitación expira el {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Añadir un marcador de posición para mostrar si no hay valor que recuperar.", "add_hidden_field_id": "Añadir ID de campo oculto", "add_highlight_border": "Añadir borde destacado", + "add_highlight_border_description": "Solo se aplica a encuestas dentro del producto.", "add_logic": "Añadir lógica", "add_none_of_the_above": "Añadir \"Ninguna de las anteriores\"", "add_option": "Añadir opción", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Seguimiento actualizado y se guardará cuando guardes la encuesta.", "follow_ups_new": "Nuevo seguimiento", "follow_ups_upgrade_button_text": "Actualiza para habilitar seguimientos", - "form_styling": "Estilo del formulario", "formbricks_sdk_is_not_connected": "El SDK de Formbricks no está conectado", "four_points": "4 puntos", "heading": "Encabezado", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Límites de respuestas, redirecciones y más.", "response_options": "Opciones de respuesta", "roundness": "Redondez", - "roundness_description": "Controla qué tan redondeadas están las esquinas de la tarjeta.", + "roundness_description": "Controla qué tan redondeadas están las esquinas.", "row_used_in_logic_error": "Esta fila se utiliza en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.", "rows": "Filas", "save_and_close": "Guardar y cerrar", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Esta encuesta gratuita y de código abierto ha sido cerrada", "survey_display_settings": "Ajustes de visualización de la encuesta", "survey_placement": "Ubicación de la encuesta", + "survey_styling": "Estilo del formulario", "survey_trigger": "Activador de la encuesta", "switch_multi_language_on_to_get_started": "Activa el modo multiidioma para comenzar 👉", "target_block_not_found": "Bloque objetivo no encontrado", @@ -1755,7 +1758,6 @@ "welcome_message": "Mensaje de bienvenida", "without_a_filter_all_of_your_users_can_be_surveyed": "Sin un filtro, todos tus usuarios pueden ser encuestados.", "you_have_not_created_a_segment_yet": "Aún no has creado un segmento", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Necesitas tener dos o más idiomas configurados en tu proyecto para trabajar con traducciones.", "your_description_here_recall_information_with": "Tu descripción aquí. Recupera información con @", "your_question_here_recall_information_with": "Tu pregunta aquí. Recupera información con @", "your_web_app": "Tu aplicación web", @@ -2162,7 +2164,7 @@ "advanced_styling_field_description_size": "Tamaño de fuente de la descripción", "advanced_styling_field_description_size_description": "Escala el texto de la descripción.", "advanced_styling_field_description_weight": "Grosor de fuente de la descripción", - "advanced_styling_field_description_weight_description": "Hace el texto de la descripción más ligero o más grueso.", + "advanced_styling_field_description_weight_description": "Hace el texto de descripción más ligero o más grueso.", "advanced_styling_field_font_size": "Tamaño de fuente", "advanced_styling_field_font_weight": "Grosor de fuente", "advanced_styling_field_headline_color": "Color del titular", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Colorea la porción rellena de la barra.", "advanced_styling_field_input_border_radius_description": "Redondea las esquinas del campo.", "advanced_styling_field_input_font_size_description": "Escala el texto escrito en los campos.", - "advanced_styling_field_input_height_description": "Controla la altura mínima del campo de entrada.", + "advanced_styling_field_input_height_description": "Controla la altura mínima de la entrada.", "advanced_styling_field_input_padding_x_description": "Añade espacio a la izquierda y a la derecha.", "advanced_styling_field_input_padding_y_description": "Añade espacio en la parte superior e inferior.", "advanced_styling_field_input_placeholder_opacity_description": "Atenúa el texto de sugerencia del marcador de posición.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Colorea el texto escrito en los campos de entrada.", "advanced_styling_field_option_bg": "Fondo", "advanced_styling_field_option_bg_description": "Rellena los elementos de opción.", + "advanced_styling_field_option_border": "Color del borde", + "advanced_styling_field_option_border_description": "Delimita las opciones de radio y casillas de verificación.", "advanced_styling_field_option_border_radius_description": "Redondea las esquinas de las opciones.", "advanced_styling_field_option_font_size_description": "Escala el texto de la etiqueta de opción.", "advanced_styling_field_option_label": "Color de la etiqueta", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "¡No, gracias!", "preview_survey_question_2_headline": "¿Quieres estar al tanto?", "preview_survey_question_2_subheader": "Esta es una descripción de ejemplo.", + "preview_survey_question_open_text_headline": "¿Hay algo más que te gustaría compartir?", + "preview_survey_question_open_text_placeholder": "Escribe tu respuesta aquí...", + "preview_survey_question_open_text_subheader": "Tus comentarios nos ayudan a mejorar.", "preview_survey_welcome_card_headline": "¡Bienvenido!", "prioritize_features_description": "Identifica las funciones que tus usuarios necesitan más y menos.", "prioritize_features_name": "Priorizar funciones", diff --git a/apps/web/locales/fr-FR.json b/apps/web/locales/fr-FR.json index a3bea34e47..fa45f1a622 100644 --- a/apps/web/locales/fr-FR.json +++ b/apps/web/locales/fr-FR.json @@ -191,6 +191,7 @@ "days": "jours", "default": "Par défaut", "delete": "Supprimer", + "delete_what": "Supprimer {deleteWhat}", "description": "Description", "dev_env": "Environnement de développement", "development": "Développement", @@ -287,6 +288,7 @@ "move_down": "Déplacer vers le bas", "move_up": "Déplacer vers le haut", "multiple_languages": "Plusieurs langues", + "my_product": "mon produit", "name": "Nom", "new": "Nouveau", "new_version_available": "Formbricks {version} est là. Mettez à jour maintenant !", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Salut {userName}", "email_customization_preview_email_text": "Cette est une prévisualisation d'e-mail pour vous montrer quel logo sera rendu dans les e-mails.", "error_deleting_organization_please_try_again": "Erreur lors de la suppression de l'organisation. Veuillez réessayer.", - "from_your_organization": "de votre organisation", + "from_your_organization": "{memberName} de votre organisation", "invitation_sent_once_more": "Invitation envoyée une fois de plus.", "invite_deleted_successfully": "Invitation supprimée avec succès", "invite_expires_on": "L'invitation expire le {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Ajouter un espace réservé à afficher s'il n'y a pas de valeur à rappeler.", "add_hidden_field_id": "Ajouter un champ caché ID", "add_highlight_border": "Ajouter une bordure de surlignage", + "add_highlight_border_description": "S'applique uniquement aux sondages intégrés au produit.", "add_logic": "Ajouter de la logique", "add_none_of_the_above": "Ajouter \"Aucun des éléments ci-dessus\"", "add_option": "Ajouter une option", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "\"Suivi mis à jour et sera enregistré une fois que vous sauvegarderez le sondage.\"", "follow_ups_new": "Nouveau suivi", "follow_ups_upgrade_button_text": "Passez à la version supérieure pour activer les relances", - "form_styling": "Style de formulaire", "formbricks_sdk_is_not_connected": "Le SDK Formbricks n'est pas connecté", "four_points": "4 points", "heading": "En-tête", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Limites de réponse, redirections et plus.", "response_options": "Options de réponse", "roundness": "Rondeur", - "roundness_description": "Contrôle l'arrondi des coins de la carte.", + "roundness_description": "Contrôle l'arrondi des coins.", "row_used_in_logic_error": "Cette ligne est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.", "rows": "Lignes", "save_and_close": "Enregistrer et fermer", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Cette enquête gratuite et open-source a été fermée", "survey_display_settings": "Paramètres d'affichage de l'enquête", "survey_placement": "Placement de l'enquête", + "survey_styling": "Style de formulaire", "survey_trigger": "Déclencheur d'enquête", "switch_multi_language_on_to_get_started": "Activez le mode multilingue pour commencer 👉", "target_block_not_found": "Bloc cible non trouvé", @@ -1755,7 +1758,6 @@ "welcome_message": "Message de bienvenue", "without_a_filter_all_of_your_users_can_be_surveyed": "Sans filtre, tous vos utilisateurs peuvent être sondés.", "you_have_not_created_a_segment_yet": "Tu n'as pas encore créé de segment.", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Vous devez avoir deux langues ou plus configurées dans votre espace de travail pour travailler avec les traductions.", "your_description_here_recall_information_with": "Votre description ici. Rappelez-vous des informations avec @", "your_question_here_recall_information_with": "Votre question ici. Rappelez-vous des informations avec @", "your_web_app": "Votre application web", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Colore la partie remplie de la barre.", "advanced_styling_field_input_border_radius_description": "Arrondit les coins du champ de saisie.", "advanced_styling_field_input_font_size_description": "Ajuste la taille du texte saisi dans les champs.", - "advanced_styling_field_input_height_description": "Contrôle la hauteur minimale du champ de saisie.", + "advanced_styling_field_input_height_description": "Contrôle la hauteur min. du champ de saisie.", "advanced_styling_field_input_padding_x_description": "Ajoute de l'espace à gauche et à droite.", "advanced_styling_field_input_padding_y_description": "Ajoute de l'espace en haut et en bas.", "advanced_styling_field_input_placeholder_opacity_description": "Atténue le texte d'indication du placeholder.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Colore le texte saisi dans les champs.", "advanced_styling_field_option_bg": "Arrière-plan", "advanced_styling_field_option_bg_description": "Remplit les éléments d'option.", + "advanced_styling_field_option_border": "Couleur de bordure", + "advanced_styling_field_option_border_description": "Contours des options de boutons radio et de cases à cocher.", "advanced_styling_field_option_border_radius_description": "Arrondit les coins des options.", "advanced_styling_field_option_font_size_description": "Ajuste la taille du texte des libellés d'option.", "advanced_styling_field_option_label": "Couleur de l'étiquette", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Non, merci !", "preview_survey_question_2_headline": "Souhaitez-vous être informé ?", "preview_survey_question_2_subheader": "Ceci est un exemple de description.", + "preview_survey_question_open_text_headline": "Autre chose que vous aimeriez partager ?", + "preview_survey_question_open_text_placeholder": "Entrez votre réponse ici...", + "preview_survey_question_open_text_subheader": "Vos commentaires nous aident à nous améliorer.", "preview_survey_welcome_card_headline": "Bienvenue !", "prioritize_features_description": "Identifiez les fonctionnalités dont vos utilisateurs ont le plus et le moins besoin.", "prioritize_features_name": "Prioriser les fonctionnalités", diff --git a/apps/web/locales/hu-HU.json b/apps/web/locales/hu-HU.json index 58f871be98..88b0a6ee17 100644 --- a/apps/web/locales/hu-HU.json +++ b/apps/web/locales/hu-HU.json @@ -191,6 +191,7 @@ "days": "napok", "default": "Alapértelmezett", "delete": "Törlés", + "delete_what": "{deleteWhat} törlése", "description": "Leírás", "dev_env": "Fejlesztői környezet", "development": "Fejlesztés", @@ -287,6 +288,7 @@ "move_down": "Mozgatás le", "move_up": "Mozgatás fel", "multiple_languages": "Több nyelv", + "my_product": "saját termék", "name": "Név", "new": "Új", "new_version_available": "A Formbricks {version} megérkezett. Frissítsen most!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Helló {userName}", "email_customization_preview_email_text": "Ez egy e-mail előnézet, amely azt mutatja meg, hogy melyik logó fog megjelenni az e-mailekben.", "error_deleting_organization_please_try_again": "Hiba a szervezet törlésekor. Próbálja meg újra.", - "from_your_organization": "a szervezetétől", + "from_your_organization": "{memberName} a szervezetből", "invitation_sent_once_more": "A meghívó még egyszer elküldve.", "invite_deleted_successfully": "A meghívó sikeresen törölve", "invite_expires_on": "A meghívó lejár ekkor: {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Helykitöltő hozzáadása annak megjelenítéshez, hogy nincs visszahívandó érték.", "add_hidden_field_id": "Rejtett mezőazonosító hozzáadása", "add_highlight_border": "Kiemelési szegély hozzáadása", + "add_highlight_border_description": "Csak a terméken belüli felmérésekre vonatkozik.", "add_logic": "Logika hozzáadása", "add_none_of_the_above": "„A fentiek közül egyik sem” hozzáadása", "add_option": "Lehetőség hozzáadása", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "A követés frissítve, és akkor lesz elmentve, ha elmenti a kérdőívet.", "follow_ups_new": "Új követés", "follow_ups_upgrade_button_text": "Magasabb csomagra váltás a követések engedélyezéséhez", - "form_styling": "Űrlap stílusának beállítása", "formbricks_sdk_is_not_connected": "A Formbricks SDK nincs csatlakoztatva", "four_points": "4 pont", "heading": "Címsor", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Válaszkorlátok, átirányítások és egyebek.", "response_options": "Válasz beállításai", "roundness": "Kerekesség", - "roundness_description": "Annak vezérlése, hogy a kártya sarkai mennyire legyenek lekerekítve.", + "roundness_description": "Szabályozza a sarkok lekerekítését.", "row_used_in_logic_error": "Ez a sor használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.", "rows": "Sorok", "save_and_close": "Mentés és bezárás", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Ez a szabad és nyílt forráskódú kérdőív le lett zárva", "survey_display_settings": "Kérdőív megjelenítésének beállításai", "survey_placement": "Kérdőív elhelyezése", + "survey_styling": "Űrlap stílusának beállítása", "survey_trigger": "Kérdőív aktiválója", "switch_multi_language_on_to_get_started": "Kapcsolja be a többnyelvűséget a kezdéshez 👉", "target_block_not_found": "A célblokk nem található", @@ -1755,7 +1758,6 @@ "welcome_message": "Üdvözlő üzenet", "without_a_filter_all_of_your_users_can_be_surveyed": "Szűrő nélkül az összes felhasználója megkérdezhető.", "you_have_not_created_a_segment_yet": "Még nem hozott létre szakaszt", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Be kell állítania kettő vagy több nyelvet a munkaterületen a fordításokkal való munkához.", "your_description_here_recall_information_with": "Ide jön a leírás. Információk visszahívása a @ karakterrel.", "your_question_here_recall_information_with": "Ide jön a kérdés. Információk visszahívása a @ karakterrel.", "your_web_app": "Saját webalkalmazás", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Kiszínezi a sáv kitöltött részét.", "advanced_styling_field_input_border_radius_description": "Lekerekíti a beviteli mező sarkait.", "advanced_styling_field_input_font_size_description": "Átméretezi a beviteli mezőkbe beírt szöveget.", - "advanced_styling_field_input_height_description": "A beviteli mező minimális magasságát szabályozza.", + "advanced_styling_field_input_height_description": "Szabályozza a beviteli mező minimális magasságát.", "advanced_styling_field_input_padding_x_description": "Térközt ad hozzá balra és jobbra.", "advanced_styling_field_input_padding_y_description": "Térközt ad hozzá fent és lent.", "advanced_styling_field_input_placeholder_opacity_description": "Elhalványítja a helykitöltő súgószöveget.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Kiszínezi a beviteli mezőkbe beírt szöveget.", "advanced_styling_field_option_bg": "Háttér", "advanced_styling_field_option_bg_description": "Kitölti a választási lehetőség elemeit.", + "advanced_styling_field_option_border": "Szegély színe", + "advanced_styling_field_option_border_description": "A rádiógomb és jelölőnégyzet opciók körvonalát határozza meg.", "advanced_styling_field_option_border_radius_description": "Lekerekíti a választási lehetőség sarkait.", "advanced_styling_field_option_font_size_description": "Átméretezi a választási lehetőség címkéjének szövegét.", "advanced_styling_field_option_label": "Címke színe", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Nem, köszönöm!", "preview_survey_question_2_headline": "Szeretne naprakész maradni?", "preview_survey_question_2_subheader": "Ez egy példa a leírásra.", + "preview_survey_question_open_text_headline": "Bármi egyéb, amit meg szeretne osztani?", + "preview_survey_question_open_text_placeholder": "Írja be ide a válaszát…", + "preview_survey_question_open_text_subheader": "A visszajelzése segít nekünk a fejlődésben.", "preview_survey_welcome_card_headline": "Üdvözöljük!", "prioritize_features_description": "A felhasználóknak leginkább és legkevésbé szükséges funkciók azonosítása.", "prioritize_features_name": "Funkciók rangsorolása", diff --git a/apps/web/locales/ja-JP.json b/apps/web/locales/ja-JP.json index 4b97529b16..197886ce9c 100644 --- a/apps/web/locales/ja-JP.json +++ b/apps/web/locales/ja-JP.json @@ -191,6 +191,7 @@ "days": "日", "default": "デフォルト", "delete": "削除", + "delete_what": "{deleteWhat}を削除", "description": "説明", "dev_env": "開発環境", "development": "開発", @@ -287,6 +288,7 @@ "move_down": "下に移動", "move_up": "上に移動", "multiple_languages": "多言語", + "my_product": "マイプロダクト", "name": "名前", "new": "新規", "new_version_available": "Formbricks {version} が利用可能です。今すぐアップグレード!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "こんにちは、{userName}さん", "email_customization_preview_email_text": "これは、メールに表示されるロゴを確認するためのプレビューメールです。", "error_deleting_organization_please_try_again": "組織の削除中にエラーが発生しました。もう一度お試しください。", - "from_your_organization": "あなたの組織から", + "from_your_organization": "組織から{memberName}を削除", "invitation_sent_once_more": "招待状を再度送信しました。", "invite_deleted_successfully": "招待を正常に削除しました", "invite_expires_on": "招待は{date}に期限切れ", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "質問がスキップされた場合に表示するプレースホルダーを追加:", "add_hidden_field_id": "非表示フィールドIDを追加", "add_highlight_border": "ハイライトボーダーを追加", + "add_highlight_border_description": "プロダクト内サーベイにのみ適用されます。", "add_logic": "ロジックを追加", "add_none_of_the_above": "\"いずれも該当しません\" を追加", "add_option": "オプションを追加", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "フォローアップ が 更新され、 アンケートを 保存すると保存されます。", "follow_ups_new": "新しいフォローアップ", "follow_ups_upgrade_button_text": "フォローアップを有効にするためにアップグレード", - "form_styling": "フォームのスタイル", "formbricks_sdk_is_not_connected": "Formbricks SDKが接続されていません", "four_points": "4点", "heading": "見出し", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "回答数の上限、リダイレクトなど。", "response_options": "回答オプション", "roundness": "丸み", - "roundness_description": "カードの角の丸みを調整します。", + "roundness_description": "角の丸みを調整します。", "row_used_in_logic_error": "この行は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。", "rows": "行", "save_and_close": "保存して閉じる", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "この無料のオープンソースフォームは閉鎖されました", "survey_display_settings": "フォーム表示設定", "survey_placement": "フォームの配置", + "survey_styling": "フォームのスタイル", "survey_trigger": "フォームのトリガー", "switch_multi_language_on_to_get_started": "多言語機能をオンにして開始 👉", "target_block_not_found": "対象ブロックが見つかりません", @@ -1755,7 +1758,6 @@ "welcome_message": "ウェルカムメッセージ", "without_a_filter_all_of_your_users_can_be_surveyed": "フィルターがなければ、すべてのユーザーがフォームに回答できます。", "you_have_not_created_a_segment_yet": "まだセグメントを作成していません", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "翻訳を使用するには、ワークスペースに2つ以上の言語を設定する必要があります。", "your_description_here_recall_information_with": "ここにあなたの説明。@ で情報を呼び出す", "your_question_here_recall_information_with": "ここにあなたの質問。@ で情報を呼び出す", "your_web_app": "あなたのウェブアプリ", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "バーの塗りつぶし部分に色を付けます。", "advanced_styling_field_input_border_radius_description": "入力フィールドの角を丸めます。", "advanced_styling_field_input_font_size_description": "入力フィールド内の入力テキストのサイズを調整します。", - "advanced_styling_field_input_height_description": "入力フィールドの最小の高さを制御します。", + "advanced_styling_field_input_height_description": "入力欄の最小の高さを調整します。", "advanced_styling_field_input_padding_x_description": "左右にスペースを追加します。", "advanced_styling_field_input_padding_y_description": "上下にスペースを追加します。", "advanced_styling_field_input_placeholder_opacity_description": "プレースホルダーのヒントテキストを薄くします。", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "入力フィールドに入力されたテキストの色を設定します。", "advanced_styling_field_option_bg": "背景", "advanced_styling_field_option_bg_description": "オプション項目を塗りつぶします。", + "advanced_styling_field_option_border": "枠線の色", + "advanced_styling_field_option_border_description": "ラジオボタンとチェックボックスの選択肢の輪郭を設定します。", "advanced_styling_field_option_border_radius_description": "オプションの角を丸くします。", "advanced_styling_field_option_font_size_description": "オプションラベルのテキストサイズを調整します。", "advanced_styling_field_option_label": "ラベルの色", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "いいえ、結構です!", "preview_survey_question_2_headline": "最新情報を知りたいですか?", "preview_survey_question_2_subheader": "これは説明の例です。", + "preview_survey_question_open_text_headline": "他に共有したいことはありますか?", + "preview_survey_question_open_text_placeholder": "ここに回答を入力してください...", + "preview_survey_question_open_text_subheader": "あなたのフィードバックは、私たちの改善に役立ちます。", "preview_survey_welcome_card_headline": "ようこそ!", "prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。", "prioritize_features_name": "機能の優先順位付け", diff --git a/apps/web/locales/nl-NL.json b/apps/web/locales/nl-NL.json index b3d4f5d624..d853d357c4 100644 --- a/apps/web/locales/nl-NL.json +++ b/apps/web/locales/nl-NL.json @@ -191,6 +191,7 @@ "days": "dagen", "default": "Standaard", "delete": "Verwijderen", + "delete_what": "Verwijder {deleteWhat}", "description": "Beschrijving", "dev_env": "Ontwikkelomgeving", "development": "Ontwikkeling", @@ -287,6 +288,7 @@ "move_down": "Ga naar beneden", "move_up": "Ga omhoog", "multiple_languages": "Meerdere talen", + "my_product": "mijn product", "name": "Naam", "new": "Nieuw", "new_version_available": "Formbricks {version} is hier. Upgrade nu!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Hé {userName}", "email_customization_preview_email_text": "Dit is een e-mailvoorbeeld om u te laten zien welk logo in de e-mails wordt weergegeven.", "error_deleting_organization_please_try_again": "Fout bij verwijderen van organisatie. Probeer het opnieuw.", - "from_your_organization": "vanuit uw organisatie", + "from_your_organization": "{memberName} uit je organisatie", "invitation_sent_once_more": "Uitnodiging nogmaals verzonden.", "invite_deleted_successfully": "Uitnodiging succesvol verwijderd", "invite_expires_on": "Uitnodiging verloopt op {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Voeg een tijdelijke aanduiding toe om aan te geven of er geen waarde is om te onthouden.", "add_hidden_field_id": "Voeg een verborgen veld-ID toe", "add_highlight_border": "Markeerrand toevoegen", + "add_highlight_border_description": "Geldt alleen voor in-product enquêtes.", "add_logic": "Voeg logica toe", "add_none_of_the_above": "Voeg 'Geen van bovenstaande' toe", "add_option": "Optie toevoegen", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Follow-up bijgewerkt en wordt opgeslagen zodra u de enquête opslaat.", "follow_ups_new": "Nieuw vervolg", "follow_ups_upgrade_button_text": "Upgrade om follow-ups mogelijk te maken", - "form_styling": "Vorm styling", "formbricks_sdk_is_not_connected": "Formbricks SDK is niet verbonden", "four_points": "4 punten", "heading": "Rubriek", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Reactielimieten, omleidingen en meer.", "response_options": "Reactieopties", "roundness": "Rondheid", - "roundness_description": "Bepaalt hoe afgerond de kaarthoeken zijn.", + "roundness_description": "Bepaalt hoe afgerond de hoeken zijn.", "row_used_in_logic_error": "Deze rij wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.", "rows": "Rijen", "save_and_close": "Opslaan en sluiten", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Deze gratis en open source-enquête is gesloten", "survey_display_settings": "Enquêteweergave-instellingen", "survey_placement": "Enquête plaatsing", + "survey_styling": "Vorm styling", "survey_trigger": "Enquêtetrigger", "switch_multi_language_on_to_get_started": "Schakel meertaligheid in om te beginnen 👉", "target_block_not_found": "Doelblok niet gevonden", @@ -1755,7 +1758,6 @@ "welcome_message": "Welkomstbericht", "without_a_filter_all_of_your_users_can_be_surveyed": "Zonder filter kunnen al uw gebruikers worden bevraagd.", "you_have_not_created_a_segment_yet": "U heeft nog geen segment aangemaakt", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Je moet twee of meer talen hebben ingesteld in je werkruimte om met vertalingen te kunnen werken.", "your_description_here_recall_information_with": "Uw beschrijving hier. Roep informatie op met @", "your_question_here_recall_information_with": "Uw vraag hier. Roep informatie op met @", "your_web_app": "Uw web-app", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Kleurt het gevulde deel van de balk.", "advanced_styling_field_input_border_radius_description": "Rondt de invoerhoeken af.", "advanced_styling_field_input_font_size_description": "Schaalt de getypte tekst in invoervelden.", - "advanced_styling_field_input_height_description": "Bepaalt de minimale hoogte van het invoerveld.", + "advanced_styling_field_input_height_description": "Bepaalt de min. hoogte van het invoerveld.", "advanced_styling_field_input_padding_x_description": "Voegt ruimte toe aan de linker- en rechterkant.", "advanced_styling_field_input_padding_y_description": "Voegt ruimte toe aan de boven- en onderkant.", "advanced_styling_field_input_placeholder_opacity_description": "Vervaagt de tijdelijke aanwijzingstekst.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Kleurt de getypte tekst in invoervelden.", "advanced_styling_field_option_bg": "Achtergrond", "advanced_styling_field_option_bg_description": "Vult de optie-items.", + "advanced_styling_field_option_border": "Randkleur", + "advanced_styling_field_option_border_description": "Omlijnt radio- en checkboxopties.", "advanced_styling_field_option_border_radius_description": "Rondt de hoeken van opties af.", "advanced_styling_field_option_font_size_description": "Schaalt de tekst van optielabels.", "advanced_styling_field_option_label": "Labelkleur", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Nee, dank je!", "preview_survey_question_2_headline": "Wil je op de hoogte blijven?", "preview_survey_question_2_subheader": "Dit is een voorbeeldbeschrijving.", + "preview_survey_question_open_text_headline": "Wil je nog iets delen?", + "preview_survey_question_open_text_placeholder": "Typ hier je antwoord...", + "preview_survey_question_open_text_subheader": "Je feedback helpt ons verbeteren.", "preview_survey_welcome_card_headline": "Welkom!", "prioritize_features_description": "Identificeer functies die uw gebruikers het meest en het minst nodig hebben.", "prioritize_features_name": "Geef prioriteit aan functies", diff --git a/apps/web/locales/pt-BR.json b/apps/web/locales/pt-BR.json index 62abc45c89..aaaf950986 100644 --- a/apps/web/locales/pt-BR.json +++ b/apps/web/locales/pt-BR.json @@ -191,6 +191,7 @@ "days": "dias", "default": "Padrão", "delete": "Apagar", + "delete_what": "Excluir {deleteWhat}", "description": "Descrição", "dev_env": "Ambiente de Desenvolvimento", "development": "Desenvolvimento", @@ -287,6 +288,7 @@ "move_down": "Descer", "move_up": "Subir", "multiple_languages": "Vários idiomas", + "my_product": "meu produto", "name": "Nome", "new": "Novo", "new_version_available": "Formbricks {version} chegou. Atualize agora!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Oi {userName}", "email_customization_preview_email_text": "Esta é uma pré-visualização de e-mail para mostrar qual logo será renderizado nos e-mails.", "error_deleting_organization_please_try_again": "Erro ao deletar a organização. Por favor, tente novamente.", - "from_your_organization": "da sua organização", + "from_your_organization": "{memberName} da sua organização", "invitation_sent_once_more": "Convite enviado de novo.", "invite_deleted_successfully": "Convite deletado com sucesso", "invite_expires_on": "O convite expira em {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Adicionar um texto padrão para mostrar se a pergunta for ignorada:", "add_hidden_field_id": "Adicionar campo oculto ID", "add_highlight_border": "Adicionar borda de destaque", + "add_highlight_border_description": "Aplica-se apenas a pesquisas no produto.", "add_logic": "Adicionar lógica", "add_none_of_the_above": "Adicionar \"Nenhuma das opções acima\"", "add_option": "Adicionar opção", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Acompanhamento atualizado e será salvo assim que você salvar a pesquisa.", "follow_ups_new": "Novo acompanhamento", "follow_ups_upgrade_button_text": "Atualize para habilitar os Acompanhamentos", - "form_styling": "Estilização de Formulários", "formbricks_sdk_is_not_connected": "O SDK do Formbricks não está conectado", "four_points": "4 pontos", "heading": "Título", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.", "response_options": "Opções de Resposta", "roundness": "Circularidade", - "roundness_description": "Controla o arredondamento dos cantos do cartão.", + "roundness_description": "Controla o arredondamento dos cantos.", "row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.", "rows": "linhas", "save_and_close": "Salvar e Fechar", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Essa pesquisa gratuita e de código aberto foi encerrada", "survey_display_settings": "Configurações de Exibição da Pesquisa", "survey_placement": "Posicionamento da Pesquisa", + "survey_styling": "Estilização de Formulários", "survey_trigger": "Gatilho de Pesquisa", "switch_multi_language_on_to_get_started": "Ative o modo multilíngue para começar 👉", "target_block_not_found": "Bloco de destino não encontrado", @@ -1755,7 +1758,6 @@ "welcome_message": "Mensagem de boas-vindas", "without_a_filter_all_of_your_users_can_be_surveyed": "Sem um filtro, todos os seus usuários podem ser pesquisados.", "you_have_not_created_a_segment_yet": "Você ainda não criou um segmento.", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Você precisa ter dois ou mais idiomas configurados em seu espaço de trabalho para trabalhar com traduções.", "your_description_here_recall_information_with": "Sua descrição aqui. Lembre-se de informações com @", "your_question_here_recall_information_with": "Sua pergunta aqui. Lembre-se de informações com @", "your_web_app": "Sua aplicação web", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.", "advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.", "advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.", - "advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.", + "advanced_styling_field_input_height_description": "Controla a altura mínima da entrada.", "advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.", "advanced_styling_field_input_padding_y_description": "Adiciona espaço na parte superior e inferior.", "advanced_styling_field_input_placeholder_opacity_description": "Esmaece o texto de dica do placeholder.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.", "advanced_styling_field_option_bg": "Fundo", "advanced_styling_field_option_bg_description": "Preenche os itens de opção.", + "advanced_styling_field_option_border": "Cor da borda", + "advanced_styling_field_option_border_description": "Contorna as opções de botões de rádio e caixas de seleção.", "advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.", "advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto do rótulo da opção.", "advanced_styling_field_option_label": "Cor do rótulo", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Não, obrigado!", "preview_survey_question_2_headline": "Quer ficar por dentro?", "preview_survey_question_2_subheader": "Este é um exemplo de descrição.", + "preview_survey_question_open_text_headline": "Tem mais alguma coisa que você gostaria de compartilhar?", + "preview_survey_question_open_text_placeholder": "Digite sua resposta aqui...", + "preview_survey_question_open_text_subheader": "Seu feedback nos ajuda a melhorar.", "preview_survey_welcome_card_headline": "Bem-vindo!", "prioritize_features_description": "Identifique os recursos que seus usuários mais e menos precisam.", "prioritize_features_name": "Priorizar Funcionalidades", diff --git a/apps/web/locales/pt-PT.json b/apps/web/locales/pt-PT.json index 303c3d4739..2255827669 100644 --- a/apps/web/locales/pt-PT.json +++ b/apps/web/locales/pt-PT.json @@ -191,6 +191,7 @@ "days": "dias", "default": "Padrão", "delete": "Eliminar", + "delete_what": "Eliminar {deleteWhat}", "description": "Descrição", "dev_env": "Ambiente de Desenvolvimento", "development": "Desenvolvimento", @@ -287,6 +288,7 @@ "move_down": "Mover para baixo", "move_up": "Mover para cima", "multiple_languages": "Várias línguas", + "my_product": "o meu produto", "name": "Nome", "new": "Novo", "new_version_available": "Formbricks {version} está aqui. Atualize agora!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Olá {userName}", "email_customization_preview_email_text": "Esta é uma pré-visualização de email para mostrar qual logotipo será exibido nos emails.", "error_deleting_organization_please_try_again": "Erro ao eliminar a organização. Por favor, tente novamente.", - "from_your_organization": "da sua organização", + "from_your_organization": "{memberName} da sua organização", "invitation_sent_once_more": "Convite enviado mais uma vez.", "invite_deleted_successfully": "Convite eliminado com sucesso", "invite_expires_on": "O convite expira em {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Adicionar um espaço reservado para mostrar se não houver valor para recordar.", "add_hidden_field_id": "Adicionar ID do campo oculto", "add_highlight_border": "Adicionar borda de destaque", + "add_highlight_border_description": "Aplica-se apenas a inquéritos no produto.", "add_logic": "Adicionar lógica", "add_none_of_the_above": "Adicionar \"Nenhuma das Opções Acima\"", "add_option": "Adicionar opção", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Seguimento atualizado e será guardado assim que guardar o questionário.", "follow_ups_new": "Novo acompanhamento", "follow_ups_upgrade_button_text": "Atualize para ativar os acompanhamentos", - "form_styling": "Estilo do formulário", "formbricks_sdk_is_not_connected": "O SDK do Formbricks não está conectado", "four_points": "4 pontos", "heading": "Cabeçalho", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.", "response_options": "Opções de Resposta", "roundness": "Arredondamento", - "roundness_description": "Controla o arredondamento dos cantos do cartão.", + "roundness_description": "Controla o arredondamento dos cantos.", "row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.", "rows": "Linhas", "save_and_close": "Guardar e Fechar", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Este inquérito gratuito e de código aberto foi encerrado", "survey_display_settings": "Configurações de Exibição do Inquérito", "survey_placement": "Colocação do Inquérito", + "survey_styling": "Estilo do formulário", "survey_trigger": "Desencadeador de Inquérito", "switch_multi_language_on_to_get_started": "Ative o modo multilingue para começar 👉", "target_block_not_found": "Bloco de destino não encontrado", @@ -1755,7 +1758,6 @@ "welcome_message": "Mensagem de boas-vindas", "without_a_filter_all_of_your_users_can_be_surveyed": "Sem um filtro, todos os seus utilizadores podem ser pesquisados.", "you_have_not_created_a_segment_yet": "Ainda não criou um segmento", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Precisa de ter dois ou mais idiomas configurados no seu espaço de trabalho para trabalhar com traduções.", "your_description_here_recall_information_with": "A sua descrição aqui. Recorde a informação com @", "your_question_here_recall_information_with": "A sua pergunta aqui. Recorde a informação com @", "your_web_app": "A sua aplicação web", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.", "advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.", "advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.", - "advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.", + "advanced_styling_field_input_height_description": "Controla a altura mínima da entrada.", "advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.", "advanced_styling_field_input_padding_y_description": "Adiciona espaço no topo e na base.", "advanced_styling_field_input_placeholder_opacity_description": "Atenua o texto de sugestão do placeholder.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.", "advanced_styling_field_option_bg": "Fundo", "advanced_styling_field_option_bg_description": "Preenche os itens de opção.", + "advanced_styling_field_option_border": "Cor do contorno", + "advanced_styling_field_option_border_description": "Contorna as opções de botões de rádio e caixas de seleção.", "advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.", "advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto da etiqueta da opção.", "advanced_styling_field_option_label": "Cor da etiqueta", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Não, obrigado!", "preview_survey_question_2_headline": "Quer manter-se atualizado?", "preview_survey_question_2_subheader": "Este é um exemplo de descrição.", + "preview_survey_question_open_text_headline": "Mais alguma coisa que gostaria de partilhar?", + "preview_survey_question_open_text_placeholder": "Escreva a sua resposta aqui...", + "preview_survey_question_open_text_subheader": "O seu feedback ajuda-nos a melhorar.", "preview_survey_welcome_card_headline": "Bem-vindo!", "prioritize_features_description": "Identifique as funcionalidades que os seus utilizadores precisam mais e menos.", "prioritize_features_name": "Priorizar Funcionalidades", diff --git a/apps/web/locales/ro-RO.json b/apps/web/locales/ro-RO.json index c2400b0385..8f710bc871 100644 --- a/apps/web/locales/ro-RO.json +++ b/apps/web/locales/ro-RO.json @@ -191,6 +191,7 @@ "days": "zile", "default": "Implicit", "delete": "Șterge", + "delete_what": "Șterge {deleteWhat}", "description": "Descriere", "dev_env": "Mediu de dezvoltare", "development": "Dezvoltare", @@ -287,6 +288,7 @@ "move_down": "Mută în jos", "move_up": "Mută sus", "multiple_languages": "Mai multe limbi", + "my_product": "produsul meu", "name": "Nume", "new": "Nou", "new_version_available": "Formbricks {version} este disponibil. Actualizați acum!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Salut {userName}", "email_customization_preview_email_text": "Acesta este o previzualizare a e-mailului pentru a vă arăta ce logo va fi afișat în e-mailurile.", "error_deleting_organization_please_try_again": "Eroare la ștergerea organizației. Vă rugăm să încercați din nou.", - "from_your_organization": "din organizația ta", + "from_your_organization": "{memberName} din organizația ta", "invitation_sent_once_more": "Invitație trimisă din nou.", "invite_deleted_successfully": "Invitație ștearsă cu succes", "invite_expires_on": "Invitația expiră pe {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Adaugă un placeholder pentru a afișa dacă nu există valoare de reamintit", "add_hidden_field_id": "Adăugați ID câmp ascuns", "add_highlight_border": "Adaugă bordură evidențiată", + "add_highlight_border_description": "Se aplică doar sondajelor din produs.", "add_logic": "Adaugă logică", "add_none_of_the_above": "Adăugați \"Niciuna dintre cele de mai sus\"", "add_option": "Adăugați opțiune", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Urmărirea a fost actualizată și va fi salvată odată ce salvați sondajul.", "follow_ups_new": "Follow-up nou", "follow_ups_upgrade_button_text": "Actualizați pentru a activa urmărările", - "form_styling": "Stilizare formular", "formbricks_sdk_is_not_connected": "SDK Formbricks nu este conectat", "four_points": "4 puncte", "heading": "Titlu", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Limite de răspunsuri, redirecționări și altele.", "response_options": "Opțiuni răspuns", "roundness": "Rotunjire", - "roundness_description": "Controlează cât de rotunjite sunt colțurile cardului.", + "roundness_description": "Controlează cât de rotunjite sunt colțurile.", "row_used_in_logic_error": "Această linie este folosită în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.", "rows": "Rânduri", "save_and_close": "Salvează & Închide", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Acest sondaj gratuit și open-source a fost închis", "survey_display_settings": "Setări de afișare a sondajului", "survey_placement": "Amplasarea sondajului", + "survey_styling": "Stilizare formular", "survey_trigger": "Declanșator sondaj", "switch_multi_language_on_to_get_started": "Activați opțiunea multi-limbă pentru a începe 👉", "target_block_not_found": "Blocul țintă nu a fost găsit", @@ -1755,7 +1758,6 @@ "welcome_message": "Mesaj de bun venit", "without_a_filter_all_of_your_users_can_be_surveyed": "Fără un filtru, toți utilizatorii pot fi chestionați.", "you_have_not_created_a_segment_yet": "Nu ai creat încă un segment", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Trebuie să aveți cel puțin două limbi configurate în spațiul de lucru pentru a putea lucra cu traduceri.", "your_description_here_recall_information_with": "Descrierea ta aici. Reamintiți informațiile cu @", "your_question_here_recall_information_with": "Întrebarea ta aici. Reamintiți informațiile cu @", "your_web_app": "Aplicația dumneavoastră web", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Colorează textul introdus în câmpuri.", "advanced_styling_field_option_bg": "Fundal", "advanced_styling_field_option_bg_description": "Umple elementele de opțiune.", + "advanced_styling_field_option_border": "Culoare contur", + "advanced_styling_field_option_border_description": "Evidențiază opțiunile radio și checkbox.", "advanced_styling_field_option_border_radius_description": "Rotunjește colțurile opțiunilor.", "advanced_styling_field_option_font_size_description": "Redimensionează textul etichetei opțiunii.", "advanced_styling_field_option_label": "Culoare etichetă", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Nu, mulţumesc!", "preview_survey_question_2_headline": "Vrei să fii în temă?", "preview_survey_question_2_subheader": "Aceasta este o descriere exemplu.", + "preview_survey_question_open_text_headline": "Mai vrei să împărtășești ceva?", + "preview_survey_question_open_text_placeholder": "Tastează răspunsul aici...", + "preview_survey_question_open_text_subheader": "Feedbackul tău ne ajută să ne îmbunătățim.", "preview_survey_welcome_card_headline": "Bun venit!", "prioritize_features_description": "Identificați caracteristicile de care utilizatorii dumneavoastră au cel mai mult și cel mai puțin nevoie.", "prioritize_features_name": "Prioritizați caracteristicile", diff --git a/apps/web/locales/ru-RU.json b/apps/web/locales/ru-RU.json index 1d9e710532..be6d5bad69 100644 --- a/apps/web/locales/ru-RU.json +++ b/apps/web/locales/ru-RU.json @@ -191,6 +191,7 @@ "days": "дни", "default": "По умолчанию", "delete": "Удалить", + "delete_what": "Удалить {deleteWhat}", "description": "Описание", "dev_env": "Dev Environment", "development": "Разработка", @@ -287,6 +288,7 @@ "move_down": "Переместить вниз", "move_up": "Переместить вверх", "multiple_languages": "Несколько языков", + "my_product": "мой продукт", "name": "Имя", "new": "Новый", "new_version_available": "Formbricks {version} уже здесь. Обновитесь сейчас!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "Привет, {userName}", "email_customization_preview_email_text": "Это предварительный просмотр письма, чтобы показать, какой логотип будет отображаться в письмах.", "error_deleting_organization_please_try_again": "Ошибка при удалении организации. Пожалуйста, попробуйте ещё раз.", - "from_your_organization": "из вашей организации", + "from_your_organization": "{memberName} из вашей организации", "invitation_sent_once_more": "Приглашение отправлено ещё раз.", "invite_deleted_successfully": "Приглашение успешно удалено", "invite_expires_on": "Приглашение истекает {date}", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Добавить плейсхолдер, который будет показан, если нет значения для отображения.", "add_hidden_field_id": "Добавить скрытый ID поля", "add_highlight_border": "Добавить выделяющую рамку", + "add_highlight_border_description": "Применяется только к опросам внутри продукта.", "add_logic": "Добавить логику", "add_none_of_the_above": "Добавить вариант «Ничего из вышеперечисленного»", "add_option": "Добавить вариант", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Фоллоу-ап обновлён и будет сохранён после сохранения опроса.", "follow_ups_new": "Новый фоллоу-ап", "follow_ups_upgrade_button_text": "Обновите тариф для активации фоллоу-апов", - "form_styling": "Оформление формы", "formbricks_sdk_is_not_connected": "Formbricks SDK не подключён", "four_points": "4 балла", "heading": "Заголовок", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Лимиты ответов, перенаправления и другое.", "response_options": "Параметры ответа", "roundness": "Скругление", - "roundness_description": "Определяет степень скругления углов карточки.", + "roundness_description": "Определяет степень скругления углов.", "row_used_in_logic_error": "Эта строка используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите её из логики.", "rows": "Строки", "save_and_close": "Сохранить и закрыть", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Этот бесплатный и открытый опрос был закрыт", "survey_display_settings": "Настройки отображения опроса", "survey_placement": "Размещение опроса", + "survey_styling": "Оформление формы", "survey_trigger": "Триггер опроса", "switch_multi_language_on_to_get_started": "Включите многоязычный режим, чтобы начать 👉", "target_block_not_found": "Целевой блок не найден", @@ -1755,7 +1758,6 @@ "welcome_message": "Приветственное сообщение", "without_a_filter_all_of_your_users_can_be_surveyed": "Без фильтра все ваши пользователи могут быть опрошены.", "you_have_not_created_a_segment_yet": "Вы ещё не создали сегмент", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Для работы с переводами необходимо настроить два или более языков в рабочем пространстве.", "your_description_here_recall_information_with": "Ваша инструкция здесь. Вспомните информацию с помощью @", "your_question_here_recall_information_with": "Ваш вопрос здесь. Вспомните информацию с помощью @", "your_web_app": "Ваше веб-приложение", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Задаёт цвет заполненной части полосы.", "advanced_styling_field_input_border_radius_description": "Скругляет углы полей ввода.", "advanced_styling_field_input_font_size_description": "Масштабирует введённый текст в полях ввода.", - "advanced_styling_field_input_height_description": "Определяет минимальную высоту поля ввода.", + "advanced_styling_field_input_height_description": "Управляет минимальной высотой поля ввода.", "advanced_styling_field_input_padding_x_description": "Добавляет отступы слева и справа.", "advanced_styling_field_input_padding_y_description": "Добавляет пространство сверху и снизу.", "advanced_styling_field_input_placeholder_opacity_description": "Делает текст подсказки менее заметным.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Задаёт цвет введённого текста в полях.", "advanced_styling_field_option_bg": "Фон", "advanced_styling_field_option_bg_description": "Заливает фон элементов опций.", + "advanced_styling_field_option_border": "Цвет границы", + "advanced_styling_field_option_border_description": "Обводка для вариантов radio и checkbox.", "advanced_styling_field_option_border_radius_description": "Скругляет углы опций.", "advanced_styling_field_option_font_size_description": "Изменяет размер текста метки опции.", "advanced_styling_field_option_label": "Цвет метки", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Нет, спасибо!", "preview_survey_question_2_headline": "Хотите быть в курсе событий?", "preview_survey_question_2_subheader": "Это пример описания.", + "preview_survey_question_open_text_headline": "Есть ли ещё что-то, чем хочешь поделиться?", + "preview_survey_question_open_text_placeholder": "Введи свой ответ здесь...", + "preview_survey_question_open_text_subheader": "Твой отзыв помогает нам становиться лучше.", "preview_survey_welcome_card_headline": "Добро пожаловать!", "prioritize_features_description": "Определите, какие функции наиболее и наименее важны для ваших пользователей.", "prioritize_features_name": "Приоритизация функций", diff --git a/apps/web/locales/sv-SE.json b/apps/web/locales/sv-SE.json index 07707eb2e5..6f09b01276 100644 --- a/apps/web/locales/sv-SE.json +++ b/apps/web/locales/sv-SE.json @@ -191,6 +191,7 @@ "days": "dagar", "default": "Standard", "delete": "Ta bort", + "delete_what": "Ta bort {deleteWhat}", "description": "Beskrivning", "dev_env": "Utvecklingsmiljö", "development": "Utveckling", @@ -287,6 +288,7 @@ "move_down": "Flytta ner", "move_up": "Flytta upp", "multiple_languages": "Flera språk", + "my_product": "min produkt", "name": "Namn", "new": "Ny", "new_version_available": "Formbricks {version} är här. Uppgradera nu!", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "Lägg till en platshållare att visa om det inte finns något värde att återkalla.", "add_hidden_field_id": "Lägg till dolt fält-ID", "add_highlight_border": "Lägg till markerad kant", + "add_highlight_border_description": "Gäller bara för undersökningar i produkten.", "add_logic": "Lägg till logik", "add_none_of_the_above": "Lägg till \"Inget av ovanstående\"", "add_option": "Lägg till alternativ", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "Uppföljning uppdaterad och sparas när du sparar enkäten.", "follow_ups_new": "Ny uppföljning", "follow_ups_upgrade_button_text": "Uppgradera för att aktivera uppföljningar", - "form_styling": "Formulärstil", "formbricks_sdk_is_not_connected": "Formbricks SDK är inte anslutet", "four_points": "4 poäng", "heading": "Rubrik", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "Svarsgränser, omdirigeringar och mer.", "response_options": "Svarsalternativ", "roundness": "Rundhet", - "roundness_description": "Styr hur rundade kortets hörn är.", + "roundness_description": "Styr hur rundade hörnen är.", "row_used_in_logic_error": "Denna rad används i logiken för fråga {questionIndex}. Vänligen ta bort den från logiken först.", "rows": "Rader", "save_and_close": "Spara och stäng", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "Denna gratis och öppenkällkodsenkät har stängts", "survey_display_settings": "Visningsinställningar för enkät", "survey_placement": "Enkätplacering", + "survey_styling": "Formulärstil", "survey_trigger": "Enkätutlösare", "switch_multi_language_on_to_get_started": "Slå på flerspråkighet för att komma igång 👉", "target_block_not_found": "Målblock hittades inte", @@ -1755,7 +1758,6 @@ "welcome_message": "Välkomstmeddelande", "without_a_filter_all_of_your_users_can_be_surveyed": "Utan ett filter kan alla dina användare enkäteras.", "you_have_not_created_a_segment_yet": "Du har inte skapat ett segment ännu", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Du måste ha två eller fler språk inställda i din arbetsyta för att kunna arbeta med översättningar.", "your_description_here_recall_information_with": "Din beskrivning här. Återkalla information med @", "your_question_here_recall_information_with": "Din fråga här. Återkalla information med @", "your_web_app": "Din webbapp", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "Färglägger den fyllda delen av stapeln.", "advanced_styling_field_input_border_radius_description": "Rundar av hörnen på inmatningsfält.", "advanced_styling_field_input_font_size_description": "Ändrar storleken på texten i inmatningsfält.", - "advanced_styling_field_input_height_description": "Styr den minsta höjden på inmatningsfältet.", + "advanced_styling_field_input_height_description": "Styr minsta höjden på inmatningsfältet.", "advanced_styling_field_input_padding_x_description": "Lägger till utrymme till vänster och höger.", "advanced_styling_field_input_padding_y_description": "Lägger till utrymme upptill och nedtill.", "advanced_styling_field_input_placeholder_opacity_description": "Tonar ut platshållartexten.", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "Färgar den inmatade texten i fälten.", "advanced_styling_field_option_bg": "Bakgrund", "advanced_styling_field_option_bg_description": "Fyller alternativraderna.", + "advanced_styling_field_option_border": "Kantfärg", + "advanced_styling_field_option_border_description": "Markerar radio- och kryssrutealternativ.", "advanced_styling_field_option_border_radius_description": "Rundar hörnen på alternativen.", "advanced_styling_field_option_font_size_description": "Skalar textstorleken på alternativetiketten.", "advanced_styling_field_option_label": "Etikettfärg", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "Nej, tack!", "preview_survey_question_2_headline": "Vill du hållas uppdaterad?", "preview_survey_question_2_subheader": "Det här är ett exempel på en beskrivning.", + "preview_survey_question_open_text_headline": "Något mer du vill dela med dig av?", + "preview_survey_question_open_text_placeholder": "Skriv ditt svar här...", + "preview_survey_question_open_text_subheader": "Din feedback hjälper oss att bli bättre.", "preview_survey_welcome_card_headline": "Välkommen!", "prioritize_features_description": "Identifiera vilka funktioner dina användare behöver mest och minst.", "prioritize_features_name": "Prioritera funktioner", diff --git a/apps/web/locales/zh-Hans-CN.json b/apps/web/locales/zh-Hans-CN.json index db1d7a6e1c..89ea04ef27 100644 --- a/apps/web/locales/zh-Hans-CN.json +++ b/apps/web/locales/zh-Hans-CN.json @@ -191,6 +191,7 @@ "days": "天", "default": "默认", "delete": "删除", + "delete_what": "删除{deleteWhat}", "description": "描述", "dev_env": "开发 环境", "development": "开发环境", @@ -287,6 +288,7 @@ "move_down": "下移", "move_up": "上移", "multiple_languages": "多种 语言", + "my_product": "我的产品", "name": "名称", "new": "新建", "new_version_available": "Formbricks {version} 在 这里。立即 升级!", @@ -1083,7 +1085,7 @@ "email_customization_preview_email_heading": "嘿 {userName}", "email_customization_preview_email_text": "这 是 一封 电子邮件 预览,展示 哪个 徽标 将在 电子邮件 中 渲染。", "error_deleting_organization_please_try_again": "删除 组织时 出错 。 请重试 。", - "from_your_organization": "来自你的组织", + "from_your_organization": "来自您组织的{memberName}", "invitation_sent_once_more": "再次发送邀请。", "invite_deleted_successfully": "邀请 删除 成功", "invite_expires_on": "邀请将于 {date} 过期", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "添加 占位符 显示 如果 没有 值以 回忆", "add_hidden_field_id": "添加 隐藏 字段 ID", "add_highlight_border": "添加 高亮 边框", + "add_highlight_border_description": "仅适用于产品内调查。", "add_logic": "添加逻辑", "add_none_of_the_above": "添加 “以上 都 不 是”", "add_option": "添加 选项", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "后续 操作 已 更新, 并且 在 你 保存 调查 后 将 被 保存。", "follow_ups_new": "新的跟进", "follow_ups_upgrade_button_text": "升级 以启用 跟进", - "form_styling": "表单 样式", "formbricks_sdk_is_not_connected": "Formbricks SDK 未连接", "four_points": "4 分", "heading": "标题", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "响应 限制 、 重定向 和 更多 。", "response_options": "响应 选项", "roundness": "圆度", - "roundness_description": "控制卡片角的圆润程度。", + "roundness_description": "控制圆角的弧度。", "row_used_in_logic_error": "\"这个 行 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"", "rows": "行", "save_and_close": "保存 和 关闭", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "此 免费 & 开源 调查 已 关闭", "survey_display_settings": "调查显示设置", "survey_placement": "调查 放置", + "survey_styling": "表单 样式", "survey_trigger": "调查 触发", "switch_multi_language_on_to_get_started": "开启多语言以开始使用 👉", "target_block_not_found": "未找到目标区块", @@ -1755,7 +1758,6 @@ "welcome_message": "欢迎 信息", "without_a_filter_all_of_your_users_can_be_surveyed": "没有 过滤器 时 ,所有 用户 都可以 被 调查 。", "you_have_not_created_a_segment_yet": "您 还没有 创建 段落", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "要使用翻译功能,您的工作区需设置两种或以上语言。", "your_description_here_recall_information_with": "在此输入描述。 调用信息与 @", "your_question_here_recall_information_with": "在此输入你的问题。 调用信息与 @", "your_web_app": "您的 网页应用", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "设置进度条已填充部分的颜色。", "advanced_styling_field_input_border_radius_description": "设置输入框圆角。", "advanced_styling_field_input_font_size_description": "调整输入框内文字大小。", - "advanced_styling_field_input_height_description": "设置输入框的最小高度。", + "advanced_styling_field_input_height_description": "控制输入框的最小高度。", "advanced_styling_field_input_padding_x_description": "增加输入框左右间距。", "advanced_styling_field_input_padding_y_description": "为输入框上下添加间距。", "advanced_styling_field_input_placeholder_opacity_description": "调整占位提示文字的透明度。", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "设置输入框内已输入文字的颜色。", "advanced_styling_field_option_bg": "背景色", "advanced_styling_field_option_bg_description": "设置选项项的背景色。", + "advanced_styling_field_option_border": "边框颜色", + "advanced_styling_field_option_border_description": "为单选框和复选框选项添加轮廓。", "advanced_styling_field_option_border_radius_description": "设置选项的圆角。", "advanced_styling_field_option_font_size_description": "调整选项标签文字的大小。", "advanced_styling_field_option_label": "标签颜色", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "不,谢谢!", "preview_survey_question_2_headline": "想 了解 最新信息吗?", "preview_survey_question_2_subheader": "这是一个示例描述。", + "preview_survey_question_open_text_headline": "还有什么想和我们分享的吗?", + "preview_survey_question_open_text_placeholder": "请在这里输入你的答案...", + "preview_survey_question_open_text_subheader": "你的反馈能帮助我们改进。", "preview_survey_welcome_card_headline": "欢迎!", "prioritize_features_description": "确定 用户 最 需要 和 最 不 需要 的 功能。", "prioritize_features_name": "优先 功能", diff --git a/apps/web/locales/zh-Hant-TW.json b/apps/web/locales/zh-Hant-TW.json index 8a14b44fa2..9c528fb07e 100644 --- a/apps/web/locales/zh-Hant-TW.json +++ b/apps/web/locales/zh-Hant-TW.json @@ -191,6 +191,7 @@ "days": "天", "default": "預設", "delete": "刪除", + "delete_what": "刪除{deleteWhat}", "description": "描述", "dev_env": "開發環境", "development": "開發", @@ -287,6 +288,7 @@ "move_down": "下移", "move_up": "上移", "multiple_languages": "多種語言", + "my_product": "我的產品", "name": "名稱", "new": "新增", "new_version_available": "Formbricks '{'version'}' 已推出。立即升級!", @@ -1248,6 +1250,7 @@ "add_fallback_placeholder": "新增 預設 以顯示是否沒 有 值 可 回憶 。", "add_hidden_field_id": "新增隱藏欄位 ID", "add_highlight_border": "新增醒目提示邊框", + "add_highlight_border_description": "僅適用於產品內調查。", "add_logic": "新增邏輯", "add_none_of_the_above": "新增 \"以上皆非\"", "add_option": "新增選項", @@ -1446,7 +1449,6 @@ "follow_ups_modal_updated_successfull_toast": "後續 動作 已 更新 並 將 在 你 儲存 調查 後 儲存", "follow_ups_new": "新增後續追蹤", "follow_ups_upgrade_button_text": "升級以啟用後續追蹤", - "form_styling": "表單樣式設定", "formbricks_sdk_is_not_connected": "Formbricks SDK 未連線", "four_points": "4 分", "heading": "標題", @@ -1619,7 +1621,7 @@ "response_limits_redirections_and_more": "回應限制、重新導向等。", "response_options": "回應選項", "roundness": "圓角", - "roundness_description": "調整卡片邊角的圓弧度。", + "roundness_description": "調整邊角的圓潤程度。", "row_used_in_logic_error": "此 row 用於問題 '{'questionIndex'}' 的邏輯中。請先從邏輯中移除。", "rows": "列", "save_and_close": "儲存並關閉", @@ -1665,6 +1667,7 @@ "survey_completed_subheading": "此免費且開源的問卷已關閉", "survey_display_settings": "問卷顯示設定", "survey_placement": "問卷位置", + "survey_styling": "表單樣式設定", "survey_trigger": "問卷觸發器", "switch_multi_language_on_to_get_started": "請開啟多語言功能以開始使用 👉", "target_block_not_found": "找不到目標區塊", @@ -1755,7 +1758,6 @@ "welcome_message": "歡迎訊息", "without_a_filter_all_of_your_users_can_be_surveyed": "如果沒有篩選器,則可以調查您的所有使用者。", "you_have_not_created_a_segment_yet": "您尚未建立區隔", - "you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "您必須在工作區中設定兩種或以上語言,才能進行翻譯作業。", "your_description_here_recall_information_with": "您的描述在這裡。使用 @ 回憶資訊", "your_question_here_recall_information_with": "您的問題在這裡。使用 @ 回憶資訊", "your_web_app": "您的 Web 應用程式", @@ -2176,7 +2178,7 @@ "advanced_styling_field_indicator_bg_description": "設定進度條已填滿部分的顏色。", "advanced_styling_field_input_border_radius_description": "調整輸入框的圓角。", "advanced_styling_field_input_font_size_description": "調整輸入框內輸入文字的大小。", - "advanced_styling_field_input_height_description": "設定輸入欄位的最小高度。", + "advanced_styling_field_input_height_description": "控制輸入欄的最小高度。", "advanced_styling_field_input_padding_x_description": "在左右兩側增加間距。", "advanced_styling_field_input_padding_y_description": "在上方和下方增加間距。", "advanced_styling_field_input_placeholder_opacity_description": "讓提示文字變得更淡。", @@ -2185,6 +2187,8 @@ "advanced_styling_field_input_text_description": "設定輸入文字的顏色。", "advanced_styling_field_option_bg": "背景", "advanced_styling_field_option_bg_description": "填滿選項項目背景。", + "advanced_styling_field_option_border": "邊框顏色", + "advanced_styling_field_option_border_description": "為單選框和核取方塊選項加上外框。", "advanced_styling_field_option_border_radius_description": "讓選項的邊角變圓。", "advanced_styling_field_option_font_size_description": "調整選項標籤文字的大小。", "advanced_styling_field_option_label": "標籤顏色", @@ -3004,6 +3008,9 @@ "preview_survey_question_2_choice_2_label": "不用了,謝謝!", "preview_survey_question_2_headline": "想要緊跟最新動態嗎?", "preview_survey_question_2_subheader": "這是一個範例說明。", + "preview_survey_question_open_text_headline": "還有什麼想和我們分享的嗎?", + "preview_survey_question_open_text_placeholder": "在此輸入您的答案...", + "preview_survey_question_open_text_subheader": "您的回饋能幫助我們進步。", "preview_survey_welcome_card_headline": "歡迎!", "prioritize_features_description": "找出您的使用者最需要和最不需要的功能。", "prioritize_features_name": "優先排序功能", diff --git a/apps/web/modules/analysis/components/SingleResponseCard/components/SingleResponseCardBody.tsx b/apps/web/modules/analysis/components/SingleResponseCard/components/SingleResponseCardBody.tsx index ed843be4ac..51850440d3 100644 --- a/apps/web/modules/analysis/components/SingleResponseCard/components/SingleResponseCardBody.tsx +++ b/apps/web/modules/analysis/components/SingleResponseCard/components/SingleResponseCardBody.tsx @@ -42,7 +42,7 @@ export const SingleResponseCardBody = ({ return ( + className="ml-0.5 mr-0.5 rounded-md border border-slate-200 bg-slate-50 px-1 py-0.5 text-sm first:ml-0"> @{part} ); diff --git a/apps/web/modules/auth/email-change-without-verification-success/page.tsx b/apps/web/modules/auth/email-change-without-verification-success/page.tsx index b0d270f685..54ce7682be 100644 --- a/apps/web/modules/auth/email-change-without-verification-success/page.tsx +++ b/apps/web/modules/auth/email-change-without-verification-success/page.tsx @@ -15,7 +15,7 @@ export const EmailChangeWithoutVerificationSuccessPage = async () => { } return ( -
+

{t("auth.email-change.email_change_success")} diff --git a/apps/web/modules/auth/forgot-password/components/forgot-password-form.tsx b/apps/web/modules/auth/forgot-password/components/forgot-password-form.tsx index e0ce651e01..39bcd13aa2 100644 --- a/apps/web/modules/auth/forgot-password/components/forgot-password-form.tsx +++ b/apps/web/modules/auth/forgot-password/components/forgot-password-form.tsx @@ -60,7 +60,7 @@ export const ForgotPasswordForm = () => { onChange={(e) => field.onChange(e)} autoComplete="email" required - className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 shadow-sm sm:text-sm" + className="block w-full rounded-md border-slate-300 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" /> {error?.message && {error.message}} diff --git a/apps/web/modules/auth/invite/types/invites.ts b/apps/web/modules/auth/invite/types/invites.ts index 367413cbd3..ef0a070a22 100644 --- a/apps/web/modules/auth/invite/types/invites.ts +++ b/apps/web/modules/auth/invite/types/invites.ts @@ -1,8 +1,10 @@ import { Invite } from "@prisma/client"; import { TUserLocale } from "@formbricks/types/user"; -export interface InviteWithCreator - extends Pick { +export interface InviteWithCreator extends Pick< + Invite, + "id" | "expiresAt" | "organizationId" | "role" | "teamIds" +> { creator: { name: string | null; email: string; diff --git a/apps/web/modules/auth/layout.tsx b/apps/web/modules/auth/layout.tsx index a9e3631f1e..71bd243bac 100644 --- a/apps/web/modules/auth/layout.tsx +++ b/apps/web/modules/auth/layout.tsx @@ -24,7 +24,7 @@ export const AuthLayout = async ({ children }: { children: React.ReactNode }) =>
-
{children}
+
{children}
diff --git a/apps/web/modules/auth/lib/authOptions.test.ts b/apps/web/modules/auth/lib/authOptions.test.ts index 55ca265eb7..2f5d934e5e 100644 --- a/apps/web/modules/auth/lib/authOptions.test.ts +++ b/apps/web/modules/auth/lib/authOptions.test.ts @@ -195,6 +195,7 @@ describe("authOptions", () => { vi.mocked(applyIPRateLimit).mockRejectedValue( new Error("Maximum number of requests reached. Please try again later.") ); + const findUniqueSpy = vi.spyOn(prisma.user, "findUnique"); const credentials = { email: mockUser.email, password: mockPassword }; @@ -202,7 +203,7 @@ describe("authOptions", () => { "Maximum number of requests reached. Please try again later." ); - expect(prisma.user.findUnique).not.toHaveBeenCalled(); + expect(findUniqueSpy).not.toHaveBeenCalled(); }); test("should use correct rate limit configuration", async () => { @@ -281,6 +282,7 @@ describe("authOptions", () => { vi.mocked(applyIPRateLimit).mockRejectedValue( new Error("Maximum number of requests reached. Please try again later.") ); + const findUniqueSpy = vi.spyOn(prisma.user, "findUnique"); const credentials = { token: "sometoken" }; @@ -288,7 +290,7 @@ describe("authOptions", () => { "Maximum number of requests reached. Please try again later." ); - expect(prisma.user.findUnique).not.toHaveBeenCalled(); + expect(findUniqueSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/apps/web/modules/auth/login/components/login-form.tsx b/apps/web/modules/auth/login/components/login-form.tsx index 85a98cfd8d..f9533fd9c3 100644 --- a/apps/web/modules/auth/login/components/login-form.tsx +++ b/apps/web/modules/auth/login/components/login-form.tsx @@ -184,7 +184,7 @@ export const LoginForm = ({ value={field.value} onChange={(email) => field.onChange(email)} placeholder="work@email.com" - className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 shadow-sm sm:text-sm" + className="block w-full rounded-md border-slate-300 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" /> {error?.message && {error.message}}

@@ -207,7 +207,7 @@ export const LoginForm = ({ aria-label="password" aria-required="true" required - className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 pr-8 shadow-sm sm:text-sm" + className="block w-full rounded-md border-slate-300 pr-8 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" value={field.value} onChange={(password) => field.onChange(password)} /> @@ -221,7 +221,7 @@ export const LoginForm = ({
+ className="text-xs text-slate-500 hover:text-brand-dark"> {t("auth.login.forgot_your_password")}
diff --git a/apps/web/modules/auth/signup/components/signup-form.tsx b/apps/web/modules/auth/signup/components/signup-form.tsx index 34baef82de..0741c07d7c 100644 --- a/apps/web/modules/auth/signup/components/signup-form.tsx +++ b/apps/web/modules/auth/signup/components/signup-form.tsx @@ -222,7 +222,7 @@ export const SignupForm = ({ placeholder="*******" aria-placeholder="password" required - className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md shadow-sm sm:text-sm" + className="block w-full rounded-md shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" /> {error?.message && {error.message}}
diff --git a/apps/web/modules/auth/verify-email-change/page.tsx b/apps/web/modules/auth/verify-email-change/page.tsx index f4813eac26..373c934f37 100644 --- a/apps/web/modules/auth/verify-email-change/page.tsx +++ b/apps/web/modules/auth/verify-email-change/page.tsx @@ -6,7 +6,7 @@ export const VerifyEmailChangePage = async ({ searchParams }) => { const { token } = await searchParams; return ( -
+
diff --git a/apps/web/modules/ee/billing/components/pricing-table.tsx b/apps/web/modules/ee/billing/components/pricing-table.tsx index a28d5ca07d..35a652502d 100644 --- a/apps/web/modules/ee/billing/components/pricing-table.tsx +++ b/apps/web/modules/ee/billing/components/pricing-table.tsx @@ -138,7 +138,7 @@ export const PricingTable = ({
-

+

{t("environments.settings.billing.current_plan")}:{" "} {organization.billing.plan} {cancellingOn && ( @@ -203,7 +203,7 @@ export const PricingTable = ({

{t("environments.settings.billing.monthly_identified_users")} @@ -226,7 +226,7 @@ export const PricingTable = ({

{t("common.workspaces")}

{organization.billing.limits.projects && ( @@ -264,7 +264,7 @@ export const PricingTable = ({
diff --git a/apps/web/modules/ee/contacts/[contactId]/components/contact-control-bar.tsx b/apps/web/modules/ee/contacts/[contactId]/components/contact-control-bar.tsx index 294f387eb5..bec1f55775 100644 --- a/apps/web/modules/ee/contacts/[contactId]/components/contact-control-bar.tsx +++ b/apps/web/modules/ee/contacts/[contactId]/components/contact-control-bar.tsx @@ -99,7 +99,7 @@ export const ContactControlBar = ({ { const description = row.original.description; return description ? ( -
+
) : ( diff --git a/apps/web/modules/ee/contacts/components/upload-contacts-attribute.tsx b/apps/web/modules/ee/contacts/components/upload-contacts-attribute.tsx index 19bae9c7a6..79a3052da2 100644 --- a/apps/web/modules/ee/contacts/components/upload-contacts-attribute.tsx +++ b/apps/web/modules/ee/contacts/components/upload-contacts-attribute.tsx @@ -132,7 +132,7 @@ export const UploadContactsAttributes = ({ return ( <> - {csvColumn} + {csvColumn}
processCsvRecord(record, processingContext)); + const CHUNK_SIZE = 50; + const allResults: (TContact | null)[] = []; - const results = await Promise.all(contactPromises); - return { contacts: results.filter((contact): contact is TContact => contact !== null) }; + for (let i = 0; i < csvData.length; i += CHUNK_SIZE) { + const chunk = csvData.slice(i, i + CHUNK_SIZE); + const chunkResults = await Promise.all( + chunk.map((record) => processCsvRecord(record, processingContext)) + ); + allResults.push(...chunkResults); + } + + return { contacts: allResults.filter((contact): contact is TContact => contact !== null) }; } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseError(error.message); diff --git a/apps/web/modules/ee/contacts/segments/components/edit-segment-modal.tsx b/apps/web/modules/ee/contacts/segments/components/edit-segment-modal.tsx index b16afa8670..472cca0d4b 100644 --- a/apps/web/modules/ee/contacts/segments/components/edit-segment-modal.tsx +++ b/apps/web/modules/ee/contacts/segments/components/edit-segment-modal.tsx @@ -93,7 +93,7 @@ export const EditSegmentModal = ({ key={tab.title} className={`mr-4 px-1 pb-3 focus:outline-none ${ activeTab === index - ? "border-brand-dark border-b-2 font-semibold text-slate-900" + ? "border-b-2 border-brand-dark font-semibold text-slate-900" : "text-slate-500 hover:text-slate-700" }`} onClick={() => handleTabClick(index)}> diff --git a/apps/web/modules/ee/contacts/segments/components/segment-table-data-row.tsx b/apps/web/modules/ee/contacts/segments/components/segment-table-data-row.tsx index e1a0ff8282..45943498aa 100644 --- a/apps/web/modules/ee/contacts/segments/components/segment-table-data-row.tsx +++ b/apps/web/modules/ee/contacts/segments/components/segment-table-data-row.tsx @@ -42,7 +42,7 @@ export const SegmentTableDataRow = ({
-
+
{surveys?.length}
@@ -52,7 +52,7 @@ export const SegmentTableDataRow = ({ }).replace("about", "")}
-
+
{format(createdAt, "do 'of' MMMM, yyyy")}
diff --git a/apps/web/modules/ee/contacts/segments/components/targeting-card.tsx b/apps/web/modules/ee/contacts/segments/components/targeting-card.tsx index b5cd3a4f75..c0207abc4b 100644 --- a/apps/web/modules/ee/contacts/segments/components/targeting-card.tsx +++ b/apps/web/modules/ee/contacts/segments/components/targeting-card.tsx @@ -176,7 +176,7 @@ export function TargetingCard({ asChild className="h-full w-full cursor-pointer rounded-lg hover:bg-slate-50">
-
+
) : ( -

+

{t("environments.workspace.languages.no_language_found")}

)} diff --git a/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx b/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx index c12465d70f..56f19cc721 100644 --- a/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx +++ b/apps/web/modules/ee/multi-language-surveys/components/multi-language-card.tsx @@ -56,7 +56,7 @@ export const MultiLanguageCard: FC = ({ const { t } = useTranslation(); const environmentId = localSurvey.environmentId; const open = activeElementId === "multiLanguage"; - const [isMultiLanguageActivated, setIsMultiLanguageActivated] = useState(localSurvey.languages.length > 1); + const [isMultiLanguageActivated, setIsMultiLanguageActivated] = useState(localSurvey.languages.length > 0); const [confirmationModalInfo, setConfirmationModalInfo] = useState({ title: "", open: false, @@ -188,7 +188,7 @@ export const MultiLanguageCard: FC = ({

@@ -250,19 +250,15 @@ export const MultiLanguageCard: FC = ({ /> ) : ( <> - {projectLanguages.length <= 1 && ( -

- {projectLanguages.length === 0 - ? t("environments.surveys.edit.no_languages_found_add_first_one_to_get_started") - : t( - "environments.surveys.edit.you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations" - )} + {projectLanguages.length === 0 && ( +
+ {t("environments.surveys.edit.no_languages_found_add_first_one_to_get_started")}
)} - {projectLanguages.length > 1 && ( + {projectLanguages.length > 0 && (
{isMultiLanguageAllowed && !isMultiLanguageActivated ? ( -
+
{t("environments.surveys.edit.switch_multi_language_on_to_get_started")}
) : null} @@ -276,7 +272,7 @@ export const MultiLanguageCard: FC = ({ setConfirmationModalInfo={setConfirmationModalInfo} locale={locale} /> - {defaultLanguage ? ( + {defaultLanguage && projectLanguages.length > 1 ? ( field.onChange(password)} value={field.value} - className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 shadow-sm sm:text-sm" + className="block w-full rounded-md border-slate-300 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" /> {error?.message && {error.message}} diff --git a/apps/web/modules/ee/two-factor-auth/components/disable-two-factor-modal.tsx b/apps/web/modules/ee/two-factor-auth/components/disable-two-factor-modal.tsx index 72d7c773b4..5ece8bd615 100644 --- a/apps/web/modules/ee/two-factor-auth/components/disable-two-factor-modal.tsx +++ b/apps/web/modules/ee/two-factor-auth/components/disable-two-factor-modal.tsx @@ -109,7 +109,7 @@ export const DisableTwoFactorModal = ({ open, setOpen }: DisableTwoFactorModalPr required onChange={(password) => field.onChange(password)} value={field.value} - className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 shadow-sm sm:text-sm" + className="block w-full rounded-md border-slate-300 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" /> {error?.message && {error.message}} diff --git a/apps/web/modules/ee/two-factor-auth/components/two-factor-backup.tsx b/apps/web/modules/ee/two-factor-auth/components/two-factor-backup.tsx index d3e6b0b72d..ec549435e7 100644 --- a/apps/web/modules/ee/two-factor-auth/components/two-factor-backup.tsx +++ b/apps/web/modules/ee/two-factor-auth/components/two-factor-backup.tsx @@ -35,7 +35,7 @@ export const TwoFactorBackup = ({ form }: TwoFactorBackupProps) => { id="totpBackup" required placeholder="XXXXX-XXXXX" - className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 shadow-sm sm:text-sm" + className="block w-full rounded-md border-slate-300 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" value={field.value} onChange={(e) => field.onChange(e.target.value)} /> diff --git a/apps/web/modules/ee/whitelabel/email-customization/components/email-customization-settings.tsx b/apps/web/modules/ee/whitelabel/email-customization/components/email-customization-settings.tsx index 4f2ceb97c9..9ac826d3ec 100644 --- a/apps/web/modules/ee/whitelabel/email-customization/components/email-customization-settings.tsx +++ b/apps/web/modules/ee/whitelabel/email-customization/components/email-customization-settings.tsx @@ -206,7 +206,7 @@ export const EmailCustomizationSettings = ({
{t("environments.settings.general.logo_in_email_header")} -
+
{logoUrl && (
@@ -276,7 +276,7 @@ export const EmailCustomizationSettings = ({
-
+
+ {t("common.only_owners_managers_and_manage_access_members_can_perform_this_action")} diff --git a/apps/web/modules/integrations/webhooks/components/webhook-detail-modal.tsx b/apps/web/modules/integrations/webhooks/components/webhook-detail-modal.tsx index 514e117748..74b5b358d0 100644 --- a/apps/web/modules/integrations/webhooks/components/webhook-detail-modal.tsx +++ b/apps/web/modules/integrations/webhooks/components/webhook-detail-modal.tsx @@ -70,7 +70,7 @@ export const WebhookModal = ({ open, setOpen, webhook, surveys, isReadOnly }: We type="button" className={`mr-4 px-1 pb-3 focus:outline-none ${ activeTab === index - ? "border-brand-dark border-b-2 font-semibold text-slate-900" + ? "border-b-2 border-brand-dark font-semibold text-slate-900" : "text-slate-500 hover:text-slate-700" }`} onClick={() => handleTabClick(index)}> diff --git a/apps/web/modules/organization/settings/api-keys/components/edit-api-keys.tsx b/apps/web/modules/organization/settings/api-keys/components/edit-api-keys.tsx index f735e1b9de..b40b99e05d 100644 --- a/apps/web/modules/organization/settings/api-keys/components/edit-api-keys.tsx +++ b/apps/web/modules/organization/settings/api-keys/components/edit-api-keys.tsx @@ -134,7 +134,7 @@ export const EditAPIKeys = ({ organizationId, apiKeys, locale, isReadOnly, proje return (
- {apiKey} + {apiKey}
{apiKeysLocal?.length === 0 ? ( -
+
{t("environments.workspace.api_keys.no_api_keys_yet")}
) : ( diff --git a/apps/web/modules/organization/settings/api-keys/loading.tsx b/apps/web/modules/organization/settings/api-keys/loading.tsx index c19854bdbf..b12b9c9fb9 100644 --- a/apps/web/modules/organization/settings/api-keys/loading.tsx +++ b/apps/web/modules/organization/settings/api-keys/loading.tsx @@ -10,7 +10,7 @@ const LoadingCard = () => { return (
-

+

{t("common.loading")}

diff --git a/apps/web/modules/organization/settings/api-keys/types/api-keys.ts b/apps/web/modules/organization/settings/api-keys/types/api-keys.ts index 81de781f2a..f6c9b7f748 100644 --- a/apps/web/modules/organization/settings/api-keys/types/api-keys.ts +++ b/apps/web/modules/organization/settings/api-keys/types/api-keys.ts @@ -50,8 +50,10 @@ export const TApiKeyEnvironmentPermission = z.object({ export type TApiKeyEnvironmentPermission = z.infer; -export interface TApiKeyWithEnvironmentPermission - extends Pick { +export interface TApiKeyWithEnvironmentPermission extends Pick< + ApiKey, + "id" | "label" | "createdAt" | "organizationAccess" +> { apiKeyEnvironments: TApiKeyEnvironmentPermission[]; } diff --git a/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx b/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx index 9f40b78134..bfdc75071f 100644 --- a/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx +++ b/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx @@ -168,7 +168,7 @@ export const MemberActions = ({ organization, member, invite, showDeleteButton } { - const inviteUserActionResult = await inviteUserAction({ - organizationId: organization.id, - email: email.toLowerCase(), - name, - role, - teamIds, - }); - return { - email, - success: Boolean(inviteUserActionResult?.data), - }; - }) - ); - let failedInvites: string[] = []; - let successInvites: string[] = []; - invitePromises.forEach((invite) => { + const inviteResults: { email: string; success: boolean }[] = []; + for (const { name, email, role, teamIds } of data) { + const inviteUserActionResult = await inviteUserAction({ + organizationId: organization.id, + email: email.toLowerCase(), + name, + role, + teamIds, + }); + inviteResults.push({ + email, + success: Boolean(inviteUserActionResult?.data), + }); + } + const failedInvites: string[] = []; + const successInvites: string[] = []; + inviteResults.forEach((invite) => { if (!invite.success) { failedInvites.push(invite.email); } else { diff --git a/apps/web/modules/organization/settings/teams/types/invites.ts b/apps/web/modules/organization/settings/teams/types/invites.ts index 3434a2c06c..728c668c38 100644 --- a/apps/web/modules/organization/settings/teams/types/invites.ts +++ b/apps/web/modules/organization/settings/teams/types/invites.ts @@ -3,8 +3,10 @@ import { z } from "zod"; import { ZInvite } from "@formbricks/database/zod/invites"; import { ZUserName } from "@formbricks/types/user"; -export interface TInvite - extends Omit {} +export interface TInvite extends Omit< + Invite, + "deprecatedRole" | "organizationId" | "creatorId" | "acceptorId" | "teamIds" +> {} export interface InviteWithCreator extends Pick { creator: { diff --git a/apps/web/modules/projects/settings/(setup)/components/ActionActivityTab.tsx b/apps/web/modules/projects/settings/(setup)/components/ActionActivityTab.tsx index dc2ff668b0..f462c65bce 100644 --- a/apps/web/modules/projects/settings/(setup)/components/ActionActivityTab.tsx +++ b/apps/web/modules/projects/settings/(setup)/components/ActionActivityTab.tsx @@ -151,7 +151,7 @@ export const ActionActivityTab = ({

{ACTION_TYPE_ICON_LOOKUP[actionClass.type]}
-

{actionClass.type}

+

{actionClass.type}

diff --git a/apps/web/modules/projects/settings/general/components/custom-scripts-form.tsx b/apps/web/modules/projects/settings/general/components/custom-scripts-form.tsx index 60da3212e2..3e93d2d554 100644 --- a/apps/web/modules/projects/settings/general/components/custom-scripts-form.tsx +++ b/apps/web/modules/projects/settings/general/components/custom-scripts-form.tsx @@ -90,7 +90,7 @@ export const CustomScriptsForm: React.FC = ({ project, i rows={8} placeholder={t("environments.workspace.general.custom_scripts_placeholder")} className={cn( - "focus:border-brand-dark flex w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 font-mono text-xs text-slate-800 placeholder:text-slate-400 focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50", + "flex w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 font-mono text-xs text-slate-800 placeholder:text-slate-400 focus:border-brand-dark focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", isReadOnly && "bg-slate-50" )} {...field} diff --git a/apps/web/modules/projects/settings/general/components/delete-project-render.tsx b/apps/web/modules/projects/settings/general/components/delete-project-render.tsx index 8c230db35b..28809e6292 100644 --- a/apps/web/modules/projects/settings/general/components/delete-project-render.tsx +++ b/apps/web/modules/projects/settings/general/components/delete-project-render.tsx @@ -89,7 +89,7 @@ export const DeleteProjectRender = ({ )}
- {!IS_FORMBRICKS_CLOUD && !IS_DEVELOPMENT && ( - - )}
); diff --git a/apps/web/modules/projects/settings/lib/tag.test.ts b/apps/web/modules/projects/settings/lib/tag.test.ts index b06931f7ea..28e2675f4e 100644 --- a/apps/web/modules/projects/settings/lib/tag.test.ts +++ b/apps/web/modules/projects/settings/lib/tag.test.ts @@ -152,13 +152,13 @@ describe("tag lib", () => { .mockResolvedValueOnce(baseTag as any) .mockResolvedValueOnce(newTag as any); vi.mocked(prisma.response.findMany).mockResolvedValueOnce([{ id: "resp1" }] as any); - vi.mocked(prisma.$transaction).mockResolvedValueOnce(undefined).mockResolvedValueOnce(undefined); + vi.mocked(prisma.$transaction).mockResolvedValueOnce(undefined); const result = await mergeTags(baseTag.id, newTag.id); expect(result).toEqual(ok(newTag)); expect(prisma.tag.findUnique).toHaveBeenCalledWith({ where: { id: baseTag.id } }); expect(prisma.tag.findUnique).toHaveBeenCalledWith({ where: { id: newTag.id } }); expect(prisma.response.findMany).toHaveBeenCalled(); - expect(prisma.$transaction).toHaveBeenCalledTimes(2); + expect(prisma.$transaction).toHaveBeenCalledTimes(1); }); test("merges tags with no responses with both tags", async () => { vi.mocked(prisma.tag.findUnique) @@ -195,6 +195,20 @@ describe("tag lib", () => { }); } }); + test("returns error when merging a tag into itself", async () => { + const result = await mergeTags(baseTag.id, baseTag.id); + expect(result.ok).toBe(false); + + if (!result.ok) { + expect(result.error).toStrictEqual({ + code: "merge_same_tag", + message: "Cannot merge a tag into itself", + }); + } + + expect(prisma.tag.findUnique).not.toHaveBeenCalled(); + expect(prisma.$transaction).not.toHaveBeenCalled(); + }); test("throws on prisma error", async () => { vi.mocked(prisma.tag.findUnique).mockRejectedValueOnce(new Error("fail")); const result = await mergeTags(baseTag.id, newTag.id); diff --git a/apps/web/modules/projects/settings/lib/tag.ts b/apps/web/modules/projects/settings/lib/tag.ts index 73505fe78f..c143574e31 100644 --- a/apps/web/modules/projects/settings/lib/tag.ts +++ b/apps/web/modules/projects/settings/lib/tag.ts @@ -72,6 +72,13 @@ export const mergeTags = async ( ): Promise }>> => { validateInputs([originalTagId, ZId], [newTagId, ZId]); + if (originalTagId === newTagId) { + return err({ + code: TagError.MERGE_SAME_TAG, + message: "Cannot merge a tag into itself", + }); + } + try { let originalTag: TTag | null; @@ -103,90 +110,35 @@ export const mergeTags = async ( }); } - // finds all the responses that have both the tags - let responsesWithBothTags = await prisma.response.findMany({ + // Find responses that have both tags to avoid unique constraint violations during merge + const responsesWithBothTags = await prisma.response.findMany({ where: { - AND: [ - { - tags: { - some: { - tagId: { - in: [originalTagId], - }, - }, - }, - }, - { - tags: { - some: { - tagId: { - in: [newTagId], - }, - }, - }, - }, - ], + AND: [{ tags: { some: { tagId: originalTagId } } }, { tags: { some: { tagId: newTagId } } }], }, + select: { id: true }, }); - if (!!responsesWithBothTags?.length) { - await Promise.all( - responsesWithBothTags.map(async (response) => { - await prisma.$transaction([ - prisma.tagsOnResponses.deleteMany({ - where: { - responseId: response.id, - tagId: { - in: [originalTagId, newTagId], - }, - }, - }), - - prisma.tagsOnResponses.create({ - data: { - responseId: response.id, - tagId: newTagId, - }, - }), - ]); - }) - ); - - await prisma.$transaction([ - prisma.tagsOnResponses.updateMany({ - where: { - tagId: originalTagId, - }, - data: { - tagId: newTagId, - }, - }), - - prisma.tag.delete({ - where: { - id: originalTagId, - }, - }), - ]); - - return ok(newTag); - } + const conflictResponseIds = responsesWithBothTags.map((r) => r.id); await prisma.$transaction([ + // Remove originalTag from responses that already have newTag (prevents unique constraint violation) + ...(conflictResponseIds.length > 0 + ? [ + prisma.tagsOnResponses.deleteMany({ + where: { + responseId: { in: conflictResponseIds }, + tagId: originalTagId, + }, + }), + ] + : []), + // Move all remaining originalTag associations to newTag prisma.tagsOnResponses.updateMany({ - where: { - tagId: originalTagId, - }, - data: { - tagId: newTagId, - }, - }), - - prisma.tag.delete({ - where: { - id: originalTagId, - }, + where: { tagId: originalTagId }, + data: { tagId: newTagId }, }), + // Delete the original tag + prisma.tag.delete({ where: { id: originalTagId } }), ]); return ok(newTag); diff --git a/apps/web/modules/projects/settings/look/components/theme-styling.tsx b/apps/web/modules/projects/settings/look/components/theme-styling.tsx index 9bcea700a9..717b7e73f3 100644 --- a/apps/web/modules/projects/settings/look/components/theme-styling.tsx +++ b/apps/web/modules/projects/settings/look/components/theme-styling.tsx @@ -203,7 +203,9 @@ export const ThemeStyling = ({ field.onChange(color)} containerClass="w-full" /> @@ -214,8 +216,8 @@ export const ThemeStyling = ({
@@ -159,7 +159,7 @@ export const ProjectLookSettingsLoading = () => { title="Formbricks Signature" description="We love your support but understand if you toggle it off.">
-
+
diff --git a/apps/web/modules/projects/settings/tags/components/merge-tags-combobox.tsx b/apps/web/modules/projects/settings/tags/components/merge-tags-combobox.tsx index 97501c08f3..010a3420d8 100644 --- a/apps/web/modules/projects/settings/tags/components/merge-tags-combobox.tsx +++ b/apps/web/modules/projects/settings/tags/components/merge-tags-combobox.tsx @@ -34,7 +34,7 @@ export const MergeTagsCombobox = ({ tags, onSelect }: MergeTagsComboboxProps) => @@ -43,7 +43,7 @@ export const MergeTagsCombobox = ({ tags, onSelect }: MergeTagsComboboxProps) =>
diff --git a/apps/web/modules/projects/settings/tags/components/single-tag.tsx b/apps/web/modules/projects/settings/tags/components/single-tag.tsx index cb7f2dc2a9..77196e4f28 100644 --- a/apps/web/modules/projects/settings/tags/components/single-tag.tsx +++ b/apps/web/modules/projects/settings/tags/components/single-tag.tsx @@ -125,12 +125,12 @@ export const SingleTag: React.FC = ({
-
+
{tagCountLoading ? :

{tagCount}

}
{!isReadOnly && ( -
+
{isMergingTags ? (
@@ -152,7 +152,7 @@ export const SingleTag: React.FC = ({ diff --git a/apps/web/modules/projects/settings/types/tag.ts b/apps/web/modules/projects/settings/types/tag.ts index 9dfdc5baaf..6ea1b10afa 100644 --- a/apps/web/modules/projects/settings/types/tag.ts +++ b/apps/web/modules/projects/settings/types/tag.ts @@ -1,5 +1,6 @@ export enum TagError { TAG_NOT_FOUND = "tag_not_found", TAG_NAME_ALREADY_EXISTS = "tag_name_already_exists", + MERGE_SAME_TAG = "merge_same_tag", UNEXPECTED_ERROR = "unexpected_error", } diff --git a/apps/web/modules/survey/components/element-form-input/index.tsx b/apps/web/modules/survey/components/element-form-input/index.tsx index 6498c487f7..ce2aecb598 100644 --- a/apps/web/modules/survey/components/element-form-input/index.tsx +++ b/apps/web/modules/survey/components/element-form-input/index.tsx @@ -391,7 +391,7 @@ export const ElementFormInput = ({ return (
{label && ( -
+
{id === "headline" && currentElement && updateElement && (
@@ -521,7 +521,7 @@ export const ElementFormInput = ({ return (
{label && ( -
+
)} @@ -568,7 +568,7 @@ export const ElementFormInput = ({
1 ? "pr-24" : "" }`} dir="auto" diff --git a/apps/web/modules/survey/components/template-list/components/start-from-scratch-template.tsx b/apps/web/modules/survey/components/template-list/components/start-from-scratch-template.tsx index bc8fce54ee..9bf0cbe51a 100644 --- a/apps/web/modules/survey/components/template-list/components/start-from-scratch-template.tsx +++ b/apps/web/modules/survey/components/template-list/components/start-from-scratch-template.tsx @@ -51,7 +51,7 @@ export const StartFromScratchTemplate = ({ const cardContent = ( <> - +

{customSurvey.name}

{customSurvey.description}

{showCreateSurveyButton && ( diff --git a/apps/web/modules/survey/editor/components/add-action-modal.tsx b/apps/web/modules/survey/editor/components/add-action-modal.tsx index f88af0a333..b174160e07 100644 --- a/apps/web/modules/survey/editor/components/add-action-modal.tsx +++ b/apps/web/modules/survey/editor/components/add-action-modal.tsx @@ -93,7 +93,7 @@ export const AddActionModal = ({ key={tab.title} className={`mr-4 px-1 pb-3 focus:outline-none ${ activeTab === index - ? "border-brand-dark border-b-2 font-semibold text-slate-900" + ? "border-b-2 border-brand-dark font-semibold text-slate-900" : "text-slate-500 hover:text-slate-700" }`} onClick={() => handleTabClick(index)}> diff --git a/apps/web/modules/survey/editor/components/add-element-button.tsx b/apps/web/modules/survey/editor/components/add-element-button.tsx index b6f929e399..7af00f440d 100644 --- a/apps/web/modules/survey/editor/components/add-element-button.tsx +++ b/apps/web/modules/survey/editor/components/add-element-button.tsx @@ -38,7 +38,7 @@ export const AddElementButton = ({ addElement, project, isCxMode }: AddElementBu )}>
-
+
@@ -67,7 +67,7 @@ export const AddElementButton = ({ addElement, project, isCxMode }: AddElementBu onMouseEnter={() => setHoveredElementId(elementType.id)} onMouseLeave={() => setHoveredElementId(null)}>
-
diff --git a/apps/web/modules/survey/editor/components/bulk-edit-options-modal.tsx b/apps/web/modules/survey/editor/components/bulk-edit-options-modal.tsx index 1625167910..832579e0b0 100644 --- a/apps/web/modules/survey/editor/components/bulk-edit-options-modal.tsx +++ b/apps/web/modules/survey/editor/components/bulk-edit-options-modal.tsx @@ -177,7 +177,7 @@ export const BulkEditOptionsModal = ({ } }} rows={15} - className="focus:border-brand w-full rounded-md border border-slate-300 bg-white p-3 font-mono text-sm focus:outline-none" + className="w-full rounded-md border border-slate-300 bg-white p-3 font-mono text-sm focus:border-brand focus:outline-none" placeholder={t("environments.surveys.edit.bulk_edit_description")} /> {validationError &&
{validationError}
} diff --git a/apps/web/modules/survey/editor/components/cta-element-form.tsx b/apps/web/modules/survey/editor/components/cta-element-form.tsx index 6efd8be4f5..b4269e07e4 100644 --- a/apps/web/modules/survey/editor/components/cta-element-form.tsx +++ b/apps/web/modules/survey/editor/components/cta-element-form.tsx @@ -111,7 +111,7 @@ export const CTAElementForm = ({ description={t("environments.surveys.edit.button_external_description")} childBorder customContainerClass="p-0 mt-4"> -
+
{/* The highlight container is absolutely positioned behind the input */}
{highlightedJSX} diff --git a/apps/web/modules/survey/editor/components/file-upload-element-form.tsx b/apps/web/modules/survey/editor/components/file-upload-element-form.tsx index 55c6016d9b..705a613430 100644 --- a/apps/web/modules/survey/editor/components/file-upload-element-form.tsx +++ b/apps/web/modules/survey/editor/components/file-upload-element-form.tsx @@ -172,7 +172,7 @@ export const FileUploadElementForm = ({ updateElement(elementIdx, { maxSizeInMB: Number.parseInt(e.target.value, 10) }); }} - className="mr-2 ml-2 inline w-20 bg-white text-center text-sm" + className="ml-2 mr-2 inline w-20 bg-white text-center text-sm" /> MB

diff --git a/apps/web/modules/survey/editor/components/form-styling-settings.tsx b/apps/web/modules/survey/editor/components/form-styling-settings.tsx index 7e4577d101..0a73ed4d89 100644 --- a/apps/web/modules/survey/editor/components/form-styling-settings.tsx +++ b/apps/web/modules/survey/editor/components/form-styling-settings.tsx @@ -67,7 +67,7 @@ export const FormStylingSettings = ({

- {t("environments.surveys.edit.form_styling")} + {t("environments.surveys.edit.survey_styling")}

{t("environments.surveys.edit.style_the_question_texts_descriptions_and_input_fields")} @@ -125,6 +125,9 @@ export const FormStylingSettings = ({ description={t( "environments.workspace.look.advanced_styling_field_headline_weight_description" )} + step={100} + min={100} + max={900} />

@@ -292,6 +301,9 @@ export const FormStylingSettings = ({ description={t( "environments.workspace.look.advanced_styling_field_button_font_weight_description" )} + step={100} + min={100} + max={900} /> +
@@ -191,7 +191,7 @@ export const HiddenFieldsCard = ({ ); }) ) : ( -

+

{t("environments.surveys.edit.no_hidden_fields_yet_add_first_one_below")}

)} diff --git a/apps/web/modules/survey/editor/components/how-to-send-card.tsx b/apps/web/modules/survey/editor/components/how-to-send-card.tsx index a35aa9cb7f..0450caa3f9 100644 --- a/apps/web/modules/survey/editor/components/how-to-send-card.tsx +++ b/apps/web/modules/survey/editor/components/how-to-send-card.tsx @@ -106,7 +106,7 @@ export const HowToSendCard = ({ localSurvey, setLocalSurvey, environment }: HowT className="h-full w-full cursor-pointer" id="howToSendCardTrigger">
-
+
diff --git a/apps/web/modules/survey/editor/components/logo-settings-card.tsx b/apps/web/modules/survey/editor/components/logo-settings-card.tsx index 2a8b680c1a..19fdc31f6e 100644 --- a/apps/web/modules/survey/editor/components/logo-settings-card.tsx +++ b/apps/web/modules/survey/editor/components/logo-settings-card.tsx @@ -124,7 +124,7 @@ export const LogoSettingsCard = ({ disabled && "cursor-not-allowed opacity-60 hover:bg-white" )}>
-
+

{option.name}

@@ -273,7 +273,7 @@ export const RecontactOptionsCard = ({ localSurvey, setLocalSurvey }: RecontactO

{option.name}

diff --git a/apps/web/modules/survey/editor/components/redirect-url-form.tsx b/apps/web/modules/survey/editor/components/redirect-url-form.tsx index e140d22123..968d4d0022 100644 --- a/apps/web/modules/survey/editor/components/redirect-url-form.tsx +++ b/apps/web/modules/survey/editor/components/redirect-url-form.tsx @@ -45,7 +45,7 @@ export const RedirectUrlForm = ({ localSurvey, endingCard, updateSurvey }: Redir
{/* The highlight container is absolutely positioned behind the input */}
{highlightedJSX} diff --git a/apps/web/modules/survey/editor/components/response-options-card.tsx b/apps/web/modules/survey/editor/components/response-options-card.tsx index 0ea5d0f948..63a4504a3b 100644 --- a/apps/web/modules/survey/editor/components/response-options-card.tsx +++ b/apps/web/modules/survey/editor/components/response-options-card.tsx @@ -205,7 +205,7 @@ export const ResponseOptionsCard = ({ )}>
-
+
{t("environments.surveys.edit.completed_responses")}

@@ -310,7 +310,7 @@ export const ResponseOptionsCard = ({ handleClosedSurveyMessageChange({ heading: e.target.value })} diff --git a/apps/web/modules/survey/editor/components/styling-view.tsx b/apps/web/modules/survey/editor/components/styling-view.tsx index 03ab831657..8e83b79294 100644 --- a/apps/web/modules/survey/editor/components/styling-view.tsx +++ b/apps/web/modules/survey/editor/components/styling-view.tsx @@ -9,7 +9,12 @@ import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { TProjectStyling } from "@formbricks/types/project"; import { TSurvey, TSurveyStyling } from "@formbricks/types/surveys/types"; -import { STYLE_DEFAULTS, deriveNewFieldsFromLegacy, getSuggestedColors } from "@/lib/styling/constants"; +import { + COLOR_DEFAULTS, + STYLE_DEFAULTS, + deriveNewFieldsFromLegacy, + getSuggestedColors, +} from "@/lib/styling/constants"; import { FormStylingSettings } from "@/modules/survey/editor/components/form-styling-settings"; import { LogoSettingsCard } from "@/modules/survey/editor/components/logo-settings-card"; import { AlertDialog } from "@/modules/ui/components/alert-dialog"; @@ -92,7 +97,8 @@ export const StylingView = ({ const [confirmSuggestColorsOpen, setConfirmSuggestColorsOpen] = useState(false); const handleSuggestColors = () => { - const currentBrandColor = form.getValues().brandColor?.light ?? STYLE_DEFAULTS.brandColor?.light; + const currentBrandColor = + form.getValues().brandColor?.light ?? STYLE_DEFAULTS.brandColor?.light ?? COLOR_DEFAULTS.brandColor; const suggested = getSuggestedColors(currentBrandColor); for (const [key, value] of Object.entries(suggested)) { @@ -235,7 +241,7 @@ export const StylingView = ({ field.onChange(color)} containerClass="w-full" /> @@ -245,8 +251,8 @@ export const StylingView = ({ />
-
+
{!isStorageConfigured && (
diff --git a/apps/web/modules/survey/editor/lib/survey.ts b/apps/web/modules/survey/editor/lib/survey.ts index 438fe233ea..743aaa563e 100644 --- a/apps/web/modules/survey/editor/lib/survey.ts +++ b/apps/web/modules/survey/editor/lib/survey.ts @@ -222,6 +222,7 @@ export const updateSurvey = async (updatedSurvey: TSurvey): Promise => newFollowUps.length > 0 ? { data: newFollowUps.map((followUp) => ({ + id: followUp.id, name: followUp.name, trigger: followUp.trigger, action: followUp.action, diff --git a/apps/web/modules/survey/follow-ups/components/follow-up-item.tsx b/apps/web/modules/survey/follow-ups/components/follow-up-item.tsx index 68f495e26f..246576eea1 100644 --- a/apps/web/modules/survey/follow-ups/components/follow-up-item.tsx +++ b/apps/web/modules/survey/follow-ups/components/follow-up-item.tsx @@ -155,7 +155,7 @@ export const FollowUpItem = ({
-
+
+ + +
); diff --git a/apps/web/modules/ui/components/data-table/components/data-table-header.tsx b/apps/web/modules/ui/components/data-table/components/data-table-header.tsx index 94463c7be2..7a8af483c4 100644 --- a/apps/web/modules/ui/components/data-table/components/data-table-header.tsx +++ b/apps/web/modules/ui/components/data-table/components/data-table-header.tsx @@ -68,7 +68,7 @@ export const DataTableHeader = ({ onTouchStart={header.getResizeHandler()} data-testid="column-resize-handle" className={cn( - "absolute top-0 right-0 hidden h-full w-1 cursor-col-resize bg-slate-500", + "absolute right-0 top-0 hidden h-full w-1 cursor-col-resize bg-slate-500", header.column.getIsResizing() ? "bg-black" : "bg-slate-500", !header.column.getCanResize() ? "hidden" : "group-hover:block" )}> diff --git a/apps/web/modules/ui/components/data-table/components/selected-row-settings.tsx b/apps/web/modules/ui/components/data-table/components/selected-row-settings.tsx index a60537110b..fb8279150b 100644 --- a/apps/web/modules/ui/components/data-table/components/selected-row-settings.tsx +++ b/apps/web/modules/ui/components/data-table/components/selected-row-settings.tsx @@ -66,10 +66,14 @@ export const SelectedRowSettings = ({ setIsDeleting(true); const rowsToBeDeleted = table.getFilteredSelectedRowModel().rows.map((row) => row.id); - if (type === "response") { - await Promise.all(rowsToBeDeleted.map((rowId) => deleteAction(rowId, { decrementQuotas }))); - } else { - await Promise.all(rowsToBeDeleted.map((rowId) => deleteAction(rowId))); + const CHUNK_SIZE = 5; + for (let i = 0; i < rowsToBeDeleted.length; i += CHUNK_SIZE) { + const chunk = rowsToBeDeleted.slice(i, i + CHUNK_SIZE); + if (type === "response") { + await Promise.all(chunk.map((rowId) => deleteAction(rowId, { decrementQuotas }))); + } else { + await Promise.all(chunk.map((rowId) => deleteAction(rowId))); + } } // Update the row list UI @@ -141,7 +145,7 @@ export const SelectedRowSettings = ({ return ( <> -
+
{`${selectedRowCount} ${selectedTypeLabel} ${t("common.selected")}`}