Просмотр исходного кода

Merge pull request #12042 from overleaf/jel-change-plan

[web] Change plan UI now in a modal and update the style

GitOrigin-RevId: 1b501b4a972ca676cff32f531862c15c0c8f9e61
Jessica Lawshe 3 лет назад
Родитель
Сommit
b10b7b1f91

+ 10 - 4
services/web/frontend/js/features/subscription/components/dashboard/states/active/active.tsx

@@ -7,10 +7,13 @@ import { CancelSubscriptionButton } from './cancel-subscription-button'
 import { CancelSubscription } from './cancel-plan/cancel-subscription'
 import { PendingPlanChange } from './pending-plan-change'
 import { TrialEnding } from './trial-ending'
-import { ChangePlan } from './change-plan/change-plan'
 import { PendingAdditionalLicenses } from './pending-additional-licenses'
 import { ContactSupportToChangeGroupPlan } from './contact-support-to-change-group-plan'
 import isInFreeTrial from '../../../../util/is-in-free-trial'
+import { ChangePlanModal } from './change-plan/modals/change-plan-modal'
+import { ConfirmChangePlanModal } from './change-plan/modals/confirm-change-plan-modal'
+import { KeepCurrentPlanModal } from './change-plan/modals/keep-current-plan-modal'
+import { ChangeToGroupModal } from './change-plan/modals/change-to-group-modal'
 
 export function ActiveSubscription({
   subscription,
@@ -18,7 +21,7 @@ export function ActiveSubscription({
   subscription: RecurlySubscription
 }) {
   const { t } = useTranslation()
-  const { recurlyLoadError, setShowChangePersonalPlan, showCancellation } =
+  const { recurlyLoadError, setModalIdShown, showCancellation } =
     useSubscriptionDashboardContext()
 
   if (showCancellation) return <CancelSubscription />
@@ -59,7 +62,7 @@ export function ActiveSubscription({
               {' '}
               <button
                 className="btn-inline-link"
-                onClick={() => setShowChangePersonalPlan(true)}
+                onClick={() => setModalIdShown('change-plan')}
               >
                 {t('change_plan')}
               </button>
@@ -120,7 +123,10 @@ export function ActiveSubscription({
         <CancelSubscriptionButton subscription={subscription} />
       )}
 
-      <ChangePlan />
+      <ChangePlanModal />
+      <ConfirmChangePlanModal />
+      <KeepCurrentPlanModal />
+      <ChangeToGroupModal />
     </>
   )
 }

+ 0 - 40
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-plan.tsx

@@ -1,40 +0,0 @@
-import { useTranslation } from 'react-i18next'
-import LoadingSpinner from '../../../../../../../shared/components/loading-spinner'
-import { useSubscriptionDashboardContext } from '../../../../../context/subscription-dashboard-context'
-import { ChangeToGroupPlan } from './change-to-group-plan'
-import { ConfirmChangePlanModal } from './modals/confirm-change-plan-modal'
-import { IndividualPlansTable } from './individual-plans-table'
-import { KeepCurrentPlanModal } from './modals/keep-current-plan-modal'
-import { ChangeToGroupModal } from './modals/change-to-group-modal'
-
-export function ChangePlan() {
-  const { t } = useTranslation()
-  const {
-    plans,
-    queryingIndividualPlansData,
-    recurlyLoadError,
-    showChangePersonalPlan,
-  } = useSubscriptionDashboardContext()
-
-  if (!showChangePersonalPlan || !plans || recurlyLoadError) return null
-
-  if (queryingIndividualPlansData) {
-    return (
-      <>
-        <h2>{t('change_plan')}</h2>
-        <LoadingSpinner />
-      </>
-    )
-  } else {
-    return (
-      <>
-        <h2>{t('change_plan')}</h2>
-        <IndividualPlansTable plans={plans} />
-        <ChangeToGroupPlan />
-        <ConfirmChangePlanModal />
-        <KeepCurrentPlanModal />
-        <ChangeToGroupModal />
-      </>
-    )
-  }
-}

+ 3 - 3
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-to-group-plan.tsx

@@ -10,13 +10,13 @@ export function ChangeToGroupPlan() {
   }
 
   return (
-    <>
-      <h2>{t('looking_multiple_licenses')}</h2>
+    <div className="card-gray text-center mt-3">
+      <h2 style={{ marginTop: 0 }}>{t('looking_multiple_licenses')}</h2>
       <p style={{ margin: 0 }}>{t('reduce_costs_group_licenses')}</p>
       <br />
       <button className="btn btn-primary" onClick={handleClick}>
         {t('change_to_group_plan')}
       </button>
-    </>
+    </div>
   )
 }

+ 2 - 3
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/individual-plans-table.tsx

@@ -92,10 +92,9 @@ function PlansRows({ plans }: { plans: Array<Plan> }) {
 
 export function IndividualPlansTable({ plans }: { plans: Array<Plan> }) {
   const { t } = useTranslation()
-  const { recurlyLoadError, showChangePersonalPlan } =
-    useSubscriptionDashboardContext()
+  const { recurlyLoadError } = useSubscriptionDashboardContext()
 
-  if (!showChangePersonalPlan || !plans || recurlyLoadError) return null
+  if (!plans || recurlyLoadError) return null
 
   return (
     <table className="table table-vertically-centered-cells">

+ 52 - 0
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-plan-modal.tsx

@@ -0,0 +1,52 @@
+import { Modal } from 'react-bootstrap'
+import { useTranslation } from 'react-i18next'
+import { SubscriptionDashModalIds } from '../../../../../../../../../../types/subscription/dashboard/modal-ids'
+import AccessibleModal from '../../../../../../../../shared/components/accessible-modal'
+import LoadingSpinner from '../../../../../../../../shared/components/loading-spinner'
+import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
+import { ChangeToGroupPlan } from '../change-to-group-plan'
+import { IndividualPlansTable } from '../individual-plans-table'
+
+function ChangePlanOptions() {
+  const { plans, queryingIndividualPlansData, recurlyLoadError } =
+    useSubscriptionDashboardContext()
+
+  if (!plans || recurlyLoadError) return null
+
+  if (queryingIndividualPlansData) {
+    return (
+      <>
+        <LoadingSpinner />
+      </>
+    )
+  } else {
+    return (
+      <>
+        <div className="table-outlined-container">
+          <IndividualPlansTable plans={plans} />
+        </div>
+        <ChangeToGroupPlan />
+      </>
+    )
+  }
+}
+
+export function ChangePlanModal() {
+  const modalId: SubscriptionDashModalIds = 'change-plan'
+  const { t } = useTranslation()
+  const { handleCloseModal, modalIdShown } = useSubscriptionDashboardContext()
+
+  if (modalIdShown !== modalId) return null
+
+  return (
+    <AccessibleModal id={modalId} show animation onHide={handleCloseModal}>
+      <Modal.Header closeButton>
+        <Modal.Title>{t('change_plan')}</Modal.Title>
+      </Modal.Header>
+
+      <Modal.Body>
+        <ChangePlanOptions />
+      </Modal.Body>
+    </AccessibleModal>
+  )
+}

+ 0 - 7
services/web/frontend/js/features/subscription/context/subscription-dashboard-context.tsx

@@ -66,8 +66,6 @@ type SubscriptionDashboardContextValue = {
   setRecurlyLoadError: React.Dispatch<React.SetStateAction<boolean>>
   showCancellation: boolean
   setShowCancellation: React.Dispatch<React.SetStateAction<boolean>>
-  showChangePersonalPlan: boolean
-  setShowChangePersonalPlan: React.Dispatch<React.SetStateAction<boolean>>
   leavingGroupId?: string
   setLeavingGroupId: React.Dispatch<React.SetStateAction<string | undefined>>
 }
@@ -86,7 +84,6 @@ export function SubscriptionDashboardProvider({
   >()
   const [recurlyLoadError, setRecurlyLoadError] = useState(false)
   const [showCancellation, setShowCancellation] = useState(false)
-  const [showChangePersonalPlan, setShowChangePersonalPlan] = useState(false)
   const [plans, setPlans] = useState([])
   const [queryingIndividualPlansData, setQueryingIndividualPlansData] =
     useState(true)
@@ -268,8 +265,6 @@ export function SubscriptionDashboardProvider({
       setRecurlyLoadError,
       showCancellation,
       setShowCancellation,
-      showChangePersonalPlan,
-      setShowChangePersonalPlan,
       leavingGroupId,
       setLeavingGroupId,
     }),
@@ -304,8 +299,6 @@ export function SubscriptionDashboardProvider({
       setRecurlyLoadError,
       showCancellation,
       setShowCancellation,
-      showChangePersonalPlan,
-      setShowChangePersonalPlan,
       leavingGroupId,
       setLeavingGroupId,
     ]

+ 64 - 0
services/web/frontend/stylesheets/app/plans.less

@@ -578,3 +578,67 @@
     }
   }
 }
+
+/**
+	Subscription Dash
+*/
+
+#change-plan {
+  .modal-dialog {
+    &:extend(.modal-lg);
+  }
+
+  table {
+    @media only screen and (min-width: @screen-md-min) {
+      th:last-child,
+      td:last-child {
+        width: 1%; // will expand to fit the content
+        text-align: center;
+      }
+    }
+
+    @media only screen and (max-width: @screen-sm-max) {
+      display: block;
+
+      thead {
+        display: none;
+      }
+
+      tbody,
+      td,
+      tr {
+        display: inline-block;
+        padding-top: 0;
+        padding-bottom: 0;
+        text-align: center;
+        width: 100%;
+      }
+
+      td:first-child {
+        padding-top: @padding-md;
+      }
+      td:last-child {
+        padding-top: @padding-sm;
+        padding-bottom: @padding-md;
+      }
+
+      tr:first-child {
+        td:first-child {
+          padding-top: @padding-sm;
+        }
+      }
+
+      tr {
+        border-bottom: 1px solid @table-border-color;
+        td,
+        th {
+          border: 0 !important;
+        }
+      }
+
+      tr:last-child {
+        border-bottom: 0;
+      }
+    }
+  }
+}

+ 6 - 0
services/web/frontend/stylesheets/components/card.less

@@ -65,3 +65,9 @@
 .card-content {
   padding: @padding-md;
 }
+
+.card-gray {
+  &:extend(.card);
+  background-color: @neutral-10;
+  border-radius: @card-border-radius;
+}

+ 11 - 1
services/web/frontend/stylesheets/components/tables.less

@@ -83,7 +83,7 @@ th {
   }
 }
 
-// Bordered version
+// Bordered versions
 //
 // Add borders all around the table and between all the columns.
 
@@ -233,3 +233,13 @@ table {
     }
   }
 }
+
+.table-outlined-container {
+  border: @border-width-sm solid @table-border-color;
+  padding: @padding-sm @padding-sm 0 @padding-sm;
+  border-radius: @border-radius-base-new;
+
+  table {
+    margin-bottom: 0;
+  }
+}

+ 3 - 0
services/web/frontend/stylesheets/core/variables.less

@@ -165,8 +165,10 @@
 @line-height-small: 1.5;
 
 @border-radius-base: 3px;
+@border-radius-base-new: 4px;
 @border-radius-large: 5px;
 @border-radius-small: 2px;
+@border-width-sm: 1px;
 @border-width-base: 3px;
 @border-color-base: @ol-blue-gray-2;
 
@@ -932,6 +934,7 @@
 
 // Cards
 @card-box-shadow: none;
+@card-border-radius: @border-radius-base-new;
 
 // Project table
 @structured-list-link-color: @ol-blue;

+ 3 - 0
services/web/frontend/stylesheets/variables/all.less

@@ -107,8 +107,10 @@
 @line-height-small: 1.5;
 
 @border-radius-base: 3px;
+@border-radius-base-new: 4px;
 @border-radius-large: 5px;
 @border-radius-small: 2px;
+@border-width-sm: 1px;
 @border-width-base: 3px; // only used by plans and cards
 @border-size: 1px;
 @border-color-base: @neutral-60;
@@ -720,6 +722,7 @@
 
 // Cards
 @card-box-shadow: none;
+@card-border-radius: @border-radius-base-new;
 
 // Project table
 @structured-list-link-color: @blue;

+ 1 - 11
services/web/test/frontend/features/subscription/components/dashboard/states/active/change-plan/change-plan.test.tsx

@@ -1,7 +1,6 @@
 import { expect } from 'chai'
 import { fireEvent, screen, waitFor, within } from '@testing-library/react'
 import userEvent from '@testing-library/user-event'
-import { ChangePlan } from '../../../../../../../../../frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-plan'
 import { groupPlans, plans } from '../../../../../fixtures/plans'
 import {
   annualActiveSubscription,
@@ -22,7 +21,7 @@ import {
 } from '../../../../../../../../../frontend/js/features/subscription/data/subscription-url'
 import { renderActiveSubscription } from '../../../../../helpers/render-active-subscription'
 
-describe('<ChangePlan />', function () {
+describe('<ChangePlanModal />', function () {
   let reloadStub: () => void
   const originalLocation = window.location
   const plansMetaTag = { name: 'ol-plans', value: plans }
@@ -42,15 +41,6 @@ describe('<ChangePlan />', function () {
     })
   })
 
-  it('does not render the UI when showChangePersonalPlan is false', function () {
-    window.metaAttributesCache.delete('ol-plans')
-    const { container } = renderWithSubscriptionDashContext(<ChangePlan />, {
-      metaTags: [plansMetaTag],
-    })
-
-    expect(container.firstChild).to.be.null
-  })
-
   it('renders the individual plans table and group plans UI', async function () {
     renderActiveSubscription(annualActiveSubscription)
     const button = screen.getByRole('button', { name: 'Change plan' })

+ 1 - 0
services/web/types/subscription/dashboard/modal-ids.ts

@@ -3,3 +3,4 @@ export type SubscriptionDashModalIds =
   | 'change-to-group'
   | 'keep-current-plan'
   | 'leave-group'
+  | 'change-plan'