feat: handle errors for profiles and invite users api

This commit is contained in:
Yunus M 2024-10-23 20:08:23 +05:30
parent 6c350f30aa
commit 4dc5615d2f
9 changed files with 93 additions and 40 deletions

View File

@ -4,6 +4,7 @@
"SERVICE_METRICS": "SigNoz | Service Metrics",
"SERVICE_MAP": "SigNoz | Service Map",
"GET_STARTED": "SigNoz | Get Started",
"GET_STARTED_V2": "SigNoz | Get Started",
"GET_STARTED_APPLICATION_MONITORING": "SigNoz | Get Started | APM",
"GET_STARTED_LOGS_MANAGEMENT": "SigNoz | Get Started | Logs",
"GET_STARTED_INFRASTRUCTURE_MONITORING": "SigNoz | Get Started | Infrastructure",

View File

@ -1,26 +1,20 @@
import { GatewayApiV2Instance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { UpdateProfileProps } from 'types/api/onboarding/types';
const updateProfile = async (
props: UpdateProfileProps,
): Promise<SuccessResponse<UpdateProfileProps> | ErrorResponse> => {
try {
const response = await GatewayApiV2Instance.put('/profiles/me', {
...props,
});
const response = await GatewayApiV2Instance.put('/profiles/me', {
...props,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
};
export default updateProfile;

View File

@ -1,25 +1,18 @@
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import axios, { AxiosError } from 'axios';
import axios from 'api';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, UsersProps } from 'types/api/user/inviteUsers';
const inviteUsers = async (
users: UsersProps,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post(`/invite/bulk`, {
...users,
});
const response = await axios.post(`/invite/bulk`, users);
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data,
};
};
export default inviteUsers;

View File

@ -41,6 +41,7 @@
display: flex;
align-items: center;
gap: 8px;
text-transform: capitalize;
}
}
}

View File

@ -2,6 +2,8 @@ import './InviteTeamMembers.styles.scss';
import { Color } from '@signozhq/design-tokens';
import { Button, Input, Select, Typography } from 'antd';
import inviteUsers from 'api/user/inviteUsers';
import { AxiosError } from 'axios';
import { cloneDeep, debounce, isEmpty } from 'lodash-es';
import {
ArrowLeft,
@ -11,6 +13,8 @@ import {
TriangleAlert,
} from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
import { useMutation } from 'react-query';
import { ErrorResponse } from 'types/api';
import { v4 as uuid } from 'uuid';
interface TeamMember {
@ -37,18 +41,17 @@ function InviteTeamMembers({
const [teamMembersToInvite, setTeamMembersToInvite] = useState<
TeamMember[] | null
>(teamMembers);
const [emailValidity, setEmailValidity] = useState<Record<string, boolean>>(
{},
);
const [hasInvalidEmails, setHasInvalidEmails] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const defaultTeamMember: TeamMember = {
email: '',
role: 'EDITOR',
name: '',
frontendBaseUrl: '',
frontendBaseUrl: window.location.origin,
id: '',
};
@ -65,7 +68,10 @@ function InviteTeamMembers({
}, [teamMembers]);
const handleAddTeamMember = (): void => {
const newTeamMember = { ...defaultTeamMember, id: uuid() };
const newTeamMember = {
...defaultTeamMember,
id: uuid(),
};
setTeamMembersToInvite((prev) => [...(prev || []), newTeamMember]);
};
@ -89,10 +95,34 @@ function InviteTeamMembers({
return isValid;
};
const handleError = (error: AxiosError): void => {
const errorMessage = error.response?.data as ErrorResponse;
setError(errorMessage.error);
};
const { mutate: sendInvites, isLoading: isSendingInvites } = useMutation(
inviteUsers,
{
onSuccess: (): void => {
onNext();
},
onError: (error): void => {
handleError(error as AxiosError);
},
},
);
const handleNext = (): void => {
if (validateAllUsers()) {
setTeamMembers(teamMembersToInvite || []);
onNext();
setError(null);
setHasInvalidEmails(false);
sendInvites({
users: teamMembersToInvite || [],
});
}
};
@ -129,6 +159,10 @@ function InviteTeamMembers({
}
};
const handleDoLater = (): void => {
onNext();
};
return (
<div className="questions-container">
<Typography.Title level={3} className="title">
@ -209,20 +243,33 @@ function InviteTeamMembers({
</div>
)}
{error && (
<div className="error-message-container">
<Typography.Text className="error-message" type="danger">
<TriangleAlert size={14} /> {error}
</Typography.Text>
</div>
)}
<div className="next-prev-container">
<Button type="default" className="next-button" onClick={onBack}>
<ArrowLeft size={14} />
Back
</Button>
<Button type="primary" className="next-button" onClick={handleNext}>
<Button
type="primary"
className="next-button"
onClick={handleNext}
loading={isSendingInvites}
>
Send Invites
<ArrowRight size={14} />
</Button>
</div>
<div className="do-later-container">
<Button type="link" onClick={onNext}>
<Button type="link" onClick={handleDoLater}>
I&apos;ll do this later
</Button>
</div>

View File

@ -14,6 +14,7 @@ interface OptimiseSignozNeedsProps {
setOptimiseSignozDetails: (details: OptimiseSignozDetails) => void;
onNext: () => void;
onBack: () => void;
onWillDoLater: () => void;
isUpdatingProfile: boolean;
}
@ -48,6 +49,7 @@ function OptimiseSignozNeeds({
setOptimiseSignozDetails,
onNext,
onBack,
onWillDoLater,
}: OptimiseSignozNeedsProps): JSX.Element {
const [logsPerDay, setLogsPerDay] = useState<number>(
optimiseSignozDetails?.logsPerDay || 25,
@ -95,6 +97,8 @@ function OptimiseSignozNeeds({
services: 0,
});
onWillDoLater();
logEvent('Onboarding: Optimise SigNoz Needs: Will do later', {
logsPerDay: 0,
hostsPerDay: 0,

View File

@ -5,6 +5,7 @@ import updateProfileAPI from 'api/onboarding/updateProfile';
import editOrg from 'api/user/editOrg';
import { AxiosError } from 'axios';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import ROUTES from 'constants/routes';
import { InviteTeamMembersProps } from 'container/OrganizationSettings/PendingInvitesContainer';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
@ -187,7 +188,7 @@ function OnboardingQuestionaire(): JSX.Element {
};
const handleOnboardingComplete = (): void => {
history.push('/');
history.push(ROUTES.APPLICATION);
};
return (
@ -222,6 +223,7 @@ function OnboardingQuestionaire(): JSX.Element {
setOptimiseSignozDetails={setOptimiseSignozDetails}
onBack={(): void => setCurrentStep(2)}
onNext={handleUpdateProfile}
onWillDoLater={(): void => setCurrentStep(4)} // This is temporary, only to skip gateway api call as it's not setup on staging yet
/>
)}

View File

@ -1,10 +1,9 @@
import { User } from 'types/reducer/app';
import { ROLES } from 'types/roles';
export interface UserProps {
name: User['name'];
email: User['email'];
role: ROLES;
role: string;
frontendBaseUrl: string;
}

View File

@ -0,0 +1,12 @@
import { NotificationInstance } from 'antd/es/notification/interface';
import axios from 'axios';
import { SOMETHING_WENT_WRONG } from 'constants/api';
export const showErrorNotification = (
notifications: NotificationInstance,
err: Error,
): void => {
notifications.error({
message: axios.isAxiosError(err) ? err.message : SOMETHING_WENT_WRONG,
});
};