Created
October 17, 2022 01:00
-
-
Save shawnco/7f39af9ff1224706ff5501c1f139b9e3 to your computer and use it in GitHub Desktop.
Time Management Web App section 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}`); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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