Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Column chooser #5495

Open
wants to merge 50 commits into
base: 4.x
Choose a base branch
from
Open

Conversation

ksn135
Copy link
Contributor

@ksn135 ksn135 commented Nov 29, 2022

Sometimes you have too many columns/properties in entity, but some of them required by one user, some other by another one.
Let's assume we have a list of employees with many fields. By default you need to scroll page left and right to see them all.
first_AdobeExpress
Just add the following lines to your existing code and see the results.

use EasyCorp\Bundle\EasyAdminBundle\Provider\SessionSelectedColumnStorageProvider;

class EmployeeCrudController extends AbstractCrudController
{
    public function __construct(
        private SessionSelectedColumnStorageProvider $sessionSelectedColumnStorageProvider
    ) {}

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityLabelInSingular('admin_label.employee.singular') 
            ->setEntityLabelInPlural('admin_label.employee.plural') 
            ->setupColumnChooser($this->sessionSelectedColumnStorageProvider) // <= magic is here
        ;
    }

Now you can see new global action button on index page named "Column chooser".
It allows you choose which columns to hide/show and reorder them as you wish:
demo
Add video:

second-2.mov

Selected columns saved in user session:
Screenshot 2022-11-29 at 15 40 37
Another suggested option is to save selected columns in your user object in database.
Your user entity MUST implement interface EasyCorp\Bundle\EasyAdminBundle\Interfaces\UserParametersStorageInterface

use EasyCorp\Bundle\EasyAdminBundle\Provider\UserSelectedColumnStorageProvider;

class EmployeeCrudController extends AbstractCrudController
{

    public function __construct(
        private UserSelectedColumnStorageProvider $userSelectedColumnStorageProvider
    ) {}

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityLabelInSingular('admin_label.employee.singular') 
            ->setEntityLabelInPlural('admin_label.employee.plural') 
            ->setupColumnChooser($this->userSelectedColumnStorageProvider) // <= magic is here
        ;
    }

In my case I store parameters in related table. But your implementation may be slightly different.
Screenshot 2022-11-29 at 16 19 58

By default column chooseruse all fields from index and detail pages.
Sometimes you want to setup it more precisely.
You may globally setup in your dashboard controller and then make customisation on controller basis.

// DashboadController.php
use EasyCorp\Bundle\EasyAdminBundle\Provider\UserSelectedColumnStorageProvider;

class DashboardController extends AbstractDashboardController
{
   public function __construct(
        private TranslatorInterface $translator,
        private UserSelectedColumnStorageProvider $userSelectedColumnStorageProvider,
    ) {}

    public function configureCrud(): Crud
    {
        return Crud::new()
            ->setColumnChooserSelectedColumnStorageProvider($this->userSelectedColumnStorageProvider)
            ->setPaginatorPageSize(10);
    }
}

// EmployeeCrudController.php
class EmployeeCrudController extends AbstractCrudController
{
    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityLabelInSingular('admin_label.employee.singular') 
            ->setEntityLabelInPlural('admin_label.employee.plural') 
            ->enableColumnChooser() // enable it
            ->setColumnChooserColumns(
                ['imageName', 'familyName', 'givenName', 'phone', 'email'], // default 
                [], // get all avaiable fields from index and detail page or specify only allowed
                ['deletedAt', 'virtualField'] // or may just exclude some fields
            )
        ;
    }

Screenshot 2022-11-29 at 16 43 13

@ahmedyakoubi
Copy link

nice feature <3 . great job mann

…: fix 6 "errors" with message "Call to function is_callable() with callable(): mixed will always evaluate to true."
@ksn135
Copy link
Contributor Author

ksn135 commented Nov 30, 2022

@javiereguiluz please review this PR and tell me what you think about it. Thanks in advance.

@ksn135
Copy link
Contributor Author

ksn135 commented Dec 1, 2022

yarn build add unnecessary slash before path in app.css and in manifest.json.
I don't know why. Probably I should use another build tool (or special command line option) for building assets.

@TheoD02
Copy link

TheoD02 commented Dec 2, 2022

Hi!

Super nice feature ! thanks for the contribution i hope it can be implemented :D

I noticed a change in the sidebar, a padding of 40px is added on the ul.menu container
image

The active fields don't show up in the modal when they are well configured and even after reset.
image

Similar case for the default fields, if I modify it and reload my page it keeps the old fields defined I have to reset to have the new ones

I could not yet see the code for the moment, I do not know if it is possible to make a control of the default & active fields, because if not sent in the configureFields function then the modal returns bad information on fields possibly impossible to display

If we select no columns, the index columns is not defined so we have an error
image
image

Reduce the number of functions implemented in ConfigCrud and set up a DTO for the configuration of the complete module ?
If no config we do not display the column chooser

Good work, I try to contribute if I find some time :)

@ksn135
Copy link
Contributor Author

ksn135 commented Dec 2, 2022

Hi @TheoD02 !

I noticed a change in the sidebar, a padding of 40px is added on the ul.menu container

Yes, it's strange.
As I mention before something wrong on my side with assets building tool (MacBook Pro, yarn 1.22.19). Probably padding issue has the same source.

The active fields don't show up in the modal when they are well configured and even after reset. image
Similar case for the default fields, if I modify it and reload my page it keeps the old fields defined I have to reset to have the new ones

What storage class are you using? Session?
I'll try investigate it later on this weekend.

I could not yet see the code for the moment, I do not know if it is possible to make a control of the default & active fields, because if not sent in the configureFields function then the modal returns bad information on fields possibly impossible to display

AFAIK with empty configureFields it will not work for now.

If we select no columns, the index columns is not defined so we have an error

Ought, my bad. Fix it. Thanks!

Reduce the number of functions implemented in ConfigCrud and set up a DTO for the configuration of the complete module ? If no config we do not display the column chooser

Good idea. I thought about it. For example, make it configurable in configureFields:

class EmployeeCrudController extends AbstractCrudController
{
    public function configureFields(string $pageName): iterable
    {
    public function configureFields(string $pageName): iterable
    {
        yield FormField::addTab('admin_label.common.panel.main');

        yield FormField::addPanel('admin_label.employee.panel.name')->setIcon('fa fa-user')->addCssClass('danger');

        yield TextField::new('imageName', 'admin_label.employee.field.imageName'); // ->setFormType(VichImageType::class);;
        // $this->getParameter('app.dirs.profile');

        yield TextField::new('familyName', 'admin_label.employee.field.familyName')->setColumns(4)
            ->addToDefaultColumns(); // <= add it to default columns in list
        yield TextField::new('givenName', 'admin_label.employee.field.givenName')->setColumns(4)
            ->addToDefaultColumns(); // <= add it to default columns in list
        yield TextField::new('patronymic', 'admin_label.employee.field.patronymic')->setColumns(4);

        yield DateField::new('birthday', 'admin_label.employee.field.birthday');

        yield FormField::addPanel('admin_label.employee.panel.contacts')->setIcon('fa fa-phone');

        yield EmailField::new('email', 'admin_label.employee.field.email')
            ->addToDefaultColumns(); // <= add it to default columns in list

        yield TelephoneField::new('phone', 'admin_label.employee.field.phone')
            ->addToDefaultColumns(); // <= add it to default columns in list

        yield FormField::addPanel('admin_label.employee.panel.work')->setIcon('fa fa-briefcase');

        yield AssociationField::new('department', 'admin_label.employee.field.department');
        yield TextField::new('position', 'admin_label.employee.field.position');

        if ($this->security->isGranted('ROLE_SUPER_ADMIN')) {
            yield FormField::addTab('admin_label.employee.panel.settings');

            yield FormField::addPanel('admin_label.employee.panel.auth')->setIcon('fa fa-key')->addCssClass('required');

            yield BooleanField::new('active', 'admin_label.employee.field.active');
            yield TextField::new('username', 'admin_label.employee.field.username')->hideOnIndex();
            yield TextField::new('plainPassword', 'admin_label.employee.field.plainPassword')->onlyOnForms()
               ->excludeFromColumnChooser() // never show this field in columnChooser

            yield FormField::addPanel('admin_label.employee.panel.access')->setIcon('fa fa-user-lock');

            yield ArrayField::new('roles', 'admin_label.employee.field.roles')->onlyOnForms();
        }

        yield FormField::addTab('admin_label.common.panel.additional');

        yield IdField::new('id', 'admin_label.common.field.id')->onlyOnDetail();

        yield DateTimeField::new('createdAt', 'admin_label.common.field.createdAt')->onlyOnDetail();
        yield DateTimeField::new('updatedAt', 'admin_label.common.field.updatedAt')->onlyOnDetail();
        yield AssociationField::new('createdBy', 'admin_label.common.field.createdBy')->onlyOnDetail();
        yield AssociationField::new('updatedBy', 'admin_label.common.field.updatedBy')->onlyOnDetail();

        yield DateTimeField::new('deletedAt', 'admin_label.common.field.deletedAt')->onlyOnDetail();

        yield TextField::new('virtualField')->onlyOnDetail();
    }
}

But I'd like to hear comments from @javiereguiluz first.

@TheoD02
Copy link

TheoD02 commented Dec 2, 2022

As I #5495 (comment) something wrong on my side with assets building tool (MacBook Pro, yarn 1.22.19). Probably padding issue has the same source.

I rebuild from my side (Docker on WSL2) i didn't have any issue during build, but just this padding added.

What storage class are you using? Session?

Currently yes, i don't have any User entity i don't have test with entity
Additional information, when i don't define $availableColumns after reset or save & reload all fields appear in modal

Good idea. I thought about it. For example, make it configurable in configureFields:

Exactly ! I think about the same idea with EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait we can add on each EA included Field and for user custom Field too ! That would be the best implementation we wait for @javiereguiluz response about that 😄

Default :
All returned field would be available in modal for selection expect excluded.
If all fields don't call addToDefaultColumns() => all field to set default

If at least one call addToDefaultColumns() set only thoses who have called this method


And I just thought of a feature, it would be cool to be able to propose a preset for example in the case of a company with several services that use the data differently, only possible via DB or then via a config file or function like configureCrud for configureColumnChooserPresets([])

Config 1:
id
name
phone

Config 2 :
id
email
address
updatedAt

@ksn135
Copy link
Contributor Author

ksn135 commented Dec 3, 2022

As I #5495 (comment) something wrong on my side with assets building tool (MacBook Pro, yarn 1.22.19). Probably padding issue has the same source.

I rebuild from my side (Docker on WSL2) i didn't have any issue during build

What tool are you using to build assets? Yarn? What is the version number?

but just this padding added.

I did NOT change any stylesheets. May be it comes from sortablejs?

What storage class are you using? Session?
Currently yes, i don't have any User entity i don't have test with entity Additional information, when i don't define $availableColumns after reset or save & reload all fields appear in modal

I'll try to reproduce and fix it.

Good idea. I thought about it. For example, make it configurable in configureFields:
Exactly ! I think about the same idea with EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait we can add on each EA included Field and for user custom Field too ! That would be the best implementation we wait for @javiereguiluz response about that 😄

Yep! Still waiting...

Default : All returned field would be available in modal for selection expect excluded. If all fields don't call addToDefaultColumns() => all field to set default
If at least one call addToDefaultColumns() set only thoses who have called this method

it's looks great to me.

And I just thought of a feature, it would be cool to be able to propose a preset for example in the case of a company with several services that use the data differently, only possible via DB or then via a config file or function like configureCrud for configureColumnChooserPresets([])

Config 1: id name phone

Config 2 : id email address updatedAt

I understand your point of view.
I think it's better to do it in the configureCrud function

@TheoD02
Copy link

TheoD02 commented Dec 5, 2022

What tool are you using to build assets? Yarn? What is the version number?

Using version 1.22.15 (using http://devilbox.org/ stack with docker)

I did NOT change any stylesheets. May be it comes from sortablejs?

I tested without import "sortablejs" but have the same result, in changed files in didn't see anything about styles too.

I think it's better to do it in the configureCrud function

Yes, have time to reflect about what is the best for this implementation

@ksn135
Copy link
Contributor Author

ksn135 commented Dec 8, 2022

Hi @TheoD02 !

if I not sent in the configureFields function then the modal returns bad information on fields possibly impossible to display

Please take a look. I hope that finally fixed it.

@TheoD02
Copy link

TheoD02 commented Jan 17, 2023

Hello back from long time !

I tested the current state of PR.

Something confuse me, will explain :

I have a basic "post" entity that have this following fields :

  • id
  • title
  • description
  • content
  • createdAt
  • updatedAt

I made a simple Crud for this entity, and configured column chooser with the following :

return parent::configureCrud($crud)
           ->enableColumnChooser()
           ->setColumnChooserSelectedColumnStorageProvider($this->sessionSelectedColumnStorageProvider)
           ->setColumnChooserColumns(
               ['id', 'title'],
               ['content', 'description'],
               ['updatedAt']
           )
       ;

With this configuration, I get the following configuration in admin entity crud page
image

I only have id and title fields in modal available to choose. But id, title, description and content should be available to choose that is rights ? Because id/title is on default fields and content/description is on availableColumns.
Tested to add id/title in availableColumns too, but didn't change anything.

With this configuration, my expect was to have default list with id/title only showed by default and availability to add/remove field of the availableColumns list.

I can't modify the sort of fields, disable one field of the list didn't save anything that i have changed in session after reload.

-- EDIT
After some code review, seems to be caused in the "getCurrentColumns" function it return only id and title instead of id, title, description, content

Replacing :

return array_flip(array_merge(
            array_combine($this->getSelectedColumns(), $this->getSelectedColumns()),
            $this->indexAvailableColumnsWithLabels,
));

With :

return array_flip(array_merge(
            array_combine($this->getSelectedColumns(), $this->getSelectedColumns()),
            $this->indexAvailableColumnsWithLabels,
            array_combine($this->getAvailableColumns(), $this->getAvailableColumns()),
));

after this change all seem fine for now, need more testing but fix the above problem 😄

@ksn135
Copy link
Contributor Author

ksn135 commented Jan 17, 2023

Hello @TheoD02 !

after this change all seem fine for now, need more testing but fix the above problem 😄

O! Great news!

PS: Just in time! I just started building a test project from scratch https://github.com/ksn135/ea-cc-test
Hope it's no longer needed! )

PS2: I didn't see your last edit in the GitHub's letter
Just now I saw that you already found a solution!

@ksn135
Copy link
Contributor Author

ksn135 commented Jan 17, 2023

renamed:    src/Interfaces/SelectedColumnStorageProviderInterface.php -> src/Contracts/ColumnStorage/SelectedColumnStorageProviderInterface.php
renamed:    src/Interfaces/UserParametersStorageInterface.php -> src/Contracts/ColumnStorage/UserParametersStorageInterface.php

@TheoD02
Copy link

TheoD02 commented Jan 23, 2023

Hi,
Checked the project you provided.

Seem to be fine now 😄

I found a case that just for user experience :
When no one case is checked, prevent possibility to click on "Save and reload" button and maybe show a message for informate the user at least 1 column is mandatory

Another one idea come in my head, is to add a bridge for manage column for each entity directly from admin.
That will prevent for example make a change in a sprint, and wait X weeks before having a change in production.

I imagine that with optional extension package to install to let user the choice to use this if wanted and not overload EA with table.

Adding one function to choose between configuration in code, or through the package from admin (DashboardController, possible override in each Crud ? Is really necessary ?)

@ksn135
Copy link
Contributor Author

ksn135 commented Jan 24, 2023

Hi!

Seem to be fine now 😄

Super!

I found a case that just for user experience : When no one case is checked, prevent possibility to click on "Save and reload" button and maybe show a message for informate the user at least 1 column is mandatory

Ok, I'll make it later.

Another one idea come in my head, is to add a bridge for manage column for each entity directly from admin.

Well, I don't understand you very well.

I have already implemented the ability to store user column settings in the database in the table of user parameters, not in the attributes of the session via UserSelectedColumnStorageProvider
Screenshot 2023-01-24 at 11 01 04
That's exactly how it works for me.
Screenshot 2023-01-24 at 10 57 47

@TheoD022222
Copy link

Well, I don't understand you very well.

Hello,

Ok this function is a luxury, but imagine that you want to change the default configuration on the "order" table for all users.

That is, for example, currently you have the fields :

  • id
  • reference
  • buyer_last_name
  • buyer_first_name
  • address
  • country

Today the default fields are configured in the code, we would have something like this:

->setColumnChooserColumns(
      ['reference', 'buyer_last_name', 'country'], // default
)

Let's say you want to change this field to default, but without having to do any development, deployment or code changes.

Wouldn't it be interesting to be able to change this columns directly from a user interface to configure the fields defined by default?

This is a proposal, it can be useless for many people, that's why I indicate in my previous message that it could be configurable either via the code, or via an interface if you wish.

@ksn135
Copy link
Contributor Author

ksn135 commented Mar 1, 2023

Hi, I'm ready to help. Apparently we don't fully understand each other.
As I wrote earlier, the current version allows you to store user settings not only in the session, but also in the database.
If we're talking about the default settings, they can probably also be stored and easily retrieved from the database.
For example, you can take default columns from the database and pass them as a parameter to setColumnChooserColumns function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants