Commit d468231c authored by ZeHiro's avatar ZeHiro
Browse files

Add routing to the client

parent f257255c
......@@ -52,9 +52,7 @@ class Budget extends React.Component {
let periodDate = { year: this.state.year, month: this.state.month };
let fromDate = moment(periodDate).toDate();
let toDate = moment(periodDate).endOf('month').toDate();
this.props.showOperations(catId, fromDate, toDate);
this.props.mainApp.setState({ showing: 'reports' });
}
render() {
......@@ -85,6 +83,7 @@ class Budget extends React.Component {
updateCategory={ this.props.updateCategory }
showOperations={ this.showOperations }
displayInPercent={ this.state.displayInPercent }
currentAccountId={ this.props.currentAccountId }
/>);
});
......@@ -215,9 +214,6 @@ class Budget extends React.Component {
}
Budget.propTypes = {
// The mainApp component.
mainApp: React.PropTypes.object.isRequired,
// The list of categories.
categories: React.PropTypes.array.isRequired,
......@@ -235,8 +231,9 @@ Budget.propTypes = {
periods: React.PropTypes.array.isRequired
};
const Export = connect(state => {
let operations = get.currentOperations(state);
const Export = connect((state, ownProps) => {
let currentAccountId = ownProps.match.params.currentAccountId;
let operations = get.operationsByAccountIds(state, currentAccountId);
let periods = [];
if (operations.length) {
......@@ -262,7 +259,8 @@ const Export = connect(state => {
return {
categories: get.categoriesButNone(state),
operations,
periods
periods,
currentAccountId
};
}, dispatch => {
return {
......
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { actions } from '../../store';
import {
round2
......@@ -28,6 +32,7 @@ const BudgetListItem = props => {
const handleViewOperations = () => {
props.showOperations(props.cat.id);
props.showSearchDetails();
};
let { cat: category, amount } = props;
......@@ -176,10 +181,13 @@ const BudgetListItem = props => {
{ remainingText }
</td>
<td className="hidden-xs">
<button
className="btn btn-sm btn-info fa fa-search"
onClick={ handleViewOperations }
/>
<Link
to={ `/reports/${props.currentAccountId}` }
onClick={ handleViewOperations }>
<i
className="btn btn-sm btn-info fa fa-search"
/>
</Link>
</td>
</tr>
);
......@@ -200,7 +208,13 @@ BudgetListItem.propTypes = {
// A method to display the reports component inside the main app, pre-filled
// with the year/month and category filters.
showOperations: React.PropTypes.func.isRequired
showOperations: React.PropTypes.func.isRequired,
// A string indicating which account is active
currentAccountId: React.PropTypes.string.isRequired
};
export default BudgetListItem;
const Export = connect(null, dispatch => (
{ showSearchDetails: () => actions.toggleSearchDetails(dispatch, true) }
))(BudgetListItem);
export default Export;
import React from 'react';
import { connect } from 'react-redux';
import { Route, Switch, Redirect } from 'react-router-dom';
import { get } from '../../store';
import { assert, translate as $t } from '../../helpers';
import { translate as $t } from '../../helpers';
import InOutChart from './in-out-chart';
import BalanceChart from './balance-chart';
......@@ -11,86 +12,77 @@ import DefaultParamsModal from './default-params-modal';
import TabMenu from '../ui/tab-menu.js';
// Components
class ChartsComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
kind: props.defaultDisplay
};
this.handleTabChange = this.changeKind.bind(this);
}
changeKind(kind) {
this.setState({ kind });
}
render() {
let chartComponent = '';
switch (this.state.kind) {
case 'all': {
chartComponent = (
<OperationsByCategoryChart
operations={ this.props.operations }
const ChartsComponent = props => {
const { currentAccountId } = props.match.params;
const pathPrefix = '/charts';
let menuItems = new Map();
menuItems.set(`${pathPrefix}/all/${currentAccountId}`, $t('client.charts.by_category'));
menuItems.set(`${pathPrefix}/balance/${currentAccountId}`, $t('client.charts.balance'));
menuItems.set(`${pathPrefix}/earnings/${currentAccountId}`, $t('client.charts.differences_all'));
const { defaultDisplay, account, operations, operationsCurrentAccounts } = props;
const allChart = () => <OperationsByCategoryChart operations={ operations } />;
const balanceChart = () => (
<BalanceChart
operations={ operations }
account={ account }
/>
);
const posNegChart = () => <InOutChart operations={ operationsCurrentAccounts } />;
return (
<div className="top-panel panel panel-default">
<div className="panel-heading">
<h3 className="title panel-title">
{ $t('client.charts.title') }
</h3>
<div className="panel-options">
<span
className="option-legend fa fa-cog"
title={ $t('client.general.default_parameters') }
data-toggle="modal"
data-target="#defaultParams"
/>
);
break;
}
case 'balance': {
chartComponent = (
<BalanceChart
operations={ this.props.operations }
account={ this.props.account }
/>
);
break;
}
case 'earnings': {
chartComponent = <InOutChart operations={ this.props.operationsCurrentAccounts } />;
break;
}
default: assert(false, 'unexpected chart kind');
}
let menuItems = new Map();
menuItems.set('all', $t('client.charts.by_category'));
menuItems.set('balance', $t('client.charts.balance'));
menuItems.set('earnings', $t('client.charts.differences_all'));
return (
<div className="top-panel panel panel-default">
<div className="panel-heading">
<h3 className="title panel-title">
{ $t('client.charts.title') }
</h3>
<div className="panel-options">
<span
className='option-legend fa fa-cog'
title={ $t('client.general.default_parameters') }
data-toggle="modal"
data-target='#defaultParams'
/>
</div>
<DefaultParamsModal modalId='defaultParams' />
</div>
<DefaultParamsModal modalId='defaultParams' />
</div>
<div className="panel-body">
<TabMenu
onChange={ this.handleTabChange }
defaultValue={ this.state.kind }
tabs={ menuItems }
/>
<div className="tab-content">
{ chartComponent }
</div>
<div className="panel-body">
<TabMenu
selected={ props.location.pathname }
tabs={ menuItems }
history={ props.history }
location={ props.location }
/>
<div className="tab-content">
<Switch>
<Route
path={ `${pathPrefix}/all/${currentAccountId}` }
component={ allChart }
/>
<Route
path={ `${pathPrefix}/balance/${currentAccountId}` }
component={ balanceChart }
/>
<Route
path={ `${pathPrefix}/earnings/${currentAccountId}` }
component={ posNegChart }
/>
<Redirect
to={ `${pathPrefix}/${defaultDisplay}/${currentAccountId}` }
push={ false }
/>
</Switch>
</div>
</div>
);
}
}
</div>
);
};
ChartsComponent.propTypes = {
// The kind of chart to display: by categories, balance, or in and outs for all accounts.
......@@ -103,16 +95,25 @@ ChartsComponent.propTypes = {
operations: React.PropTypes.array.isRequired,
// The operations for the current accounts.
operationsCurrentAccounts: React.PropTypes.array.isRequired
operationsCurrentAccounts: React.PropTypes.array.isRequired,
// The history object, providing access to the history API.
// Automatically added by the Route component.
history: React.PropTypes.object.isRequired,
// Location object (contains the current path). Automatically added by react-router.
location: React.PropTypes.object.isRequired
};
const Export = connect(state => {
const Export = connect((state, ownProps) => {
let accountId = ownProps.match.params.currentAccountId;
let account = get.accountById(state, accountId);
let currentAccessId = account.bankAccess;
// FIXME find a more efficient way to do this.
let currentAccounts = get.currentAccounts(state).map(account => account.id);
let currentAccounts = get.accountsByAccessId(state, currentAccessId).map(acc => acc.id);
let operationsCurrentAccounts = get.operationsByAccountIds(state, currentAccounts);
let account = get.currentAccount(state);
let operations = get.currentOperations(state);
let operations = get.operationsByAccountIds(state, accountId);
let defaultDisplay = get.setting(state, 'defaultChartDisplayType');
......
......@@ -71,9 +71,10 @@ function computePrevNextThreshold(current) {
return [previousThreshold, nextThreshold];
}
export default connect(state => {
let currentOperations = get.currentOperations(state);
let formatCurrency = get.currentAccount(state).formatCurrency;
export default connect((state, props) => {
let { currentAccountId } = props.match.params;
let currentOperations = get.operationsByAccountIds(state, currentAccountId);
let formatCurrency = get.accountById(state, currentAccountId).formatCurrency;
let duplicateThreshold = parseFloat(get.setting(state, 'duplicateThreshold'));
......
import React from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import { translate as $t } from '../../helpers';
import ImportModule from '../settings/backup/import';
import NewBankForm from '../settings/bank-accesses/form';
import WeboobParameters from '../settings/weboob';
import TabMenu from '../ui/tab-menu.js';
export default props => {
let pathPrefix = '/initialize';
let menuItems = new Map();
menuItems.set(`${pathPrefix}/new-bank`, $t('client.settings.new_bank_form_title'));
menuItems.set(`${pathPrefix}/import`, $t('client.accountwizard.import_title'));
menuItems.set(`${pathPrefix}/advanced`, $t('client.accountwizard.advanced'));
const renderBankForm = () => <NewBankForm expanded={ true } />;
const renderImport = () => (
<div>
<p>
{ $t('client.accountwizard.import') }
</p>
<ImportModule />
</div>
);
export default () => {
return (
<div className="wizard panel panel-default">
<div className="panel-heading">
......@@ -18,51 +37,30 @@ export default () => {
<p>
{ $t('client.accountwizard.content') }
</p>
<ul className="nav nav-tabs">
<li className="active">
<a
href="#bank_form"
data-toggle="tab">
{ $t('client.settings.new_bank_form_title') }
</a>
</li>
<li>
<a
href="#import"
data-toggle="tab">
{ $t('client.accountwizard.import_title') }
</a>
</li>
<li>
<a
href="#advanced"
data-toggle="tab">
{ $t('client.accountwizard.advanced') }
</a>
</li>
</ul>
<div className="tab-content">
<div
className="tab-pane active"
id="bank_form">
<NewBankForm expanded={ true } />
</div>
<div
className="tab-pane"
id="import">
<p>
{ $t('client.accountwizard.import') }
</p>
<ImportModule />
</div>
<div
className="tab-pane"
id="advanced">
<WeboobParameters />
</div>
</div>
<TabMenu
selected={ props.location.pathname }
tabs={ menuItems }
history={ props.history }
location={ props.location }
/>
<Switch>
<Route
path={ `${pathPrefix}/new-bank` }
render={ renderBankForm }
/>
<Route
path={ `${pathPrefix}/import` }
render={ renderImport }
/>
<Route
path={ `${pathPrefix}/advanced` }
component={ WeboobParameters }
/>
<Redirect
to={ `${pathPrefix}/new-bank` }
push={ false }
/>
</Switch>
</div>
</div>
);
......
import React from 'react';
import { connect } from 'react-redux';
import { actions } from '../../store';
import { NavLink } from 'react-router-dom';
const AccountListItem = props => {
let account = props.account;
let { account, location } = props;
let total = props.balance;
let color = total >= 0 ? 'positive' : 'negative';
let currentPathname = location.pathname;
let currentAccountId = props.currentAccountId;
let newPathname = currentPathname.replace(currentAccountId, account.id);
return (
<li
key={ `account-details account-list-item-${account.id}` }
className={ props.active ? 'active' : '' }>
<a
href="#"
onClick={ props.handleClick }>
<span>
{ account.title }
</span>
<span className={ `amount ${color}` }>
{ account.formatCurrency(parseFloat(total.toFixed(2))) }
</span>
</a>
key={ `account-details-account-list-item-${account.id}` }>
<div>
<NavLink
to={ newPathname }
activeClassName="active">
<span>
{ account.title }
</span>
<span className={ `amount ${color}` }>
{ account.formatCurrency(parseFloat(total.toFixed(2))) }
</span>
</NavLink>
</div>
</li>
);
};
......@@ -33,14 +35,9 @@ AccountListItem.propTypes = {
// the account balance
balance: React.PropTypes.number,
// Whether the account is the current account selected
active: React.PropTypes.bool.isRequired
// The location object containing the current path.
// Needed to rerender the accounts links on route change
location: React.PropTypes.object.isRequired
};
export default connect(null, (dispatch, props) => {
return {
handleClick: () => {
actions.setCurrentAccountId(dispatch, props.account.id);
}
};
})(AccountListItem);
export default AccountListItem;
......@@ -52,8 +52,9 @@ class BankListItemComponent extends React.Component {
<AccountListItem
key={ acc.id }
account={ acc }
location={ this.props.location }
currentAccountId={ this.props.currentAccountId }
balance={ this.props.accountsBalances.get(acc.id) }
active={ this.props.activeAccountId === acc.id }
/>
));
}
......@@ -66,12 +67,12 @@ class BankListItemComponent extends React.Component {
className={ this.props.active ? 'active' : '' }>
<div className={ `icon icon-${this.props.access.uuid}` } />
<div className="bank-name">
<a
href="#"
<div
className="clickable"
onClick={ this.handleClick }>
<span>{ this.props.access.name }</span>
<span className={ `bank-details-toggle fa fa-${stateLabel}-square` } />
</a>
</div>
<p className="bank-sum">
<span>Total</span>
{ totalElement }
......@@ -90,7 +91,11 @@ BankListItemComponent.propTypes = {
access: React.PropTypes.object.isRequired,
// Whether the bank is the current bank selected
active: React.PropTypes.bool.isRequired
active: React.PropTypes.bool.isRequired,
// The location object containing the current path.
// Needed to rerender the accounts links on route change
location: React.PropTypes.object.isRequired
};
const Export = connect((state, props) => {
......@@ -125,9 +130,8 @@ const Export = connect((state, props) => {
accounts,
accountsBalances,
total,
totalPositive,
activeAccountId: get.currentAccountId(state)
totalPositive
};
}, null)(BankListItemComponent);
})(BankListItemComponent);
export default Export;
......@@ -5,14 +5,16 @@ import { get } from '../../store';
import BankListItemComponent from './bank';
const BankListComponent = props => {
let { currentAccessId, currentAccountId } = props;
let banks = props.accesses.map(access => {
let isActive = props.active === access.id;
let isActive = currentAccessId === access.id;
return (
<BankListItemComponent
key={ access.id }
access={ access }
active={ isActive }
location={ props.location }
currentAccountId={ currentAccountId }
/>
);
});
......@@ -29,13 +31,20 @@ BankListComponent.propTypes = {
accesses: React.PropTypes.array.isRequired,
// The id of the current access
active: React.PropTypes.string.isRequired
currentAccessId: React.PropTypes.string.isRequired,
// The location object containing the current path.
// Needed to rerender the accounts links on route change
location: React.PropTypes.object.isRequired
};
const Export = connect(state => {
const Export = connect((state, oldProps) => {
let access = get.accessByAccountId(state, oldProps.currentAccountId);
let currentAccessId = access !== null ? access.id : '';
return {
accesses: get.accesses(state),
active: get.currentAccessId(state)
currentAccessId
};
})(BankListComponent);
......
import React from 'react';
import { NavLink } from 'react-router-dom';
import { connect } from 'react-redux';
import { translate as $t } from '../../helpers';
import { get } from '../../store';
import About from './about';
import BankList from './banks';
const Menu = props => {
let { currentAccountId } = props.match.params;
const determineSubsection = (section, defaultSubsection) => {
if (props.match.params.section === section &&
typeof props.match.params.subsection !== 'undefined') {
return props.match.params.subsection;
}
return defaultSubsection;
};