From 710487ebf9a064cb566ffe266feea6017ab9fda4 Mon Sep 17 00:00:00 2001 From: "Andrew Revinsky (DART)" Date: Fri, 18 Mar 2016 04:20:46 +0300 Subject: [PATCH] Account Detail page --- js-frontend/gulpfile.babel.js | 8 +- js-frontend/src/App.js | 11 +- js-frontend/src/actions/entities.js | 17 +++ js-frontend/src/components/AuthComponent.js | 2 +- js-frontend/src/components/HeaderLinks.js | 4 +- js-frontend/src/constants/ACTION_TYPES.js | 6 ++ .../src/controls/bootstrap/EmailSignInForm.js | 2 +- .../src/controls/bootstrap/EmailSignUpForm.js | 2 +- js-frontend/src/reducers/data/entities.js | 12 +++ js-frontend/src/reducers/data/index.js | 4 +- js-frontend/src/reducers/index.js | 16 +++ js-frontend/src/reducers/ui/account.js | 42 ++++++++ js-frontend/src/reducers/ui/index.js | 15 +++ js-frontend/src/utils/api.js | 6 +- js-frontend/src/views/Account.js | 102 ++++++++++++++---- js-frontend/src/views/MyAccounts.js | 5 +- js-frontend/src/views/SignIn.js | 4 +- 17 files changed, 216 insertions(+), 42 deletions(-) create mode 100644 js-frontend/src/reducers/index.js create mode 100644 js-frontend/src/reducers/ui/account.js create mode 100644 js-frontend/src/reducers/ui/index.js diff --git a/js-frontend/gulpfile.babel.js b/js-frontend/gulpfile.babel.js index 7b07872..6bec754 100644 --- a/js-frontend/gulpfile.babel.js +++ b/js-frontend/gulpfile.babel.js @@ -89,19 +89,19 @@ gulp.task('serve:start', ['serve:static'], () => { //} proxy: { - '/user' : { + '/user*' : { target: 'http://localhost:8080' }, '/login' : { target: 'http://localhost:8080' }, - '/customers' : { + '/customers*' : { target: 'http://localhost:8080' }, - '/accounts' : { + '/accounts*' : { target: 'http://localhost:8080' }, - '/transfers' : { + '/transfers*' : { target: 'http://localhost:8080' } } diff --git a/js-frontend/src/App.js b/js-frontend/src/App.js index 6973d75..31dcd63 100644 --- a/js-frontend/src/App.js +++ b/js-frontend/src/App.js @@ -17,8 +17,10 @@ import { ReduxRouter} from "redux-router"; //import { configure as reduxAuthConfigure, authStateReducer } from "redux-auth"; //import { authStateReducer } from "redux-auth"; -import authStateReducer from './reducers/auth'; -import appStateReducer from './reducers/data' +//import authStateReducer from './reducers/auth'; +//import appStateReducer from './reducers/data'; +import mainReducer from './reducers'; + import { configure as reduxAuthConfigure } from './actions/configure'; //import { AuthGlobals } from "redux-auth/bootstrap-theme"; @@ -36,13 +38,11 @@ import Account from "./views/Account"; import SignIn from "./views/SignIn"; import SignUp from "./views/SignUp"; //import GlobalComponents from "./views/partials/GlobalComponents"; -const AuthGlobals = () => (
); class App extends React.Component { render() { return ( - {this.props.children} ); @@ -52,8 +52,7 @@ class App extends React.Component { export function initialize({cookies, isServer, currentLocation, userAgent} = {}) { const reducer = combineReducers({ - auth: authStateReducer, - app: appStateReducer, + app: mainReducer, router: routerStateReducer }); diff --git a/js-frontend/src/actions/entities.js b/js-frontend/src/actions/entities.js index 5affcd5..d684d21 100644 --- a/js-frontend/src/actions/entities.js +++ b/js-frontend/src/actions/entities.js @@ -27,6 +27,10 @@ export const accountRefCreateComplete = makeActionCreator(T.ACCOUNTS.CREATE_REF_ export const accountRefCreateError = makeActionCreator(T.ACCOUNTS.CREATE_REF_ERROR, 'error'); export const accountRefCreateFormUpdate = makeActionCreator(T.ACCOUNTS.CREATE_REF_FORM_UPDATE, 'key', 'value'); +export const accountRequested = makeActionCreator(T.ACCOUNT.SINGLE_START); +export const accountComplete = makeActionCreator(T.ACCOUNT.SINGLE_COMPLETE, 'payload'); +export const accountError = makeActionCreator(T.ACCOUNT.SINGLE_ERROR, 'error'); + export function accountsList(userId) { return dispatch => { @@ -70,4 +74,17 @@ export function fetchOwnAccounts(customerId) { dispatch(accountsListReceived(data)); }); }; +} + +export function fetchAccount(accountId) { + return dispatch => { + dispatch(accountRequested()); + return api.apiRetrieveAccount(accountId) + .then(data => { + dispatch(accountComplete(data)); + }) + .catch(err => { + dispatch(accountError(err)); + }); + }; } \ No newline at end of file diff --git a/js-frontend/src/components/AuthComponent.js b/js-frontend/src/components/AuthComponent.js index ecea095..64d288d 100644 --- a/js-frontend/src/components/AuthComponent.js +++ b/js-frontend/src/components/AuthComponent.js @@ -50,7 +50,7 @@ export function requireAuthentication(Component) { //token: state.auth.token, //userName: state.auth.userName, //isAuthenticated: state.auth.isAuthenticated - isAuthenticated: state.auth.user.isSignedIn + isAuthenticated: state.app.auth.user.isSignedIn }) }; diff --git a/js-frontend/src/components/HeaderLinks.js b/js-frontend/src/components/HeaderLinks.js index 3f106b0..c5dbc27 100644 --- a/js-frontend/src/components/HeaderLinks.js +++ b/js-frontend/src/components/HeaderLinks.js @@ -57,9 +57,9 @@ export class HeaderLinks extends React.Component { export default connect(({ //dispatch, router, - auth + app }) => ({ //dispatch, router, - auth + auth: app.auth }))(HeaderLinks); diff --git a/js-frontend/src/constants/ACTION_TYPES.js b/js-frontend/src/constants/ACTION_TYPES.js index 9570587..cf2c11c 100644 --- a/js-frontend/src/constants/ACTION_TYPES.js +++ b/js-frontend/src/constants/ACTION_TYPES.js @@ -44,6 +44,12 @@ export default defineActionTypes({ CREATE_REF_COMPLETE CREATE_REF_ERROR CREATE_REF_FORM_UPDATE + `, + + ACCOUNT: ` + SINGLE_START + SINGLE_COMPLETE + SINGLE_ERROR ` }) diff --git a/js-frontend/src/controls/bootstrap/EmailSignInForm.js b/js-frontend/src/controls/bootstrap/EmailSignInForm.js index bd75e17..a990822 100644 --- a/js-frontend/src/controls/bootstrap/EmailSignInForm.js +++ b/js-frontend/src/controls/bootstrap/EmailSignInForm.js @@ -96,4 +96,4 @@ class EmailSignInForm extends React.Component { } } -export default connect(({auth}) => ({auth}))(EmailSignInForm); \ No newline at end of file +export default connect(({app}) => ({auth: app.auth}))(EmailSignInForm); \ No newline at end of file diff --git a/js-frontend/src/controls/bootstrap/EmailSignUpForm.js b/js-frontend/src/controls/bootstrap/EmailSignUpForm.js index d7b0079..df0d634 100644 --- a/js-frontend/src/controls/bootstrap/EmailSignUpForm.js +++ b/js-frontend/src/controls/bootstrap/EmailSignUpForm.js @@ -205,4 +205,4 @@ class EmailSignUpForm extends React.Component { } } -export default connect(({auth}) => ({auth}))(EmailSignUpForm); \ No newline at end of file +export default connect(({app}) => ({auth: app.auth}))(EmailSignUpForm); \ No newline at end of file diff --git a/js-frontend/src/reducers/data/entities.js b/js-frontend/src/reducers/data/entities.js index 92c596c..8cad2c8 100644 --- a/js-frontend/src/reducers/data/entities.js +++ b/js-frontend/src/reducers/data/entities.js @@ -52,6 +52,18 @@ export const entities = (state = {...initialState}, action) => { ...hashMap }; } + + case T.ACCOUNT.SINGLE_COMPLETE: { + const { payload = {} } = action; + const { accountId } = payload; + if (!accountId) { + return state; + } + return { + ...state, + [accountId]: payload + }; + } case T.ENTITIES.RECEIVED_LIST: default: return state; diff --git a/js-frontend/src/reducers/data/index.js b/js-frontend/src/reducers/data/index.js index 2a46597..d43d9cc 100644 --- a/js-frontend/src/reducers/data/index.js +++ b/js-frontend/src/reducers/data/index.js @@ -11,9 +11,9 @@ import { transfers } from './transfers'; import { entities } from './entities'; const dataReducer = combineReducers({ - accounts, transfers, - entities + entities, + accounts }); export default dataReducer; \ No newline at end of file diff --git a/js-frontend/src/reducers/index.js b/js-frontend/src/reducers/index.js new file mode 100644 index 0000000..305915f --- /dev/null +++ b/js-frontend/src/reducers/index.js @@ -0,0 +1,16 @@ +/** + * Created by andrew on 18/03/16. + */ +import { combineReducers } from 'redux'; + +import authStateReducer from './auth'; +import appStateReducer from './data' +import uiReducer from './ui' + +const mainReducer = combineReducers({ + auth: authStateReducer, + data: appStateReducer, + ui: uiReducer +}); + +export default mainReducer; \ No newline at end of file diff --git a/js-frontend/src/reducers/ui/account.js b/js-frontend/src/reducers/ui/account.js new file mode 100644 index 0000000..ef03518 --- /dev/null +++ b/js-frontend/src/reducers/ui/account.js @@ -0,0 +1,42 @@ +/** + * Created by andrew on 15/03/16. + */ +/** + * Created by andrew on 15/03/16. + */ +import T from '../../constants/ACTION_TYPES'; +import { combineReducers } from 'redux'; + + +const initialState = { + loading: false, + errors: [] +}; + +export const account = (state = { ...initialState }, action ) => { + switch(action.type) { + case T.ACCOUNT.SINGLE_START: { + return { + ...state, + loading: true + }; + } + case T.ACCOUNT.SINGLE_COMPLETE: { + return { + ...initialState + }; + } + case T.ACCOUNT.SINGLE_ERROR: { + const { error } = action; + return { + ...state, + loading: false, + errors: [ error ] + }; + + } + + default: + return state; + } +}; diff --git a/js-frontend/src/reducers/ui/index.js b/js-frontend/src/reducers/ui/index.js new file mode 100644 index 0000000..16fd213 --- /dev/null +++ b/js-frontend/src/reducers/ui/index.js @@ -0,0 +1,15 @@ +/** + * Created by andrew on 15/03/16. + */ +/** + * Created by andrew on 25/02/16. + */ +import { combineReducers } from 'redux'; + +import { account } from './account'; + +const uiReducer = combineReducers({ + account +}); + +export default uiReducer; \ No newline at end of file diff --git a/js-frontend/src/utils/api.js b/js-frontend/src/utils/api.js index 8b4cb37..7306710 100644 --- a/js-frontend/src/utils/api.js +++ b/js-frontend/src/utils/api.js @@ -68,7 +68,9 @@ export function apiCreateAccount(customerId, { export function apiRetrieveAccounts(customerId) { const params = {customerId }; - const query = Object.keys(params).map(key => [encodeURIComponent(key), encodeURIComponent(params[key])].join('=')).join('&') + const query = Object.keys(params).map(key => [encodeURIComponent(key), encodeURIComponent(params[key])].join('=')).join('&'); + + return fetch(`${getAccountsUrl()}?${query}`, { headers: { "Accept": "application/json", @@ -82,7 +84,7 @@ export function apiRetrieveAccounts(customerId) { } export function apiRetrieveAccount(accountId) { - return fetch(`${getCurrentUserUrl()}/${accountId}`, { + return fetch(`${getAccountsUrl()}/${accountId}`, { headers: { "Accept": "application/json", "Content-Type": "application/json" diff --git a/js-frontend/src/views/Account.js b/js-frontend/src/views/Account.js index 243bcd2..3b7efb6 100644 --- a/js-frontend/src/views/Account.js +++ b/js-frontend/src/views/Account.js @@ -6,16 +6,18 @@ import React from "react"; import { connect } from "react-redux"; import { PageHeader, OverlayTrigger, Tooltip, Grid, Col, Row, Nav, NavItem, ButtonGroup, Button, Table } from "react-bootstrap"; +import * as BS from "react-bootstrap"; import Select from "react-select"; +import Spinner from "react-loader"; import { Link, IndexLink} from "react-router"; + + import IndexPanel from "./../components/partials/IndexPanel"; import * as Modals from './modals'; - +import * as A from '../actions/entities'; const resetModals = { - showAccountModal: false, - show3rdPartyAccountModal: false, - showDeleteAccountModal: false + showAccountModal: false }; export class Account extends React.Component { @@ -24,6 +26,17 @@ export class Account extends React.Component { this.state = { ...resetModals }; } + componentWillMount() { + const { + id: customerId + } = this.props.auth.user.attributes; + this.props.dispatch(A.fetchOwnAccounts(customerId)); + + const { dispatch, params } = this.props; + const { accountId } = params; + dispatch(A.fetchAccount(accountId)); + } + createAccountModal() { this.setState({ showAccountModal: true @@ -31,10 +44,12 @@ export class Account extends React.Component { } createAccountModalConfirmed() { - + debugger; } - accountChanged(){} + accountChanged(){ + + } close() { this.setState({ @@ -44,14 +59,58 @@ export class Account extends React.Component { render () { - debugger; - const { showAccountModal } = this.state; + const { params } = this.props; + const { loading, errors } = this.props.ui; + const { entities, accounts } = this.props.data; + const { accountId } = params; + + const account = entities[accountId]; + + + if (loading) { + return (

Loading..

); + } + + if (!account) { + if (errors.length) { + return (

Error loading specified account

); + } else { + return (

Loading..

); + } + } + + const transferTo = [].concat(accounts.own.reduce((memo, item, idx) => { + const { balance, title, accountId: itemAccountId } = item; + + if (itemAccountId != accountId) { + memo.push({ + value: itemAccountId , + label: `${title}: $${ Number(balance).toFixed(2) }` + }); + } + return memo; + }, []), + accounts.other.reduce((memo, item, idx) => { + if (!((item.id == accountId) || (item.accountId == accountId))) { + memo.push({ + value: item.accountId || item.id, + label: `${item.title}${ item.description ? ': ' + item.description.substr(0, 10): '' }` + }); + } + return memo; + }, [])); + + const { title = '[No title]', description: descriptionRaw, balance: balanceRaw } = account; + + const balance = ((balanceRaw > 0 && balanceRaw < 1) ? '$0' : '$') + Number(balanceRaw).toFixed(2); + + const description = descriptionRaw || '[No description provided]'; return (
- Accounts + Account