Define server error messages to display in FE from the server (#12973)
Currently, when a server query or mutation from the front-end fails, the error message defined server-side is displayed in a snackbar in the front-end. These error messages usually contain technical details that don't belong to the user interface, such as "ObjectMetadataCollection not found" or "invalid ENUM value for ...". **BE** In addition to the original error message that is still needed (for the request response, debugging, sentry monitoring etc.), we add a `displayedErrorMessage` that will be used in the snackbars. It's only relevant to add it for the messages that will reach the FE (ie. not in jobs or in rest api for instance) and if it can help the user sort out / fix things (ie. we do add displayedErrorMessage for "Cannot create multiple draft versions for the same workflow" or "Cannot delete [field], please update the label identifier field first", but not "Object metadata does not exist"), even if in practice in the FE users should not be able to perform an action that will not work (ie should not be able to save creation of multiple draft versions of the same workflows). **FE** To ease the usage we replaced enqueueSnackBar with enqueueErrorSnackBar and enqueueSuccessSnackBar with an api that only requires to pass on the error. If no displayedErrorMessage is specified then the default error message is `An error occured.`
This commit is contained in:
@ -7,10 +7,10 @@ import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState
|
||||
import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex';
|
||||
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
@ -78,7 +78,7 @@ const StyledMainButton = styled(MainButton)`
|
||||
|
||||
export const PasswordReset = () => {
|
||||
const { t } = useLingui();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
|
||||
|
||||
const workspacePublicData = useRecoilValue(workspacePublicDataState);
|
||||
|
||||
@ -108,8 +108,8 @@ export const PasswordReset = () => {
|
||||
},
|
||||
skip: !passwordResetToken || isTokenValid,
|
||||
onError: (error) => {
|
||||
enqueueSnackBar(error?.message ?? 'Token Invalid', {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
navigate(AppPath.Index);
|
||||
},
|
||||
@ -137,15 +137,15 @@ export const PasswordReset = () => {
|
||||
});
|
||||
|
||||
if (!data?.updatePasswordViaResetToken.success) {
|
||||
enqueueSnackBar(t`There was an error while updating password.`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`There was an error while updating password.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLoggedIn) {
|
||||
enqueueSnackBar(t`Password has been updated`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Password has been updated`,
|
||||
});
|
||||
navigate(AppPath.Index);
|
||||
return;
|
||||
@ -161,14 +161,9 @@ export const PasswordReset = () => {
|
||||
navigate(AppPath.Index);
|
||||
} catch (err) {
|
||||
logError(err);
|
||||
enqueueSnackBar(
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: t`An error occurred while updating password`,
|
||||
{
|
||||
variant: SnackBarVariant.Error,
|
||||
},
|
||||
);
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: err instanceof ApolloError ? err : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -15,12 +15,12 @@ import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
@ -59,7 +59,7 @@ type Form = z.infer<typeof validationSchema>;
|
||||
export const CreateProfile = () => {
|
||||
const { t } = useLingui();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
@ -131,15 +131,15 @@ export const CreateProfile = () => {
|
||||
|
||||
setNextOnboardingStatus();
|
||||
} catch (error: any) {
|
||||
enqueueSnackBar(error?.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
currentWorkspaceMember?.id,
|
||||
setNextOnboardingStatus,
|
||||
enqueueSnackBar,
|
||||
enqueueErrorSnackBar,
|
||||
setCurrentWorkspaceMember,
|
||||
setCurrentUser,
|
||||
updateOneRecord,
|
||||
|
||||
@ -13,10 +13,10 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
|
||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { motion } from 'framer-motion';
|
||||
@ -65,7 +65,7 @@ const StyledPendingCreationLoader = styled(motion.div)`
|
||||
|
||||
export const CreateWorkspace = () => {
|
||||
const { t } = useLingui();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
|
||||
|
||||
@ -127,14 +127,15 @@ export const CreateWorkspace = () => {
|
||||
setNextOnboardingStatus();
|
||||
} catch (error: any) {
|
||||
setPendingCreationLoaderStep(PendingCreationLoaderStep.None);
|
||||
enqueueSnackBar(error?.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
activateWorkspace,
|
||||
enqueueSnackBar,
|
||||
enqueueErrorSnackBar,
|
||||
loadCurrentUser,
|
||||
refreshObjectMetadataItems,
|
||||
setNextOnboardingStatus,
|
||||
|
||||
@ -4,7 +4,6 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { calendarBookingPageIdState } from '@/client-config/states/calendarBookingPageIdState';
|
||||
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
@ -65,7 +64,7 @@ type FormInput = z.infer<typeof validationSchema>;
|
||||
export const InviteTeam = () => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const { sendInvitation } = useCreateWorkspaceInvitation();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
@ -120,10 +119,12 @@ export const InviteTeam = () => {
|
||||
if (isDefined(currentWorkspace?.inviteHash)) {
|
||||
const inviteLink = `${window.location.origin}/invite/${currentWorkspace?.inviteHash}`;
|
||||
navigator.clipboard.writeText(inviteLink);
|
||||
enqueueSnackBar(t`Link copied to clipboard`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Link copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -143,15 +144,17 @@ export const InviteTeam = () => {
|
||||
throw result.errors;
|
||||
}
|
||||
if (emails.length > 0) {
|
||||
enqueueSnackBar(t`Invite link sent to email addresses`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
duration: 2000,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Invite link sent to email addresses`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setNextOnboardingStatus();
|
||||
},
|
||||
[enqueueSnackBar, sendInvitation, setNextOnboardingStatus, t],
|
||||
[enqueueSuccessSnackBar, sendInvitation, setNextOnboardingStatus, t],
|
||||
);
|
||||
|
||||
const handleSkip = async () => {
|
||||
|
||||
@ -12,7 +12,6 @@ import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
@ -23,6 +22,7 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { WorkspaceInviteLink } from '@/workspace/components/WorkspaceInviteLink';
|
||||
import { WorkspaceInviteTeam } from '@/workspace/components/WorkspaceInviteTeam';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
@ -94,7 +94,7 @@ const StyledNoMembers = styled(TableCell)`
|
||||
|
||||
export const SettingsWorkspaceMembers = () => {
|
||||
const { t } = useLingui();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const [workspaceMemberToDelete, setWorkspaceMemberToDelete] = useState<
|
||||
string | undefined
|
||||
@ -127,9 +127,9 @@ export const SettingsWorkspaceMembers = () => {
|
||||
};
|
||||
|
||||
useGetWorkspaceInvitationsQuery({
|
||||
onError: (error: Error) => {
|
||||
enqueueSnackBar(error.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
onError: (error: ApolloError) => {
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
@ -140,9 +140,11 @@ export const SettingsWorkspaceMembers = () => {
|
||||
const handleRemoveWorkspaceInvitation = async (appTokenId: string) => {
|
||||
const result = await deleteWorkspaceInvitation({ appTokenId });
|
||||
if (isDefined(result.errors)) {
|
||||
enqueueSnackBar(t`Error deleting invitation`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Error deleting invitation`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -150,9 +152,11 @@ export const SettingsWorkspaceMembers = () => {
|
||||
const handleResendWorkspaceInvitation = async (appTokenId: string) => {
|
||||
const result = await resendInvitation({ appTokenId });
|
||||
if (isDefined(result.errors)) {
|
||||
enqueueSnackBar(t`Error resending invitation`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Error resending invitation`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -11,20 +11,20 @@ import {
|
||||
settingsDataModelObjectAboutFormSchema,
|
||||
} from '@/settings/data-model/validation-schemas/settingsDataModelObjectAboutFormSchema';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { useState } from 'react';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { useState } from 'react';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
export const SettingsNewObject = () => {
|
||||
const { t } = useLingui();
|
||||
const navigate = useNavigateSettings();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { createOneObjectMetadataItem } = useCreateOneObjectMetadataItem();
|
||||
|
||||
@ -56,8 +56,8 @@ export const SettingsNewObject = () => {
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@ -23,9 +23,9 @@ import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/vali
|
||||
import { SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { H2Title, IconArchive, IconArchiveOff } from 'twenty-ui/display';
|
||||
@ -47,7 +47,7 @@ export const SettingsObjectFieldEdit = () => {
|
||||
const navigateApp = useNavigateApp();
|
||||
const { t } = useLingui();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const { objectNamePlural = '', fieldName = '' } = useParams();
|
||||
const { findObjectMetadataItemByNamePlural } =
|
||||
@ -147,8 +147,8 @@ export const SettingsObjectFieldEdit = () => {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -13,7 +13,6 @@ import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/vali
|
||||
import { SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { View } from '@/views/types/View';
|
||||
@ -51,7 +50,7 @@ export const SettingsObjectNewFieldConfigure = () => {
|
||||
const fieldType =
|
||||
(searchParams.get('fieldType') as SettingsFieldType) ||
|
||||
FieldMetadataType.TEXT;
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const { findActiveObjectMetadataItemByNamePlural } =
|
||||
useFilteredObjectMetadataItems();
|
||||
@ -161,14 +160,11 @@ export const SettingsObjectNewFieldConfigure = () => {
|
||||
'duplicate key value violates unique constraint "IndexOnNameObjectMetadataIdAndWorkspaceIdUnique"',
|
||||
);
|
||||
|
||||
enqueueSnackBar(
|
||||
isDuplicateFieldNameInObject
|
||||
enqueueErrorSnackBar({
|
||||
message: isDuplicateFieldNameInObject
|
||||
? t`Please use different names for your source and destination fields`
|
||||
: (error as Error).message,
|
||||
{
|
||||
variant: SnackBarVariant.Error,
|
||||
},
|
||||
);
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (!activeObjectMetadataItem) return null;
|
||||
|
||||
@ -17,7 +17,6 @@ import { ApiKey } from '@/settings/developers/types/api-key/ApiKey';
|
||||
import { computeNewExpirationDate } from '@/settings/developers/utils/computeNewExpirationDate';
|
||||
import { formatExpiration } from '@/settings/developers/utils/formatExpiration';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
@ -50,7 +49,7 @@ const REGENERATE_API_KEY_MODAL_ID = 'regenerate-api-key-modal';
|
||||
|
||||
export const SettingsDevelopersApiKeyDetail = () => {
|
||||
const { t } = useLingui();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const { openModal } = useModal();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@ -97,9 +96,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
navigate(SettingsPath.APIs);
|
||||
}
|
||||
} catch (err) {
|
||||
enqueueSnackBar(t`Error deleting api key: ${err}`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
});
|
||||
enqueueErrorSnackBar({ message: t`Error deleting api key.` });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@ -149,8 +146,8 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
enqueueSnackBar(t`Error regenerating api key: ${err}`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Error regenerating api key.`,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@ -17,15 +17,15 @@ import { useIsSettingsIntegrationEnabled } from '@/settings/integrations/hooks/u
|
||||
import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { CreateRemoteServerInput } from '~/generated-metadata/graphql';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
|
||||
const createRemoteServerInputPostgresSchema =
|
||||
settingsIntegrationPostgreSQLConnectionFormSchema.transform<CreateRemoteServerInput>(
|
||||
@ -79,7 +79,7 @@ export const SettingsIntegrationNewDatabaseConnection = () => {
|
||||
);
|
||||
|
||||
const { createOneDatabaseConnection } = useCreateOneDatabaseConnection();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const isIntegrationEnabled = useIsSettingsIntegrationEnabled(databaseKey);
|
||||
|
||||
@ -131,8 +131,8 @@ export const SettingsIntegrationNewDatabaseConnection = () => {
|
||||
connectionId,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
@ -20,7 +20,7 @@ export const SettingsSecurityApprovedAccessDomain = () => {
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const [createApprovedAccessDomain] = useCreateApprovedAccessDomainMutation();
|
||||
|
||||
@ -62,20 +62,20 @@ export const SettingsSecurityApprovedAccessDomain = () => {
|
||||
},
|
||||
},
|
||||
onCompleted: () => {
|
||||
enqueueSnackBar(t`Please check your email for a verification link.`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Please check your email for a verification link.`,
|
||||
});
|
||||
navigate(SettingsPath.Security);
|
||||
},
|
||||
onError: (error) => {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,21 +7,21 @@ import { SettingSecurityNewSSOIdentityFormValues } from '@/settings/security/typ
|
||||
import { sSOIdentityProviderDefaultValues } from '@/settings/security/utils/sSOIdentityProviderDefaultValues';
|
||||
import { SSOIdentitiesProvidersParamsSchema } from '@/settings/security/validation-schemas/SSOIdentityProviderSchema';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import pick from 'lodash.pick';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
export const SettingsSecuritySSOIdentifyProvider = () => {
|
||||
const navigate = useNavigateSettings();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const { createSSOIdentityProvider } = useCreateSSOIdentityProvider();
|
||||
|
||||
const form = useForm<SettingSecurityNewSSOIdentityFormValues>({
|
||||
@ -48,8 +48,8 @@ export const SettingsSecuritySSOIdentifyProvider = () => {
|
||||
|
||||
navigate(SettingsPath.Security);
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -8,17 +8,18 @@ import { usePublishOneServerlessFunction } from '@/settings/serverless-functions
|
||||
import { useServerlessFunctionUpdateFormState } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState';
|
||||
import { useUpdateOneServerlessFunction } from '@/settings/serverless-functions/hooks/useUpdateOneServerlessFunction';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { TabList } from '@/ui/layout/tab-list/components/TabList';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconCode, IconSettings, IconTestPipe } from 'twenty-ui/display';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { getErrorMessageFromApolloError } from '~/utils/get-error-message-from-apollo-error.util';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
@ -26,7 +27,7 @@ const SERVERLESS_FUNCTION_DETAIL_ID = 'serverless-function-detail';
|
||||
|
||||
export const SettingsServerlessFunctionDetail = () => {
|
||||
const { serverlessFunctionId = '' } = useParams();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
|
||||
const [activeTabId, setActiveTabId] = useRecoilComponentStateV2(
|
||||
activeTabIdComponentState,
|
||||
SERVERLESS_FUNCTION_DETAIL_ID,
|
||||
@ -88,12 +89,9 @@ export const SettingsServerlessFunctionDetail = () => {
|
||||
}));
|
||||
await handleSave();
|
||||
} catch (err) {
|
||||
enqueueSnackBar(
|
||||
(err as Error)?.message || 'An error occurred while reset function',
|
||||
{
|
||||
variant: SnackBarVariant.Error,
|
||||
},
|
||||
);
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: err instanceof ApolloError ? err : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -102,17 +100,16 @@ export const SettingsServerlessFunctionDetail = () => {
|
||||
await publishOneServerlessFunction({
|
||||
id: serverlessFunctionId,
|
||||
});
|
||||
enqueueSnackBar(`New function version has been published`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: `New function version has been published`,
|
||||
});
|
||||
} catch (err) {
|
||||
enqueueSnackBar(
|
||||
(err as Error)?.message ||
|
||||
'An error occurred while publishing new version',
|
||||
{
|
||||
variant: SnackBarVariant.Error,
|
||||
},
|
||||
);
|
||||
enqueueErrorSnackBar({
|
||||
message:
|
||||
err instanceof ApolloError
|
||||
? getErrorMessageFromApolloError(err)
|
||||
: 'An error occurred while publishing new version',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableBody } from '@/ui/layout/table/components/TableBody';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconCopy } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { CustomDomainValidRecords } from '~/generated/graphql';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { IconCopy } from 'twenty-ui/display';
|
||||
|
||||
const StyledTable = styled(Table)`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
@ -46,7 +45,7 @@ export const SettingsCustomDomainRecords = ({
|
||||
}: {
|
||||
records: CustomDomainValidRecords['records'];
|
||||
}) => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
@ -54,9 +53,11 @@ export const SettingsCustomDomainRecords = ({
|
||||
|
||||
const copyToClipboard = (value: string) => {
|
||||
navigator.clipboard.writeText(value);
|
||||
enqueueSnackBar(t`Copied to clipboard!`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Copied to clipboard!`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirect
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
@ -60,7 +59,7 @@ export const SettingsDomain = () => {
|
||||
})
|
||||
.required();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
|
||||
|
||||
@ -105,11 +104,11 @@ export const SettingsDomain = () => {
|
||||
customDomain:
|
||||
customDomain && customDomain.length > 0 ? customDomain : null,
|
||||
});
|
||||
enqueueSnackBar(t`Custom domain updated`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Custom domain updated`,
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: (error: ApolloError) => {
|
||||
if (
|
||||
error instanceof ApolloError &&
|
||||
error.graphQLErrors[0]?.extensions?.code === 'CONFLICT'
|
||||
@ -119,8 +118,8 @@ export const SettingsDomain = () => {
|
||||
message: t`Subdomain already taken`,
|
||||
});
|
||||
}
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -136,7 +135,7 @@ export const SettingsDomain = () => {
|
||||
subdomain,
|
||||
},
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: (error: ApolloError) => {
|
||||
if (
|
||||
error instanceof ApolloError &&
|
||||
error.graphQLErrors[0]?.extensions?.code === 'CONFLICT'
|
||||
@ -147,8 +146,8 @@ export const SettingsDomain = () => {
|
||||
message: t`Subdomain already taken`,
|
||||
});
|
||||
}
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
},
|
||||
onCompleted: async () => {
|
||||
@ -163,8 +162,8 @@ export const SettingsDomain = () => {
|
||||
subdomain,
|
||||
});
|
||||
|
||||
enqueueSnackBar(t`Subdomain updated`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Subdomain updated`,
|
||||
});
|
||||
|
||||
await redirectToWorkspaceDomain(currentUrl.toString());
|
||||
@ -179,14 +178,14 @@ export const SettingsDomain = () => {
|
||||
subdomainValue === currentWorkspace?.subdomain &&
|
||||
customDomainValue === currentWorkspace?.customDomain
|
||||
) {
|
||||
return enqueueSnackBar(t`No change detected`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
return enqueueErrorSnackBar({
|
||||
message: t`No change detected`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!values || !currentWorkspace) {
|
||||
return enqueueSnackBar(t`Invalid form values`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
return enqueueErrorSnackBar({
|
||||
message: t`Invalid form values`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user