Kasianov Nikolai Alekseevich
3 months ago
19 changed files with 575 additions and 685 deletions
@ -0,0 +1,111 @@
|
||||
{{ template "base" . }} |
||||
|
||||
{{ define "content" }} |
||||
|
||||
<h1 style="display: none;" id="categoryId">{{.CurrentGroupId}}</h1> |
||||
|
||||
<!-- Main --> |
||||
<main class="d-flex flex-wrap"> |
||||
|
||||
<!-- Sidebar --> |
||||
<div id="sidebar" class="flex-shrink-1 p-2 d-flex flex-column align-items-stretch bg-body-tertiary" style="width: 380px;"> |
||||
<a href="/" class="d-flex align-items-center flex-shrink-0 p-3 link-body-emphasis text-decoration-none border-bottom"> |
||||
<svg class="bi pe-none me-2" width="30" height="24"><use xlink:href="#bootstrap"></use></svg> |
||||
<span class="fs-5 fw-semibold">Categories</span> |
||||
</a> |
||||
{{ range .Groups }} |
||||
<div class="list-group list-group-flush border-bottom scrollarea"> |
||||
<a href="/group/{{.ID}}" class="list-group-item list-group-item-action active py-3 lh-sm" aria-current="true"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">{{ .Name }}</strong> |
||||
<small>{{ .TimeCreatedUnix }}</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Is removable: {{ .Removable }}</div> |
||||
</a> |
||||
</div> |
||||
{{ end }} |
||||
</div> |
||||
|
||||
|
||||
<div class="p-2 flex-grow-1"> |
||||
<form action="javascript:void(0);"> |
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="col"> |
||||
<input type="text" class="form-control" id="new-todo-text" placeholder="TODO text"> |
||||
</div> |
||||
<div class="col"> |
||||
<input type="datetime-local" name="new-todo-due" id="new-todo-due" placeholder="Due"> |
||||
</div> |
||||
<div class="col"> |
||||
<button id="new-todo-submit" class="btn btn-primary">Add</button> |
||||
<button class="btn btn-secondary" id="show-done">Show Done</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</form> |
||||
|
||||
<div class="container text-center"> |
||||
<table class="table table-hover"> |
||||
<thead> |
||||
<th>ToDo</th> |
||||
<th>Created</th> |
||||
<th>Due</th> |
||||
<th>Group Id</th> |
||||
</thead> |
||||
<tbody id="todos" class="text-break"> |
||||
{{ range .Todos }} |
||||
<tr> |
||||
<td>{{ .Text }}</td> |
||||
<td>{{ .TimeCreatedUnix }}</td> |
||||
<td>{{ .DueUnix }}</td> |
||||
<td>{{ .GroupID }}</td> |
||||
</tr> |
||||
{{ end }} |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
|
||||
<script> |
||||
document.addEventListener('DOMContentLoaded', async function() { |
||||
document.getElementById("new-todo-text").focus(); |
||||
|
||||
let showDoneButton = document.getElementById("show-done"); |
||||
showDoneButton.addEventListener("click", (event) => { |
||||
// Rename the button |
||||
showDoneButton.innerText = "Show To Do"; |
||||
showDoneButton.className = "btn btn-success"; |
||||
|
||||
// Make it "reset to default" |
||||
showDoneButton.addEventListener("click", (event) => { |
||||
location.reload(); |
||||
}); |
||||
}); |
||||
|
||||
|
||||
// "Add" button |
||||
document.getElementById("new-todo-submit").addEventListener("click", async (event) => { |
||||
let newTodoTextInput = document.getElementById("new-todo-text"); |
||||
let newTodoText = newTodoTextInput.value; |
||||
if (newTodoText.length < 1) { |
||||
newTodoTextInput.setCustomValidity("At least one character is needed!"); |
||||
return; |
||||
} else { |
||||
newTodoTextInput.setCustomValidity(""); |
||||
} |
||||
newTodoTextInput.value = ""; |
||||
|
||||
let groupId = document.getElementById("categoryId").innerText; |
||||
|
||||
// Make a request |
||||
let response = await postNewTodo({text: newTodoText, groupId: Number(groupId)}); |
||||
if (response.ok) { |
||||
location.reload(); |
||||
} |
||||
}); |
||||
}); |
||||
</script> |
||||
|
||||
{{ end }} |
@ -0,0 +1,12 @@
|
||||
{{ template "base" . }} |
||||
|
||||
{{ define "content" }} |
||||
<main class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column"> |
||||
<div class="p-2 flex-fill text-wrap text-center"> |
||||
<h1>Error!</h1> |
||||
<p>You have encountered an error</p> |
||||
<p>Try to reload the page or try again later</p> |
||||
</div> |
||||
</main> |
||||
|
||||
{{ end }} |
@ -1,270 +0,0 @@
|
||||
{{ template "base" . }} |
||||
|
||||
{{ define "content" }} |
||||
|
||||
<!-- Sidebar --> |
||||
<div id="sidebar" class="flex-shrink-1 p-2 d-flex flex-column align-items-stretch bg-body-tertiary" style="width: 380px;"> |
||||
<a href="/" class="d-flex align-items-center flex-shrink-0 p-3 link-body-emphasis text-decoration-none border-bottom"> |
||||
<svg class="bi pe-none me-2" width="30" height="24"><use xlink:href="#bootstrap"></use></svg> |
||||
<span class="fs-5 fw-semibold">List group</span> |
||||
</a> |
||||
<div class="list-group list-group-flush border-bottom scrollarea"> |
||||
<a href="#" class="list-group-item list-group-item-action active py-3 lh-sm" aria-current="true"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small>Wed</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Tues</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Mon</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
|
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm" aria-current="true"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Wed</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Tues</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Mon</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm" aria-current="true"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Wed</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Tues</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Mon</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm" aria-current="true"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Wed</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Tues</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
<a href="#" class="list-group-item list-group-item-action py-3 lh-sm"> |
||||
<div class="d-flex w-100 align-items-center justify-content-between"> |
||||
<strong class="mb-1">List group item heading</strong> |
||||
<small class="text-body-secondary">Mon</small> |
||||
</div> |
||||
<div class="col-10 mb-1 small">Some placeholder content in a paragraph below the heading and date.</div> |
||||
</a> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
<form action="javascript:void(0);"> |
||||
<div class="container text-center"> |
||||
<div class="row"> |
||||
<div class="col"> |
||||
<input type="text" class="form-control" id="new-todo-text" placeholder="TODO text"> |
||||
</div> |
||||
<div class="col"> |
||||
<button id="new-todo-submit" class="btn btn-primary">Add</button> |
||||
<button class="btn btn-secondary" id="show-done">Show Done</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</form> |
||||
|
||||
|
||||
<div class="container text-center" style="margin-top: 4ch;"> |
||||
<table id="todos" class="table table-hover" style="word-wrap: break-word;"> |
||||
{{ for .Todos }} |
||||
<tr> |
||||
{{ .Name }} |
||||
</tr> |
||||
{{ end }} |
||||
</table> |
||||
</div> |
||||
|
||||
|
||||
<script> |
||||
async function displayTodos(showDone) { |
||||
let username = getUsername(); |
||||
let password = getUserPassword(); |
||||
|
||||
// Fetch and display TODOs |
||||
let response = await getTodos(username, password); |
||||
let todosJson = await response.json(); |
||||
let todosDisplayed = []; |
||||
|
||||
if (response.ok && todosJson != null) { |
||||
let todosDiv = document.getElementById("todos"); |
||||
// Clear what we've had before |
||||
todosDiv.innerHTML = ""; |
||||
|
||||
todosJson.forEach((item) => { |
||||
if (showDone === true && item.isDone == true) { |
||||
// An already done Todo |
||||
|
||||
let todoDeleteBtnID = "btn-delete-" + String(item.id); |
||||
|
||||
// Display |
||||
let timeCreated = new Date(item.timeCreatedUnix * 1000); |
||||
let timeDone = new Date(item.completionTimeUnix * 1000); |
||||
todosDiv.innerHTML += "<tr><td>" + item.text + "</td>" + |
||||
"<td>" + " " + timeCreated.getDate() + "/" + (timeCreated.getMonth() + 1) + "/" + timeCreated.getFullYear() + " | " + |
||||
timeDone.getDate() + "/" + (timeDone.getMonth() + 1) + "/" + timeDone.getFullYear() + "</td>" + |
||||
"<td>" + "<button class='btn btn-danger' id='" + |
||||
todoDeleteBtnID + "'><img src='/static/images/trash3-fill.svg'></button></td></tr>"; |
||||
|
||||
|
||||
|
||||
todosDisplayed.push({item: item, buttonDel: todoDeleteBtnID}); |
||||
|
||||
|
||||
} else if (showDone === false && item.isDone == false) { |
||||
// A yet to be done Todo |
||||
|
||||
let todoCompleteBtnID = "btn-complete-" + String(item.id); |
||||
let todoDeleteBtnID = "btn-delete-" + String(item.id); |
||||
let todoEditBtnID = "btn-edit-" + String(item.id); |
||||
|
||||
// Display |
||||
let timeCreated = new Date(item.timeCreatedUnix * 1000); |
||||
todosDiv.innerHTML += "<tr><td>" + item.text + "</td>" + |
||||
"<td>" + " " + timeCreated.getDate() + "/" + (timeCreated.getMonth() + 1) + "/" + timeCreated.getFullYear() + "</td>" + |
||||
"<td><button class='btn btn-success' id='" + todoCompleteBtnID + "'>" + |
||||
"<img src='/static/images/check.svg'></button><button class='btn btn-danger' id='" + |
||||
todoDeleteBtnID + "'><img src='/static/images/trash3-fill.svg'></button></td></tr>"; |
||||
|
||||
todosDisplayed.push({item: item, buttonDel: todoDeleteBtnID, buttonComplete: todoCompleteBtnID}); |
||||
|
||||
} |
||||
}); |
||||
} |
||||
|
||||
|
||||
// Loop over all buttons (doesn't matter which ones because the amounts are equal) |
||||
for (let i = 0; i < todosDisplayed.length; i++) { |
||||
let elem = todosDisplayed[i]; |
||||
|
||||
if (showDone === false && elem.item.isDone === false) { |
||||
// Done button |
||||
document.getElementById(elem.buttonComplete).addEventListener("click", async (event) => { |
||||
// Mark as done |
||||
elem.item.isDone = true; |
||||
// Set completion time |
||||
elem.item.completionTimeUnix = Math.floor(Date.now() / 1000); |
||||
|
||||
// Update |
||||
response = await updateTodo(username, password, elem.item.id, elem.item); |
||||
if (response.ok) { |
||||
location.reload(); |
||||
} |
||||
}); |
||||
|
||||
// Delete button |
||||
document.getElementById(elem.buttonDel).addEventListener("click", async (event) => { |
||||
response = await deleteTodo(username, password, elem.item.id); |
||||
if (response.ok) { |
||||
location.reload(); |
||||
} |
||||
}); |
||||
} else { |
||||
// Delete button |
||||
document.getElementById(elem.buttonDel).addEventListener("click", async (event) => { |
||||
response = await deleteTodo(username, password, elem.item.id); |
||||
if (response.ok) { |
||||
location.reload(); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async function() { |
||||
let username = getUsername(); |
||||
let password = getUserPassword(); |
||||
|
||||
document.getElementById("new-todo-text").focus(); |
||||
|
||||
|
||||
let showDoneButton = document.getElementById("show-done"); |
||||
showDoneButton.addEventListener("click", (event) => { |
||||
displayTodos(true); // Re-display without reloading |
||||
|
||||
// Rename the button |
||||
showDoneButton.innerText = "Show To Do"; |
||||
showDoneButton.className = "btn btn-success"; |
||||
|
||||
// Make it "reset to default" |
||||
showDoneButton.addEventListener("click", (event) => { |
||||
location.reload(); |
||||
}); |
||||
}); |
||||
|
||||
|
||||
// "Add" button |
||||
document.getElementById("new-todo-submit").addEventListener("click", async (event) => { |
||||
let newTodoTextInput = document.getElementById("new-todo-text"); |
||||
let newTodoText = newTodoTextInput.value; |
||||
if (newTodoText.length < 1) { |
||||
newTodoTextInput.setCustomValidity("At least one character is needed!"); |
||||
return; |
||||
} else { |
||||
newTodoTextInput.setCustomValidity(""); |
||||
} |
||||
newTodoTextInput.value = ""; |
||||
|
||||
// Make a request |
||||
let response = await postNewTodo(username, password, {text: newTodoText}); |
||||
if (response.ok) { |
||||
location.reload(); |
||||
} |
||||
}); |
||||
|
||||
|
||||
// Fetch and display TODOs |
||||
await displayTodos(false); |
||||
}, false) |
||||
</script> |
||||
|
||||
{{ end }} |
@ -1,83 +1,83 @@
|
||||
/* |
||||
2023 Kasyanov Nikolay Alexeyevich (Unbewohnte) |
||||
2024 Kasyanov Nikolay Alexeyevich (Unbewohnte) |
||||
*/ |
||||
|
||||
|
||||
async function post(url, login, password, json) { |
||||
async function post(url, json) { |
||||
return fetch(url, { |
||||
method: "POST", |
||||
credentials: "include", |
||||
headers: { |
||||
"Authorization": "Basic " + btoa(login + ":" + password), |
||||
"Content-Type": "application/json", |
||||
}, |
||||
body: JSON.stringify(json) |
||||
}) |
||||
} |
||||
|
||||
async function postNewTodo(login, password, newTodo) { |
||||
return post("/api/todo/create", login, password, newTodo) |
||||
|
||||
async function postNewTodo(newTodo) { |
||||
return post("/api/todo/create", newTodo) |
||||
} |
||||
|
||||
async function postNewGroup(login, password, newGroup) { |
||||
return post("/api/group/create", login, password, newGroup) |
||||
async function postNewGroup(newGroup) { |
||||
return post("/api/group/create", newGroup) |
||||
} |
||||
|
||||
async function postNewUser(newUser) { |
||||
return post("/api/user/create", "", "", newUser) |
||||
return post("/api/user/create", newUser) |
||||
} |
||||
|
||||
async function get(url, login, password) { |
||||
async function get(url) { |
||||
return fetch(url, { |
||||
method: "GET", |
||||
credentials: "include", |
||||
headers: { |
||||
"Authorization": "Basic " + btoa(login + ":" + password), |
||||
"Content-Type": "application/json", |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
async function getUser(login, password) { |
||||
return get("/api/user/get", login, password); |
||||
async function getUser() { |
||||
return get("/api/user/get"); |
||||
} |
||||
|
||||
async function getTodos(login, password) { |
||||
return get("/api/todo/get", login, password); |
||||
async function getTodos() { |
||||
return get("/api/todo/get"); |
||||
} |
||||
|
||||
async function getGroup(login, password) { |
||||
return get("/api/group/get", login, password); |
||||
async function getGroup() { |
||||
return get("/api/group/get"); |
||||
} |
||||
|
||||
async function getAllGroups(login, password) { |
||||
return get("/api/user/get", login, password); |
||||
async function getAllGroups() { |
||||
return get("/api/user/get"); |
||||
} |
||||
|
||||
async function del(url, login, password) { |
||||
async function del(url) { |
||||
return fetch(url, { |
||||
method: "DELETE", |
||||
credentials: "include", |
||||
headers: { |
||||
"Authorization": "Basic " + btoa(login + ":" + password), |
||||
"Content-Type": "application/json", |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
async function deleteTodo(login, password, id) { |
||||
return del("/api/todo/delete/"+id, login, password); |
||||
async function deleteTodo(id) { |
||||
return del("/api/todo/delete/"+id); |
||||
} |
||||
|
||||
async function update(url, login, password, json) { |
||||
return post(url, login, password, json); |
||||
async function update(url, json) { |
||||
return post(url, json); |
||||
} |
||||
|
||||
async function updateTodo(login, password, id, updatedTodo) { |
||||
return update("/api/todo/update/"+id, login, password, updatedTodo); |
||||
async function updateTodo(id, updatedTodo) { |
||||
return update("/api/todo/update/"+id, updatedTodo); |
||||
} |
||||
|
||||
async function updateGroup(login, password, id, updatedGroup) { |
||||
return update("/api/group/update/"+id, login, password, updateGroup); |
||||
async function updateGroup(id, updatedGroup) { |
||||
return update("/api/group/update/"+id, updatedGroup); |
||||
} |
||||
|
||||
async function updateUser(login, password, updatedUser) { |
||||
return update("/api/group/update/"+login, login, password, updatedUser); |
||||
async function updateUser(updatedUser) { |
||||
return update("/api/user/update", updatedUser); |
||||
} |
Loading…
Reference in new issue