Skip to content

Commit

Permalink
feat(todo): enhance todo functionality and error handling
Browse files Browse the repository at this point in the history
- Refactored the JavaScript code for creating and displaying todo items to use a template-based approach.
- Improved error handling and user feedback for adding, deleting, and updating todo items.
- Updated API endpoints and views to include better validation and error messages.
- Enhanced the UI with new icons and required fields for better user experience.
- Added a hidden template for todo items to streamline the creation of new items dynamically.
  • Loading branch information
psyray committed Sep 10, 2024
1 parent 9e45e8e commit 22583af
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 114 deletions.
22 changes: 15 additions & 7 deletions web/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,11 +580,21 @@ def post(self, request):
req = self.request
data = req.data

subdomain_id = data.get('subdomain_id')
scan_history_id = data.get('scan_history_id')
subdomain_id = data.get('subdomain')
scan_history_id = data.get('scan_history')
title = data.get('title')
description = data.get('description')
project = data.get('project')

if not title:
return Response({"status": False, "error": "Title is required."}, status=400)
if scan_history_id is None:
return Response({"status": False, "error": "Scan history is required."}, status=400)
if subdomain_id is None:
return Response({"status": False, "error": "Subdomain is required."}, status=400)
if not project:
return Response({"status": False, "error": "Project is required."}, status=400)


try:
project = Project.objects.get(slug=project)
Expand All @@ -608,12 +618,10 @@ def post(self, request):

note.project = project
note.save()
response = {'status': True}
return Response({"status": True, "error": False, "id": note.id}, status=200)
except Exception as e:
response = {'status': False, 'message': str(e)}

return Response(response)

logger.error(e)
return Response({"status": False, "error": "An error occurred."}, status=400)

class ToggleSubdomainImportantStatus(APIView):
def post(self, request):
Expand Down
161 changes: 75 additions & 86 deletions web/recon_note/static/note/js/todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,47 +89,6 @@ function populateTodofunction(project=null){
$_targetText += ' Subdomain : ' + $_taskSubdomain;
}

$html = '<div class="todo-item all-list">'+
'<div class="todo-item-inner">'+
'<div class="n-chk text-center">'+
'<label class="new-control new-checkbox checkbox-primary">'+
'<input type="checkbox" class="form-check-input inbox-chkbox">'+
'<span class="new-control-indicator"></span>'+
'</label>'+
'</div>'+

'<div class="todo-content">'+
'<h5 class="todo-heading">'+htmlEncode($_task)+'</h5>'+
'<p class="target">'+$_targetText+'</h5>'+
"<p class='todo-text' >"+htmlEncode($_taskDescriptionText)+"</p>"+
'</div>'+

'<div class="action-dropdown custom-dropdown-icon">'+
'<div class="dropdown dropstart">'+
'<a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">'+
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-more-vertical"><circle cx="12" cy="12" r="1"></circle><circle cx="12" cy="5" r="1"></circle><circle cx="12" cy="19" r="1"></circle></svg>'+
'</a>'+

'<div class="dropdown-menu">'+
'<a class="important dropdown-item" href="javascript:void(0);">Toggle Important</a>'+
'<a class="dropdown-item delete" href="javascript:void(0);">Delete</a>'+
'</div>'+
'</div>'+
'</div>'+

'</div>'+
'</div>';


$("#ct").prepend($html);
$('#addTaskModal').modal('hide');
checkCheckbox();
todoItem();
importantDropdown();
deleteDropdown();
new dynamicBadgeNotification('allList');
$(".list-actions#all-list").trigger('click');

data = {
'title': $_task,
'description': $_taskDescriptionText
Expand All @@ -154,25 +113,64 @@ function populateTodofunction(project=null){
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(res => res.json())
.then(res => console.log(res));
}).then(function (response) {
return response.json().then(function(data) {
swal.queue([{
title: response.status === 200 ? 'Note added successfully!' :
response.status === 400 ? 'Oops! Unable to add todo!\r\n' + data.error :
response.status === 404 ? 'Oops! Note not found!\r\n' + data.error :
'Oops! An error occurred!\r\n' + data.error,
icon: response.status === 200 ? 'success' : 'error'
}]);

if (response.status === 200) {
const newNote = {
id: data.id,
title: $_task,
description: $_taskDescriptionText,
domain_name: $_targetText,
subdomain_name: $_taskSubdomain,
is_done: false
};

let todoHTML = $('#todo-template').html();

todoHTML = todoHTML
.replace(/{task_id}/g, newNote.id)
.replace(/{title}/g, htmlEncode(newNote.title))
.replace(/{target_text}/g, newNote.domain_name ? `Domain: ${newNote.domain_name}` : '')
.replace(/{description}/g, htmlEncode(newNote.description))
.replace(/{is_done}/g, newNote.is_done ? 'todo-task-done' : '')
.replace(/{checked}/g, newNote.is_done ? 'checked' : '');

// Créer un nouvel élément avec la classe todo-item
const $newTodo = $('<div class="todo-item all-list"></div>').append(todoHTML);

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
DOM text
is reinterpreted as HTML without escaping meta-characters.

$("#ct").prepend($newTodo);
$('#addTaskModal').modal('hide');
checkCheckbox();
todoItem();
importantDropdown();
deleteDropdown();
new dynamicBadgeNotification('allList');
$(".list-actions#all-list").trigger('click');
}
});
});
});

$('.tab-title .nav-pills a.nav-link').on('click', function(event) {
$(this).parents('.mail-box-container').find('.tab-title').removeClass('mail-menu-show')
$(this).parents('.mail-box-container').find('.mail-overlay').removeClass('mail-overlay-show')
})

}

function dynamicBadgeNotification( setTodoCategoryCount ) {
function dynamicBadgeNotification(setTodoCategoryCount) {
var todoCategoryCount = setTodoCategoryCount;

// Get Parents Div(s)
var get_ParentsDiv = $('.todo-item');
var get_TodoAllListParentsDiv = $('.todo-item.all-list');
var get_TodoCompletedListParentsDiv = $('.todo-item.todo-task-done');
var get_TodoImportantListParentsDiv = $('.todo-item.todo-task-important');
var get_TodoAllListParentsDiv = $('.todo-item.all-list').not('.todo-item-template'); // Ignorer le modèle
var get_TodoCompletedListParentsDiv = $('.todo-item.todo-task-done').not('.todo-item-template'); // Ignorer le modèle
var get_TodoImportantListParentsDiv = $('.todo-item.todo-task-important').not('.todo-item-template'); // Ignorer le modèle

// Get Parents Div(s) Counts
var get_TodoListElementsCount = get_TodoAllListParentsDiv.length;
Expand All @@ -184,7 +182,6 @@ function dynamicBadgeNotification( setTodoCategoryCount ) {
var getBadgeCompletedTaskListDiv = $('#todo-task-done .todo-badge');
var getBadgeImportantTaskListDiv = $('#todo-task-important .todo-badge');


if (todoCategoryCount === 'allList') {
if (get_TodoListElementsCount === 0) {
getBadgeTodoAllListDiv.text('');
Expand Down Expand Up @@ -242,54 +239,48 @@ function deleteDropdown() {
swal.queue([{
title: 'Are you sure you want to delete this Recon Note?',
text: "You won't be able to revert this!",
type: 'warning',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Delete',
padding: '2em',
showLoaderOnConfirm: true,
preConfirm: function() {
return fetch('../delete_note', {
return fetch('/recon_note/delete_note', {
method: 'POST',
credentials: "same-origin",
headers: {
"X-CSRFToken": getCookie("csrftoken")
},
body: JSON.stringify({
'id': parseInt(id),
})
body: JSON.stringify({ 'id': parseInt(id) })
})
.then(function (response) {
if(!$(main_this).parents('.todo-item').hasClass('todo-task-trash')) {
var getTodoParent = $(main_this).parents('.todo-item');
var getTodoClass = getTodoParent.attr('class');

var getFirstClass = getTodoClass.split(' ')[1];
var getSecondClass = getTodoClass.split(' ')[2];
var getThirdClass = getTodoClass.split(' ')[3];

if (getFirstClass === 'all-list') {
getTodoParent.removeClass(getFirstClass);
}
if (getSecondClass === 'todo-task-done' || getSecondClass === 'todo-task-important') {
getTodoParent.removeClass(getSecondClass);
const errorMessages = {
400: 'Oops! Unable to delete todo!',
404: 'Oops! Note not found!',
200: 'Note deleted successfully!'
};

if (response.status in errorMessages) {
swal.insertQueueStep({
icon: response.status === 200 ? 'success' : 'error',
title: errorMessages[response.status]
});

if (response.status === 200) {
const getTodoParent = $(main_this).parents('.todo-item');
getTodoParent.remove();
new dynamicBadgeNotification('allList');
new dynamicBadgeNotification('completedList');
new dynamicBadgeNotification('importantList');
}
if (getThirdClass === 'todo-task-done' || getThirdClass === 'todo-task-important') {
getTodoParent.removeClass(getThirdClass);
}
$(main_this).parents('.todo-item').addClass('todo-task-trash');
} else if($(main_this).parents('.todo-item').hasClass('todo-task-trash')) {
$(main_this).parents('.todo-item').removeClass('todo-task-trash');
}
new dynamicBadgeNotification('allList');
new dynamicBadgeNotification('completedList');
new dynamicBadgeNotification('importantList');
})
.catch(function() {
swal.insertQueueStep({
type: 'error',
title: 'Oops! Unable to delete todo!'
})
})
});
});
}
}]);
});
Expand All @@ -303,16 +294,15 @@ function checkCheckbox() {
$(this).parents('.todo-item').removeClass('todo-task-done');
}
new dynamicBadgeNotification('completedList');
fetch('../flip_todo_status', {
fetch('/recon_note/flip_todo_status', {
method: 'post',
headers: {
"X-CSRFToken": getCookie("csrftoken")
},
body: JSON.stringify({
'id': parseInt(this.id.split('_')[1]),
})
}).then(res => res.json())
.then(res => console.log(res));
}).then(res => res.json());
});
}

Expand Down Expand Up @@ -344,16 +334,15 @@ function importantDropdown() {
$("#important-badge-"+badge_id).empty();
}
new dynamicBadgeNotification('importantList');
fetch('../flip_important_status', {
fetch('/recon_note/flip_important_status', {
method: 'post',
headers: {
"X-CSRFToken": getCookie("csrftoken")
},
body: JSON.stringify({
'id': parseInt(this.id.split('_')[1]),
})
}).then(res => res.json())
.then(res => console.log(res));
}).then(res => res.json());
});
}

Expand Down
Loading

0 comments on commit 22583af

Please sign in to comment.