Skip to content
This repository has been archived by the owner on Mar 25, 2018. It is now read-only.

POLY2_WEB_HOUR1 #13

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions RELAY_WEB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Part 3 (web)
What a shame to our people, humans did not know us before our arrival on earth! Luckily, we hired a political attaché specializing in the image of foreign peoples. With all the craze for the cryptocurrencies, our political commitment suggests developing a web platform to exchange PGG "PenGuinGold" against CAD.

```
Poly2
Julien Dufresne
Philipe Kavalec
Vincent Labonte
```

```
how to build/run your docker image
how to run your tests
localhost:????/mySuperDupeApp
(optional) yourPublicCloudUrl
```

**Languages/Frameworks**: React, Jest, ES6, Webpack, Enzyme

# Instructions
- [ ] one page application
- [ ] the state should change without page refresh
- [ ] the app will be "in memory" you can either only change the state of the application.
- [ ] you need at least one wallet per currency you want to trade
- [ ] you can simulate that your user is already logged in your application initial state

## Bonus points (18pts)
- [ ] Deploy on a public cloud. (ex: Heroku) (0.5 pt)
- [ ] Public CI pipeline (ex: CircleCi is available for public repositories for free)
*(add link to your pipeline in the readme.md)
- [ ] build (1pt)
- [ ] run tests (1pt)
- [ ] deploy from a pipeline on public cloud service (ex: Heroku) (0.5 pt)
- [ ] Unit Test
- [ ] branches coverages
- required how to run in readme.md
- (total number of features done * % branch coverage)/2
- (Corrector reserve the right to consider a test as integration test instead of unit tests. tip (shallow render))
- [ ] Mobile responsiveness (2pt)
8 changes: 8 additions & 0 deletions RELAY_WEB_HOUR_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hour 1
_Give access to trades.json, accounts.json_
- [ ] (1pt) Create wallet in an account
- [ ] (1pt) Display wallets of an account
- [ ] (1pt) Deposit PGG or CAD to a wallet
- [ ] (1pt) Display wallet currency and amount
- [ ] (1pt) Display the PGG/CAD current price (price of the latest trade)
- [ ] (2pt) Display your account value CAD + PGG of all wallets (need to have a current PGG/CAD price)
9 changes: 5 additions & 4 deletions app/api/fakeFetch.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import wallets from './wallets.json';
import accounts from './account.json';

const delay = (ms) => (
new Promise((resolve) => setTimeout(resolve, ms))
Expand All @@ -8,12 +9,12 @@ export const fakeFetch = (urlGetter, options) => (
delay(300).then(() => {
//ugly hack to create immutable copy of file.
const walletsCopied = JSON.parse(JSON.stringify(wallets));
const accountsCopied = JSON.parse(JSON.stringify(accounts));
switch (urlGetter) {
case "/api/wallets":
switch (true) {
default:
return walletsCopied;
}
return walletsCopied;
case "/api/accounts":
return accountsCopied;
default:
throw new Error('Unknown urlGetter:');
}
Expand Down
7 changes: 7 additions & 0 deletions app/containers/Account/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LOAD_ACCOUNTS } from './constants';

export function loadAccounts() {
return {
type: LOAD_ACCOUNTS,
};
}
2 changes: 2 additions & 0 deletions app/containers/Account/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const LOAD_ACCOUNTS = 'crypto_exchange/App/LOAD_ACCOUNTS';
export const ACCOUNTS = 'accounts';
78 changes: 78 additions & 0 deletions app/containers/Account/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { withStyles } from 'material-ui/styles/index';

import { createStructuredSelector } from 'reselect';
import { loadAccounts } from './actions';
import injectReducer from '../../utils/injectReducer';
import injectSaga from '../../utils/injectSaga';
import { ACCOUNTS } from './constants';
import reducer from './reducer';
import saga from './saga';
import {makeSelectAccounts, selectAccounts} from './selectors';
import { makeSelectLoading } from '../../asyncDisplayer/containers/IsLoading/selectors';
import { makeSelectError } from '../../asyncDisplayer/containers/HasError/selectors';
import LoadingError from '../../asyncDisplayer/components/LoadingError';


const styles = () => ({
test: {
border: '2px solid',
},
});

class Account extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div>
<LoadingError
loading={this.props.accountsLoading}
error={this.props.accountsError}
errorNode={<p>error</p>}
>
<div>
<button onClick={this.props.onLoadAccounts} className={this.props.classes.test}>
click here to load accounts
</button>
{this.props.accounts.map((account, index) => (
<h3>Username {account.username}:</h3>
))}
</div>
</LoadingError>
</div>
);
}
}

export const mapStateToProps = createStructuredSelector({
accounts: makeSelectAccounts(),
accountsLoading: makeSelectLoading(selectAccounts),
accountsError: makeSelectError(selectAccounts),
});

export const mapDispatchToProps = (dispatch, ownProps) => ({
onLoadAccounts: (evt) => {
if (evt !== undefined && evt.preventDefault) evt.preventDefault();
dispatch(loadAccounts());
},
});

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withSurveyReducer = injectReducer({
key: ACCOUNTS,
reducer,
});

const withSurveySaga = injectSaga({
key: ACCOUNTS,
saga,
});


export default compose(
withSurveyReducer,
withSurveySaga,
withConnect,
)(withStyles(styles)(Account));
33 changes: 33 additions & 0 deletions app/containers/Account/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { fromJS } from 'immutable';
import { combineReducers } from 'redux-immutable';

import {LOAD_ACCOUNTS } from './constants';
import isLoadingReducer from '../../asyncDisplayer/containers/IsLoading/reducer';
import hasErrorReducer from '../../asyncDisplayer/containers/HasError/reducer';
import { SUCCESS } from '../../asyncDisplayer/containers/constants';



export const ACCOUNTS_REDUCER_INITIAL_STATE = fromJS({
accounts: []
});

function accountReducer(state = ACCOUNTS_REDUCER_INITIAL_STATE, action) {
switch (action.type) {
case `${LOAD_ACCOUNTS}${SUCCESS}`:
return state
.set('accounts', fromJS(action.entity));
default:
return state;
}
}

const options = {
action: LOAD_ACCOUNTS,
};

export default combineReducers({
entity: accountReducer,
isLoading: isLoadingReducer(options),
hasError: hasErrorReducer(options),
});
22 changes: 22 additions & 0 deletions app/containers/Account/saga.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { call, put, takeLatest } from 'redux-saga/effects';

import request from '../../api/request';
import { errorAction, successAction } from '../../asyncDisplayer/containers/actions';
import { LOAD_ACCOUNTS } from './constants';

export function* fetchAccounts(url, data) {
try {
const accounts = yield call(request, url, data);
yield put(successAction(LOAD_ACCOUNTS, accounts));
} catch (err) {
yield put(errorAction(LOAD_ACCOUNTS, err));
}
}

export function* getAccounts(data) {
yield fetchAccounts("/api/accounts", data);
}

export default function* instrumentResultData() {
yield takeLatest(LOAD_ACCOUNTS, getAccounts);
}
13 changes: 13 additions & 0 deletions app/containers/Account/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createSelector } from 'reselect';

const selectAccounts = (state) => state.get('accounts');

const makeSelectAccounts = () => createSelector(
selectAccounts,
(state) => state.getIn(['entity', 'accounts']).toJS()
);

export {
selectAccounts,
makeSelectAccounts,
};
7 changes: 2 additions & 5 deletions app/containers/HomePage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,13 @@
*/

import React from 'react';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import Wallet from '../Wallet';
import Account from '../Account';

export default class HomePage extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div>
<FormattedMessage {...messages.header} />
<Wallet />
<Account/>
</div>
);
}
Expand Down