Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVE] Redesign Administration > Invites #17390

Merged
merged 2 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 0 additions & 80 deletions app/invites/client/admin/adminInvites.html

This file was deleted.

93 changes: 0 additions & 93 deletions app/invites/client/admin/adminInvites.js

This file was deleted.

11 changes: 0 additions & 11 deletions app/invites/client/admin/route.js

This file was deleted.

11 changes: 0 additions & 11 deletions app/invites/client/admin/startup.js

This file was deleted.

158 changes: 158 additions & 0 deletions app/invites/client/components/InvitesPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import {
Button,
Icon,
Table,
Box,
} from '@rocket.chat/fuselage';
import { useMediaQuery } from '@rocket.chat/fuselage-hooks';
import React, { useState, useEffect } from 'react';
import moment from 'moment';

import { Page } from '../../../../client/components/basic/Page';
import { useTranslation } from '../../../../client/contexts/TranslationContext';
import { useEndpoint } from '../../../../client/contexts/ServerContext';
import { useModal } from '../../../../client/hooks/useModal';
import { useToastMessageDispatch } from '../../../../client/contexts/ToastMessagesContext';
import { GenericTable } from '../../../ui/client/components/GenericTable';
import { useFormatDateAndTime } from '../../../../client/hooks/useFormatDateAndTime';


function InviteRow({ _id, createdAt, expires, days, uses, maxUses, onRemove }) {
const t = useTranslation();
const formatDateAndTime = useFormatDateAndTime();
const modal = useModal();
const dispatchToastMessage = useToastMessageDispatch();

const removeInvite = useEndpoint('DELETE', `removeInvite/${ _id }`);

const daysToExpire = ({ expires, days }) => {
if (days > 0) {
if (expires < Date.now()) {
return t('Expired');
}

return moment(expires).fromNow(true);
}

return t('Never');
};

const maxUsesLeft = ({ maxUses, uses }) => {
if (maxUses > 0) {
if (uses >= maxUses) {
return 0;
}

return maxUses - uses;
}

return t('Unlimited');
};

const handleRemoveButtonClick = async (event) => {
event.stopPropagation();

modal.open({ // TODO REFACTOR
text: t('Are_you_sure_you_want_to_delete_this_record'),
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#DD6B55',
confirmButtonText: t('Yes'),
cancelButtonText: t('No'),
closeOnConfirm: true,
html: false,
}, async (confirmed) => {
if (!confirmed) {
return;
}

try {
await removeInvite();
onRemove && onRemove(_id);
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
});
};

const notSmall = useMediaQuery('(min-width: 768px)');

return <Table.Row>
<Table.Cell>
<Box textStyle='p1' textColor='hint'>{_id}</Box>
</Table.Cell>
{notSmall && <>
<Table.Cell>
{formatDateAndTime(createdAt)}
</Table.Cell>
<Table.Cell>
{daysToExpire({ expires, days })}
</Table.Cell>
<Table.Cell>
{uses}
</Table.Cell>
<Table.Cell>
{maxUsesLeft({ maxUses, uses })}
</Table.Cell>
</>}
<Table.Cell>
<Button ghost danger small square onClick={handleRemoveButtonClick}>
<Icon name='cross' size='x20' />
</Button>
</Table.Cell>
</Table.Row>;
}

function InvitesPage() {
const t = useTranslation();

const [invites, setInvites] = useState([]);

const listInvites = useEndpoint('GET', 'listInvites');

useEffect(() => {
const loadInvites = async () => {
const result = await listInvites() || [];

const invites = result.map((data) => ({
...data,
createdAt: new Date(data.createdAt),
expires: data.expires ? new Date(data.expires) : '',
}));

setInvites(invites);
};

loadInvites();
}, []);

const handleInviteRemove = (_id) => {
setInvites((invites = []) => invites.filter((invite) => invite._id !== _id));
};

const notSmall = useMediaQuery('(min-width: 768px)');

return <Page>
<Page.Header title={t('Invites')} />
<Page.ContentShadowScroll>
<GenericTable
results={invites}
header={
<>
<Table.Cell is='th' width={notSmall ? '20%' : '80%'}>{t('Token')}</Table.Cell>
{ notSmall && <>
<Table.Cell is='th' width='35%'>{t('Created_at')}</Table.Cell>
<Table.Cell is='th' width='20%'>{t('Expiration')}</Table.Cell>
<Table.Cell is='th' width='10%'>{t('Uses')}</Table.Cell>
<Table.Cell is='th' width='10%'>{t('Uses_left')}</Table.Cell>
</>}
<Table.Cell is='th' />
</>
}
renderRow={(invite) => <InviteRow key={invite._id} {...invite} onRemove={handleInviteRemove} />}
/>
</Page.ContentShadowScroll>
</Page>;
}

export default InvitesPage;
17 changes: 17 additions & 0 deletions app/invites/client/components/InvitesRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';

import { usePermission } from '../../../../client/contexts/AuthorizationContext';
import NotAuthorizedPage from '../../../ui-admin/client/components/NotAuthorizedPage';
import InvitesPage from './InvitesPage';

function InvitesRoute() {
const canCreateInviteLinks = usePermission('create-invite-links');

if (!canCreateInviteLinks) {
return <NotAuthorizedPage />;
}

return <InvitesPage />;
}

export default InvitesRoute;
16 changes: 14 additions & 2 deletions app/invites/client/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
import './admin/route';
import './admin/startup';
import { registerAdminRoute, registerAdminSidebarItem } from '../../ui-admin/client';
import { hasPermission } from '../../authorization/client';

registerAdminSidebarItem({
href: 'invites',
i18nLabel: 'Invites',
icon: 'user-plus',
permissionGranted: () => hasPermission('create-invite-links'),
});

registerAdminRoute('/invites', {
name: 'invites',
lazyRouteComponent: () => import('./components/InvitesRoute'),
});