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

Demo: Use grid in dialog as multi-select, forward data-grid-props #2428

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

Ben-Ho
Copy link
Contributor

@Ben-Ho Ben-Ho commented Aug 14, 2024

Requirement

Select many entities, probably required to search for them by applying filters.
The UX-Team defined using a grid with checkboxes and filter-button, similar to all other grids. If there are already items selected they have to be preselected regardless of paging. There is no special sorting for preselected items to be on first page.

Example

This can be used in multiple cases like assigning them to another entity, adding them to some kind of list or queue. It just provides the list of selected id's.

Solution

Using selection-feature of DataGrid, hiding away grid-config (e.g. columns, filter, sorting) in demo/admin/src/products/categories/ProductsSelectGrid.tsx, but forwarding DataGrid selection-feature-props to allow to add selection-behavior.

Notes

  • There are many changes in this pull-request to provide the basic structure to navigate to, and to actually do something with the selected ids. They probably also need to be considered.
  • This solution, in contrast to Demo: Use grid in dialog as multi-select #2203 , does not introduce new props but forwards the very similar DataGrid-Props. Everything else is identically.
  • Another use case for the select-grid is to use it in a FinalForm-Form to add id's to the create/update mutation of the base-entity. We should probably add this use case as well. (And check with UX-Team if this should really be supported)

Open issues

  • Inline-Dialog in demo/admin/src/products/categories/AssignedProductsGrid.tsx should actually be some comet SaveDialog to provide required default-features like SaveBoundary and check-unsaved-changes-on-close-dialog
  • Missing example for select-grid usage in FinalForm-Form.
  • Discussion: When to "create a new grid" vs "add use case to an existing grid"? Example demo/admin/src/products/ProductsGrid.tsx reused as AssignedProductsGrid. (Differences: Add-Button-behavior, CrudContextMenu-Delete-Button-behaviour, probably no copy/paste behavior)
  • Reminder: Allow support for "Select/Assign all"-Feature in project

@Ben-Ho Ben-Ho requested review from nsams and johnnyomair August 14, 2024 21:26
@Ben-Ho Ben-Ho self-assigned this Aug 14, 2024
<AssignProductsGrid productCategoryId={productCategoryId} />
</DialogContent>
<DialogActions>
{/* TODO Missing close-dialog-unsaved-changes-check */}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that should be working correclty, <Savable hasChanges is true it should work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked again, it does not trigger a popup. I currently don't know why... probably because of the subRoutePath? opening the dialog does not change the path/url. it's still the same as for the routerTab.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look: you are using a plain mui Dialog. That obviously doesn't integrate our SaveBoundary.

You have to use our EditDialog instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I started with comet-EditDialog but it had some issues with RouterTabs and StackPage so @thomasdax98 recommended to use plain mui Dialog. (probably you missed it: https://vividplanet.slack.com/archives/CTBMBLF38/p1718879902959449)

Comment on lines 47 to 53
const initialValues = useMemo(() => {
return data?.products.nodes ? data.products.nodes.map((product) => product.id) : [];
}, [data]);
const [values, setValues] = useState<string[]>(initialValues);
useEffect(() => {
setValues(initialValues);
}, [initialValues]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure that can be done simpler. You now have 4 things that work somehow together: useQuery, useMemo, useState and useEffect. That is too complicated. (I would not care if you do that in an application, but we are creating best practice here - that might serve as template for code generation)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright, I have no idea... maybe use useReducer? do you have any idea? or @johnnyomair?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, useReducer would be an overkill. We could probably eliminate initialValues altogether:

  1. For reset, we can use data.products.nodes
  2. For hasChanges, we can also use data.products.nodes. We could also add a hasChanges which is only updated in onChange, which would remove the need to sort the arrays in hasChanges

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only managed to remove useMemo, it's still required to use useEffect because data.products is initially undefined so values has to be updated. (and useState can't be called after if (loading) ...)

Copy link
Collaborator

@johnnyomair johnnyomair Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the best I've came up with, eliminating two out of four:

const { data, error, loading } = useQuery<GQLGetProductIdsForProductCategoryQuery, GQLGetProductIdsForProductCategoryQueryVariables>(
    getProductIdsForProductCategory,
    {
        variables: { id: productCategoryId },
        onCompleted: (data) => {
            setValues(data.products.nodes.map((product) => product.id));
        },
    },
);

const [values, setValues] = useState<string[]>([]);

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I'd still suggest having a hasChanges state instead of comparing and sorting two arrays at every rendern. But this is probably premature optimization

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think?

I'll take it :-D

BTW, I'd still suggest having a hasChanges state instead of comparing and sorting two arrays at every rendern. But this is probably premature optimization

I already added a console.log and it seems it's not called that often. probably nearly as often as the hasChange-state would change/needed to be checked. (for every click on one of the checkboxes, and additionally on closing the dialog)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it needed that this is an additional grid? Couldn't this be the "normal" products grid with a pre-applied filter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was before. but you mentioned a second grid. It was also discussed to do more specific generating instead of generating an ultra generic grid because generating already reduces the cost of duplication. (at least that's what I understood) and maybe we would also do some config-sharing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure back then and I'm still not sure now 😁

Maybe my "problem" is with having a second handmade grid. For the generator it should be okay though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a dilemma, because we said want to generate how we would implement :-D

to achieve this it would probably require some additional changes in admin-generator... e.g. allow to change delete-button behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep it like this and defer the decision to later.

demo/admin/src/products/categories/ProductsSelectGrid.tsx Outdated Show resolved Hide resolved
demo/admin/src/products/categories/AssignedProductsTab.tsx Outdated Show resolved Hide resolved
Copy link
Collaborator

@johnnyomair johnnyomair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix the lint error

@Ben-Ho Ben-Ho requested a review from johnnyomair October 1, 2024 07:44
johnnyomair
johnnyomair previously approved these changes Oct 1, 2024
@Ben-Ho
Copy link
Contributor Author

Ben-Ho commented Oct 30, 2024

@johnnyomair I think this pull-request is ready. But this demo-code should probably use EditDialog instead of Dialog, but there are still some issues as far as I know.

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.

4 participants