mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-28 21:23:17 -05:00
Merge pull request #9622 from overleaf/mf-add-portal-templates-2
Add institution templates to the react version of the new project dropdown GitOrigin-RevId: 32bf0b1b559ea3da744430902cc016e7c2a918d9
This commit is contained in:
parent
c42cedbcdc
commit
7608d37c0a
6 changed files with 175 additions and 60 deletions
|
@ -1,4 +1,5 @@
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
|
const Settings = require('@overleaf/settings')
|
||||||
const ProjectHelper = require('./ProjectHelper')
|
const ProjectHelper = require('./ProjectHelper')
|
||||||
const ProjectGetter = require('./ProjectGetter')
|
const ProjectGetter = require('./ProjectGetter')
|
||||||
const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
|
const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
|
||||||
|
@ -19,6 +20,17 @@ const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
||||||
const UserPrimaryEmailCheckHandler = require('../User/UserPrimaryEmailCheckHandler')
|
const UserPrimaryEmailCheckHandler = require('../User/UserPrimaryEmailCheckHandler')
|
||||||
const UserController = require('../User/UserController')
|
const UserController = require('../User/UserController')
|
||||||
|
|
||||||
|
/** @typedef {import("./types").GetProjectsRequest} GetProjectsRequest */
|
||||||
|
/** @typedef {import("./types").GetProjectsResponse} GetProjectsResponse */
|
||||||
|
/** @typedef {import("../../../../types/project/dashboard/api").Project} Project */
|
||||||
|
/** @typedef {import("../../../../types/project/dashboard/api").Filters} Filters */
|
||||||
|
/** @typedef {import("../../../../types/project/dashboard/api").Page} Page */
|
||||||
|
/** @typedef {import("../../../../types/project/dashboard/api").Sort} Sort */
|
||||||
|
/** @typedef {import("./types").AllUsersProjects} AllUsersProjects */
|
||||||
|
/** @typedef {import("./types").MongoProject} MongoProject */
|
||||||
|
|
||||||
|
/** @typedef {import("../Tags/types").Tag} Tag */
|
||||||
|
|
||||||
const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
|
const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
|
||||||
if (!affiliation.institution) return false
|
if (!affiliation.institution) return false
|
||||||
|
|
||||||
|
@ -38,16 +50,29 @@ const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @typedef {import("./types").GetProjectsRequest} GetProjectsRequest */
|
const _buildPortalTemplatesList = affiliations => {
|
||||||
/** @typedef {import("./types").GetProjectsResponse} GetProjectsResponse */
|
if (affiliations == null) {
|
||||||
/** @typedef {import("../../../../types/project/dashboard/api").Project} Project */
|
affiliations = []
|
||||||
/** @typedef {import("../../../../types/project/dashboard/api").Filters} Filters */
|
}
|
||||||
/** @typedef {import("../../../../types/project/dashboard/api").Page} Page */
|
|
||||||
/** @typedef {import("../../../../types/project/dashboard/api").Sort} Sort */
|
|
||||||
/** @typedef {import("./types").AllUsersProjects} AllUsersProjects */
|
|
||||||
/** @typedef {import("./types").MongoProject} MongoProject */
|
|
||||||
|
|
||||||
/** @typedef {import("../Tags/types").Tag} Tag */
|
const portalTemplates = []
|
||||||
|
const uniqueAffiliations = _.uniqBy(affiliations, 'institution.id')
|
||||||
|
for (const aff of uniqueAffiliations) {
|
||||||
|
const hasSlug = aff.portal?.slug
|
||||||
|
const hasTemplates = aff.portal?.templates_count > 0
|
||||||
|
|
||||||
|
if (hasSlug && hasTemplates) {
|
||||||
|
const portalPath = aff.institution.isUniversity ? '/edu/' : '/org/'
|
||||||
|
const portalTemplateURL = Settings.siteUrl + portalPath + aff.portal?.slug
|
||||||
|
|
||||||
|
portalTemplates.push({
|
||||||
|
name: aff.institution.name,
|
||||||
|
url: portalTemplateURL,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return portalTemplates
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("express").Request} req
|
* @param {import("express").Request} req
|
||||||
|
@ -158,6 +183,8 @@ async function projectListReactPage(req, res, next) {
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const portalTemplates = _buildPortalTemplatesList(userAffiliations)
|
||||||
|
|
||||||
const { allInReconfirmNotificationPeriods } = userEmailsData
|
const { allInReconfirmNotificationPeriods } = userEmailsData
|
||||||
|
|
||||||
const notifications =
|
const notifications =
|
||||||
|
@ -258,6 +285,7 @@ async function projectListReactPage(req, res, next) {
|
||||||
allInReconfirmNotificationPeriods,
|
allInReconfirmNotificationPeriods,
|
||||||
survey,
|
survey,
|
||||||
tags,
|
tags,
|
||||||
|
portalTemplates,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ block append meta
|
||||||
meta(name="ol-reconfirmedViaSAML" content=reconfirmedViaSAML)
|
meta(name="ol-reconfirmedViaSAML" content=reconfirmedViaSAML)
|
||||||
meta(name="ol-survey" data-type="json" content=survey)
|
meta(name="ol-survey" data-type="json" content=survey)
|
||||||
meta(name="ol-tags" data-type="json" content=tags)
|
meta(name="ol-tags" data-type="json" content=tags)
|
||||||
|
meta(name="ol-portalTemplates" data-type="json" content=portalTemplates)
|
||||||
|
|
||||||
block content
|
block content
|
||||||
main.content.content-alt.project-list-react#project-list-root
|
main.content.content-alt.project-list-react#project-list-root
|
||||||
|
|
|
@ -292,6 +292,7 @@
|
||||||
"importing_and_merging_changes_in_github": "",
|
"importing_and_merging_changes_in_github": "",
|
||||||
"in_order_to_match_institutional_metadata_2": "",
|
"in_order_to_match_institutional_metadata_2": "",
|
||||||
"in_order_to_match_institutional_metadata_associated": "",
|
"in_order_to_match_institutional_metadata_associated": "",
|
||||||
|
"institution": "",
|
||||||
"institution_acct_successfully_linked_2": "",
|
"institution_acct_successfully_linked_2": "",
|
||||||
"institution_and_role": "",
|
"institution_and_role": "",
|
||||||
"institutional_leavers_survey_notification": "",
|
"institutional_leavers_survey_notification": "",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { useState } from 'react'
|
||||||
import { Dropdown, MenuItem } from 'react-bootstrap'
|
import { Dropdown, MenuItem } from 'react-bootstrap'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { ExposedSettings } from '../../../../../types/exposed-settings'
|
import { ExposedSettings } from '../../../../../types/exposed-settings'
|
||||||
|
import type { PortalTemplate } from '../../../../../types/portal-template'
|
||||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||||
import getMeta from '../../../utils/meta'
|
import getMeta from '../../../utils/meta'
|
||||||
import NewProjectButtonModal, {
|
import NewProjectButtonModal, {
|
||||||
|
@ -24,6 +25,7 @@ function NewProjectButton({
|
||||||
const { templateLinks } = getMeta('ol-ExposedSettings') as ExposedSettings
|
const { templateLinks } = getMeta('ol-ExposedSettings') as ExposedSettings
|
||||||
const [modal, setModal] =
|
const [modal, setModal] =
|
||||||
useState<Nullable<NewProjectButtonModalVariant>>(null)
|
useState<Nullable<NewProjectButtonModalVariant>>(null)
|
||||||
|
const portalTemplates = getMeta('ol-portalTemplates') as PortalTemplate[]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -48,6 +50,22 @@ function NewProjectButton({
|
||||||
<MenuItem onClick={() => setModal('import_from_github')}>
|
<MenuItem onClick={() => setModal('import_from_github')}>
|
||||||
{t('import_from_github')}
|
{t('import_from_github')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
{portalTemplates?.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<MenuItem divider />
|
||||||
|
<MenuItem header>
|
||||||
|
{`${t('institution')} ${t('templates')}`}
|
||||||
|
</MenuItem>
|
||||||
|
{portalTemplates.map((portalTemplate, index) => (
|
||||||
|
<MenuItem
|
||||||
|
key={`portal-template-${index}`}
|
||||||
|
href={`${portalTemplate.url}#templates`}
|
||||||
|
>
|
||||||
|
{portalTemplate.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
<MenuItem divider />
|
<MenuItem divider />
|
||||||
<MenuItem header>{t('templates')}</MenuItem>
|
<MenuItem header>{t('templates')}</MenuItem>
|
||||||
{templateLinks.map((templateLink, index) => (
|
{templateLinks.map((templateLink, index) => (
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { expect } from 'chai'
|
||||||
import NewProjectButton from '../../../../../frontend/js/features/project-list/components/new-project-button'
|
import NewProjectButton from '../../../../../frontend/js/features/project-list/components/new-project-button'
|
||||||
|
|
||||||
describe('<NewProjectButton />', function () {
|
describe('<NewProjectButton />', function () {
|
||||||
|
describe('for every user (affiliated and non-affiliated)', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
window.metaAttributesCache.set('ol-ExposedSettings', {
|
window.metaAttributesCache.set('ol-ExposedSettings', {
|
||||||
templateLinks: [
|
templateLinks: [
|
||||||
|
@ -29,7 +30,7 @@ describe('<NewProjectButton />', function () {
|
||||||
window.metaAttributesCache = new Map()
|
window.metaAttributesCache = new Map()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('opens a dropdown', function () {
|
it('shows the correct dropdown menu', function () {
|
||||||
// static menu
|
// static menu
|
||||||
screen.getByText('Blank Project')
|
screen.getByText('Blank Project')
|
||||||
screen.getByText('Example Project')
|
screen.getByText('Example Project')
|
||||||
|
@ -69,4 +70,66 @@ describe('<NewProjectButton />', function () {
|
||||||
|
|
||||||
expect(screen.queryByRole('dialog')).to.be.null
|
expect(screen.queryByRole('dialog')).to.be.null
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('for affiliated user with custom templates', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
window.metaAttributesCache.set('ol-ExposedSettings', {
|
||||||
|
templateLinks: [
|
||||||
|
{
|
||||||
|
name: 'Academic Journal',
|
||||||
|
url: '/gallery/tagged/academic-journal',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'View All',
|
||||||
|
url: '/latex/templates',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
window.metaAttributesCache.set('ol-portalTemplates', [
|
||||||
|
{
|
||||||
|
name: 'Affiliation 1',
|
||||||
|
url: '/edu/test-new-template',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
window.metaAttributesCache = new Map()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the correct dropdown menu', function () {
|
||||||
|
render(<NewProjectButton id="test" />)
|
||||||
|
|
||||||
|
const newProjectButton = screen.getByRole('button', {
|
||||||
|
name: 'New Project',
|
||||||
|
})
|
||||||
|
|
||||||
|
fireEvent.click(newProjectButton)
|
||||||
|
// static menu
|
||||||
|
screen.getByText('Blank Project')
|
||||||
|
screen.getByText('Example Project')
|
||||||
|
screen.getByText('Upload Project')
|
||||||
|
screen.getByText('Import from GitHub')
|
||||||
|
|
||||||
|
// static text for institution templates
|
||||||
|
screen.getByText('Institution Templates')
|
||||||
|
|
||||||
|
// dynamic menu based on portalTemplates
|
||||||
|
const affiliationTemplate = screen.getByRole('menuitem', {
|
||||||
|
name: 'Affiliation 1',
|
||||||
|
})
|
||||||
|
expect(affiliationTemplate.getAttribute('href')).to.equal(
|
||||||
|
'/edu/test-new-template#templates'
|
||||||
|
)
|
||||||
|
|
||||||
|
// static text
|
||||||
|
screen.getByText('Templates')
|
||||||
|
|
||||||
|
// dynamic menu based on templateLinks
|
||||||
|
screen.getByText('Academic Journal')
|
||||||
|
screen.getByText('View All')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
4
services/web/types/portal-template.ts
Normal file
4
services/web/types/portal-template.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export type PortalTemplate = {
|
||||||
|
name: string
|
||||||
|
url: string
|
||||||
|
}
|
Loading…
Reference in a new issue