Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.

Commit 754fdf5

Browse files
feat: add subscriptions section to orders page (#289)
* feat: design changes for subscriptions (#276) * feat: api integration for subscription related changes (#282) * feat: add manage subscriptions url and update subscription upsell (#288) * test: add more tests for subscriptions --------- Co-authored-by: Nawfal Ahmed <111358247+NawfalAhmed@users.noreply.github.com> Co-authored-by: Nawfal Ahmed <nawfal.ahmed@arbisoft.com>
1 parent dcf9f9b commit 754fdf5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1511
-154
lines changed

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ LMS_BASE_URL=''
44
CREDENTIALS_BASE_URL=''
55
COMMERCE_COORDINATOR_BASE_URL=''
66
ECOMMERCE_BASE_URL=''
7+
SUBSCRIPTIONS_BASE_URL=''
78
LOGIN_URL=''
89
LOGOUT_URL=''
910
CSRF_TOKEN_API_PATH=''
@@ -20,3 +21,4 @@ LOGO_URL=''
2021
LOGO_TRADEMARK_URL=''
2122
LOGO_WHITE_URL=''
2223
FAVICON_URL=''
24+
ENABLE_B2C_SUBSCRIPTIONS=false

.env.development

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ LMS_BASE_URL=http://localhost:18000
55
CREDENTIALS_BASE_URL=http://localhost:18150
66
COMMERCE_COORDINATOR_BASE_URL=http://localhost:8140
77
ECOMMERCE_BASE_URL=http://localhost:18130
8+
SUBSCRIPTIONS_BASE_URL=http://localhost:18750
89
LOGIN_URL=http://localhost:18000/login
910
LOGOUT_URL=http://localhost:18000/login
1011
CSRF_TOKEN_API_PATH=/csrf/api/v1/token
@@ -21,3 +22,4 @@ LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
2122
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
2223
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
2324
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
25+
ENABLE_B2C_SUBSCRIPTIONS=true

.env.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ LMS_BASE_URL=http://localhost:18000
55
CREDENTIALS_BASE_URL=http://localhost:18150
66
COMMERCE_COORDINATOR_BASE_URL=http://localhost:8140
77
ECOMMERCE_BASE_URL=http://localhost:18130
8+
SUBSCRIPTIONS_BASE_URL=http://localhost:18750
89
LOGIN_URL=http://localhost:18000/login
910
LOGOUT_URL=http://localhost:18000/login
1011
CSRF_TOKEN_API_PATH=/csrf/api/v1/token
@@ -21,3 +22,4 @@ LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
2122
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
2223
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
2324
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
25+
ENABLE_B2C_SUBSCRIPTIONS=true

package-lock.json

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"redux": "4.2.0",
7070
"redux-logger": "3.0.6",
7171
"redux-saga": "1.1.3",
72+
"redux-saga-routines": "^3.2.3",
7273
"redux-thunk": "2.4.1",
7374
"reselect": "4.1.6",
7475
"universal-cookie": "4.0.4",

src/common/utils.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { call, put } from 'redux-saga/effects';
12
import camelCase from 'lodash.camelcase';
23
import snakeCase from 'lodash.snakecase';
34

@@ -79,3 +80,23 @@ export class AsyncActionType {
7980
return `${this.topic}__${this.name}__RESET`;
8081
}
8182
}
83+
84+
/**
85+
* A higher order helper function to create a redux-saga generator function
86+
*
87+
* it handles the boilerplate of making a call to an API
88+
* and dispatching the appropriate actions.
89+
*/
90+
export function createFetchHandler(routine, apiCall) {
91+
return function* handleFetch() {
92+
try {
93+
yield put(routine.request());
94+
const result = yield call(apiCall);
95+
yield put(routine.success(result));
96+
} catch (error) {
97+
yield put(routine.failure(error));
98+
} finally {
99+
yield put(routine.fulfill());
100+
}
101+
};
102+
}

src/index.jsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
APP_READY,
1010
initialize,
1111
subscribe,
12+
getConfig,
13+
mergeConfig,
1214
} from '@edx/frontend-platform';
1315

1416
import Header from '@edx/frontend-component-header';
@@ -17,17 +19,43 @@ import Footer from '@edx/frontend-component-footer';
1719
import messages from './i18n';
1820
import configureStore from './store';
1921
import NotFoundPage from './components/NotFoundPage';
20-
import { ConnectedOrderHistoryPage } from './order-history';
22+
import { OrdersAndSubscriptionsPage } from './orders-and-subscriptions';
23+
import { ManageSubscriptionsPage } from './subscriptions';
2124

2225
import './index.scss';
2326

27+
/**
28+
* TEMPORARY
29+
*
30+
* Until we add the following keys in frontend-platform,
31+
* use mergeConfig to join it with the rest of the config items
32+
* (so we don't need to get it separately from process.env).
33+
* After we add the keys to frontend-platform, this mergeConfig can go away
34+
*/
35+
mergeConfig({
36+
COMMERCE_COORDINATOR_BASE_URL: process.env.COMMERCE_COORDINATOR_BASE_URL,
37+
ENABLE_B2C_SUBSCRIPTIONS: process.env.ENABLE_B2C_SUBSCRIPTIONS,
38+
SUBSCRIPTIONS_BASE_URL: process.env.SUBSCRIPTIONS_BASE_URL,
39+
SUPPORT_URL: process.env.SUPPORT_URL,
40+
});
41+
2442
subscribe(APP_READY, () => {
43+
if (process.env.NODE_ENV === 'development') {
44+
global.analytics?.debug();
45+
}
46+
2547
ReactDOM.render(
2648
<AppProvider store={configureStore()}>
2749
<Header />
2850
<main>
2951
<Switch>
30-
<Route path="/orders" component={ConnectedOrderHistoryPage} />
52+
{getConfig().ENABLE_B2C_SUBSCRIPTIONS === 'true' ? (
53+
<Route
54+
path="/manage-subscriptions"
55+
component={ManageSubscriptionsPage}
56+
/>
57+
) : null}
58+
<Route path="/orders" component={OrdersAndSubscriptionsPage} />
3159
<Route path="/notfound" component={NotFoundPage} />
3260
<Route path="*" component={NotFoundPage} />
3361
</Switch>
@@ -39,7 +67,12 @@ subscribe(APP_READY, () => {
3967
});
4068

4169
subscribe(APP_INIT_ERROR, (error) => {
42-
ReactDOM.render(<IntlProvider locale="en"><ErrorPage message={error.message} /></IntlProvider>, document.getElementById('root'));
70+
ReactDOM.render(
71+
<IntlProvider locale="en">
72+
<ErrorPage message={error.message} />
73+
</IntlProvider>,
74+
document.getElementById('root'),
75+
);
4376
});
4477

4578
initialize({

src/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ $fa-font-path: "~font-awesome/fonts";
1111
@import "~@edx/frontend-component-footer/dist/footer";
1212

1313
@import "./order-history/style";
14+
@import "./orders-and-subscriptions/style";
1415

1516
.word-break-all {
1617
word-break: break-all !important;

src/order-history/OrderHistoryPage.jsx

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ class OrderHistoryPage extends React.Component {
2727
this.handlePageSelect = this.handlePageSelect.bind(this);
2828
}
2929

30-
componentDidMount() {
31-
// TODO: We should fetch based on the route (ex: /orders/list/page/1)
32-
this.props.fetchOrders(1);
33-
}
34-
3530
handlePageSelect(page) {
3631
// TODO: We should update the url and trigger this fetching based on the route
3732
this.props.fetchOrders(page);
@@ -182,33 +177,39 @@ class OrderHistoryPage extends React.Component {
182177
} = this.props;
183178
const loaded = !loading && !loadingError;
184179
const hasOrders = orders.length > 0;
180+
const heading = this.props.intl.formatMessage(
181+
messages['ecommerce.order.history.page.heading'],
182+
);
185183

186184
return (
187-
<div className="page__order-history container-fluid py-5">
188-
<h1>
189-
{this.props.intl.formatMessage(messages['ecommerce.order.history.page.heading'])}
190-
</h1>
191-
{loadingError ? this.renderError() : null}
192-
{loaded && hasOrders ? (
193-
<>
194-
<MediaQuery query="(max-width: 768px)">
195-
{this.renderMobileOrdersTable()}
196-
</MediaQuery>
197-
<MediaQuery query="(min-width: 769px)">
198-
{this.renderOrdersTable()}
199-
</MediaQuery>
200-
{this.renderPagination()}
201-
</>
202-
) : null}
203-
{loaded && !hasOrders ? this.renderEmptyMessage() : null}
204-
{loading ? this.renderLoading() : null}
205-
</div>
185+
<section className="page__order-history">
186+
{this.props.isB2CSubsEnabled ? <h2>{heading}</h2> : <h1>{heading}</h1>}
187+
<div>
188+
{loadingError ? this.renderError() : null}
189+
{loaded && hasOrders ? (
190+
<>
191+
<MediaQuery query="(max-width: 768px)">
192+
{this.renderMobileOrdersTable()}
193+
</MediaQuery>
194+
<MediaQuery query="(min-width: 769px)">
195+
{this.renderOrdersTable()}
196+
</MediaQuery>
197+
{this.renderPagination()}
198+
</>
199+
) : null}
200+
{loaded && !hasOrders ? this.renderEmptyMessage() : null}
201+
{loading && !this.props.isB2CSubsEnabled
202+
? this.renderLoading()
203+
: null}
204+
</div>
205+
</section>
206206
);
207207
}
208208
}
209209

210210
OrderHistoryPage.propTypes = {
211211
intl: intlShape.isRequired,
212+
isB2CSubsEnabled: PropTypes.bool.isRequired,
212213
orders: PropTypes.arrayOf(PropTypes.shape({
213214
datePlaced: PropTypes.string,
214215
total: PropTypes.string,

src/order-history/OrderHistoryPage.test.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import configureMockStore from 'redux-mock-store';
88
import ConnectedOrderHistoryPage from './OrderHistoryPage';
99

1010
const mockStore = configureMockStore();
11-
const storeMocks = {
12-
ordersLoaded: require('./__mocks__/ordersLoaded.mockStore'),
13-
};
11+
const storeMocks = require('../store/__mocks__/mockStore');
12+
1413
const requiredOrderHistoryPageProps = {
14+
isB2CSubsEnabled: false,
1515
fetchOrders: () => {},
1616
};
1717

@@ -29,7 +29,7 @@ describe('<OrderHistoryPage />', () => {
2929
const tree = renderer
3030
.create((
3131
<IntlProvider locale="en">
32-
<Provider store={mockStore(storeMocks.ordersLoaded)}>
32+
<Provider store={mockStore(storeMocks)}>
3333
<ConnectedOrderHistoryPage {...requiredOrderHistoryPageProps} />
3434
</Provider>
3535
</IntlProvider>

0 commit comments

Comments
 (0)