mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
[web] Share modal shows downgraded editors (#20015)
* add hasBeenDowngraded prop for EditMember * reduce padding on share modal collab row, add prompt to hasBeenDowngraded Select * share modal select styling tweaks to allow for inline warning icon * always show editor limit subtitle when in downgraded state * add AccessLevelsChanged warning, tweak owner row styling * conditionally set hasBeenDowngraded prop. make invited member row styling more consistent between warning/enforcement * add an info state for access level changed notification * add notification for lost edit access on collaborator share modal, TSify SendInvitesNotice * fix member privilege alignment in collaborator share modal * show blue upgrade CTA when some pending editors have been resolved * automatically show share modal to owners when has pending editors or is over collab limit * only show lost edit access warning in read-only share modal to pending editors --------- Co-authored-by: Thomas <thomas-@users.noreply.github.com> GitOrigin-RevId: e3b88052a48b8f598299ffc55b7c24cb793da151
This commit is contained in:
parent
3e06c0086e
commit
2dcf87e3f6
14 changed files with 280 additions and 61 deletions
|
@ -29,6 +29,7 @@
|
|||
"accepted_invite": "",
|
||||
"accepting_invite_as": "",
|
||||
"access_denied": "",
|
||||
"access_levels_changed": "",
|
||||
"account_has_been_link_to_institution_account": "",
|
||||
"account_has_past_due_invoice_change_plan_warning": "",
|
||||
"account_managed_by_group_administrator": "",
|
||||
|
@ -745,8 +746,10 @@
|
|||
"library": "",
|
||||
"license_for_educational_purposes": "",
|
||||
"limited_offer": "",
|
||||
"limited_to_n_editors": "",
|
||||
"limited_to_n_editors_per_project": "",
|
||||
"limited_to_n_editors_per_project_plural": "",
|
||||
"limited_to_n_editors_plural": "",
|
||||
"line": "",
|
||||
"line_height": "",
|
||||
"line_width_is_the_width_of_the_line_in_the_current_environment": "",
|
||||
|
@ -985,6 +988,7 @@
|
|||
"percent_is_the_percentage_of_the_line_width": "",
|
||||
"plan": "",
|
||||
"plan_tooltip": "",
|
||||
"please_ask_the_project_owner_to_upgrade_more_editors": "",
|
||||
"please_ask_the_project_owner_to_upgrade_to_track_changes": "",
|
||||
"please_change_primary_to_remove": "",
|
||||
"please_check_your_inbox": "",
|
||||
|
@ -1228,6 +1232,8 @@
|
|||
"select_a_project": "",
|
||||
"select_a_project_figure_modal": "",
|
||||
"select_a_row_or_a_column_to_delete": "",
|
||||
"select_access_level": "",
|
||||
"select_access_levels": "",
|
||||
"select_all": "",
|
||||
"select_all_projects": "",
|
||||
"select_an_output_file": "",
|
||||
|
@ -1451,7 +1457,9 @@
|
|||
"this_field_is_required": "",
|
||||
"this_grants_access_to_features_2": "",
|
||||
"this_is_a_labs_experiment": "",
|
||||
"this_project_already_has_maximum_editors": "",
|
||||
"this_project_exceeded_compile_timeout_limit_on_free_plan": "",
|
||||
"this_project_exceeded_editor_limit": "",
|
||||
"this_project_has_more_than_max_collabs": "",
|
||||
"this_project_is_public": "",
|
||||
"this_project_is_public_read_only": "",
|
||||
|
@ -1652,6 +1660,7 @@
|
|||
"view_metrics_commons_subtext": "",
|
||||
"view_metrics_group_subtext": "",
|
||||
"view_more": "",
|
||||
"view_only_downgraded": "",
|
||||
"view_options": "",
|
||||
"view_pdf": "",
|
||||
"view_your_invoices": "",
|
||||
|
@ -1722,6 +1731,8 @@
|
|||
"you_can_only_add_n_people_to_edit_a_project": "",
|
||||
"you_can_only_add_n_people_to_edit_a_project_plural": "",
|
||||
"you_can_request_a_maximum_of_limit_fixes_per_day": "",
|
||||
"you_can_select_or_invite": "",
|
||||
"you_can_select_or_invite_plural": "",
|
||||
"you_cant_add_or_change_password_due_to_sso": "",
|
||||
"you_cant_join_this_group_subscription": "",
|
||||
"you_dont_have_any_repositories": "",
|
||||
|
@ -1756,6 +1767,7 @@
|
|||
"your_plan_is_limited_to_n_editors": "",
|
||||
"your_plan_is_limited_to_n_editors_plural": "",
|
||||
"your_project_exceeded_compile_timeout_limit_on_free_plan": "",
|
||||
"your_project_exceeded_editor_limit": "",
|
||||
"your_project_near_compile_timeout_limit": "",
|
||||
"your_projects": "",
|
||||
"your_role": "",
|
||||
|
@ -1769,6 +1781,7 @@
|
|||
"youre_joining": "",
|
||||
"youre_on_free_trial_which_ends_on": "",
|
||||
"youre_signed_in_as_logout": "",
|
||||
"youve_lost_edit_access": "",
|
||||
"youve_unlinked_all_users": "",
|
||||
"zoom_in": "",
|
||||
"zoom_out": "",
|
||||
|
|
|
@ -54,7 +54,7 @@ export default function EditMember({ member }) {
|
|||
|
||||
return (
|
||||
<Form horizontal id="share-project-form" onSubmit={handleSubmit}>
|
||||
<FormGroup className="project-member">
|
||||
<FormGroup className="project-member row">
|
||||
<Col xs={7}>
|
||||
<FormControl.Static>{member.email}</FormControl.Static>
|
||||
</Col>
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import Notification from '@/shared/components/notification'
|
||||
import { upgradePlan } from '../../../../main/account-upgrade'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
|
||||
|
||||
type AccessLevelsChangedProps = {
|
||||
somePendingEditorsResolved: boolean
|
||||
}
|
||||
export default function AccessLevelsChanged({
|
||||
somePendingEditorsResolved,
|
||||
}: AccessLevelsChangedProps) {
|
||||
const { t } = useTranslation()
|
||||
const { features } = useProjectContext()
|
||||
const user = useUserContext()
|
||||
|
||||
return (
|
||||
<div className="add-collaborators-upgrade">
|
||||
<Notification
|
||||
isActionBelowContent
|
||||
type={somePendingEditorsResolved ? 'info' : 'warning'}
|
||||
title={
|
||||
somePendingEditorsResolved
|
||||
? t('select_access_levels')
|
||||
: t('access_levels_changed')
|
||||
}
|
||||
content={
|
||||
somePendingEditorsResolved ? (
|
||||
<p>{t('your_project_exceeded_editor_limit')}</p>
|
||||
) : (
|
||||
<p>
|
||||
{t('this_project_exceeded_editor_limit')}{' '}
|
||||
{t('you_can_select_or_invite', {
|
||||
count: features.collaborators,
|
||||
})}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
action={
|
||||
<div className="upgrade-actions">
|
||||
{user.allowedFreeTrial ? (
|
||||
<StartFreeTrialButton
|
||||
buttonProps={{ variant: 'secondary', size: 'small' }}
|
||||
source="project-sharing"
|
||||
variant="exceeds"
|
||||
>
|
||||
{t('upgrade')}
|
||||
</StartFreeTrialButton>
|
||||
) : (
|
||||
<Button
|
||||
bsSize="sm"
|
||||
className="btn-secondary"
|
||||
onClick={() => {
|
||||
upgradePlan('project-sharing')
|
||||
}}
|
||||
>
|
||||
{t('upgrade')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
href="https://www.overleaf.com/blog/changes-to-project-sharing"
|
||||
bsSize="sm"
|
||||
className="btn-link"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={() => {
|
||||
sendMB('paywall-info-click', {
|
||||
'paywall-type': 'project-sharing',
|
||||
content: 'blog',
|
||||
variant: 'exceeds',
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('read_more')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -13,11 +13,12 @@ import type { ProjectContextMember } from '@/shared/context/types/project-contex
|
|||
import { PermissionsLevel } from '@/features/ide-react/types/permissions'
|
||||
import { linkSharingEnforcementDate } from '../../utils/link-sharing'
|
||||
|
||||
type PermissionsOption = PermissionsLevel | 'removeAccess'
|
||||
type PermissionsOption = PermissionsLevel | 'removeAccess' | 'downgraded'
|
||||
|
||||
type EditMemberProps = {
|
||||
member: ProjectContextMember
|
||||
hasExceededCollaboratorLimit: boolean
|
||||
hasBeenDowngraded: boolean
|
||||
canAddCollaborators: boolean
|
||||
}
|
||||
|
||||
|
@ -29,6 +30,7 @@ type Privilege = {
|
|||
export default function EditMember({
|
||||
member,
|
||||
hasExceededCollaboratorLimit,
|
||||
hasBeenDowngraded,
|
||||
canAddCollaborators,
|
||||
}: EditMemberProps) {
|
||||
const [privileges, setPrivileges] = useState<PermissionsOption>(
|
||||
|
@ -133,7 +135,7 @@ export default function EditMember({
|
|||
<div className="email-warning">
|
||||
{member.email}
|
||||
{member.pendingEditor && (
|
||||
<div className="subtitle">Pending editor</div>
|
||||
<div className="subtitle">{t('view_only_downgraded')}</div>
|
||||
)}
|
||||
{shouldWarnMember() && (
|
||||
<div className="subtitle">
|
||||
|
@ -146,7 +148,7 @@ export default function EditMember({
|
|||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={2}>
|
||||
<Col xs={1}>
|
||||
{privileges !== member.privileges && privilegeChangePending && (
|
||||
<ChangePrivilegesActions
|
||||
handleReset={() => setPrivileges(member.privileges)}
|
||||
|
@ -154,7 +156,9 @@ export default function EditMember({
|
|||
)}
|
||||
</Col>
|
||||
|
||||
<Col xs={3}>
|
||||
<Col xs={4} className="project-member-select">
|
||||
{hasBeenDowngraded && <Icon type="warning" fw />}
|
||||
|
||||
<SelectPrivilege
|
||||
value={privileges}
|
||||
handleChange={value => {
|
||||
|
@ -162,6 +166,7 @@ export default function EditMember({
|
|||
handlePrivilegeChange(value.key)
|
||||
}
|
||||
}}
|
||||
hasBeenDowngraded={hasBeenDowngraded}
|
||||
canAddCollaborators={canAddCollaborators}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -182,12 +187,14 @@ EditMember.propTypes = {
|
|||
type SelectPrivilegeProps = {
|
||||
value: string
|
||||
handleChange: (item: Privilege | null | undefined) => void
|
||||
hasBeenDowngraded: boolean
|
||||
canAddCollaborators: boolean
|
||||
}
|
||||
|
||||
function SelectPrivilege({
|
||||
value,
|
||||
handleChange,
|
||||
hasBeenDowngraded,
|
||||
canAddCollaborators,
|
||||
}: SelectPrivilegeProps) {
|
||||
const { t } = useTranslation()
|
||||
|
@ -203,25 +210,50 @@ function SelectPrivilege({
|
|||
[t]
|
||||
)
|
||||
|
||||
const downgradedPseudoPrivilege: Privilege = {
|
||||
key: 'downgraded',
|
||||
label: t('select_access_level'),
|
||||
}
|
||||
|
||||
function getPrivilegeSubtitle(privilege: PermissionsOption) {
|
||||
return !canAddCollaborators &&
|
||||
privilege === 'readAndWrite' &&
|
||||
value !== 'readAndWrite'
|
||||
? t('limited_to_n_editors_per_project', { count: features.collaborators })
|
||||
if (!hasBeenDowngraded) {
|
||||
return !canAddCollaborators &&
|
||||
privilege === 'readAndWrite' &&
|
||||
value !== 'readAndWrite'
|
||||
? t('limited_to_n_editors_per_project', {
|
||||
count: features.collaborators,
|
||||
})
|
||||
: ''
|
||||
}
|
||||
|
||||
return privilege === 'readAndWrite'
|
||||
? t('limited_to_n_editors', {
|
||||
count: features.collaborators,
|
||||
})
|
||||
: ''
|
||||
}
|
||||
|
||||
function isPrivilegeDisabled(privilege: PermissionsOption) {
|
||||
return (
|
||||
!canAddCollaborators &&
|
||||
privilege === 'readAndWrite' &&
|
||||
(hasBeenDowngraded || value !== 'readAndWrite')
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
items={privileges}
|
||||
itemToKey={item => item.key}
|
||||
itemToString={item => (item ? item.label : '')}
|
||||
itemToSubtitle={item => (item ? getPrivilegeSubtitle(item.key) : '')}
|
||||
itemToDisabled={item =>
|
||||
item ? getPrivilegeSubtitle(item.key) !== '' : false
|
||||
}
|
||||
itemToDisabled={item => (item ? isPrivilegeDisabled(item.key) : false)}
|
||||
defaultItem={privileges.find(item => item.key === value)}
|
||||
selected={privileges.find(item => item.key === value)}
|
||||
selected={
|
||||
hasBeenDowngraded
|
||||
? downgradedPseudoPrivilege
|
||||
: privileges.find(item => item.key === value)
|
||||
}
|
||||
name="privileges"
|
||||
onSelectedItemChanged={handleChange}
|
||||
selectedIcon
|
||||
|
|
|
@ -9,13 +9,13 @@ export default function OwnerInfo() {
|
|||
|
||||
return (
|
||||
<Row className="project-member">
|
||||
<Col xs={9}>
|
||||
<Col xs={8}>
|
||||
<div className="project-member-email-icon">
|
||||
<Icon type="user" fw />
|
||||
<div className="email-warning">{owner?.email}</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={3} className="text-left">
|
||||
<Col xs={4} className="text-right">
|
||||
{t('owner')}
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
import { Col, Row } from 'react-bootstrap'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
|
||||
export default function SendInvitesNotice() {
|
||||
const { publicAccessLevel } = useProjectContext()
|
||||
|
||||
return (
|
||||
<Row className="public-access-level public-access-level--notice">
|
||||
<Col xs={12} className="text-center">
|
||||
<AccessLevel level={publicAccessLevel} />
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
function AccessLevel({ level }) {
|
||||
const { t } = useTranslation()
|
||||
switch (level) {
|
||||
case 'private':
|
||||
return t('to_add_more_collaborators')
|
||||
|
||||
case 'tokenBased':
|
||||
return t('to_change_access_permissions')
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
AccessLevel.propTypes = {
|
||||
level: PropTypes.string,
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import { Col, Row } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import Notification from '@/shared/components/notification'
|
||||
import { PublicAccessLevel } from '../../../../../../types/public-access-level'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
|
||||
export default function SendInvitesNotice() {
|
||||
const { publicAccessLevel } = useProjectContext()
|
||||
const { isPendingEditor } = useEditorContext()
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isPendingEditor && (
|
||||
<Notification
|
||||
isActionBelowContent
|
||||
type="info"
|
||||
title={t('youve_lost_edit_access')}
|
||||
content={
|
||||
<div>
|
||||
<p>{t('this_project_already_has_maximum_editors')}</p>
|
||||
<p>{t('please_ask_the_project_owner_to_upgrade_more_editors')}</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Row className="public-access-level public-access-level--notice">
|
||||
<Col xs={12} className="text-center">
|
||||
<AccessLevel level={publicAccessLevel} />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type AccessLevelProps = {
|
||||
level: PublicAccessLevel | undefined
|
||||
}
|
||||
|
||||
function AccessLevel({ level }: AccessLevelProps) {
|
||||
const { t } = useTranslation()
|
||||
switch (level) {
|
||||
case 'private':
|
||||
return <span>{t('to_add_more_collaborators')}</span>
|
||||
|
||||
case 'tokenBased':
|
||||
return <span>{t('to_change_access_permissions')}</span>
|
||||
|
||||
default:
|
||||
return <span>''</span>
|
||||
}
|
||||
}
|
|
@ -2,18 +2,30 @@ import { Row } from 'react-bootstrap'
|
|||
import AddCollaborators from './add-collaborators'
|
||||
import AddCollaboratorsUpgrade from './add-collaborators-upgrade'
|
||||
import CollaboratorsLimitUpgrade from './collaborators-limit-upgrade'
|
||||
import AccessLevelsChanged from './access-levels-changed'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default function SendInvites({
|
||||
canAddCollaborators,
|
||||
hasExceededCollaboratorLimit,
|
||||
haveAnyEditorsBeenDowngraded,
|
||||
somePendingEditorsResolved,
|
||||
}) {
|
||||
return (
|
||||
<Row className="invite-controls">
|
||||
{hasExceededCollaboratorLimit && <AddCollaboratorsUpgrade />}
|
||||
{!canAddCollaborators && !hasExceededCollaboratorLimit && (
|
||||
<CollaboratorsLimitUpgrade />
|
||||
{hasExceededCollaboratorLimit && !haveAnyEditorsBeenDowngraded && (
|
||||
<AddCollaboratorsUpgrade />
|
||||
)}
|
||||
|
||||
{haveAnyEditorsBeenDowngraded && (
|
||||
<AccessLevelsChanged
|
||||
somePendingEditorsResolved={somePendingEditorsResolved}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!canAddCollaborators &&
|
||||
!hasExceededCollaboratorLimit &&
|
||||
!haveAnyEditorsBeenDowngraded && <CollaboratorsLimitUpgrade />}
|
||||
<AddCollaborators readOnly={!canAddCollaborators} />
|
||||
</Row>
|
||||
)
|
||||
|
@ -22,4 +34,6 @@ export default function SendInvites({
|
|||
SendInvites.propTypes = {
|
||||
canAddCollaborators: PropTypes.bool,
|
||||
hasExceededCollaboratorLimit: PropTypes.bool,
|
||||
haveAnyEditorsBeenDowngraded: PropTypes.bool,
|
||||
somePendingEditorsResolved: PropTypes.bool,
|
||||
}
|
||||
|
|
|
@ -37,6 +37,26 @@ export default function ShareModalBody() {
|
|||
)
|
||||
}, [members, invites, features, isProjectOwner])
|
||||
|
||||
// determine if some but not all pending editors' permissions have been resolved,
|
||||
// for moving between warning and info notification states etc.
|
||||
const somePendingEditorsResolved = useMemo(() => {
|
||||
return (
|
||||
members.some(member => member.privileges === 'readAndWrite') &&
|
||||
members.some(member => member.pendingEditor)
|
||||
)
|
||||
}, [members])
|
||||
|
||||
const haveAnyEditorsBeenDowngraded = useMemo(() => {
|
||||
if (!isProjectOwner || !features) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (features.collaborators === -1) {
|
||||
return false
|
||||
}
|
||||
return members.some(member => member.pendingEditor)
|
||||
}, [features, isProjectOwner, members])
|
||||
|
||||
const hasExceededCollaboratorLimit = useMemo(() => {
|
||||
if (!isProjectOwner || !features) {
|
||||
return false
|
||||
|
@ -58,6 +78,8 @@ export default function ShareModalBody() {
|
|||
<SendInvites
|
||||
canAddCollaborators={canAddCollaborators}
|
||||
hasExceededCollaboratorLimit={hasExceededCollaboratorLimit}
|
||||
haveAnyEditorsBeenDowngraded={haveAnyEditorsBeenDowngraded}
|
||||
somePendingEditorsResolved={somePendingEditorsResolved}
|
||||
/>
|
||||
) : (
|
||||
<SendInvitesNotice />
|
||||
|
@ -72,6 +94,7 @@ export default function ShareModalBody() {
|
|||
key={member._id}
|
||||
member={member}
|
||||
hasExceededCollaboratorLimit={hasExceededCollaboratorLimit}
|
||||
hasBeenDowngraded={member.pendingEditor ?? false}
|
||||
canAddCollaborators={canAddCollaborators}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
@ -68,7 +68,7 @@ const ShareProjectModal = React.memo(function ShareProjectModal({
|
|||
|
||||
// split test: link-sharing-warning
|
||||
// show the new share modal if project owner
|
||||
// is over collaborator limit (once every 24 hours)
|
||||
// is over collaborator limit or has pending editors (once every 24 hours)
|
||||
useEffect(() => {
|
||||
const hasExceededCollaboratorLimit = () => {
|
||||
if (!isProjectOwner || !project.features) {
|
||||
|
@ -80,7 +80,8 @@ const ShareProjectModal = React.memo(function ShareProjectModal({
|
|||
}
|
||||
return (
|
||||
project.members.filter(member => member.privileges === 'readAndWrite')
|
||||
.length > (project.features.collaborators ?? 1)
|
||||
.length > (project.features.collaborators ?? 1) ||
|
||||
project.members.some(member => member.pendingEditor)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@ import Icon from '@/shared/components/icon'
|
|||
export default function ViewMember({ member }) {
|
||||
return (
|
||||
<Row className="project-member">
|
||||
<Col xs={9}>
|
||||
<Col xs={8}>
|
||||
<div className="project-member-email-icon">
|
||||
<Icon type="user" fw />
|
||||
<div className="email-warning">{member.email}</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={3} className="text-left">
|
||||
<Col xs={4} className="text-right">
|
||||
<MemberPrivileges privileges={member.privileges} />
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
@ -101,12 +101,7 @@ export const EditorProvider: FC = ({ children }) => {
|
|||
|
||||
const isPendingEditor = useMemo(
|
||||
() =>
|
||||
members?.some(
|
||||
member =>
|
||||
member._id === userId &&
|
||||
member.pendingEditor &&
|
||||
member.privileges === 'readAndWrite'
|
||||
),
|
||||
members?.some(member => member._id === userId && member.pendingEditor),
|
||||
[members, userId]
|
||||
)
|
||||
|
||||
|
|
|
@ -191,4 +191,28 @@
|
|||
.invite-warning {
|
||||
margin-bottom: @line-height-computed / 2;
|
||||
}
|
||||
|
||||
.project-member-select {
|
||||
padding: 0;
|
||||
.fa-warning {
|
||||
color: @brand-warning;
|
||||
}
|
||||
}
|
||||
|
||||
.project-member.form-group,
|
||||
.project-member.row {
|
||||
margin: 0 -30px;
|
||||
}
|
||||
.project-member .select-wrapper {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
.select-trigger {
|
||||
gap: @spacing-02;
|
||||
}
|
||||
}
|
||||
.project-member.row {
|
||||
padding-right: 1.28571429em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
"accepted_invite": "Accepted invite",
|
||||
"accepting_invite_as": "You are accepting this invite as",
|
||||
"access_denied": "Access Denied",
|
||||
"access_levels_changed": "Access levels changed",
|
||||
"account": "Account",
|
||||
"account_has_been_link_to_institution_account": "Your __appName__ account on <b>__email__</b> has been linked to your <b>__institutionName__</b> institutional account.",
|
||||
"account_has_past_due_invoice_change_plan_warning": "Your account currently has a past due invoice. You will not be able to change your plan until this is resolved.",
|
||||
|
@ -1085,8 +1086,10 @@
|
|||
"license": "License",
|
||||
"license_for_educational_purposes": "This license is for educational purposes (applies to students or faculty using __appName__ for teaching)",
|
||||
"limited_offer": "Limited offer",
|
||||
"limited_to_n_editors": "Limited to __count__ editor",
|
||||
"limited_to_n_editors_per_project": "Limited to __count__ editor per project",
|
||||
"limited_to_n_editors_per_project_plural": "Limited to __count__ editors per project",
|
||||
"limited_to_n_editors_plural": "Limited to __count__ editors",
|
||||
"line_height": "Line Height",
|
||||
"line_width_is_the_width_of_the_line_in_the_current_environment": "Line width is the width of the line in the current environment. e.g. a full page width in single-column layout or half a page width in a two-column layout.",
|
||||
"link": "Link",
|
||||
|
@ -1455,6 +1458,7 @@
|
|||
"plans_amper_pricing": "Plans & Pricing",
|
||||
"plans_and_pricing": "Plans and Pricing",
|
||||
"plans_and_pricing_lowercase": "plans and pricing",
|
||||
"please_ask_the_project_owner_to_upgrade_more_editors": "Please ask the project owner to upgrade their plan to allow more editors.",
|
||||
"please_ask_the_project_owner_to_upgrade_to_track_changes": "Please ask the project owner to upgrade to use track changes",
|
||||
"please_change_primary_to_remove": "Please change your primary email in order to remove",
|
||||
"please_check_your_inbox": "Please check your inbox",
|
||||
|
@ -1771,6 +1775,8 @@
|
|||
"select_a_project": "Select a Project",
|
||||
"select_a_project_figure_modal": "Select a project",
|
||||
"select_a_row_or_a_column_to_delete": "Select a row or a column to delete",
|
||||
"select_access_level": "Select access level",
|
||||
"select_access_levels": "Select access levels",
|
||||
"select_all": "Select all",
|
||||
"select_all_projects": "Select all projects",
|
||||
"select_an_output_file": "Select an Output File",
|
||||
|
@ -2064,7 +2070,9 @@
|
|||
"this_grants_access_to_features_2": "This grants you access to <0>__appName__</0> <0>__featureType__</0> features.",
|
||||
"this_is_a_labs_experiment": "This is a Labs experiment",
|
||||
"this_is_your_template": "This is your template from your project",
|
||||
"this_project_already_has_maximum_editors": "This project already has the maximum number of editors permitted on the owner’s plan. This means you can view but not edit the project.",
|
||||
"this_project_exceeded_compile_timeout_limit_on_free_plan": "This project exceeded the compile timeout limit on our free plan.",
|
||||
"this_project_exceeded_editor_limit": "This project exceeded the editor limit for your plan. All collaborators now have view-only access.",
|
||||
"this_project_has_more_than_max_collabs": "This project has more than the maximum number of collaborators allowed on the project owner’s Overleaf plan. This means you could lose edit access from __linkSharingDate__.",
|
||||
"this_project_is_public": "This project is public and can be edited by anyone with the URL.",
|
||||
"this_project_is_public_read_only": "This project is public and can be viewed but not edited by anyone with the URL",
|
||||
|
@ -2300,6 +2308,7 @@
|
|||
"view_metrics_commons_subtext": "Monitor and download usage metrics for your Commons subscription",
|
||||
"view_metrics_group_subtext": "Monitor and download usage metrics for your group subscription",
|
||||
"view_more": "View more",
|
||||
"view_only_downgraded": "View only. Upgrade to restore edit access.",
|
||||
"view_options": "View options",
|
||||
"view_pdf": "View PDF",
|
||||
"view_source": "View Source",
|
||||
|
@ -2391,6 +2400,8 @@
|
|||
"you_can_only_add_n_people_to_edit_a_project_plural": "You can only add __count__ people to edit a project with you on your current plan. Upgrade to add more.",
|
||||
"you_can_opt_in_and_out_of_the_program_at_any_time_on_this_page": "You can <0>opt in and out</0> of the program at any time on this page",
|
||||
"you_can_request_a_maximum_of_limit_fixes_per_day": "You can request a maximum of __limit__ fixes per day. Please try again tomorrow.",
|
||||
"you_can_select_or_invite": "You can select or invite __count__ editor on your current plan, or upgrade to get more.",
|
||||
"you_can_select_or_invite_plural": "You can select or invite __count__ editors on your current plan, or upgrade to get more.",
|
||||
"you_cant_add_or_change_password_due_to_sso": "You can’t add or change your password because your group or organization uses <0>single sign-on (SSO)</0>.",
|
||||
"you_cant_join_this_group_subscription": "You can’t join this group subscription",
|
||||
"you_cant_reset_password_due_to_sso": "You can’t reset your password because your group or organization uses SSO. <0>Log in with SSO</0>.",
|
||||
|
@ -2434,6 +2445,7 @@
|
|||
"your_plan_is_limited_to_n_editors": "Your plan allows __count__ collaborator with edit access and unlimited viewers.",
|
||||
"your_plan_is_limited_to_n_editors_plural": "Your plan allows __count__ collaborators with edit access and unlimited viewers.",
|
||||
"your_project_exceeded_compile_timeout_limit_on_free_plan": "Your project exceeded the compile timeout limit on our free plan.",
|
||||
"your_project_exceeded_editor_limit": "Your project exceeded the editor limit and access levels were changed. Select a new access level for your collaborators, or upgrade to add more editors.",
|
||||
"your_project_near_compile_timeout_limit": "Your project is near the compile timeout limit for our free plan.",
|
||||
"your_projects": "Your Projects",
|
||||
"your_questions_answered": "Your questions answered",
|
||||
|
@ -2450,6 +2462,7 @@
|
|||
"youre_on_free_trial_which_ends_on": "You’re on a free trial which ends on <0>__date__</0>.",
|
||||
"youre_signed_in_as_logout": "You’re signed in as <0>__email__</0>. <1>Log out.</1>",
|
||||
"youre_signed_up": "You’re signed up",
|
||||
"youve_lost_edit_access": "You’ve lost edit access",
|
||||
"youve_unlinked_all_users": "You’ve unlinked all users",
|
||||
"zh-CN": "Chinese",
|
||||
"zip_contents_too_large": "Zip contents too large",
|
||||
|
|
Loading…
Reference in a new issue