-
+
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
-
+
-
+
+ -
-
+
+
+
+
+
+
+
+
+
`;
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;