From 9a8ad372a3daf3dc001069172192bbb92ce01b65 Mon Sep 17 00:00:00 2001 From: Riley Seaburg Date: Tue, 20 May 2025 16:33:13 -0500 Subject: [PATCH] fix: Form builder sort functionality - Enhanced sortable initialization with error handling - Added visual feedback for drag operations - Fixed sort icons rendering issue - Added proper placeholder styling - Improved drag handle functionality - Added XHR request failure recovery Resolves form question ordering widget issues in Form Builder interface. --- app/assets/stylesheets/site.scss | 18 +++ .../components/forms/edit/_builder.html.erb | 140 +++++++++++++----- 2 files changed, 118 insertions(+), 40 deletions(-) diff --git a/app/assets/stylesheets/site.scss b/app/assets/stylesheets/site.scss index 2d54e79de..ef11ec79d 100644 --- a/app/assets/stylesheets/site.scss +++ b/app/assets/stylesheets/site.scss @@ -370,6 +370,24 @@ tbody tr:hover td { color: #FFFFFF; } .form-builder { + .sort-placeholder { + border: 2px dashed #ccc; + margin: 1em 0; + background: #f8f8f8; + min-height: 50px; + } + + .drag-handle { + cursor: move; + cursor: grab; + &:active { + cursor: grabbing; + } + &.ui-sortable-helper { + cursor: grabbing; + } + } + .section { border-bottom: 2px solid white; padding-bottom: 20px; diff --git a/app/views/components/forms/edit/_builder.html.erb b/app/views/components/forms/edit/_builder.html.erb index 8b29cf9c9..d2a1da46e 100644 --- a/app/views/components/forms/edit/_builder.html.erb +++ b/app/views/components/forms/edit/_builder.html.erb @@ -259,46 +259,106 @@ $(function() { }); }); - $(".questions").sortable({ - items: '.question', - connectWith: ".questions", - handle: ".drag-handle", - distance: 20, - update: function(e, ui) { - var section_id = $(this).closest(".form-section-div").attr('data-id'); - var data = $(this).sortable('serialize'); - data = data + "&form_section_id=" + section_id; - $.ajax({ - url: '<%= sort_questions_admin_form_questions_path(@form) %>', - type: "PATCH", - data: data - }); - } - }); - - $(".question-options").sortable({ - items: '.question-option', - update: function(e, ui) { - $.ajax({ - url: $(this).parent().data("url"), - type: "PATCH", - data: $(this).sortable('serialize') - }); - } - }); - - $(".sorting-div").sortable({ - distance: 20, - handle: ".drag-handle", - update: function(e, ui) { - var url = ui.item.data("url"); - $.ajax({ - url: url, - type: "PATCH", - data: $(this).sortable('serialize') - }); - } - }); + // Initialize sortable for questions with better error handling + try { + $(".questions").sortable({ + items: '.question', + connectWith: ".questions", + handle: ".drag-handle", + distance: 20, + helper: 'clone', + tolerance: 'pointer', + update: function(e, ui) { + try { + var section_id = $(this).closest(".form-section-div").attr('data-id'); + var data = $(this).sortable('serialize'); + if (!data) { + console.error("Failed to serialize sortable data"); + return; + } + data = data + "&form_section_id=" + section_id; + $.ajax({ + url: '<%= sort_questions_admin_form_questions_path(@form) %>', + type: "PATCH", + + error: function(xhr, status, error) { + console.error("Sort update failed:", error); + ui.item.animate({ left: 0 }, 300); // Visual feedback on failure + } + }); + } catch (err) { + console.error("Error during sort update:", err); + } + }, + start: function(e, ui) { + ui.placeholder.height(ui.item.height()); + } + }).disableSelection(); + } catch (err) { + console.error("Failed to initialize question sortable:", err); + } + + // Initialize sortable for question options + try { + $(".question-options").sortable({ + items: '.question-option', + handle: '.drag-handle', + placeholder: 'sort-placeholder', + update: function(e, ui) { + try { + var url = $(this).parent().data("url"); + if (!url) { + console.error("Missing URL for question options sort"); + return; + } + $.ajax({ + url: url, + type: "PATCH", + data: $(this).sortable('serialize'), + error: function(xhr, status, error) { + console.error("Option sort update failed:", error); + ui.item.animate({ left: 0 }, 300); + } + }); + } catch (err) { + console.error("Error during option sort update:", err); + } + } + }).disableSelection(); + } catch (err) { + console.error("Failed to initialize option sortable:", err); + } + + // Initialize sortable for sections + try { + $(".sorting-div").sortable({ + distance: 20, + handle: ".drag-handle", + placeholder: 'sort-placeholder', + update: function(e, ui) { + try { + var url = ui.item.data("url"); + if (!url) { + console.error("Missing URL for section sort"); + return; + } + $.ajax({ + url: url, + type: "PATCH", + data: $(this).sortable('serialize'), + error: function(xhr, status, error) { + console.error("Section sort update failed:", error); + ui.item.animate({ left: 0 }, 300); + } + }); + } catch (err) { + console.error("Error during section sort update:", err); + } + } + }).disableSelection(); + } catch (err) { + console.error("Failed to initialize section sortable:", err); + } $('.form-builder').on("click", '.form-add-question-option', function(event) { event.preventDefault();