Skip to content

Instantly share code, notes, and snippets.

@shawnco
Created October 17, 2022 01:00
Show Gist options
  • Save shawnco/7f39af9ff1224706ff5501c1f139b9e3 to your computer and use it in GitHub Desktop.
Save shawnco/7f39af9ff1224706ff5501c1f139b9e3 to your computer and use it in GitHub Desktop.
Time Management Web App section 3
import {useState, useEffect} from 'react';
const url = 'http://localhost:3001/api/task';
const headers = {
'Content-Type': 'application/json'
};
const API = {
async update(id, body) {
const res = await fetch(`${url}/${id}`, {method: 'PUT', headers, body: JSON.stringify(body)});
const {result} = await res.json();
return result;
}
}
const Workspace = props => {
const [task, setTask] = useState({});
useEffect(() => {
const socket = new WebSocket('ws://localhost:8080');
socket.onmessage = e => {
try {
const data = JSON.parse(e.data);
setTask(data.task);
} catch (e) {
console.log(e);
}
}
}, []);
const handleClick = async e => {
const {id, completed} = task;
const result = await API.update(id, {completed: !completed});
setTask({...task, completed: !completed});
};
if (task.id) {
return <>
<table border='1'>
<thead>
<tr>
<th>Completed</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type='checkbox' name='completed' checked={task.completed} onChange={handleClick} /></td>
<td>{task.name}</td>
</tr>
</tbody>
</table>
</>
} else {
return <b>Waiting for a task to start</b>
}
}
export default Workspace;
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const {Sequelize} = require('sequelize');
const path = require('path');
const TaskSchedule = require('./task_schedule');
const Socket = require('./socket');
const db = new Sequelize('task_schedule', 'username', 'password', {
host: 'localhost',
dialect: 'mysql',
operatorAliases: false,
pool: { max: 5, min: 0, acquire: 30000, idle: 10000 }
});
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
});
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;
},
async getIncomplete() {
return Task.findAll({where: {completed: false}});
}
}
const socket = new Socket();
const taskSchedule = new TaskSchedule(TaskFunctions, socket);
taskSchedule.rebuildSchedule();
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.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);
await taskSchedule.rebuildSchedule();
res.send({result});
});
app.put('/api/task/:id', async (req, res) => {
const result = await TaskFunctions.update(req.params.id, req.body);
await taskSchedule.rebuildSchedule();
res.send({result});
});
app.delete('/api/task/:id', async (req,res) => {
const result = await TaskFunctions.del(+req.params.id);
await taskSchedule.rebuildSchedule();
res.send({result});
});
app.listen(PORT, () => {
console.log(`App is live on port ${PORT}`);
});
const {WebSocketServer, OPEN} = require('ws');
class Socket {
constructor() {
this.wss = new WebSocketServer({port: 8080});
this.wss.on('connection', ws => console.log('Connected!'));
}
async sendTask(task) {
this.wss.clients.forEach(client => {
if (client.readyState === OPEN) {
const data = {
type: 'task_start',
task
};
client.send(JSON.stringify(data));
}
});
}
}
module.exports = Socket;
const schedule = require('node-schedule');
class TaskSchedule {
constructor(taskFns, socket) {
this.taskFns = taskFns;
this.socket = socket;
process.on('SIGINT', async () => {
await schedule.gracefulShutdown();
process.exit(0);
});
}
addTask(task) {
const {id, cron_string} = task;
const job = schedule.scheduleJob(`job:${id}`, cron_string, () => {
this.socket.sendTask(task);
});
return job;
}
async populateTasks() {
const tasks = await this.taskFns.getIncomplete();
return tasks.map(t => this.addTask(t));
}
clearTasks() {
Object.keys(schedule.scheduledJobs).map(k => {
const job = schedule.scheduledJobs[k];
job.cancel();
});
}
async rebuildSchedule() {
this.clearTasks();
await this.populateTasks();
}
}
module.exports = TaskSchedule;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment