~girishm/op-mattermost

0511ee28294d737cae89727997a246d4d771e0b7 — girishm a month ago a74d05d
additional command handling
3 files changed, 196 insertions(+), 143 deletions(-)

M resource/routes.js
M resource/uiActions.js
M resource/util.js
M resource/routes.js => resource/routes.js +41 -24
@@ 33,11 33,33 @@ module.exports = (app, axios) => {
  });

  app.post('/', (req, res) => {
    const {command, token} = req.body;
    const {command, token, text} = req.body;
    if(token === process.env.MATTERMOST_SLASH_TOKEN) {
      console.log("Request Body to / ", JSON.stringify(req.body, null, 2));
      if(command === "/op") {
        uiActions.showMenuBtn(req, res, axios);
        switch (text) {
          case 'lt':
            uiActions.showSelProject(req, res, axios, "showSelWP");
            break;
          case 'cwp':
            uiActions.showSelProject(req, res, axios, "createWP");
            break;
          case 'tl':
            uiActions.getTimeLog(req, res, axios, '');
            break;
          case 'dtl':
            uiActions.showTimeLogSel(req, res, axios, '');
            break;
          case 'dwp':
            uiActions.showDelWPSel(req, res, axios, '');
            break;
          case 'bye':
            uiActions.showByeMsg(req, res, '');
            break;
          default:
            uiActions.showMenuBtn(req, res, axios);
            break;
        }
      }
      else {
        res.send("*I don't understand ", command, ". Let's try again...* \n `/op`").status(500);


@@ 49,15 71,15 @@ module.exports = (app, axios) => {
  });

  app.post('/createTimeLog', (req, res) => {
    console.log("Create time log request: ", req);
    console.log("Create time log request: ", JSON.stringify(req.body, null, 2));
    uiActions.showSelProject(req, res, axios, "showSelWP");
  });

  app.post('/projSel', (req, res) => {
    console.log("Project submit request: ", req);
    console.log("Project submit request: ", JSON.stringify(req.body, null, 2));
    switch (req.body.context.action) {
      case 'showSelWP':
        uiActions.showSelWP(req, res, axios, "showTimeLogDlg");
        uiActions.showSelWP(req, res, axios, "showTimeLogDlg", 'update');
        break;
      case 'createWP':
        uiActions.createWP(req, res, axios);


@@ 69,7 91,7 @@ module.exports = (app, axios) => {
  });

  app.post('/wpSel', (req, res) => {
    console.log("Work package submit request: ", req);
    console.log("Work package submit request: ", JSON.stringify(req.body, null, 2));
    switch (req.body.context.action) {
      case 'showTimeLogDlg':
        uiActions.loadTimeLogDlg(req, res, axios);


@@ 84,22 106,22 @@ module.exports = (app, axios) => {
  });

  app.post('/logTime', (req, res) => {
    console.log("Time log submit request: ", req);
    console.log("Time log submit request: ", JSON.stringify(req.body, null, 2));
    uiActions.handleSubmission(req, res, axios);
  });

  app.get('/getLogo', (req, res) => {
    console.log("Logo image request: ", req);
    console.log("Logo image request: ", JSON.stringify(req.body, null, 2));
    res.sendFile(__dirname + '/op_logo.png');
  });

  app.post('/getTimeLog', (req, res) => {
    console.log("Request to getTimeLog: ", req);
    uiActions.getTimeLog(req, res, axios);
    console.log("Request to getTimeLog: ", JSON.stringify(req.body, null, 2));
    uiActions.getTimeLog(req, res, axios, 'update');
  });

  app.post('/delTimeLog', (req, res) => {
    console.log("Request to delTimeLog: ", req);
    console.log("Request to delTimeLog: ", JSON.stringify(req.body, null, 2));
    switch (req.body.context.action) {
      case "delSelTimeLog":
        uiActions.delTimeLog(req, res, axios);


@@ 108,40 130,35 @@ module.exports = (app, axios) => {
        uiActions.cnfDelTimeLog(req, res);
        break;
      default:
        uiActions.showTimeLogSel(req, res, axios);
        uiActions.showTimeLogSel(req, res, axios, 'update');
        break;
    }
  });

  app.post('/createWP', (req, res) => {
    console.log("Request to createWP: ", req);
    console.log("Request to createWP: ", JSON.stringify(req.body, null, 2));
    uiActions.showSelProject(req, res, axios, "createWP");
  });

  app.post('/saveWP', (req, res) => {
    console.log("Work package save request: ", req);
    console.log("Work package save request: ", JSON.stringify(req.body, null, 2));
    uiActions.saveWP(req, res, axios);
  })

  app.post('/delWP', (req, res) => {
    console.log("Work package delete request: ", req);
    console.log("Work package delete request: ", JSON.stringify(req.body, null, 2));
    switch (req.body.context.action) {
      case 'delWP':
        uiActions.delWP(req, res, axios);
        break;
      default:
        uiActions.showDelWPSel(req, res, axios);
        uiActions.showDelWPSel(req, res, axios, 'update');
        break;
    }
  });

  app.post('/bye', (req, res) => {
    console.log("Request to showBye handler: ", req);
    let byeMsg = JSON.stringify({
      "update": {
        "message": ":wave:",
        "props": {}
      },
    });
    res.type('application/json').send(byeMsg).status(200);
    console.log("Request to showBye handler: ", JSON.stringify(req.body, null, 2));
    uiActions.showByeMsg(req, res, 'update');
  });
}
\ No newline at end of file

M resource/uiActions.js => resource/uiActions.js +58 -36
@@ 46,7 46,7 @@ class UIactions {
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("Projects obtained from OP: %o", response);
      console.log("Projects obtained from OP: %o", response.data);
      let projectOptArray = [];
      response.data._embedded.elements.forEach(element => {
        projectOptArray.push({


@@ 64,13 64,13 @@ class UIactions {
      console.log("optArray for projects", projectOptJSON);
      res.set('Content-Type', 'application/json').send(JSON.stringify(projectOptJSON)).status(200);
    }).catch(error => {
      console.log("Error in getting projects from OP", error);
      console.log("Error in getting projects from OP", error.response.data.message);
      res.send("Open Project server down!!").status(500);
      return false;
    });
  }

  showSelWP(req, res, axios, action) {
  showSelWP(req, res, axios, action, mode) {
    // noinspection JSUnresolvedVariable
    this.projectId = req.body.context.selected_option.slice(this.optLen);
    axios({


@@ 79,7 79,7 @@ class UIactions {
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("WP obtained from OP: %o", response);
      console.log("WP obtained from OP: %o", response.data);
      let wpOptArray = [];
      response.data._embedded.elements.forEach(element => {
        wpOptArray.push({


@@ 87,11 87,11 @@ class UIactions {
          "value": "opt" + element.id
        });
      });
      let wpOptJSON = this.util.getWpOptJSON(this.intURL, wpOptArray, action);
      let wpOptJSON = this.util.getWpOptJSON(this.intURL, wpOptArray, action, mode);
      console.log("opt Array for WP: ", wpOptJSON);
      res.set('Content-Type', 'application/json').send(JSON.stringify(wpOptJSON)).status(200);
    }, (reason) => {
      console.log("Request failed for /work_packages: %o", reason);
    }, (error) => {
      console.log("Request failed for /work_packages: %o", error.response.data.message);
      this.message.showMsg(req, res, axios, this.util.wpFetchErrMsg);
      return false;
    });


@@ 112,7 112,7 @@ class UIactions {
        }
      }
    }).then((response) => {
      console.log("Activities obtained from OP: %o", response);
      console.log("Activities obtained from OP: %o", response.data);
      let activityOptArray = [];
      response.data._embedded.schema.activity._embedded.allowedValues.forEach(element => {
        activityOptArray.push({


@@ 133,12 133,12 @@ class UIactions {
        });
        res.type('application/json').send(updateMsg).status(200);
      }).catch(error => {
        console.log("Error while creating projects dialog", error);
        console.log("Error while creating projects dialog", error.response.data.message);
        this.message.showMsg(req, res, axios, this.util.dlgCreateErrMsg);
        return false;
      });
    }).catch((error) => {
      console.log("Error in fetching activities: ", error);
      console.log("Error in fetching activities: ", error.response.data.message);
      this.message.showMsg(req, res, axios, this.util.activityFetchErrMsg);
      return false;
    });


@@ 178,11 178,11 @@ class UIactions {
            },
            auth: this.opAuth
          }).then((response) => {
            console.log("Time logged. Save response: %o", response);
            console.log("Time logged. Save response: %o", response.data);
            this.message.showMsg(req, res, axios, "Time entry ID - " + response.data.id + this.util.timeLogSuccessMsg);
            return true;
          }).catch((error) => {
            console.log("OP time entries create error: %o", error);
            console.log("OP time entries create error: %o", error.response.data.message);
            if (error.response.status === 403) {
              this.message.showMsg(req, res, axios, this.util.timeLogForbiddenMsg);
            }


@@ 211,14 211,14 @@ class UIactions {
    }
  }

  getTimeLog(req, res, axios) {
  getTimeLog(req, res, axios, mode) {
    axios({
      url: 'time_entries?sortBy=[["createdAt", "desc"]]',
      method: 'get',
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("Time entries obtained from OP: %o", response);
      console.log("Time entries obtained from OP: %o", response.data);
      let timeLogArray = [];
      response.data._embedded.elements.forEach(element => {
        timeLogArray.push({


@@ 231,21 231,21 @@ class UIactions {
          "comment": element.comment.raw
        });
      });
      res.set('Content-Type', 'application/json').send(this.util.getTimeLogJSON(timeLogArray)).status(200);
      res.set('Content-Type', 'application/json').send(this.util.getTimeLogJSON(timeLogArray, mode)).status(200);
    }).catch((error) => {
      console.log("Error in getting time logs: ", error);
      console.log("Error in getting time logs: ", error.response.data.message);
      this.message.showMsg(req, res, axios, this.util.timeLogFetchErrMsg);
    });
  };

  showTimeLogSel(req, res, axios) {
  showTimeLogSel(req, res, axios, mode) {
    axios({
      url: 'time_entries?sortBy=[["createdAt", "desc"]]',
      method: 'get',
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("Time entries obtained from OP: %o", response);
      console.log("Time entries obtained from OP: %o", response.data);
      let timeLogArray = [];
      response.data._embedded.elements.forEach(element => {
        timeLogArray.push({


@@ 253,9 253,9 @@ class UIactions {
          "text":  element.comment.raw + '-' + element.spentOn + '-' + this.moment.duration(element.hours, "h").humanize() + '-' + element._links.workPackage.title + '-' + element._links.activity.title + '-' + element._links.project.title
        });
      });
      res.set('Content-Type', 'application/json').send(this.util.getTimeLogOptJSON(this.intURL, timeLogArray, "cnfDelTimeLog")).status(200);
      res.set('Content-Type', 'application/json').send(this.util.getTimeLogOptJSON(this.intURL, timeLogArray, "cnfDelTimeLog", mode)).status(200);
    }).catch((error) => {
      console.log("Error in getting time logs: ", error);
      console.log("Error in getting time logs: ", error.response.data.message);
      this.message.showMsg(req, res, axios, this.util.timeLogFetchErrMsg);
    });
  };


@@ 273,10 273,10 @@ class UIactions {
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("Time entry deleted. Response %o", response);
      console.log("Time entry deleted. Response %o", response.data);
      res.set('Content-Type', 'application/json').send(JSON.stringify(this.util.getTimeLogDelMsgJSON(this.util.timeLogDelMsg, this.intURL))).status(200);
    }).catch((error) => {
      console.log("Error in time entry deletion: ", error);
      console.log("Error in time entry deletion: ", error.response.data.message);
      this.message.showMsg(req, res, axios, this.util.timeLogDelErrMsg);
      return false;
    });


@@ 290,7 290,7 @@ class UIactions {
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("Response from get types: ", response);
      console.log("Response from get types: ", response.data);
      let typeArray = [];
      response.data._embedded.elements.forEach(element => {
        typeArray.push({


@@ 304,7 304,7 @@ class UIactions {
        baseURL: this.opURL,
        auth: this.opAuth
      }).then((response) => {
        console.log("Response from get available assignees: ", response);
        console.log("Response from get available assignees: ", response.data);
        let assigneeArray = [];
        response.data._embedded.elements.forEach(element => {
          assigneeArray.push({


@@ 314,7 314,7 @@ class UIactions {
        });
        let wpCreateDlgJSON = this.util.getWpCreateJSON(req.body.trigger_id, this.intURL, typeArray, assigneeArray);
        axios.post(this.mmURL + 'actions/dialogs/open', wpCreateDlgJSON).then(response => {
          console.log("Response from wp create dialog: ", response);
          console.log("Response from wp create dialog: ", response.data);
          let updateMsg = JSON.stringify({
            "update": {
              "message": "Updated!",


@@ 324,12 324,12 @@ class UIactions {
          });
          res.type('application/json').send(updateMsg).status(200);
        }).catch(error => {
          console.log("Error while creating work package dialog", error);
          console.log("Error while creating work package dialog", error.response.data.message);
          this.message.showMsg(req, res, axios, this.util.dlgCreateErrMsg);
        });
      });
    }).catch((error) => {
      console.log("Error in fetching types: ", error);
      console.log("Error in fetching types: ", error.response.data.message);
      this.message.showMsg(req, res, axios, this.util.typeFetchErrMsg);
      return false;
    });


@@ 364,16 364,19 @@ class UIactions {
        data: postWpData,
        auth: this.opAuth
      }).then(response => {
        console.log("Work package saved. Save response: %o", response);
        console.log("Work package saved. Save response: %o", response.data);
        this.message.showMsg(req, res, axios, "Work package ID - " + response.data.id + this.util.saveWPSuccessMsg);
        return true;
      }).catch((error) => {
        console.log("OP WP entries create error: %o", error);
        console.log("OP WP entries create error: %o", error.response.data.message);
        if (error.response.status === 403) {
          this.message.showMsg(req, res, axios, this.util.timeLogForbiddenMsg);
          this.message.showMsg(req, res, axios, this.util.wpCreateForbiddenMsg);
        }
        else if (error.response.status === 422) {
          this.message.showMsg(req, res, axios, this.util.wpTypeErrMsg);
        }
        else {
          this.message.showMsg(req, res, axios, this.util.timeLogFailMsg);
          this.message.showMsg(req, res, axios, this.util.genericErrMsg);
        }
        return false;
      });


@@ 388,14 391,14 @@ class UIactions {
    }
  };

  showDelWPSel(req, res, axios) {
  showDelWPSel(req, res, axios, mode) {
    axios({
      url: '/work_packages?sortBy=[["created_at","desc"]]',
      method: 'get',
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("WP obtained from OP: %o", response);
      console.log("WP obtained from OP: %o", response.data);
      let wpOptArray = [];
      response.data._embedded.elements.forEach(element => {
        wpOptArray.push({


@@ 403,9 406,12 @@ class UIactions {
          "value": "opt" + element.id
        });
      });
      let wpOptJSON = this.util.getWpOptJSON(this.intURL, wpOptArray, "cnfDelWP");
      let wpOptJSON = this.util.getWpOptJSON(this.intURL, wpOptArray, "cnfDelWP", mode);
      console.log("opt Array for WP: ", wpOptJSON);
      res.set('Content-Type', 'application/json').send(JSON.stringify(wpOptJSON)).status(200);
    }).catch((error) => {
      console.log("Error is show work package selection: ", error.response.data.message);
      this.message.showMsg(req, res, axios, this.util.wpFetchErrMsg);
    });
  }



@@ 422,10 428,10 @@ class UIactions {
      baseURL: this.opURL,
      auth: this.opAuth
    }).then((response) => {
      console.log("WP deleted. Response %o", response);
      console.log("WP deleted. Response %o", response.data);
      res.set('Content-Type', 'application/json').send(JSON.stringify(this.util.getWPDelMsgJSON(this.util.wpDelMsg))).status(200);
    }).catch((error) => {
      console.log("Error in work package deletion: ", error);
      console.log("Error in work package deletion: ", error.response.data.message);
      if(error.response.status === 403) {
        this.message.showMsg(req, res, axios, this.util.wpForbiddenMsg);
      }


@@ 447,5 453,21 @@ class UIactions {
      res.set('Content-Type', 'application/json').send(JSON.stringify(this.util.getMenuBtnJSON(this.intURL, this.currentUser))).status(200);
    });
  }

  showByeMsg(req, res, mode) {
    let byeMsg = {
        "message": ":wave:",
        "props": {}
    };
    if(mode === 'update') {
      byeMsg = {
        "update": byeMsg
      };
    }
    else {
      byeMsg.text = ":wave:";
    }
    res.type('application/json').send(JSON.stringify(byeMsg)).status(200);
  }
}
module.exports = UIactions;
\ No newline at end of file

M resource/util.js => resource/util.js +97 -83
@@ 22,25 22,29 @@ const moment = require("moment");
class Util {

  constructor() {
    this.timeLogSuccessMsg = "\n**Time logged! You are awesome :sunglasses: **\n To view time logged try `/op`";
    this.timeLogSuccessMsg = "\n**Time logged! You are awesome :sunglasses: **\n To view time logged try `/op tl`";
    this.timeLogForbiddenMsg = "**It seems that you don't have permission to log time for this project :confused: **"
    this.timeLogFailMsg = "**That didn't work :pensive: An internal error occurred!**";
    this.timeLogDelMsg = "**Time log deleted!**";
    this.timeLogDelErrMsg = "**That didn't work :pensive: Couldn't delete time log\n Please try again...`/op`**";
    this.timeLogDelErrMsg = "**That didn't work :pensive: Couldn't delete time log\n Please try again...`/op dtl`**";
    this.cnfDelTimeLogMsg = "**Confirm time log deletion?**"
    this.dateErrMsg = "**It seems that date was incorrect :thinking: Please enter a date within last one year and in YYYY-MM-DD format. **";
    this.billableHoursErrMsg = "**It seems that billable hours was incorrect :thinking: Please note billable hours should be less than or equal to logged hours. **";
    this.dlgCreateErrMsg = "**It's an internal problem. Dialog creation failed :pensive: Can you please try `/op` again?**";

    this.wpDtlEmptyMsg = "**Work package details not entered :( Let's try again...**\n `/op`";
    this.saveWPSuccessMsg = "\n**Work package created! You are awesome :sunglasses: **\n To log time for a work package try `/op`";
    this.saveWPSuccessMsg = "\n**Work package created! You are awesome :sunglasses: **\n To log time for a work package try `/op lt`";
    this.wpFetchErrMsg = "**That didn't work :pensive: Couldn't fetch work packages from OP**";
    this.cnfDelWPMsg = "**Confirm work package deletion?** This will delete all associated time entries and child work packages";
    this.wpDelErrMsg = "**That didn't work :pensive: Couldn't delete work package\n Please try again... `/op`**";
    this.wpDelErrMsg = "**That didn't work :pensive: Couldn't delete work package\n Please try again... `/op dwp`**";
    this.wpForbiddenMsg = "**You are not authorized to delete this work package**:angry:";
    this.wpDelMsg = "**Work package deleted successfully!**";
    this.wpTypeErrMsg = "**Work package type is not set to one of the allowed values. Couldn't create work package :pensive: **";
    this.wpCreateForbiddenMsg = "**It seems that you don't have permission to create work package for this project :confused: **"

    this.dateErrMsg = "**It seems that date was incorrect :thinking: Please enter a date within last one year and in YYYY-MM-DD format. **";
    this.billableHoursErrMsg = "**It seems that billable hours was incorrect :thinking: Please note billable hours should be less than or equal to logged hours. **";
    this.dlgCreateErrMsg = "**It's an internal problem. Dialog creation failed :pensive: Can you please try `/op lt` again?**";
    this.activityFetchErrMsg = "**That didn't work :pensive: Couldn't fetch activities from OP**";
    this.typeFetchErrMsg = "**That didn't work :pensive: Couldn't to fetch types from OP**";
    this.dlgCancelMsg = "** If you would like to try again then, `/op` **";
    this.dlgCancelMsg = "** If you would like to try again then, `/op cwp` **";
    this.genericErrMsg = "** Unknown error occurred :pensive: Can you please try again? **";
  }



@@ 159,81 163,88 @@ class Util {
    }
  }

  getWpOptJSON(url, optArray, action) {
    return {
      "update": {
        "response_type": "in_channel",
        "message": "*Please select a work package*",
        "props": {
          "attachments": [
            {
              "actions": [
                {
                  "name": "Type to search for a work package...",
                  "integration": {
                    "url": url + "wpSel",
                    "context": {
                      "action": action
                    }
                  },
                  "type": "select",
                  "options": optArray
                },
                {
                  "name": "Cancel WP search",
                  "integration": {
                    "url": url + "createTimeLog"
  getWpOptJSON(url, optArray, action, mode) {
    let wpOptJSON = {
      "response_type": "in_channel",
      "message": "*Please select a work package*",
      "props": {
        "attachments": [
          {
            "actions": [
              {
                "name": "Type to search for a work package...",
                "integration": {
                  "url": url + "wpSel",
                  "context": {
                    "action": action
                  }
                }]
            }
          ]
        }
                },
                "type": "select",
                "options": optArray
              },
              {
                "name": "Cancel WP search",
                "integration": {
                  "url": url + "createTimeLog"
                }
              }]
          }
        ]
      }
    };

    if(mode === 'update') {
      wpOptJSON = {
        "update": wpOptJSON
      }
    }

    return wpOptJSON;
  }

  getTimeLogOptJSON(url, optArray, action) {
  getTimeLogOptJSON(url, optArray, action, mode) {
    let timeLogOptJSON = {
      "response_type": "in_channel",
      "props": {}
    }
    if(optArray.length !== 0) {
      return {
        "update": {
          "response_type": "in_channel",
          "message": "*Please select a time log*",
          "props": {
            "attachments": [
      timeLogOptJSON.props =  {
        "attachments": [
          {
            "actions": [
              {
                "actions": [
                  {
                    "name": "Type to search for a time log...",
                    "integration": {
                      "url": url + "delTimeLog",
                      "context": {
                        "action": action
                      }
                    },
                    "type": "select",
                    "options": optArray
                  },
                  {
                    "name": "Cancel search",
                    "integration": {
                      "url": url + "bye"
                    }
                  }]
              }
            ]
                "name": "Type to search for a time log...",
                "integration": {
                  "url": url + "delTimeLog",
                  "context": {
                    "action": action
                  }
                },
                "type": "select",
                "options": optArray
              },
              {
                "name": "Cancel search",
                "integration": {
                  "url": url + "bye"
                }
              }]
          }
        }
      };
        ]
      }
    }
    else {
      timeLogOptJSON.text = "Couldn't find time entries to delete :confused: Try logging time using `/op`";
    }

    if(mode === 'update') {
      return {
        "update": {
          "response_type": "in_channel",
          "message": "Couldn't find time entries to delete :confused: Try logging time using `/op`",
          "props": {}
        }
        "update": timeLogOptJSON
      };
    }
    else {
      return timeLogOptJSON;
    }
  }

  getTimeLogDelMsgJSON(msg, url) {


@@ 262,8 273,13 @@ class Util {
    };
  }

  getTimeLogJSON(timeLogArray) {
  getTimeLogJSON(timeLogArray, mode) {
    let tableTxt = '';
    let timeLogObj = {
        "response_type": "in_channel",
        "props": {}
    };

    if (timeLogArray.length !== 0) {
      tableTxt = "#### Time entries logged by you\n";
      tableTxt += "| Spent On | Project | Work Package | Activity | Logged Time | Billed Time | Comment |\n";


@@ 274,23 290,21 @@ class Util {
        }
        tableTxt += "| " + element.spentOn + " | " + element.project + " | " + element.workPackage + " | " + element.activity + " | " + element.loggedHours + " | " + element.billableHours + " | " + element.comment.replace(/\n/g, " ") + " |\n";
      });
    }
    else {
      tableTxt = "Couldn't find time entries logged by you :confused: Try logging time using `/op`";
    }

      return {
        "update": {
          "message": tableTxt,
          "props": {}
        }
    if(mode === 'update') {
      timeLogObj = {
        "update": timeLogObj
      };
      timeLogObj.update.message = tableTxt;
    }
    else {
      tableTxt = "Couldn't find time entries logged by you :confused: Try logging time using `/op`";
      return {
        "update": {
          "message": tableTxt,
          "props": {}
        }
      }
      timeLogObj.text = tableTxt;
    }
    return timeLogObj;
  }

  getWPDelMsgJSON(msg) {