thoughtbot / administrate

A Rails engine that helps you put together a super-flexible admin dashboard.
http://administrate-demo.herokuapp.com
MIT License
5.9k stars 1.12k forks source link

Manual sorting of HasMany children list #2276

Open tancrede opened 2 years ago

tancrede commented 2 years ago

Dear all

The evolutions in the way Administrate handle sorting within the dashboards makes the gem flexible and super useful. However, there is still a particular case which is not managed and which prevents me from having an administration tool entirely based on the configuration files of Administrator. : being able to reorder manually the list of children within a HasMany relation, based on a specific position attribute.

use case : I have an album template that contains many images. These images must be ordered manually by the person in charge of the album, according to his artistic touch, the story he wishes to tell.

I feel that this feature is probably cumbersome to implement, since it would likely impact pagination and other well-established features and add specific behavior regarding the existing/non existing positions when children's data are imported. Acts_as_list had a bunch of methods allowing things like this I guess.

However, I have the intuition that such an ordering solution, which corresponds well to a data administration action, would be useful for the management of many data models.

In the case of a "simple" HasMany relationship, it would suffice to pass two pieces of information to the dashboard: specify the classification mode and specify the name of the attribute that contains the position of each child.

  ATTRIBUTE_TYPES = {
    pictures: Field::HasMany.with_options(sortable: true, sortable_index_position: :position),
  }.freeze

A last option could allow to specify the method of interaction allowing to modify the classification if there were several.

I hope this idea will interest some of you :)

pablobm commented 2 years ago

I'm not sure I understand. HasMany has an option sort_by that can be used to tell which property to sort by. Would that work?

tancrede commented 2 years ago

Dear Pablobm,

You're right. You can already sort a list by using a specific attribute. But the idea here is to add actions to be able to reorder this list.

This means having, for each element of the list, actions like "move up" "move down" and methods that recalculate the index position of every member of the list.

Sorry for my bad English. I hope it's more relevant now.

tancrede commented 2 years ago

Here is an example (see the screen capture).

Capture d’écran 2022-10-09 à 18 43 30

This Fortress object has multiple Names. The order is important.

For now, I have a "position" attribute that allows me to set an order within the Fortress model, and then use it within the dashboard :

class Fortress < ApplicationRecord
...
has_many :names, -> { order(position: :asc)}
...
end

Manually modifying the positions is of course possible, but tedious and obviously not satisfactory as soon as there are more than a few elements.

Ideally, one could imagine a drag'n drop action that recalculates all positions after the drop. Otherwise, individual movement actions (up or down) at the level of each element.

pablobm commented 2 years ago

I see. So I'm not sure about implementing this generally in the gem, and I've been looking into how this could be implemented by overriding things...

First, and this is a bit clunky but it should work: you could create a new action that show along with "Edit" and "Destroy" and is "Reorder" (or similar). There you can have your own interface to do this, built with normal Rails. You could provide your own version of app/views/administrate/application/show.html.erb or a more specific one, in your case: spec/example_app/app/views/admin/fortresses/show.html.erb. This will allow you to alter the template to add new actions to the top. Then you link to a page of yours within the /admin namespace.

Something better would be if we could make new actions appear in the "HasMany" table. This is not possible at the moment. It's possible in the index page, but not in HasMany listings. The example app shows this in the Customers page, where it includes a "Become" action: https://administrate-prototype.herokuapp.com/admin/customers

The reason that this is not possible in HasMany pages is because of the way that Rails loads partials... but it should be possible to override. It requires going into the internals of Rails a bit to figure it out, but I think it's possible.

How confident are you with researching this sort of stuff? Would you be able to look into it and report back?

nanego commented 2 years ago

If you prefer not to replace the whole 'show.html.erb' view from Administrate, you can inject a link in the page, using the excellent Deface Gem (https://github.com/spree/deface).

First, add the gem to your Gemfile: gem 'deface'

Then, create an override: Create a new file for each view you want to change: app/overrides/administrate/application/_collection.rb Specify in this file how you'd like to override the view:

Deface::Override.new(:virtual_path => "administrate/application/_collection",
                 :name => "add-ordering-link-to-collection",
                 :insert_after => "table",
                 :text => "<%= link_to 'Reorder', '#' %>")

The code above insert a link after each collection. But you can also inject any type of code you need, just as you would do in any Rails view: HTML, Ruby, JS, CSS