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

Question: what hook determines the data that is sent to resource data a list? #151

Open
osseonews opened this issue Nov 14, 2024 · 12 comments

Comments

@osseonews
Copy link
Contributor

We are struggling to figure out where the data comes from for any resource on a List page. So for example in OrderList.tsx in the orders app, you have the FilteredList component, which sends the data to the ListItemOrder template. But the data provided thru the ListItemOrder is a resource prop. Where does the data come from for the resource? It's only has certain information from the order, but we wanted to display additional information on the list. We thought we can do that by adding a query to the FilteredList, like below, but none of that information shows up in the resource for the list. So how do we get additional data in the list? The only optio we saw that worked was to just call a useOrdersDetails hook for every single item in the list, but this seems like a wasteful query and we will hit the API a significant amount of time for each item in the list. But how can we do it otherwise? Which hook controls the data that is sent back to the list?

<FilteredList
          type='orders'
          ItemTemplate={ListItemOrder}
          query={{
            include: ['customer']
          }}
          metricsQuery={{
            search: {
              limit: 25,
              sort: 'desc',
              sort_by: isPendingOrdersList
                ? 'order.updated_at'
                : 'order.placed_at',
              fields: ['order.*', 'billing_address.*', 'market.*','customer.*']
            }
          }}
          hideTitle={viewTitle === presets.pending.viewTitle}
          emptyState={
            <ListEmptyState
              scope={
                hasActiveFilter
                  ? 'userFiltered'
                  : viewTitle !== presets.history.viewTitle
                    ? 'presetView'
                    : 'history'
              }
            />
          }
        />

@osseonews
Copy link
Contributor Author

OK, I see the problem here. The List Item is controlled by the ResourceListItem component. This component has a transform function apparently, so only some of the resource data is displayed, based on the hard coded transform function in the actual app elements code. I suggest you allow an additional data type prop that allows you to pass in additional data you want to be displayed as part of the ResourceListItem, because the tranform function doesn't always give the data that is needed. I know we can just create a new ListeItem component, but that's a bit more work. It would be easier if the ResourceListItem accepted some additional data that is not transformed. Maybe allow the ability to pass in an additional component into the ResourceListItem, which can then display the additional data.

@gciotola
Copy link
Contributor

Hi @osseonews, I believe what you are trying to achieve would be possibile with the existing components.

The FilteredList component is a wrapper around the ResourceList exported by the useResourceList filters.
This means that the filtered list use the ResourceList but applies automatically the filters selected from the UI.

The orders app is the only one that leverages the metrics api (and not our core api) to fetch the list of orders, for performance reasons.
The data returned from metrics api does not support the including of relationships as our core api does.

What I see is not clear from the developer point of view is that is not possible to set both metricsQuery and query, since the first one will have priority.

When you specify a metricsQuery prop, the components will use the metrics api to fetch data and then then the transformation function will be executed to adapt the metrics response to the shape of our core api. Of course the transformation will be done only for the attributes that exists in metrics api.

What you can do now is to get rid of the metricsQuery prop and only use query prop, adding the include you want.
You will also need to modify the filter instruction object that is now shaped fore the metrics api attributes.

Mainly you will need to revert to the state where the app was before using the metrics api. A reference can still be found here: https://github.com/commercelayer/app-orders/pull/146/files

Let me know if this gets too complicate or it's still doable.

@osseonews
Copy link
Contributor Author

@gciotola Thanks. Actually, after messing around a bit with the code, we ended up doing the exact same thing as you suggested. The metricsquery is what really threw us off and when we deleted it, we noticed that you can't use that and query at the same time. But, if you get rid of the metricsquery you also need to fix the filter in the search for order. Thanks for the reference to the old repo, that was helpful in fixing some outstanding issues we had.

BTW, what's the benefit of using ResourceList alone over the FilteredList? From what I can tell the Resource list has built in transformers and they are not necessarily the data we actually need in most instances. So we are busy changing the ResourceList to FitleredLists when we need to customize things.

@gciotola
Copy link
Contributor

BTW, what's the benefit of using ResourceList alone over the FilteredList? From what I can tell the Resource list has built in transformers and they are not necessarily the data we actually need in most instances. So we are busy changing the ResourceList to FitleredLists when we need to customize things.

They are exactly the same component.
The only component is ResourceList that is responsible to list (with infinite scrolling) the specified resource with the specified query.

FilteredList needs to be used along with other filters component since it is already connected with the selected filters as specified here

If you do not have a search bar and the filters page (<SearchWithNav />), you don't need FilteredList.

To clarify

the standard usage of <ResourceList> is destructuring the component from its hook useResourceList and pass the filters you want as your normal filters query (same objected used by the sdk)

const { ResourceList } = useResourceList({
  type: 'orders',
  query: {
    fields: ['number', 'market'],
    filters: {
      status_eq: 'placed'
    },
    pageSize: 25,
    sort: [...],
    include: ['market']
  }
})
)

return (
  <ResourceList 
       title='My orders list'  
       ItemTemplate={(props) => <div>order number {props.resource?.number} </div> } 
  />
)

But if you don't want to build the filters object yourself, you can use the our filters hook that returns also the components to build the filters form and a search bar, along with a ResourceList that automatically reacts when user interact with the search bar or with the filters form.
In fact the query prop on the FilteredList won't allow to set filters key, but you can only add fields, sort, pageSize and include keys.
Under the hood it uses ResourceList (view source) and automatically applies filters from URL query string (that are generated by FiltersForm component or SearchWithNav)

image

So finally, to answer the following:

what's the benefit of using ResourceList alone over the FilteredList?
There is not really a benefit, but ResourceList is the base component used to build FilteredList (from a component composition point of view).

Imagine you want to list the orders of a specific customer, for a specific market, with placed status (fixed list, not dinamically filtrable by the user). You can simply do:

const { ResourceList } = useResourceList({
  type: 'orders',
  query: {
    filters: {
      status_eq: 'placed',
      markets_id_in: ['xxxx'],
      customer_id_eq: 'zzzzz'
    },
    pageSize: 25
  }
})
)

return (
  <ResourceList 
       title='My orders list'  
       ItemTemplate={(props) => <div>order number {props.resource?.number} </div> } 
  />
)

@osseonews
Copy link
Contributor Author

osseonews commented Nov 15, 2024

Right but when you use ResourceList the component has a built in transform and it only returns the data that is hard coded into the ResourceList component. Basically, it seems like a quick way to create a list from a resource. That's at least how we see the results. But you can't customize the display of the list at all, to include additional data etc. So when we want to customize the list, we have been using FilteredList. Is that the correct approach?

@osseonews
Copy link
Contributor Author

For example, take a look at your code in the customers app. CustomerList component uses FilteredList to display the data, while CustomerOrders component uses ResourceList.

@gciotola
Copy link
Contributor

gciotola commented Nov 15, 2024

Right but when you use ResourceList the component has a built in transform and it only returns the data that is hard coded into the ResourceList component. Basically, it seems like a quick way to create a list from a resource. That's at least how we see the results. But you can't customize the display of the list at all, to include additional data etc. So when we want to customize the list, we have been using FilteredList. Is that the correct approach?

No, that's not correct. The transformation function is only used when metrics api is used (so when metricsQuery prop is defined).
The ItemTemplate prop is the function that will be used to render each single item of the list as retuned by the API request.

If you look at the example above, I am using ItemTemplate to fill the list with a custom div.
Let me re-writing it adding an include to show for each order the customer email and the payment method

const { ResourceList } = useResourceList({
  type: 'orders',
  query: {
    filters: { 
      // optional filters
    },
   include: ['customer', 'payment_method'],
    pageSize: 25
  }
})
)

return (
  <ResourceList 
       title='My orders list'  
       ItemTemplate={(props) => 
             // props.resource is now a single `order` because the `useResourceList` has been initialized with `type: orders`
             <div className="single-list-item">
                 <ul>
                    <li>order number {props.resource?.number}</li>
                    <li>placed at {props.resource?.placed_at}</li>
                    <li>from customer {props.resource?.customer?.email}</li>
                    <li>payed by {props.resource?. payment_method?.name}</li>
                 </ul>
              </div>
            } 
  />
)

You can do whatever you want with ItemTemplate for both ResourceList (from useResourceList) and FilteredList (from useResourceFilters)

@osseonews
Copy link
Contributor Author

metricsQuery: Oh ok, that's what confused us. Because we were on the orders app. now i understand.

But, based on what you wrote, why in the Customers App, do you use Filtered List in CustomerList component instead of ResourceList?

@gciotola
Copy link
Contributor

gciotola commented Nov 15, 2024

because CustomerList has a filtrable list of customers (as any other*List page)

Applied filters will be added in query-string and list is automatically filtered every time the user interact with the other filters component
image


image

@osseonews
Copy link
Contributor Author

Ah ok. I think I am finally understanding this and when to use what. Thanks!

@osseonews
Copy link
Contributor Author

Ok, so I've been confusing things. Our issue is with the ResourceListItem component. This has a built in transformer, so if you pass data to it from ResourceList or FilterList, you can't get all the data you want and you need to create a separate component. This is a bit annoying, if I'm understanding this correctly. Why does ResourceListItem have built in transformers?

@gciotola
Copy link
Contributor

gciotola commented Nov 18, 2024

That's right. ResourceListItem is a component that we use to share the same UI (for a specific resource list) in all apps.
In this way we have the same item to preview an order or a customer everywhere.

But it's just a pre-made component, you can build your custom list item component to be used in ItemTemplate prop.
It will receive the resource fetched from the list with the query (filters and include) you have specified.

Not all apps have a prebuilt ResourceListItem imported from app-elements. Example the gift card apps has a custom one defined directly inside the app.

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

No branches or pull requests

2 participants