Skip to content

Commit

Permalink
Introduce new filters for product types (#5411)
Browse files Browse the repository at this point in the history
* Migrate from tabs to filter presets

* Add changeset

* Update test id

* Fix import in providers and extract getFilterElement with test

* Build flags

* Add new conditional filters

* Add tests

* Add changeset

* Fix type
  • Loading branch information
poulch authored and witoszekdev committed Feb 18, 2025
1 parent a04abc7 commit 2ab3653
Show file tree
Hide file tree
Showing 34 changed files with 664 additions and 115 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-penguins-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

Product types list page uses now new filters. New filters are under feature flag and are enabled by default.
46 changes: 30 additions & 16 deletions .featureFlags/generated.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
// @ts-nocheck

import W24585 from "./images/collection-filters.jpg"
import L16463 from "./images/customers-filters.png"
import L94878 from "./images/discounts-list.png"
import E09437 from "./images/draft-orders-filters.png"
import E78766 from "./images/gift-cards-filters.png"
import X68306 from "./images/improved_refunds.png"
import E85468 from "./images/page-filters.png"
import B37301 from "./images/vouchers-filters.png"
import B52941 from "./images/collection-filters.jpg"
import N65883 from "./images/customers-filters.png"
import M49541 from "./images/discounts-list.png"
import J34031 from "./images/draft-orders-filters.png"
import A69414 from "./images/gift-cards-filters.png"
import Y18806 from "./images/improved_refunds.png"
import V51684 from "./images/page-filters.png"
import D27626 from "./images/product-types-filters.png"
import M01387 from "./images/vouchers-filters.png"

const collection_filters = () => (<><p><img src={W24585} alt="new filters"/>
const collection_filters = () => (<><p><img src={B52941} alt="new filters"/>
Experience the new look and enhanced abilities of new filtering mechanism.
Easily combine any criteria you want, and quickly browse their values.</p>
</>)
const customers_filters = () => (<><p><img src={L16463} alt="new filters"/>
const customers_filters = () => (<><p><img src={N65883} alt="new filters"/>
Experience the new look and enhanced abilities of new fitering mechanism.
Easily combine any criteria you want, and quickly browse their values.</p>
</>)
const discounts_rules = () => (<><p><img src={L94878} alt="Discount rules"/></p>
const discounts_rules = () => (<><p><img src={M49541} alt="Discount rules"/></p>
<p>Apply the new discounts rules to narrow your promotions audience.
Set up conditions and channels that must be fulfilled to apply defined reward.</p>
</>)
const draft_orders_filters = () => (<><p><img src={E09437} alt="new filters"/>
const draft_orders_filters = () => (<><p><img src={J34031} alt="new filters"/>
Experience the new look and enhanced abilities of new fitering mechanism.
Easily combine any criteria you want, and quickly browse their values.</p>
</>)
const gift_cards_filters = () => (<><p><img src={E78766} alt="new filters"/>
const gift_cards_filters = () => (<><p><img src={A69414} alt="new filters"/>
Experience the new look and enhanced abilities of new fitering mechanism.
Easily combine any criteria you want, and quickly browse their values.</p>
</>)
const improved_refunds = () => (<><p><img src={X68306} alt="Improved refunds"/></p>
const improved_refunds = () => (<><p><img src={Y18806} alt="Improved refunds"/></p>
<h3 id="enable-the-enhanced-refund-feature-to-streamline-your-refund-process">Enable the enhanced refund feature to streamline your refund process:</h3>
<ul>
<li><p>• Choose between automatic calculations based on selected items or enter refund amounts directly for overcharges and custom adjustments.</p>
Expand All @@ -39,11 +40,15 @@ const improved_refunds = () => (<><p><img src={X68306} alt="Improved refunds"/><
</ul>

</>)
const pages_filters = () => (<><p><img src={E85468} alt="new filters"/>
const pages_filters = () => (<><p><img src={V51684} alt="new filters"/>
Experience the new look and enhanced abilities of new fitering mechanism.
Easily combine any criteria you want, and quickly browse their values.</p>
</>)
const vouchers_filters = () => (<><p><img src={B37301} alt="new filters"/>
const product_types_filters = () => (<><p><img src={D27626} alt="new filters"/>
Experience the new look and enhanced abilities of new fitering mechanism.
Easily combine any criteria you want, and quickly browse their values.</p>
</>)
const vouchers_filters = () => (<><p><img src={M01387} alt="new filters"/>
Experience the new look and enhanced abilities of new fitering mechanism.
Easily combine any criteria you want, and quickly browse their values.</p>
</>)
Expand Down Expand Up @@ -111,6 +116,15 @@ export const AVAILABLE_FLAGS = [{
enabled: true,
payload: "default",
}
},{
name: "product_types_filters",
displayName: "Product types filtering",
component: product_types_filters,
visible: true,
content: {
enabled: true,
payload: "default",
}
},{
name: "vouchers_filters",
displayName: "Vouchers filtering",
Expand Down
Binary file added .featureFlags/images/product-types-filters.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions .featureFlags/product_types_filters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
name: product_types_filters
displayName: Product types filtering
enabled: true
payload: "default"
visible: true
---

![new filters](./images/product-types-filters.png)
Experience the new look and enhanced abilities of new fitering mechanism.
Easily combine any criteria you want, and quickly browse their values.
8 changes: 8 additions & 0 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3742,6 +3742,10 @@
"M59JhX": {
"string": "Manual"
},
"M61TN/": {
"context": "product type shippable",
"string": "Shippable"
},
"M6mWHL": {
"context": "tooltip helper text",
"string": "You do not have permissions to create a manual refund."
Expand Down Expand Up @@ -4781,6 +4785,10 @@
"SceSNp": {
"string": "Remove following permissions:"
},
"SgFE10": {
"context": "product type digital",
"string": "Digital"
},
"Sjd7wm": {
"context": "product filter label",
"string": "Product"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { UrlEntry, UrlToken } from "../../../ValueProvider/UrlToken";
import { InitialProductTypesStateResponse } from "./InitialProductTypesState";

describe("ConditionalFilter / API / Page / InitialProductTypesState", () => {
it("should filter by product type", () => {
// Arrange
const initialPageState = InitialProductTypesStateResponse.empty();

initialPageState.typeOfProduct = [
{
label: "Type 1",
value: "type-1",
slug: "type-1",
},
{
label: "Type 2",
value: "type-2",
slug: "type-2",
},
];

const token = UrlToken.fromUrlEntry(new UrlEntry("s0.typeOfProduct", "type-2"));
const expectedOutput = [
{
label: "Type 2",
value: "type-2",
slug: "type-2",
},
];

// Act
const result = initialPageState.filterByUrlToken(token);

// Assert
expect(result).toEqual(expectedOutput);
});

it("should filter by configurable", () => {
// Arrange
const initialPageState = InitialProductTypesStateResponse.empty();

initialPageState.configurable = [
{
label: "Yes",
value: "CONFIGURABLE",
slug: "yes",
},
{
label: "No",
value: "SIMPLE",
slug: "no",
},
];

const token = UrlToken.fromUrlEntry(new UrlEntry("s0.configurable", "no"));
const expectedOutput = [
{
label: "No",
value: "SIMPLE",
slug: "no",
},
];

// Act
const result = initialPageState.filterByUrlToken(token);

// Assert
expect(result).toEqual(expectedOutput);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ItemOption } from "@dashboard/components/ConditionalFilter/FilterElement/ConditionValue";
import { UrlToken } from "@dashboard/components/ConditionalFilter/ValueProvider/UrlToken";

export interface InitialProductTypesState {
typeOfProduct: ItemOption[];
configurable: ItemOption[];
}

export class InitialProductTypesStateResponse implements InitialProductTypesState {
constructor(
public typeOfProduct: ItemOption[] = [],
public configurable: ItemOption[] = [],
) {}

public static empty() {
return new InitialProductTypesStateResponse();
}

public filterByUrlToken(token: UrlToken) {
const entry = this.getEntryByName(token.name);

if (!token.isLoadable()) {
return [token.value] as string[];
}

return (entry as ItemOption[]).filter(({ slug }) => slug && token.value.includes(slug));
}

private getEntryByName(name: string): ItemOption[] {
switch (name) {
case "typeOfProduct":
return this.typeOfProduct;
case "configurable":
return this.configurable;
default:
return [];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { InitialProductTypesStateResponse } from "@dashboard/components/ConditionalFilter/API/initialState/productTypes/InitialProductTypesState";
import { ProductTypeConfigurable, ProductTypeEnum } from "@dashboard/graphql";
import { useState } from "react";
import { useIntl } from "react-intl";

import { ProductTypesFetchingParams } from "../../../ValueProvider/TokenArray/fetchingParams";
import { BooleanValuesHandler, EnumValuesHandler } from "../../Handler";

export interface InitialProductTypesAPIState {
data: InitialProductTypesStateResponse;
loading: boolean;
fetchQueries: (params: ProductTypesFetchingParams) => Promise<void>;
}

export const useInitialProductTypesState = (): InitialProductTypesAPIState => {
const intl = useIntl();
const [data, setData] = useState<InitialProductTypesStateResponse>(
InitialProductTypesStateResponse.empty(),
);
const [loading, setLoading] = useState(true);

const fetchQueries = async ({ typeOfProduct }: ProductTypesFetchingParams) => {
const typeOfProductInit = new EnumValuesHandler(
ProductTypeEnum,
"typeOfProduct",
intl,
typeOfProduct,
);

const configurableInit = new BooleanValuesHandler([
{
label: "Yes",
value: ProductTypeConfigurable.CONFIGURABLE,
type: "configurable",
slug: "true",
},
{
label: "No",
value: ProductTypeConfigurable.SIMPLE,
type: "configurable",
slug: "false",
},
]);

const initialState = {
typeOfProduct: await typeOfProductInit.fetch(),
configurable: await configurableInit.fetch(),
};

setData(
new InitialProductTypesStateResponse(initialState.typeOfProduct, initialState.configurable),
);
setLoading(false);
};

return {
data,
loading,
fetchQueries,
};
};
15 changes: 15 additions & 0 deletions src/components/ConditionalFilter/API/intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
OrderChargeStatusEnum,
OrderStatusFilter,
PaymentChargeStatusEnum,
ProductTypeEnum,
VoucherDiscountType,
} from "@dashboard/graphql";
import { transformOrderStatus, transformPaymentStatus } from "@dashboard/misc";
Expand All @@ -16,6 +17,7 @@ import {
chargeStatusMessages,
collectionFilterMessages,
discountTypeMessages,
productTypeMessages,
voucherStatusMessages,
} from "./messages";

Expand Down Expand Up @@ -94,6 +96,17 @@ const getPublishedLabel = (status: CollectionPublished, intl: IntlShape) => {
}
};

export const getProductTypeLabel = (type: ProductTypeEnum, intl: IntlShape) => {
switch (type) {
case ProductTypeEnum.DIGITAL:
return intl.formatMessage(productTypeMessages.digital);
case ProductTypeEnum.SHIPPABLE:
return intl.formatMessage(productTypeMessages.shippable);
default:
return type;
}
};

export const getLocalizedLabel = (rowType: LeftOperand["type"], value: string, intl: IntlShape) => {
switch (rowType) {
case "paymentStatus":
Expand All @@ -110,6 +123,8 @@ export const getLocalizedLabel = (rowType: LeftOperand["type"], value: string, i
return getDiscountTypeLabel(value as VoucherDiscountType, intl);
case "voucherStatus":
return getVoucherStatusLabel(value as DiscountStatusEnum, intl);
case "typeOfProduct":
return getProductTypeLabel(value as ProductTypeEnum, intl);
default:
return value;
}
Expand Down
13 changes: 13 additions & 0 deletions src/components/ConditionalFilter/API/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,16 @@ export const collectionFilterMessages = defineMessages({
id: "ThUvIL",
},
});

export const productTypeMessages = defineMessages({
digital: {
defaultMessage: "Digital",
description: "product type digital",
id: "SgFE10",
},
shippable: {
defaultMessage: "Shippable",
description: "product type shippable",
id: "M61TN/",
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,8 @@ import { IntlShape, useIntl } from "react-intl";
import { FilterContainer, FilterElement } from "../../FilterElement";
import { FilterAPIProvider } from "../FilterAPIProvider";
import { ChannelHandler, EnumValuesHandler, Handler, NoopValuesHandler } from "../Handler";
import { getFilterElement } from "../utils";

const getFilterElement = (value: FilterContainer, index: number): FilterElement => {
const possibleFilterElement = value[index];

if (typeof possibleFilterElement !== "string" && !Array.isArray(possibleFilterElement)) {
return possibleFilterElement;
}

throw new Error("Unknown filter element used to create API handler");
};
const createAPIHandler = (
selectedRow: FilterElement,
client: ApolloClient<unknown>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FilterAPIProvider } from "@dashboard/components/ConditionalFilter/API/FilterAPIProvider";
import { FilterAPIProvider } from "../FilterAPIProvider";

export const useDiscountFilterAPIProvider = (): FilterAPIProvider => {
const fetchRightOptions = async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FilterAPIProvider } from "@dashboard/components/ConditionalFilter/API/FilterAPIProvider";
import { FilterAPIProvider } from "../FilterAPIProvider";

export const useDraftOrderFilterAPIProvider = (): FilterAPIProvider => {
const fetchRightOptions = async () => {
Expand Down
Loading

0 comments on commit 2ab3653

Please sign in to comment.