From 2dcf87e3f64b89c2e22dc360463ddf8607621504 Mon Sep 17 00:00:00 2001 From: roo hutton Date: Mon, 26 Aug 2024 14:03:37 +0100 Subject: [PATCH] [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 GitOrigin-RevId: e3b88052a48b8f598299ffc55b7c24cb793da151 --- .../web/frontend/extracted-translations.json | 13 +++ .../components/edit-member.jsx | 2 +- .../access-levels-changed.tsx | 84 +++++++++++++++++++ .../restricted-link-sharing/edit-member.tsx | 56 ++++++++++--- .../restricted-link-sharing/owner-info.jsx | 4 +- .../send-invites-notice.jsx | 33 -------- .../send-invites-notice.tsx | 53 ++++++++++++ .../restricted-link-sharing/send-invites.jsx | 20 ++++- .../share-modal-body.tsx | 23 +++++ .../share-project-modal.tsx | 5 +- .../restricted-link-sharing/view-member.jsx | 4 +- .../js/shared/context/editor-context.tsx | 7 +- .../stylesheets/app/editor/share.less | 24 ++++++ services/web/locales/en.json | 13 +++ 14 files changed, 280 insertions(+), 61 deletions(-) create mode 100644 services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx delete mode 100644 services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/send-invites-notice.jsx create mode 100644 services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/send-invites-notice.tsx diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 80ae1b7bda..b5bea122fb 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -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": "", diff --git a/services/web/frontend/js/features/share-project-modal/components/edit-member.jsx b/services/web/frontend/js/features/share-project-modal/components/edit-member.jsx index 354db93ba0..9505dc5d26 100644 --- a/services/web/frontend/js/features/share-project-modal/components/edit-member.jsx +++ b/services/web/frontend/js/features/share-project-modal/components/edit-member.jsx @@ -54,7 +54,7 @@ export default function EditMember({ member }) { return (
- + {member.email} diff --git a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx new file mode 100644 index 0000000000..b4d220cde3 --- /dev/null +++ b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx @@ -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 ( +
+ {t('your_project_exceeded_editor_limit')}

+ ) : ( +

+ {t('this_project_exceeded_editor_limit')}{' '} + {t('you_can_select_or_invite', { + count: features.collaborators, + })} +

+ ) + } + action={ +
+ {user.allowedFreeTrial ? ( + + {t('upgrade')} + + ) : ( + + )} + +
+ } + /> +
+ ) +} diff --git a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/edit-member.tsx b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/edit-member.tsx index 623b86b8a0..bf8042c329 100644 --- a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/edit-member.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/edit-member.tsx @@ -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( @@ -133,7 +135,7 @@ export default function EditMember({
{member.email} {member.pendingEditor && ( -
Pending editor
+
{t('view_only_downgraded')}
)} {shouldWarnMember() && (
@@ -146,7 +148,7 @@ export default function EditMember({
- + {privileges !== member.privileges && privilegeChangePending && ( setPrivileges(member.privileges)} @@ -154,7 +156,9 @@ export default function EditMember({ )} - + + {hasBeenDowngraded && } + { @@ -162,6 +166,7 @@ export default function EditMember({ handlePrivilegeChange(value.key) } }} + hasBeenDowngraded={hasBeenDowngraded} canAddCollaborators={canAddCollaborators} /> @@ -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 (