Skip to content

Instantly share code, notes, and snippets.

@shawnco
Created October 17, 2022 21:23
Show Gist options
  • Save shawnco/b73aea609c73206e1ae42c4f3c99de75 to your computer and use it in GitHub Desktop.
Save shawnco/b73aea609c73206e1ae42c4f3c99de75 to your computer and use it in GitHub Desktop.
Time Management Web App section 4
import {useState} from 'react';
const url = 'http://localhost:3001/api/task';
const headers = {
'Content-Type': 'application/json'
};
const API = {
async create(task, content) {
const res = await fetch(`${url}/${task}/notes`, {
method: 'POST',
headers,
body: JSON.stringify({
task,
content,
time: new Date()
})
});
const {result} = await res.json();
return result;
},
async update(task, body) {
const res = await fetch(`${url}/${task}/notes`, {
method: 'PUT',
headers,
body: JSON.stringify(body)
});
const {result} = await res.json();
return result;
},
async del(task, id) {
const res = await fetch(`${url}/${task}/notes`, {
method: 'DELETE',
headers,
body: JSON.stringify({id})
});
const {result} = await res.json();
return result;
}
};
const NewNote = props => {
const [content, setContent] = useState('');
const handleChange = e => setContent(e.target.value);
const save = async e => {
const result = await API.create(props.task, content);
props.handleSave(result);
setContent('');
}
return <tr>
<td></td>
<td><textarea cols={40} rows={10} value={content} onChange={handleChange} /></td>
<td><button onClick={save}>Save</button></td>
<td></td>
</tr>
}
const NoteRow = props => {
const [content, setContent] = useState(props.note.content);
const handleChange = e => setContent(e.target.value);
const update = async e => {
const {task} = props;
const result = await API.update(task, {id: props.note.id, content});
props.handleUpdate(result);
}
const del = async e => {
if (window.confirm('Delete note?')) {
const {task} = props;
const result = await API.del(task, props.note.id);
props.handleDelete(props.note.id);
}
}
return <tr>
<td>{(new Date(props.note.time)).toLocaleString()}</td>
<td><textarea cols={40} rows={10} value={content} onChange={handleChange} /></td>
<td><button onClick={update}>Update</button></td>
<td><button onClick={del}>Delete</button></td>
</tr>
}
const TaskNotes = props => {
const [notes, setNotes] = useState(props.notes);
const handleSave = note => {
setNotes([...notes, note])
}
const handleUpdate = note => {
const oldNotes = [...notes];
const find = oldNotes.find(n => n.id == note.id);
if (find) {
find.content = note.content;
}
setNotes(oldNotes);
}
const handleDelete = id => {
const oldNotes = [...notes];
const index = oldNotes.findIndex(n => n.id == id);
if (index > -1) {
oldNotes.splice(index, 1);
}
setNotes(oldNotes);
}
return <table border='1'>
<thead>
<tr>
<th>Date/Time</th>
<th>Content</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{notes.map(n => <NoteRow
task={props.task}
key={n.id}
note={n}
handleUpdate={handleUpdate}
handleDelete={handleDelete}
/>)}
<NewNote task={props.task} handleSave={handleSave} />
</tbody>
</table>
}
export default TaskNotes;
import {useState, useEffect} from 'react';
import TaskNotes from './task_notes';
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({});
const [notes, setNotes] = useState([]);
useEffect(() => {
const socket = new WebSocket('ws://localhost:8080');
socket.onmessage = e => {
try {
const data = JSON.parse(e.data);
setTask(data.task);
setNotes(data.taskNotes);
} 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>
<TaskNotes task={task.id} notes={notes} />
</>
} 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 TaskNoteFunctions = {
async getByTask(task) {
return TaskNote.findAll({where:{task}});
},
async create(data) {
return TaskNote.create({
task: data.task,
time: data.time,
content: data.content
});
},
async update(id, data) {
const note = await TaskNote.findByPk(id);
note.content = data.content;
return note.save();
},
async del(id) {
const note = await TaskNote.destroy({where:{id}});
return id;
}
}
const socket = new Socket(TaskNoteFunctions);
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.get('/api/task/:id/notes', async (req, res) => {
const result = await TaskNoteFunctions.getByTask(+req.params.id);
res.send({result});
});
app.post('/api/task/:id/notes', async (req, res) => {
const result = await TaskNoteFunctions.create({
id: req.params.id,
...req.body
});
res.send({result});
});
app.put('/api/task/:id/notes', async (req, res) => {
const {id, ...data} = req.body;
const result = await TaskNoteFunctions.update(id, data);
res.send({result})
});
app.delete('/api/task/:id/notes', async (req, res) => {
const {id} = req.body;
const result = await TaskNoteFunctions.del(id);
res.send({result})
});
app.listen(PORT, () => {
console.log(`App is live on port ${PORT}`);
});
const {WebSocketServer, OPEN} = require('ws');
class Socket {
constructor(taskNoteFns) {
this.taskNoteFns = taskNoteFns;
this.wss = new WebSocketServer({port: 8080});
this.wss.on('connection', ws => console.log('Connected!'));
}
async sendTask(task) {
const taskNotes = await this.taskNoteFns.getByTask(task.id);
this.wss.clients.forEach(client => {
if (client.readyState === OPEN) {
const data = {
type: 'task_start',
task,
taskNotes
};
client.send(JSON.stringify(data));
}
});
}
}
module.exports = Socket;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment