#route.rb
 resources :branches do
    member do
      get :updateposition
      get :mappa
      get :ul
    end
  end
#controller_branches.rb
def updateposition
  @branch_root = @branch.root
  new_position = params[:position].to_i
  parent_id = params[:parent_id]

  respond_to do |format|
    if @branch.update(parent_id: parent_id)
      # acts_as_list è 1-based, ma se il valore è 0, forziamo a 1
      @branch.insert_at(new_position)

      format.html { redirect_to ordinabile_branch_path(@branch_root) } # , notice:  "Branch spostato con successo." }
      format.json { render :show, status: :ok, location: ordinabile_branch_path(@branch_root) }
    else
      format.html { render :edit, status: :unprocessable_entity }
      format.json { render json: @branch.errors, status: :unprocessable_entity }
    end
  end
end
# branch.rb
# == Schema Information
#
# Table name: branches
#
#  id                 :integer          not null, primary key
#  user_id            :integer          not null
#  slug               :string
#  parent_id          :integer
#  position           :integer
#  content_id         :integer
#  slug_note          :string
#  user_note_username :string
#  child_id           :integer
#  mycategory_id      :integer          not null
#  created_at         :datetime         not null
#  updated_at         :datetime         not null
#
# Indexes
#
#  index_branches_on_mycategory_id  (mycategory_id)
#  index_branches_on_user_id        (user_id)
#

class Branch < ApplicationRecord
  belongs_to :user
  belongs_to :mycategory, optional: true

  has_many :category, through: :mycategory

  # Gerarchia dell'albero (Parent-Child)
  belongs_to :parent, class_name: "Branch", foreign_key: "parent_id", optional: true
  has_many :children, class_name: "Branch", foreign_key: "parent_id"

  # Collegamenti tra rami separati
  belongs_to :link_child, class_name: "Branch", foreign_key: "child_id", optional: true
  has_many :linked_parents, class_name: "Branch", foreign_key: "child_id", dependent: :nullify

  # Gestisce l'ordine tra i figli dello stesso parent
  acts_as_list scope: :parent_id

  # Validazioni
  validates :slug, presence: true, uniqueness: true
  #  validates :slug_note, uniqueness: true, allow_nil: true
  def root
    self.class.where(id: self_and_ancestors_ids.first).first
  end

  def self_and_ancestors_ids
    ids = [ id ]
    current = self
    while current.parent_id.present?
      ids.unshift(current.parent_id)
      current = current.parent
    end
    ids
  end
end

<!--branches/show.html.erb-->
<nav class="tabs__list" aria-label="Home and Settings">
  <%= link_to "Mappa", branch_path(@branch), class: "btn tabs__button", aria: { current: "page" } %>
  <%= link_to "Ordinabile", ordinabile_branch_path(@branch), class: "btn tabs__button" %>
  <%= link_to "Ul", ul_branch_path(@branch), class: "btn tabs__button" %>
</nav>
<%= render 'tree_ascii' %>
<!--branches/mappa.html.erb-->
<nav class="tabs__list" aria-label="Home and Settings">
  <%= link_to "Mappa", branch_path(@branch), class: "btn tabs__button" %>
  <%= link_to "Ordinabile", ordinabile_branch_path(@branch), class: "btn tabs__button", aria: { current: "page" } %>
  <%= link_to "Ul", ul_branch_path(@branch), class: "btn tabs__button" %>
</nav>
<div class="flex rounded-lg border">
  <div class="flex items-center justify-center p-3 border-ie resizable-inline">
    <%= render 'sidebar' %>
  </div>
  <div class="flex  grow p-3">
    <div class="">
      <h1 class="text-xl font-bold"><%= @branch.slug %><%= link_to "New children", new_branch_path(parent_id: @branch.id), class: "btn m-4" %></h1>
      <div class="flex flex-wrap items-center gap mb-2" >
        <%= link_to "Edit this branch", edit_branch_path(@branch), class: "btn" %>
        <%= link_to "Back to branches", branches_path, class: "btn" %>
        <%= button_to "Destroy this branch", @branch, method: :delete, class: "btn" %>
      </div>
      <%= render "branches/sortable_simple", branches: @branch.children, branch: @branch %>
    </div>
  </div>
</div>
<%#= render 'tree_ascii' %>
<%#= render "ul_list", branches: @branch.children, parent_id: nil %>
</div>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
<script>
  function makeSortable(element) {
    new Sortable(element, {
      group: 'nested',
      animation: 150,
      fallbackOnBody: true,
      swapThreshold: 0.65,
      onAdd: function (evt) {
        // Apply sortable to new nested lists
        let nestedUl = evt.item.querySelector('ul');
        if (nestedUl) makeSortable(nestedUl);
      },
      onEnd: function (evt) {
        let item = evt.item;
        let itemId = item.dataset.id;
        let parentUl = item.closest('ul');
        let parentLi = parentUl.closest('li');
        let parentId = parentLi ? parentLi.dataset.id : null;

        // Calcola la nuova posizione (index all’interno della lista)
        let newPosition = Array.from(parentUl.children).indexOf(item);

        // Esegui redirect alla nuova posizione
        if (itemId && parentId != null && newPosition != null) {
          const correctedPosition = parseInt(newPosition) + 1;

          const url = `/branches/${itemId}/updateposition?parent_id=${parentId}&position=${correctedPosition}`;

          window.location.href = url;
        }
      }

    });

    // Rendere anche le sotto-liste sortable
    Array.from(element.querySelectorAll('ul')).forEach(ul => makeSortable(ul));
  }

  // Inizializza il sortable principale
  makeSortable(document.getElementById('nested-list'));
</script>


<!--branches/sortable_simple.html.erb-->
<ul>
  <li class="list-group-item " data-id="<%= @branch.id %>" data-parent-id="<%= @branch.parent_id %>">
    <span class="drag-handle"></span> <%= @branch.slug %>
    <ul id="nested-list" class="nested-sortable" data-sortable-target="list">
      <%= render partial: "sortable_child_simple", collection: @branch.children.order(:position), as: :branch %>
    </ul>
  </li>
</ul>


<!--branches/sortable_simple_child.html.erb-->
<li class="list-group-item" data-id="<%= branch.id %>">
  <span class="drag-handle"></span>
  id: <%= branch.id %> - <%= branch.slug %>
  <% if branch.children.any? %>
    <ul class="nested-sortable" data-sortable-target="list">
      <%= render partial: "sortable_child_simple", collection: branch.children.order(:position), as: :branch %>
    </ul>
  <% else %>
    <ul class="nested-sortable" data-sortable-target="list">
    </ul>
  <% end %>
</li>

/* sortable.css */
 .list-group {
    display: block;
    padding: 10px;
    border: 1px solid #ddd;
    background-color: #f8f8f8;
    margin-bottom: 10px;
  }

  .list-group-item {
    padding: 10px;
    background: white;
    border: 1px solid #ccc;
    margin: 5px 0;
    cursor: grab;
    border-radius: 5px;
  }

  /* Quando l'elemento viene trascinato */
  .sortable-ghost {
    opacity: 0.5;
    background: #ddd;
  }

Table of contents