Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Day4 - Dashboard read functionality #2

Merged
merged 4 commits into from Oct 21, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -92,3 +92,14 @@ one way or another redux is an implementation to observer design pattern (aka pu
- running the code will show results from server.
- resources: https://redux.js.org/advanced/asyncactions
# Day 4
1- add dashboard actions to load initial data (user profile)
2- create progress bar gauge
3- create metrics container with static data
4- try css grid to make metrics boxes responsive
5- add request builder to centralize request exit point
6- add user repo (not working yet)

This comment has been minimized.

Copy link
@blabadi

blabadi Nov 1, 2018

Author Owner

it's working forgot to update the note!

@@ -21,5 +21,10 @@
"devDependencies": {
"redux-devtools": "^3.4.1"
},
"proxy": "http://localhost:8080/"
"proxy": {
"/api": {
"target": "http://18.222.254.118:8080",
"changeOrigin" : true
}
}

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

this is to connect to aws deployed back end, the changeOrigin true is for firefox, in my case the proxy didn't work without it.

}
@@ -26,3 +26,18 @@
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

.content {
margin-top: 2em
}

.card {
/*overflow: hidden;*/
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 9px 0 rgba(0, 0, 0, 0.12);
-webkit-transition: .25s box-shadow;
transition: .25s box-shadow;
}

.card:focus, .card:hover {
box-shadow: 0 0px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 12px 0 rgba(0, 0, 0, 0.15);
}
@@ -16,9 +16,11 @@ class App extends Component {
<div className="App container">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
<h1 className="App-title">Welcome to NuTracker</h1>
</header>
<Dashboard></Dashboard>
<div className="content">
<Dashboard></Dashboard>
</div>
</div>
</Provider>
);
@@ -0,0 +1,37 @@
export default class Util {

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

helper class to host reusable non logic methods

static formatDateTime = (date) => {
const options = {
year: 'numeric', month: 'numeric', day: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric',
hour12: true,
};

return new Intl.DateTimeFormat('en-US', options).format(date);
}
static formatDate = (date) => {
const options = {
year: 'numeric', month: 'numeric', day: 'numeric'
};

return new Intl.DateTimeFormat('en-US', options).format(date);
}
static titleCase(input) {
if (!input) {
return '';
} else {
return input.replace(/\w\S*/g, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase()));
}
}

static dateToDatestamp = (date) => {
const dateMap = {};
Intl.DateTimeFormat({month : '2-digit', year: 'numeric', day: '2-digit'})
.formatToParts(date)
.forEach( ({type, value}) => {
if (type !== 'literal') {
dateMap[type] = value;
}
});
return dateMap.year + dateMap.month + dateMap.day;
}
}
@@ -0,0 +1,7 @@
export const PERIOD_CHANGED = 'PERIOD_CHANGED';

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

this action is fired when the user wants to change the day the to fetch entries fore


export const changePeriod = (from, to) => ({
type: PERIOD_CHANGED,
from,
to
});
@@ -0,0 +1,28 @@
import entryRepo from '../repos/EntryRepo';

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

this action is to do an async call to get entries for a specific period of time.

export const LOAD_ENTIRES = 'LOAD_ENTIRES';
export const RECEIVE_ENTRIES = 'RECEIVE_ENTRIES'


export const fetchEntries = (start, end) => (dispatch) => {
dispatch(loadEntries(start, end));
return entryRepo.find(start, end)
.then(entries => {
dispatch(receiveEntries(start, end, entries))
}
);
};

export const loadEntries = (start, end) => ({
type: LOAD_ENTIRES,
start,
end
});

export const receiveEntries = (start, end, entries) => ({
type: RECEIVE_ENTRIES,
start,
end,
entries
});


@@ -0,0 +1,5 @@

export const CALCULATE_METRICS = 'CALCULATE_METRICS';
export const calculateMetrics = () => ({
type: CALCULATE_METRICS
});
@@ -3,16 +3,14 @@ export const SEARCH_FOOD = 'SEARCH_FOOD';
export const RECEIVE_SEARCH_FOOD_RESULTS = 'RECEIVE_SEARCH_FOOD_RESULTS'


export const fetchFood = (term) => {
return (dispatch) => {
dispatch(searchFood);
return foodRepo.findFood(term)
.then(foods => {
dispatch(receiveSearchFoodResults(term, foods))
}
);
}
}
export const fetchFood = (term) => (dispatch) => {
dispatch(searchFood(term));
return foodRepo.findFood(term)
.then(foods => {
dispatch(receiveSearchFoodResults(term, foods))
}
);
};

export const searchFood = (term) => ({
type: SEARCH_FOOD,
@@ -0,0 +1,22 @@
import userRepo from '../repos/UserRepo'

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

this is to perform an async call to load a user profile by id

export const LOAD_USER = 'LOAD_USER';
export const RECEIVE_USER = 'RECEIVE_USER';

export const loadUser = (id) => (dispatch) => {
dispatch(loadingUser);
return userRepo.findUser(id)
.then(user => {
dispatch(receiveUser(user))
}
);
};

export const loadingUser = () => ({
type: LOAD_USER
});

export const receiveUser = (user) => ({
type: RECEIVE_USER,
user
});

@@ -1,18 +1,53 @@
import React, {Component} from 'react';
import FoodSearch from './FoodSearch';
import TargetsMetrics from './TargetsMetrics'
import { connect } from 'react-redux';
import { loadUser} from '../actions/userActions';
import DayEntries from './DayEntries';
import EntriesDateNavigator from './EntriesDateNavigator';

class Dashboard extends Component {
componentDidMount() {

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

when component is loaded and mounted we load the user information

this.loadDashboard();
}

loadDashboard() {
Promise.all([
this.props.loadUser('bashar')
]).catch(err=> console.log(err));
}

render() {
return (
<div className="row">
<div className="col-md-12">
<FoodSearch></FoodSearch>
<div className="mb-4">
Hello, {this.props.user.name}
</div>
<div className="mb-4">
<FoodSearch></FoodSearch>
</div>
<div className="mb-4">
<TargetsMetrics/>
</div>
<div className="mb-3">
<EntriesDateNavigator/>
</div>
<DayEntries/>
</div>
</div>
);
}
}

const mapDispatchToProps = dispatch => ({
loadUser: name => dispatch(loadUser(name))
})


const mapStateToProps = (state) => {
return {
user: state.user
}
}
export default Dashboard;

export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

wrapped the dashboard with a container because it evolved to call actions and receive properties and no longer stateless

@@ -0,0 +1,56 @@
import {fetchEntries} from '../actions/entriesActions'
import Entry from '../presentational/entry/Entry';
import {connect} from 'react-redux';
import React, {Component} from 'react'
import Util from '../Util';

class DayEntries extends Component {

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

new component to display a list of entries

componentDidMount() {
this.props.fetchEntries(Util.dateToDatestamp(this.props.from), Util.dateToDatestamp(this.props.to));

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

load entries on very first time this got added

}

// https://github.com/reduxjs/redux/issues/448
componentWillReceiveProps(nextProps) {
if (nextProps.from !== this.props.from && nextProps.to !== this.props.to) {
this.props.fetchEntries(Util.dateToDatestamp(nextProps.from),

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

this is to responds to changes in the date period (triggered by Date Navigator component), we reload the entries if the date period changed.

Util.dateToDatestamp(nextProps.to))
}
}

renderEntries() {
return this.props.entries.map(e => {
return (
<div key={e.id}>
<Entry entry={e} />
</div>
)
});
}

render() {
return (
<div>
{ this.props.entries.length > 0 ? this.renderEntries() : (<em>No entries yet.</em>)}
</div>
);
}


}

const mapDispatchToProps = dispatch => ({
fetchEntries: (s, e) => dispatch(fetchEntries(s, e))
})

const mapStateToProps = (state) => {
return {
entries: state.entries,
from: state.period.from,
to: state.period.to
}
}

export default connect(
mapStateToProps,
mapDispatchToProps
)(DayEntries)
@@ -0,0 +1,21 @@
import {connect} from 'react-redux';
import DateNavigator from '../presentational/date-nav/DateNavigator'
import {changePeriod} from '../actions/dateNavActions'

const mapDispatchToProps = dispatch => {
return {
onDateChanged: (from, to) => dispatch(changePeriod(from, to)),
};
};

const mapStateToProps = (state) => {
return {
from: state.period.from,
to: state.period.to
}
}

export default connect(
mapStateToProps,
mapDispatchToProps
)(DateNavigator)
@@ -3,7 +3,9 @@ import * as searchFoodActions from '../actions/searchFoodActions'
import {connect} from 'react-redux';

const mapDispatchToProps = dispatch => ({
onTermChange: term => dispatch(searchFoodActions.fetchFood(term))
onTermChange: term => {
return dispatch(searchFoodActions.fetchFood(term))
}
})

const mapStateToProps = (state) => {
No changes.
@@ -0,0 +1,43 @@
import React, {Component} from 'react'
import {connect} from 'react-redux'
import MetricsList from '../presentational/metric/MetricsList'
import {calculateMetrics} from '../actions/metricsActions'

class TargetsMetrics extends Component {

This comment has been minimized.

Copy link
@blabadi

blabadi Oct 20, 2018

Author Owner

this component to display the progress bars for each macro after calculating the totals based on the day entries.

componentWillReceiveProps() {
this.props.calculateMetrics();
}

render() {
return (<MetricsList metrics={this.props.metrics} />);
}
}

const mapStateToProps = (state) => {
if (state.user.profile && state.entries) {
const periodMetrics = {calories: 0, proteins: 0, fats: 0, carbs: 0};
state.entries.forEach((ent) => {
periodMetrics.calories += Math.round(ent.amount * ent.food.calories);
periodMetrics.carbs += Math.round(ent.amount * ent.food.carbs);
periodMetrics.fats += Math.round(ent.amount * ent.food.fat);
periodMetrics.proteins += Math.round(ent.amount * ent.food.protein);
});
return {
metrics: [
{name: 'calories', target: state.user.profile.targets.calories, current: periodMetrics.calories},
{name: 'proteins', target: state.user.profile.targets.protein, current: periodMetrics.proteins},
{name: 'fats', target: state.user.profile.targets.fats, current: periodMetrics.fats},
{name: 'carbs', target: state.user.profile.targets.carbs, current: periodMetrics.carbs}
]
}
} else {
return { metrics: [] };
}
}
const mapDispatchToProps = (dispatch) => ({
calculateMetrics
})

export default connect(
mapStateToProps, mapDispatchToProps
)(TargetsMetrics)
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.