From e69c4f4e2ee23db13f221332542364811d69ad25 Mon Sep 17 00:00:00 2001 From: Alex-Laptop Date: Thu, 21 Mar 2019 21:04:22 -0700 Subject: [PATCH] finished created main react components --- src/classes/workout.js | 27 ++++++++++------- src/components/main/main.component.js | 5 ++-- src/components/manage.row/manage.row.js | 13 +++++++-- src/components/manage/manage.component.js | 6 ++-- src/components/manage/manage.js | 13 ++++++++- src/components/recent.row/recent.row.css | 0 src/components/recent.row/recent.row.js | 21 ++++++++++++++ src/components/recent/recent.component.js | 30 +++++++++++++++++++ src/components/recent/recent.css | 7 +++++ src/components/recent/recent.js | 30 +++++++++++++++++++ src/constants.js | 1 + src/reducers/view.js | 35 +++++++++++++++++++---- src/reducers/workouts.js | 21 +++++++++++++- test/workouts.reducer.tests.js | 16 +++++++++++ 14 files changed, 199 insertions(+), 26 deletions(-) create mode 100644 src/components/recent.row/recent.row.css create mode 100644 src/components/recent/recent.component.js create mode 100644 src/components/recent/recent.css diff --git a/src/classes/workout.js b/src/classes/workout.js index c357e98..fdfef9b 100644 --- a/src/classes/workout.js +++ b/src/classes/workout.js @@ -3,19 +3,19 @@ const {DEFAULT_ATTRIBUTES} = constants; export default class workout { constructor(toClone) { - if(toClone === void(0)) { - this.attributes = {...DEFAULT_ATTRIBUTES}; - this.datesDone = []; - this.name = "New Workout"; - this.description = ""; - } else { + if((toClone !== void(0))&&(toClone instanceof workout)) { this.attributes = {...toClone.attributes}; this.datesDone = [...toClone.datesDone]; this.name = toClone.name; this.description = toClone.description; + } else { + this.attributes = {...DEFAULT_ATTRIBUTES}; + this.datesDone = []; + this.name = "New Workout"; + this.description = ""; } } - add(dates) { // add new workout to + add(dates) { // add new workout to datesDone if(!(dates instanceof Array)) { throw new TypeError('Workout::add(dates) expects parameter `dates` to be an array of dates'); } @@ -27,14 +27,15 @@ export default class workout { ...new Set([ ...this.datesDone, ...dates.map((date) => { - date = Date.parse(date); - return new Date(date.getTime() - (date.getTimezoneOffset() * 60000 )) + return new Date(date) .toISOString() .split("T")[0]; }) ]) ]; - this.datesDone.sort(); + this.datesDone.sort((a,b) => { + return a { let val; - if(data[field] === void(0)) { + if(field=="done") { + return createElement("td",{key:"cell-"+field}, + createElement("input",{type:"button",value:"Completed",onClick:this.completeWorkout}) + ); + } else if(data[field] === void(0)) { return createElement("td",{key:"cell-attr-"+field,onClick:this.toggleAttribute.bind(this,field)}, (data.attributes[field])?"Yes":"No" ); diff --git a/src/components/manage/manage.component.js b/src/components/manage/manage.component.js index fa980e9..4c1f279 100644 --- a/src/components/manage/manage.component.js +++ b/src/components/manage/manage.component.js @@ -18,15 +18,16 @@ export default class Manage extends React.Component { workouts, changeDescription, changeName, + completeWorkout, toggleAttribute } = this.props; - console.log(workouts); const headers = [ "workout name", ...DEFAULT_ATTRIBUTES_ORDER, "times done", "last done", - "description" + "description", + "" ].map((x) => { return createElement("th",{key:"head-"+x},x); }); @@ -34,6 +35,7 @@ export default class Manage extends React.Component { return createElement(manageRow,{ key:"row-"+i, data:workouts[i], + complete:() => completeWorkout(i), description:(val) => changeDescription(i,val), name:(val) => changeName(i,val), toggle:(attr) => toggleAttribute(i,attr) diff --git a/src/components/manage/manage.js b/src/components/manage/manage.js index 7bec5ed..a29d3d5 100644 --- a/src/components/manage/manage.js +++ b/src/components/manage/manage.js @@ -3,7 +3,7 @@ import {connect} from 'react-redux'; import Manage from './manage.component.js'; import {constants} from '../../constants.js'; -const {CHANGE_ATTRIBUTE,CHANGE_WORKOUT_DESCRIPTION,CHANGE_WORKOUT_NAME,NEW_WORKOUT} = constants; +const {ADD_WORKOUT,CHANGE_ATTRIBUTE,CHANGE_WORKOUT_DESCRIPTION,CHANGE_WORKOUT_NAME,NEW_WORKOUT} = constants; const mapStateToProps = (state) => { const {workouts} = state; @@ -28,6 +28,17 @@ const mapDispatchToProps = (dispatch) => { name:val }); }, + completeWorkout:(workout) => { + const now = new Date(); + dispatch({ + type:ADD_WORKOUT, + name:workout, + toAdd:[new Date(now.getTime() - (now.getTimezoneOffset() * 60000 )) + .toISOString() + .split("T")[0] + ] + }); + }, newWorkout:() => { dispatch({ type:NEW_WORKOUT diff --git a/src/components/recent.row/recent.row.css b/src/components/recent.row/recent.row.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/recent.row/recent.row.js b/src/components/recent.row/recent.row.js index e69de29..00e9bfb 100644 --- a/src/components/recent.row/recent.row.js +++ b/src/components/recent.row/recent.row.js @@ -0,0 +1,21 @@ +import style from './recent.row.css'; + +export default class recentRow extends React.Component { + constructor(props) { + super(props); + this.handleDateChange = this.handleDateChange.bind(this); + } + handleDateChange(event) { + const {date,dateChange} = this.props; + dateChange(date,event.target.value); + } + render() { + const {name,date} = this.props; + return createElement("tr",null, + createElement("td",null,name), + createElement("td",null, + createElement("input",{defaultValue:date,onBlur:this.handleDateChange}) + ) + ); + } +} \ No newline at end of file diff --git a/src/components/recent/recent.component.js b/src/components/recent/recent.component.js new file mode 100644 index 0000000..85c9264 --- /dev/null +++ b/src/components/recent/recent.component.js @@ -0,0 +1,30 @@ +import style from './recent.css'; + +import recentRow from '../recent.row/recent.row.js'; + +export default class Recent extends React.Component { + render() { + const {data,handleDateChange} = this.props; + const headers = ["workout name","date"].map((x) => { + return createElement("th",{key:"head-"+x},x); + }); + const rows = data.map((i) => { + return createElement(recentRow,{ + key:"row-"+i.name+"-"+i.date, + name:i.name, + date:i.date, + dateChange:(old,date) => handleDateChange(i.name,old,date) + }); + }); + return createElement("div",{className:style.container}, + createElement("table",{className:style.table}, + createElement("thead",null, + createElement("tr",null, + headers + ) + ), + createElement("tbody",null,rows) + ) + ); + } +} \ No newline at end of file diff --git a/src/components/recent/recent.css b/src/components/recent/recent.css new file mode 100644 index 0000000..1f1752b --- /dev/null +++ b/src/components/recent/recent.css @@ -0,0 +1,7 @@ +:local(.container) { + +} + +:local(.table) { + +} \ No newline at end of file diff --git a/src/components/recent/recent.js b/src/components/recent/recent.js index e69de29..68c89ac 100644 --- a/src/components/recent/recent.js +++ b/src/components/recent/recent.js @@ -0,0 +1,30 @@ +import {connect} from 'react-redux'; + +import Recent from './recent.component.js'; + +import {constants} from '../../constants.js'; +const {CHANGE_WORKOUT_DATE} = constants; + +const mapStateToProps = (state) => { + return { + data:state.view.data + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + handleDateChange:(workout,old,date) => { + dispatch({ + type:CHANGE_WORKOUT_DATE, + workout, + old, + new:date + }); + } + }; +} + +export const recent = connect( + mapStateToProps, + mapDispatchToProps +)(Recent); \ No newline at end of file diff --git a/src/constants.js b/src/constants.js index 2837c42..013ea86 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,6 +2,7 @@ const constants = { ADD_WORKOUT:"ADD_WORKOUT", CHANGE_ATTRIBUTE:"CHANGE_ATTRIBUTE", CHANGE_VIEW:"CHANGE_VIEW", + CHANGE_WORKOUT_DATE:"CHANGE_WORKOUT_DATE", CHANGE_WORKOUT_DESCRIPTION:"CHANGE_WORKOUT_DESCRIPTION", CHANGE_WORKOUT_NAME:"CHANGE_WORKOUT_NAME", DEFAULT_ATTRIBUTES:{ diff --git a/src/reducers/view.js b/src/reducers/view.js index 9bc807c..f7b3821 100644 --- a/src/reducers/view.js +++ b/src/reducers/view.js @@ -15,15 +15,36 @@ const defaultState = { sortOrder:"desc" }; +const generateDaysAgo = (workouts) => { + const daysAgo = {...defaultDaysAgo}; + const now = new Date(); + for(let i in workouts) { + const last = workouts[i].last_done[0]; + if(last === void(0)) { + continue; + } + let lastDate = new Date(last); + lastDate = new Date(lastDate.getTime() + (now.getTimezoneOffset() * 60000)); + for(let attr in workouts[i].attributes) { + if(workouts[i].attributes[attr]) { + const diff = Math.floor((now-lastDate)/(1000*60*60*24)); + if((daysAgo[attr]<0)||(diff x instanceof workout)) { + if(!Object.keys(action.workouts).every((x) => action.workouts[x] instanceof workout)) { return state; } - console.log('here'); switch(action.type) { case CHANGE_VIEW: if((action.view === void(0))||(action.view == state.view)) { @@ -44,6 +65,7 @@ export default function view(state = defaultState,action) { description:actions.workouts[i].description }); } + newStateManageView.daysAgo = generateDaysAgo(action.workouts); return newStateManageView; case "recent": const newStateRecentView = { @@ -51,14 +73,15 @@ export default function view(state = defaultState,action) { view:"recent" }; newStateRecentView.data = []; - for(let i=0;i { }); + describe(CHANGE_WORKOUT_DESCRIPTION,() => { + + it('should not change the state when an invalid action is given',() => { + assert.fail('Not implemented'); + }); + + it('should not change the state when trying to change the description for a nonexistent workout',() => { + assert.fail('Not implemented'); + }); + + it('should successfully change the description of an already existing workout',() => { + assert.fail('Not implemented'); + }); + + }); + describe(DELETE_WORKOUT,() => { it('should not change the state when an invalid action is given',() => { -- 2.39.5