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_METRICS": "SigNoz | Service Metrics",
|
||||||
"SERVICE_MAP": "SigNoz | Service Map",
|
"SERVICE_MAP": "SigNoz | Service Map",
|
||||||
"GET_STARTED": "SigNoz | Get Started",
|
"GET_STARTED": "SigNoz | Get Started",
|
||||||
|
"GET_STARTED_V2": "SigNoz | Get Started",
|
||||||
"GET_STARTED_APPLICATION_MONITORING": "SigNoz | Get Started | APM",
|
"GET_STARTED_APPLICATION_MONITORING": "SigNoz | Get Started | APM",
|
||||||
"GET_STARTED_LOGS_MANAGEMENT": "SigNoz | Get Started | Logs",
|
"GET_STARTED_LOGS_MANAGEMENT": "SigNoz | Get Started | Logs",
|
||||||
"GET_STARTED_INFRASTRUCTURE_MONITORING": "SigNoz | Get Started | Infrastructure",
|
"GET_STARTED_INFRASTRUCTURE_MONITORING": "SigNoz | Get Started | Infrastructure",
|
||||||
|
@ -1,26 +1,20 @@
|
|||||||
import { GatewayApiV2Instance } from 'api';
|
import { GatewayApiV2Instance } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
|
||||||
import { AxiosError } from 'axios';
|
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { UpdateProfileProps } from 'types/api/onboarding/types';
|
import { UpdateProfileProps } from 'types/api/onboarding/types';
|
||||||
|
|
||||||
const updateProfile = async (
|
const updateProfile = async (
|
||||||
props: UpdateProfileProps,
|
props: UpdateProfileProps,
|
||||||
): Promise<SuccessResponse<UpdateProfileProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<UpdateProfileProps> | ErrorResponse> => {
|
||||||
try {
|
const response = await GatewayApiV2Instance.put('/profiles/me', {
|
||||||
const response = await GatewayApiV2Instance.put('/profiles/me', {
|
...props,
|
||||||
...props,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
error: null,
|
error: null,
|
||||||
message: response.data.status,
|
message: response.data.status,
|
||||||
payload: response.data.data,
|
payload: response.data.data,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default updateProfile;
|
export default updateProfile;
|
||||||
|
@ -1,25 +1,18 @@
|
|||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import axios from 'api';
|
||||||
import axios, { AxiosError } from 'axios';
|
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { PayloadProps, UsersProps } from 'types/api/user/inviteUsers';
|
import { PayloadProps, UsersProps } from 'types/api/user/inviteUsers';
|
||||||
|
|
||||||
const inviteUsers = async (
|
const inviteUsers = async (
|
||||||
users: UsersProps,
|
users: UsersProps,
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
const response = await axios.post(`/invite/bulk`, users);
|
||||||
const response = await axios.post(`/invite/bulk`, {
|
|
||||||
...users,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
error: null,
|
error: null,
|
||||||
message: response.data.status,
|
message: response.data.status,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default inviteUsers;
|
export default inviteUsers;
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ import './InviteTeamMembers.styles.scss';
|
|||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Input, Select, Typography } from 'antd';
|
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 { cloneDeep, debounce, isEmpty } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
@ -11,6 +13,8 @@ import {
|
|||||||
TriangleAlert,
|
TriangleAlert,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
|
import { ErrorResponse } from 'types/api';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
interface TeamMember {
|
interface TeamMember {
|
||||||
@ -37,18 +41,17 @@ function InviteTeamMembers({
|
|||||||
const [teamMembersToInvite, setTeamMembersToInvite] = useState<
|
const [teamMembersToInvite, setTeamMembersToInvite] = useState<
|
||||||
TeamMember[] | null
|
TeamMember[] | null
|
||||||
>(teamMembers);
|
>(teamMembers);
|
||||||
|
|
||||||
const [emailValidity, setEmailValidity] = useState<Record<string, boolean>>(
|
const [emailValidity, setEmailValidity] = useState<Record<string, boolean>>(
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
const [hasInvalidEmails, setHasInvalidEmails] = useState<boolean>(false);
|
const [hasInvalidEmails, setHasInvalidEmails] = useState<boolean>(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const defaultTeamMember: TeamMember = {
|
const defaultTeamMember: TeamMember = {
|
||||||
email: '',
|
email: '',
|
||||||
role: 'EDITOR',
|
role: 'EDITOR',
|
||||||
name: '',
|
name: '',
|
||||||
frontendBaseUrl: '',
|
frontendBaseUrl: window.location.origin,
|
||||||
id: '',
|
id: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,7 +68,10 @@ function InviteTeamMembers({
|
|||||||
}, [teamMembers]);
|
}, [teamMembers]);
|
||||||
|
|
||||||
const handleAddTeamMember = (): void => {
|
const handleAddTeamMember = (): void => {
|
||||||
const newTeamMember = { ...defaultTeamMember, id: uuid() };
|
const newTeamMember = {
|
||||||
|
...defaultTeamMember,
|
||||||
|
id: uuid(),
|
||||||
|
};
|
||||||
setTeamMembersToInvite((prev) => [...(prev || []), newTeamMember]);
|
setTeamMembersToInvite((prev) => [...(prev || []), newTeamMember]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -89,10 +95,34 @@ function InviteTeamMembers({
|
|||||||
return isValid;
|
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 => {
|
const handleNext = (): void => {
|
||||||
if (validateAllUsers()) {
|
if (validateAllUsers()) {
|
||||||
setTeamMembers(teamMembersToInvite || []);
|
setTeamMembers(teamMembersToInvite || []);
|
||||||
onNext();
|
|
||||||
|
setError(null);
|
||||||
|
setHasInvalidEmails(false);
|
||||||
|
|
||||||
|
sendInvites({
|
||||||
|
users: teamMembersToInvite || [],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,6 +159,10 @@ function InviteTeamMembers({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDoLater = (): void => {
|
||||||
|
onNext();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="questions-container">
|
<div className="questions-container">
|
||||||
<Typography.Title level={3} className="title">
|
<Typography.Title level={3} className="title">
|
||||||
@ -209,20 +243,33 @@ function InviteTeamMembers({
|
|||||||
</div>
|
</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">
|
<div className="next-prev-container">
|
||||||
<Button type="default" className="next-button" onClick={onBack}>
|
<Button type="default" className="next-button" onClick={onBack}>
|
||||||
<ArrowLeft size={14} />
|
<ArrowLeft size={14} />
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button type="primary" className="next-button" onClick={handleNext}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="next-button"
|
||||||
|
onClick={handleNext}
|
||||||
|
loading={isSendingInvites}
|
||||||
|
>
|
||||||
Send Invites
|
Send Invites
|
||||||
<ArrowRight size={14} />
|
<ArrowRight size={14} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="do-later-container">
|
<div className="do-later-container">
|
||||||
<Button type="link" onClick={onNext}>
|
<Button type="link" onClick={handleDoLater}>
|
||||||
I'll do this later
|
I'll do this later
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,7 @@ interface OptimiseSignozNeedsProps {
|
|||||||
setOptimiseSignozDetails: (details: OptimiseSignozDetails) => void;
|
setOptimiseSignozDetails: (details: OptimiseSignozDetails) => void;
|
||||||
onNext: () => void;
|
onNext: () => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
|
onWillDoLater: () => void;
|
||||||
isUpdatingProfile: boolean;
|
isUpdatingProfile: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ function OptimiseSignozNeeds({
|
|||||||
setOptimiseSignozDetails,
|
setOptimiseSignozDetails,
|
||||||
onNext,
|
onNext,
|
||||||
onBack,
|
onBack,
|
||||||
|
onWillDoLater,
|
||||||
}: OptimiseSignozNeedsProps): JSX.Element {
|
}: OptimiseSignozNeedsProps): JSX.Element {
|
||||||
const [logsPerDay, setLogsPerDay] = useState<number>(
|
const [logsPerDay, setLogsPerDay] = useState<number>(
|
||||||
optimiseSignozDetails?.logsPerDay || 25,
|
optimiseSignozDetails?.logsPerDay || 25,
|
||||||
@ -95,6 +97,8 @@ function OptimiseSignozNeeds({
|
|||||||
services: 0,
|
services: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onWillDoLater();
|
||||||
|
|
||||||
logEvent('Onboarding: Optimise SigNoz Needs: Will do later', {
|
logEvent('Onboarding: Optimise SigNoz Needs: Will do later', {
|
||||||
logsPerDay: 0,
|
logsPerDay: 0,
|
||||||
hostsPerDay: 0,
|
hostsPerDay: 0,
|
||||||
|
@ -5,6 +5,7 @@ import updateProfileAPI from 'api/onboarding/updateProfile';
|
|||||||
import editOrg from 'api/user/editOrg';
|
import editOrg from 'api/user/editOrg';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import { InviteTeamMembersProps } from 'container/OrganizationSettings/PendingInvitesContainer';
|
import { InviteTeamMembersProps } from 'container/OrganizationSettings/PendingInvitesContainer';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
@ -187,7 +188,7 @@ function OnboardingQuestionaire(): JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnboardingComplete = (): void => {
|
const handleOnboardingComplete = (): void => {
|
||||||
history.push('/');
|
history.push(ROUTES.APPLICATION);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -222,6 +223,7 @@ function OnboardingQuestionaire(): JSX.Element {
|
|||||||
setOptimiseSignozDetails={setOptimiseSignozDetails}
|
setOptimiseSignozDetails={setOptimiseSignozDetails}
|
||||||
onBack={(): void => setCurrentStep(2)}
|
onBack={(): void => setCurrentStep(2)}
|
||||||
onNext={handleUpdateProfile}
|
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 { User } from 'types/reducer/app';
|
||||||
import { ROLES } from 'types/roles';
|
|
||||||
|
|
||||||
export interface UserProps {
|
export interface UserProps {
|
||||||
name: User['name'];
|
name: User['name'];
|
||||||
email: User['email'];
|
email: User['email'];
|
||||||
role: ROLES;
|
role: string;
|
||||||
frontendBaseUrl: 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