diff --git a/.changeset/eager-snails-do.md b/.changeset/eager-snails-do.md new file mode 100644 index 00000000000..c5905a49c15 --- /dev/null +++ b/.changeset/eager-snails-do.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +Gift cards filters now have option to filder by gift card code explicitly. Previously this was available only in search box, which uses `search` query, this however might not be available immediately after creating a gift card, due to indexing running in the background. diff --git a/playwright/pages/giftCardsPage.ts b/playwright/pages/giftCardsPage.ts index a8198168f8f..29077180493 100644 --- a/playwright/pages/giftCardsPage.ts +++ b/playwright/pages/giftCardsPage.ts @@ -4,6 +4,7 @@ import { ExportGiftCardsDialog } from "@dialogs/exportGiftCardsDialog"; import { IssueGiftCardDialog } from "@dialogs/issueGiftCardDialog"; import { ResendGiftCardCodeDialog } from "@dialogs/resendGiftCardCodeDialog"; import { SetGiftCardsBalanceDialog } from "@dialogs/setGiftCardBalanceDialog"; +import { FiltersPage } from "@pageElements/filtersPage"; import { MetadataSeoPage } from "@pageElements/metadataSeoPage"; import { BasePage } from "@pages/basePage"; import { expect, type Page } from "@playwright/test"; @@ -23,6 +24,8 @@ export class GiftCardsPage extends BasePage { readonly setGiftCardsBalanceDialog: SetGiftCardsBalanceDialog; + readonly filtersPage!: FiltersPage; + constructor( page: Page, readonly issueCardButton = page.getByTestId("issue-card-button"), @@ -55,6 +58,7 @@ export class GiftCardsPage extends BasePage { this.deleteDialog = new DeleteDialog(page); this.exportGiftCardsDialog = new ExportGiftCardsDialog(page); this.setGiftCardsBalanceDialog = new SetGiftCardsBalanceDialog(page); + this.filtersPage = new FiltersPage(page); } async clickIssueCardButton() { diff --git a/playwright/pages/pageElements/filtersPage.ts b/playwright/pages/pageElements/filtersPage.ts index ee3d51d9946..8f74f81af71 100644 --- a/playwright/pages/pageElements/filtersPage.ts +++ b/playwright/pages/pageElements/filtersPage.ts @@ -38,4 +38,11 @@ export class FiltersPage extends BasePage { await this.clickRightInput(); await this.dropDownOptions.filter({ hasText: channelName }).click(); } + + async pickTextFilter(filterKind: string, value: string) { + await this.clickAddFilterButton(); + await this.clickLeftInput(); + await this.dropDownOptions.filter({ hasText: filterKind }).click(); + await this.rightInput.fill(value); + } } diff --git a/playwright/tests/giftCards.spec.ts b/playwright/tests/giftCards.spec.ts index 89be4f97522..58874b88579 100644 --- a/playwright/tests/giftCards.spec.ts +++ b/playwright/tests/giftCards.spec.ts @@ -51,7 +51,9 @@ test("TC: SALEOR_106 Issue gift card with specific customer and expiry date #e2e await giftCardsPage.issueGiftCardDialog.clickOkButton(); await giftCardsPage.giftCardDialog.waitFor({ state: "hidden" }); await giftCardsPage.gotoGiftCardsListView(); - await giftCardsPage.searchAndFindRowIndexes(fullCode); + await giftCardsPage.clickFilterButton(); + await giftCardsPage.filtersPage.pickTextFilter("Code", fullCode); + await giftCardsPage.filtersPage.clickSaveFiltersButton(); expect( await giftCardsPage.gridCanvas.locator("table tbody tr").count(), "There should be only one gift card visible on list", diff --git a/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.test.ts b/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.test.ts index 6f16ac21f99..e9a22ee28ac 100644 --- a/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.test.ts +++ b/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.test.ts @@ -159,6 +159,34 @@ describe("ConditionalFilter / API / Page / InitialGiftCardsState", () => { expect(result).toEqual(expectedOutput); }); + it("should filter by code", () => { + // Arrange + const initialPageState = InitialGiftCardsStateResponse.empty(); + + initialPageState.code = [ + { + label: "06FA-658A-EDB0", + value: "06FA-658A-EDB0", + slug: "06FA-658A-EDB0", + }, + ]; + + const token = UrlToken.fromUrlEntry(new UrlEntry("s0.code", "06FA-658A-EDB0")); + const expectedOutput = [ + { + label: "06FA-658A-EDB0", + value: "06FA-658A-EDB0", + slug: "06FA-658A-EDB0", + }, + ]; + + // Act + const result = initialPageState.filterByUrlToken(token); + + // Assert + expect(result).toEqual(expectedOutput); + }); + it("should filter by is active", () => { // Arrange const initialPageState = InitialGiftCardsStateResponse.empty(); diff --git a/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.ts b/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.ts index 824c9fea1f5..b061c2a5df2 100644 --- a/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.ts +++ b/src/components/ConditionalFilter/API/initialState/giftCards/InitialGiftCardsState.ts @@ -7,6 +7,7 @@ export interface InitialGiftCardsState { isActive: ItemOption[]; tags: ItemOption[]; usedBy: ItemOption[]; + code: ItemOption[]; } export class InitialGiftCardsStateResponse implements InitialGiftCardsState { @@ -16,6 +17,7 @@ export class InitialGiftCardsStateResponse implements InitialGiftCardsState { public isActive: ItemOption[] = [], public tags: ItemOption[] = [], public usedBy: ItemOption[] = [], + public code: ItemOption[] = [], ) {} public static empty() { @@ -44,6 +46,8 @@ export class InitialGiftCardsStateResponse implements InitialGiftCardsState { return this.tags; case "usedBy": return this.usedBy; + case "code": + return this.code; default: return []; } diff --git a/src/components/ConditionalFilter/API/initialState/giftCards/useInitialGiftCardsState.ts b/src/components/ConditionalFilter/API/initialState/giftCards/useInitialGiftCardsState.ts index 30d3fa9eaf8..0d5a9634d91 100644 --- a/src/components/ConditionalFilter/API/initialState/giftCards/useInitialGiftCardsState.ts +++ b/src/components/ConditionalFilter/API/initialState/giftCards/useInitialGiftCardsState.ts @@ -31,7 +31,13 @@ export const useInitialGiftCardsState = () => { const [loading, setLoading] = useState(true); const queriesToRun: Array> = []; - const fetchQueries = async ({ usedBy, products, currency, tags }: GiftCardsFetchingParams) => { + const fetchQueries = async ({ + usedBy, + products, + currency, + tags, + code, + }: GiftCardsFetchingParams) => { if (products.length > 0) { queriesToRun.push( client.query<_SearchProductOperandsQuery, _SearchProductOperandsQueryVariables>({ @@ -66,7 +72,7 @@ export const useInitialGiftCardsState = () => { } const data = await Promise.all(queriesToRun); - const initialState = createInitialGiftCardsState(data, tags); + const initialState = createInitialGiftCardsState(data, tags, code); setData( new InitialGiftCardsStateResponse( @@ -75,6 +81,7 @@ export const useInitialGiftCardsState = () => { initialState.isActive, initialState.tags, initialState.usedBy, + initialState.code, ), ); setLoading(false); diff --git a/src/components/ConditionalFilter/API/initialState/helpers.ts b/src/components/ConditionalFilter/API/initialState/helpers.ts index 6f2246fcb22..91caf14d8c4 100644 --- a/src/components/ConditionalFilter/API/initialState/helpers.ts +++ b/src/components/ConditionalFilter/API/initialState/helpers.ts @@ -300,6 +300,7 @@ export const createInitialPageState = (data: InitialPageAPIResponse[]) => export const createInitialGiftCardsState = ( data: InitialGiftCardsAPIResponse[], tags: string[], + code: string[], ): InitialGiftCardsState => { return data.reduce( (acc, query) => { @@ -336,6 +337,7 @@ export const createInitialGiftCardsState = ( products: [], tags: tags?.map(tag => ({ label: tag, value: tag, slug: tag })) ?? [], usedBy: [], + code: code?.map(c => ({ label: c, value: c, slug: c })) ?? [], } as InitialGiftCardsState, ); }; diff --git a/src/components/ConditionalFilter/API/providers/GiftCardsFilterAPIProvider.tsx b/src/components/ConditionalFilter/API/providers/GiftCardsFilterAPIProvider.tsx index 773d7eddaa5..57216fb495e 100644 --- a/src/components/ConditionalFilter/API/providers/GiftCardsFilterAPIProvider.tsx +++ b/src/components/ConditionalFilter/API/providers/GiftCardsFilterAPIProvider.tsx @@ -8,6 +8,7 @@ import { CustomerHandler, GiftCardTagsHandler, type Handler, + NoopValuesHandler, ProductsHandler, } from "../Handler"; import { getFilterElement } from "../utils"; @@ -35,6 +36,10 @@ const createAPIHandler = ( return new CustomerHandler(client, inputValue); } + if (rowType === "code") { + return new NoopValuesHandler([]); + } + if (rowType === "isActive") { return new BooleanValuesHandler([ { diff --git a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.test.ts b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.test.ts index 3bf793367d7..8c5d06a2bae 100644 --- a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.test.ts +++ b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.test.ts @@ -107,6 +107,7 @@ describe("TokenArray / fetchingParams / getEmptyFetchingPrams", () => { products: [], tags: [], usedBy: [], + code: [], }); }); @@ -185,6 +186,7 @@ describe("TokenArray / fetchingParams / toGiftCardsFetchingParams", () => { products: [], tags: [], usedBy: [], + code: [], }; const token = { @@ -203,6 +205,7 @@ describe("TokenArray / fetchingParams / toGiftCardsFetchingParams", () => { products: ["product-1", "product-2", "product-3"], tags: [], usedBy: [], + code: [], }); }); }); diff --git a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts index f66143a116e..7bac36cfb67 100644 --- a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts +++ b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts @@ -51,6 +51,7 @@ export interface GiftCardsFetchingParams { products: string[]; tags: string[]; usedBy: string[]; + code: string[]; } export interface CollectionFetchingParams { @@ -137,6 +138,7 @@ const emptyGiftCardsFetchingParams: GiftCardsFetchingParams = { products: [], tags: [], usedBy: [], + code: [], }; const emptyCollectionFetchingParams: CollectionFetchingParams = { diff --git a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts index ad33e88ca39..f5561d8364c 100644 --- a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts +++ b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts @@ -52,7 +52,7 @@ const VOUCHER_STATICS = ["channel", "discountType", "voucherStatus"]; const PAGE_STATIC = ["pageTypes"]; -const GIFT_CARDS_STATICS = ["currency", "products", "isActive", "tags", "usedBy"]; +const GIFT_CARDS_STATICS = ["currency", "products", "isActive", "tags", "usedBy", "code"]; const COLLECTION_STATICS = ["channel", "published"]; diff --git a/src/components/ConditionalFilter/constants.ts b/src/components/ConditionalFilter/constants.ts index 2eb5d50c059..fbc55ace40e 100644 --- a/src/components/ConditionalFilter/constants.ts +++ b/src/components/ConditionalFilter/constants.ts @@ -63,6 +63,7 @@ export const STATIC_CONDITIONS = { isPreorder: [{ type: "select", label: "is", value: "input-1" }], isGiftCardUsed: [{ type: "select", label: "is", value: "input-1" }], isGiftCardBought: [{ type: "select", label: "is", value: "input-1" }], + code: [{ type: "text", label: "is", value: "input-1" }], status: [ { type: "combobox", @@ -718,6 +719,13 @@ export const STATIC_GIFT_CARDS_OPTIONS: LeftOperand[] = [ type: "usedBy", slug: "usedBy", }, + { + value: "code", + label: "Code", + type: "code", + slug: "code", + maxOccurrences: 1, + }, ]; export const STATIC_CUSTOMER_OPTIONS: LeftOperand[] = [