From 232e469a2b8575883186f271e6496b7b04bc1d96 Mon Sep 17 00:00:00 2001 From: Horilla Date: Fri, 1 Aug 2025 12:20:56 +0530 Subject: [PATCH] [UPDT] HORILLA: Add HX-Refresh response handling to login_required for HTMX requests --- horilla/decorators.py | 2 + .../templates/generic/pipeline/kanban.html | 136 +++++++++--------- 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/horilla/decorators.py b/horilla/decorators.py index 58de6be21..a36498212 100755 --- a/horilla/decorators.py +++ b/horilla/decorators.py @@ -258,6 +258,8 @@ def login_required(view_func): or not employee or not employee.is_active ): + if request.headers.get("HX-Request"): + return HttpResponse(status=204, headers={"HX-Refresh": "true"}) return redirect(redirect_url) try: func = view_func(request, *args, **kwargs) diff --git a/horilla_views/templates/generic/pipeline/kanban.html b/horilla_views/templates/generic/pipeline/kanban.html index 17148c336..e57ab1493 100644 --- a/horilla_views/templates/generic/pipeline/kanban.html +++ b/horilla_views/templates/generic/pipeline/kanban.html @@ -99,9 +99,9 @@ $(document).ready(function () { // Define the viewId var viewId = '#{{view_id}}' - + let draggedCard = null - + // Initialize drag events for cards function initDragAndDrop() { // Drag start @@ -111,44 +111,44 @@ draggedCard.addClass('dragging') }, 0) }) - + // Drag end $(viewId + ' .kanban-card').on('dragend', function () { $(this).removeClass('dragging') draggedCard = null updateTaskCounts() }) - + // Handle dragover to allow drops $(viewId + ' .cards-container').on('dragover', function (e) { e.preventDefault() $(this).addClass('drop-zone') }) - + // Remove drop zone highlighting $(viewId + ' .cards-container').on('dragleave', function () { $(this).removeClass('drop-zone') }) - + // Handle drops $(viewId + ' .cards-container').on('drop', function (e) { e.preventDefault() $(this).removeClass('drop-zone') - + if (draggedCard) { // Find the "Add task" button and insert before it const addTaskButton = $(this).find('.add-task') draggedCard.insertBefore(addTaskButton) - + const itemId = draggedCard.data('id') const column = $(this).closest('.kanban-column') const newStage = column.data('stage') - + console.log(`Item ${itemId} moved to ${newStage}`) - + // Update card styling based on new column updateCardStyling(draggedCard, newStage) - + // 🔁 Replace with AJAX to update backend /* $.ajax({ @@ -167,28 +167,28 @@ } }) } - + // Initialize profile dropdowns for cards function initProfileDropdowns() { // Avatar click to show dropdown $(viewId).on('click', '.avatar, .avatar-placeholder', function (e) { e.stopPropagation() const dropdown = $(this).siblings('.assignee-dropdown') - + // Close all other dropdowns first $(viewId + ' .assignee-dropdown.active') .not(dropdown) .removeClass('active') - + // Toggle this dropdown dropdown.toggleClass('active') }) - + // Assignee selection $(viewId).on('click', '.assignee-option', function () { const profileContainer = $(this).closest('.profile-container') const initials = $(this).data('initials') - + if (initials === 'UN') { // Unassign profileContainer.find('.avatar, .avatar-placeholder').replaceWith($('
+
')) @@ -197,39 +197,39 @@ const personName = $(this).find('span').text() const avatar = $(this).find('.avatar').clone() avatar.attr('title', personName) - + profileContainer.find('.avatar, .avatar-placeholder').replaceWith(avatar) } - + // Close dropdown profileContainer.find('.assignee-dropdown').removeClass('active') }) - + // Close dropdowns when clicking elsewhere $(document).on('click', function () { $(viewId + ' .assignee-dropdown.active').removeClass('active') }) } - + // Initialize column collapse/expand functionality function initColumnCollapse() { $(viewId + ' .column-header').on('click', function () { $(this).closest('.kanban-column').toggleClass('collapsed') }) } - + // Handle adding new tasks function initAddTask() { $(viewId + ' .add-task').on('click', function () { const column = $(this).closest('.kanban-column') const stage = column.data('stage') - + // Create a new card const newCard = createNewTaskCard(stage) - + // Add it before the add button $(this).before(newCard) - + // Initialize drag events for the new card newCard .on('dragstart', function () { @@ -243,19 +243,19 @@ draggedCard = null updateTaskCounts() }) - + // Update counts updateTaskCounts() - + console.log(`New task created in ${stage}`) }) } - + // Create a new task card function createNewTaskCard(stage) { // Create unique ID for the card const newId = Date.now() - + // Create the card with proper structure const newCard = $('
').attr('data-id', newId).append(`
New Task
@@ -288,13 +288,13 @@ `) - + // Apply styling based on column updateCardStyling(newCard, stage) - + return newCard } - + // Update card styling based on column function updateCardStyling(card, stage) { if (stage === 'todo') { @@ -314,7 +314,7 @@ }) } } - + // Update task counts for each column function updateTaskCounts() { $(viewId + ' .kanban-column').each(function () { @@ -322,7 +322,7 @@ $(this).find('.task-count').text(cardCount) }) } - + // Initialize everything function init() { initDragAndDrop() @@ -331,7 +331,7 @@ initAddTask() updateTaskCounts() } - + // Run initialization init() }) @@ -342,9 +342,9 @@ const columns = document.querySelectorAll('.kanban-column') const cardsContainers = document.querySelectorAll('.cards-container') const addTaskButtons = document.querySelectorAll('.add-task') - + let draggedCard = null - + // Initialize card dragging cards.forEach((card) => { card.addEventListener('dragstart', () => { @@ -353,19 +353,19 @@ card.classList.add('dragging') }, 0) }) - + card.addEventListener('dragend', () => { draggedCard = null card.classList.remove('dragging') updateTaskCounts() }) - + // Set up profile dropdown functionality const profileContainer = card.querySelector('.profile-container') if (profileContainer) { const avatar = profileContainer.querySelector('.avatar, .avatar-placeholder') const dropdown = profileContainer.querySelector('.assignee-dropdown') - + avatar.addEventListener('click', (e) => { e.stopPropagation() // Close all other dropdowns first @@ -376,14 +376,14 @@ }) dropdown.classList.toggle('active') }) - + // Handle assignee selection const assigneeOptions = dropdown.querySelectorAll('.assignee-option') assigneeOptions.forEach((option) => { option.addEventListener('click', () => { const initials = option.getAttribute('data-initials') const currentAvatar = profileContainer.querySelector('.avatar, .avatar-placeholder') - + if (initials === 'UN') { // Unassign profileContainer.innerHTML = ` @@ -403,16 +403,16 @@ ` } - + // Reset event listeners for the new elements const newDropdown = profileContainer.querySelector('.assignee-dropdown') const newAvatar = profileContainer.querySelector('.avatar, .avatar-placeholder') - + newAvatar.addEventListener('click', (e) => { e.stopPropagation() newDropdown.classList.toggle('active') }) - + // Reset assignee option event listeners const newOptions = newDropdown.querySelectorAll('.assignee-option') newOptions.forEach((newOpt) => { @@ -422,13 +422,13 @@ this.click() // This is a simplified approach }) }) - + dropdown.classList.remove('active') }) }) } }) - + // Handle column collapse document.querySelectorAll('.column-header').forEach((header) => { header.addEventListener('click', () => { @@ -436,43 +436,43 @@ column.classList.toggle('collapsed') }) }) - + // Close dropdowns when clicking outside document.addEventListener('click', () => { document.querySelectorAll('.assignee-dropdown.active').forEach((dropdown) => { dropdown.classList.remove('active') }) }) - + // Handle drag and drop for columns cardsContainers.forEach((container) => { container.addEventListener('dragover', (e) => { e.preventDefault() container.classList.add('drop-zone') }) - + container.addEventListener('dragleave', () => { container.classList.remove('drop-zone') }) - + container.addEventListener('drop', (e) => { e.preventDefault() container.classList.remove('drop-zone') - + if (draggedCard) { // Find the "Add task" button and insert before it const addTaskButton = container.querySelector('.add-task') container.insertBefore(draggedCard, addTaskButton) - + const itemId = draggedCard.getAttribute('data-id') const column = container.closest('.kanban-column') const newStage = column.getAttribute('data-stage') - + console.log(`Item ${itemId} moved to ${newStage}`) - + // Update card styling based on new column updateCardStyling(draggedCard, newStage) - + // 🔁 Replace with AJAX to update backend /* fetch('/api/kanban/update-stage/', { @@ -490,21 +490,21 @@ } }) }) - + // Handle add task button clicks addTaskButtons.forEach((button) => { button.addEventListener('click', () => { const column = button.closest('.kanban-column') const stage = column.getAttribute('data-stage') const container = column.querySelector('.cards-container') - + // Create a new card with a unique ID (this is just for demo purposes) const newId = Date.now() const newCard = document.createElement('div') newCard.className = 'kanban-card' newCard.setAttribute('draggable', 'true') newCard.setAttribute('data-id', newId) - + // Set the default content for the new card newCard.innerHTML = `
New Task
@@ -514,13 +514,13 @@ Today ` - + // Apply styling based on the column updateCardStyling(newCard, stage) - + // Add the new card before the add task button container.insertBefore(newCard, button) - + // Set up drag events for the new card newCard.addEventListener('dragstart', () => { draggedCard = newCard @@ -528,26 +528,26 @@ newCard.classList.add('dragging') }, 0) }) - + newCard.addEventListener('dragend', () => { draggedCard = null newCard.classList.remove('dragging') updateTaskCounts() }) - + // Update counts updateTaskCounts() - + console.log(`New task created in ${stage}`) }) }) - + // Function to update card styling based on column function updateCardStyling(card, stage) { // Remove all potential styling classes first card.style.borderLeftColor = '' card.style.background = '' - + // Apply styling based on stage if (stage === 'todo') { card.style.borderLeftColor = '#f43f5e' @@ -560,7 +560,7 @@ card.style.background = '#ecfdf5' } } - + // Function to update task counts function updateTaskCounts() { columns.forEach((column) => { @@ -569,7 +569,7 @@ countSpan.textContent = cards }) } - + // Initial count update updateTaskCounts()