Browse Source

Feature: ToDo dropdown

master
parent
commit
192c48eaa6
  1. 52
      pages/category.html
  2. 17
      src/db/group.go
  3. 33
      src/server/endpoints.go

52
pages/category.html

@ -16,13 +16,13 @@
<div class="list-group list-group-flush border-bottom scrollarea"> <div class="list-group list-group-flush border-bottom scrollarea">
{{ range .Groups }} {{ range .Groups }}
<a href="/group/{{.ID}}" class="list-group-item list-group-item-action py-3 lh-sm {{if eq .ID $.CurrentGroupId}} active {{end}}" aria-current="true"> <a id="group-{{.ID}}" href="/group/{{.ID}}" class="list-group-item list-group-item-action py-3 lh-sm {{if eq .ID $.CurrentGroupId}} active {{end}}" aria-current="true" ondragover="allowDrop(event);" ondrop="drop(event);">
<div class="d-flex w-100 align-items-center justify-content-between"> <div id="group-{{.ID}}" class="d-flex w-100 align-items-center justify-content-between">
<strong class="mb-1">{{ .Name }}</strong> <strong id="group-{{.ID}}" class="mb-1">{{ .Name }}</strong>
<small>{{ .TimeCreated }}</small> <small id="group-{{.ID}}">{{ .TimeCreated }}</small>
</div> </div>
{{ if not .Removable }} {{ if not .Removable }}
<div class="col-10 mb-1 small">Not removable</div> <div id="group-{{.ID}}" class="col-10 mb-1 small">Not removable</div>
{{ end }} {{ end }}
</a> </a>
{{ end }} {{ end }}
@ -61,10 +61,11 @@
<tbody class="text-break"> <tbody class="text-break">
{{ range .Todos }} {{ range .Todos }}
{{ if not .IsDone }} {{ if not .IsDone }}
<tr> <tr draggable="true" id="todo-{{.ID}}" ondragstart="dragStart(event);">
<td>{{ .Text }}</td> <td class="todo-text">{{ .Text }}</td>
<td>{{ .TimeCreated }}</td> <td class="todo-created">{{ .TimeCreated }}</td>
<td>{{ .Due }}</td> <td class="todo-due">{{ .Due }}</td>
<td class="todo-due-unix" style="display: none;">{{ .DueUnix }}</td>
<td> <td>
<button class="btn btn-success" onclick="markAsDoneRefresh({{.ID}});"><img src='/static/images/check.svg'></button> <button class="btn btn-success" onclick="markAsDoneRefresh({{.ID}});"><img src='/static/images/check.svg'></button>
<button class="btn btn-danger" onclick="deleteTodoRefresh({{.ID}});"><img src='/static/images/trash3-fill.svg'></button> <button class="btn btn-danger" onclick="deleteTodoRefresh({{.ID}});"><img src='/static/images/trash3-fill.svg'></button>
@ -121,6 +122,39 @@ async function showDone() {
dueTodos.style.display = "none"; dueTodos.style.display = "none";
} }
function allowDrop(event) {
event.preventDefault();
}
function dragStart(event) {
event.dataTransfer.setData("text", event.target.id);
event.dataTransfer.effectAllowed = "move";
}
async function drop(event) {
event.preventDefault();
var todoPageId = event.dataTransfer.getData("text");
let draggedTodo = document.getElementById(todoPageId);
let todoId = todoPageId.split("-")[1];
let targetGroupId = event.target.id.split("-")[1];
if (targetGroupId == document.getElementById("categoryId").innerText) {
// Do nothing
return;
}
console.log("passed");
// Add a copy of this ToDo in the corresponding group
let result = await postNewTodo({
text: draggedTodo.getElementsByClassName("todo-text")[0].innerText,
groupId: Number(targetGroupId),
dueUnix: Number(draggedTodo.getElementsByClassName("todo-due-unix")[0].innerText),
});
// Delete this ToDo in this group
await deleteTodoRefresh(todoId);
}
document.addEventListener('DOMContentLoaded', async function() { document.addEventListener('DOMContentLoaded', async function() {
document.getElementById("new-todo-text").focus(); document.getElementById("new-todo-text").focus();

17
src/db/group.go

@ -150,6 +150,23 @@ func (db *DB) DeleteTodoGroup(id uint64) error {
return err return err
} }
// Deletes all ToDos associated with this group and then the group itself
func (db *DB) DeleteTodoGroupClean(groupId uint64) error {
_, err := db.Exec("DELETE FROM todos WHERE group_id=?",
groupId,
)
if err != nil {
return err
}
_, err = db.Exec(
"DELETE FROM todo_groups WHERE id=?",
groupId,
)
return err
}
// Updates TODO group's name // Updates TODO group's name
func (db *DB) UpdateTodoGroup(groupID uint64, updatedGroup TodoGroup) error { func (db *DB) UpdateTodoGroup(groupID uint64, updatedGroup TodoGroup) error {
_, err := db.Exec( _, err := db.Exec(

33
src/server/endpoints.go

@ -407,6 +407,11 @@ func (s *Server) EndpointTodoDelete(w http.ResponseWriter, req *http.Request) {
func (s *Server) EndpointTodoCreate(w http.ResponseWriter, req *http.Request) { func (s *Server) EndpointTodoCreate(w http.ResponseWriter, req *http.Request) {
// Create a new TODO // Create a new TODO
defer req.Body.Close() defer req.Body.Close()
if req.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Read body // Read body
body, err := io.ReadAll(req.Body) body, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
@ -431,6 +436,16 @@ func (s *Server) EndpointTodoCreate(w http.ResponseWriter, req *http.Request) {
} }
// Add TODO to the database // Add TODO to the database
if newTodo.GroupID == 0 {
http.Error(w, "No group ID was provided", http.StatusBadRequest)
return
}
if !s.db.DoesUserOwnGroup(newTodo.GroupID, GetLoginFromReq(req)) {
http.Error(w, "You do not own this group", http.StatusForbidden)
return
}
newTodo.OwnerLogin = GetLoginFromReq(req) newTodo.OwnerLogin = GetLoginFromReq(req)
newTodo.TimeCreatedUnix = uint64(time.Now().Unix()) newTodo.TimeCreatedUnix = uint64(time.Now().Unix())
err = s.db.CreateTodo(newTodo) err = s.db.CreateTodo(newTodo)
@ -485,6 +500,11 @@ func (s *Server) EndpointTodoGroupDelete(w http.ResponseWriter, req *http.Reques
// Delete an existing group // Delete an existing group
defer req.Body.Close() defer req.Body.Close()
if req.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Check if given user actually owns this group // Check if given user actually owns this group
if !IsUserAuthorizedReq(req, s.db) { if !IsUserAuthorizedReq(req, s.db) {
http.Error(w, "Invalid user auth data", http.StatusForbidden) http.Error(w, "Invalid user auth data", http.StatusForbidden)
@ -516,8 +536,8 @@ func (s *Server) EndpointTodoGroupDelete(w http.ResponseWriter, req *http.Reques
return return
} }
// Delete // Delete all ToDos associated with this group and then delete the group itself
err = s.db.DeleteTodoGroup(groupId) err = s.db.DeleteTodoGroupClean(groupId)
if err != nil { if err != nil {
logger.Error("[Server][EndpointGroupDelete] Failed to delete %s's TODO group: %s", GetLoginFromReq(req), err) logger.Error("[Server][EndpointGroupDelete] Failed to delete %s's TODO group: %s", GetLoginFromReq(req), err)
http.Error(w, "Failed to delete TODO group", http.StatusInternalServerError) http.Error(w, "Failed to delete TODO group", http.StatusInternalServerError)
@ -525,13 +545,19 @@ func (s *Server) EndpointTodoGroupDelete(w http.ResponseWriter, req *http.Reques
} }
// Success! // Success!
logger.Info("[Server][EndpointGroupDelete] Deleted group ID: %d for %s", groupId, GetLoginFromReq(req)) logger.Info("[Server][EndpointGroupDelete] Cleanly deleted group ID: %d for %s", groupId, GetLoginFromReq(req))
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
func (s *Server) EndpointTodoGroupCreate(w http.ResponseWriter, req *http.Request) { func (s *Server) EndpointTodoGroupCreate(w http.ResponseWriter, req *http.Request) {
// Create a new TODO group // Create a new TODO group
defer req.Body.Close() defer req.Body.Close()
if req.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Read body // Read body
body, err := io.ReadAll(req.Body) body, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
@ -568,7 +594,6 @@ func (s *Server) EndpointTodoGroupCreate(w http.ResponseWriter, req *http.Reques
// Success! // Success!
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
logger.Info("[Server] Created a new TODO group for %s", newGroup.OwnerLogin) logger.Info("[Server] Created a new TODO group for %s", newGroup.OwnerLogin)
} }
func (s *Server) EndpointTodoGroupGet(w http.ResponseWriter, req *http.Request) { func (s *Server) EndpointTodoGroupGet(w http.ResponseWriter, req *http.Request) {

Loading…
Cancel
Save