Last active
September 10, 2022 11:50
-
-
Save calogxro/6e601e07c2a937df4418d104fb717570 to your computer and use it in GitHub Desktop.
ToDo app to sample Caffeine backend (https://github.com/rehacktive/caffeine) - DEMO: http://3.72.37.48:8001/
This file contains hidden or 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
<!-- | |
# Caffeinated # | |
ToDo app made with Vue.js | |
to sample Caffeine backend (https://github.com/rehacktive/caffeine) | |
Created on Oct 24, 2021 | |
by Calogero Miraglia (https://github.com/calogxro) | |
GitHub: https://gist.github.com/calogxro/6e601e07c2a937df4418d104fb717570 | |
--> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" /> | |
<style> | |
/*sticky footer*/ | |
html, body { | |
height: 100%; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
} | |
.content { | |
flex: 1 0 auto; | |
} | |
footer { | |
flex-shrink: 0; | |
} | |
/*end sticky footer*/ | |
body { | |
font-family: Arial, Helvetica, sans-serif; | |
max-width: 700px; | |
margin: 0px auto; | |
} | |
header { | |
display: inline-block; | |
} | |
header h1 { | |
float: left; | |
margin: 0; | |
color: mediumseagreen; | |
} | |
header h1 span { | |
color: orangered; | |
} | |
header .logo { | |
color: white; | |
background-color: orangered; | |
text-decoration: none; | |
display: inline-block; | |
padding: 10px; | |
outline: 1px solid orangered; | |
outline-offset: 1px; | |
margin: 2px; | |
} | |
header .logo { | |
float: left; | |
margin-right: 20px; | |
margin-bottom: 20px; | |
} | |
footer, footer a { | |
font-size: small; | |
text-align: center; | |
color: lightseagreen; | |
} | |
header, footer, .content { | |
padding: 20px; | |
} | |
button { | |
padding: 10px; | |
min-width: 80px; | |
} | |
form label { | |
display: inline-block; | |
margin-bottom: 5px; | |
} | |
form input { | |
width: 50%; | |
padding: 10px; | |
display: block; | |
margin-bottom: 10px; | |
} | |
form#addForm input { | |
display: inline-block; | |
} | |
table#todos { | |
width: 100%; | |
margin: 30px auto; | |
border-collapse: collapse; | |
} | |
table#todos tr { | |
border-top: 1px solid cyan; | |
border-bottom: 1px solid cyan; | |
height: 60px; | |
} | |
table#todos tr:hover { | |
background-color: lightblue; | |
} | |
table#todos td { | |
padding: 10px; | |
} | |
table#todos td:nth-child(2) { | |
color: grey; | |
text-align: right; | |
font-size: x-large; | |
} | |
table#todos tr td:nth-child(2) * { | |
visibility: hidden; | |
} | |
table#todos tr:hover td:nth-child(2) * { | |
visibility: visible; | |
} | |
table#todos .action { | |
cursor: pointer; | |
padding-left: 0.5em; | |
} | |
table#todos .action:hover { | |
color: black; | |
} | |
table#todos tr.done td:first-child { | |
text-decoration: line-through; | |
} | |
</style> | |
</head> | |
<body> | |
<header> | |
<a href="https://gist.github.com/calogxro/6e601e07c2a937df4418d104fb717570" | |
class="logo"><span><i class="fas fa-mug-hot"></i> Caffeinated</span></a> | |
<h1>My To<span>Do</span> List</h1> | |
</header> | |
<div id="app" class="content"> | |
<form id="addForm" v-if="showAddForm" v-on:submit.prevent="addTodo"> | |
<input v-model="newText" type="text" placeholder="Thing to do"/> | |
<button>Add</button> | |
</form> | |
<form v-else v-on:submit.prevent="modifyTodo"> | |
<label>Id</label> | |
<input v-model="selectedTodo.id" type="text" disabled/> | |
<label>Text</label> | |
<input v-model="newText" type="text" placeholder="Thing to do"/><br> | |
<button value="replace">Modify</button> | |
<button value="cancel">Cancel</button> | |
</form> | |
<table id="todos"> | |
<tr class="sticky"> | |
<td>Learn JavaScript</td> | |
<td><span class="sticky-label">sticky</span></td> | |
</tr> | |
<tr class="sticky"> | |
<td>Learn Vue.js</td> | |
<td><span class="sticky-label">sticky</span></td> | |
</tr> | |
<tr class="sticky"> | |
<td>Build Something Awesome</td> | |
<td><span class="sticky-label">sticky</span></td> | |
</tr> | |
<tr v-for="todo in todos" v-bind:class="{ done: todo.done }"> | |
<td> | |
{{todo.text}} | |
</td> | |
<td class="actions"> | |
<!-- toggle done/undone --> | |
<i v-if="todo.done" class="fas fa-times-circle action" @click="toggleDoneTodo(todo.id)" title="Mark as undone"></i> | |
<i v-else class="fas fa-check-circle action" @click="toggleDoneTodo(todo.id)" title="Mark as done"></i> | |
<!-- end toggle done/undone --> | |
<i class="fas fa-edit action" @click="editTodo(todo.id)" title="Edit"></i> | |
<i class="fas fa-trash-alt action" @click="deleteTodo(todo.id)" title="Delete"></i> | |
</td> | |
</tr> | |
</table> | |
</div> | |
<footer> | |
© 2021 <a href="https://github.com/calogxro">Calogero Miraglia</a> | | |
Made in Sicily <i class="fas fa-umbrella-beach"></i> with Vue.js | | |
Powered by <a href="https://github.com/rehacktive">aw4y</a>'s | |
<a href="https://github.com/rehacktive/caffeine">Caffeine</a> | | |
<a href="https://gist.github.com/calogxro/6e601e07c2a937df4418d104fb717570" | |
>GitHub</a> | |
</footer> | |
<script> | |
const HOST = document.location.hostname | |
const ENDPOINT = `http://${HOST}:8000/ns/todos` | |
const app = new Vue({ | |
el: "#app", | |
data: { | |
nextId: 1, | |
todos: [], | |
showAddForm: true, | |
newText: "", | |
selectedTodo: null | |
}, | |
mounted() { | |
axios.get(ENDPOINT) | |
.then(response => { | |
for (const data of response.data) { | |
this.todos.push({ | |
id: data.value.id, | |
text: data.value.text, | |
done: data.value.done | |
}) | |
// update nextId | |
if (data.value.id >= this.nextId) { | |
this.nextId = data.value.id + 1 | |
} | |
} | |
this.sort() | |
}) | |
.catch(error => { | |
// If a "400 Bad Request" error is returned | |
// it means the collection does not exist | |
if (error.response.status !== 400) { | |
this.onError(error) | |
} | |
}) | |
}, | |
methods: { | |
addTodo() { | |
if (this.newText.trim() === "") { | |
return | |
} | |
axios.post(`${ENDPOINT}/${this.nextId}`, { | |
id: this.nextId, | |
text: this.newText, | |
done: false | |
}) | |
.then(response => { | |
this.newText = "" | |
this.todos.push({ | |
id: response.data.id, | |
text: response.data.text, | |
done: response.data.done | |
}) | |
this.nextId++ | |
this.sort() | |
}) | |
.catch(error => { | |
this.onError(error) | |
}) | |
}, | |
deleteTodo(id) { | |
axios.delete(`${ENDPOINT}/${id}`) | |
.then(response => { | |
this.todos = this.todos.filter(todo => { | |
return todo.id !== id | |
}) | |
// the deleted element might be selected | |
this.resetForm() | |
}) | |
.catch(error => { | |
this.onError(error) | |
}) | |
}, | |
editTodo(id) { | |
this.showAddForm = false | |
this.selectedTodo = this.todos.find(todo => { | |
return todo.id === id | |
}) | |
this.newText = this.selectedTodo.text | |
this.scrollToTop() | |
}, | |
modifyTodo(event) { | |
const id = this.selectedTodo.id | |
const action = event.submitter.value | |
if (action === "cancel") { | |
this.resetForm() | |
} else if (action === "replace") { | |
axios.post(`${ENDPOINT}/${id}`, { | |
id: id, | |
text: this.newText, | |
done: this.selectedTodo.done | |
}) | |
.then(response => { | |
this.selectedTodo.text = this.newText | |
this.resetForm() | |
}) | |
.catch(error => { | |
this.onError(error) | |
}) | |
} | |
}, | |
toggleDoneTodo(id) { | |
let todo = this.todos.find(todo => { | |
return todo.id === id | |
}) | |
axios.post(`${ENDPOINT}/${id}`, { | |
id: id, | |
text: todo.text, | |
done: ! todo.done | |
}) | |
.then(response => { | |
todo.done = response.data.done | |
this.sort() | |
}) | |
.catch(error => { | |
this.onError(error) | |
}) | |
}, | |
sort() { | |
this.todos.sort((a, b) => { | |
if (a.done !== b.done) { | |
if (a.done) { | |
return 1 | |
} | |
return -1 | |
} | |
return a.id - b.id | |
}) | |
}, | |
resetForm() { | |
this.newText = "" | |
this.selectedTodo = null | |
this.showAddForm = true | |
}, | |
scrollToTop() { | |
window.scrollTo(0, 0) | |
}, | |
onError(error) { | |
alert(error) | |
} | |
} | |
}) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment