mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-22 10:36:57 +00:00
Merge pull request #21782 from overleaf/ls-update-group-member-management-page
update group member management page for flexible licensing GitOrigin-RevId: 605fb760a1f73763e49978cf4aea81bb88ffb425
This commit is contained in:
parent
ac8d8d6edc
commit
56f6f77ba2
7 changed files with 169 additions and 27 deletions
|
@ -11,6 +11,7 @@ import {
|
|||
import { SSOConfig } from '../../models/SSOConfig.js'
|
||||
import { Parser as CSVParser } from 'json2csv'
|
||||
import { expressify } from '@overleaf/promise-utils'
|
||||
import SplitTestHandler from '../SplitTests/SplitTestHandler.js'
|
||||
|
||||
async function manageGroupMembers(req, res, next) {
|
||||
const { entity: subscription, entityConfig } = req
|
||||
|
@ -29,6 +30,12 @@ async function manageGroupMembers(req, res, next) {
|
|||
)
|
||||
const ssoConfig = await SSOConfig.findById(subscription.ssoConfig).exec()
|
||||
|
||||
await SplitTestHandler.promises.getAssignment(
|
||||
req,
|
||||
res,
|
||||
'flexible-group-licensing'
|
||||
)
|
||||
|
||||
res.render('user_membership/group-members-react', {
|
||||
name: entityName,
|
||||
groupId: entityPrimaryKey,
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"add_more_editors": "",
|
||||
"add_more_managers": "",
|
||||
"add_more_members": "",
|
||||
"add_more_users": "",
|
||||
"add_new_email": "",
|
||||
"add_ons_are": "",
|
||||
"add_or_remove_project_from_tag": "",
|
||||
|
@ -751,11 +752,13 @@
|
|||
"invite": "",
|
||||
"invite_expired": "",
|
||||
"invite_more_collabs": "",
|
||||
"invite_more_members": "",
|
||||
"invite_not_accepted": "",
|
||||
"invite_resend_limit_hit": "",
|
||||
"invited_to_group": "",
|
||||
"invited_to_group_have_individual_subcription": "",
|
||||
"invited_to_join": "",
|
||||
"inviting": "",
|
||||
"ip_address": "",
|
||||
"is_email_affiliated": "",
|
||||
"issued_on": "",
|
||||
|
@ -1871,10 +1874,12 @@
|
|||
"you_dont_have_any_repositories": "",
|
||||
"you_have_0_free_suggestions_left": "",
|
||||
"you_have_1_free_suggestion_left": "",
|
||||
"you_have_1_user_and_your_plan_supports_up_to_y": "",
|
||||
"you_have_added_x_of_group_size_y": "",
|
||||
"you_have_been_invited_to_transfer_management_of_your_account": "",
|
||||
"you_have_been_invited_to_transfer_management_of_your_account_to": "",
|
||||
"you_have_been_removed_from_this_project_and_will_be_redirected_to_project_dashboard": "",
|
||||
"you_have_x_users_and_your_plan_supports_up_to_y": "",
|
||||
"you_need_to_configure_your_sso_settings": "",
|
||||
"you_will_be_able_to_reassign_subscription": "",
|
||||
"youll_get_best_results_in_visual_but_can_be_used_in_source": "",
|
||||
|
|
|
@ -7,6 +7,7 @@ import getMeta from '../../../utils/meta'
|
|||
import { useGroupMembersContext } from '../context/group-members-context'
|
||||
import ErrorAlert from './error-alert'
|
||||
import MembersList from './members-table/members-list'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
|
||||
export default function GroupMembers() {
|
||||
const { isReady } = useWaitForI18n()
|
||||
|
@ -23,6 +24,9 @@ export default function GroupMembers() {
|
|||
paths,
|
||||
} = useGroupMembersContext()
|
||||
const [emailString, setEmailString] = useState<string>('')
|
||||
const flexibleGroupLicensingEnabled = useFeatureFlag(
|
||||
'flexible-group-licensing'
|
||||
)
|
||||
|
||||
const groupId = getMeta('ol-groupId')
|
||||
const groupName = getMeta('ol-groupName')
|
||||
|
@ -44,6 +48,43 @@ export default function GroupMembers() {
|
|||
addMembers(emailString)
|
||||
}
|
||||
|
||||
const groupSizeDetails = () => {
|
||||
if (flexibleGroupLicensingEnabled) {
|
||||
return (
|
||||
<small data-testid="group-size-details">
|
||||
<strong>
|
||||
{users.length === 1
|
||||
? t('you_have_1_user_and_your_plan_supports_up_to_y', {
|
||||
groupSize,
|
||||
})
|
||||
: t('you_have_x_users_and_your_plan_supports_up_to_y', {
|
||||
addedUsersSize: users.length,
|
||||
groupSize,
|
||||
})}
|
||||
</strong>{' '}
|
||||
<a
|
||||
href="/user/subscription/group/add-users"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{t('add_more_users')}
|
||||
</a>
|
||||
</small>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<small>
|
||||
<Trans
|
||||
i18nKey="you_have_added_x_of_group_size_y"
|
||||
components={[<strong />, <strong />]} // eslint-disable-line react/jsx-key
|
||||
values={{ addedUsersSize: users.length, groupSize }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</small>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<Row>
|
||||
|
@ -60,17 +101,7 @@ export default function GroupMembers() {
|
|||
<div className="card">
|
||||
<div className="page-header">
|
||||
<div className="pull-right">
|
||||
{selectedUsers.length === 0 && (
|
||||
<small>
|
||||
<Trans
|
||||
i18nKey="you_have_added_x_of_group_size_y"
|
||||
components={[<strong />, <strong />]} // eslint-disable-line react/jsx-key
|
||||
values={{ addedUsersSize: users.length, groupSize }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</small>
|
||||
)}
|
||||
{selectedUsers.length === 0 && groupSizeDetails()}
|
||||
{removeMemberLoading ? (
|
||||
<Button bsStyle="danger" disabled>
|
||||
{t('removing')}…
|
||||
|
@ -93,8 +124,15 @@ export default function GroupMembers() {
|
|||
</div>
|
||||
<hr />
|
||||
{users.length < groupSize && (
|
||||
<div className="add-more-members-form">
|
||||
<p className="small">{t('add_more_members')}</p>
|
||||
<div
|
||||
className="add-more-members-form"
|
||||
data-testid="add-more-members-form"
|
||||
>
|
||||
<p className="small">
|
||||
{flexibleGroupLicensingEnabled
|
||||
? t('invite_more_members')
|
||||
: t('add_more_members')}
|
||||
</p>
|
||||
<ErrorAlert error={inviteError} />
|
||||
<Form horizontal onSubmit={onAddMembersSubmit} className="form">
|
||||
<Row>
|
||||
|
@ -110,11 +148,16 @@ export default function GroupMembers() {
|
|||
<Col xs={4}>
|
||||
{inviteMemberLoading ? (
|
||||
<Button bsStyle="primary" disabled>
|
||||
{t('adding')}…
|
||||
{flexibleGroupLicensingEnabled
|
||||
? t('inviting')
|
||||
: t('adding')}
|
||||
…
|
||||
</Button>
|
||||
) : (
|
||||
<Button bsStyle="primary" onClick={onAddMembersSubmit}>
|
||||
{t('add')}
|
||||
{flexibleGroupLicensingEnabled
|
||||
? t('invite')
|
||||
: t('add')}
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
|
|
|
@ -2,13 +2,16 @@ import '../base'
|
|||
import ReactDOM from 'react-dom'
|
||||
import GroupMembers from '../../../../features/group-management/components/group-members'
|
||||
import { GroupMembersProvider } from '../../../../features/group-management/context/group-members-context'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
|
||||
const element = document.getElementById('subscription-manage-group-root')
|
||||
if (element) {
|
||||
ReactDOM.render(
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>,
|
||||
<SplitTestProvider>
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
</SplitTestProvider>,
|
||||
element
|
||||
)
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
"add_more_editors": "Add more editors",
|
||||
"add_more_managers": "Add more managers",
|
||||
"add_more_members": "Add more members",
|
||||
"add_more_users": "Add more users.",
|
||||
"add_new_email": "Add new email",
|
||||
"add_ons_are": "<strong>Add-ons:</strong> __addOnName__",
|
||||
"add_or_remove_project_from_tag": "Add or remove project from tag __tagName__",
|
||||
|
@ -1051,6 +1052,7 @@
|
|||
"invite": "Invite",
|
||||
"invite_expired": "The invite may have expired",
|
||||
"invite_more_collabs": "Invite more collaborators",
|
||||
"invite_more_members": "Invite more members",
|
||||
"invite_not_accepted": "Invite not yet accepted",
|
||||
"invite_not_valid": "This is not a valid project invite",
|
||||
"invite_not_valid_description": "The invite may have expired. Please contact the project owner",
|
||||
|
@ -1062,6 +1064,7 @@
|
|||
"invited_to_group_register": "To accept __inviterName__’s invitation you’ll need to create an account.",
|
||||
"invited_to_group_register_benefits": "__appName__ is a collaborative online LaTeX editor, with thousands of ready-to-use templates and an array of LaTeX learning resources to help you get started.",
|
||||
"invited_to_join": "You have been invited to join",
|
||||
"inviting": "Inviting",
|
||||
"ip_address": "IP Address",
|
||||
"is_email_affiliated": "Is your email affiliated with an institution? ",
|
||||
"is_longer_than_n_characters": "is at least __n__ characters long",
|
||||
|
@ -2535,10 +2538,12 @@
|
|||
"you_get_access_to_info": "These features are available only to you (the subscriber).",
|
||||
"you_have_0_free_suggestions_left": "You have 0 free suggestions left",
|
||||
"you_have_1_free_suggestion_left": "You have 1 free suggestion left",
|
||||
"you_have_1_user_and_your_plan_supports_up_to_y": "You have 1 user and your plan supports up to __groupSize__.",
|
||||
"you_have_added_x_of_group_size_y": "You have added <0>__addedUsersSize__</0> of <1>__groupSize__</1> available members",
|
||||
"you_have_been_invited_to_transfer_management_of_your_account": "You have been invited to transfer management of your account.",
|
||||
"you_have_been_invited_to_transfer_management_of_your_account_to": "You have been invited to transfer management of your account to __groupName__.",
|
||||
"you_have_been_removed_from_this_project_and_will_be_redirected_to_project_dashboard": "You have been removed from this project, and will no longer have access to it. You will be redirected to your project dashboard momentarily.",
|
||||
"you_have_x_users_and_your_plan_supports_up_to_y": "You have __addedUsersSize__ users and your plan supports up to __groupSize__.",
|
||||
"you_need_to_configure_your_sso_settings": "You need to configure and test your SSO settings before enabling SSO",
|
||||
"you_plus_1": "You + 1",
|
||||
"you_plus_10": "You + 10",
|
||||
|
|
|
@ -2,6 +2,7 @@ import '../../../helpers/bootstrap-3'
|
|||
import GroupMembers from '@/features/group-management/components/group-members'
|
||||
import { GroupMembersProvider } from '@/features/group-management/context/group-members-context'
|
||||
import { User } from '../../../../../types/group-management/user'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
|
||||
const GROUP_ID = '777fff777fff'
|
||||
const PATHS = {
|
||||
|
@ -14,9 +15,11 @@ const PATHS = {
|
|||
describe('GroupMembers', function () {
|
||||
function mountGroupMembersProvider() {
|
||||
cy.mount(
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
<SplitTestProvider>
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
</SplitTestProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -47,9 +50,11 @@ describe('GroupMembers', function () {
|
|||
})
|
||||
|
||||
cy.mount(
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
<SplitTestProvider>
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
</SplitTestProvider>
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -456,4 +461,75 @@ describe('GroupMembers', function () {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with flexible group licensing enabled', function () {
|
||||
const JOHN_DOE = {
|
||||
_id: 'abc123def456',
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
email: 'john.doe@test.com',
|
||||
last_active_at: new Date('2023-01-15'),
|
||||
invite: false,
|
||||
}
|
||||
const BOBBY_LAPOINTE = {
|
||||
_id: 'bcd234efa567',
|
||||
first_name: 'Bobby',
|
||||
last_name: 'Lapointe',
|
||||
email: 'bobby.lapointe@test.com',
|
||||
last_active_at: new Date('2023-01-02'),
|
||||
invite: false,
|
||||
}
|
||||
|
||||
it('renders the group members page with the new text', function () {
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-groupId', GROUP_ID)
|
||||
win.metaAttributesCache.set('ol-groupName', 'My Awesome Team')
|
||||
win.metaAttributesCache.set('ol-groupSize', 10)
|
||||
win.metaAttributesCache.set('ol-users', [JOHN_DOE, BOBBY_LAPOINTE])
|
||||
win.metaAttributesCache.set('ol-splitTestVariants', {
|
||||
'flexible-group-licensing': 'enabled',
|
||||
})
|
||||
})
|
||||
|
||||
cy.mount(
|
||||
<SplitTestProvider>
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
</SplitTestProvider>
|
||||
)
|
||||
|
||||
cy.findByTestId('group-size-details').contains(
|
||||
'You have 2 users and your plan supports up to 10. Add more users.'
|
||||
)
|
||||
cy.findByTestId('add-more-members-form').within(() => {
|
||||
cy.contains('Invite more members')
|
||||
cy.get('button').contains('Invite')
|
||||
})
|
||||
})
|
||||
|
||||
it('renders the group members page with new text when only has one group member', function () {
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-groupId', GROUP_ID)
|
||||
win.metaAttributesCache.set('ol-groupName', 'My Awesome Team')
|
||||
win.metaAttributesCache.set('ol-groupSize', 10)
|
||||
win.metaAttributesCache.set('ol-users', [JOHN_DOE])
|
||||
win.metaAttributesCache.set('ol-splitTestVariants', {
|
||||
'flexible-group-licensing': 'enabled',
|
||||
})
|
||||
})
|
||||
|
||||
cy.mount(
|
||||
<SplitTestProvider>
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
</SplitTestProvider>
|
||||
)
|
||||
|
||||
cy.findByTestId('group-size-details').contains(
|
||||
'You have 1 user and your plan supports up to 10. Add more users.'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@ import '../../../helpers/bootstrap-3'
|
|||
import GroupMembers from '@/features/group-management/components/group-members'
|
||||
import { GroupMembersProvider } from '@/features/group-management/context/group-members-context'
|
||||
import { User } from '../../../../../types/group-management/user'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
|
||||
const GROUP_ID = '777fff777fff'
|
||||
const JOHN_DOE: User = {
|
||||
|
@ -57,9 +58,11 @@ const PATHS = {
|
|||
|
||||
function mountGroupMembersProvider() {
|
||||
cy.mount(
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
<SplitTestProvider>
|
||||
<GroupMembersProvider>
|
||||
<GroupMembers />
|
||||
</GroupMembersProvider>
|
||||
</SplitTestProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue