diff --git a/package-lock.json b/package-lock.json
index 5103f8d50d..c16d16ce08 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7441,6 +7441,16 @@
"url-parse": "^1.4.7"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.6",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -8108,6 +8118,15 @@
"@types/node": "*"
}
},
+ "node_modules/@types/bootstrap": {
+ "version": "5.2.6",
+ "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz",
+ "integrity": "sha512-BlAc3YATdasbHoxMoBWODrSF6qwQO/E9X8wVxCCSa6rWjnaZfpkr2N6pUMCY6jj2+wf0muUtLySbvU9etX6YqA==",
+ "dev": true,
+ "dependencies": {
+ "@popperjs/core": "^2.9.2"
+ }
+ },
"node_modules/@types/bson": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz",
@@ -34569,6 +34588,7 @@
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^8.0.0",
"@testing-library/user-event": "^14.2.0",
+ "@types/bootstrap": "^5.2.6",
"@types/chai": "^4.3.0",
"@types/events": "^3.0.0",
"@types/express": "^4.17.13",
@@ -43579,6 +43599,7 @@
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^8.0.0",
"@testing-library/user-event": "^14.2.0",
+ "@types/bootstrap": "*",
"@types/chai": "^4.3.0",
"@types/events": "^3.0.0",
"@types/express": "^4.17.13",
@@ -43732,7 +43753,7 @@
"passport-local": "^1.0.0",
"passport-oauth2": "^1.5.0",
"passport-orcid": "0.0.4",
- "passport-saml": "3.2.3",
+ "passport-saml": "^3.2.3",
"passport-twitter": "^1.0.4",
"pdfjs-dist213": "npm:pdfjs-dist@2.13.216",
"pdfjs-dist31": "npm:pdfjs-dist@3.1.81",
@@ -45576,6 +45597,12 @@
"url-parse": "^1.4.7"
}
},
+ "@popperjs/core": {
+ "version": "2.11.6",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
+ "dev": true
+ },
"@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -46120,6 +46147,15 @@
"@types/node": "*"
}
},
+ "@types/bootstrap": {
+ "version": "5.2.6",
+ "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz",
+ "integrity": "sha512-BlAc3YATdasbHoxMoBWODrSF6qwQO/E9X8wVxCCSa6rWjnaZfpkr2N6pUMCY6jj2+wf0muUtLySbvU9etX6YqA==",
+ "dev": true,
+ "requires": {
+ "@popperjs/core": "^2.9.2"
+ }
+ },
"@types/bson": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz",
diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json
index fc82cffcff..8206d91c5e 100644
--- a/services/web/frontend/extracted-translations.json
+++ b/services/web/frontend/extracted-translations.json
@@ -432,6 +432,7 @@
"linked_file": "",
"loading": "",
"loading_github_repositories": "",
+ "loading_prices": "",
"loading_recent_github_commits": "",
"log_entry_description": "",
"log_entry_maximum_entries": "",
@@ -820,6 +821,7 @@
"toolbar_undo": "",
"total_per_month": "",
"total_per_year": "",
+ "total_with_subtotal_and_tax": "",
"total_words": "",
"track_changes": "",
"trash": "",
diff --git a/services/web/frontend/js/features/subscription/components/dashboard/generic-error-alert.tsx b/services/web/frontend/js/features/subscription/components/dashboard/generic-error-alert.tsx
new file mode 100644
index 0000000000..57921c67c1
--- /dev/null
+++ b/services/web/frontend/js/features/subscription/components/dashboard/generic-error-alert.tsx
@@ -0,0 +1,18 @@
+import classNames from 'classnames'
+import { useTranslation } from 'react-i18next'
+
+export default function GenericErrorAlert({
+ className,
+}: {
+ className?: string
+}) {
+ const { t } = useTranslation()
+ const alertClassName = classNames('alert', 'alert-danger', className)
+
+ return (
+
+ {t('generic_something_went_wrong')}. {t('try_again')}.{' '}
+ {t('generic_if_problem_continues_contact_us')}.
+
+ )
+}
diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx
index 4002b72c75..14bee2fb20 100644
--- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx
+++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx
@@ -3,9 +3,11 @@ import { Modal } from 'react-bootstrap'
import { useTranslation, Trans } from 'react-i18next'
import { GroupPlans } from '../../../../../../../../../../types/subscription/dashboard/group-plans'
import { Subscription } from '../../../../../../../../../../types/subscription/dashboard/subscription'
+import { PriceForDisplayData } from '../../../../../../../../../../types/subscription/plan'
import AccessibleModal from '../../../../../../../../shared/components/accessible-modal'
import getMeta from '../../../../../../../../utils/meta'
import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
+import GenericErrorAlert from '../../../../generic-error-alert'
const educationalPercentDiscount = 40
const groupSizeForEducationalDiscount = 10
@@ -53,21 +55,66 @@ function EducationDiscountAppliedOrNot({ groupSize }: { groupSize: string }) {
)
}
-function GroupPrice() {
+function GroupPrice({
+ groupPlanToChangeToPrice,
+ queryingGroupPlanToChangeToPrice,
+}: {
+ groupPlanToChangeToPrice?: PriceForDisplayData
+ queryingGroupPlanToChangeToPrice: boolean
+}) {
const { t } = useTranslation()
+
+ const totalPrice =
+ !queryingGroupPlanToChangeToPrice &&
+ groupPlanToChangeToPrice?.totalForDisplay
+ ? groupPlanToChangeToPrice.totalForDisplay
+ : '…'
+
+ const perUserPrice =
+ !queryingGroupPlanToChangeToPrice &&
+ groupPlanToChangeToPrice?.perUserDisplayPrice
+ ? groupPlanToChangeToPrice.perUserDisplayPrice
+ : '…'
+
return (
<>
- X / {t('year')}
+ {totalPrice} / {t('year')}
- {/* TODO: price */}
-
+ {queryingGroupPlanToChangeToPrice ? (
+ t('loading_prices')
+ ) : (
+
+ )}
+
+
- {/* TODO: price */}
-
+
+
+
+
+ {queryingGroupPlanToChangeToPrice ? (
+ t('loading_prices')
+ ) : (
+
+ )}
+
>
)
@@ -78,10 +125,13 @@ export function ChangeToGroupModal() {
const { t } = useTranslation()
const {
groupPlanToChangeToCode,
+ groupPlanToChangeToPrice,
+ groupPlanToChangeToPriceError,
groupPlanToChangeToSize,
groupPlanToChangeToUsage,
handleCloseModal,
modalIdShown,
+ queryingGroupPlanToChangeToPrice,
setGroupPlanToChangeToCode,
setGroupPlanToChangeToSize,
setGroupPlanToChangeToUsage,
@@ -100,8 +150,6 @@ export function ChangeToGroupModal() {
function handleGetInTouchButton() {
handleCloseModal()
-
- // @ts-ignore
$('[data-ol-contact-form-modal="contact-us"]').modal()
}
@@ -142,10 +190,16 @@ export function ChangeToGroupModal() {
+ {groupPlanToChangeToPriceError &&
}
-
+
{t('each_user_will_have_access_to')}:
@@ -257,11 +311,34 @@ export function ChangeToGroupModal() {
+ {groupPlanToChangeToPrice?.includesTax && (
+
+ ,
+ ]}
+ />
+
+ )}
{t('new_subscription_will_be_billed_immediately')}
-
+