mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-12 12:46:37 +00:00
Merge pull request #15946 from overleaf/msm-new-notification-others
[web] new notification style for dashboard alerts GitOrigin-RevId: cf5bea32a89f35d63ca4bdc2df2619352d421ff3
This commit is contained in:
parent
a50ca25f3a
commit
a04b5c0418
16 changed files with 645 additions and 203 deletions
|
@ -5,6 +5,7 @@ import Notification from '../notification'
|
|||
import * as eventTracking from '../../../../../infrastructure/event-tracking'
|
||||
import { Modal, Button } from 'react-bootstrap'
|
||||
import AccessibleModal from '../../../../../shared/components/accessible-modal'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
interface VariantContents {
|
||||
default: string
|
||||
|
@ -25,6 +26,10 @@ type INRBannerProps = {
|
|||
|
||||
export default function INRBanner({ variant, splitTestName }: INRBannerProps) {
|
||||
const { t } = useTranslation()
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const [dismissedUntil, setDismissedUntil] = usePersistedState<
|
||||
Date | undefined
|
||||
>(`has_dismissed_inr_banner_until`)
|
||||
|
@ -107,9 +112,9 @@ export default function INRBanner({ variant, splitTestName }: INRBannerProps) {
|
|||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t('get_discounted_plan')}
|
||||
|
@ -133,9 +138,9 @@ export default function INRBanner({ variant, splitTestName }: INRBannerProps) {
|
|||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="success"
|
||||
bsStyle={newNotificationStyle ? null : 'success'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t('get_discounted_plan')} ₹
|
||||
|
|
|
@ -16,6 +16,10 @@ const LATAM_CURRENCIES = {
|
|||
|
||||
export default function LATAMBanner() {
|
||||
const { t } = useTranslation()
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const [dismissedAt, setDismissedAt] = usePersistedState<Date | undefined>(
|
||||
`has_dismissed_latam_banner`
|
||||
)
|
||||
|
@ -97,9 +101,9 @@ export default function LATAMBanner() {
|
|||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{t('get_discounted_plan')}
|
||||
|
|
|
@ -20,6 +20,10 @@ export default function GroupsAndEnterpriseBanner() {
|
|||
)
|
||||
const groupsAndEnterpriseBannerVariant: GroupsAndEnterpriseBannerVariant =
|
||||
getMeta('ol-groupsAndEnterpriseBannerVariant')
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
|
||||
const hasDismissedGroupsAndEnterpriseBanner = hasRecentlyDismissedBanner()
|
||||
|
||||
|
@ -68,7 +72,11 @@ export default function GroupsAndEnterpriseBanner() {
|
|||
body={<BannerContent variant={groupsAndEnterpriseBannerVariant} />}
|
||||
action={
|
||||
<a
|
||||
className="pull-right btn btn-info btn-sm"
|
||||
className={
|
||||
newNotificationStyle
|
||||
? 'btn btn-secondary btn-sm'
|
||||
: 'pull-right btn btn-info btn-sm'
|
||||
}
|
||||
href={contactSalesUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
|
|
|
@ -25,6 +25,10 @@ function ReconfirmAffiliation({
|
|||
}: ReconfirmAffiliationProps) {
|
||||
const { t } = useTranslation()
|
||||
const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const { error, isLoading, isError, isSuccess, runAsync } = useAsync()
|
||||
const [hasSent, setHasSent] = useState(false)
|
||||
const [isPending, setIsPending] = useState(false)
|
||||
|
@ -95,7 +99,7 @@ function ReconfirmAffiliation({
|
|||
|
||||
return (
|
||||
<div className="w-100">
|
||||
<Icon type="warning" />
|
||||
{!newNotificationStyle && <Icon type="warning" />}
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsSize="sm"
|
||||
|
|
|
@ -18,16 +18,15 @@ function ReconfirmationInfo() {
|
|||
<Notification
|
||||
key={`reconfirmation-period-email-${userEmail.email}`}
|
||||
bsStyle="info"
|
||||
>
|
||||
<Notification.Body>
|
||||
body={
|
||||
<div className="reconfirm-notification">
|
||||
<ReconfirmAffiliation
|
||||
email={userEmail.email}
|
||||
institution={userEmail.affiliation.institution}
|
||||
/>
|
||||
</div>
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
{userEmails.map(userEmail =>
|
||||
|
@ -37,13 +36,12 @@ function ReconfirmationInfo() {
|
|||
key={`samlIdentifier-email-${userEmail.email}`}
|
||||
bsStyle="info"
|
||||
onDismiss={() => {}}
|
||||
>
|
||||
<Notification.Body>
|
||||
body={
|
||||
<ReconfirmationInfoSuccess
|
||||
institution={userEmail.affiliation?.institution}
|
||||
/>
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -38,6 +38,10 @@ function CommonNotification({ notification }: CommonNotificationProps) {
|
|||
const { t } = useTranslation()
|
||||
const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
|
||||
const user = getMeta('ol-user', []) as Pick<User, 'features'>
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const { isLoading, isSuccess, error, runAsync } = useAsync<
|
||||
never,
|
||||
FetchError
|
||||
|
@ -90,16 +94,21 @@ function CommonNotification({ notification }: CommonNotificationProps) {
|
|||
action={
|
||||
accepted ? (
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={
|
||||
newNotificationStyle ? 'btn-secondary' : 'pull-right'
|
||||
}
|
||||
href={`/project/${notification.messageOpts.projectId}`}
|
||||
>
|
||||
{t('open_project')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
className={
|
||||
newNotificationStyle ? 'btn-secondary' : 'pull-right'
|
||||
}
|
||||
bsSize="sm"
|
||||
disabled={isLoading}
|
||||
onClick={() => handleAcceptInvite(notification)}
|
||||
|
@ -128,9 +137,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
|
|||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
|
||||
href="https://www.overleaf.com/events/wfh2020"
|
||||
>
|
||||
View
|
||||
|
@ -189,9 +198,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
|
|||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
|
||||
href={
|
||||
notification.messageOpts.ssoEnabled
|
||||
? `${samlInitPath}?university_id=${notification.messageOpts.institutionId}&auto=/project`
|
||||
|
@ -219,9 +228,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
|
|||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="danger"
|
||||
bsStyle={newNotificationStyle ? null : 'danger'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
|
||||
href="/user/settings"
|
||||
>
|
||||
Account Settings
|
||||
|
|
|
@ -103,8 +103,53 @@ function ConfirmEmailNotification({ userEmail }: { userEmail: UserEmailData }) {
|
|||
// already have premium features.
|
||||
if (emailHasLicenceAfterConfirming(userEmail) && isOnFreeOrIndividualPlan()) {
|
||||
return (
|
||||
<Notification bsStyle="info">
|
||||
<Notification.Body data-testid="notification-body">
|
||||
<Notification
|
||||
bsStyle="info"
|
||||
body={
|
||||
<div data-testid="notification-body">
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Icon type="spinner" spin /> {t('resending_confirmation_email')}
|
||||
…
|
||||
</>
|
||||
) : isError ? (
|
||||
<div aria-live="polite">{getUserFacingMessage(error)}</div>
|
||||
) : (
|
||||
<>
|
||||
<Trans
|
||||
i18nKey="one_step_away_from_professional_features"
|
||||
components={[<strong />]} // eslint-disable-line react/jsx-key
|
||||
/>
|
||||
<button
|
||||
className="pull-right btn btn-info btn-sm"
|
||||
onClick={() => handleResendConfirmationEmail(userEmail)}
|
||||
>
|
||||
{t('resend_email')}
|
||||
</button>
|
||||
<br />
|
||||
<Trans
|
||||
i18nKey="institution_has_overleaf_subscription"
|
||||
values={{
|
||||
institutionName: userEmail.affiliation?.institution.name,
|
||||
emailAddress: userEmail.email,
|
||||
}}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
components={[<strong />]} // eslint-disable-line react/jsx-key
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Notification
|
||||
bsStyle="warning"
|
||||
body={
|
||||
<div data-testid="pro-notification-body">
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Icon type="spinner" spin /> {t('resending_confirmation_email')}
|
||||
|
@ -114,60 +159,21 @@ function ConfirmEmailNotification({ userEmail }: { userEmail: UserEmailData }) {
|
|||
<div aria-live="polite">{getUserFacingMessage(error)}</div>
|
||||
) : (
|
||||
<>
|
||||
<Trans
|
||||
i18nKey="one_step_away_from_professional_features"
|
||||
components={[<strong />]} // eslint-disable-line react/jsx-key
|
||||
/>
|
||||
<button
|
||||
className="pull-right btn btn-info btn-sm"
|
||||
{t('please_confirm_email', {
|
||||
emailAddress: userEmail.email,
|
||||
})}{' '}
|
||||
<Button
|
||||
bsStyle="link"
|
||||
className="btn-inline-link"
|
||||
onClick={() => handleResendConfirmationEmail(userEmail)}
|
||||
>
|
||||
{t('resend_email')}
|
||||
</button>
|
||||
<br />
|
||||
<Trans
|
||||
i18nKey="institution_has_overleaf_subscription"
|
||||
values={{
|
||||
institutionName: userEmail.affiliation?.institution.name,
|
||||
emailAddress: userEmail.email,
|
||||
}}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
components={[<strong />]} // eslint-disable-line react/jsx-key
|
||||
/>
|
||||
({t('resend_confirmation_email')})
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Notification bsStyle="warning">
|
||||
<Notification.Body data-testid="pro-notification-body">
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Icon type="spinner" spin /> {t('resending_confirmation_email')}
|
||||
…
|
||||
</>
|
||||
) : isError ? (
|
||||
<div aria-live="polite">{getUserFacingMessage(error)}</div>
|
||||
) : (
|
||||
<>
|
||||
{t('please_confirm_email', {
|
||||
emailAddress: userEmail.email,
|
||||
})}{' '}
|
||||
<Button
|
||||
bsStyle="link"
|
||||
className="btn-inline-link"
|
||||
onClick={() => handleResendConfirmationEmail(userEmail)}
|
||||
>
|
||||
({t('resend_confirmation_email')})
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { Dispatch, SetStateAction } from 'react'
|
|||
import Notification from '../../notification'
|
||||
import { GroupInvitationStatus } from './hooks/use-group-invitation-notification'
|
||||
import type { NotificationGroupInvitation } from '../../../../../../../../types/project/dashboard/notification'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
type GroupInvitationCancelIndividualSubscriptionNotificationProps = {
|
||||
setGroupInvitationStatus: Dispatch<SetStateAction<GroupInvitationStatus>>
|
||||
|
@ -19,30 +20,41 @@ export default function GroupInvitationCancelIndividualSubscriptionNotification(
|
|||
notification,
|
||||
}: GroupInvitationCancelIndividualSubscriptionNotificationProps) {
|
||||
const { t } = useTranslation()
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const {
|
||||
messageOpts: { inviterName },
|
||||
} = notification
|
||||
|
||||
return (
|
||||
<Notification bsStyle="info" onDismiss={dismissGroupInviteNotification}>
|
||||
<Notification.Body>
|
||||
{t('invited_to_group_have_individual_subcription', { inviterName })}
|
||||
</Notification.Body>
|
||||
<Notification.Action className="group-invitation-cancel-subscription-notification-buttons">
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsSize="sm"
|
||||
className="me-1"
|
||||
onClick={() =>
|
||||
setGroupInvitationStatus(GroupInvitationStatus.AskToJoin)
|
||||
}
|
||||
>
|
||||
{t('not_now')}
|
||||
</Button>
|
||||
<Button bsStyle="info" bsSize="sm" onClick={cancelPersonalSubscription}>
|
||||
{t('cancel_my_subscription')}
|
||||
</Button>
|
||||
</Notification.Action>
|
||||
</Notification>
|
||||
<Notification
|
||||
bsStyle="info"
|
||||
onDismiss={dismissGroupInviteNotification}
|
||||
body={t('invited_to_group_have_individual_subcription', { inviterName })}
|
||||
action={
|
||||
<div className="group-invitation-cancel-subscription-notification-buttons">
|
||||
<Button
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
bsSize="sm"
|
||||
className={newNotificationStyle ? 'me-1 btn-secondary' : 'me-1'}
|
||||
onClick={() =>
|
||||
setGroupInvitationStatus(GroupInvitationStatus.AskToJoin)
|
||||
}
|
||||
>
|
||||
{t('not_now')}
|
||||
</Button>
|
||||
<Button
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
className={newNotificationStyle ? 'btn-secondary' : ''}
|
||||
bsSize="sm"
|
||||
onClick={cancelPersonalSubscription}
|
||||
>
|
||||
{t('cancel_my_subscription')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Button } from 'react-bootstrap'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import Notification from '../../notification'
|
||||
import type { NotificationGroupInvitation } from '../../../../../../../../types/project/dashboard/notification'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
type GroupInvitationNotificationProps = {
|
||||
acceptGroupInvite: () => void
|
||||
|
@ -17,26 +18,30 @@ export default function GroupInvitationNotificationJoin({
|
|||
dismissGroupInviteNotification,
|
||||
}: GroupInvitationNotificationProps) {
|
||||
const { t } = useTranslation()
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const {
|
||||
messageOpts: { inviterName },
|
||||
} = notification
|
||||
|
||||
return (
|
||||
<Notification bsStyle="info" onDismiss={dismissGroupInviteNotification}>
|
||||
<Notification.Body>
|
||||
{t('invited_to_group', { inviterName })}
|
||||
</Notification.Body>
|
||||
<Notification.Action>
|
||||
<Notification
|
||||
bsStyle="info"
|
||||
onDismiss={dismissGroupInviteNotification}
|
||||
body={t('invited_to_group', { inviterName })}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
bsSize="sm"
|
||||
className="pull-right"
|
||||
className={newNotificationStyle ? 'btn-secondary' : 'pull-right'}
|
||||
onClick={acceptGroupInvite}
|
||||
disabled={isAcceptingInvitation}
|
||||
>
|
||||
{t('join_now')}
|
||||
</Button>
|
||||
</Notification.Action>
|
||||
</Notification>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Notification from '../../notification'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Icon from '../../../../../../shared/components/icon'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
type GroupInvitationSuccessfulNotificationProps = {
|
||||
hideNotification: () => void
|
||||
|
@ -10,13 +11,23 @@ export default function GroupInvitationSuccessfulNotification({
|
|||
hideNotification,
|
||||
}: GroupInvitationSuccessfulNotificationProps) {
|
||||
const { t } = useTranslation()
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
|
||||
return (
|
||||
<Notification bsStyle="success" onDismiss={hideNotification}>
|
||||
<Notification.Body>
|
||||
<Icon type="check-circle" fw aria-hidden="true" className="me-1" />
|
||||
{t('congratulations_youve_successfully_join_group')}
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
<Notification
|
||||
bsStyle="success"
|
||||
onDismiss={hideNotification}
|
||||
body={
|
||||
<>
|
||||
{!newNotificationStyle && (
|
||||
<Icon type="check-circle" fw aria-hidden="true" className="me-1" />
|
||||
)}
|
||||
{t('congratulations_youve_successfully_join_group')}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ function Institution() {
|
|||
'ol-notificationsInstitution',
|
||||
[]
|
||||
) as InstitutionType[]
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const { handleDismiss } = useAsyncDismiss()
|
||||
|
||||
if (!notificationsInstitution.length) {
|
||||
|
@ -41,47 +45,51 @@ function Institution() {
|
|||
) => (
|
||||
<Fragment key={index}>
|
||||
{templateKey === 'notification_institution_sso_available' && (
|
||||
<Notification bsStyle="info">
|
||||
<Notification.Body>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="can_link_institution_email_acct_to_institution_acct"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName, email, institutionName }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</p>
|
||||
<div>
|
||||
<Trans
|
||||
i18nKey="doing_this_allow_log_in_through_institution"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>{' '}
|
||||
<a href="/learn/how-to/Institutional_Login">
|
||||
{t('learn_more')}
|
||||
</a>
|
||||
</div>
|
||||
</Notification.Body>
|
||||
<Notification.Action>
|
||||
<Notification
|
||||
bsStyle="info"
|
||||
body={
|
||||
<>
|
||||
{' '}
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="can_link_institution_email_acct_to_institution_acct"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName, email, institutionName }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</p>
|
||||
<div>
|
||||
<Trans
|
||||
i18nKey="doing_this_allow_log_in_through_institution"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>{' '}
|
||||
<a href="/learn/how-to/Institutional_Login">
|
||||
{t('learn_more')}
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
className={newNotificationStyle ? 'btn-secondary' : ''}
|
||||
bsSize="sm"
|
||||
href={`${samlInitPath}?university_id=${institutionId}&auto=/project&email=${email}`}
|
||||
>
|
||||
{t('link_account')}
|
||||
</Button>
|
||||
</Notification.Action>
|
||||
</Notification>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{templateKey === 'notification_institution_sso_linked' && (
|
||||
<Notification
|
||||
bsStyle="info"
|
||||
onDismiss={() => id && handleDismiss(id)}
|
||||
>
|
||||
<Notification.Body>
|
||||
body={
|
||||
<Trans
|
||||
i18nKey="account_has_been_link_to_institution_account"
|
||||
components={{ b: <b /> }}
|
||||
|
@ -89,76 +97,88 @@ function Institution() {
|
|||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{templateKey === 'notification_institution_sso_non_canonical' && (
|
||||
<Notification
|
||||
bsStyle="warning"
|
||||
onDismiss={() => id && handleDismiss(id)}
|
||||
>
|
||||
<Notification.Body>
|
||||
<Icon type="exclamation-triangle" fw />
|
||||
<Trans
|
||||
i18nKey="tried_to_log_in_with_email"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName, email: requestedEmail }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>{' '}
|
||||
<Trans
|
||||
i18nKey="in_order_to_match_institutional_metadata_associated"
|
||||
components={{ b: <b /> }}
|
||||
values={{ email: institutionEmail }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
body={
|
||||
<>
|
||||
{!newNotificationStyle && (
|
||||
<>
|
||||
<Icon type="exclamation-triangle" fw />{' '}
|
||||
</>
|
||||
)}
|
||||
<Trans
|
||||
i18nKey="tried_to_log_in_with_email"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName, email: requestedEmail }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>{' '}
|
||||
<Trans
|
||||
i18nKey="in_order_to_match_institutional_metadata_associated"
|
||||
components={{ b: <b /> }}
|
||||
values={{ email: institutionEmail }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{templateKey ===
|
||||
'notification_institution_sso_already_registered' && (
|
||||
<Notification
|
||||
bsStyle="info"
|
||||
onDismiss={() => id && handleDismiss(id)}
|
||||
>
|
||||
<Notification.Body>
|
||||
<Trans
|
||||
i18nKey="tried_to_register_with_email"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName, email }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>{' '}
|
||||
{t('we_logged_you_in')}
|
||||
</Notification.Body>
|
||||
<Notification.Action>
|
||||
body={
|
||||
<>
|
||||
<Trans
|
||||
i18nKey="tried_to_register_with_email"
|
||||
components={{ b: <b /> }}
|
||||
values={{ appName, email }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>{' '}
|
||||
{t('we_logged_you_in')}
|
||||
</>
|
||||
}
|
||||
action={
|
||||
<Button
|
||||
bsStyle="info"
|
||||
bsStyle={newNotificationStyle ? null : 'info'}
|
||||
className={newNotificationStyle ? 'btn-secondary' : ''}
|
||||
bsSize="sm"
|
||||
href="/learn/how-to/Institutional_Login"
|
||||
>
|
||||
{t('find_out_more')}
|
||||
</Button>
|
||||
</Notification.Action>
|
||||
</Notification>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{templateKey === 'notification_institution_sso_error' && (
|
||||
<Notification
|
||||
bsStyle="danger"
|
||||
onDismiss={() => id && handleDismiss(id)}
|
||||
>
|
||||
<Notification.Body>
|
||||
<Icon type="exclamation-triangle" fw />{' '}
|
||||
{t('generic_something_went_wrong')}.
|
||||
<div>
|
||||
{error?.translatedMessage
|
||||
? error?.translatedMessage
|
||||
: error?.message}
|
||||
</div>
|
||||
{error?.tryAgain ? `${t('try_again')}.` : null}
|
||||
</Notification.Body>
|
||||
</Notification>
|
||||
body={
|
||||
<>
|
||||
{!newNotificationStyle && (
|
||||
<>
|
||||
<Icon type="exclamation-triangle" fw />{' '}
|
||||
</>
|
||||
)}
|
||||
{t('generic_something_went_wrong')}.
|
||||
<div>
|
||||
{error?.translatedMessage
|
||||
? error?.translatedMessage
|
||||
: error?.message}
|
||||
</div>
|
||||
{error?.tryAgain ? `${t('try_again')}.` : null}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
|
|
|
@ -61,13 +61,15 @@ function Notification({
|
|||
bsStyle === 'danger' ? 'error' : bsStyle
|
||||
) as NotificationType
|
||||
return (
|
||||
<NewNotification
|
||||
type={newNotificationType}
|
||||
isDismissible={onDismiss != null}
|
||||
onDismiss={handleDismiss}
|
||||
content={body as React.ReactElement}
|
||||
action={action}
|
||||
/>
|
||||
<li>
|
||||
<NewNotification
|
||||
type={newNotificationType}
|
||||
isDismissible={onDismiss != null}
|
||||
onDismiss={handleDismiss}
|
||||
content={body as React.ReactElement}
|
||||
action={action}
|
||||
/>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import getMeta from '../../../../utils/meta'
|
|||
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
|
||||
import customLocalStorage from '../../../../infrastructure/local-storage'
|
||||
import { sendMB } from '../../../../infrastructure/event-tracking'
|
||||
import classNames from 'classnames'
|
||||
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||
|
||||
const isChromium = () =>
|
||||
|
@ -34,6 +35,10 @@ const EnrollmentNotification: JSXElementConstructor<{
|
|||
}> = enrollmentNotificationModule?.import.default
|
||||
|
||||
function UserNotifications() {
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const groupSubscriptionsPendingEnrollment: Subscription[] = getMeta(
|
||||
'ol-groupSubscriptionsPendingEnrollment',
|
||||
[]
|
||||
|
@ -61,7 +66,7 @@ function UserNotifications() {
|
|||
}
|
||||
|
||||
const show =
|
||||
user.writefull?.enabled === true || // show to any users who have writefull enabled regardless of split test
|
||||
user?.writefull?.enabled === true || // show to any users who have writefull enabled regardless of split test
|
||||
(!writefullIntegrationSplitTestEnabled && // show old banner to users who are not in the split test, who are on chrome and havent dismissed
|
||||
isChromium() &&
|
||||
getMeta('ol-showWritefullPromoBanner'))
|
||||
|
@ -71,7 +76,7 @@ function UserNotifications() {
|
|||
location: 'dashboard-banner',
|
||||
page: '/project',
|
||||
name:
|
||||
user.writefull?.enabled === true ||
|
||||
user?.writefull?.enabled === true ||
|
||||
writefullIntegrationSplitTestEnabled
|
||||
? 'writefull-premium'
|
||||
: 'writefull',
|
||||
|
@ -83,7 +88,11 @@ function UserNotifications() {
|
|||
const [dismissedWritefull, setDismissedWritefull] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="user-notifications">
|
||||
<div
|
||||
className={classNames('user-notifications', {
|
||||
'notification-list': newNotificationStyle,
|
||||
})}
|
||||
>
|
||||
<ul className="list-unstyled">
|
||||
{EnrollmentNotification &&
|
||||
groupSubscriptionsPendingEnrollment.map(subscription => (
|
||||
|
@ -110,7 +119,7 @@ function UserNotifications() {
|
|||
splitTestName={inrGeoBannerSplitTestName}
|
||||
/>
|
||||
) : null}
|
||||
{writefullIntegrationSplitTestEnabled || user.writefull?.enabled ? (
|
||||
{writefullIntegrationSplitTestEnabled || user?.writefull?.enabled ? (
|
||||
<WritefullPremiumPromoBanner
|
||||
show={showWritefull}
|
||||
setShow={setShowWritefull}
|
||||
|
|
|
@ -44,7 +44,7 @@ function WritefullPremiumPromoBanner({
|
|||
}
|
||||
action={
|
||||
<a
|
||||
className="btn btn-secondary"
|
||||
className="btn btn-secondary btn-sm"
|
||||
href="https://my.writefull.com/overleaf-invite?code=OVERLEAF10&redirect=plans"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
|
|
|
@ -2,6 +2,7 @@ import { memo, useCallback } from 'react'
|
|||
import Notification from './notification'
|
||||
import { sendMB } from '../../../../infrastructure/event-tracking'
|
||||
import customLocalStorage from '../../../../infrastructure/local-storage'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
const eventSegmentation = {
|
||||
location: 'dashboard-banner',
|
||||
|
@ -18,6 +19,10 @@ function WritefullPromoBanner({
|
|||
setShow: (value: boolean) => void
|
||||
onDismiss: () => void
|
||||
}) {
|
||||
const newNotificationStyle = getMeta(
|
||||
'ol-newNotificationStyle',
|
||||
false
|
||||
) as boolean
|
||||
const handleOpenLink = useCallback(() => {
|
||||
sendMB('promo-click', eventSegmentation)
|
||||
}, [])
|
||||
|
@ -47,7 +52,11 @@ function WritefullPromoBanner({
|
|||
}
|
||||
action={
|
||||
<a
|
||||
className="pull-right btn btn-info btn-sm"
|
||||
className={
|
||||
newNotificationStyle
|
||||
? 'btn btn-secondary btn-sm'
|
||||
: 'pull-right btn btn-info btn-sm'
|
||||
}
|
||||
href="https://my.writefull.com/overleaf-invite?code=OVERLEAF10"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
import UserNotifications from '../../js/features/project-list/components/notifications/user-notifications'
|
||||
import { ProjectListProvider } from '../../js/features/project-list/context/project-list-context'
|
||||
import useFetchMock from '../hooks/use-fetch-mock'
|
||||
import {
|
||||
commonSetupMocks,
|
||||
errorsMocks,
|
||||
fakeReconfirmationUsersData,
|
||||
institutionSetupMocks,
|
||||
reconfirmAffiliationSetupMocks,
|
||||
reconfirmationSetupMocks,
|
||||
setCommonMeta,
|
||||
setInstitutionMeta,
|
||||
setReconfirmAffiliationMeta,
|
||||
setReconfirmationMeta,
|
||||
} from './helpers/emails'
|
||||
|
||||
export const ProjectInvite = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
templateKey: 'notification_project_invite',
|
||||
messageOpts: {
|
||||
projectId: '123',
|
||||
projectName: 'Abc Project',
|
||||
userName: 'fakeUser',
|
||||
token: 'abcdef',
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const ProjectInviteNetworkError = (args: any) => {
|
||||
useFetchMock(errorsMocks)
|
||||
setCommonMeta({
|
||||
templateKey: 'notification_project_invite',
|
||||
messageOpts: {
|
||||
projectId: '123',
|
||||
projectName: 'Abc Project',
|
||||
userName: 'fakeUser',
|
||||
token: 'abcdef',
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const Wfh2020UpgradeOffer = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'wfh_2020_upgrade_offer',
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const IPMatchedAffiliationSsoEnabled = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_ip_matched_affiliation',
|
||||
messageOpts: {
|
||||
university_name: 'Abc University',
|
||||
institutionId: '456',
|
||||
ssoEnabled: true,
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const IPMatchedAffiliationSsoDisabled = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_ip_matched_affiliation',
|
||||
messageOpts: {
|
||||
university_name: 'Abc University',
|
||||
institutionId: '456',
|
||||
ssoEnabled: false,
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const TpdsFileLimit = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_tpds_file_limit',
|
||||
messageOpts: {
|
||||
projectName: 'Abc Project',
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const DropBoxDuplicateProjectNames = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_dropbox_duplicate_project_names',
|
||||
messageOpts: {
|
||||
projectName: 'Abc Project',
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const DropBoxUnlinkedDueToLapsedReconfirmation = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_dropbox_unlinked_due_to_lapsed_reconfirmation',
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const NotificationGroupInvitation = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_group_invitation',
|
||||
messageOpts: {
|
||||
inviterName: 'John Doe',
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const NotificationGroupInvitationCancelSubscription = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_group_invitation',
|
||||
messageOpts: {
|
||||
inviterName: 'John Doe',
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
window.metaAttributesCache.set('ol-hasIndividualRecurlySubscription', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const NonSpecificMessage = (args: any) => {
|
||||
useFetchMock(commonSetupMocks)
|
||||
setCommonMeta({ _id: 1, html: 'Non specific message' })
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const InstitutionSsoAvailable = (args: any) => {
|
||||
useFetchMock(institutionSetupMocks)
|
||||
setInstitutionMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_institution_sso_available',
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const InstitutionSsoLinked = (args: any) => {
|
||||
useFetchMock(institutionSetupMocks)
|
||||
setInstitutionMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_institution_sso_linked',
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const InstitutionSsoNonCanonical = (args: any) => {
|
||||
useFetchMock(institutionSetupMocks)
|
||||
setInstitutionMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_institution_sso_non_canonical',
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const InstitutionSsoAlreadyRegistered = (args: any) => {
|
||||
useFetchMock(institutionSetupMocks)
|
||||
setInstitutionMeta({
|
||||
_id: 1,
|
||||
templateKey: 'notification_institution_sso_already_registered',
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const InstitutionSsoError = (args: any) => {
|
||||
useFetchMock(institutionSetupMocks)
|
||||
setInstitutionMeta({
|
||||
templateKey: 'notification_institution_sso_error',
|
||||
error: {
|
||||
message: 'message',
|
||||
translatedMessage: 'Translated Message',
|
||||
tryAgain: true,
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const ResendConfirmationEmail = (args: any) => {
|
||||
useFetchMock(reconfirmationSetupMocks)
|
||||
setReconfirmationMeta()
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const ResendConfirmationEmailNetworkError = (args: any) => {
|
||||
useFetchMock(errorsMocks)
|
||||
setReconfirmationMeta()
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const ReconfirmAffiliation = (args: any) => {
|
||||
useFetchMock(reconfirmAffiliationSetupMocks)
|
||||
setReconfirmAffiliationMeta()
|
||||
window.metaAttributesCache.set('ol-allInReconfirmNotificationPeriods', [
|
||||
fakeReconfirmationUsersData,
|
||||
])
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const ReconfirmAffiliationNetworkError = (args: any) => {
|
||||
useFetchMock(errorsMocks)
|
||||
setReconfirmAffiliationMeta()
|
||||
window.metaAttributesCache.set('ol-allInReconfirmNotificationPeriods', [
|
||||
fakeReconfirmationUsersData,
|
||||
])
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const ReconfirmedAffiliationSuccess = (args: any) => {
|
||||
useFetchMock(reconfirmAffiliationSetupMocks)
|
||||
setReconfirmAffiliationMeta()
|
||||
window.metaAttributesCache.set('ol-userEmails', [fakeReconfirmationUsersData])
|
||||
window.metaAttributesCache.set('ol-newNotificationStyle', true)
|
||||
return (
|
||||
<ProjectListProvider>
|
||||
<UserNotifications {...args} />
|
||||
</ProjectListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Project List / Notifications New Style',
|
||||
component: UserNotifications,
|
||||
}
|
Loading…
Add table
Reference in a new issue