[web] Migrate group management to React (#11293)

* Rename manage group entry point

* Migrate group management root page to React

* Add cypress tests for the group management react page

* Fix linting

* Add checkbox labels for screen-readers + remove unused classes

* Await on add/remove members calls

* Display the export CSV link for a full group

* Display error message when group is full

* Sort locales

* Handle the managers management page in React version

* Fix missing type in GroupMemberRow

* Split members and managers React pages

* Build API paths on frontend side + add cypress tests for each page

* Fix linting

* Update unit tests

* Review improvements

* Type API errors

GitOrigin-RevId: d124a9d24cbf33de8aacc5d69e9d46e7bcda93c5
This commit is contained in:
Alexandre Bourdin
2023-02-07 09:04:18 +00:00
committed by Copybot
parent b1cf4aa1e9
commit ed40a87cdc
29 changed files with 1678 additions and 45 deletions
@@ -20,7 +20,7 @@ const SplitTestHandler = require('../SplitTests/SplitTestHandler')
const CSVParser = require('json2csv').Parser
const logger = require('@overleaf/logger')
async function index(req, res, next) {
async function manageGroupMembers(req, res, next) {
try {
const assignment = await SplitTestHandler.promises.getAssignment(
req,
@@ -28,7 +28,7 @@ async function index(req, res, next) {
'subscription-pages-react'
)
if (assignment.variant === 'active') {
await _indexReact(req, res, next)
await _manageGroupMembersReact(req, res, next)
} else {
await _indexAngular(req, res, next)
}
@@ -41,7 +41,85 @@ async function index(req, res, next) {
}
}
function _indexReact(req, res, next) {
async function manageGroupManagers(req, res, next) {
try {
const assignment = await SplitTestHandler.promises.getAssignment(
req,
res,
'subscription-pages-react'
)
if (assignment.variant === 'active') {
await _renderManagersPage(
req,
res,
next,
'user_membership/group-managers-react'
)
} else {
await _indexAngular(req, res, next)
}
} catch (error) {
logger.warn(
{ err: error },
'failed to get "subscription-pages-react" split test assignment'
)
await _indexAngular(req, res, next)
}
}
async function manageInstitutionManagers(req, res, next) {
try {
const assignment = await SplitTestHandler.promises.getAssignment(
req,
res,
'subscription-pages-react'
)
if (assignment.variant === 'active') {
await _renderManagersPage(
req,
res,
next,
'user_membership/institution-managers-react'
)
} else {
await _indexAngular(req, res, next)
}
} catch (error) {
logger.warn(
{ err: error },
'failed to get "subscription-pages-react" split test assignment'
)
await _indexAngular(req, res, next)
}
}
async function managePublisherManagers(req, res, next) {
try {
const assignment = await SplitTestHandler.promises.getAssignment(
req,
res,
'subscription-pages-react'
)
if (assignment.variant === 'active') {
await _renderManagersPage(
req,
res,
next,
'user_membership/publisher-managers-react'
)
} else {
await _indexAngular(req, res, next)
}
} catch (error) {
logger.warn(
{ err: error },
'failed to get "subscription-pages-react" split test assignment'
)
await _indexAngular(req, res, next)
}
}
async function _manageGroupMembersReact(req, res, next) {
const { entity, entityConfig } = req
return entity.fetchV1Data(function (error, entity) {
if (error != null) {
@@ -60,14 +138,40 @@ function _indexReact(req, res, next) {
if (entityConfig.fields.name) {
entityName = entity[entityConfig.fields.name]
}
return res.render('user_membership/index-react', {
return res.render('user_membership/group-members-react', {
name: entityName,
groupId: entityPrimaryKey,
users,
groupSize: entity.membersLimit,
})
}
)
})
}
async function _renderManagersPage(req, res, next, template) {
const { entity, entityConfig } = req
return entity.fetchV1Data(function (error, entity) {
if (error != null) {
return next(error)
}
return UserMembershipHandler.getUsers(
entity,
entityConfig,
function (error, users) {
let entityName
if (error != null) {
return next(error)
}
const entityPrimaryKey =
entity[entityConfig.fields.primaryKey].toString()
if (entityConfig.fields.name) {
entityName = entity[entityConfig.fields.name]
}
return res.render(template, {
name: entityName,
users,
groupSize: entityConfig.hasMembersLimit
? entity.membersLimit
: undefined,
translations: entityConfig.translations,
paths: entityConfig.pathsFor(entityPrimaryKey),
groupId: entityPrimaryKey,
})
}
)
@@ -108,7 +212,10 @@ function _indexAngular(req, res, next) {
}
module.exports = {
index,
manageGroupMembers,
manageGroupManagers,
manageInstitutionManagers,
managePublisherManagers,
add(req, res, next) {
const { entity, entityConfig } = req
const email = EmailHelper.parseEmail(req.body.email)
@@ -22,7 +22,7 @@ module.exports = {
webRouter.get(
'/manage/groups/:id/members',
UserMembershipMiddleware.requireGroupManagementAccess,
UserMembershipController.index
UserMembershipController.manageGroupMembers
)
webRouter.post(
'/manage/groups/:id/invites',
@@ -51,7 +51,7 @@ module.exports = {
webRouter.get(
'/manage/groups/:id/managers',
UserMembershipMiddleware.requireGroupManagersManagementAccess,
UserMembershipController.index
UserMembershipController.manageGroupManagers
)
webRouter.post(
'/manage/groups/:id/managers',
@@ -68,7 +68,7 @@ module.exports = {
webRouter.get(
'/manage/institutions/:id/managers',
UserMembershipMiddleware.requireInstitutionManagementAccess,
UserMembershipController.index
UserMembershipController.manageInstitutionManagers
)
webRouter.post(
'/manage/institutions/:id/managers',
@@ -85,7 +85,7 @@ module.exports = {
webRouter.get(
'/manage/publishers/:id/managers',
UserMembershipMiddleware.requirePublisherManagementAccess,
UserMembershipController.index
UserMembershipController.managePublisherManagers
)
webRouter.post(
'/manage/publishers/:id/managers',
@@ -0,0 +1,12 @@
extends ../layout-marketing
block entrypointVar
- entrypoint = 'pages/user/subscription/group-management/group-managers'
block append meta
meta(name="ol-users", data-type="json", content=users)
meta(name="ol-groupId", data-type="string", content=groupId)
meta(name="ol-groupName", data-type="string", content=name)
block content
main.content.content-alt#subscription-manage-group-root
@@ -0,0 +1,13 @@
extends ../layout-marketing
block entrypointVar
- entrypoint = 'pages/user/subscription/group-management/group-members'
block append meta
meta(name="ol-users", data-type="json", content=users)
meta(name="ol-groupId", data-type="string", content=groupId)
meta(name="ol-groupName", data-type="string", content=name)
meta(name="ol-groupSize", data-type="json", content=groupSize)
block content
main.content.content-alt#subscription-manage-group-root
@@ -1,12 +0,0 @@
extends ../layout-marketing
block entrypointVar
- entrypoint = 'pages/user/membership/groups'
block append meta
meta(name="ol-users", data-type="json", content=users)
meta(name="ol-paths", data-type="json", content=paths)
meta(name="ol-groupSize", data-type="json", content=groupSize)
block content
main.content.content-alt#subscription-manage-groups-root
@@ -0,0 +1,12 @@
extends ../layout-marketing
block entrypointVar
- entrypoint = 'pages/user/subscription/group-management/institution-managers'
block append meta
meta(name="ol-users", data-type="json", content=users)
meta(name="ol-groupId", data-type="string", content=groupId)
meta(name="ol-groupName", data-type="string", content=name)
block content
main.content.content-alt#subscription-manage-group-root
@@ -0,0 +1,12 @@
extends ../layout-marketing
block entrypointVar
- entrypoint = 'pages/user/subscription/group-management/publisher-managers'
block append meta
meta(name="ol-users", data-type="json", content=users)
meta(name="ol-groupId", data-type="string", content=groupId)
meta(name="ol-groupName", data-type="string", content=name)
block content
main.content.content-alt#subscription-manage-group-root