Laravel-Backpack / CRUD

Build custom admin panels. Fast!
https://backpackforlaravel.com
MIT License
3.17k stars 896 forks source link

[Feature Request] details row to accept widgets by default #5347

Closed pxpm closed 1 month ago

pxpm commented 1 year ago

Feature Request

What's the feature you think Backpack should have?

I got this request/idea from a discussion in https://github.com/Laravel-Backpack/community-forum/discussions/712 where @susanu shared some code he's using in his project.

Basically the idea is to replace the empty details row blade template with a template that outputs widgets you can configure in your crud panels.

Have you already implemented a prototype solution, for your own project?

@susanu did implemented a prototype, it's in the linked discussion.

Do you see this as a core feature or an add-on?

I think we could do this change as a NON-BC in core.

susanu commented 3 months ago

Hi guys,

Any news about this?

pxpm commented 3 months ago

Hey @susanu I've been working on other stuff and didn't bring this to team attention yet so I am not sure if we are going to pursue this direction.

What I mean is that I think there are separated tasks here: 1 - change the default details_row template to look for widgets in details_row section 2 - add a new widget: column 3 - the way you proposed details_row to work look f*cking cool 😎 , but 100% we are not going to add tabs or glue columns, we made that mistake before with custom_html "field", we learned our lesson. 🙃

Regarding 1) and 2), I think the code you provided is pretty much the required thing. 3) is where things start to get interesting. Analyzing the current and possible future direction, can we come up with a way to have those tabs, rows, columns but not making them "columns" ? My first thought was using the "div" widget https://backpackforlaravel.com/docs/6.x/base-widgets#div-1 or creating some new "details_row_widget" if we can't come up with something that works with div, but theoretically we should be able to use div to make the layout. Regarding tabs, we probably need a new widget for that right ? A widget that works similar to div, and the definition would be something like:

[
    'type'    => 'div',
    'class'   => 'row',
    'content' => [ // widgets
        [ 'type' => 'tabs', 'content' => [
            'tab1' => [
                'title' => 'My Tab 1'
                'content' => ['type' => 'card' ... ],
            ],
            'tab2' => [
                'title' => 'My Tab 1',
                'content' => ['type' => 'card' ... ],
            ],
        ] 
    ]
]

What do you think ?

susanu commented 3 months ago

So, for me, details_row should contain information about the entry, current widgets will not work in this case because the information is static.

Another idea that crossed my mind is, instead of treating tabs as a column, we should have another entity type here, not sure how to name it. This new entity would be a combination between widgets and columns and can be used in details_row and on UpdateOperation alongside fields.

I often need to display information about the current entry and the relationships in the edit page of the crud.

pxpm commented 3 months ago

What you mean is static ? You have $entry in the details_row template, so any widget you "build" there, can use the $entry too. For example, you can display a card widget for the current entry:

// details_row.blade.php
@php
Widget::add([
    'type'    => 'div',
    'class'   => 'row',
    'section' => 'details_row',
    'content' => [ // widgets
        [ 'type' => 'card', 'content' => ['body' => $entry->id, 'header' => 'ID'], 'class' => 'col-md-6' ],
        [ 'type' => 'card', 'content' => ['body' => $entry->text, 'header' => 'TITLE'], 'class' => 'col-md-6' ],
    ]
])
@endphp

<div class="m-t-10 m-b-10 p-l-10 p-r-10 p-t-10 p-b-10" bp-section="crud-details-row">
    <div class="row">
        <div class="col-md-12">
            @include(backpack_view('inc.widgets'), ['widgets' => app('widgets')->where('section', 'details_row')->toArray()])
        </div>
    </div>
</div>
<div class="clearfix"></div>

image

So doing 1) that you proposed would be quite easy and open a lot of opportunities. What I am thinking now is also an easy solution where you could write your widgets definition without having to create the blade file yourself. Maybe we can introduce a setupDetailsRow() function so that you can add the widgets from the controller ?

Note: one thing I noticed is that we probably need to accept an array of stacks instead of a single string, so that the same widget definition can be reused in multiple operations that use different stacks.

I think doing "just" this would allow you to achieve the desired results creating a couple of additional widgets (for eg. "tabs widget", "column widget", "table widget" etc .., that is something we can do), instead of "one function columns", or introducing some kind of new concept in crud to build specific stuff with tabs.

What do you think ?

susanu commented 3 months ago

Hmm, your example is not good i think because you setup the widget inside the details_row.blade.php details_row.blade.php should ONLY contain this:


<div class="m-t-10 m-b-10 p-l-10 p-r-10 p-t-10 p-b-10" bp-section="crud-details-row">
    <div class="row">
        <div class="col-md-12">
            @include(backpack_view('inc.widgets'), ['widgets' => app('widgets')->where('section', 'details_row')->toArray()])
        </div>
    </div>
</div>
<div class="clearfix"></div>

Then you can add widgets from setupListOperation function. In this case, the information is static because there is no $entry there. On top of that, is very limited in terms of posibilities, displaying columns makes more sense.

Maybe creating some simple components which can be configured from the controller. The components can be also combined and displayed in multiple places. Example of components:

What do you think?

pxpm commented 3 months ago

That was an example and exactly why I said "a new method setupDetailsRow", because in that method you will have the entry similar as if you were writing the widgets on the blade.

Those components you are talking about make sense if we were building an "UI library" to use with Backpack. It would be awesome, but it's out of scope for the issue we are discussing here.

So, to sum up my idea more clearly on "how we can make details row accept widgets by default":

That method can also return the html/view to render in addition to the widgets.

public function setupDetailsRow($entry) 
{
Widget::add() ... 

return view('my-view'); // or no return at all if you setup the details row exclusively using widgets ?
// this will render this view "inside" the default backpack details row view. 
}

So we modify the current showDetailsRow() method to something like this:

public function showDetailsRow($id)
    {
        $this->crud->hasAccessOrFail('list');

        // get entry ID from Request (makes sure its the last ID for nested resources)
        $id = $this->crud->getCurrentEntryId() ?? $id;

        $this->data['entry'] = $this->crud->getEntry($id);
        $this->data['crud'] = $this->crud;
+      $this->data['html'] = $this->setupDetailsRow($entry); // we setup widgets and can return some html here

        // load the view from /resources/views/vendor/backpack/crud/ if it exists, otherwise load the one in the package
        return view($this->crud->getDetailsRowView(), $this->data);
    }
// the default backpack details row become something like this
<div class="m-t-10 m-b-10 p-l-10 p-r-10 p-t-10 p-b-10" bp-section="crud-details-row">
    <div class="row">
        <div class="col-md-12">
            @include(backpack_view('inc.widgets'), ['widgets' => app('widgets')->where('section', 'details_row')->toArray()])
        </div>
    </div>
</div>
<div class="clearfix"></div>
{!! $html !!}

Does this make sense ?

susanu commented 3 months ago

Yes, looks good. On the setupDetailsRow function, no need to return a view, just setup the widgets. They can change the view using CRUD settings.

susanu commented 3 months ago

I come to you with a different matter. What if i want to use widgets on the Show operation?

For example. I have to display a table column in the Show operation. Since the table has a lot of columns, it doesn't look good normally.

Normal look:

CRUD::column('shipping_details')
    ->type('table')
    ->columns([
        'company' => 'Company',
        'telephone' => 'Telephone',
        'address' => 'Address',
        'zip' => 'Zip',
        'city' => 'City',
        'province' => 'Province',
        'country' => 'Country',
        'country_code' => 'Country code',
        'email' => 'Email',
    ]);

image

Widget look (yes, yes, you hate the column widget type but for me it does the job):

Widget::add([
    'type' => 'column',
    'section' => 'after_content',
    'class' => 'mt-4',
    'column' => [
        'name' => 'shipping_details',
        'type' => 'table',
        'columns' => [
            'company' => 'Company',
            'telephone' => 'Telephone',
            'address' => 'Address',
            'zip' => 'Zip',
            'city' => 'City',
            'province' => 'Province',
            'country' => 'Country',
            'country_code' => 'Country code',
            'email' => 'Email',
        ]
    ]
]);

image

pxpm commented 3 months ago

Hey @susanu thanks for the example. 100% using it as a widget looks way way cleaner.

Naaa I don't hate your column widget. I find it very useful and a good idea. We may even end up using it or a similar idea to that. It would probably be the perfect complement for the table widget we started working on: https://github.com/Laravel-Backpack/CRUD/pull/5523 Or maybe we don't even need the column widget as a separate entity of the table, it's possible we may incorporate everything, because I don't see a use case for a column widget outside of a table.

I just don't think it's savvy to push everything at the same time, making this a huuuuge feature, when we can do this incrementally and getting benefits on each iteration. It's easier for us to test, iterate on the UI/UX of small bits to make sure we think about edges, how people can use this outside "our" use case etc.

So yeah, totally agree with making details row composable with widgets, it was an excellent idea if you ask me. Then work on widgets that will make that experience even better (the table, the column if needed etc).

Widgets are in nature re-usable, so it's expectable that the same table you use in a detail row, you could re-use in the show view.

This is just a scope management on my side, you gave us great ideas and examples and everything, that's way way more than we asked or expect when people request features, so we are very grateful for your time and effort, I just try to keep those ideas on their own place and manageable from our side 🙏

Cheers, keep it up 👍

simke92 commented 3 months ago

Hi,

I saw this feature request and it inspired me to try adding a Livewire component inside the details_row. While the component renders correctly, I'm encountering an issue where the actions don't work. I've tried a simple example from the Livewire documentation using the Counter component. Although there are no errors on the page, the buttons do not work.

Has anyone successfully integrated Livewire with details_row, or does anyone have suggestions on how to troubleshoot this?

Thanks in advance for any assistance!

pxpm commented 1 month ago

Hi,

I saw this feature request and it inspired me to try adding a Livewire component inside the details_row. While the component renders correctly, I'm encountering an issue where the actions don't work. I've tried a simple example from the Livewire documentation using the Counter component. Although there are no errors on the page, the buttons do not work.

Has anyone successfully integrated Livewire with details_row, or does anyone have suggestions on how to troubleshoot this?

Thanks in advance for any assistance!

Most likely you are only adding livewire on the "ajax call" to details row, and you should probably need to have livewire already on page before that call is done and maybe try with: window.livewire.rescan(); after the ajax call to details row is done.

Never did that, but if you find the solution let us know 👍

In the meanwhile we merged this feature on https://github.com/Laravel-Backpack/CRUD/releases/tag/6.7.35

Cheers