Account Detail page

This commit is contained in:
Andrew Revinsky (DART)
2016-03-18 04:20:46 +03:00
parent df0d391521
commit 710487ebf9
17 changed files with 216 additions and 42 deletions

View File

@@ -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'
}
}

View File

@@ -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 = () => (<div></div>);
class App extends React.Component {
render() {
return (
<Container>
<AuthGlobals />
{this.props.children}
</Container>
);
@@ -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
});

View File

@@ -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));
});
};
}

View File

@@ -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
})
};

View File

@@ -57,9 +57,9 @@ export class HeaderLinks extends React.Component {
export default connect(({
//dispatch,
router,
auth
app
}) => ({
//dispatch,
router,
auth
auth: app.auth
}))(HeaderLinks);

View File

@@ -44,6 +44,12 @@ export default defineActionTypes({
CREATE_REF_COMPLETE
CREATE_REF_ERROR
CREATE_REF_FORM_UPDATE
`,
ACCOUNT: `
SINGLE_START
SINGLE_COMPLETE
SINGLE_ERROR
`
})

View File

@@ -96,4 +96,4 @@ class EmailSignInForm extends React.Component {
}
}
export default connect(({auth}) => ({auth}))(EmailSignInForm);
export default connect(({app}) => ({auth: app.auth}))(EmailSignInForm);

View File

@@ -205,4 +205,4 @@ class EmailSignUpForm extends React.Component {
}
}
export default connect(({auth}) => ({auth}))(EmailSignUpForm);
export default connect(({app}) => ({auth: app.auth}))(EmailSignUpForm);

View File

@@ -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;

View File

@@ -11,9 +11,9 @@ import { transfers } from './transfers';
import { entities } from './entities';
const dataReducer = combineReducers({
accounts,
transfers,
entities
entities,
accounts
});
export default dataReducer;

View File

@@ -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;

View File

@@ -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;
}
};

View File

@@ -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;

View File

@@ -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"

View File

@@ -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 (<h2><Spinner ref="spinner" loaded={false} /> Loading..</h2>);
}
if (!account) {
if (errors.length) {
return (<h2>Error loading specified account</h2>);
} else {
return (<h2><Spinner ref="spinner" loaded={false} /> Loading..</h2>);
}
}
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 (
<div>
<PageHeader>
Accounts
Account
<Nav pullRight={true}>
<ButtonGroup>
<Button bsStyle={"link"} onClick={this.createAccountModal.bind(this)}>Edit</Button>
@@ -64,17 +123,17 @@ export class Account extends React.Component {
<Row>
<Col xs={4}>Title:</Col>
<Col xs={8}><strong>Account Title #1</strong></Col>
<Col xs={8}><strong>{ title }</strong></Col>
</Row>
<Row>
<Col xs={4}>Balance:</Col>
<Col xs={8}><strong>$100.00</strong></Col>
<Col xs={8}><strong>{ balance }</strong></Col>
</Row>
<Row>
<Col xs={4}>Description:</Col>
<Col xs={8}><strong>Savings with progressive interest (0.7%)</strong></Col>
<Col xs={8}><strong>{ description }</strong></Col>
</Row>
</IndexPanel>
@@ -91,19 +150,17 @@ export class Account extends React.Component {
<Select
value={''}
clearable={false}
options={[
{value: "default", label: "Default"},
{value: "bootstrap", label: "Bootstrap"},
{value: "materialUi", label: "Material UI"}
]}
options={transferTo}
onChange={this.accountChanged.bind(this)} />
</Col>
<Col xs={3}>
<label>Amount:</label>
<BS.Input type="text" />
</Col>
<Col xs={3}>
<label>Description:</label>
</Col>
<BS.Input type="textarea" />
</Col>
<Col xs={2}>
<br/>
<Button bsStyle="primary">Transfer</Button>
@@ -140,6 +197,7 @@ export class Account extends React.Component {
<Modals.NewAccountModal show={showAccountModal}
action={this.createAccountModalConfirmed.bind(this)}
account={{ loading: true }}
onHide={this.close.bind(this)}
key={0} />
@@ -150,4 +208,10 @@ export class Account extends React.Component {
}
}
export default connect(({auth}) => ({auth}))(Account);
export default connect(({
app
}) => ({
auth: app.auth,
data: app.data,
ui: app.ui.account
}))(Account);

View File

@@ -283,8 +283,9 @@ class MyAccounts extends React.Component {
</IndexPanel>
*/
export default connect(({ auth, app }) => {
export default connect(({ app }) => {
return ({
auth, app
auth: app.auth,
app: app.data
})
})(MyAccounts);

View File

@@ -62,9 +62,9 @@ export class SignIn extends React.Component {
export default connect(({
//dispatch,
routes,
auth
app
}) => ({
//dispatch,
routes,
auth
auth: app.auth
}))(SignIn);