diff --git a/apps/meteor/client/views/admin/users/AdminUserUpgrade.tsx b/apps/meteor/client/views/admin/users/AdminUserUpgrade.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6b2baa1e2b9cef1784281b97f9bc305e93f34551 --- /dev/null +++ b/apps/meteor/client/views/admin/users/AdminUserUpgrade.tsx @@ -0,0 +1,32 @@ +import { Button, ButtonGroup, ContextualbarEmptyContent, ContextualbarFooter } from '@rocket.chat/fuselage'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { useExternalLink } from '../../../hooks/useExternalLink'; +import { useCheckoutUrl } from '../subscription/hooks/useCheckoutUrl'; + +const AdminUserUpgrade = () => { + const t = useTranslation(); + const router = useRouter(); + const manageSubscriptionUrl = useCheckoutUrl()({ target: 'user-page', action: 'buy_more' }); + const openExternalLink = useExternalLink(); + + return ( + <> + <ContextualbarScrollableContent h='full'> + <ContextualbarEmptyContent icon='warning' title={t('Seat_limit_reached')} subtitle={t('Seat_limit_reached_Description')} /> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button onClick={() => router.navigate('/admin/users')}>{t('Cancel')}</Button> + <Button primary role='link' onClick={() => openExternalLink(manageSubscriptionUrl)}> + {t('Buy_more_seats')} + </Button> + </ButtonGroup> + </ContextualbarFooter> + </> + ); +}; + +export default AdminUserUpgrade; diff --git a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx index 70fe8636175ccf5b145b42e031378ef9c446cb2f..2fd4c86d16b4f94472a5241e1894030ba37fce07 100644 --- a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx +++ b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx @@ -1,7 +1,7 @@ -import { Button, ButtonGroup } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup, ContextualbarIcon } from '@rocket.chat/fuselage'; import { usePermission, useRouteParameter, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React, { useEffect, useRef } from 'react'; +import React, { useRef } from 'react'; import UserPageHeaderContentWithSeatsCap from '../../../../ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap'; import { useSeatsCap } from '../../../../ee/client/views/admin/users/useSeatsCap'; @@ -12,10 +12,12 @@ import AdminInviteUsers from './AdminInviteUsers'; import AdminUserForm from './AdminUserForm'; import AdminUserFormWithData from './AdminUserFormWithData'; import AdminUserInfoWithData from './AdminUserInfoWithData'; +import AdminUserUpgrade from './AdminUserUpgrade'; import UsersTable from './UsersTable'; const UsersPage = (): ReactElement => { const t = useTranslation(); + const seatsCap = useSeatsCap(); const reload = useRef(() => null); @@ -28,21 +30,13 @@ const UsersPage = (): ReactElement => { const isCreateUserDisabled = useShouldPreventAction('activeUsers'); - useEffect(() => { - if (!context || !seatsCap) { - return; - } - - if (isCreateUserDisabled && !['edit', 'info'].includes(context)) { - router.navigate('/admin/users'); - } - }, [router, context, seatsCap, isCreateUserDisabled]); - const handleReload = (): void => { seatsCap?.reload(); reload.current(); }; + const isRoutePrevented = context && ['new', 'invite'].includes(context) && isCreateUserDisabled; + return ( <Page flexDirection='row'> <Page> @@ -71,6 +65,7 @@ const UsersPage = (): ReactElement => { {context && ( <Contextualbar is='aside' aria-labelledby=''> <ContextualbarHeader> + {context === 'upgrade' && <ContextualbarIcon name='user-plus' />} <ContextualbarTitle> {context === 'info' && t('User_Info')} {context === 'edit' && t('Edit_User')} @@ -81,8 +76,9 @@ const UsersPage = (): ReactElement => { </ContextualbarHeader> {context === 'info' && id && <AdminUserInfoWithData uid={id} onReload={handleReload} />} {context === 'edit' && id && <AdminUserFormWithData uid={id} onReload={handleReload} />} - {context === 'new' && <AdminUserForm onReload={handleReload} />} - {context === 'invite' && <AdminInviteUsers />} + {!isRoutePrevented && context === 'new' && <AdminUserForm onReload={handleReload} />} + {!isRoutePrevented && context === 'invite' && <AdminInviteUsers />} + {isRoutePrevented && <AdminUserUpgrade />} </Contextualbar> )} </Page> diff --git a/apps/meteor/ee/client/views/admin/users/ReachedSeatsCapModal.tsx b/apps/meteor/ee/client/views/admin/users/ReachedSeatsCapModal.tsx deleted file mode 100644 index a8c5fcf9ea2d1a8ba1775d4f10fef685f4dad768..0000000000000000000000000000000000000000 --- a/apps/meteor/ee/client/views/admin/users/ReachedSeatsCapModal.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Modal, Button, Box } from '@rocket.chat/fuselage'; -import type { ReactElement } from 'react'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -type ReachedSeatsCapModalProps = { - onClose: () => void; - onContinue: () => void; - onBuyMoreSeats: () => void; - showContinue: boolean; -}; - -const ReachedSeatsCapModal = ({ onClose, onContinue, onBuyMoreSeats, showContinue }: ReachedSeatsCapModalProps): ReactElement => { - const { t } = useTranslation(); - - return ( - <Modal> - <Modal.Header> - <Modal.Title>{t('Seat_limit_reached')}</Modal.Title> - <Modal.Close onClick={onClose} /> - </Modal.Header> - <Modal.Content> - <Box is='p' mbe={16}> - {t('Seat_limit_reached_Description')} - </Box> - </Modal.Content> - <Modal.Footer> - <Modal.FooterControllers> - {showContinue && <Button onClick={onContinue}>{t('Continue')}</Button>} - <Button primary onClick={onBuyMoreSeats}> - {t('Buy_more_seats')} - </Button> - </Modal.FooterControllers> - </Modal.Footer> - </Modal> - ); -}; - -export default ReachedSeatsCapModal; diff --git a/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx b/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx index 4a2b618ff4b2eb3b45409d544515db62cdf3d72b..6fddef20a95688d44a60f9627a116de2e140d58c 100644 --- a/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx +++ b/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx @@ -1,12 +1,11 @@ import { Button, ButtonGroup, Margins } from '@rocket.chat/fuselage'; -import { useSetModal, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; import { useExternalLink } from '../../../../../client/hooks/useExternalLink'; import { useShouldPreventAction } from '../../../../../client/hooks/useShouldPreventAction'; import { useCheckoutUrl } from '../../../../../client/views/admin/subscription/hooks/useCheckoutUrl'; -import ReachedSeatsCapModal from './ReachedSeatsCapModal'; import SeatsCapUsage from './SeatsCapUsage'; type UserPageHeaderContentWithSeatsCapProps = { @@ -20,40 +19,16 @@ const UserPageHeaderContentWithSeatsCap = ({ activeUsers, maxActiveUsers }: User const t = useTranslation(); const router = useRouter(); - const setModal = useSetModal(); - const closeModal = (): void => setModal(null); - - const openExternalLink = useExternalLink(); const manageSubscriptionUrl = useCheckoutUrl()({ target: 'user-page', action: 'buy_more' }); + const openExternalLink = useExternalLink(); - const withReachedLimit = (fn: () => void) => (): void => { - if (isCreateUserDisabled) { - setModal( - <ReachedSeatsCapModal - onClose={closeModal} - onContinue={() => { - router.navigate('/admin/users/new'); - closeModal(); - }} - onBuyMoreSeats={() => { - openExternalLink(manageSubscriptionUrl); - }} - showContinue={!isCreateUserDisabled} - />, - ); - return; - } - - fn(); - }; - - const handleNewButtonClick = withReachedLimit(() => { + const handleNewButtonClick = () => { router.navigate('/admin/users/new'); - }); + }; - const handleInviteButtonClick = withReachedLimit(() => { + const handleInviteButtonClick = () => { router.navigate('/admin/users/invite'); - }); + }; return ( <> @@ -68,7 +43,7 @@ const UserPageHeaderContentWithSeatsCap = ({ activeUsers, maxActiveUsers }: User {t('New_user')} </Button> {isCreateUserDisabled && ( - <Button is='a' href={manageSubscriptionUrl} target='_blank' rel='noopener noreferrer' primary> + <Button primary role='link' onClick={() => openExternalLink(manageSubscriptionUrl)}> {t('Buy_more_seats')} </Button> )} diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 8f51a67ac8b7a866a81f9a0d430ba232025a8538..8bcb42394641d628e9c366ac5ad2436105a8db64 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -6188,6 +6188,6 @@ "Unlock_premium_capabilities": "Unlock premium capabilities", "Unlimited_seats": "Unlimited seats", "Seat_limit_reached": "Seat limit reached", - "Seat_limit_reached_Description": "Your workspace has reached its contractual seat limit and soon you won't be able to add more users.", + "Seat_limit_reached_Description": "Your workspace reached its contractual seat limit. Buy more seats to add more users.", "Buy_more_seats": "Buy more seats" }