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:
@ -4,8 +4,8 @@ import { useRecoilValue } from 'recoil';
|
||||
import { z } from 'zod';
|
||||
|
||||
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 { ApolloError } from '@apollo/client';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
ConnectionParameters,
|
||||
@ -38,7 +38,7 @@ export const useImapConnectionForm = ({
|
||||
}: UseImapConnectionFormProps = {}) => {
|
||||
const { t } = useLingui();
|
||||
const navigate = useNavigateSettings();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
@ -70,16 +70,12 @@ export const useImapConnectionForm = ({
|
||||
formValues: ConnectionParameters & { handle: string },
|
||||
) => {
|
||||
if (!currentWorkspace?.id) {
|
||||
enqueueSnackBar('Workspace ID is missing', {
|
||||
variant: SnackBarVariant.Error,
|
||||
});
|
||||
enqueueErrorSnackBar({});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentWorkspaceMember?.id) {
|
||||
enqueueSnackBar('Workspace member ID is missing', {
|
||||
variant: SnackBarVariant.Error,
|
||||
});
|
||||
enqueueErrorSnackBar({});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -112,19 +108,16 @@ export const useImapConnectionForm = ({
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSnackBar(
|
||||
connectedAccountId
|
||||
enqueueSuccessSnackBar({
|
||||
message: connectedAccountId
|
||||
? t`IMAP connection successfully updated`
|
||||
: t`IMAP connection successfully created`,
|
||||
{
|
||||
variant: SnackBarVariant.Success,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
navigate(SettingsPath.Accounts);
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState';
|
||||
import { SettingsAdminWorkspaceContent } from '@/settings/admin-panel/components/SettingsAdminWorkspaceContent';
|
||||
import { userLookupResultState } from '@/settings/admin-panel/states/userLookupResultState';
|
||||
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 { TabList } from '@/ui/layout/tab-list/components/TabList';
|
||||
@ -40,7 +39,7 @@ const StyledContainer = styled.div`
|
||||
|
||||
export const SettingsAdminGeneral = () => {
|
||||
const [userIdentifier, setUserIdentifier] = useState('');
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const [activeTabId, setActiveTabId] = useRecoilComponentStateV2(
|
||||
activeTabIdComponentState,
|
||||
@ -76,8 +75,8 @@ export const SettingsAdminGeneral = () => {
|
||||
},
|
||||
onError: (error) => {
|
||||
setIsUserLookupLoading(false);
|
||||
enqueueSnackBar(error.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -7,7 +7,6 @@ import { useImpersonationAuth } from '@/settings/admin-panel/hooks/useImpersonat
|
||||
import { useImpersonationRedirect } from '@/settings/admin-panel/hooks/useImpersonationRedirect';
|
||||
import { userLookupResultState } from '@/settings/admin-panel/states/userLookupResultState';
|
||||
import { WorkspaceInfo } from '@/settings/admin-panel/types/WorkspaceInfo';
|
||||
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';
|
||||
@ -57,7 +56,7 @@ export const SettingsAdminWorkspaceContent = ({
|
||||
activeWorkspace,
|
||||
}: SettingsAdminWorkspaceContentProps) => {
|
||||
const canManageFeatureFlags = useRecoilValue(canManageFeatureFlagsState);
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
@ -74,9 +73,7 @@ export const SettingsAdminWorkspaceContent = ({
|
||||
|
||||
const handleImpersonate = async (workspaceId: string) => {
|
||||
if (!userLookupResult?.user.id) {
|
||||
enqueueSnackBar(t`Please search for a user first`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
});
|
||||
enqueueErrorSnackBar({ message: t`Please search for a user first` });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -98,8 +95,8 @@ export const SettingsAdminWorkspaceContent = ({
|
||||
);
|
||||
},
|
||||
onError: (error) => {
|
||||
enqueueSnackBar(`Failed to impersonate user. ${error.message}`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: `Failed to impersonate user. ${error.message}`,
|
||||
});
|
||||
},
|
||||
}).finally(() => {
|
||||
@ -128,8 +125,8 @@ export const SettingsAdminWorkspaceContent = ({
|
||||
if (isDefined(previousValue)) {
|
||||
updateFeatureFlagState(workspaceId, featureFlag, previousValue);
|
||||
}
|
||||
enqueueSnackBar(`Failed to update feature flag. ${error.message}`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: `Failed to update feature flag. ${error.message}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
@ -33,15 +32,17 @@ export const SettingsAdminConfigCopyableText = ({
|
||||
multiline = false,
|
||||
maxRows,
|
||||
}: SettingsAdminConfigCopyableTextProps) => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
const copyToClipboardDebounced = useDebouncedCallback((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} />,
|
||||
},
|
||||
});
|
||||
}, 200);
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { useClientConfig } from '@/client-config/hooks/useClientConfig';
|
||||
import { GET_DATABASE_CONFIG_VARIABLE } from '@/settings/admin-panel/config-variables/graphql/queries/getDatabaseConfigVariable';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ConfigVariableValue } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -14,7 +13,7 @@ import {
|
||||
|
||||
export const useConfigVariableActions = (variableName: string) => {
|
||||
const { t } = useLingui();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const { refetch: refetchClientConfig } = useClientConfig();
|
||||
|
||||
const [updateDatabaseConfigVariable] =
|
||||
@ -68,12 +67,12 @@ export const useConfigVariableActions = (variableName: string) => {
|
||||
|
||||
await refetchClientConfig();
|
||||
|
||||
enqueueSnackBar(t`Variable updated successfully.`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Variable updated successfully.`,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackBar(t`Failed to update variable`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to update variable`,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -98,12 +97,12 @@ export const useConfigVariableActions = (variableName: string) => {
|
||||
|
||||
await refetchClientConfig();
|
||||
|
||||
enqueueSnackBar(t`Variable deleted successfully.`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Variable deleted successfully.`,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackBar(t`Failed to remove override`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to remove override`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { SettingsAdminTableCard } from '@/settings/admin-panel/components/SettingsAdminTableCard';
|
||||
import { WorkerMetricsTooltip } from '@/settings/admin-panel/health-status/components/WorkerMetricsTooltip';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
@ -45,7 +44,7 @@ export const WorkerMetricsGraph = ({
|
||||
timeRange,
|
||||
}: WorkerMetricsGraphProps) => {
|
||||
const theme = useTheme();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const { loading, data } = useGetQueueMetricsQuery({
|
||||
variables: {
|
||||
@ -54,8 +53,8 @@ export const WorkerMetricsGraph = ({
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
onError: (error) => {
|
||||
enqueueSnackBar(`Error fetching worker metrics: ${error.message}`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: `Error fetching worker metrics: ${error.message}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -6,8 +6,8 @@ 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 { ApolloError } from '@apollo/client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
@ -23,7 +23,7 @@ export const SettingsUpdateDataModelObjectAboutForm = ({
|
||||
objectMetadataItem,
|
||||
}: SettingsUpdateDataModelObjectAboutFormProps) => {
|
||||
const navigate = useNavigateSettings();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const setUpdatedObjectNamePlural = useSetRecoilState(
|
||||
updatedObjectNamePluralState,
|
||||
);
|
||||
@ -117,15 +117,20 @@ export const SettingsUpdateDataModelObjectAboutForm = ({
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof ZodError) {
|
||||
enqueueSnackBar(error.issues[0].message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: error.issues[0].message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
});
|
||||
if (error instanceof ApolloError) {
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
enqueueErrorSnackBar({});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -7,9 +7,9 @@ import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdat
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getActiveFieldMetadataItems } from '@/object-metadata/utils/getActiveFieldMetadataItems';
|
||||
import { objectMetadataItemSchema } from '@/object-metadata/validation-schemas/objectMetadataItemSchema';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@ -48,7 +48,7 @@ export const SettingsDataModelObjectIdentifiersForm = ({
|
||||
mode: 'onTouched',
|
||||
resolver: zodResolver(settingsDataModelObjectIdentifiersFormSchema),
|
||||
});
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
||||
|
||||
const handleSave = async (
|
||||
@ -67,12 +67,12 @@ export const SettingsDataModelObjectIdentifiersForm = ({
|
||||
formConfig.reset(undefined, { keepValues: true });
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
enqueueSnackBar(error.issues[0].message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: error.issues[0].message,
|
||||
});
|
||||
} else {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
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 { useLingui } from '@lingui/react/macro';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { IconCopy } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@ -25,7 +24,7 @@ export const ApiKeyInput = ({ apiKey }: ApiKeyInputProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledLinkContainer>
|
||||
@ -35,10 +34,12 @@ export const ApiKeyInput = ({ apiKey }: ApiKeyInputProps) => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSnackBar(t`API Key copied to clipboard`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`API Key copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(apiKey);
|
||||
}}
|
||||
|
||||
@ -5,11 +5,13 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { WebhookFormMode } from '@/settings/developers/constants/WebhookFormMode';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { useWebhookForm } from '../useWebhookForm';
|
||||
|
||||
// Mock dependencies
|
||||
const mockNavigateSettings = jest.fn();
|
||||
const mockEnqueueSnackBar = jest.fn();
|
||||
const mockEnqueueSuccessSnackBar = jest.fn();
|
||||
const mockEnqueueErrorSnackBar = jest.fn();
|
||||
const mockCreateOneRecord = jest.fn();
|
||||
const mockUpdateOneRecord = jest.fn();
|
||||
const mockDeleteOneRecord = jest.fn();
|
||||
@ -20,7 +22,8 @@ jest.mock('~/hooks/useNavigateSettings', () => ({
|
||||
|
||||
jest.mock('@/ui/feedback/snack-bar-manager/hooks/useSnackBar', () => ({
|
||||
useSnackBar: () => ({
|
||||
enqueueSnackBar: mockEnqueueSnackBar,
|
||||
enqueueSuccessSnackBar: mockEnqueueSuccessSnackBar,
|
||||
enqueueErrorSnackBar: mockEnqueueErrorSnackBar,
|
||||
}),
|
||||
}));
|
||||
|
||||
@ -106,14 +109,15 @@ describe('useWebhookForm', () => {
|
||||
secret: 'test-secret',
|
||||
});
|
||||
|
||||
expect(mockEnqueueSnackBar).toHaveBeenCalledWith(
|
||||
'Webhook https://test.com/webhook created successfully',
|
||||
{ variant: 'success' },
|
||||
);
|
||||
expect(mockEnqueueSuccessSnackBar).toHaveBeenCalledWith({
|
||||
message: 'Webhook https://test.com/webhook created successfully',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle creation errors', async () => {
|
||||
const error = new Error('Creation failed');
|
||||
const error = new ApolloError({
|
||||
graphQLErrors: [{ message: 'Creation failed' }],
|
||||
});
|
||||
mockCreateOneRecord.mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(
|
||||
@ -130,8 +134,8 @@ describe('useWebhookForm', () => {
|
||||
|
||||
await result.current.handleSave(formData);
|
||||
|
||||
expect(mockEnqueueSnackBar).toHaveBeenCalledWith('Creation failed', {
|
||||
variant: 'error',
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
apolloError: error,
|
||||
});
|
||||
});
|
||||
|
||||
@ -216,7 +220,9 @@ describe('useWebhookForm', () => {
|
||||
});
|
||||
|
||||
it('should handle update errors', async () => {
|
||||
const error = new Error('Update failed');
|
||||
const error = new ApolloError({
|
||||
graphQLErrors: [{ message: 'Update failed' }],
|
||||
});
|
||||
mockUpdateOneRecord.mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(
|
||||
@ -237,8 +243,8 @@ describe('useWebhookForm', () => {
|
||||
|
||||
await result.current.handleSave(formData);
|
||||
|
||||
expect(mockEnqueueSnackBar).toHaveBeenCalledWith('Update failed', {
|
||||
variant: 'error',
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
apolloError: error,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -297,10 +303,9 @@ describe('useWebhookForm', () => {
|
||||
await result.current.deleteWebhook();
|
||||
|
||||
expect(mockDeleteOneRecord).toHaveBeenCalledWith(webhookId);
|
||||
expect(mockEnqueueSnackBar).toHaveBeenCalledWith(
|
||||
'Webhook deleted successfully',
|
||||
{ variant: 'success' },
|
||||
);
|
||||
expect(mockEnqueueSuccessSnackBar).toHaveBeenCalledWith({
|
||||
message: 'Webhook deleted successfully',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle deletion without webhookId', async () => {
|
||||
@ -311,14 +316,15 @@ describe('useWebhookForm', () => {
|
||||
|
||||
await result.current.deleteWebhook();
|
||||
|
||||
expect(mockEnqueueSnackBar).toHaveBeenCalledWith(
|
||||
'Webhook ID is required for deletion',
|
||||
{ variant: 'error' },
|
||||
);
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
message: 'Webhook ID is required for deletion',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle deletion errors', async () => {
|
||||
const error = new Error('Deletion failed');
|
||||
const error = new ApolloError({
|
||||
graphQLErrors: [{ message: 'Deletion failed' }],
|
||||
});
|
||||
mockDeleteOneRecord.mockRejectedValue(error);
|
||||
|
||||
const { result } = renderHook(
|
||||
@ -332,8 +338,8 @@ describe('useWebhookForm', () => {
|
||||
|
||||
await result.current.deleteWebhook();
|
||||
|
||||
expect(mockEnqueueSnackBar).toHaveBeenCalledWith('Deletion failed', {
|
||||
variant: 'error',
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
apolloError: error,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -13,8 +13,9 @@ import {
|
||||
WebhookFormValues,
|
||||
} from '@/settings/developers/validation-schemas/webhookFormSchema';
|
||||
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 { ApolloError } from '@apollo/client';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
@ -28,7 +29,7 @@ type UseWebhookFormProps = {
|
||||
|
||||
export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
const navigate = useNavigateSettings();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const isCreationMode = mode === WebhookFormMode.Create;
|
||||
|
||||
@ -134,28 +135,29 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
...webhookData,
|
||||
});
|
||||
|
||||
enqueueSnackBar(
|
||||
`Webhook ${createdWebhook?.targetUrl} created successfully`,
|
||||
{
|
||||
variant: SnackBarVariant.Success,
|
||||
},
|
||||
);
|
||||
const targetUrl = createdWebhook?.targetUrl
|
||||
? `${createdWebhook?.targetUrl}`
|
||||
: '';
|
||||
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Webhook ${targetUrl} created successfully`,
|
||||
});
|
||||
|
||||
navigate(
|
||||
createdWebhook ? SettingsPath.WebhookDetail : SettingsPath.Webhooks,
|
||||
createdWebhook ? { webhookId: createdWebhook.id } : undefined,
|
||||
);
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdate = async (formValues: WebhookFormValues) => {
|
||||
if (!webhookId) {
|
||||
enqueueSnackBar('Webhook ID is required for updates', {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Webhook ID is required for updates`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -177,12 +179,14 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
|
||||
formConfig.reset(formValues);
|
||||
|
||||
enqueueSnackBar(`Webhook ${webhookData.targetUrl} updated successfully`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
const targetUrl = webhookData.targetUrl ? `${webhookData.targetUrl}` : '';
|
||||
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Webhook ${targetUrl} updated successfully`,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -222,22 +226,22 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
|
||||
const deleteWebhook = async () => {
|
||||
if (!webhookId) {
|
||||
enqueueSnackBar('Webhook ID is required for deletion', {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Webhook ID is required for deletion`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteOneWebhook(webhookId);
|
||||
enqueueSnackBar('Webhook deleted successfully', {
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Webhook deleted successfully`,
|
||||
});
|
||||
|
||||
navigate(SettingsPath.Webhooks);
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,13 +9,14 @@ import {
|
||||
} from '@/settings/integrations/database-connection/utils/editDatabaseConnection';
|
||||
import { SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration';
|
||||
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 { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Section } from '@react-email/components';
|
||||
import pick from 'lodash.pick';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { H2Title, Info } from 'twenty-ui/display';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
RemoteServer,
|
||||
@ -24,7 +25,6 @@ import {
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { H2Title, Info } from 'twenty-ui/display';
|
||||
|
||||
export const SettingsIntegrationEditDatabaseConnectionContent = ({
|
||||
connection,
|
||||
@ -37,7 +37,7 @@ export const SettingsIntegrationEditDatabaseConnectionContent = ({
|
||||
databaseKey: string;
|
||||
tables: RemoteTable[];
|
||||
}) => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const navigate = useNavigateSettings();
|
||||
|
||||
const editConnectionSchema = getEditionSchemaForForm(databaseKey);
|
||||
@ -87,8 +87,8 @@ export const SettingsIntegrationEditDatabaseConnectionContent = ({
|
||||
connectionId: connection?.id,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackBar((error as Error).message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -12,7 +12,6 @@ import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDr
|
||||
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
|
||||
import { settingsRolesIsLoadingState } from '@/settings/roles/states/settingsRolesIsLoadingState';
|
||||
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';
|
||||
@ -81,7 +80,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
|
||||
const { loadCurrentUser } = useAuth();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
if (!isDefined(settingsRolesIsLoading)) {
|
||||
return <></>;
|
||||
@ -129,8 +128,8 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
);
|
||||
|
||||
if (isDefined(dirtyFields.label) && dirtyFields.label === '') {
|
||||
enqueueSnackBar(t`Role name cannot be empty`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Role name cannot be empty`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -8,8 +8,8 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { SettingsCard } from '@/settings/components/SettingsCard';
|
||||
import { SettingsSSOIdentitiesProvidersListCardWrapper } from '@/settings/security/components/SSO/SettingsSSOIdentitiesProvidersListCardWrapper';
|
||||
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import isPropValid from '@emotion/is-prop-valid';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
@ -26,7 +26,7 @@ const StyledLink = styled(Link, {
|
||||
`;
|
||||
|
||||
export const SettingsSSOIdentitiesProvidersListCard = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
@ -42,9 +42,9 @@ export const SettingsSSOIdentitiesProvidersListCard = () => {
|
||||
onCompleted: (data) => {
|
||||
setSSOIdentitiesProviders(data?.getSSOIdentityProviders ?? []);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
enqueueSnackBar(error.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
onError: (error: ApolloError) => {
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
/* @license Enterprise */
|
||||
|
||||
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 { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { H2Title, IconCopy } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
|
||||
const StyledInputsContainer = styled.div`
|
||||
display: flex;
|
||||
@ -37,7 +36,7 @@ const StyledButtonCopy = styled.div`
|
||||
|
||||
export const SettingsSSOOIDCForm = () => {
|
||||
const { control } = useFormContext();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
@ -66,10 +65,12 @@ export const SettingsSSOOIDCForm = () => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSnackBar(t`Authorized URL copied to clipboard`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Authorized URL copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(authorizedUrl);
|
||||
}}
|
||||
@ -91,10 +92,12 @@ export const SettingsSSOOIDCForm = () => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSnackBar(t`Redirect Url copied to clipboard`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Redirect Url copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(redirectionUrl);
|
||||
}}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
/* @license Enterprise */
|
||||
|
||||
import { parseSAMLMetadataFromXMLFile } from '@/settings/security/utils/parseSAMLMetadataFromXMLFile';
|
||||
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 { useTheme } from '@emotion/react';
|
||||
@ -9,9 +8,7 @@ import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { ChangeEvent, useRef } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import {
|
||||
H2Title,
|
||||
HorizontalSeparator,
|
||||
@ -20,7 +17,9 @@ import {
|
||||
IconDownload,
|
||||
IconUpload,
|
||||
} from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
|
||||
const StyledUploadFileContainer = styled.div`
|
||||
align-items: center;
|
||||
@ -56,7 +55,7 @@ const StyledButtonCopy = styled.div`
|
||||
`;
|
||||
|
||||
export const SettingsSSOSAMLForm = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { setValue, getValues, watch, trigger } = useFormContext();
|
||||
const { t } = useLingui();
|
||||
@ -67,9 +66,11 @@ export const SettingsSSOSAMLForm = () => {
|
||||
const samlMetadataParsed = parseSAMLMetadataFromXMLFile(text);
|
||||
e.target.value = '';
|
||||
if (!samlMetadataParsed.success) {
|
||||
return enqueueSnackBar(t`Invalid File`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
return enqueueErrorSnackBar({
|
||||
message: t`Invalid File`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
setValue('ssoURL', samlMetadataParsed.data.ssoUrl);
|
||||
@ -103,9 +104,11 @@ export const SettingsSSOSAMLForm = () => {
|
||||
`${REACT_APP_SERVER_BASE_URL}/auth/saml/metadata/${getValues('id')}`,
|
||||
);
|
||||
if (!response.ok) {
|
||||
return enqueueSnackBar(t`Metadata file generation failed`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
return enqueueErrorSnackBar({
|
||||
message: t`Metadata file generation failed`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
const text = await response.text();
|
||||
@ -177,10 +180,12 @@ export const SettingsSSOSAMLForm = () => {
|
||||
Icon={IconCopy}
|
||||
title="Copy"
|
||||
onClick={() => {
|
||||
enqueueSnackBar('ACS Url copied to clipboard', {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`ACS Url copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(acsUrl);
|
||||
}}
|
||||
@ -202,10 +207,12 @@ export const SettingsSSOSAMLForm = () => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSnackBar(t`Entity ID copied to clipboard`, {
|
||||
variant: SnackBarVariant.Success,
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Entity ID copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(entityID);
|
||||
}}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { useDeleteSSOIdentityProvider } from '@/settings/security/hooks/useDeleteSSOIdentityProvider';
|
||||
import { useUpdateSSOIdentityProvider } from '@/settings/security/hooks/useUpdateSSOIdentityProvider';
|
||||
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
@ -24,7 +23,7 @@ export const SettingsSecuritySSORowDropdownMenu = ({
|
||||
}: SettingsSecuritySSORowDropdownMenuProps) => {
|
||||
const dropdownId = `settings-account-row-${SSOIdp.id}`;
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
@ -40,9 +39,11 @@ export const SettingsSecuritySSORowDropdownMenu = ({
|
||||
identityProviderId,
|
||||
});
|
||||
if (isDefined(result.errors)) {
|
||||
enqueueSnackBar(t`Error deleting SSO Identity Provider`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Error deleting SSO Identity Provider`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -58,9 +59,11 @@ export const SettingsSecuritySSORowDropdownMenu = ({
|
||||
: SsoIdentityProviderStatus.Active,
|
||||
});
|
||||
if (isDefined(result.errors)) {
|
||||
enqueueSnackBar(t`Error editing SSO Identity Provider`, {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Error editing SSO Identity Provider`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -2,8 +2,8 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
||||
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
@ -30,7 +30,7 @@ const StyledSettingsSecurityOptionsList = styled.div`
|
||||
export const SettingsSecurityAuthProvidersOptionsList = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const SSOIdentitiesProviders = useRecoilValue(SSOIdentitiesProvidersState);
|
||||
const authProviders = useRecoilValue(authProvidersState);
|
||||
|
||||
@ -72,12 +72,9 @@ export const SettingsSecurityAuthProvidersOptionsList = () => {
|
||||
allAuthProvidersEnabled.filter((isAuthEnabled) => isAuthEnabled).length <=
|
||||
1
|
||||
) {
|
||||
return enqueueSnackBar(
|
||||
t`At least one authentication method must be enabled`,
|
||||
{
|
||||
variant: SnackBarVariant.Error,
|
||||
},
|
||||
);
|
||||
return enqueueErrorSnackBar({
|
||||
message: t`At least one authentication method must be enabled`,
|
||||
});
|
||||
}
|
||||
|
||||
setCurrentWorkspace({
|
||||
@ -97,8 +94,8 @@ export const SettingsSecurityAuthProvidersOptionsList = () => {
|
||||
...currentWorkspace,
|
||||
[key]: !currentWorkspace[key],
|
||||
});
|
||||
enqueueSnackBar(err?.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: err instanceof ApolloError ? err : undefined,
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -120,8 +117,8 @@ export const SettingsSecurityAuthProvidersOptionsList = () => {
|
||||
isPublicInviteLinkEnabled: value,
|
||||
});
|
||||
} catch (err: any) {
|
||||
enqueueSnackBar(err?.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: err instanceof ApolloError ? err : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,8 +7,8 @@ import { SettingsListCard } from '@/settings/components/SettingsListCard';
|
||||
import { SettingsSecurityApprovedAccessDomainRowDropdownMenu } from '@/settings/security/components/approvedAccessDomains/SettingsSecurityApprovedAccessDomainRowDropdownMenu';
|
||||
import { SettingsSecurityApprovedAccessDomainValidationEffect } from '@/settings/security/components/approvedAccessDomains/SettingsSecurityApprovedAccessDomainValidationEffect';
|
||||
import { approvedAccessDomainsState } from '@/settings/security/states/ApprovedAccessDomainsState';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState } from 'recoil';
|
||||
@ -22,7 +22,7 @@ const StyledLink = styled(Link)`
|
||||
`;
|
||||
|
||||
export const SettingsApprovedAccessDomainsListCard = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useLingui();
|
||||
|
||||
@ -36,8 +36,8 @@ export const SettingsApprovedAccessDomainsListCard = () => {
|
||||
setApprovedAccessDomains(data?.getApprovedAccessDomains ?? []);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
enqueueSnackBar(error.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { approvedAccessDomainsState } from '@/settings/security/states/ApprovedAccessDomainsState';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { UnwrapRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
|
||||
@ -25,7 +25,7 @@ export const SettingsSecurityApprovedAccessDomainRowDropdownMenu = ({
|
||||
approvedAccessDomainsState,
|
||||
);
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
@ -47,9 +47,11 @@ export const SettingsSecurityApprovedAccessDomainRowDropdownMenu = ({
|
||||
},
|
||||
});
|
||||
if (isDefined(result.errors)) {
|
||||
enqueueSnackBar('Error deleting approved access domain', {
|
||||
variant: SnackBarVariant.Error,
|
||||
duration: 2000,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Could not delete approved access domain`,
|
||||
options: {
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useEffect } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -8,7 +8,7 @@ import { useValidateApprovedAccessDomainMutation } from '~/generated-metadata/gr
|
||||
export const SettingsSecurityApprovedAccessDomainValidationEffect = () => {
|
||||
const [validateApprovedAccessDomainMutation] =
|
||||
useValidateApprovedAccessDomainMutation();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const [searchParams] = useSearchParams();
|
||||
const approvedAccessDomainId = searchParams.get('wtdId');
|
||||
const validationToken = searchParams.get('validationToken');
|
||||
@ -23,15 +23,19 @@ export const SettingsSecurityApprovedAccessDomainValidationEffect = () => {
|
||||
},
|
||||
},
|
||||
onCompleted: () => {
|
||||
enqueueSnackBar('Approved access domain validated', {
|
||||
dedupeKey: 'approved-access-domain-validation-dedupe-key',
|
||||
variant: SnackBarVariant.Success,
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Approved access domain validated`,
|
||||
options: {
|
||||
dedupeKey: 'approved-access-domain-validation-dedupe-key',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
enqueueSnackBar('Error validating approved access domain', {
|
||||
dedupeKey: 'approved-access-domain-validation-error-dedupe-key',
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Error validating approved access domain`,
|
||||
options: {
|
||||
dedupeKey: 'approved-access-domain-validation-error-dedupe-key',
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -2,15 +2,15 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconLifebuoy } from 'twenty-ui/display';
|
||||
import { Card } from 'twenty-ui/layout';
|
||||
import { useUpdateWorkspaceMutation } from '~/generated-metadata/graphql';
|
||||
|
||||
export const ToggleImpersonate = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||
currentWorkspaceState,
|
||||
@ -35,8 +35,8 @@ export const ToggleImpersonate = () => {
|
||||
allowImpersonation: value,
|
||||
});
|
||||
} catch (err: any) {
|
||||
enqueueSnackBar(err?.message, {
|
||||
variant: SnackBarVariant.Error,
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: err instanceof ApolloError ? err : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user