From 3e94c7573b102860417aff12ddc5bce11c3df341 Mon Sep 17 00:00:00 2001 From: Michael LoTurco Date: Wed, 28 Feb 2018 14:24:06 -0500 Subject: [PATCH] Add Store and Search Component Add search component to entitlement support app, requires the addition of a Provider and store to the EntitlementSupportPage componenet and wraps search in a Main component to contain additional pieces of the page as added Learner-3925 --- .../jsx/entitlements/components/Main/Main.jsx | 27 +++++++++++ .../components/Main/MainContainer.jsx | 20 ++++++++ .../entitlements/components/Search/Search.jsx | 47 +++++++++++++++++++ .../components/Search/SearchContainer.jsx | 20 ++++++++ .../entitlements/data/actions/constants.js | 12 +++++ .../entitlements/data/actions/entitlement.js | 35 ++++++++++++++ .../jsx/entitlements/data/actions/error.js | 15 ++++++ .../jsx/entitlements/data/api/client.js | 4 +- .../data/reducers/entitlements.js | 12 +++++ .../jsx/entitlements/data/reducers/error.js | 15 ++++++ .../jsx/entitlements/data/reducers/index.js | 8 ++++ .../support/jsx/entitlements/data/store.js | 21 +++++++++ .../static/support/jsx/entitlements/index.jsx | 15 +++--- lms/templates/support/entitlement.html | 2 +- package.json | 3 ++ 15 files changed, 247 insertions(+), 9 deletions(-) create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/components/Main/Main.jsx create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/components/Main/MainContainer.jsx create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/components/Search/Search.jsx create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/components/Search/SearchContainer.jsx create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/data/actions/constants.js create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/data/actions/entitlement.js create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/data/actions/error.js create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/entitlements.js create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/error.js create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/index.js create mode 100644 lms/djangoapps/support/static/support/jsx/entitlements/data/store.js diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/components/Main/Main.jsx b/lms/djangoapps/support/static/support/jsx/entitlements/components/Main/Main.jsx new file mode 100644 index 0000000000..3c09151fd2 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/components/Main/Main.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { StatusAlert } from '@edx/paragon'; +import SearchContainer from '../Search/SearchContainer.jsx'; + +const Main = props => ( +
+ +

+ Entitlement Support Page +

+ +
+); + +Main.propTypes = { + errorMessage: PropTypes.string.isRequired, + dismissErrorMessage: PropTypes.func.isRequired, +}; + +export default Main; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/components/Main/MainContainer.jsx b/lms/djangoapps/support/static/support/jsx/entitlements/components/Main/MainContainer.jsx new file mode 100644 index 0000000000..39c342b0a1 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/components/Main/MainContainer.jsx @@ -0,0 +1,20 @@ +import { connect } from 'react-redux'; + +import { dismissError } from '../../data/actions/error'; +import Main from './Main.jsx'; + + +const mapStateToProps = state => ({ + errorMessage: state.error, +}); + +const mapDispatchToProps = dispatch => ({ + dismissErrorMessage: () => dispatch(dismissError()), +}); + +const MainContainer = connect( + mapStateToProps, + mapDispatchToProps, +)(Main); + +export default MainContainer; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/components/Search/Search.jsx b/lms/djangoapps/support/static/support/jsx/entitlements/components/Search/Search.jsx new file mode 100644 index 0000000000..9cd62bc100 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/components/Search/Search.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { InputText } from '@edx/paragon'; +import PropTypes from 'prop-types'; + + +class Search extends React.Component { + constructor(props) { + super(props); + this.state = { username: '' }; + + this.handleSubmit = this.handleSubmit.bind(this); + this.handleUsernameChange = this.handleUsernameChange.bind(this); + } + + handleSubmit(event) { + event.preventDefault(); + // updating state will cause react to re-render dom, the default refresh is unneeded + this.props.fetchEntitlements(this.state.username); + } + + handleUsernameChange(username) { + this.setState({ username }); + } + + render() { + return ( +
+ + + + ); + } +} + +Search.propTypes = { + fetchEntitlements: PropTypes.func.isRequired, +}; + +export default Search; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/components/Search/SearchContainer.jsx b/lms/djangoapps/support/static/support/jsx/entitlements/components/Search/SearchContainer.jsx new file mode 100644 index 0000000000..80bdd3b240 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/components/Search/SearchContainer.jsx @@ -0,0 +1,20 @@ +import { connect } from 'react-redux'; + +import { fetchEntitlements } from '../../data/actions/entitlement'; +import Search from './Search.jsx'; + +const mapStateToProps = state => ({ + entitlements: state.entitlements, +}); + + +const mapDispatchToProps = dispatch => ({ + fetchEntitlements: username => dispatch(fetchEntitlements(username)), +}); + +const SearchContainer = connect( + mapStateToProps, + mapDispatchToProps, +)(Search); + +export default SearchContainer; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/constants.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/constants.js new file mode 100644 index 0000000000..07b700bda0 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/constants.js @@ -0,0 +1,12 @@ +export const entitlementActions = { + fetch: { + SUCCESS: 'FETCH_ENTITLEMENTS_SUCCESS', + FAILURE: 'FETCH_ENTITLEMENTS_FAILURE', + }, +}; + +export const errorActions = { + DISPLAY_ERROR: 'DISPLAY_ERROR', + DISMISS_ERROR: 'DISMISS_ERROR', +}; + diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/entitlement.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/entitlement.js new file mode 100644 index 0000000000..e87397beac --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/entitlement.js @@ -0,0 +1,35 @@ +import { getEntitlements } from '../api/client'; + +import { entitlementActions } from './constants'; +import { displayError } from './error'; + +const fetchEntitlementsSuccess = entitlements => ({ + type: entitlementActions.fetch.SUCCESS, + entitlements, +}); + +const fetchEntitlementsFailure = error => + dispatch => + dispatch(displayError('Error Getting Entitlements', error)); + +const fetchEntitlements = username => + (dispatch) => { + getEntitlements(username) + .then((response) => { + if (response.ok) { + return response.json(); + } + throw new Error(response); + }) + .then( + json => dispatch(fetchEntitlementsSuccess(json.results)), + error => dispatch(fetchEntitlementsFailure(error)), + ); + }; + + +export { + fetchEntitlements, + fetchEntitlementsSuccess, + fetchEntitlementsFailure, +}; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/error.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/error.js new file mode 100644 index 0000000000..e859381ed8 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/actions/error.js @@ -0,0 +1,15 @@ +import { errorActions } from './constants'; + +const displayError = (message, error) => ({ + type: errorActions.DISPLAY_ERROR, + error: `${message}: ${error}`, +}); + +const dismissError = () => ({ + type: errorActions.DISMISS_ERROR, +}); + +export { + displayError, + dismissError, +}; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/api/client.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/api/client.js index 4271011dae..807edec4e9 100644 --- a/lms/djangoapps/support/static/support/jsx/entitlements/data/api/client.js +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/api/client.js @@ -9,7 +9,7 @@ const HEADERS = { 'X-CSRFToken': Cookies.get('csrftoken'), }; -const requestEntitlements = ({ username }) => fetch( +const getEntitlements = username => fetch( `${entitlementApi}/?user=${username}`, { credentials: 'same-origin', method: 'get', @@ -54,7 +54,7 @@ const updateEntitlement = ({ entitlementUuid, unenrolledRun, action, comments }) export { - requestEntitlements, + getEntitlements, createEntitlement, updateEntitlement, }; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/entitlements.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/entitlements.js new file mode 100644 index 0000000000..1707dcf3bd --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/entitlements.js @@ -0,0 +1,12 @@ +import { entitlementActions } from '../actions/constants'; + +const entitlements = (state = [], action) => { + switch (action.type) { + case entitlementActions.fetch.SUCCESS: + return action.entitlements; + default: + return state; + } +}; + +export default entitlements; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/error.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/error.js new file mode 100644 index 0000000000..f640f3ed94 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/error.js @@ -0,0 +1,15 @@ +import { errorActions, entitlementActions } from '../actions/constants'; + +const error = (state = '', action) => { + switch (action.type) { + case errorActions.DISPLAY_ERROR: + return action.error; + case errorActions.DISMISS_ERROR: + case entitlementActions.fetch.SUCCESS: + return ''; + default: + return state; + } +}; + +export default error; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/index.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/index.js new file mode 100644 index 0000000000..71f1ad1bed --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/reducers/index.js @@ -0,0 +1,8 @@ +import { combineReducers } from 'redux'; + +import entitlements from './entitlements'; +import error from './error'; + +const rootReducer = combineReducers({ entitlements, error }); + +export default rootReducer; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/data/store.js b/lms/djangoapps/support/static/support/jsx/entitlements/data/store.js new file mode 100644 index 0000000000..15d64ddb30 --- /dev/null +++ b/lms/djangoapps/support/static/support/jsx/entitlements/data/store.js @@ -0,0 +1,21 @@ +import { createStore, applyMiddleware } from 'redux'; +import thunkMiddleware from 'redux-thunk'; + +import rootReducer from './reducers/index'; + +const defaultState = { + entitlements: [], + error: '', +}; + +const configureStore = initialState => + createStore( + rootReducer, + initialState, + applyMiddleware(thunkMiddleware), + ); + + +const store = configureStore(defaultState); + +export default store; diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx b/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx index ce5b7487cc..a024373726 100644 --- a/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx +++ b/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx @@ -1,9 +1,12 @@ import React from 'react'; -const EntitlementSupportPage = () => ( -
- Base Entitlement Support Page -
-); +import { Provider } from 'react-redux'; +import store from './data/store'; -export default EntitlementSupportPage; +import MainContainer from './components/Main/MainContainer.jsx'; + +export const EntitlementSupportPage = props => ( + + + +); diff --git a/lms/templates/support/entitlement.html b/lms/templates/support/entitlement.html index f193160f4c..71c03d930d 100644 --- a/lms/templates/support/entitlement.html +++ b/lms/templates/support/entitlement.html @@ -26,7 +26,7 @@ from openedx.core.djangolib.js_utils import js_escaped_string id="entitlement-support-page", props={ 'ecommerceUrl': ecommerce_url, - 'supportReasons': support_actions + 'supportActions': support_actions } ) } diff --git a/package.json b/package.json index e2ea065c90..3dcc15c0f9 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,10 @@ "raw-loader": "0.5.1", "react": "16.1.0", "react-dom": "16.1.0", + "react-redux": "5.0.7", "react-slick": "0.16.0", + "redux": "3.7.2", + "redux-thunk": "2.2.0", "requirejs": "2.3.5", "rtlcss": "2.2.1", "sass-loader": "6.0.6",