diff --git a/src/index.jsx b/src/index.jsx index c1c6a91d7..648d29462 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -17,7 +17,7 @@ import { messages as paragonMessages } from '@edx/paragon'; import messages from './i18n'; import configureStore from './store'; import NotFoundPage from './components/NotFoundPage'; -import { ConnectedOrderHistoryPage } from './order-history'; +import { OrderAndSubscriptionsPage } from './order-and-subscriptions'; import './index.scss'; @@ -27,7 +27,7 @@ subscribe(APP_READY, () => {
- + diff --git a/src/index.scss b/src/index.scss index 298b6f251..6d877f105 100755 --- a/src/index.scss +++ b/src/index.scss @@ -11,6 +11,7 @@ $fa-font-path: "~font-awesome/fonts"; @import "~@edx/frontend-component-footer/dist/footer"; @import "./order-history/style"; +@import "./order-and-subscriptions/style"; .word-break-all { word-break: break-all !important; diff --git a/src/order-and-subscriptions/OrderAndSubscriptionsPage.jsx b/src/order-and-subscriptions/OrderAndSubscriptionsPage.jsx new file mode 100644 index 000000000..f7c5372aa --- /dev/null +++ b/src/order-and-subscriptions/OrderAndSubscriptionsPage.jsx @@ -0,0 +1,48 @@ +import React, { useEffect } from 'react'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; + +import Subscriptions from '../subscriptions'; +import Payments from '../order-history'; + +import messages from './OrderAndSubscriptionsPage.messages'; + +const OrderAndSubscriptionsPage = ({ intl }) => { + const isB2CSubsEnabled = true; + + const message = (id) => intl.formatMessage(messages[id]); + + useEffect(() => { + if (isB2CSubsEnabled) { + document.title = 'Orders and Subscriptions | edX'; + } + }, [isB2CSubsEnabled]); + + if (!isB2CSubsEnabled) { + return ( +
+ +
+ ); + } + + return ( +
+
+

+ {message('ecommerce.order.history.main.heading')} +

+ + {message('ecommerce.order.history.main.subtitle')} + +
+ + +
+ ); +}; + +OrderAndSubscriptionsPage.propTypes = { + intl: intlShape.isRequired, +}; + +export default injectIntl(OrderAndSubscriptionsPage); diff --git a/src/order-and-subscriptions/OrderAndSubscriptionsPage.messages.jsx b/src/order-and-subscriptions/OrderAndSubscriptionsPage.messages.jsx new file mode 100644 index 000000000..8fa11b51b --- /dev/null +++ b/src/order-and-subscriptions/OrderAndSubscriptionsPage.messages.jsx @@ -0,0 +1,17 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + 'ecommerce.order.history.main.heading': { + id: 'ecommerce.order.history.main.heading', + defaultMessage: 'My orders and subscriptions', + description: 'Heading for orders and subscriptions page.', + }, + 'ecommerce.order.history.main.subtitle': { + id: 'ecommerce.order.history.main.subtitle', + defaultMessage: + 'Manage your program subscriptions and view your order history.', + description: 'Subtitle of Heading for orders and subscriptions page.', + }, +}); + +export default messages; diff --git a/src/order-and-subscriptions/_style.scss b/src/order-and-subscriptions/_style.scss new file mode 100644 index 000000000..e05e562a6 --- /dev/null +++ b/src/order-and-subscriptions/_style.scss @@ -0,0 +1,23 @@ +.page__order-and-subscriptions { + display: flex; + flex-direction: column; + gap: 2.5rem; + + section, .section { + display: flex; + flex-direction: column; + gap: 1rem; + + .section-gap-sm { + gap: 0.75rem; + } + + h1, h2 { + margin-bottom: 0; + } + } + + .subscription-scrollable { + max-height: 295px; + } +} diff --git a/src/order-and-subscriptions/index.js b/src/order-and-subscriptions/index.js new file mode 100644 index 000000000..5463e4c55 --- /dev/null +++ b/src/order-and-subscriptions/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export { default as OrderAndSubscriptionsPage } from './OrderAndSubscriptionsPage'; diff --git a/src/order-history/OrderHistoryPage.jsx b/src/order-history/OrderHistoryPage.jsx index 5f79b609b..e0147c5de 100644 --- a/src/order-history/OrderHistoryPage.jsx +++ b/src/order-history/OrderHistoryPage.jsx @@ -184,31 +184,44 @@ class OrderHistoryPage extends React.Component { const hasOrders = orders.length > 0; return ( -
-

- {this.props.intl.formatMessage(messages['ecommerce.order.history.page.heading'])} -

- {loadingError ? this.renderError() : null} - {loaded && hasOrders ? ( - <> - - {this.renderMobileOrdersTable()} - - - {this.renderOrdersTable()} - - {this.renderPagination()} - - ) : null} - {loaded && !hasOrders ? this.renderEmptyMessage() : null} - {loading ? this.renderLoading() : null} -
+
+ {this.props.isB2CSubsEnabled ? ( +

+ {this.props.intl.formatMessage( + messages['ecommerce.order.history.payments.heading'], + )} +

+ ) : ( +

+ {this.props.intl.formatMessage( + messages['ecommerce.order.history.page.heading'], + )} +

+ )} +
+ {loadingError ? this.renderError() : null} + {loaded && hasOrders ? ( + <> + + {this.renderMobileOrdersTable()} + + + {this.renderOrdersTable()} + + {this.renderPagination()} + + ) : null} + {loaded && !hasOrders ? this.renderEmptyMessage() : null} + {loading ? this.renderLoading() : null} +
+
); } } OrderHistoryPage.propTypes = { intl: intlShape.isRequired, + isB2CSubsEnabled: PropTypes.bool.isRequired, orders: PropTypes.arrayOf(PropTypes.shape({ datePlaced: PropTypes.string, total: PropTypes.string, diff --git a/src/order-history/OrderHistoryPage.messages.jsx b/src/order-history/OrderHistoryPage.messages.jsx index a3d1830ff..a9f8692ac 100644 --- a/src/order-history/OrderHistoryPage.messages.jsx +++ b/src/order-history/OrderHistoryPage.messages.jsx @@ -1,6 +1,11 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; const messages = defineMessages({ + 'ecommerce.order.history.payments.heading': { + id: 'ecommerce.order.history.payments.heading', + defaultMessage: 'Payments', + description: 'Heading for payments section.', + }, 'ecommerce.order.history.page.heading': { id: 'ecommerce.order.history.page.heading', defaultMessage: 'Order History', diff --git a/src/order-history/OrderHistoryPage.test.jsx b/src/order-history/OrderHistoryPage.test.jsx index 7efa3dc3a..867a6af91 100644 --- a/src/order-history/OrderHistoryPage.test.jsx +++ b/src/order-history/OrderHistoryPage.test.jsx @@ -12,6 +12,7 @@ const storeMocks = { ordersLoaded: require('./__mocks__/ordersLoaded.mockStore'), }; const requiredOrderHistoryPageProps = { + isB2CSubsEnabled: false, fetchOrders: () => {}, }; diff --git a/src/order-history/__snapshots__/OrderHistoryPage.test.jsx.snap b/src/order-history/__snapshots__/OrderHistoryPage.test.jsx.snap index 72dc1816a..5746ebdd1 100644 --- a/src/order-history/__snapshots__/OrderHistoryPage.test.jsx.snap +++ b/src/order-history/__snapshots__/OrderHistoryPage.test.jsx.snap @@ -1,96 +1,98 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` Renders correctly in various states renders orders table with pagination 1`] = ` -

Order History

-
+
    - - -
  • - +
  • +
  • -
    - Next - - +
    + Next + - - - -
    - -
  • -
- - + + + + + + + + + + + `; diff --git a/src/order-history/index.js b/src/order-history/index.js index a9d8d0136..e7f019f0e 100644 --- a/src/order-history/index.js +++ b/src/order-history/index.js @@ -1,11 +1,7 @@ -import ConnectedOrderHistoryPage from './OrderHistoryPage'; +import OrderHistoryPage from './OrderHistoryPage'; import reducer from './reducer'; import saga from './saga'; import { storeName } from './selectors'; -export { - ConnectedOrderHistoryPage, - reducer, - saga, - storeName, -}; +export default OrderHistoryPage; +export { reducer, saga, storeName }; diff --git a/src/subscriptions/SubscriptionScrollView.jsx b/src/subscriptions/SubscriptionScrollView.jsx new file mode 100644 index 000000000..8db6e347a --- /dev/null +++ b/src/subscriptions/SubscriptionScrollView.jsx @@ -0,0 +1,61 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Card, Badge, Scrollable } from '@edx/paragon'; + +const SubscriptionScrollView = ({ + subscriptions = [ + { + title: 'Blockchain Fundamentals', + org: 'University of California, Berkeley', + subscriptionStatus: 'trial', + }, + { + title: 'Critical Thinking', + org: 'Simmons University', + subscriptionStatus: 'active', + }, + { + title: 'Blockchain Fundamentals', + org: 'University of California, Berkeley', + subscriptionStatus: 'inActive', + }, + { + title: 'Critical Thinking', + org: 'Simmons University', + subscriptionStatus: 'inactive', + }, + ], +}) => ( + +
+ {subscriptions.map(({ title, org, subscriptionStatus }) => ( + +
+

{title}

+ + {subscriptionStatus.toLowerCase()} + +
+

{org}

+
+ ))} +
+
+); + +SubscriptionScrollView.propTypes = { + subscriptions: PropTypes.arrayOf( + PropTypes.shape({ + title: PropTypes.string.isRequired, + org: PropTypes.string.isRequired, + subscriptionStatus: PropTypes.string.isRequired, + }), + ), +}; + +SubscriptionScrollView.defaultProps = { + subscriptions: [], +}; + +export default SubscriptionScrollView; diff --git a/src/subscriptions/SubscriptionUpsell.jsx b/src/subscriptions/SubscriptionUpsell.jsx new file mode 100644 index 000000000..4ae20969c --- /dev/null +++ b/src/subscriptions/SubscriptionUpsell.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { useMediaQuery } from 'react-responsive'; + +import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { Alert, Badge, Button } from '@edx/paragon'; +import { Search } from '@edx/paragon/icons'; + +const SubscriptionUpsell = () => ( + + + , + ]} + stacked={useMediaQuery({ query: '(max-width: 834px)' })} + > + + + + + + + + +); + +export default SubscriptionUpsell; diff --git a/src/subscriptions/Subscriptions.jsx b/src/subscriptions/Subscriptions.jsx new file mode 100644 index 000000000..1d88f07e3 --- /dev/null +++ b/src/subscriptions/Subscriptions.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { Button } from '@edx/paragon'; +import { Launch } from '@edx/paragon/icons'; + +import messages from './Subscriptions.messages'; +import SubscriptionUpsell from './SubscriptionUpsell'; +import SubscriptionScrollView from './SubscriptionScrollView'; + +const Subscriptions = ({ intl }) => { + const hasSubscriptions = true; + + const message = (id, values = {}) => intl.formatMessage(messages[id], values); + const buttonLabel = message( + 'ecommerce.order.history.subscriptions.manage.button', + ); + + const renderEmpty = () => ( + <> + + {message('ecommerce.order.history.subscriptions.subtitle.empty')} + + + + ); + + const renderSubscriptions = () => ( + <> +
+ + {message('ecommerce.order.history.subscriptions.subtitle', { + buttonLabel: {buttonLabel}, + })} + + +
+ + + ); + + return ( +
+

{message('ecommerce.order.history.subscriptions.heading')}

+ {hasSubscriptions ? renderSubscriptions() : renderEmpty()} +
+ ); +}; + +Subscriptions.propTypes = { + intl: intlShape.isRequired, +}; + +export default injectIntl(Subscriptions); diff --git a/src/subscriptions/Subscriptions.messages.jsx b/src/subscriptions/Subscriptions.messages.jsx new file mode 100644 index 000000000..594fb880e --- /dev/null +++ b/src/subscriptions/Subscriptions.messages.jsx @@ -0,0 +1,28 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + 'ecommerce.order.history.subscriptions.heading': { + id: 'ecommerce.order.history.subscriptions.heading', + defaultMessage: 'Subscriptions', + description: 'Heading for subscriptions section.', + }, + 'ecommerce.order.history.subscriptions.subtitle': { + id: 'ecommerce.order.history.subscriptions.subtitle', + defaultMessage: + 'To view your receipts, change your payment method or cancel your subscription, click {buttonLabel}.', + description: 'Subtitle for subscriptions section.', + }, + 'ecommerce.order.history.subscriptions.subtitle.empty': { + id: 'ecommerce.order.history.subscriptions.subtitle.empty', + defaultMessage: 'You do not have any active or previous subscriptions.', + description: + 'Subtitle for subscriptions section when there are no subscriptions.', + }, + 'ecommerce.order.history.subscriptions.manage.button': { + id: 'ecommerce.order.history.subscriptions.manage.button', + defaultMessage: 'Manage my subscription', + description: 'Button text for managing subscriptions.', + }, +}); + +export default messages; diff --git a/src/subscriptions/index.js b/src/subscriptions/index.js new file mode 100644 index 000000000..efbf2022d --- /dev/null +++ b/src/subscriptions/index.js @@ -0,0 +1,3 @@ +import Subscriptions from './Subscriptions'; + +export default Subscriptions;