Add sorting products by update at date (#1581)

* Add sorting products by update at date

* Update messages

* Change columns order in product list view

* Display updated date column by default
This commit is contained in:
Dawid Tarasiuk 2021-11-17 13:49:22 +01:00 committed by GitHub
parent c3cc2b03de
commit b50225aaa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 113 additions and 44 deletions

View file

@ -6013,18 +6013,10 @@
"context": "modal button", "context": "modal button",
"string": "Upload URL" "string": "Upload URL"
}, },
"src_dot_products_dot_components_dot_ProductListPage_dot_1134347598": {
"context": "product price",
"string": "Price"
},
"src_dot_products_dot_components_dot_ProductListPage_dot_1542417144": { "src_dot_products_dot_components_dot_ProductListPage_dot_1542417144": {
"context": "button", "context": "button",
"string": "Create Product" "string": "Create Product"
}, },
"src_dot_products_dot_components_dot_ProductListPage_dot_1952810469": {
"context": "product type",
"string": "Type"
},
"src_dot_products_dot_components_dot_ProductListPage_dot_2059406063": { "src_dot_products_dot_components_dot_ProductListPage_dot_2059406063": {
"context": "export products to csv file, button", "context": "export products to csv file, button",
"string": "Export Products" "string": "Export Products"
@ -6078,10 +6070,6 @@
"context": "product is visible", "context": "product is visible",
"string": "Visible" "string": "Visible"
}, },
"src_dot_products_dot_components_dot_ProductList_dot_1134347598": {
"context": "product price",
"string": "Price"
},
"src_dot_products_dot_components_dot_ProductList_dot_150865454": { "src_dot_products_dot_components_dot_ProductList_dot_150865454": {
"context": "product type", "context": "product type",
"string": "Simple" "string": "Simple"
@ -6089,30 +6077,38 @@
"src_dot_products_dot_components_dot_ProductList_dot_1657559629": { "src_dot_products_dot_components_dot_ProductList_dot_1657559629": {
"string": "No products found" "string": "No products found"
}, },
"src_dot_products_dot_components_dot_ProductList_dot_1952810469": {
"context": "product type",
"string": "Type"
},
"src_dot_products_dot_components_dot_ProductList_dot_2754779425": { "src_dot_products_dot_components_dot_ProductList_dot_2754779425": {
"context": "product type", "context": "product type",
"string": "Configurable" "string": "Configurable"
}, },
"src_dot_products_dot_components_dot_ProductList_dot_3326160357": {
"context": "product channels",
"string": "Availability"
},
"src_dot_products_dot_components_dot_ProductList_dot_636461959": { "src_dot_products_dot_components_dot_ProductList_dot_636461959": {
"context": "product", "context": "product",
"string": "Name" "string": "Name"
}, },
"src_dot_products_dot_components_dot_ProductList_dot_availability": {
"context": "product channels",
"string": "Availability"
},
"src_dot_products_dot_components_dot_ProductList_dot_price": {
"context": "product price",
"string": "Price"
},
"src_dot_products_dot_components_dot_ProductList_dot_published": { "src_dot_products_dot_components_dot_ProductList_dot_published": {
"context": "product publication date", "context": "product publication date",
"string": "Published on {date}" "string": "Published on {date}"
}, },
"src_dot_products_dot_components_dot_ProductList_dot_type": {
"context": "product type",
"string": "Type"
},
"src_dot_products_dot_components_dot_ProductList_dot_unpublished": { "src_dot_products_dot_components_dot_ProductList_dot_unpublished": {
"context": "product publication date", "context": "product publication date",
"string": "Unpublished" "string": "Unpublished"
}, },
"src_dot_products_dot_components_dot_ProductList_dot_updatedAt": {
"context": "product updated at",
"string": "Last updated"
},
"src_dot_products_dot_components_dot_ProductList_dot_willBePublished": { "src_dot_products_dot_components_dot_ProductList_dot_willBePublished": {
"context": "product publication date", "context": "product publication date",
"string": "Becomes published on {date}" "string": "Becomes published on {date}"

View file

@ -22,7 +22,11 @@ export const DEFAULT_INITIAL_PAGINATION_DATA: Pagination = {
export const PAGINATE_BY = 20; export const PAGINATE_BY = 20;
export const VALUES_PAGINATE_BY = 10; export const VALUES_PAGINATE_BY = 10;
export type ProductListColumns = "productType" | "availability" | "price"; export type ProductListColumns =
| "productType"
| "availability"
| "price"
| "date";
export interface AppListViewSettings { export interface AppListViewSettings {
[ListViews.APPS_LIST]: ListSettings; [ListViews.APPS_LIST]: ListSettings;
@ -79,7 +83,7 @@ export const defaultListSettings: AppListViewSettings = {
rowNumber: PAGINATE_BY rowNumber: PAGINATE_BY
}, },
[ListViews.PRODUCT_LIST]: { [ListViews.PRODUCT_LIST]: {
columns: ["availability", "price", "productType"], columns: ["availability", "price", "productType", "date"],
rowNumber: PAGINATE_BY rowNumber: PAGINATE_BY
}, },
[ListViews.SALES_LIST]: { [ListViews.SALES_LIST]: {

View file

@ -13,6 +13,7 @@ export interface ProductAttributeAssignmentUpdate_productAttributeAssignmentUpda
__typename: "ProductError"; __typename: "ProductError";
field: string | null; field: string | null;
message: string | null; message: string | null;
attributes: string[] | null;
} }
export interface ProductAttributeAssignmentUpdate_productAttributeAssignmentUpdate_productType_taxType { export interface ProductAttributeAssignmentUpdate_productAttributeAssignmentUpdate_productType_taxType {

View file

@ -8,6 +8,7 @@ import {
import AvailabilityStatusLabel from "@saleor/components/AvailabilityStatusLabel"; import AvailabilityStatusLabel from "@saleor/components/AvailabilityStatusLabel";
import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown"; import { ChannelsAvailabilityDropdown } from "@saleor/components/ChannelsAvailabilityDropdown";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
import Date from "@saleor/components/Date";
import MoneyRange from "@saleor/components/MoneyRange"; import MoneyRange from "@saleor/components/MoneyRange";
import ResponsiveTable from "@saleor/components/ResponsiveTable"; import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
@ -36,7 +37,7 @@ import classNames from "classnames";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { messages } from "./messages"; import { columnsMessages, messages } from "./messages";
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ theme => ({
@ -52,6 +53,9 @@ const useStyles = makeStyles(
}, },
colType: { colType: {
width: 200 width: 200
},
colDate: {
width: 200
} }
}, },
colAttribute: { colAttribute: {
@ -160,6 +164,9 @@ export const ProductList: React.FC<ProductListProps> = props => {
{gridAttributesFromSettings.map(gridAttribute => ( {gridAttributesFromSettings.map(gridAttribute => (
<col className={classes.colAttribute} key={gridAttribute} /> <col className={classes.colAttribute} key={gridAttribute} />
))} ))}
<DisplayColumn column="date" displayColumns={settings.columns}>
<col className={classes.colDate} />
</DisplayColumn>
<DisplayColumn column="price" displayColumns={settings.columns}> <DisplayColumn column="price" displayColumns={settings.columns}>
<col className={classes.colPrice} /> <col className={classes.colPrice} />
</DisplayColumn> </DisplayColumn>
@ -200,10 +207,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
} }
onClick={() => onSort(ProductListUrlSortField.productType)} onClick={() => onSort(ProductListUrlSortField.productType)}
> >
<FormattedMessage <FormattedMessage {...columnsMessages.type} />
defaultMessage="Type"
description="product type"
/>
</TableCellHeader> </TableCellHeader>
</DisplayColumn> </DisplayColumn>
<DisplayColumn <DisplayColumn
@ -226,10 +230,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
) )
} }
> >
<FormattedMessage <FormattedMessage {...columnsMessages.availability} />
defaultMessage="Availability"
description="product channels"
/>
</TableCellHeader> </TableCellHeader>
</DisplayColumn> </DisplayColumn>
{gridAttributesFromSettings.map(gridAttributeFromSettings => { {gridAttributesFromSettings.map(gridAttributeFromSettings => {
@ -261,6 +262,20 @@ export const ProductList: React.FC<ProductListProps> = props => {
</TableCellHeader> </TableCellHeader>
); );
})} })}
<DisplayColumn column="date" displayColumns={settings.columns}>
<TableCellHeader
data-test-id="colDateHeader"
className={classes.colDate}
direction={
sort.sort === ProductListUrlSortField.date
? getArrowDirection(sort.asc)
: undefined
}
onClick={() => onSort(ProductListUrlSortField.date)}
>
<FormattedMessage {...columnsMessages.updatedAt} />
</TableCellHeader>
</DisplayColumn>
<DisplayColumn column="price" displayColumns={settings.columns}> <DisplayColumn column="price" displayColumns={settings.columns}>
<TableCellHeader <TableCellHeader
data-test-id="colPriceHeader" data-test-id="colPriceHeader"
@ -276,10 +291,7 @@ export const ProductList: React.FC<ProductListProps> = props => {
!canBeSorted(ProductListUrlSortField.price, !!selectedChannelId) !canBeSorted(ProductListUrlSortField.price, !!selectedChannelId)
} }
> >
<FormattedMessage <FormattedMessage {...columnsMessages.price} />
defaultMessage="Price"
description="product price"
/>
</TableCellHeader> </TableCellHeader>
</DisplayColumn> </DisplayColumn>
</TableHead> </TableHead>
@ -413,6 +425,18 @@ export const ProductList: React.FC<ProductListProps> = props => {
}, <Skeleton />)} }, <Skeleton />)}
</TableCell> </TableCell>
))} ))}
<DisplayColumn
column="date"
displayColumns={settings.columns}
>
<TableCell className={classes.colDate} data-test="date">
{product?.updatedAt ? (
<Date date={product.updatedAt} />
) : (
<Skeleton />
)}
</TableCell>
</DisplayColumn>
<DisplayColumn <DisplayColumn
column="price" column="price"
displayColumns={settings.columns} displayColumns={settings.columns}

View file

@ -14,3 +14,22 @@ export const messages = defineMessages({
description: "product publication date" description: "product publication date"
} }
}); });
export const columnsMessages = defineMessages({
availability: {
defaultMessage: "Availability",
description: "product channels"
},
price: {
defaultMessage: "Price",
description: "product price"
},
type: {
defaultMessage: "Type",
description: "product type"
},
updatedAt: {
defaultMessage: "Last updated",
description: "product updated at"
}
});

View file

@ -34,6 +34,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import { ProductListUrlSortField } from "../../urls"; import { ProductListUrlSortField } from "../../urls";
import ProductList from "../ProductList"; import ProductList from "../ProductList";
import { columnsMessages } from "../ProductList/messages";
import { import {
createFilterStructure, createFilterStructure,
ProductFilterKeys, ProductFilterKeys,
@ -115,19 +116,17 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
const columns: ColumnPickerChoice[] = [ const columns: ColumnPickerChoice[] = [
{ {
label: intl.formatMessage({ label: intl.formatMessage(columnsMessages.price),
defaultMessage: "Price",
description: "product price"
}),
value: "price" as ProductListColumns value: "price" as ProductListColumns
}, },
{ {
label: intl.formatMessage({ label: intl.formatMessage(columnsMessages.type),
defaultMessage: "Type",
description: "product type"
}),
value: "productType" as ProductListColumns value: "productType" as ProductListColumns
}, },
{
label: intl.formatMessage(columnsMessages.updatedAt),
value: "date" as ProductListColumns
},
...availableInGridAttributes.map(attribute => ({ ...availableInGridAttributes.map(attribute => ({
label: attribute.name, label: attribute.name,
value: `attribute:${attribute.id}` value: `attribute:${attribute.id}`

View file

@ -740,6 +740,7 @@ export const products = (
): ProductList_products_edges_node[] => [ ): ProductList_products_edges_node[] => [
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [], attributes: [],
channelListings: [ channelListings: [
{ {
@ -830,6 +831,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [], attributes: [],
channelListings: [ channelListings: [
{ {
@ -920,6 +922,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [], attributes: [],
channelListings: [ channelListings: [
{ {
@ -1010,6 +1013,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1123,6 +1127,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1236,6 +1241,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1350,6 +1356,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1463,6 +1470,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1576,6 +1584,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1689,6 +1698,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1802,6 +1812,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -1915,6 +1926,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2028,6 +2040,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2141,6 +2154,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2254,6 +2268,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2367,6 +2382,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2480,6 +2496,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2593,6 +2610,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2706,6 +2724,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",
@ -2819,6 +2838,7 @@ export const products = (
}, },
{ {
__typename: "Product", __typename: "Product",
updatedAt: "2020-06-22T13:52:05.094636+00:00",
attributes: [ attributes: [
{ {
__typename: "SelectedAttribute", __typename: "SelectedAttribute",

View file

@ -158,6 +158,7 @@ const productListQuery = gql`
edges { edges {
node { node {
...ProductFragment ...ProductFragment
updatedAt
attributes { attributes {
attribute { attribute {
id id

View file

@ -110,6 +110,7 @@ export interface ProductList_products_edges_node {
thumbnail: ProductList_products_edges_node_thumbnail | null; thumbnail: ProductList_products_edges_node_thumbnail | null;
productType: ProductList_products_edges_node_productType; productType: ProductList_products_edges_node_productType;
channelListings: ProductList_products_edges_node_channelListings[] | null; channelListings: ProductList_products_edges_node_channelListings[] | null;
updatedAt: any | null;
attributes: ProductList_products_edges_node_attributes[]; attributes: ProductList_products_edges_node_attributes[];
} }

View file

@ -50,7 +50,8 @@ export enum ProductListUrlSortField {
productType = "productType", productType = "productType",
status = "status", status = "status",
price = "price", price = "price",
rank = "rank" rank = "rank",
date = "date"
} }
export type ProductListUrlSort = Sort<ProductListUrlSortField>; export type ProductListUrlSort = Sort<ProductListUrlSortField>;
export interface ProductListUrlQueryParams export interface ProductListUrlQueryParams

View file

@ -16,6 +16,7 @@ export function canBeSorted(
case ProductListUrlSortField.productType: case ProductListUrlSortField.productType:
case ProductListUrlSortField.attribute: case ProductListUrlSortField.attribute:
case ProductListUrlSortField.rank: case ProductListUrlSortField.rank:
case ProductListUrlSortField.date:
return true; return true;
case ProductListUrlSortField.price: case ProductListUrlSortField.price:
case ProductListUrlSortField.status: case ProductListUrlSortField.status:
@ -39,6 +40,8 @@ export function getSortQueryField(
return ProductOrderField.PUBLISHED; return ProductOrderField.PUBLISHED;
case ProductListUrlSortField.rank: case ProductListUrlSortField.rank:
return ProductOrderField.RANK; return ProductOrderField.RANK;
case ProductListUrlSortField.date:
return ProductOrderField.DATE;
default: default:
return undefined; return undefined;
} }