Merge pull request #10132 from overleaf/ii-dashboard-are-you-affiliated-migration

[web] Project dashboard are you affiliated migration

GitOrigin-RevId: 455ad915dd023c5fab3ce9a5165aa7e960915f1f
This commit is contained in:
ilkin-overleaf 2022-10-31 17:02:59 +02:00 committed by Copybot
parent 3d9c8f80f4
commit 7180ee8700
8 changed files with 147 additions and 0 deletions

View file

@ -293,6 +293,7 @@ async function projectListReactPage(req, res, next) {
notifications,
notificationsInstitution,
user,
userAffiliations,
userEmails,
reconfirmedViaSAML,
allInReconfirmNotificationPeriods,

View file

@ -13,6 +13,7 @@ block append meta
meta(name="ol-userEmails" data-type="json" content=userEmails)
meta(name="ol-allInReconfirmNotificationPeriods" data-type="json" content=allInReconfirmNotificationPeriods)
meta(name="ol-user" data-type="json" content=user)
meta(name="ol-userAffiliations" data-type="json" content=userAffiliations)
meta(name="ol-reconfirmedViaSAML" content=reconfirmedViaSAML)
meta(name="ol-survey" data-type="json" content=survey)
meta(name="ol-tags" data-type="json" content=tags)

View file

@ -30,6 +30,7 @@
"archived": "",
"archived_projects": "",
"archiving_projects_wont_affect_collaborators": "",
"are_you_affiliated_with_an_institution": "",
"are_you_still_at": "",
"ascending": "",
"ask_proj_owner_to_upgrade_for_git_bridge": "",

View file

@ -10,6 +10,7 @@ import CurrentPlanWidget from './current-plan-widget/current-plan-widget'
import NewProjectButton from './new-project-button'
import ProjectListTable from './table/project-list-table'
import SidebarFilters from './sidebar/sidebar-filters'
import AddAffiliation, { useAddAffiliation } from './sidebar/add-affiliation'
import SurveyWidget from './survey-widget'
import WelcomeMessage from './welcome-message'
import LoadingBranded from '../../../shared/components/loading-branded'
@ -42,6 +43,7 @@ function ProjectListPageContent() {
setSearchText,
selectedProjects,
} = useProjectListContext()
const { show: showAddAffiliationWidget } = useAddAffiliation()
useEffect(() => {
eventTracking.sendMB('loads_v2_dash', {})
@ -62,6 +64,8 @@ function ProjectListPageContent() {
<aside className="project-list-sidebar-react">
<NewProjectButton id="new-project-button-sidebar" />
<SidebarFilters />
{showAddAffiliationWidget && <hr />}
<AddAffiliation />
</aside>
</div>
</div>

View file

@ -0,0 +1,34 @@
import { useTranslation } from 'react-i18next'
import { Button } from 'react-bootstrap'
import { useProjectListContext } from '../../context/project-list-context'
import getMeta from '../../../../utils/meta'
import { Affiliation } from '../../../../../../types/affiliation'
import { ExposedSettings } from '../../../../../../types/exposed-settings'
export function useAddAffiliation() {
const { totalProjectsCount } = useProjectListContext()
const { isOverleaf } = getMeta('ol-ExposedSettings') as ExposedSettings
const userAffiliations = getMeta('ol-userAffiliations', []) as Affiliation[]
return { show: isOverleaf && totalProjectsCount && !userAffiliations.length }
}
function AddAffiliation() {
const { t } = useTranslation()
const { show } = useAddAffiliation()
if (!show) {
return null
}
return (
<div className="text-centered user-profile">
<p>{t('are_you_affiliated_with_an_institution')}</p>
<Button bsStyle="info" href="/user/settings">
{t('add_affiliation')}
</Button>
</div>
)
}
export default AddAffiliation

View file

@ -0,0 +1,29 @@
import AddAffiliation from '../../js/features/project-list/components/sidebar/add-affiliation'
import { ProjectListProvider } from '../../js/features/project-list/context/project-list-context'
import useFetchMock from '../hooks/use-fetch-mock'
import { projectsData } from '../../../test/frontend/features/project-list/fixtures/projects-data'
export const Add = (args: any) => {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-ExposedSettings', {
isOverleaf: true,
})
window.metaAttributesCache.set('ol-userAffiliations', [])
useFetchMock(fetchMock => {
fetchMock.post(/\/api\/project/, {
projects: projectsData,
totalSize: projectsData.length,
})
})
return (
<ProjectListProvider>
<AddAffiliation {...args} />
</ProjectListProvider>
)
}
export default {
title: 'Project List / Affiliation',
component: AddAffiliation,
}

View file

@ -1584,6 +1584,7 @@
"link_institutional_email_get_started": "Link an institutional email address to your account to get started.",
"looks_like_youre_at": "It looks like youre at <0>__institutionName__</0>!",
"add_affiliation": "Add Affiliation",
"are_you_affiliated_with_an_institution": "Are you affiliated with an institution?",
"did_you_know_institution_providing_professional": "Did you know that __institutionName__ is providing <0>free __appName__ Professional features</0> to everyone at __institutionName__?",
"add_email_to_claim_features": "Add an institutional email address to claim your features.",
"please_change_primary_to_remove": "Please change your primary email in order to remove",

View file

@ -0,0 +1,76 @@
import { screen, waitFor } from '@testing-library/react'
import { expect } from 'chai'
import fetchMock from 'fetch-mock'
import { renderWithProjectListContext } from '../../helpers/render-with-context'
import AddAffiliation from '../../../../../../frontend/js/features/project-list/components/sidebar/add-affiliation'
import { Affiliation } from '../../../../../../types/affiliation'
describe('Add affiliation widget', function () {
const validateNonExistence = () => {
expect(screen.queryByText(/are you affiliated with an institution/i)).to.be
.null
expect(screen.queryByRole('link', { name: /add affiliation/i })).to.be.null
}
beforeEach(function () {
window.metaAttributesCache = new Map()
fetchMock.reset()
})
afterEach(function () {
window.metaAttributesCache = new Map()
fetchMock.reset()
})
it('renders the component', async function () {
window.metaAttributesCache.set('ol-ExposedSettings', { isOverleaf: true })
window.metaAttributesCache.set('ol-userAffiliations', [])
renderWithProjectListContext(<AddAffiliation />)
await fetchMock.flush(true)
await waitFor(() => expect(fetchMock.called('/api/project')))
screen.getByText(/are you affiliated with an institution/i)
const addAffiliationLink = screen.getByRole('link', {
name: /add affiliation/i,
})
expect(addAffiliationLink.getAttribute('href')).to.equal('/user/settings')
})
it('does not render when `isOverleaf` is `false`', async function () {
window.metaAttributesCache.set('ol-ExposedSettings', { isOverleaf: false })
window.metaAttributesCache.set('ol-userAffiliations', [])
renderWithProjectListContext(<AddAffiliation />)
await fetchMock.flush(true)
await waitFor(() => expect(fetchMock.called('/api/project')))
validateNonExistence()
})
it('does not render when there no projects', async function () {
window.metaAttributesCache.set('ol-ExposedSettings', { isOverleaf: true })
window.metaAttributesCache.set('ol-userAffiliations', [])
renderWithProjectListContext(<AddAffiliation />, { projects: [] })
await fetchMock.flush(true)
await waitFor(() => expect(fetchMock.called('/api/project')))
validateNonExistence()
})
it('does not render when there are affiliations', async function () {
window.metaAttributesCache.set('ol-ExposedSettings', { isOverleaf: true })
window.metaAttributesCache.set('ol-userAffiliations', [{} as Affiliation])
renderWithProjectListContext(<AddAffiliation />)
await fetchMock.flush(true)
await waitFor(() => expect(fetchMock.called('/api/project')))
validateNonExistence()
})
})