diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json
index 22bb1cea0b..7d8fb8ae4f 100644
--- a/services/web/frontend/extracted-translations.json
+++ b/services/web/frontend/extracted-translations.json
@@ -1,4 +1,6 @@
{
+ "10_editors_per_project": "",
+ "12x_more_compile_time": "",
"1_2_width": "",
"1_4_width": "",
"3_4_width": "",
@@ -162,6 +164,7 @@
"back_to_editor": "",
"back_to_subscription": "",
"back_to_your_projects": "",
+ "basic_compile_time": "",
"become_an_advisor": "",
"before_you_use_error_assistant": "",
"beta_program_already_participating": "",
@@ -271,6 +274,7 @@
"compact": "",
"company_name": "",
"compare": "",
+ "compare_all_plans": "",
"comparing_from_x_to_y": "",
"compile_error_entry_description": "",
"compile_error_handling": "",
@@ -308,6 +312,7 @@
"continue_github_merge": "",
"continue_to": "",
"continue_using_free_features": "",
+ "continue_with_free_plan": "",
"copied": "",
"copy": "",
"copy_code": "",
@@ -558,6 +563,7 @@
"for_universities": "",
"format": "",
"found_matching_deleted_users": "",
+ "free": "",
"free_7_day_trial_billed_annually": "",
"free_7_day_trial_billed_monthly": "",
"free_plan_label": "",
@@ -865,6 +871,7 @@
"lets_get_those_premium_features": "",
"library": "",
"license_for_educational_purposes_confirmation": "",
+ "limited_document_history": "",
"limited_offer": "",
"limited_to_n_editors": "",
"limited_to_n_editors_per_project": "",
@@ -1062,6 +1069,7 @@
"ok_join_project": "",
"on": "",
"on_free_plan_upgrade_to_access_features": "",
+ "one_editor_per_project": "",
"one_step_away_from_professional_features": "",
"only_group_admin_or_managers_can_delete_your_account_1": "",
"only_group_admin_or_managers_can_delete_your_account_2": "",
@@ -1142,6 +1150,7 @@
"pending_additional_licenses": "",
"pending_addon_cancellation": "",
"pending_invite": "",
+ "per_month": "",
"per_user": "",
"percent_is_the_percentage_of_the_line_width": "",
"permanently_disables_the_preview": "",
@@ -1266,6 +1275,7 @@
"reauthorize_github_account": "",
"recaptcha_conditions": "",
"recent_commits_in_github": "",
+ "recommended": "",
"recompile": "",
"recompile_from_scratch": "",
"recompile_pdf": "",
@@ -1555,6 +1565,7 @@
"sso_user_explanation_enabled_with_group_name": "",
"sso_user_explanation_ready_with_admin_email": "",
"sso_user_explanation_ready_with_group_name": "",
+ "standard": "",
"start_a_free_trial": "",
"start_by_adding_your_email": "",
"start_by_fixing_the_first_error_in_your_doc": "",
@@ -1570,6 +1581,7 @@
"stop_on_validation_error": "",
"store_your_work": "",
"stretch_width_to_text": "",
+ "student": "",
"student_disclaimer": "",
"subject": "",
"subject_area": "",
@@ -1813,6 +1825,7 @@
"unknown_group": "",
"unlimited_collaborators_per_project": "",
"unlimited_collabs": "",
+ "unlimited_document_history": "",
"unlimited_projects": "",
"unlink": "",
"unlink_all_users": "",
@@ -2019,6 +2032,7 @@
"your_affiliation_is_confirmed": "",
"your_browser_does_not_support_this_feature": "",
"your_compile_timed_out": "",
+ "your_current_plan": "",
"your_current_plan_gives_you": "",
"your_current_plan_supports_up_to_x_users": "",
"your_current_project_will_revert_to_the_version_from_time": "",
diff --git a/services/web/frontend/js/shared/components/upgrade-prompt.tsx b/services/web/frontend/js/shared/components/upgrade-prompt.tsx
new file mode 100644
index 0000000000..9797f6a02a
--- /dev/null
+++ b/services/web/frontend/js/shared/components/upgrade-prompt.tsx
@@ -0,0 +1,165 @@
+import OLBadge from '@/features/ui/components/ol/ol-badge'
+import OLButton from '@/features/ui/components/ol/ol-button'
+import OLCloseButton from '@/features/ui/components/ol/ol-close-button'
+import OLCol from '@/features/ui/components/ol/ol-col'
+import OLRow from '@/features/ui/components/ol/ol-row'
+import MaterialIcon from '@/shared/components/material-icon'
+import { PropsWithChildren } from 'react'
+import { Container } from 'react-bootstrap-5'
+import { Trans, useTranslation } from 'react-i18next'
+
+type IconListItemProps = PropsWithChildren<{
+ icon: string
+}>
+
+function IconListItem({ icon, children }: IconListItemProps) {
+ return (
+
+
+
+
+ {children}
+
+ )
+}
+
+type PlansLinkProps = {
+ itmCampaign: string
+}
+function PlansLink({
+ children,
+ itmCampaign,
+}: PropsWithChildren) {
+ return (
+
+ {children}
+
+
+ )
+}
+
+type UpgradePromptProps = {
+ title: string
+ summary: string
+ onClose: () => void
+ planPricing: { student: string; standard: string }
+ itmCampaign: string
+ isStudent?: boolean
+}
+
+export function UpgradePrompt({
+ title,
+ summary,
+ onClose,
+ planPricing,
+ itmCampaign,
+ isStudent = false,
+}: UpgradePromptProps) {
+ const { t } = useTranslation()
+ const planPrice = isStudent ? planPricing.student : planPricing.standard
+ const planCode = isStudent
+ ? 'student_free_trial_7_days'
+ : 'collaborator_free_trial_7_days'
+
+ return (
+
+
+ onClose()} />
+
+
+ {title}
+ {summary}
+
+
+
+
+
+
+ {isStudent ? t('student') : t('standard')}
+
+
+
+ {t('recommended')}
+
+
+
+
+
+ {planPrice}{' '}
+ {t('per_month')}
+
+
+
+
+
+ {t('12x_more_compile_time')}
+
+
+ {t('10_editors_per_project')}
+
+
+ {t('unlimited_document_history')}
+
+
+
+
+
+ {t('try_for_free')}
+
+
+
+
+
+
+
+ {t('free')}
+
+
+
+ {/* Invisible span here to hold the correct height to match a card with a price */}
+
+ {t('your_current_plan')}
+
+
+
+
+
+ {t('basic_compile_time')}
+
+
+ {t('one_editor_per_project')}
+
+
+ {t('limited_document_history')}
+
+
+
+
+ onClose()}>
+ {t('continue_with_free_plan')}
+
+
+
+
+
+
+
+ {/* eslint-disable react/jsx-key */}
+ ]}
+ />
+ {/* eslint-disable react/jsx-key */}
+
+
+
+ )
+}
diff --git a/services/web/frontend/stories/upgrade-prompt.stories.tsx b/services/web/frontend/stories/upgrade-prompt.stories.tsx
new file mode 100644
index 0000000000..1d7d6cfe60
--- /dev/null
+++ b/services/web/frontend/stories/upgrade-prompt.stories.tsx
@@ -0,0 +1,24 @@
+import { UpgradePrompt } from '@/shared/components/upgrade-prompt'
+import { StoryObj } from '@storybook/react/*'
+import { bsVersionDecorator } from '../../.storybook/utils/with-bootstrap-switcher'
+
+type Story = StoryObj
+
+export const Generic: Story = {
+ args: {
+ title: 'Unlock more compile time',
+ summary: 'Your project took too long to compile and timed out.',
+ onClose: () => {},
+ planPricing: { student: '$9', standard: '$21' },
+ itmCampaign: 'storybook',
+ },
+}
+
+export default {
+ title: 'Shared / Components / Upgrade Prompt',
+ component: UpgradePrompt,
+ ...bsVersionDecorator,
+ args: {
+ bootstrapVersion: 5,
+ },
+}
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/all.scss b/services/web/frontend/stylesheets/bootstrap-5/components/all.scss
index 00e838bc31..ae02138fb7 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/all.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/all.scss
@@ -41,3 +41,4 @@
@import 'panel-heading';
@import 'menu-bar';
@import 'invite';
+@import 'upgrade-prompt';
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/badge.scss b/services/web/frontend/stylesheets/bootstrap-5/components/badge.scss
index d3b5b812d9..c6f09687f0 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/badge.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/badge.scss
@@ -108,6 +108,14 @@ $max-width: 160px;
font-weight: var(--badge-font-weight);
}
+.badge-premium-gradient {
+ background: var(--premium-gradient);
+ color: var(--content-primary-dark);
+ vertical-align: middle;
+ margin-left: 5px;
+ font-weight: var(--badge-font-weight);
+}
+
.badge-link {
display: inline-block;
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/upgrade-prompt.scss b/services/web/frontend/stylesheets/bootstrap-5/components/upgrade-prompt.scss
new file mode 100644
index 0000000000..ca261e7014
--- /dev/null
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/upgrade-prompt.scss
@@ -0,0 +1,77 @@
+.upgrade-prompt {
+ border-radius: var(--border-radius-base);
+ box-shadow: 0 4px 6px 0 #1e25301f;
+ padding: var(--spacing-06);
+
+ .row {
+ margin-left: 0;
+ margin-right: 0;
+ }
+
+ .upgrade-prompt-title {
+ font-weight: 600;
+ }
+
+ .upgrade-prompt-summary {
+ margin-bottom: var(--spacing-10);
+ }
+
+ .upgrade-prompt-card {
+ display: flex;
+ flex-direction: column;
+ border-radius: var(--border-radius-medium);
+ padding: var(--spacing-09);
+ height: 100%;
+ font-size: var(--font-size-02);
+
+ h3 {
+ margin: 0;
+ font-weight: 600;
+ }
+ }
+
+ .upgrade-prompt-card-premium {
+ border: 2px solid transparent;
+ background:
+ linear-gradient(white, white) padding-box,
+ var(--premium-gradient) border-box;
+ }
+
+ .upgrade-prompt-card-free {
+ border: 2px solid var(--border-divider);
+ }
+
+ .upgrade-prompt-price {
+ margin-bottom: var(--spacing-05);
+ }
+
+ .upgrade-prompt-price-number {
+ @include heading-xl;
+
+ font-weight: 600;
+ }
+
+ .upgrade-prompt-list {
+ margin-bottom: var(--spacing-08);
+
+ & .material-symbols {
+ vertical-align: bottom;
+ }
+
+ & li:not(:last-of-type) {
+ margin-bottom: var(--spacing-05);
+ }
+ }
+
+ .upgrade-prompt-all-plans {
+ @include body-xs;
+
+ margin-top: var(--spacing-07);
+ margin-bottom: 0;
+
+ & .material-symbols {
+ vertical-align: bottom;
+ font-size: var(--font-size-02);
+ }
+ }
+}
diff --git a/services/web/locales/en.json b/services/web/locales/en.json
index 63c23b1068..f3535e560b 100644
--- a/services/web/locales/en.json
+++ b/services/web/locales/en.json
@@ -1,5 +1,7 @@
{
+ "10_editors_per_project": "10 editors per project",
"12x_basic": "12x Basic",
+ "12x_more_compile_time": "12x more compile time on our fastest servers",
"1_2_width": "½ width",
"1_4_width": "¼ width",
"3_4_width": "¾ width",
@@ -206,6 +208,7 @@
"back_to_subscription": "Back to Subscription",
"back_to_your_projects": "Back to your projects",
"basic": "Basic",
+ "basic_compile_time": "Basic compile time",
"basic_compile_timeout_on_fast_servers": "Basic compile timeout on fast servers",
"become_an_advisor": "Become an __appName__ advisor",
"before_you_use_error_assistant": "Before you use Error Assist",
@@ -352,6 +355,7 @@
"compact": "Compact",
"company_name": "Company Name",
"compare": "Compare",
+ "compare_all_plans": "Compare all plans on our <0>pricing page0>",
"compare_features": "Compare features",
"comparing_from_x_to_y": "Comparing from <0>__startTime__0> to <0>__endTime__0>",
"compile_error_entry_description": "An error which prevented this project from compiling",
@@ -1149,6 +1153,7 @@
"library": "Library",
"license": "License",
"license_for_educational_purposes_confirmation": "<0>__percent__% educational discount0><1/>I confirm this subscription is for educational purposes (applies to students or faculty using __appName__ for teaching)",
+ "limited_document_history": "Limited document history",
"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",
@@ -1417,6 +1422,7 @@
"on": "On",
"on_free_plan_upgrade_to_access_features": "You are on the __appName__ Free plan. Upgrade to access these <0>Premium features0>",
"one_collaborator_per_project": "1 collaborator per project",
+ "one_editor_per_project": "1 editor per project",
"one_free_collab": "One free collaborator",
"one_per_project": "1 per project",
"one_step_away_from_professional_features": "You are one step away from accessing <0>Overleaf Professional features0>!",
@@ -1686,6 +1692,7 @@
"recaptcha_conditions": "The site is protected by reCAPTCHA and the Google <1>Privacy Policy1> and <2>Terms of Service2> apply.",
"recent": "Recent",
"recent_commits_in_github": "Recent commits in GitHub",
+ "recommended": "Recommended",
"recompile": "Recompile",
"recompile_from_scratch": "Recompile from scratch",
"recompile_pdf": "Recompile the PDF",
@@ -2346,6 +2353,7 @@
"unlimited": "Unlimited",
"unlimited_collaborators_per_project": "Unlimited collaborators per project",
"unlimited_collabs": "Unlimited collaborators",
+ "unlimited_document_history": "Unlimited document history",
"unlimited_projects": "Unlimited projects",
"unlink": "Unlink",
"unlink_all_users": "Unlink all users",
@@ -2580,6 +2588,7 @@
"your_affiliation_is_confirmed": "Your <0>__institutionName__0> affiliation is confirmed.",
"your_browser_does_not_support_this_feature": "Sorry, your browser doesn’t support this feature. Please update your browser to its latest version.",
"your_compile_timed_out": "Your compile timed out",
+ "your_current_plan": "Your current plan",
"your_current_plan_gives_you": "By pausing your subscription, you’ll be able to access your premium features faster when you need them again.",
"your_current_plan_supports_up_to_x_users": "Your current plan supports up to __users__ users.",
"your_current_project_will_revert_to_the_version_from_time": "Your current project will revert to the version from __timestamp__",