diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/general/components/DeleteOrganization.tsx b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/general/components/DeleteOrganization.tsx index dc3cc2e4bb..14df58d4a9 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/general/components/DeleteOrganization.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/general/components/DeleteOrganization.tsx @@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next"; import { TOrganization } from "@formbricks/types/organizations"; import { deleteOrganizationAction } from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/actions"; import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage"; +import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { Alert, AlertDescription } from "@/modules/ui/components/alert"; import { Button } from "@/modules/ui/components/button"; import { DeleteDialog } from "@/modules/ui/components/delete-dialog"; @@ -32,7 +33,12 @@ export const DeleteOrganization = ({ setIsDeleting(true); try { - await deleteOrganizationAction({ organizationId: organization.id }); + const result = await deleteOrganizationAction({ organizationId: organization.id }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + setIsDeleting(false); + return; + } toast.success(t("environments.settings.general.organization_deleted_successfully")); if (typeof localStorage !== "undefined") { localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS); diff --git a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/AddIntegrationModal.tsx b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/AddIntegrationModal.tsx index ef75bfd445..a26091bb1b 100644 --- a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/AddIntegrationModal.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/AddIntegrationModal.tsx @@ -21,6 +21,7 @@ import { createOrUpdateIntegrationAction } from "@/app/(app)/environments/[envir import { BaseSelectDropdown } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/BaseSelectDropdown"; import { fetchTables } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/lib/airtable"; import AirtableLogo from "@/images/airtableLogo.svg"; +import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { recallToHeadline } from "@/lib/utils/recall"; import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils"; import { AdditionalIntegrationSettings } from "@/modules/ui/components/additional-integration-settings"; @@ -268,7 +269,14 @@ export const AddIntegrationModal = ({ airtableIntegrationData.config?.data.push(integrationData); } - await createOrUpdateIntegrationAction({ environmentId, integrationData: airtableIntegrationData }); + const result = await createOrUpdateIntegrationAction({ + environmentId, + integrationData: airtableIntegrationData, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } if (isEditMode) { toast.success(t("environments.integrations.integration_updated_successfully")); } else { @@ -304,7 +312,11 @@ export const AddIntegrationModal = ({ const integrationData = structuredClone(airtableIntegrationData); integrationData.config.data.splice(index, 1); - await createOrUpdateIntegrationAction({ environmentId, integrationData }); + const result = await createOrUpdateIntegrationAction({ environmentId, integrationData }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } handleClose(); router.refresh(); diff --git a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/AddIntegrationModal.tsx b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/AddIntegrationModal.tsx index 1cdb9b19d0..0b16be342f 100644 --- a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/AddIntegrationModal.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/AddIntegrationModal.tsx @@ -165,7 +165,14 @@ export const AddIntegrationModal = ({ // create action googleSheetIntegrationData.config.data.push(integrationData); } - await createOrUpdateIntegrationAction({ environmentId, integrationData: googleSheetIntegrationData }); + const result = await createOrUpdateIntegrationAction({ + environmentId, + integrationData: googleSheetIntegrationData, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } if (selectedIntegration) { toast.success(t("environments.integrations.integration_updated_successfully")); } else { @@ -205,7 +212,14 @@ export const AddIntegrationModal = ({ googleSheetIntegrationData.config.data.splice(selectedIntegration!.index, 1); try { setIsDeleting(true); - await createOrUpdateIntegrationAction({ environmentId, integrationData: googleSheetIntegrationData }); + const result = await createOrUpdateIntegrationAction({ + environmentId, + integrationData: googleSheetIntegrationData, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } toast.success(t("environments.integrations.integration_removed_successfully")); setOpen(false); } catch (error) { @@ -266,7 +280,7 @@ export const AddIntegrationModal = ({
-
+
{surveyElements.map((question) => (
diff --git a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/components/AddIntegrationModal.tsx b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/components/AddIntegrationModal.tsx index 55003f64b0..c3409375e5 100644 --- a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/components/AddIntegrationModal.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/notion/components/AddIntegrationModal.tsx @@ -22,6 +22,7 @@ import { createEmptyMapping, } from "@/app/(app)/environments/[environmentId]/workspace/integrations/notion/components/MappingRow"; import NotionLogo from "@/images/notion.png"; +import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { recallToHeadline } from "@/lib/utils/recall"; import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils"; import { Button } from "@/modules/ui/components/button"; @@ -217,7 +218,14 @@ export const AddIntegrationModal = ({ notionIntegrationData.config.data.push(integrationData); } - await createOrUpdateIntegrationAction({ environmentId, integrationData: notionIntegrationData }); + const result = await createOrUpdateIntegrationAction({ + environmentId, + integrationData: notionIntegrationData, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } if (selectedIntegration) { toast.success(t("environments.integrations.integration_updated_successfully")); } else { @@ -236,7 +244,14 @@ export const AddIntegrationModal = ({ notionIntegrationData.config.data.splice(selectedIntegration!.index, 1); try { setIsDeleting(true); - await createOrUpdateIntegrationAction({ environmentId, integrationData: notionIntegrationData }); + const result = await createOrUpdateIntegrationAction({ + environmentId, + integrationData: notionIntegrationData, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } toast.success(t("environments.integrations.integration_removed_successfully")); setOpen(false); } catch (error) { diff --git a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/slack/components/AddChannelMappingModal.tsx b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/slack/components/AddChannelMappingModal.tsx index 88e501bbae..13bc7af4d5 100644 --- a/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/slack/components/AddChannelMappingModal.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/workspace/integrations/slack/components/AddChannelMappingModal.tsx @@ -17,6 +17,7 @@ import { TSurvey } from "@formbricks/types/surveys/types"; import { getTextContent } from "@formbricks/types/surveys/validation"; import { createOrUpdateIntegrationAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/actions"; import SlackLogo from "@/images/slacklogo.png"; +import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { recallToHeadline } from "@/lib/utils/recall"; import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils"; import { AdditionalIntegrationSettings } from "@/modules/ui/components/additional-integration-settings"; @@ -144,7 +145,14 @@ export const AddChannelMappingModal = ({ // create action slackIntegrationData.config.data.push(integrationData); } - await createOrUpdateIntegrationAction({ environmentId, integrationData: slackIntegrationData }); + const result = await createOrUpdateIntegrationAction({ + environmentId, + integrationData: slackIntegrationData, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } if (selectedIntegration) { toast.success(t("environments.integrations.integration_updated_successfully")); } else { @@ -181,7 +189,14 @@ export const AddChannelMappingModal = ({ slackIntegrationData.config.data.splice(selectedIntegration!.index, 1); try { setIsDeleting(true); - await createOrUpdateIntegrationAction({ environmentId, integrationData: slackIntegrationData }); + const result = await createOrUpdateIntegrationAction({ + environmentId, + integrationData: slackIntegrationData, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } toast.success(t("environments.integrations.integration_removed_successfully")); setOpen(false); } catch (error) { diff --git a/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx b/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx index a087442ca6..cc56f9a4e6 100644 --- a/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx +++ b/apps/web/modules/ee/contacts/segments/components/segment-settings.tsx @@ -109,7 +109,13 @@ export function SegmentSettings({ const handleDeleteSegment = async () => { try { setIsDeletingSegment(true); - await deleteSegmentAction({ segmentId: segment.id }); + const result = await deleteSegmentAction({ segmentId: segment.id }); + + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + setIsDeletingSegment(false); + return; + } setIsDeletingSegment(false); toast.success(t("environments.segments.segment_deleted_successfully")); 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 549de03e36..c0207abc4b 100644 --- a/apps/web/modules/ee/contacts/segments/components/targeting-card.tsx +++ b/apps/web/modules/ee/contacts/segments/components/targeting-card.tsx @@ -17,6 +17,7 @@ import type { import type { TSurvey } from "@formbricks/types/surveys/types"; import { cn } from "@/lib/cn"; import { structuredClone } from "@/lib/pollyfills/structuredClone"; +import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { cloneSegmentAction, createSegmentAction, @@ -135,7 +136,11 @@ export function TargetingCard({ const handleSaveSegment = async (data: TSegmentUpdateInput) => { try { if (!segment) throw new Error(t("environments.segments.invalid_segment")); - await updateSegmentAction({ segmentId: segment.id, environmentId, data }); + const result = await updateSegmentAction({ segmentId: segment.id, environmentId, data }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } toast.success(t("environments.segments.segment_saved_successfully")); setIsSegmentEditorOpen(false); diff --git a/apps/web/modules/ee/multi-language-surveys/components/edit-language.tsx b/apps/web/modules/ee/multi-language-surveys/components/edit-language.tsx index cf37396e09..8895229487 100644 --- a/apps/web/modules/ee/multi-language-surveys/components/edit-language.tsx +++ b/apps/web/modules/ee/multi-language-surveys/components/edit-language.tsx @@ -154,7 +154,12 @@ export function EditLanguage({ const performLanguageDeletion = async (languageId: string) => { try { - await deleteLanguageAction({ languageId, projectId: project.id }); + const result = await deleteLanguageAction({ languageId, projectId: project.id }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + setConfirmationModal((prev) => ({ ...prev, isOpen: false })); + return; + } setLanguages((prev) => prev.filter((lang) => lang.id !== languageId)); toast.success(t("environments.workspace.languages.language_deleted_successfully")); // Close the modal after deletion @@ -187,7 +192,7 @@ export function EditLanguage({ const handleSaveChanges = async () => { if (!validateLanguages(languages, t)) return; - await Promise.all( + const results = await Promise.all( languages.map((lang) => { return lang.id === "new" ? createLanguageAction({ @@ -201,6 +206,11 @@ export function EditLanguage({ }); }) ); + const errorResult = results.find((result) => result?.serverError); + if (errorResult) { + toast.error(getFormattedErrorMessage(errorResult)); + return; + } toast.success(t("environments.workspace.languages.languages_updated_successfully")); router.refresh(); setIsEditing(false); @@ -239,7 +249,7 @@ export function EditLanguage({ ))} ) : ( -

+

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

)} diff --git a/apps/web/modules/ee/teams/team-list/components/team-settings/delete-team.tsx b/apps/web/modules/ee/teams/team-list/components/team-settings/delete-team.tsx index a34d33382e..efb37bae81 100644 --- a/apps/web/modules/ee/teams/team-list/components/team-settings/delete-team.tsx +++ b/apps/web/modules/ee/teams/team-list/components/team-settings/delete-team.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; +import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { deleteTeamAction } from "@/modules/ee/teams/team-list/actions"; import { TTeam } from "@/modules/ee/teams/team-list/types/team"; import { Button } from "@/modules/ui/components/button"; @@ -27,6 +28,12 @@ export const DeleteTeam = ({ teamId, onDelete, isOwnerOrManager }: DeleteTeamPro setIsDeleting(true); const deleteTeamActionResponse = await deleteTeamAction({ teamId }); + if (deleteTeamActionResponse?.serverError) { + toast.error(getFormattedErrorMessage(deleteTeamActionResponse)); + setIsDeleteDialogOpen(false); + setIsDeleting(false); + return; + } if (deleteTeamActionResponse?.data) { toast.success(t("environments.settings.teams.team_deleted_successfully")); onDelete?.(); 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 3477b7bf15..41d7266c17 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 @@ -42,14 +42,27 @@ export const MemberActions = ({ organization, member, invite, showDeleteButton } if (!member && invite) { // This is an invite - await deleteInviteAction({ inviteId: invite?.id, organizationId: organization.id }); + const result = await deleteInviteAction({ inviteId: invite?.id, organizationId: organization.id }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + setIsDeleting(false); + return; + } toast.success(t("environments.settings.general.invite_deleted_successfully")); } if (member && !invite) { // This is a member - await deleteMembershipAction({ userId: member.userId, organizationId: organization.id }); + const result = await deleteMembershipAction({ + userId: member.userId, + organizationId: organization.id, + }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + setIsDeleting(false); + return; + } toast.success(t("environments.settings.general.member_deleted_successfully")); } diff --git a/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx b/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx index d919af059a..6d1d19cb00 100644 --- a/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx +++ b/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx @@ -71,7 +71,12 @@ export const OrganizationActions = ({ const handleLeaveOrganization = async () => { setLoading(true); try { - await leaveOrganizationAction({ organizationId: organization.id }); + const result = await leaveOrganizationAction({ organizationId: organization.id }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + setLoading(false); + return; + } toast.success(t("environments.settings.general.member_deleted_successfully")); router.refresh(); setLoading(false); diff --git a/apps/web/modules/projects/settings/(setup)/components/ActionSettingsTab.tsx b/apps/web/modules/projects/settings/(setup)/components/ActionSettingsTab.tsx index 51e3953da8..fb00d33cdf 100644 --- a/apps/web/modules/projects/settings/(setup)/components/ActionSettingsTab.tsx +++ b/apps/web/modules/projects/settings/(setup)/components/ActionSettingsTab.tsx @@ -8,6 +8,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { TActionClass, TActionClassInput } from "@formbricks/types/action-classes"; +import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { deleteActionClassAction, updateActionClassAction, @@ -92,10 +93,14 @@ export const ActionSettingsTab = ({ validatePermissions(isReadOnly, t); const updatedAction = buildActionObject(data, actionClass.environmentId, t); - await updateActionClassAction({ + const result = await updateActionClassAction({ actionClassId: actionClass.id, updatedAction: updatedAction, }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } setOpen(false); router.refresh(); toast.success(t("environments.actions.action_updated_successfully")); @@ -109,7 +114,11 @@ export const ActionSettingsTab = ({ const handleDeleteAction = async () => { try { setIsDeletingAction(true); - await deleteActionClassAction({ actionClassId: actionClass.id }); + const result = await deleteActionClassAction({ actionClassId: actionClass.id }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } router.refresh(); toast.success(t("environments.actions.action_deleted_successfully")); setOpen(false); diff --git a/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx b/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx index 0502306bfd..614546e380 100644 --- a/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx +++ b/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx @@ -69,7 +69,11 @@ export const SurveyDropDownMenu = ({ const handleDeleteSurvey = async (surveyId: string) => { setLoading(true); try { - await deleteSurveyAction({ surveyId }); + const result = await deleteSurveyAction({ surveyId }); + if (result?.serverError) { + toast.error(getFormattedErrorMessage(result)); + return; + } deleteSurvey(surveyId); toast.success(t("environments.surveys.survey_deleted_successfully")); } catch (error) {