Time Management Web App: Intro & Setup

Shawn
4 min readOct 12, 2022

--

Last revised 9 January, 2023

Introduction

Efficient time management is a favorite topic of mine, and also really hard. Even our best designed schedules tend to be thrown out of whack thanks to unexpected events, distractions, and just plain laziness. For these reasons I often find the typical time management app to be insufficient. This project is the start of a custom attempt to make something more flexible and convenient.

The first iteration of this project will have 3 pages:

  • A home page, which shows a list of incomplete tasks for that day.
  • A tasks page, which lets you do CRUD operations on the task list. A task will either happen daily, the same days each week, or the same days of each month.
  • A workspace page, which uses web sockets to determine which task is currently active, and lets you make notes on the task.

The tech stack for this will be:

  • React (Javascript)
  • Node (Express, Sequelize, node-schedule)
  • MySQL
  • Web sockets

This tutorial is intended for programmers who already have a reasonable grasp on React, Javascript/Node, SQL, and the like. It’s a good project for something a little more advanced than the typical beginner’s tutorial. I encourage you to experiment as you follow the tutorial, renaming things or doing things differently as you see fit. After all, following tutorials only gets you so far — making your own unique additions shows you really know how to code.

Backend Setup

After you’ve created the folder and initialized npm ( mkdir task-schedule; cd task-schedule; npm init;, etc.) it’s time to install the backend Node modules:

npm install --save express cors body-parser sequelize mysql2 ws node-schedule

To organize our code, we’ll put the server-specific code in a server folder. In there, we’ll make the basic index.js file to start the API and make the database connection:

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const {Sequelize} = require('sequelize');
const path = require('path');
const db = new Sequelize('task_schedule', 'username', 'password', {
host: 'localhost',
dialect: 'mysql',
operatorAliases: false,
pool: { max: 5, min: 0, acquire: 30000, idle: 10000 }
});
const PORT = 3001;
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname + '/../client')));
app.get('/api/test', (req, res) => {
res.send('Hello world!');
});
app.listen(PORT, () => {
console.log(`App is live on port ${PORT}`);
});

From the root of the project, run this via server node/index.js . From there you can go to http://localhost:3001/api/test in your browser and see the response Hello world!

Over in MySQL, we’ll create two tables. task will hold the core data about the tasks themselves. task_note will hold the notes associated to tasks. Tasks can have multiple notes.

create table task (id integer primary key auto_increment, name varchar(100), cron_string varchar(14), completed tinyint(1) default '0');

create table task_note (id integer primary key auto_increment, task integer, time datetime, content text);

Now that the database is setup, we can create the corresponding Sequelize models. In future iterations of the project, these will go in their own files. But for this beginning point we can put them in the index.js file.

const Task = db.define('task', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
cron_string: Sequelize.STRING,
completed: Sequelize.BOOLEAN
}, {
freezeTableName: true,
timestamps: false
});
const TaskNote = db.define('task_note', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
task: Sequelize.INTEGER,
time: Sequelize.DATE,
content: Sequelize.TEXT
}, {
freezeTableName: true,
timestamps: false
});

And underneath those we can add the CRUD functions and API endpoints that use them. For now we’ll only include the functions and routes for the tasks themselves. Once we’re ready for the task notes we’ll come back to that.

The CRUD functions:

const TaskFunctions = {
async getAll() {
return Task.findAll();
},
async get(id) {
return Task.findByPk(id);
},
async create(data) {
const task = Task.create({
name: data.name,
cron_string: data.cron_string
});
return task;
},
async update(id, data) {
const task = await Task.findByPk(id);
task.name = data.name || task.name;
task.cron_string = data.cron_string || task.cron_string;
task.completed = [true, false].includes(data.completed) ? data.completed : task.completed;
await task.save();
return task;
},
async del(id) {
const task = await Task.destroy({where: {id}});
return id;
}
}

The API endpoints:

app.get('/api/task/all', async (req, res) => {
const result = await TaskFunctions.getAll();
res.send({result});
});
app.get('/api/task/:id', async (req, res) => {
const result = await TaskFunctions.get(req.params.id);
res.send({result});
});
app.post('/api/task', async (req,res) => {
const result = await TaskFunctions.create(req.body);
res.send({result});
});
app.put('/api/task/:id', async (req, res) => {
const result = await TaskFunctions.update(req.params.id, req.body);
res.send({result});
});
app.delete('/api/task/:id', async (req,res) => {
const result = await TaskFunctions.del(+req.params.id);
res.send({result});
});

Frontend Setup

Here we’ll just setup the boilerplate for routing on the frontend, and add the tasks management page in the next section.

npx create-react-app client

Then in client we add the Node modules that’ll be needed to start the project:

npm install --save react-router react-router-dom cron-validator

Over in index.js , remove the imports and code for the CSS file and reportWebVitals . Then add the imports for routing:

import {BrowserRouter, Routes, Route} from 'react-router-dom';

And change the body of the root.render function to be:

<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path='/' element={<App />}>
</Route>
</Routes>
</BrowserRouter>
</React.StrictMode>

Finally, over in App.js , remove the imports about the logo and CSS, then add this import:

import {Outlet} from 'react-router-dom';

Then remove all the children of <div className="app"> and replace them with <Outlet /> .

Putting It Together

If you were to do npm run start , you would see a blank white page. That’s intended for now. Here’s what the code should look like at this point. (You may notice the file names in the Gist start with client_ or server_. Those should be client/ and server/, but Gists don’t allow slashes in the names.)

Time Management Web App Series

--

--