mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-04 11:25:52 +08:00
feat: handle errors for profiles and invite users api
This commit is contained in:
parent
6c350f30aa
commit
4dc5615d2f
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -41,6 +41,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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'll do this later
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
12
frontend/src/utils/error.ts
Normal file
12
frontend/src/utils/error.ts
Normal 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,
|
||||
});
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user