mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-16 03:21:27 +08:00

Co-authored-by: gitstart <gitstart@users.noreply.github.com> Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev> Co-authored-by: gitstart-app[bot] <57568882+gitstart-app[bot]@users.noreply.github.com> Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com> Co-authored-by: RubensRafael <rubensrafael2@live.com> Co-authored-by: niteshsingh1357 <niteshsingh1357@gmail.com> Co-authored-by: gitstart_bot <gitstart_bot@users.noreply.github.com> Co-authored-by: Palash Gupta <palashgdev@gmail.com>
292 lines
6.7 KiB
TypeScript
292 lines
6.7 KiB
TypeScript
import { PlusOutlined } from '@ant-design/icons';
|
|
import { Button, Modal, notification, Space, Table, Typography } from 'antd';
|
|
import { ColumnsType } from 'antd/lib/table';
|
|
import deleteInvite from 'api/user/deleteInvite';
|
|
import getPendingInvites from 'api/user/getPendingInvites';
|
|
import sendInvite from 'api/user/sendInvite';
|
|
import { INVITE_MEMBERS_HASH } from 'constants/app';
|
|
import ROUTES from 'constants/routes';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useQuery } from 'react-query';
|
|
import { useLocation } from 'react-router-dom';
|
|
import { useCopyToClipboard } from 'react-use';
|
|
import { PayloadProps } from 'types/api/user/getPendingInvites';
|
|
import { ROLES } from 'types/roles';
|
|
|
|
import InviteTeamMembers from '../InviteTeamMembers';
|
|
import { TitleWrapper } from './styles';
|
|
|
|
function PendingInvitesContainer(): JSX.Element {
|
|
const [
|
|
isInviteTeamMemberModalOpen,
|
|
setIsInviteTeamMemberModalOpen,
|
|
] = useState<boolean>(false);
|
|
const [isInvitingMembers, setIsInvitingMembers] = useState<boolean>(false);
|
|
const { t } = useTranslation(['organizationsettings', 'common']);
|
|
const [state, setText] = useCopyToClipboard();
|
|
const [notifications, NotificationElement] = notification.useNotification();
|
|
|
|
useEffect(() => {
|
|
if (state.error) {
|
|
notifications.error({
|
|
message: state.error.message,
|
|
});
|
|
}
|
|
|
|
if (state.value) {
|
|
notifications.success({
|
|
message: t('success', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
}
|
|
}, [state.error, state.value, t, notifications]);
|
|
|
|
const getPendingInvitesResponse = useQuery({
|
|
queryFn: () => getPendingInvites(),
|
|
queryKey: 'getPendingInvites',
|
|
});
|
|
|
|
const toggleModal = (value: boolean): void => {
|
|
setIsInviteTeamMemberModalOpen(value);
|
|
};
|
|
|
|
const [allMembers, setAllMembers] = useState<InviteTeamMembersProps[]>([
|
|
{
|
|
email: '',
|
|
name: '',
|
|
role: 'VIEWER',
|
|
},
|
|
]);
|
|
|
|
const [dataSource, setDataSource] = useState<DataProps[]>([]);
|
|
|
|
const { hash } = useLocation();
|
|
|
|
const getParsedInviteData = useCallback(
|
|
(payload: PayloadProps = []) =>
|
|
payload?.map((data) => ({
|
|
key: data.createdAt,
|
|
name: data.name,
|
|
email: data.email,
|
|
accessLevel: data.role,
|
|
inviteLink: `${window.location.origin}${ROUTES.SIGN_UP}?token=${data.token}`,
|
|
})),
|
|
[],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (hash === INVITE_MEMBERS_HASH) {
|
|
toggleModal(true);
|
|
}
|
|
}, [hash]);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
getPendingInvitesResponse.status === 'success' &&
|
|
getPendingInvitesResponse?.data?.payload
|
|
) {
|
|
const data = getParsedInviteData(
|
|
getPendingInvitesResponse?.data?.payload || [],
|
|
);
|
|
setDataSource(data);
|
|
}
|
|
}, [
|
|
getParsedInviteData,
|
|
getPendingInvitesResponse?.data?.payload,
|
|
getPendingInvitesResponse.status,
|
|
]);
|
|
|
|
const onRevokeHandler = async (email: string): Promise<void> => {
|
|
try {
|
|
const response = await deleteInvite({
|
|
email,
|
|
});
|
|
if (response.statusCode === 200) {
|
|
// remove from the client data
|
|
const index = dataSource.findIndex((e) => e.email === email);
|
|
|
|
if (index !== -1) {
|
|
setDataSource([
|
|
...dataSource.slice(0, index),
|
|
...dataSource.slice(index + 1, dataSource.length),
|
|
]);
|
|
}
|
|
notifications.success({
|
|
message: t('success', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
} else {
|
|
notifications.error({
|
|
message:
|
|
response.error ||
|
|
t('something_went_wrong', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
}
|
|
} catch (error) {
|
|
notifications.error({
|
|
message: t('something_went_wrong', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
}
|
|
};
|
|
|
|
const columns: ColumnsType<DataProps> = [
|
|
{
|
|
title: 'Name',
|
|
dataIndex: 'name',
|
|
key: 'name',
|
|
},
|
|
{
|
|
title: 'Emails',
|
|
dataIndex: 'email',
|
|
key: 'email',
|
|
},
|
|
{
|
|
title: 'Access Level',
|
|
dataIndex: 'accessLevel',
|
|
key: 'accessLevel',
|
|
},
|
|
{
|
|
title: 'Invite Link',
|
|
dataIndex: 'inviteLink',
|
|
key: 'Invite Link',
|
|
ellipsis: true,
|
|
},
|
|
{
|
|
title: 'Action',
|
|
dataIndex: 'action',
|
|
key: 'Action',
|
|
render: (_, record): JSX.Element => (
|
|
<Space direction="horizontal">
|
|
<Typography.Link
|
|
onClick={(): Promise<void> => onRevokeHandler(record.email)}
|
|
>
|
|
Revoke
|
|
</Typography.Link>
|
|
<Typography.Link
|
|
onClick={(): void => {
|
|
setText(record.inviteLink);
|
|
}}
|
|
>
|
|
Copy Invite Link
|
|
</Typography.Link>
|
|
</Space>
|
|
),
|
|
},
|
|
];
|
|
|
|
const onInviteClickHandler = async (): Promise<void> => {
|
|
try {
|
|
setIsInvitingMembers(true);
|
|
allMembers.forEach(
|
|
async (members): Promise<void> => {
|
|
const { error, statusCode } = await sendInvite({
|
|
email: members.email,
|
|
name: members.name,
|
|
role: members.role,
|
|
});
|
|
|
|
if (statusCode !== 200) {
|
|
notifications.error({
|
|
message:
|
|
error ||
|
|
t('something_went_wrong', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
}
|
|
},
|
|
);
|
|
|
|
setTimeout(async () => {
|
|
const { data, status } = await getPendingInvitesResponse.refetch();
|
|
if (status === 'success' && data.payload) {
|
|
setDataSource(getParsedInviteData(data?.payload || []));
|
|
}
|
|
setIsInvitingMembers(false);
|
|
toggleModal(false);
|
|
}, 2000);
|
|
} catch (error) {
|
|
notifications.error({
|
|
message: t('something_went_wrong', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
{NotificationElement}
|
|
<Modal
|
|
title={t('invite_team_members')}
|
|
open={isInviteTeamMemberModalOpen}
|
|
onCancel={(): void => toggleModal(false)}
|
|
centered
|
|
destroyOnClose
|
|
footer={[
|
|
<Button key="back" onClick={(): void => toggleModal(false)} type="default">
|
|
{t('cancel', {
|
|
ns: 'common',
|
|
})}
|
|
</Button>,
|
|
<Button
|
|
key={t('invite_team_members').toString()}
|
|
onClick={onInviteClickHandler}
|
|
type="primary"
|
|
disabled={isInvitingMembers}
|
|
loading={isInvitingMembers}
|
|
>
|
|
{t('invite_team_members')}
|
|
</Button>,
|
|
]}
|
|
>
|
|
<InviteTeamMembers allMembers={allMembers} setAllMembers={setAllMembers} />
|
|
</Modal>
|
|
|
|
<Space direction="vertical" size="middle">
|
|
<TitleWrapper>
|
|
<Typography.Title level={3}>{t('pending_invites')}</Typography.Title>
|
|
<Button
|
|
icon={<PlusOutlined />}
|
|
type="primary"
|
|
onClick={(): void => {
|
|
toggleModal(true);
|
|
}}
|
|
>
|
|
{t('invite_members')}
|
|
</Button>
|
|
</TitleWrapper>
|
|
<Table
|
|
tableLayout="fixed"
|
|
dataSource={dataSource}
|
|
columns={columns}
|
|
pagination={false}
|
|
loading={getPendingInvitesResponse.status === 'loading'}
|
|
/>
|
|
</Space>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export interface InviteTeamMembersProps {
|
|
email: string;
|
|
name: string;
|
|
role: ROLES;
|
|
}
|
|
|
|
interface DataProps {
|
|
key: number;
|
|
name: string;
|
|
email: string;
|
|
accessLevel: ROLES;
|
|
inviteLink: string;
|
|
}
|
|
export default PendingInvitesContainer;
|