Use query hooks in product list

This commit is contained in:
dominik-zeglen 2020-08-24 12:10:48 +02:00
parent 7726bacb77
commit 4df1c71a79
2 changed files with 305 additions and 347 deletions

View file

@ -150,10 +150,9 @@ const productListQuery = gql`
} }
} }
`; `;
export const TypedProductListQuery = TypedQuery< export const useProductListQuery = makeQuery<ProductList, ProductListVariables>(
ProductList, productListQuery
ProductListVariables );
>(productListQuery);
const countAllProductsQuery = gql` const countAllProductsQuery = gql`
query CountAllProducts { query CountAllProducts {
@ -288,7 +287,7 @@ const availableInGridAttributes = gql`
} }
} }
`; `;
export const AvailableInGridAttributesQuery = TypedQuery< export const useAvailableInGridAttributesQuery = makeQuery<
GridAttributes, GridAttributes,
GridAttributesVariables GridAttributesVariables
>(availableInGridAttributes); >(availableInGridAttributes);

View file

@ -45,18 +45,16 @@ import { FormattedMessage, useIntl } from "react-intl";
import ProductListPage from "../../components/ProductListPage"; import ProductListPage from "../../components/ProductListPage";
import { import {
TypedProductBulkDeleteMutation, useProductBulkDeleteMutation,
TypedProductBulkPublishMutation, useProductBulkPublishMutation,
useProductExport useProductExport
} from "../../mutations"; } from "../../mutations";
import { import {
AvailableInGridAttributesQuery, useAvailableInGridAttributesQuery,
TypedProductListQuery,
useCountAllProducts, useCountAllProducts,
useInitialProductFilterDataQuery useInitialProductFilterDataQuery,
useProductListQuery
} from "../../queries"; } from "../../queries";
import { productBulkDelete } from "../../types/productBulkDelete";
import { productBulkPublish } from "../../types/productBulkPublish";
import { import {
productAddUrl, productAddUrl,
productListUrl, productListUrl,
@ -235,6 +233,53 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
}), }),
[params, settings.rowNumber] [params, settings.rowNumber]
); );
const { data, loading, refetch } = useProductListQuery({
displayLoader: true,
variables: queryVariables
});
function filterColumnIds(columns: ProductListColumns[]) {
return columns
.filter(isAttributeColumnValue)
.map(getAttributeIdFromColumnValue);
}
const attributes = useAvailableInGridAttributesQuery({
variables: { first: 6, ids: filterColumnIds(settings.columns) }
});
const [
productBulkDelete,
productBulkDeleteOpts
] = useProductBulkDeleteMutation({
onCompleted: data => {
if (data.productBulkDelete.errors.length === 0) {
closeModal();
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges)
});
reset();
refetch();
}
}
});
const [
productBulkPublish,
productBulkPublishOpts
] = useProductBulkPublishMutation({
onCompleted: data => {
if (data.productBulkPublish.errors.length === 0) {
closeModal();
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges)
});
reset();
refetch();
}
}
});
const filterOpts = getFilterOpts( const filterOpts = getFilterOpts(
params, params,
@ -262,345 +307,259 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
} }
); );
function filterColumnIds(columns: ProductListColumns[]) { const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
return columns maybe(() => data.products.pageInfo),
.filter(isAttributeColumnValue) paginationState,
.map(getAttributeIdFromColumnValue); params
} );
return ( return (
<AvailableInGridAttributesQuery <>
variables={{ first: 6, ids: filterColumnIds(settings.columns) }} <ProductListPage
> activeAttributeSortId={params.attributeId}
{attributes => ( sort={{
<TypedProductListQuery displayLoader variables={queryVariables}> asc: params.asc,
{({ data, loading, refetch }) => { sort: params.sort
const { loadNextPage, loadPreviousPage, pageInfo } = paginate( }}
maybe(() => data.products.pageInfo), onSort={handleSort}
paginationState, availableInGridAttributes={maybe(
params () => attributes.data.availableInGrid.edges.map(edge => edge.node),
); []
)}
const handleBulkDelete = (data: productBulkDelete) => { currencySymbol={currencySymbol}
if (data.productBulkDelete.errors.length === 0) { currentTab={currentTab}
closeModal(); defaultSettings={defaultListSettings[ListViews.PRODUCT_LIST]}
notify({ filterOpts={filterOpts}
status: "success", gridAttributes={maybe(
text: intl.formatMessage(commonMessages.savedChanges) () => attributes.data.grid.edges.map(edge => edge.node),
}); []
reset(); )}
refetch(); totalGridAttributes={maybe(
() => attributes.data.availableInGrid.totalCount,
0
)}
settings={settings}
loading={attributes.loading}
hasMore={maybe(
() => attributes.data.availableInGrid.pageInfo.hasNextPage,
false
)}
onAdd={() => navigate(productAddUrl)}
disabled={loading}
products={maybe(() => data.products.edges.map(edge => edge.node))}
onFetchMore={() =>
attributes.loadMore(
(prev, next) => {
if (
prev.availableInGrid.pageInfo.endCursor ===
next.availableInGrid.pageInfo.endCursor
) {
return prev;
} }
}; return {
...prev,
const handleBulkPublish = (data: productBulkPublish) => { availableInGrid: {
if (data.productBulkPublish.errors.length === 0) { ...prev.availableInGrid,
closeModal(); edges: [
notify({ ...prev.availableInGrid.edges,
status: "success", ...next.availableInGrid.edges
text: intl.formatMessage(commonMessages.savedChanges) ],
}); pageInfo: next.availableInGrid.pageInfo
reset(); }
refetch(); };
},
{
after: attributes.data.availableInGrid.pageInfo.endCursor
}
)
}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onUpdateListSettings={updateListSettings}
pageInfo={pageInfo}
onRowClick={id => () => navigate(productUrl(id))}
onAll={resetFilters}
toolbar={
<>
<Button
color="primary"
onClick={() =>
openModal("unpublish", {
ids: listElements
})
} }
}; >
<FormattedMessage
return ( defaultMessage="Unpublish"
<TypedProductBulkDeleteMutation onCompleted={handleBulkDelete}> description="unpublish product, button"
{(productBulkDelete, productBulkDeleteOpts) => ( />
<TypedProductBulkPublishMutation </Button>
onCompleted={handleBulkPublish} <Button
> color="primary"
{(productBulkPublish, productBulkPublishOpts) => ( onClick={() =>
<> openModal("publish", {
<ProductListPage ids: listElements
activeAttributeSortId={params.attributeId} })
sort={{ }
asc: params.asc, >
sort: params.sort <FormattedMessage
}} defaultMessage="Publish"
onSort={handleSort} description="publish product, button"
availableInGridAttributes={maybe( />
() => </Button>
attributes.data.availableInGrid.edges.map( <IconButton
edge => edge.node color="primary"
), onClick={() =>
[] openModal("delete", {
)} ids: listElements
currencySymbol={currencySymbol} })
currentTab={currentTab} }
defaultSettings={ >
defaultListSettings[ListViews.PRODUCT_LIST] <DeleteIcon />
} </IconButton>
filterOpts={filterOpts} </>
gridAttributes={maybe( }
() => isChecked={isSelected}
attributes.data.grid.edges.map(edge => edge.node), selected={listElements.length}
[] toggle={toggle}
)} toggleAll={toggleAll}
totalGridAttributes={maybe( onSearchChange={handleSearchChange}
() => attributes.data.availableInGrid.totalCount, onFilterChange={changeFilters}
0 onTabSave={() => openModal("save-search")}
)} onTabDelete={() => openModal("delete-search")}
settings={settings} onTabChange={handleTabChange}
loading={attributes.loading} initialSearch={params.query || ""}
hasMore={maybe( tabs={getFilterTabs().map(tab => tab.name)}
() => onExport={() => openModal("export")}
attributes.data.availableInGrid.pageInfo />
.hasNextPage, <ActionDialog
false open={params.action === "delete"}
)} confirmButtonState={productBulkDeleteOpts.status}
onAdd={() => navigate(productAddUrl)} onClose={closeModal}
disabled={loading} onConfirm={() =>
products={maybe(() => productBulkDelete({
data.products.edges.map(edge => edge.node) variables: { ids: params.ids }
)} })
onFetchMore={() => }
attributes.loadMore( title={intl.formatMessage({
(prev, next) => { defaultMessage: "Delete Products",
if ( description: "dialog header"
prev.availableInGrid.pageInfo.endCursor === })}
next.availableInGrid.pageInfo.endCursor variant="delete"
) { >
return prev; <DialogContentText>
} <FormattedMessage
return { defaultMessage="{counter,plural,one{Are you sure you want to delete this product?} other{Are you sure you want to delete {displayQuantity} products?}}"
...prev, description="dialog content"
availableInGrid: { values={{
...prev.availableInGrid, counter: maybe(() => params.ids.length),
edges: [ displayQuantity: <strong>{maybe(() => params.ids.length)}</strong>
...prev.availableInGrid.edges, }}
...next.availableInGrid.edges />
], </DialogContentText>
pageInfo: next.availableInGrid.pageInfo </ActionDialog>
} <ActionDialog
}; open={params.action === "publish"}
}, confirmButtonState={productBulkPublishOpts.status}
{ onClose={closeModal}
after: onConfirm={() =>
attributes.data.availableInGrid.pageInfo productBulkPublish({
.endCursor variables: {
} ids: params.ids,
) isPublished: true
} }
onNextPage={loadNextPage} })
onPreviousPage={loadPreviousPage} }
onUpdateListSettings={updateListSettings} title={intl.formatMessage({
pageInfo={pageInfo} defaultMessage: "Publish Products",
onRowClick={id => () => navigate(productUrl(id))} description: "dialog header"
onAll={resetFilters} })}
toolbar={ >
<> <DialogContentText>
<Button <FormattedMessage
color="primary" defaultMessage="{counter,plural,one{Are you sure you want to publish this product?} other{Are you sure you want to publish {displayQuantity} products?}}"
onClick={() => description="dialog content"
openModal("unpublish", { values={{
ids: listElements counter: maybe(() => params.ids.length),
}) displayQuantity: <strong>{maybe(() => params.ids.length)}</strong>
} }}
> />
<FormattedMessage </DialogContentText>
defaultMessage="Unpublish" </ActionDialog>
description="unpublish product, button" <ActionDialog
/> open={params.action === "unpublish"}
</Button> confirmButtonState={productBulkPublishOpts.status}
<Button onClose={closeModal}
color="primary" onConfirm={() =>
onClick={() => productBulkPublish({
openModal("publish", { variables: {
ids: listElements ids: params.ids,
}) isPublished: false
} }
> })
<FormattedMessage }
defaultMessage="Publish" title={intl.formatMessage({
description="publish product, button" defaultMessage: "Unpublish Products",
/> description: "dialog header"
</Button> })}
<IconButton >
color="primary" <DialogContentText>
onClick={() => <FormattedMessage
openModal("delete", { defaultMessage="{counter,plural,one{Are you sure you want to unpublish this product?} other{Are you sure you want to unpublish {displayQuantity} products?}}"
ids: listElements description="dialog content"
}) values={{
} counter: maybe(() => params.ids.length),
> displayQuantity: <strong>{maybe(() => params.ids.length)}</strong>
<DeleteIcon /> }}
</IconButton> />
</> </DialogContentText>
} </ActionDialog>
isChecked={isSelected} <ProductExportDialog
selected={listElements.length} attributes={(searchAttributes.result.data?.search.edges || []).map(
toggle={toggle} edge => edge.node
toggleAll={toggleAll} )}
onSearchChange={handleSearchChange} hasMore={searchAttributes.result.data?.search.pageInfo.hasNextPage}
onFilterChange={changeFilters} loading={searchAttributes.result.loading}
onTabSave={() => openModal("save-search")} onFetch={searchAttributes.search}
onTabDelete={() => openModal("delete-search")} onFetchMore={searchAttributes.loadMore}
onTabChange={handleTabChange} open={params.action === "export"}
initialSearch={params.query || ""} confirmButtonState={exportProductsOpts.status}
tabs={getFilterTabs().map(tab => tab.name)} errors={exportProductsOpts.data?.exportProducts.errors || []}
onExport={() => openModal("export")} productQuantity={{
/> all: countAllProducts.data?.products.totalCount,
<ActionDialog filter: data?.products.totalCount
open={params.action === "delete"} }}
confirmButtonState={productBulkDeleteOpts.status} selectedProducts={listElements.length}
onClose={closeModal} warehouses={
onConfirm={() => warehouses.data?.warehouses.edges.map(edge => edge.node) || []
productBulkDelete({ }
variables: { ids: params.ids } onClose={closeModal}
}) onSubmit={data =>
} exportProducts({
title={intl.formatMessage({ variables: {
defaultMessage: "Delete Products", input: {
description: "dialog header" ...data,
})} filter,
variant="delete" ids: listElements
> }
<DialogContentText> }
<FormattedMessage })
defaultMessage="{counter,plural,one{Are you sure you want to delete this product?} other{Are you sure you want to delete {displayQuantity} products?}}" }
description="dialog content" />
values={{ <SaveFilterTabDialog
counter: maybe(() => params.ids.length), open={params.action === "save-search"}
displayQuantity: ( confirmButtonState="default"
<strong> onClose={closeModal}
{maybe(() => params.ids.length)} onSubmit={handleFilterTabSave}
</strong> />
) <DeleteFilterTabDialog
}} open={params.action === "delete-search"}
/> confirmButtonState="default"
</DialogContentText> onClose={closeModal}
</ActionDialog> onSubmit={handleFilterTabDelete}
<ActionDialog tabName={maybe(() => tabs[currentTab - 1].name, "...")}
open={params.action === "publish"} />
confirmButtonState={productBulkPublishOpts.status} </>
onClose={closeModal}
onConfirm={() =>
productBulkPublish({
variables: {
ids: params.ids,
isPublished: true
}
})
}
title={intl.formatMessage({
defaultMessage: "Publish Products",
description: "dialog header"
})}
>
<DialogContentText>
<FormattedMessage
defaultMessage="{counter,plural,one{Are you sure you want to publish this product?} other{Are you sure you want to publish {displayQuantity} products?}}"
description="dialog content"
values={{
counter: maybe(() => params.ids.length),
displayQuantity: (
<strong>
{maybe(() => params.ids.length)}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
<ActionDialog
open={params.action === "unpublish"}
confirmButtonState={productBulkPublishOpts.status}
onClose={closeModal}
onConfirm={() =>
productBulkPublish({
variables: {
ids: params.ids,
isPublished: false
}
})
}
title={intl.formatMessage({
defaultMessage: "Unpublish Products",
description: "dialog header"
})}
>
<DialogContentText>
<FormattedMessage
defaultMessage="{counter,plural,one{Are you sure you want to unpublish this product?} other{Are you sure you want to unpublish {displayQuantity} products?}}"
description="dialog content"
values={{
counter: maybe(() => params.ids.length),
displayQuantity: (
<strong>
{maybe(() => params.ids.length)}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
<ProductExportDialog
attributes={(
searchAttributes.result.data?.search.edges || []
).map(edge => edge.node)}
hasMore={
searchAttributes.result.data?.search.pageInfo
.hasNextPage
}
loading={searchAttributes.result.loading}
onFetch={searchAttributes.search}
onFetchMore={searchAttributes.loadMore}
open={params.action === "export"}
confirmButtonState={exportProductsOpts.status}
errors={
exportProductsOpts.data?.exportProducts.errors || []
}
productQuantity={{
all: countAllProducts.data?.products.totalCount,
filter: data?.products.totalCount
}}
selectedProducts={listElements.length}
warehouses={
warehouses.data?.warehouses.edges.map(
edge => edge.node
) || []
}
onClose={closeModal}
onSubmit={data =>
exportProducts({
variables: {
input: {
...data,
filter,
ids: listElements
}
}
})
}
/>
<SaveFilterTabDialog
open={params.action === "save-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleFilterTabSave}
/>
<DeleteFilterTabDialog
open={params.action === "delete-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleFilterTabDelete}
tabName={maybe(
() => tabs[currentTab - 1].name,
"..."
)}
/>
</>
)}
</TypedProductBulkPublishMutation>
)}
</TypedProductBulkDeleteMutation>
);
}}
</TypedProductListQuery>
)}
</AvailableInGridAttributesQuery>
); );
}; };
export default ProductList; export default ProductList;