finished created main react components
authorAlex-Laptop <[email protected]>
Fri, 22 Mar 2019 04:04:22 +0000 (21:04 -0700)
committerAlex-Laptop <[email protected]>
Fri, 22 Mar 2019 04:04:22 +0000 (21:04 -0700)
14 files changed:
src/classes/workout.js
src/components/main/main.component.js
src/components/manage.row/manage.row.js
src/components/manage/manage.component.js
src/components/manage/manage.js
src/components/recent.row/recent.row.css [new file with mode: 0644]
src/components/recent.row/recent.row.js
src/components/recent/recent.component.js [new file with mode: 0644]
src/components/recent/recent.css [new file with mode: 0644]
src/components/recent/recent.js
src/constants.js
src/reducers/view.js
src/reducers/workouts.js
test/workouts.reducer.tests.js

index c357e98cea22550bd50cf2d30578ad1a6ff4e404..fdfef9b6237b2f8786d31ae8fee77da314f273dc 100644 (file)
@@ -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<b;
+               });
        }
        changeDescription(str) {
                if(typeof str != "string") {
@@ -49,7 +50,11 @@ export default class workout {
                return this.datesDone.slice(0,1);
        }
        remove(date) {
-               throw new Error("Not implemented");
+               for(let i=0;i<this.datesDone.length;i++) {
+                       if(this.datesDone[i]==date) {
+                               this.datesDone.splice(i,1);
+                       }
+               }
        }
        setName(name) {
                if(typeof name != "string") {
index 026ed4dfc1ccf6fab37fc5d623380b5b87cbd38c..e272c33fb72241be44a6db769566e6e8ec0bb789 100644 (file)
@@ -3,8 +3,7 @@ import style from './main.css';
 import {daysAgo} from '../days.ago/days.ago.js';
 import header from '../header/header.js';
 import {manage} from '../manage/manage.js';
-//import {recent} from '../recent/recent.js';
-//(props.view=="manage")?createElement(manage,null):createElement(recent,null)
+import {recent} from '../recent/recent.js';
 
 export default class Main extends React.Component {
        constructor(props) {
@@ -24,7 +23,7 @@ export default class Main extends React.Component {
                        createElement("div",{className:style.viewContainer},
                                createElement(daysAgo,null),
                                createElement("input",{type:"button",onClick:this.switchView.bind(this,otherView),value:otherView}),
-                               createElement(manage,null)
+                               (props.view=="manage")?createElement(manage,null):createElement(recent,null)
                        )
                );
        }
index 0d8f7aae03787a44dfac29e97d6f2cf8a3b7d9df..afc7419a6a446a3e35359f996d805cbb5ca71809 100644 (file)
@@ -8,6 +8,7 @@ export default class manageRow extends React.Component {
                super(props);
                this.changeDescription = this.changeDescription.bind(this);
                this.changeName = this.changeName.bind(this);
+               this.completeWorkout = this.completeWorkout.bind(this);
                this.toggleAttribute = this.toggleAttribute.bind(this);
        }
        changeDescription(event) {
@@ -16,6 +17,9 @@ export default class manageRow extends React.Component {
        changeName(event) {
                this.props.name(event.target.value);
        }
+       completeWorkout() {
+               this.props.complete();
+       }
        toggleAttribute(attr) {
                this.props.toggle(attr);
        }
@@ -26,10 +30,15 @@ export default class manageRow extends React.Component {
                        ...DEFAULT_ATTRIBUTES_ORDER,
                        "times_done",
                        "last_done",
-                       "description"
+                       "description",
+                       "done"
                ].map((field) => {
                        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"
                                );
index fa980e96426a5a80243351250f3ef5fec150af70..4c1f27960a8929d3fa567f46837659ab2df61a7d 100644 (file)
@@ -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)
index 7bec5ed90b757cc3c5db787e176eea6fe83770a6..a29d3d59c455f587e16a350793392b4b3a998589 100644 (file)
@@ -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 (file)
index 0000000..e69de29
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..00e9bfbe3de4b33109f7504a76aa08c1bc51e66d 100644 (file)
@@ -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 (file)
index 0000000..85c9264
--- /dev/null
@@ -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 (file)
index 0000000..1f1752b
--- /dev/null
@@ -0,0 +1,7 @@
+:local(.container) {
+
+}
+
+:local(.table) {
+
+}
\ No newline at end of file
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..68c89acea4d3812dc70db908542a5af50a1356bb 100644 (file)
@@ -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
index 2837c42fc90fb6ec861b3cf4112df1b601c6a92f..013ea86c0b3904f8f7d84cfc2903c04f9a742263 100644 (file)
@@ -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:{
index 9bc807c02de2a386ea6a28c4ba1638625d7373e0..f7b382114549e6f5d718562d38588bb46c42f4ed 100644 (file)
@@ -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<daysAgo[attr])) {
+                                       daysAgo[attr] = diff;
+                               }
+                       }
+               }
+       }
+       return daysAgo;
+};
+
 export default function view(state = defaultState,action) {
        const {CHANGE_VIEW,SORT_VIEW} = constants;
        if((action.workouts == void(0))||(typeof action.workouts != "object" )) {
                return state;
        }
-       if(!Object.keys(action.workouts).every((x) => 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<action.workouts.length;i++) {
-                                               for(let j=0;j<action.workouts[i].datesDone;j++) {
+                                       for(let i in action.workouts) {
+                                               for(let j=0;j<action.workouts[i].datesDone.length;j++) {
                                                        newStateRecentView.data.push({
-                                                               name:actions.workouts[i].name,
-                                                               date:actions.workouts[i].datesDone[j]
+                                                               name:action.workouts[i].name,
+                                                               date:action.workouts[i].datesDone[j]
                                                        });
                                                }
                                        }
+                                       newStateRecentView.daysAgo = generateDaysAgo(action.workouts);
                                        return newStateRecentView;
                                default:
                                        return state;
index 1126ce85c306c44d78e4a57c7bed373f7a3304f3..f74f1ffb26f7ee1f77413471513cc2c14ef37309 100644 (file)
@@ -3,7 +3,7 @@ import workout from '../classes/workout.js';
 import {constants} from '../constants.js';
 
 export default function workouts(state = {},action) {
-       const {ADD_WORKOUT,CHANGE_ATTRIBUTE,CHANGE_WORKOUT_DESCRIPTION,CHANGE_WORKOUT_NAME,DEFAULT_ATTRIBUTES,DELETE_WORKOUT,NEW_WORKOUT,REMOVE_WORKOUT} = constants;
+       const {ADD_WORKOUT,CHANGE_ATTRIBUTE,CHANGE_WORKOUT_DATE,CHANGE_WORKOUT_DESCRIPTION,CHANGE_WORKOUT_NAME,DEFAULT_ATTRIBUTES,DELETE_WORKOUT,NEW_WORKOUT,REMOVE_WORKOUT} = constants;
        if(typeof action != "object") {
                return state;
        }
@@ -36,6 +36,25 @@ export default function workouts(state = {},action) {
                        } catch(err) {
                                return state;
                        }
+               case CHANGE_WORKOUT_DATE:
+                       if(action.workout === void(0)) {
+                               return state;
+                       }
+                       if((action.old === void(0))||(action.new === void(0))) {
+                               return state;
+                       }
+                       if(action.old === action.new) {
+                               return state;
+                       }
+                       const newStateAfterWorkoutDateChange = {...state};
+                       try {
+                               newStateAfterWorkoutDateChange[action.workout].remove(action.old);
+                               newStateAfterWorkoutDateChange[action.workout].add([action.new]);
+                               return newStateAfterWorkoutDateChange;
+                       } catch(err) {
+                               console.error(err);
+                               return state;
+                       }
                case CHANGE_WORKOUT_DESCRIPTION:
                        if(action.workout === void(0)) {
                                return state;
index 70940c5e70672ac398e796c34dcace892d26397a..581c84a17d79a901eb2377cebcabc845723e739d 100644 (file)
@@ -51,6 +51,22 @@ describe('workouts reducer',() => {
 
        });
 
+       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',() => {