From ce1854b2ca4047a1b674bbbad2b6cbea11ead85d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chy=C5=82a?= Date: Wed, 19 Jul 2023 15:31:33 +0200 Subject: [PATCH] Introduce datagrid in grift cards list (#3894) Co-authored-by: wojteknowacki <124166231+wojteknowacki@users.noreply.github.com> --- .changeset/wise-windows-cheat.md | 5 + .../catalog/giftCards/activateGiftCards.js | 69 ++++-- .../e2e/catalog/giftCards/exportGiftCards.js | 113 +++++----- .../catalog/giftCards/updatingGiftCards.js | 64 +++--- .../catalog/giftCard/giftCardShowMore.js | 1 + cypress/elements/shared/button-selectors.js | 1 + cypress/elements/shared/sharedElements.js | 1 + cypress/fixtures/messages.js | 1 + cypress/support/api/requests/GiftCard.js | 15 ++ cypress/support/api/utils/users.js | 2 +- cypress/support/pages/catalog/giftCardPage.js | 58 ++++- cypress/support/pages/index.js | 1 + locale/defaultMessages.json | 18 +- src/config.ts | 1 + .../GiftCardExportDialogContent.tsx | 17 +- .../GiftCardEnableDisableSection.tsx | 2 +- src/giftCards/GiftCardUpdate/mutations.ts | 22 ++ .../GiftCardListBulkActions.tsx} | 59 +++-- .../GiftCardListBulkActions/index.ts | 1 + .../GiftCardsList/GiftCardListPage.tsx | 8 +- .../GiftCardListSearchAndFilters.tsx | 114 ++++------ .../GiftCardListSearchAndFilters/messages.ts | 4 +- .../GiftCardsListDatagrid.tsx | 213 ++++++++++++++++++ .../GiftCardsListDatagrid/datagrid.ts | 187 +++++++++++++++ .../GiftCardsListDatagrid/index.ts | 1 + .../GiftCardsListDatagrid/messages.ts | 50 ++++ .../GiftCardsListHeader.tsx | 121 +++++++--- .../GiftCardsListHeaderAlert.tsx | 7 +- .../GiftCardsListHeaderAlertContent.tsx | 12 +- .../GiftCardsListHeader/styles.ts | 16 -- .../GiftCardsListOrderInfoCard.tsx | 6 +- .../GiftCardsListTable/GiftCardsListTable.tsx | 186 --------------- .../GiftCardsListTableFooter.tsx | 40 ---- .../GiftCardsListTableHeader.tsx | 141 ------------ .../GiftCardsListTableHeader/index.tsx | 2 - .../GiftCardsListTableHeader/messages.ts | 43 ---- .../GiftCardsListTableHeader/mutations.ts | 23 -- .../GiftCardsListTable/index.tsx | 2 - .../GiftCardsList/GiftCardsListTable/utils.ts | 10 - .../filters.ts | 5 +- src/giftCards/GiftCardsList/messages.ts | 42 ++++ .../GiftCardListDialogsProvider.tsx | 22 +- .../GiftCardListProvider.tsx | 96 ++++++-- src/giftCards/GiftCardsList/styles.ts | 58 ----- src/giftCards/GiftCardsList/types.ts | 57 ++++- .../CustomerGiftCardsCardListItem.tsx | 6 +- .../GiftCardDeleteDialogContent.tsx | 18 +- .../GiftCardListPageDeleteDialog.tsx | 23 +- .../useGiftCardBulkDelete.tsx | 15 +- src/graphql/hooks.generated.ts | 142 ++++++------ src/graphql/types.generated.ts | 24 +- .../useFilterPresets/useFilterPresets.ts | 16 +- src/hooks/useListSettings.ts | 2 +- src/hooks/useRowSelection.ts | 11 +- src/index.css | 4 + src/types.ts | 2 +- 56 files changed, 1222 insertions(+), 958 deletions(-) create mode 100644 .changeset/wise-windows-cheat.md rename src/giftCards/GiftCardsList/{GiftCardsListTable/GiftCardsListTableHeader/BulkEnableDisableSection.tsx => GiftCardListBulkActions/GiftCardListBulkActions.tsx} (66%) create mode 100644 src/giftCards/GiftCardsList/GiftCardListBulkActions/index.ts create mode 100644 src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx create mode 100644 src/giftCards/GiftCardsList/GiftCardsListDatagrid/datagrid.ts create mode 100644 src/giftCards/GiftCardsList/GiftCardsListDatagrid/index.ts create mode 100644 src/giftCards/GiftCardsList/GiftCardsListDatagrid/messages.ts delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListHeader/styles.ts delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableFooter.tsx delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/GiftCardsListTableHeader.tsx delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/index.tsx delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/mutations.ts delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/index.tsx delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/utils.ts rename src/giftCards/GiftCardsList/{GiftCardListSearchAndFilters => }/filters.ts (98%) delete mode 100644 src/giftCards/GiftCardsList/styles.ts diff --git a/.changeset/wise-windows-cheat.md b/.changeset/wise-windows-cheat.md new file mode 100644 index 00000000000..0e95c8114f5 --- /dev/null +++ b/.changeset/wise-windows-cheat.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": minor +--- + +Introduce datagrid on gift card list page diff --git a/cypress/e2e/catalog/giftCards/activateGiftCards.js b/cypress/e2e/catalog/giftCards/activateGiftCards.js index cd664da9e03..85db415e60c 100644 --- a/cypress/e2e/catalog/giftCards/activateGiftCards.js +++ b/cypress/e2e/catalog/giftCards/activateGiftCards.js @@ -2,6 +2,7 @@ import faker from "faker"; import { GIFT_CARD_LIST } from "../../../elements/catalog/giftCard/giftCardList"; +import { urlList } from "../../../fixtures/urlList"; import { completeCheckout } from "../../../support/api/requests/Checkout"; import { createGiftCard, @@ -18,10 +19,7 @@ import { } from "../../../support/api/utils/ordersUtils"; import * as productsUtils from "../../../support/api/utils/products/productsUtils"; import { updateTaxConfigurationForChannel } from "../../../support/api/utils/taxesUtils"; -import { - changeGiftCardActiveStatus, - enterAndSelectGiftCards, -} from "../../../support/pages/catalog/giftCardPage"; +import { giftCardsPage } from "../../../support/pages"; describe("As a admin I want to use enabled gift card in checkout", () => { const startsWith = "ActivateGiftCards"; @@ -84,7 +82,7 @@ describe("As a admin I want to use enabled gift card in checkout", () => { giftCardDeactivate(giftCard.id); }) .then(() => { - changeGiftCardActiveStatus(giftCard.id); + giftCardsPage.changeGiftCardActiveStatus(giftCard.id); dataForCheckout.voucherCode = giftCard.code; purchaseProductWithPromoCode(dataForCheckout); }) @@ -114,7 +112,7 @@ describe("As a admin I want to use enabled gift card in checkout", () => { createGiftCard(giftCardData) .then(giftCardResp => { giftCard = giftCardResp; - changeGiftCardActiveStatus(giftCard.id); + giftCardsPage.changeGiftCardActiveStatus(giftCard.id); dataForCheckout.voucherCode = giftCard.code; createCheckoutWithDisabledGiftCard(dataForCheckout); }) @@ -138,34 +136,44 @@ describe("As a admin I want to use enabled gift card in checkout", () => { "should not be able to disable several gift cards on gift card list page and use it in checkout. TC: SALEOR_1013", { tags: ["@giftCard", "@allEnv", "@stable"] }, () => { - const firstGiftCardName = `${startsWith}${faker.datatype.number()}`; - const secondGiftCardName = `${startsWith}${faker.datatype.number()}`; + const firstGiftCardTag = faker.datatype.number(); + const secondGiftCardTag = faker.datatype.number(); const amount = 10; let firstGiftCard; + let firstGiftCardCode; let secondGiftCard; - + let secondGiftCardCode; + cy.addAliasToGraphRequest("GiftCardBulkDeactivate"); createGiftCard({ - tag: firstGiftCardName, + tag: firstGiftCardTag, amount, currency: "USD", }) .then(giftCard => { firstGiftCard = giftCard; + firstGiftCardCode = firstGiftCard.code; createGiftCard({ - tag: secondGiftCardName, + tag: secondGiftCardTag, amount, currency: "USD", }); }) .then(giftCard => { secondGiftCard = giftCard; + secondGiftCardCode = secondGiftCard.code; + cy.visit( + giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [ + firstGiftCardTag, + secondGiftCardTag, + ]), + ); - enterAndSelectGiftCards([firstGiftCard.id, secondGiftCard.id]); - cy.addAliasToGraphRequest("GiftCardBulkDeactivate") - .get(GIFT_CARD_LIST.deactivateGiftCardButton) - .click() - .waitForRequestAndCheckIfNoErrors("@GiftCardBulkDeactivate") - .confirmationMessageShouldAppear(); + giftCardsPage.selectGiftCardOnListView(secondGiftCardCode); + giftCardsPage.selectGiftCardOnListView(firstGiftCardCode); + giftCardsPage.clickDeactivateButton(); + cy.waitForRequestAndCheckIfNoErrors( + "@GiftCardBulkDeactivate", + ).confirmationMessageShouldAppear(); dataForCheckout.voucherCode = firstGiftCard.code; createCheckoutWithDisabledGiftCard(dataForCheckout); dataForCheckout.voucherCode = secondGiftCard.code; @@ -192,23 +200,27 @@ describe("As a admin I want to use enabled gift card in checkout", () => { "should be able to enable several gift cards on gift card list page and use it in checkout. TC: SALEOR_1012", { tags: ["@giftCard", "@allEnv"] }, () => { - const firstGiftCardName = `${startsWith}${faker.datatype.number()}`; - const secondGiftCardName = `${startsWith}${faker.datatype.number()}`; const amount = 10; const expectedOrderPrice = shippingPrice + productPrice - amount; + const firstGiftCardTag = faker.datatype.number(); + const secondGiftCardTag = faker.datatype.number(); let firstGiftCard; + let firstGiftCardCode; let secondGiftCard; - + let secondGiftCardCode; + cy.addAliasToGraphRequest("GiftCardBulkActivate"); createGiftCard({ - tag: firstGiftCardName, + tag: firstGiftCardTag, amount, currency: "USD", isActive: false, }) .then(giftCard => { firstGiftCard = giftCard; + firstGiftCardCode = giftCard.code; + createGiftCard({ - tag: secondGiftCardName, + tag: secondGiftCardTag, amount, currency: "USD", isActive: false, @@ -216,9 +228,16 @@ describe("As a admin I want to use enabled gift card in checkout", () => { }) .then(giftCard => { secondGiftCard = giftCard; - enterAndSelectGiftCards([firstGiftCard.id, secondGiftCard.id]); - cy.addAliasToGraphRequest("GiftCardBulkActivate") - .get(GIFT_CARD_LIST.activateGiftCardButton) + secondGiftCardCode = giftCard.code; + cy.visit( + giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [ + firstGiftCardTag, + secondGiftCardTag, + ]), + ); + giftCardsPage.selectGiftCardOnListView(secondGiftCardCode); + giftCardsPage.selectGiftCardOnListView(firstGiftCardCode); + cy.get(GIFT_CARD_LIST.activateGiftCardButton) .click() .waitForRequestAndCheckIfNoErrors("@GiftCardBulkActivate") .confirmationMessageShouldAppear(); diff --git a/cypress/e2e/catalog/giftCards/exportGiftCards.js b/cypress/e2e/catalog/giftCards/exportGiftCards.js index 7a757599728..3e3709549f4 100644 --- a/cypress/e2e/catalog/giftCards/exportGiftCards.js +++ b/cypress/e2e/catalog/giftCards/exportGiftCards.js @@ -3,10 +3,7 @@ import faker from "faker"; -import { GIFT_CARD_LIST } from "../../../elements/catalog/giftCard/giftCardList"; -import { GIFT_CARD_SHOW_MORE } from "../../../elements/catalog/giftCard/giftCardShowMore"; -import { ASSIGN_ELEMENTS_SELECTORS } from "../../../elements/shared/assign-elements-selectors.js"; -import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; +import { urlList } from "../../../fixtures/urlList"; import { TEST_ADMIN_USER } from "../../../fixtures/users"; import { createGiftCard } from "../../../support/api/requests/GiftCard"; import { @@ -14,7 +11,7 @@ import { updatePlugin, } from "../../../support/api/requests/Plugins"; import { getMailWithGiftCardExportWithAttachment } from "../../../support/api/utils/users"; -import { enterAndSelectGiftCards } from "../../../support/pages/catalog/giftCardPage"; +import { giftCardsPage } from "../../../support/pages"; describe("As an admin I want to export gift card", () => { const startsWith = "updateGCard"; @@ -32,11 +29,13 @@ describe("As an admin I want to export gift card", () => { "should be able to export several gift cards to csv file. TC: SALEOR_1010", { tags: ["@giftCard", "@allEnv", "@stable"] }, () => { - const giftCard01 = `${startsWith}${faker.datatype.number()}`; - const giftCard02 = `${startsWith}${faker.datatype.number()}`; + const firstGiftCardTag = faker.datatype.number(); + const secondGiftCardTag = faker.datatype.number(); const exportId = `${faker.datatype.number()}`; - let giftCard01hash; - let giftCard02hash; + let firstGiftCardId; + let secondGiftCardId; + let firstGiftCardCode; + let secondGiftCardCode; updatePlugin( "mirumee.notifications.admin_email", @@ -44,37 +43,34 @@ describe("As an admin I want to export gift card", () => { `Your exported {{ data_type }} data #${exportId} is ready`, ); createGiftCard({ - tag: giftCard01, + tag: firstGiftCardTag, amount: 5, currency: "THB", }) - .then(hash => { - giftCard01hash = hash.id; + .then(firstGiftCard => { + firstGiftCardId = firstGiftCard.id; + firstGiftCardCode = firstGiftCard.code; createGiftCard({ - tag: giftCard02, + tag: secondGiftCardTag, amount: 10, currency: "THB", }); }) - .then(hash2 => { - giftCard02hash = hash2.id; - enterAndSelectGiftCards([giftCard01hash, giftCard02hash]); - cy - .get(ASSIGN_ELEMENTS_SELECTORS.checkbox) - .first() - .check() - .should("be.checked") - .get(GIFT_CARD_LIST.selectedAmount) - .contains("Selected 2 items") - .should("be.visible") - .get(BUTTON_SELECTORS.showMoreButton) - .click({ force: true }) - .get(GIFT_CARD_SHOW_MORE.exportCodesMenu) - .click() - .get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.csv) - .click() - .get(BUTTON_SELECTORS.submit) - .click().confirmationMessageShouldDisappear; + .then(secondGiftCard => { + secondGiftCardCode = secondGiftCard.code; + secondGiftCardId = secondGiftCard.id; + cy.visit( + giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [ + firstGiftCardTag, + secondGiftCardTag, + ]), + ); + giftCardsPage.selectGiftCardOnListView(secondGiftCardCode); + giftCardsPage.selectGiftCardOnListView(firstGiftCardCode); + giftCardsPage.openExportGiftCardsDialog(); + giftCardsPage.selectSelectedRecordsButton(); + giftCardsPage.selectExportAsCSVButton(); + cy.clickSubmitButton(); getMailWithGiftCardExportWithAttachment( TEST_ADMIN_USER.email, `Your exported gift cards data #${exportId} is ready`, @@ -90,11 +86,13 @@ describe("As an admin I want to export gift card", () => { "should be able to export several gift cards to xlsx file. TC: SALEOR_1014", { tags: ["@giftCard", "@allEnv", "@stable"] }, () => { - const giftCard01 = `${startsWith}${faker.datatype.number()}`; - const giftCard02 = `${startsWith}${faker.datatype.number()}`; + const firstGiftCardTag = faker.datatype.number(); + const secondGiftCardTag = faker.datatype.number(); const exportId = `${faker.datatype.number()}`; - let giftCard01hash; - let giftCard02hash; + let firstGiftCardId; + let secondGiftCardId; + let firstGiftCardCode; + let secondGiftCardCode; updatePlugin( "mirumee.notifications.admin_email", @@ -102,37 +100,34 @@ describe("As an admin I want to export gift card", () => { `Your exported {{ data_type }} data #${exportId} is ready`, ); createGiftCard({ - tag: giftCard01, + tag: firstGiftCardTag, amount: 5, currency: "THB", }) - .then(hash => { - giftCard01hash = hash.id; + .then(firstGiftCard => { + firstGiftCardId = firstGiftCard.id; + firstGiftCardCode = firstGiftCard.code; createGiftCard({ - tag: giftCard02, + tag: secondGiftCardTag, amount: 10, currency: "THB", }); }) - .then(hash2 => { - giftCard02hash = hash2.id; - enterAndSelectGiftCards([giftCard01hash, giftCard02hash]); - cy - .get(ASSIGN_ELEMENTS_SELECTORS.checkbox) - .first() - .check() - .should("be.checked") - .get(GIFT_CARD_LIST.selectedAmount) - .contains("Selected 2 items") - .should("be.visible") - .get(BUTTON_SELECTORS.showMoreButton) - .click({ force: true }) - .get(GIFT_CARD_SHOW_MORE.exportCodesMenu) - .click() - .get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.xlsx) - .click() - .get(BUTTON_SELECTORS.submit) - .click().confirmationMessageShouldDisappear; + .then(secondGiftCard => { + secondGiftCardId = secondGiftCard.id; + secondGiftCardCode = secondGiftCard.code; + cy.visit( + giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [ + firstGiftCardTag, + secondGiftCardTag, + ]), + ); + giftCardsPage.selectGiftCardOnListView(secondGiftCardCode); + giftCardsPage.selectGiftCardOnListView(firstGiftCardCode); + giftCardsPage.openExportGiftCardsDialog(); + giftCardsPage.selectSelectedRecordsButton(); + giftCardsPage.selectExportAsXLSXButton(); + cy.clickSubmitButton(); getMailWithGiftCardExportWithAttachment( TEST_ADMIN_USER.email, `Your exported gift cards data #${exportId} is ready`, diff --git a/cypress/e2e/catalog/giftCards/updatingGiftCards.js b/cypress/e2e/catalog/giftCards/updatingGiftCards.js index 530b86152ea..3858f28e6ca 100644 --- a/cypress/e2e/catalog/giftCards/updatingGiftCards.js +++ b/cypress/e2e/catalog/giftCards/updatingGiftCards.js @@ -3,17 +3,16 @@ import faker from "faker"; -import { GIFT_CARD_LIST } from "../../../elements/catalog/giftCard/giftCardList"; import { GIFT_CARD_UPDATE } from "../../../elements/catalog/giftCard/giftCardUpdate"; -import { ASSIGN_ELEMENTS_SELECTORS } from "../../../elements/shared/assign-elements-selectors.js"; import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; -import { giftCardDetailsUrl } from "../../../fixtures/urlList"; +import { MESSAGES } from "../../../fixtures"; +import { giftCardDetailsUrl, urlList } from "../../../fixtures/urlList"; import { createGiftCard, getGiftCardWithId, } from "../../../support/api/requests/GiftCard"; import { formatDate } from "../../../support/formatData/formatDate"; -import { enterAndSelectGiftCards } from "../../../support/pages/catalog/giftCardPage"; +import { giftCardsPage } from "../../../support/pages"; describe("As an admin I want to update gift card", () => { const startsWith = "updateGCard"; @@ -98,45 +97,44 @@ describe("As an admin I want to update gift card", () => { "should be able to delete several gift cards. TC: SALEOR_1011", { tags: ["@giftCard", "@allEnv", "@stable"] }, () => { - const giftCard01 = `${startsWith}${faker.datatype.number()}`; - const giftCard02 = `${startsWith}${faker.datatype.number()}`; - let giftCard01hash; - let giftCard02hash; + const firstGiftCardTag = faker.datatype.number(); + const secondGiftCardTag = faker.datatype.number(); + let firstGiftCardId; + let secondGiftCardId; + let firstGiftCardCode; + let secondGiftCardCode; + cy.addAliasToGraphRequest("BulkDeleteGiftCard"); createGiftCard({ - tag: giftCard01, + tag: firstGiftCardTag, amount: 3, currency: "THB", }) - .then(hash => { - giftCard01hash = hash.id; + .then(firstGiftCard => { + firstGiftCardId = firstGiftCard.id; + firstGiftCardCode = firstGiftCard.code; createGiftCard({ - tag: giftCard02, + tag: secondGiftCardTag, amount: 7, currency: "THB", }); }) - .then(hash2 => { - giftCard02hash = hash2.id; - enterAndSelectGiftCards([giftCard01hash, giftCard02hash]); - cy.get(ASSIGN_ELEMENTS_SELECTORS.checkbox) - .first() - .check() - .should("be.checked") - .get(GIFT_CARD_LIST.selectedAmount) - .contains("Selected 2 items") - .should("be.visible") - .get(BUTTON_SELECTORS.deleteItemsButton) - .first() - .click() - .get(GIFT_CARD_UPDATE.consentCheckbox) - .click() - .get(BUTTON_SELECTORS.submit) - .click() - .get(ASSIGN_ELEMENTS_SELECTORS.checkbox) - .should("not.be.visible"); - getGiftCardWithId(giftCard01.id).should("be.null"); - getGiftCardWithId(giftCard02.id).should("be.null"); + .then(secondGiftCard => { + secondGiftCardCode = secondGiftCard.code; + secondGiftCardId = secondGiftCard.id; + cy.visit( + giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [ + firstGiftCardTag, + secondGiftCardTag, + ]), + ); + giftCardsPage.selectGiftCardOnListView(secondGiftCardCode); + giftCardsPage.selectGiftCardOnListView(firstGiftCardCode); + giftCardsPage.bulkDeleteRecords(); + cy.waitForRequestAndCheckIfNoErrors("@BulkDeleteGiftCard"); + cy.contains(MESSAGES.noGiftCardsFound).should("be.visible"); + getGiftCardWithId(firstGiftCardId).should("be.null"); + getGiftCardWithId(secondGiftCardId).should("be.null"); }); }, ); diff --git a/cypress/elements/catalog/giftCard/giftCardShowMore.js b/cypress/elements/catalog/giftCard/giftCardShowMore.js index 7f459141c28..519f881103a 100644 --- a/cypress/elements/catalog/giftCard/giftCardShowMore.js +++ b/cypress/elements/catalog/giftCard/giftCardShowMore.js @@ -6,4 +6,5 @@ export const GIFT_CARD_SHOW_MORE = { csv: 'input[value="CSV"]', xlsx: 'input[value="XLSX"]', }, + exportSelectedRecords: '[data-test-id="IDS"]', }; diff --git a/cypress/elements/shared/button-selectors.js b/cypress/elements/shared/button-selectors.js index ee47cc8cde1..80c8a9f2807 100644 --- a/cypress/elements/shared/button-selectors.js +++ b/cypress/elements/shared/button-selectors.js @@ -21,4 +21,5 @@ export const BUTTON_SELECTORS = { '[data-test-id = "delete-selected-elements-icon"]', dialogBackButton: '[data-test-id="back"]', expandMetadataButton: '[data-test-id="expand"]', + bulkDeleteButton: '[data-test-id="bulk-delete-button"]', }; diff --git a/cypress/elements/shared/sharedElements.js b/cypress/elements/shared/sharedElements.js index 1dbf46e9944..b5ebf6799ef 100644 --- a/cypress/elements/shared/sharedElements.js +++ b/cypress/elements/shared/sharedElements.js @@ -2,6 +2,7 @@ export const SHARED_ELEMENTS = { body: "body", header: "[data-test-id='page-header']", progressBar: '[role="progressbar"]', + rowNumberOption: "[data-test-id='rowNumberOption']", circularProgress: '[class*="CircularProgress-circle"]', autocompleteCircle: '[class*="arrowInnerContainer"]', dataGridTable: "[data-testid='data-grid-canvas']", diff --git a/cypress/fixtures/messages.js b/cypress/fixtures/messages.js index f3c8864123a..661784e9171 100644 --- a/cypress/fixtures/messages.js +++ b/cypress/fixtures/messages.js @@ -3,4 +3,5 @@ export const MESSAGES = { confirmProductsDeletion: "Are you sure you want to delete 2 products?", invalidEmailAddress: "Enter a valid email address.", slugMustBeUnique: "Slug must be unique", + noGiftCardsFound: "No gift cards found", }; diff --git a/cypress/support/api/requests/GiftCard.js b/cypress/support/api/requests/GiftCard.js index b244aace049..74910d4abbb 100644 --- a/cypress/support/api/requests/GiftCard.js +++ b/cypress/support/api/requests/GiftCard.js @@ -40,6 +40,7 @@ export function getGiftCards(first) { node{ displayCode id + code isActive expiryDate tags{ @@ -159,3 +160,17 @@ export function deleteGiftCard(giftCardId) { }`; return cy.sendRequestWithQuery(mutation); } +export function ensureGiftCardIsCreated(giftCardId, retries = 0) { + return getGiftCards(100, giftCardId).then(giftCardsListResponse => { + if (JSON.stringify(giftCardsListResponse).includes(giftCardId)) { + return; + } else if (retries > 4) { + throw new Error( + `Gift card with id: ${giftCardId} should be on list but could not find it. Retried for ${retries} times`, + ); + } else { + cy.wait(5000); + ensureGiftCardIsAdded(giftCardId, retries + 1); + } + }); +} diff --git a/cypress/support/api/utils/users.js b/cypress/support/api/utils/users.js index 45fd9fca0a6..98f3f849ffe 100644 --- a/cypress/support/api/utils/users.js +++ b/cypress/support/api/utils/users.js @@ -156,7 +156,7 @@ export function getMailWithGiftCardExportWithAttachment( } else { cy.mpGetMailsBySubject(subject).then(mailsWithSubject => { if (!mailsWithSubject.length) { - cy.wait(10000); + cy.wait(5000); getMailWithGiftCardExportWithAttachment( email, subject, diff --git a/cypress/support/pages/catalog/giftCardPage.js b/cypress/support/pages/catalog/giftCardPage.js index bfe13c399d4..81f03705cac 100644 --- a/cypress/support/pages/catalog/giftCardPage.js +++ b/cypress/support/pages/catalog/giftCardPage.js @@ -1,8 +1,10 @@ +import { SHARED_ELEMENTS } from "../../../elements"; import { GIFT_CARD_DIALOG } from "../../../elements/catalog/giftCard/giftCardDialog"; import { GIFT_CARD_LIST, - giftCardRow + giftCardRow, } from "../../../elements/catalog/giftCard/giftCardList"; +import { GIFT_CARD_SHOW_MORE } from "../../../elements/catalog/giftCard/giftCardShowMore"; import { GIFT_CARD_UPDATE } from "../../../elements/catalog/giftCard/giftCardUpdate"; import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; import { giftCardDetailsUrl, urlList } from "../../../fixtures/urlList"; @@ -11,7 +13,7 @@ export function openAndFillUpCreateGiftCardDialog({ note, tag, amount, - currency + currency, }) { cy.visit(urlList.giftCards) .get(GIFT_CARD_LIST.issueCardButton) @@ -39,7 +41,7 @@ export function saveGiftCard() { } export const expiryPeriods = { - MONTH: GIFT_CARD_DIALOG.expirationOptions.expiryPeriodMonthType + MONTH: GIFT_CARD_DIALOG.expirationOptions.expiryPeriodMonthType, }; export function setExpiryPeriod(amount, period) { @@ -77,6 +79,20 @@ export function selectGiftCard(giftCardId) { .find(GIFT_CARD_LIST.selectGiftCardCheckbox) .click(); } +export function selectGiftCardOnListView(giftCardCode) { + const splitCode = giftCardCode.split("-"); + const lastCodeElement = splitCode.slice(-1).toString(); + cy.log(lastCodeElement); + return cy + .contains( + `${SHARED_ELEMENTS.dataGridTable} table tbody tr`, + lastCodeElement, + ) + .invoke("index") + .then(index => { + cy.clickGridCell(0, index); + }); +} export function enterAndSelectGiftCards(giftCardsIds) { const alias = "GiftCardList"; @@ -86,6 +102,40 @@ export function enterAndSelectGiftCards(giftCardsIds) { elementsGraphqlAlias: alias, elementsName: "giftCards", elementsIds: giftCardsIds, - actionFunction: selectGiftCard + actionFunction: selectGiftCard, }); } +export function openExportGiftCardsDialog() { + cy.get(BUTTON_SELECTORS.showMoreButton) + .click({ force: true }) + .get(GIFT_CARD_SHOW_MORE.exportCodesMenu) + .click(); +} +export function selectSelectedRecordsButton() { + cy.get(GIFT_CARD_SHOW_MORE.exportSelectedRecords).click(); +} +export function selectExportAsCSVButton() { + cy.get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.csv).click(); +} +export function selectExportAsXLSXButton() { + cy.get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.xlsx).click(); +} +export function clickDeactivateButton() { + cy.get(GIFT_CARD_LIST.deactivateGiftCardButton).click(); +} +export function bulkDeleteRecords() { + cy.get(BUTTON_SELECTORS.bulkDeleteButton) + .click() + .get(GIFT_CARD_UPDATE.consentCheckbox) + .click() + .get(BUTTON_SELECTORS.submit) + .click(); +} +export function getUrlWithFilteredTags(url, tagsArray) { + const urlHelper = url + "?asc=true&sort=usedBy"; + let tagsPath = ""; + tagsArray.forEach((tag, index) => { + tagsPath += `&tag%5B${index}%5D=${tag}`; + }); + return urlHelper + tagsPath; +} diff --git a/cypress/support/pages/index.js b/cypress/support/pages/index.js index 78c2bbabc4c..04fe489529a 100644 --- a/cypress/support/pages/index.js +++ b/cypress/support/pages/index.js @@ -40,3 +40,4 @@ export * as channelsPage from "./channelsPage"; export * as pagesPage from "./pagesPage"; export * as columnPickerPage from "./columnPicker"; export * as pageDetailsPage from "./pageDetailsPage"; +export * as giftCardsPage from "./catalog/giftCardPage"; diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index f0f5069653e..feb33a7ab84 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -790,6 +790,9 @@ "context": "product", "string": "Stock quantity" }, + "3a5wL8": { + "string": "Active" + }, "3bQz2o": { "context": "bulk issue gift cards success alert title", "string": "Gift Cards Issued" @@ -1283,6 +1286,10 @@ "7Chrsf": { "string": "Passwords do not match" }, + "7EDqed": { + "context": "tab name", + "string": "All gift cards" + }, "7FL+WZ": { "context": "export products to csv file, button", "string": "Export Products" @@ -5765,6 +5772,9 @@ "context": "volume units types", "string": "Volume" }, + "d68yq7": { + "string": "Delete gift cards" + }, "d7dT8o": { "context": "button, sets granted refund amount in input", "string": "Set max" @@ -6207,6 +6217,10 @@ "context": "caption", "string": "If enabled, attribute will be accessible to customers." }, + "h2hEKV": { + "context": "search gift card placeholder", + "string": "Search gift cards, e.g {exampleGiftCardCode}" + }, "h5r9+x": { "context": "sort shipping methods by zone, section header", "string": "Shipping By Zone" @@ -6819,10 +6833,6 @@ "context": "money", "string": "from {money}" }, - "labkPK": { - "context": "search gift card placeholder", - "string": "Search Gift Cards, e.g {exampleGiftCardCode}" - }, "lct0qd": { "string": "This list shows all attributes that will be assigned to pages that have this page type assigned." }, diff --git a/src/config.ts b/src/config.ts index f811f90263b..f3df67507f9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -136,6 +136,7 @@ export const defaultListSettings: AppListViewSettings = { }, [ListViews.GIFT_CARD_LIST]: { rowNumber: PAGINATE_BY, + columns: ["giftCardCode", "status", "tag", "product", "usedBy", "balance"], }, }; diff --git a/src/giftCards/GiftCardExportDialogContent/GiftCardExportDialogContent.tsx b/src/giftCards/GiftCardExportDialogContent/GiftCardExportDialogContent.tsx index 1b582e93281..59919e43310 100644 --- a/src/giftCards/GiftCardExportDialogContent/GiftCardExportDialogContent.tsx +++ b/src/giftCards/GiftCardExportDialogContent/GiftCardExportDialogContent.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import { ConfirmButton } from "@dashboard/components/ConfirmButton"; import { Task } from "@dashboard/containers/BackgroundTasks/types"; import { @@ -45,10 +44,10 @@ const GiftCardExportDialog: React.FC< const { loading: loadingGiftCardList, totalCount: filteredGiftCardsCount, - listElements, + selectedRowIds, } = useGiftCardList(); - const selectedIds = idsToExport ?? listElements; + const selectedIds = idsToExport ?? selectedRowIds; const { data: allGiftCardsCountData, loading: loadingGiftCardCount } = useGiftCardTotalCountQuery(); @@ -59,14 +58,14 @@ const GiftCardExportDialog: React.FC< onCompleted: data => { const errors = data?.exportGiftCards?.errors; - if (!errors.length) { + if (!errors?.length) { notify({ text: intl.formatMessage(messages.successAlertDescription), title: intl.formatMessage(messages.successAlertTitle), }); queue(Task.EXPORT, { - id: data.exportGiftCards.exportFile.id, + id: data?.exportGiftCards?.exportFile?.id, }); onClose(); @@ -91,7 +90,7 @@ const GiftCardExportDialog: React.FC< : exportSettingsInitialFormData, handleSubmit, ); - const allGiftCardsCount = allGiftCardsCountData?.giftCards?.totalCount; + const allGiftCardsCount = allGiftCardsCountData?.giftCards?.totalCount ?? 0; const exportScopeLabels = { allItems: intl.formatMessage( @@ -111,7 +110,7 @@ const GiftCardExportDialog: React.FC< description: "export selected items to csv file", }, { - number: listElements.length, + number: selectedRowIds.length, }, ), }; @@ -126,7 +125,9 @@ const GiftCardExportDialog: React.FC< {!loading && ( <> { +export const GiftCardListBulkActions: React.FC = () => { const intl = useIntl(); const notify = useNotifier(); - const { listElements: ids, reset, giftCards } = useGiftCardList(); + const { selectedRowIds, clearRowSelection, giftCards } = useGiftCardList(); const hasAnyEnabledCardsSelected = giftCards - .filter(getByIds(ids)) + .filter(getByIds(selectedRowIds)) .some(({ isActive }) => isActive); const areAllSelectedCardsActive = giftCards - .filter(getByIds(ids)) + .filter(getByIds(selectedRowIds)) .every(({ isActive }) => isActive); const hasAnyDisabledCardsSelected = giftCards - .filter(getByIds(ids)) + .filter(getByIds(selectedRowIds)) .some(({ isActive }) => !isActive); const areAllSelectedCardsDisabled = giftCards - .filter(getByIds(ids)) + .filter(getByIds(selectedRowIds)) .every(({ isActive }) => !isActive); const [activateGiftCards, activateGiftCardsOpts] = useGiftCardBulkActivateMutation({ onCompleted: data => { - const { errors, count } = data?.giftCardBulkActivate; - - const notifierData: IMessage = !!errors?.length + const notifierData: IMessage = !!data?.giftCardBulkActivate?.errors + ?.length ? { status: "error", text: intl.formatMessage(messages.errorActivateAlertText, { - count, + count: data?.giftCardBulkActivate?.count, }), } : { status: "success", text: intl.formatMessage(messages.successActivateAlertText, { - count, + count: data?.giftCardBulkActivate?.count, }), }; notify(notifierData); - if (!errors.length) { - reset(); + if (!data?.giftCardBulkActivate?.errors?.length) { + clearRowSelection(); } }, refetchQueries: [GIFT_CARD_LIST_QUERY], @@ -67,36 +65,39 @@ const BulkEnableDisableSection: React.FC = () => { const [deactivateGiftCards, deactivateGiftCardsOpts] = useGiftCardBulkDeactivateMutation({ onCompleted: data => { - const { errors, count } = data?.giftCardBulkDeactivate; - - const notifierData: IMessage = !!errors?.length + const notifierData: IMessage = !!data?.giftCardBulkDeactivate?.errors + ?.length ? { status: "error", text: intl.formatMessage(messages.errorDeactivateAlertText, { - count, + count: data?.giftCardBulkDeactivate?.count, }), } : { status: "success", text: intl.formatMessage(messages.successDeactivateAlertText, { - count, + count: data?.giftCardBulkDeactivate?.count, }), }; notify(notifierData); - if (!errors.length) { - reset(); + if (!data?.giftCardBulkDeactivate?.errors?.length) { + clearRowSelection(); } }, refetchQueries: [GIFT_CARD_LIST_QUERY], }); - const handleActivateGiftCards = () => - activateGiftCards({ variables: { ids } }); + const handleActivateGiftCards = async () => { + await activateGiftCards({ variables: { ids: selectedRowIds } }); + clearRowSelection(); + }; - const handleDeactivateGiftCards = () => - deactivateGiftCards({ variables: { ids } }); + const handleDeactivateGiftCards = async () => { + await deactivateGiftCards({ variables: { ids: selectedRowIds } }); + clearRowSelection(); + }; const isSelectionMixed = hasAnyEnabledCardsSelected && hasAnyDisabledCardsSelected; @@ -126,5 +127,3 @@ const BulkEnableDisableSection: React.FC = () => { ); }; - -export default BulkEnableDisableSection; diff --git a/src/giftCards/GiftCardsList/GiftCardListBulkActions/index.ts b/src/giftCards/GiftCardsList/GiftCardListBulkActions/index.ts new file mode 100644 index 00000000000..86aa1dd1d2b --- /dev/null +++ b/src/giftCards/GiftCardsList/GiftCardListBulkActions/index.ts @@ -0,0 +1 @@ +export * from "./GiftCardListBulkActions"; diff --git a/src/giftCards/GiftCardsList/GiftCardListPage.tsx b/src/giftCards/GiftCardsList/GiftCardListPage.tsx index e2aabd09a4b..c32eacd19b1 100644 --- a/src/giftCards/GiftCardsList/GiftCardListPage.tsx +++ b/src/giftCards/GiftCardsList/GiftCardListPage.tsx @@ -1,15 +1,15 @@ -import VerticalSpacer from "@dashboard/components/VerticalSpacer"; import React from "react"; +import GiftCardListSearchAndFilters from "./GiftCardListSearchAndFilters"; +import { GiftCardsListDatagrid } from "./GiftCardsListDatagrid"; import GiftCardsListHeader from "./GiftCardsListHeader"; import GiftCardsListOrderInfoCard from "./GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard"; -import GiftCardsListTable from "./GiftCardsListTable"; const GiftCardsListPage: React.FC = () => ( <> - - + + ); diff --git a/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/GiftCardListSearchAndFilters.tsx b/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/GiftCardListSearchAndFilters.tsx index 2040fb2f013..3b19814b437 100644 --- a/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/GiftCardListSearchAndFilters.tsx +++ b/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/GiftCardListSearchAndFilters.tsx @@ -1,52 +1,52 @@ // @ts-strict-ignore +import useAppChannel from "@dashboard/components/AppLayout/AppChannelContext"; +import { ListFilters } from "@dashboard/components/AppLayout/ListFilters"; +import { BulkDeleteButton } from "@dashboard/components/BulkDeleteButton"; import DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog"; -import FilterBar from "@dashboard/components/FilterBar"; -import SaveFilterTabDialog, { - SaveFilterTabDialogFormData, -} from "@dashboard/components/SaveFilterTabDialog"; +import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config"; -import { giftCardListUrl } from "@dashboard/giftCards/urls"; import { useGiftCardCurrenciesQuery } from "@dashboard/graphql"; import { getSearchFetchMoreProps } from "@dashboard/hooks/makeTopLevelSearch/utils"; import useLocalStorage from "@dashboard/hooks/useLocalStorage"; -import useNavigator from "@dashboard/hooks/useNavigator"; -import { maybe } from "@dashboard/misc"; import useCustomerSearch from "@dashboard/searches/useCustomerSearch"; import useGiftCardTagsSearch from "@dashboard/searches/useGiftCardTagsSearch"; import useProductSearch from "@dashboard/searches/useProductSearch"; -import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers"; import { mapEdgesToItems } from "@dashboard/utils/maps"; +import { Box } from "@saleor/macaw-ui/next"; import compact from "lodash/compact"; import React from "react"; -import { useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; +import { createFilterStructure, getFilterOpts } from "../filters"; +import { GiftCardListBulkActions } from "../GiftCardListBulkActions"; import { useGiftCardListDialogs } from "../providers/GiftCardListDialogsProvider"; import { useGiftCardList } from "../providers/GiftCardListProvider"; import { GiftCardListActionParamsEnum } from "../types"; -import { - createFilterStructure, - deleteFilterTab, - getActiveFilters, - getFilterOpts, - getFilterQueryParam, - getFiltersCurrentTab, - getFilterTabs, - saveFilterTab, -} from "./filters"; import { giftCardListFilterErrorMessages as errorMessages, giftCardListSearchAndFiltersMessages as messages, } from "./messages"; const GiftCardListSearchAndFilters: React.FC = () => { - const navigate = useNavigator(); const intl = useIntl(); const [selectedChannel] = useLocalStorage("channel", ""); + const { availableChannels } = useAppChannel(false); + const selectedChannelData = availableChannels.find( + channel => channel.slug === selectedChannel, + ); - const { reset, params } = useGiftCardList(); + const { + params, + changeFilters, + handleSearchChange, + onPresetSave, + onPresetDelete, + presets, + presetIdToDelete, + selectedRowIds, + } = useGiftCardList(); - const { onClose, openSearchDeleteDialog, openSearchSaveDialog } = - useGiftCardListDialogs(); + const { onClose, openDeleteDialog } = useGiftCardListDialogs(); const defaultSearchVariables = { variables: { ...DEFAULT_INITIAL_SEARCH_DATA, first: 5 }, @@ -109,75 +109,53 @@ const GiftCardListSearchAndFilters: React.FC = () => { const filterStructure = createFilterStructure(intl, filterOpts); - const tabs = getFilterTabs(); - const currentTab = getFiltersCurrentTab(params, tabs); - - const [changeFilters, resetFilters, handleSearchChange] = - createFilterHandlers({ - createUrl: giftCardListUrl, - getFilterQueryParam, - navigate, - params, - cleanupFn: reset, - }); - - const handleTabChange = (tab: number) => { - reset(); - navigate( - giftCardListUrl({ - activeTab: tab.toString(), - ...getFilterTabs()[tab - 1].data, - }), - ); - }; - - const handleTabDelete = () => { - deleteFilterTab(currentTab); - reset(); - navigate(giftCardListUrl()); - }; - - const handleTabSave = (data: SaveFilterTabDialogFormData) => { - saveFilterTab(data.name, getActiveFilters(params)); - handleTabChange(tabs.length + 1); - }; - return ( <> - tab.name)} - currentTab={currentTab} - filterStructure={filterStructure} + currencySymbol={selectedChannelData?.currencyCode || ""} initialSearch={params?.query || ""} - onAll={resetFilters} onFilterChange={changeFilters} onSearchChange={handleSearchChange} - onTabChange={handleTabChange} - onTabDelete={openSearchDeleteDialog} - onTabSave={openSearchSaveDialog} + filterStructure={filterStructure} searchPlaceholder={intl.formatMessage(messages.searchPlaceholder, { exampleGiftCardCode: "21F1-39DY-V4U2", })} - allTabLabel={intl.formatMessage(messages.defaultTabLabel)} + actions={ + + {selectedRowIds.length > 0 && ( + <> + + + + + + )} + + } /> + + tabs[currentTab - 1].name, "...")} + onSubmit={onPresetDelete} + tabName={presets[presetIdToDelete - 1]?.name ?? "..."} /> ); diff --git a/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/messages.ts b/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/messages.ts index 77a03b1e9cd..a8984ae6111 100644 --- a/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/messages.ts +++ b/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/messages.ts @@ -15,8 +15,8 @@ export const giftCardListFilterErrorMessages = defineMessages({ export const giftCardListSearchAndFiltersMessages = defineMessages({ searchPlaceholder: { - id: "labkPK", - defaultMessage: "Search Gift Cards, e.g {exampleGiftCardCode}", + id: "h2hEKV", + defaultMessage: "Search gift cards, e.g {exampleGiftCardCode}", description: "search gift card placeholder", }, defaultTabLabel: { diff --git a/src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx new file mode 100644 index 00000000000..930c5105866 --- /dev/null +++ b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/GiftCardsListDatagrid.tsx @@ -0,0 +1,213 @@ +import { ColumnPicker } from "@dashboard/components/Datagrid/ColumnPicker/ColumnPicker"; +import { useColumns } from "@dashboard/components/Datagrid/ColumnPicker/useColumns"; +import Datagrid from "@dashboard/components/Datagrid/Datagrid"; +import { + DatagridChangeStateContext, + useDatagridChangeState, +} from "@dashboard/components/Datagrid/hooks/useDatagridChange"; +import TablePagination from "@dashboard/components/TablePagination"; +import { commonTooltipMessages } from "@dashboard/components/TooltipTableCellHeader/messages"; +import { giftCardListUrl, giftCardUrl } from "@dashboard/giftCards/urls"; +import useNavigator from "@dashboard/hooks/useNavigator"; +import usePaginator from "@dashboard/hooks/usePaginator"; +import { Item } from "@glideapps/glide-data-grid"; +import { Box, useTheme } from "@saleor/macaw-ui/next"; +import isEqual from "lodash/isEqual"; +import React, { useCallback, useEffect, useMemo } from "react"; +import { useIntl } from "react-intl"; + +import { messages as filterLabels } from "../filters"; +import { useGiftCardList } from "../providers/GiftCardListProvider"; +import { canBeSorted } from "../sort"; +import { GiftCardListColummns, GiftCardUrlSortField } from "../types"; +import { createGetCellContent, getColumns } from "./datagrid"; +import { messages } from "./messages"; + +export const GiftCardsListDatagrid = () => { + const datagridState = useDatagridChangeState(); + const navigate = useNavigator(); + const intl = useIntl(); + const { + loading, + giftCards, + settings, + updateListSettings, + selectedRowIds, + setSelectedRowIds, + setClearDatagridRowSelectionCallback, + sort, + onSort, + pageInfo, + paginationState, + params, + } = useGiftCardList(); + + const isCurrencySelected = !!params.currency; + + const paginationValues = usePaginator({ + pageInfo, + paginationState, + queryString: params, + }); + + const availableColumns = useMemo(() => getColumns(intl, sort), [intl, sort]); + + const onColumnChange = useCallback( + (columns: string[]) => { + if (updateListSettings) { + updateListSettings( + "columns", + (columns as GiftCardListColummns[]).filter(Boolean), + ); + } + }, + [updateListSettings], + ); + + const { + handlers, + staticColumns, + visibleColumns, + selectedColumns, + recentlyAddedColumn, + } = useColumns({ + staticColumns: availableColumns, + selectedColumns: settings?.columns ?? [], + onSave: onColumnChange, + }); + + const { theme: currentTheme, themeValues } = useTheme(); + + const getCellContent = useCallback( + createGetCellContent( + giftCards, + visibleColumns, + intl, + themeValues, + currentTheme, + ), + [giftCards, visibleColumns, themeValues, currentTheme], + ); + + const handleHeaderClick = useCallback( + (col: number) => { + const columnName = visibleColumns[col].id; + + if (!Object.keys(GiftCardUrlSortField).includes(columnName)) { + return null; + } + + if ( + canBeSorted(columnName as GiftCardUrlSortField, isCurrencySelected) && + sort !== undefined + ) { + onSort(columnName as GiftCardUrlSortField); + } + }, + [visibleColumns, onSort, sort, isCurrencySelected], + ); + + const handleRowAnchor = useCallback( + ([, row]: Item) => giftCardUrl(giftCards[row].id), + [giftCards], + ); + + const handleRowClick = useCallback( + ([_, row]: Item) => { + navigate(giftCardUrl(giftCards[row].id)); + }, + [giftCards], + ); + + const handleGetColumnTooltipContent = useCallback( + (colIndex: number) => { + const columnName = visibleColumns[colIndex].id; + + if (canBeSorted(columnName as GiftCardUrlSortField, isCurrencySelected)) { + return ""; + } + + return intl.formatMessage(commonTooltipMessages.noFilterSelected, { + filterName: filterLabels.currencyLabel.defaultMessage, + }); + }, + [visibleColumns, isCurrencySelected], + ); + + const handleGiftCardSelectionChange = useCallback( + (rows: number[], clearSelection: () => void) => { + if (!giftCards) { + return; + } + + const rowsIds = rows.map(row => giftCards[row].id); + + const haveSaveValues = isEqual(rowsIds, selectedRowIds); + + if (!haveSaveValues) { + setSelectedRowIds(rowsIds); + } + + setClearDatagridRowSelectionCallback(clearSelection); + }, + [giftCards, selectedRowIds], + ); + + useEffect(() => { + if (params.sort && !canBeSorted(params.sort, isCurrencySelected)) { + navigate( + giftCardListUrl({ + ...params, + sort: GiftCardUrlSortField.usedBy, + }), + ); + } + }); + + return ( + + col > 0} + rows={giftCards?.length ?? 0} + availableColumns={visibleColumns} + emptyText={intl.formatMessage(messages.noData)} + onRowSelectionChange={handleGiftCardSelectionChange} + getCellContent={getCellContent} + getCellError={() => false} + selectionActions={() => null} + menuItems={() => []} + onRowClick={handleRowClick} + onHeaderClicked={handleHeaderClick} + rowAnchor={handleRowAnchor} + getColumnTooltipContent={handleGetColumnTooltipContent} + recentlyAddedColumn={recentlyAddedColumn} + renderColumnPicker={() => ( + + )} + /> + + + + + + ); +}; diff --git a/src/giftCards/GiftCardsList/GiftCardsListDatagrid/datagrid.ts b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/datagrid.ts new file mode 100644 index 00000000000..c4b9baa77c2 --- /dev/null +++ b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/datagrid.ts @@ -0,0 +1,187 @@ +import { + moneyCell, + readonlyTextCell, + tagsCell, +} from "@dashboard/components/Datagrid/customCells/cells"; +import { AvailableColumn } from "@dashboard/components/Datagrid/types"; +import { + ExtendedGiftCard, + GiftCardBase, +} from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; +import { PLACEHOLDER } from "@dashboard/giftCards/GiftCardUpdate/types"; +import { GiftCardDataFragment, GiftCardListQuery } from "@dashboard/graphql"; +import { getStatusColor } from "@dashboard/misc"; +import { Sort } from "@dashboard/types"; +import { getColumnSortDirectionIcon } from "@dashboard/utils/columns/getColumnSortDirectionIcon"; +import { GridCell, Item } from "@glideapps/glide-data-grid"; +import { DefaultTheme, ThemeTokensValues } from "@saleor/macaw-ui/next"; +import { IntlShape } from "react-intl"; + +import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages"; +import { GiftCardUrlSortField } from "../types"; +import { columnsMessages, messages } from "./messages"; + +export const getColumns = ( + intl: IntlShape, + sort?: Sort, +): AvailableColumn[] => + [ + { + id: "giftCardCode", + title: intl.formatMessage(columnsMessages.name), + width: 350, + }, + { + id: "status", + title: intl.formatMessage(columnsMessages.status), + width: 150, + }, + { + id: "tag", + title: intl.formatMessage(columnsMessages.tag), + width: 200, + }, + { + id: "product", + title: intl.formatMessage(columnsMessages.productTitle), + width: 200, + }, + { + id: "usedBy", + title: intl.formatMessage(columnsMessages.customer), + width: 200, + }, + { + id: "balance", + title: intl.formatMessage(columnsMessages.balance), + width: 200, + }, + ].map(column => ({ + ...column, + icon: sort ? getColumnSortDirectionIcon(sort, column.id) : undefined, + })); + +export const createGetCellContent = + ( + categories: Array< + ExtendedGiftCard< + NonNullable["edges"][0]["node"] + > + >, + columns: AvailableColumn[], + intl: IntlShape, + theme: ThemeTokensValues, + currentTheme: DefaultTheme, + ) => + ([column, row]: Item): GridCell => { + const columnId = columns[column]?.id; + + if (!columnId) { + return readonlyTextCell(""); + } + + const rowData = categories[row]; + + switch (columnId) { + case "giftCardCode": + return readonlyTextCell( + intl.formatMessage(messages.codeEndingWithLabel, { + last4CodeChars: rowData?.last4CodeChars ?? "", + }), + ); + case "status": + const status = getStatusText(rowData); + + if (!status) { + return tagsCell( + [ + { + tag: intl.formatMessage(messages.active), + color: getTagCellColor( + getStatusColor("success", currentTheme), + theme, + ), + }, + ], + [intl.formatMessage(messages.active)], + ); + } + + const statusLabel = intl.formatMessage(status.label); + + return tagsCell( + [ + { + tag: statusLabel, + color: getTagCellColor( + getStatusColor(status.color as any, currentTheme), + theme, + ), + }, + ], + [statusLabel], + ); + case "tag": + return readonlyTextCell(getTagCellText(rowData?.tags ?? [])); + case "product": + return readonlyTextCell(rowData?.product?.name ?? PLACEHOLDER); + case "usedBy": + if (rowData.usedBy) { + return readonlyTextCell( + `${rowData.usedBy.firstName} ${rowData.usedBy.lastName}`, + ); + } + + return readonlyTextCell(rowData?.usedByEmail ?? PLACEHOLDER); + case "balance": + return moneyCell( + rowData.currentBalance.amount, + rowData.currentBalance.currency, + ); + default: + return readonlyTextCell("", false); + } + }; + +export const getTagCellText = (tags: GiftCardDataFragment["tags"]) => { + if (!!tags.length) { + return tags.map(({ name }) => name).join(", "); + } + + return PLACEHOLDER; +}; + +export const getStatusText = ( + giftCard: ExtendedGiftCard, +) => { + const { isExpired, isActive } = giftCard; + + if (isExpired) { + return { + color: "info", + label: giftCardStatusChipMessages.expiredStatusLabel, + }; + } + + if (!isActive) { + return { + color: "error", + label: giftCardStatusChipMessages.disabledStatusLabel, + }; + } + + return null; +}; + +function getTagCellColor( + color: string, + currentTheme: ThemeTokensValues, +): string { + if (color.startsWith("#")) { + return color; + } + + return currentTheme.colors.background[ + color as keyof ThemeTokensValues["colors"]["background"] + ]; +} diff --git a/src/giftCards/GiftCardsList/GiftCardsListDatagrid/index.ts b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/index.ts new file mode 100644 index 00000000000..fa5592ecb36 --- /dev/null +++ b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/index.ts @@ -0,0 +1 @@ +export * from "./GiftCardsListDatagrid"; diff --git a/src/giftCards/GiftCardsList/GiftCardsListDatagrid/messages.ts b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/messages.ts new file mode 100644 index 00000000000..71e5b9eaa8e --- /dev/null +++ b/src/giftCards/GiftCardsList/GiftCardsListDatagrid/messages.ts @@ -0,0 +1,50 @@ +import { defineMessages } from "react-intl"; + +export const columnsMessages = defineMessages({ + name: { + id: "eLJQSh", + defaultMessage: "Gift Card", + description: "column title gift card", + }, + status: { + id: "tzMNF3", + defaultMessage: "Status", + }, + tag: { + id: "FEWgh/", + defaultMessage: "Tag", + description: "column title tag", + }, + productTitle: { + id: "bwJc6V", + defaultMessage: "Product", + description: "column title product", + }, + customer: { + id: "MJBAqv", + defaultMessage: "Used by", + description: "column title used by/customer", + }, + balance: { + id: "MbZHXE", + defaultMessage: "Balance", + description: "column title balance", + }, +}); + +export const messages = defineMessages({ + codeEndingWithLabel: { + id: "38dS1A", + defaultMessage: "Code ending with {last4CodeChars}", + description: "code ending with label", + }, + noData: { + id: "Rd0s80", + defaultMessage: "No gift cards found", + description: "no cards found title message", + }, + active: { + defaultMessage: "Active", + id: "3a5wL8", + }, +}); diff --git a/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeader.tsx b/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeader.tsx index a054f6614ca..58e1fd5d954 100644 --- a/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeader.tsx +++ b/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeader.tsx @@ -1,56 +1,113 @@ import { TopNav } from "@dashboard/components/AppLayout/TopNav"; -import { Button } from "@dashboard/components/Button"; -import CardMenu, { CardMenuItem } from "@dashboard/components/CardMenu"; -import HorizontalSpacer from "@dashboard/components/HorizontalSpacer"; +import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect"; import useNavigator from "@dashboard/hooks/useNavigator"; import { sectionNames } from "@dashboard/intl"; +import { Box, Button, ChevronRightIcon } from "@saleor/macaw-ui/next"; import React from "react"; import { useIntl } from "react-intl"; import { giftCardSettingsUrl } from "../../urls"; import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages"; import { useGiftCardListDialogs } from "../providers/GiftCardListDialogsProvider"; +import { useGiftCardList } from "../providers/GiftCardListProvider"; import GiftCardsListHeaderAlert from "./GiftCardsListHeaderAlert"; const GiftCardsListHeader: React.FC = () => { const intl = useIntl(); const navigate = useNavigator(); - const { openCreateDialog, openBulkCreateDialog, openExportDialog } = - useGiftCardListDialogs(); + const { + openCreateDialog, + openBulkCreateDialog, + openExportDialog, + openSearchDeleteDialog, + openSearchSaveDialog, + } = useGiftCardListDialogs(); - const openSettings = () => navigate(giftCardSettingsUrl); + const { + hasPresetsChange, + selectedPreset, + presets, + onPresetUpdate, + setPresetIdToDelete, + onPresetChange, + resetFilters, + isFilterPresetOpen, + setFilterPresetOpen, + } = useGiftCardList(); - const menuItems: CardMenuItem[] = [ - { - label: intl.formatMessage(messages.settings), - testId: "settingsMenuItem", - onSelect: openSettings, - }, - { - label: intl.formatMessage(messages.bulkIssue), - testId: "bulkIssueMenuItem", - onSelect: openBulkCreateDialog, - }, - { - label: intl.formatMessage(messages.exportCodes), - testId: "exportCodesMenuItem", - onSelect: openExportDialog, - }, - ]; + const openSettings = () => navigate(giftCardSettingsUrl); return ( <> - - - - + + + + + + { + setPresetIdToDelete(id); + openSearchDeleteDialog(); + }} + onUpdate={onPresetUpdate} + savedPresets={presets.map(preset => preset.name)} + activePreset={selectedPreset} + onSelectAll={resetFilters} + onSave={openSearchSaveDialog} + isOpen={isFilterPresetOpen} + onOpenChange={setFilterPresetOpen} + selectAllLabel={intl.formatMessage({ + id: "7EDqed", + defaultMessage: "All gift cards", + description: "tab name", + })} + /> + + + + + + + diff --git a/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeaderAlert.tsx b/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeaderAlert.tsx index f57486a2e49..0027ec443c3 100644 --- a/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeaderAlert.tsx +++ b/src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeaderAlert.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import { useGiftCardProductsCountQuery } from "@dashboard/graphql"; import useLocalStorage from "@dashboard/hooks/useLocalStorage"; import { Alert } from "@saleor/macaw-ui"; @@ -20,9 +19,9 @@ const GiftCardsListHeaderAlert: React.FC = () => { }); const giftCardProductTypesExist = - giftCardProductsCount?.giftCardProductTypes.totalCount > 0; + (giftCardProductsCount?.giftCardProductTypes?.totalCount ?? 0) > 0; const giftCardProductsExist = - giftCardProductsCount?.giftCardProducts.totalCount > 0; + (giftCardProductsCount?.giftCardProducts?.totalCount ?? 0) > 0; const showNoGiftCardProductsAlert = !giftCardProductsCountLoading && @@ -34,7 +33,7 @@ const GiftCardsListHeaderAlert: React.FC = () => { title={intl.formatMessage(messages.noGiftCardsAlertTitle)} variant="warning" close={false} - className="remove-icon-background" + className="remove-icon-background remove-content-padding-top " > = ({ giftCardProductTypesExist, giftCardProductsExist }) => { - const classes = useStyles({}); - const giftCardProductTypeUrl = productTypeAddUrl({ kind: ProductTypeKindEnum.GIFT_CARD, }); @@ -30,7 +32,7 @@ const GiftCardsListHeaderAlertContent: React.FC< {...messages.noGiftCardsProductTypes} values={{ createGiftCardProductType: ( - + ), @@ -47,7 +49,7 @@ const GiftCardsListHeaderAlertContent: React.FC< createGiftCardProduct: ( diff --git a/src/giftCards/GiftCardsList/GiftCardsListHeader/styles.ts b/src/giftCards/GiftCardsList/GiftCardsListHeader/styles.ts deleted file mode 100644 index 290fb714312..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListHeader/styles.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { makeStyles } from "@saleor/macaw-ui"; - -const useStyles = makeStyles( - theme => ({ - preview: { - position: "absolute", - top: theme.spacing(-4), - }, - title: { - position: "relative", - }, - }), - { name: "GiftCardListHeader" }, -); - -export default useStyles; diff --git a/src/giftCards/GiftCardsList/GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard.tsx b/src/giftCards/GiftCardsList/GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard.tsx index b530dd59fe7..dc5b87987a2 100644 --- a/src/giftCards/GiftCardsList/GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard.tsx +++ b/src/giftCards/GiftCardsList/GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard.tsx @@ -1,7 +1,7 @@ import Link from "@dashboard/components/Link"; import { orderGiftCardBoughtPath } from "@dashboard/orders/urls"; -import { Typography } from "@material-ui/core"; import { Alert } from "@saleor/macaw-ui"; +import { Text } from "@saleor/macaw-ui/next"; import React from "react"; import { FormattedMessage } from "react-intl"; @@ -9,7 +9,7 @@ import { giftCardListOrderCardMessages as messages } from "./messages"; const GiftCardsListOrderInfoCard: React.FC = () => ( - + ( ), }} /> - + ); diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx deleted file mode 100644 index 7c121e65362..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTable.tsx +++ /dev/null @@ -1,186 +0,0 @@ -// @ts-strict-ignore -import Checkbox from "@dashboard/components/Checkbox"; -import DeleteIconButton from "@dashboard/components/DeleteIconButton"; -import HorizontalSpacer from "@dashboard/components/HorizontalSpacer"; -import Link from "@dashboard/components/Link"; -import Money from "@dashboard/components/Money"; -import ResponsiveTable from "@dashboard/components/ResponsiveTable"; -import Skeleton from "@dashboard/components/Skeleton"; -import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper"; -import TableRowLink from "@dashboard/components/TableRowLink"; -import { customerUrl } from "@dashboard/customers/urls"; -import GiftCardStatusChip from "@dashboard/giftCards/components/GiftCardStatusChip/GiftCardStatusChip"; -import { PLACEHOLDER } from "@dashboard/giftCards/GiftCardUpdate/types"; -import { giftCardListUrl, giftCardUrl } from "@dashboard/giftCards/urls"; -import useNavigator from "@dashboard/hooks/useNavigator"; -import { renderCollection } from "@dashboard/misc"; -import { productUrl } from "@dashboard/products/urls"; -import { Card, TableBody, TableCell, Typography } from "@material-ui/core"; -import { PillLink } from "@saleor/macaw-ui"; -import React, { useEffect } from "react"; -import { FormattedMessage, useIntl } from "react-intl"; -import { Link as RouterLink } from "react-router-dom"; - -import GiftCardListSearchAndFilters from "../GiftCardListSearchAndFilters"; -import { giftCardsListTableMessages as messages } from "../messages"; -import { useGiftCardListDialogs } from "../providers/GiftCardListDialogsProvider"; -import { useGiftCardList } from "../providers/GiftCardListProvider"; -import { canBeSorted } from "../sort"; -import { useTableStyles as useStyles } from "../styles"; -import { GiftCardUrlSortField } from "../types"; -import GiftCardsListTableFooter from "./GiftCardsListTableFooter"; -import GiftCardsListTableHeader from "./GiftCardsListTableHeader"; -import { getTagCellText } from "./utils"; - -const GiftCardsListTable: React.FC = () => { - const intl = useIntl(); - const classes = useStyles({}); - const navigate = useNavigator(); - - const { toggle, isSelected, giftCards, numberOfColumns, params } = - useGiftCardList(); - const { openDeleteDialog } = useGiftCardListDialogs(); - - const isCurrencySelected = !!params.currency; - - useEffect(() => { - if (!canBeSorted(params.sort, isCurrencySelected)) { - navigate( - giftCardListUrl({ - ...params, - sort: GiftCardUrlSortField.usedBy, - }), - ); - } - }); - - return ( - - - - - - - {renderCollection( - giftCards, - giftCard => { - if (!giftCard) { - return ( - <> - - - - - - - - - - - ); - } - - const { - id, - last4CodeChars, - usedBy, - usedByEmail, - tags, - product, - currentBalance, - } = giftCard; - - return ( - - - toggle(id)} - /> - - -
- - {intl.formatMessage(messages.codeEndingWithLabel, { - last4CodeChars, - })} - - <> - - - -
-
- - {getTagCellText(tags)} - - - {product ? ( - - { - event.stopPropagation(); - navigate(productUrl(product?.id)); - }} - > - {product?.name} - - - ) : ( - PLACEHOLDER - )} - - - {usedBy ? ( - - - {`${usedBy?.firstName} ${usedBy?.lastName}`} - - - ) : ( - - {usedByEmail || PLACEHOLDER} - - )} - - - - - - { - event.stopPropagation(); - openDeleteDialog(id); - }} - /> - -
- ); - }, - () => ( - - - - - - ), - )} -
-
-
- ); -}; - -export default GiftCardsListTable; diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableFooter.tsx b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableFooter.tsx deleted file mode 100644 index aea7120ec45..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableFooter.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// @ts-strict-ignore -import TablePagination from "@dashboard/components/TablePagination"; -import TableRowLink from "@dashboard/components/TableRowLink"; -import usePaginator from "@dashboard/hooks/usePaginator"; -import { TableFooter } from "@material-ui/core"; -import React from "react"; - -import { useGiftCardList } from "../providers/GiftCardListProvider"; - -const GiftCardsListTableFooter: React.FC = () => { - const { - settings, - updateListSettings, - pageInfo: apiPageInfo, - paginationState, - params, - numberOfColumns, - } = useGiftCardList(); - - const paginationValues = usePaginator({ - pageInfo: apiPageInfo, - paginationState, - queryString: params, - }); - - return ( - - - - - - ); -}; - -export default GiftCardsListTableFooter; diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/GiftCardsListTableHeader.tsx b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/GiftCardsListTableHeader.tsx deleted file mode 100644 index 9019fb8466f..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/GiftCardsListTableHeader.tsx +++ /dev/null @@ -1,141 +0,0 @@ -// @ts-strict-ignore -import DeleteIconButton from "@dashboard/components/DeleteIconButton"; -import TableCellHeader, { - TableCellHeaderArrowDirection, - TableCellHeaderProps, -} from "@dashboard/components/TableCellHeader"; -import TableHead from "@dashboard/components/TableHead"; -import TooltipTableCellHeader from "@dashboard/components/TooltipTableCellHeader"; -import { commonTooltipMessages } from "@dashboard/components/TooltipTableCellHeader/messages"; -import Label, { - LabelSizes, -} from "@dashboard/orders/components/OrderHistory/Label"; -import { getArrowDirection } from "@dashboard/utils/sort"; -import { TableCell } from "@material-ui/core"; -import React from "react"; -import { MessageDescriptor, useIntl } from "react-intl"; - -import { messages as filterLabels } from "../../GiftCardListSearchAndFilters/filters"; -import { giftCardsListTableMessages as messages } from "../../messages"; -import { useGiftCardListDialogs } from "../../providers/GiftCardListDialogsProvider"; -import { useGiftCardList } from "../../providers/GiftCardListProvider"; -import { canBeSorted } from "../../sort"; -import { useTableStyles as useStyles } from "../../styles"; -import { GiftCardUrlSortField } from "../../types"; -import BulkEnableDisableSection from "./BulkEnableDisableSection"; - -interface HeaderItem { - title?: MessageDescriptor; - options?: TableCellHeaderProps; - onClick?: () => void; - direction?: TableCellHeaderArrowDirection; -} - -interface GiftCardsListTableHeaderProps { - isCurrencySelected: boolean; -} - -const GiftCardsListTableHeader: React.FC = ({ - isCurrencySelected, -}) => { - const intl = useIntl(); - const classes = useStyles({}); - - const { giftCards, numberOfColumns, loading, toggleAll, listElements } = - useGiftCardList(); - const { openDeleteDialog } = useGiftCardListDialogs(); - const { onSort, sort } = useGiftCardList(); - - const getDirection = (sortField: GiftCardUrlSortField) => - sort.sort === sortField ? getArrowDirection(sort.asc) : undefined; - - const headerItems: HeaderItem[] = [ - { - title: messages.giftCardsTableColumnGiftCardTitle, - options: { - className: classes.colCardCode, - textAlign: "left", - }, - }, - { - title: messages.giftCardsTableColumnTagTitle, - }, - { - title: messages.giftCardsTableColumnProductTitle, - onClick: () => onSort(GiftCardUrlSortField.product), - direction: getDirection(GiftCardUrlSortField.product), - }, - { - title: messages.giftCardsTableColumnCustomerTitle, - onClick: () => onSort(GiftCardUrlSortField.usedBy), - direction: getDirection(GiftCardUrlSortField.usedBy), - }, - ]; - - const headerTooltipItem: HeaderItem & { - disabled: boolean; - tooltip: string | React.ReactNodeArray; - } = { - title: messages.giftCardsTableColumnBalanceTitle, - options: { - className: classes.colBalance, - textAlign: "right", - }, - onClick: () => onSort(GiftCardUrlSortField.balance), - direction: getDirection(GiftCardUrlSortField.balance), - disabled: !canBeSorted(GiftCardUrlSortField.balance, isCurrencySelected), - tooltip: intl.formatMessage(commonTooltipMessages.noFilterSelected, { - filterName: {filterLabels.currencyLabel.defaultMessage}, - }), - }; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { title, ...headerTooltipItemProps } = headerTooltipItem; - - return ( - <> - - - - - - - - - - - - openDeleteDialog()} /> - - } - > - {headerItems.map(({ title, options, onClick, direction }) => ( - - - ))} - - - - - - ); -}; - -export default GiftCardsListTableHeader; diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/index.tsx b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/index.tsx deleted file mode 100644 index 188987ab3d2..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./GiftCardsListTableHeader"; -export { default } from "./GiftCardsListTableHeader"; diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts deleted file mode 100644 index 818f1d967c5..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { defineMessages } from "react-intl"; - -export const bulkEnableDisableSectionMessages = defineMessages({ - enableLabel: { - id: "hz+9ES", - defaultMessage: "Activate", - description: "bulk activate label", - }, - disableLabel: { - id: "IzEVek", - defaultMessage: "Deactivate", - description: "bulk disable label", - }, - deleteLabel: { - id: "qkt/Km", - defaultMessage: "Delete", - description: "bulk delete label", - }, - successActivateAlertText: { - id: "IwEQvz", - defaultMessage: - "Successfully activated gift {count,plural,one{card} other{cards}}", - description: "success activate alert message", - }, - successDeactivateAlertText: { - id: "SO56cv", - defaultMessage: - "Successfully deactivated gift {count,plural,one{card} other{cards}}", - description: "success deactivate alert message", - }, - errorActivateAlertText: { - id: "KcsJKm", - defaultMessage: - "Error activating gift {count,plural,one{card} other{cards}}", - description: "error with activatation alert message", - }, - errorDeactivateAlertText: { - id: "C90nKP", - defaultMessage: - "Errors deactivating gift {count,plural,one{card} other{cards}}", - description: "error with deactivatation alert message", - }, -}); diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/mutations.ts b/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/mutations.ts deleted file mode 100644 index 3f8b60419dd..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/mutations.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { gql } from "@apollo/client"; - -export const giftCardBulkActivate = gql` - mutation GiftCardBulkActivate($ids: [ID!]!) { - giftCardBulkActivate(ids: $ids) { - errors { - ...GiftCardError - } - count - } - } -`; - -export const giftCardBulkDeactivate = gql` - mutation GiftCardBulkDeactivate($ids: [ID!]!) { - giftCardBulkDeactivate(ids: $ids) { - errors { - ...GiftCardError - } - count - } - } -`; diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/index.tsx b/src/giftCards/GiftCardsList/GiftCardsListTable/index.tsx deleted file mode 100644 index 20b3b4347ee..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./GiftCardsListTable"; -export { default } from "./GiftCardsListTable"; diff --git a/src/giftCards/GiftCardsList/GiftCardsListTable/utils.ts b/src/giftCards/GiftCardsList/GiftCardsListTable/utils.ts deleted file mode 100644 index ede6a00726a..00000000000 --- a/src/giftCards/GiftCardsList/GiftCardsListTable/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { PLACEHOLDER } from "@dashboard/giftCards/GiftCardUpdate/types"; -import { GiftCardDataFragment } from "@dashboard/graphql"; - -export const getTagCellText = (tags: GiftCardDataFragment["tags"]) => { - if (!!tags.length) { - return tags.map(({ name }) => name).join(", "); - } - - return PLACEHOLDER; -}; diff --git a/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/filters.ts b/src/giftCards/GiftCardsList/filters.ts similarity index 98% rename from src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/filters.ts rename to src/giftCards/GiftCardsList/filters.ts index 76ef84dbe88..dcff9202c5b 100644 --- a/src/giftCards/GiftCardsList/GiftCardListSearchAndFilters/filters.ts +++ b/src/giftCards/GiftCardsList/filters.ts @@ -26,12 +26,12 @@ import { } from "@dashboard/utils/maps"; import { defineMessages, IntlShape } from "react-intl"; -import { GiftCardListUrlQueryParams } from "../types"; import { GiftCardListFilterKeys, GiftCardListFilterOpts, GiftCardListUrlFilters, GiftCardListUrlFiltersEnum, + GiftCardListUrlQueryParams, GiftCardStatusFilterEnum, SearchWithFetchMoreProps, } from "./types"; @@ -337,8 +337,7 @@ export function createFilterStructure( ]; } -export const { deleteFilterTab, getFilterTabs, saveFilterTab } = - createFilterTabUtils(GIFT_CARD_FILTERS_KEY); +export const storageUtils = createFilterTabUtils(GIFT_CARD_FILTERS_KEY); export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } = createFilterUtils( diff --git a/src/giftCards/GiftCardsList/messages.ts b/src/giftCards/GiftCardsList/messages.ts index e570513c793..78ad4a134da 100644 --- a/src/giftCards/GiftCardsList/messages.ts +++ b/src/giftCards/GiftCardsList/messages.ts @@ -111,3 +111,45 @@ export const giftCardUpdateFormMessages = defineMessages({ description: "invalid date in expirydate field content", }, }); + +export const bulkEnableDisableSectionMessages = defineMessages({ + enableLabel: { + id: "hz+9ES", + defaultMessage: "Activate", + description: "bulk activate label", + }, + disableLabel: { + id: "IzEVek", + defaultMessage: "Deactivate", + description: "bulk disable label", + }, + deleteLabel: { + id: "qkt/Km", + defaultMessage: "Delete", + description: "bulk delete label", + }, + successActivateAlertText: { + id: "IwEQvz", + defaultMessage: + "Successfully activated gift {count,plural,one{card} other{cards}}", + description: "success activate alert message", + }, + successDeactivateAlertText: { + id: "SO56cv", + defaultMessage: + "Successfully deactivated gift {count,plural,one{card} other{cards}}", + description: "success deactivate alert message", + }, + errorActivateAlertText: { + id: "KcsJKm", + defaultMessage: + "Error activating gift {count,plural,one{card} other{cards}}", + description: "error with activatation alert message", + }, + errorDeactivateAlertText: { + id: "C90nKP", + defaultMessage: + "Errors deactivating gift {count,plural,one{card} other{cards}}", + description: "error with deactivatation alert message", + }, +}); diff --git a/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx b/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx index 147204d1dea..da13ad1a712 100644 --- a/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx +++ b/src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import GiftCardListPageDeleteDialog from "@dashboard/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog"; import GiftCardBulkCreateDialog from "@dashboard/giftCards/GiftCardBulkCreateDialog"; import GiftCardCreateDialogContent from "@dashboard/giftCards/GiftCardCreateDialog"; @@ -32,10 +31,19 @@ export interface GiftCardListDialogsConsumerProps { } export const GiftCardListDialogsContext = - createContext(null); + createContext(null); -export const useGiftCardListDialogs = () => - useContext(GiftCardListDialogsContext); +export const useGiftCardListDialogs = () => { + const context = useContext(GiftCardListDialogsContext); + + if (!context) { + throw new Error( + "You are trying to use GiftCardListDialogsContext outside of its provider", + ); + } + + return context; +}; const GiftCardListDialogsProvider: React.FC< GiftCardListDialogsProviderProps @@ -57,8 +65,8 @@ const GiftCardListDialogsProvider: React.FC< const isDialogOpen = (type: GiftCardListActionParamsEnum) => params?.action === type; - const handleDeleteDialogOpen = (id?: string) => { - openDialog(DELETE, id ? { id } : undefined); + const handleDeleteDialogOpen = () => { + openDialog(DELETE); }; const openSearchDeleteDialog = () => @@ -75,7 +83,7 @@ const GiftCardListDialogsProvider: React.FC< openSearchSaveDialog, openSearchDeleteDialog, onClose, - id, + id: id ?? "", }; return ( diff --git a/src/giftCards/GiftCardsList/providers/GiftCardListProvider/GiftCardListProvider.tsx b/src/giftCards/GiftCardsList/providers/GiftCardListProvider/GiftCardListProvider.tsx index 72c29fc2191..8a969305853 100644 --- a/src/giftCards/GiftCardsList/providers/GiftCardListProvider/GiftCardListProvider.tsx +++ b/src/giftCards/GiftCardsList/providers/GiftCardListProvider/GiftCardListProvider.tsx @@ -1,5 +1,5 @@ -// @ts-strict-ignore import { ApolloError } from "@apollo/client"; +import { IFilter } from "@dashboard/components/Filter"; import { ExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; import { getExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/utils"; import { giftCardListUrl } from "@dashboard/giftCards/urls"; @@ -8,9 +8,10 @@ import { GiftCardListQueryVariables, useGiftCardListQuery, } from "@dashboard/graphql"; -import useBulkActions, { - UseBulkActionsProps, -} from "@dashboard/hooks/useBulkActions"; +import { + UseFilterPresets, + useFilterPresets, +} from "@dashboard/hooks/useFilterPresets"; import useListSettings, { UseListSettings, } from "@dashboard/hooks/useListSettings"; @@ -22,13 +23,28 @@ import { PageInfo, PaginationState, } from "@dashboard/hooks/usePaginator"; +import { + UseRowSelection, + useRowSelection, +} from "@dashboard/hooks/useRowSelection"; import { ListViews, SortPage } from "@dashboard/types"; +import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers"; import createSortHandler from "@dashboard/utils/handlers/sortHandler"; import { mapEdgesToItems } from "@dashboard/utils/maps"; import { getSortParams } from "@dashboard/utils/sort"; -import React, { createContext, useContext } from "react"; +import React, { + createContext, + Dispatch, + SetStateAction, + useContext, + useState, +} from "react"; -import { getFilterVariables } from "../../GiftCardListSearchAndFilters/filters"; +import { + getFilterQueryParam, + getFilterVariables, + storageUtils, +} from "../../filters"; import { GiftCardListColummns, GiftCardListUrlQueryParams, @@ -44,25 +60,42 @@ interface GiftCardsListProviderProps { } export interface GiftCardsListConsumerProps - extends UseBulkActionsProps, + extends UseFilterPresets, + UseRowSelection, UseListSettings, SortPage { giftCards: Array< - ExtendedGiftCard + ExtendedGiftCard< + NonNullable["edges"][0]["node"] + > >; - pageInfo: PageInfo; + pageInfo?: PageInfo; loading: boolean; params: GiftCardListUrlQueryParams; paginationState: PaginationState; numberOfColumns: number; totalCount: number; - selectedItemsCount: number; + changeFilters: (filter: IFilter) => void; + resetFilters: () => void; + handleSearchChange: (query: string) => void; + isFilterPresetOpen: boolean; + setFilterPresetOpen: Dispatch>; } export const GiftCardsListContext = - createContext(null); + createContext(null); -export const useGiftCardList = () => useContext(GiftCardsListContext); +export const useGiftCardList = () => { + const context = useContext(GiftCardsListContext); + + if (!context) { + throw new Error( + "You are trying to use GiftCardsListContext outside of its provider", + ); + } + + return context; +}; export const GiftCardsListProvider: React.FC = ({ children, @@ -71,9 +104,25 @@ export const GiftCardsListProvider: React.FC = ({ const navigate = useNavigator(); const notify = useNotifier(); - const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( - [], - ); + const [isFilterPresetOpen, setFilterPresetOpen] = useState(false); + const { clearRowSelection, ...rowSelectionUtils } = useRowSelection(params); + + const filterUtils = useFilterPresets({ + reset: clearRowSelection, + params, + getUrl: giftCardListUrl, + storageUtils, + }); + + const [changeFilters, resetFilters, handleSearchChange] = + createFilterHandlers({ + createUrl: giftCardListUrl, + getFilterQueryParam, + navigate, + params, + cleanupFn: clearRowSelection, + keepActiveTab: true, + }); const { updateListSettings, settings } = useListSettings(ListViews.GIFT_CARD_LIST); @@ -110,7 +159,8 @@ export const GiftCardsListProvider: React.FC = ({ handleError: handleGiftCardListError, }); - const giftCards = mapEdgesToItems(data?.giftCards)?.map(getExtendedGiftCard); + const giftCards = + mapEdgesToItems(data?.giftCards)?.map(getExtendedGiftCard) ?? []; const providerValues: GiftCardsListConsumerProps = { onSort: handleSort, @@ -118,18 +168,20 @@ export const GiftCardsListProvider: React.FC = ({ giftCards, totalCount: data?.giftCards?.totalCount || 0, loading, - isSelected, - listElements, - reset, - toggleAll, - toggle, - selectedItemsCount: listElements.length, + clearRowSelection, + ...rowSelectionUtils, + ...filterUtils, pageInfo: data?.giftCards?.pageInfo, paginationState, params, settings, updateListSettings, numberOfColumns, + changeFilters, + resetFilters, + handleSearchChange, + isFilterPresetOpen, + setFilterPresetOpen, }; return ( diff --git a/src/giftCards/GiftCardsList/styles.ts b/src/giftCards/GiftCardsList/styles.ts deleted file mode 100644 index 9637f134d88..00000000000 --- a/src/giftCards/GiftCardsList/styles.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { makeStyles } from "@saleor/macaw-ui"; - -export const useTableStyles = makeStyles( - theme => ({ - cardCodeContainer: { - display: "flex", - alignItems: "baseline", - }, - colCardCode: { - paddingLeft: 0, - width: 400, - }, - colDelete: { - width: 88, - }, - colBalance: { - width: 135, - }, - colProduct: { - width: 250, - }, - colBase: { - width: 150, - }, - pill: { - display: "block", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - maxWidth: "min-content", - overflow: "hidden", - }, - row: { - cursor: "pointer", - height: 70, - "& td, & th": { - height: "auto", - }, - }, - skeleton: { - paddingLeft: 0, - }, - toolbar: { - display: "flex", - gap: theme.spacing(), - marginRight: theme.spacing(-0.5), - }, - }), - { name: "GiftCardsListTable" }, -); - -export const useHeaderStyles = makeStyles( - theme => ({ - alertLink: { - fontSize: theme.typography.body2.fontSize, - }, - }), - { name: "GiftCardsListHeader" }, -); diff --git a/src/giftCards/GiftCardsList/types.ts b/src/giftCards/GiftCardsList/types.ts index 5c4a055d91b..321be26686a 100644 --- a/src/giftCards/GiftCardsList/types.ts +++ b/src/giftCards/GiftCardsList/types.ts @@ -1,14 +1,19 @@ import { ActiveTab, + AutocompleteFilterOpts, Dialog, + FetchMoreProps, + FilterOpts, + Filters, + FiltersWithMultipleValues, + MinMax, Pagination, Search, + SearchProps, SingleAction, Sort, } from "@dashboard/types"; -import { GiftCardListUrlFilters } from "./GiftCardListSearchAndFilters/types"; - export type GiftCardListColummns = | "giftCardCode" | "tag" @@ -40,3 +45,51 @@ export type GiftCardListUrlQueryParams = Pagination & GiftCardUrlSort & ActiveTab & Search; + +export enum GiftCardListUrlFiltersEnum { + currency = "currency", + initialBalanceAmountFrom = "initialBalanceAmountFrom", + initialBalanceAmountTo = "initialBalanceAmountTo", + currentBalanceAmountFrom = "currentBalanceAmountFrom", + currentBalanceAmountTo = "currentBalanceAmountTo", + status = "status", +} + +export enum GiftCardListUrlFiltersWithMultipleValuesEnum { + tag = "tag", + product = "product", + usedBy = "usedBy", +} + +export enum GiftCardListFilterKeys { + currency = "currency", + balance = "balance", + initialBalance = "initialBalance", + currentBalance = "currentBalance", + initialBalanceAmount = "initialBalanceAmount", + currentBalanceAmount = "currentBalanceAmount", + tag = "tag", + product = "product", + usedBy = "usedBy", + status = "status", +} + +export type GiftCardListUrlFilters = Filters & + FiltersWithMultipleValues; + +export interface GiftCardListFilterOpts { + tag: FilterOpts & AutocompleteFilterOpts; + currency: FilterOpts & AutocompleteFilterOpts; + product: FilterOpts & AutocompleteFilterOpts; + usedBy: FilterOpts & AutocompleteFilterOpts; + initialBalanceAmount: FilterOpts; + currentBalanceAmount: FilterOpts; + status: FilterOpts; +} + +export type SearchWithFetchMoreProps = FetchMoreProps & Search & SearchProps; + +export enum GiftCardStatusFilterEnum { + enabled = "enabled", + disabled = "disabled", +} diff --git a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx index 49ce7d96b8f..fc200aa7c7e 100644 --- a/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx +++ b/src/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCardListItem.tsx @@ -1,6 +1,8 @@ import CardMenu, { CardMenuItem } from "@dashboard/components/CardMenu"; -import { bulkEnableDisableSectionMessages } from "@dashboard/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; -import { giftCardsListTableMessages } from "@dashboard/giftCards/GiftCardsList/messages"; +import { + bulkEnableDisableSectionMessages, + giftCardsListTableMessages, +} from "@dashboard/giftCards/GiftCardsList/messages"; import useGiftCardActivateToggle from "@dashboard/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle"; import { ExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; import { CustomerGiftCardFragment } from "@dashboard/graphql"; diff --git a/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx b/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx index 900b5c22382..1b5e083e8dd 100644 --- a/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx +++ b/src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import ActionDialog, { ActionDialogProps, } from "@dashboard/components/ActionDialog"; @@ -34,10 +33,10 @@ export interface GiftCardDeleteDialogContentProps< Partial< Pick< GiftCardsListConsumerProps, - "listElements" | "selectedItemsCount" | "giftCards" | "loading" + "selectedRowIds" | "giftCards" | "loading" > > { - id?: string; + ids?: string[]; giftCard?: TGiftCard; singleDeletion: boolean; } @@ -45,14 +44,13 @@ export interface GiftCardDeleteDialogContentProps< function GiftCardDeleteDialogContent< TGiftCard extends DeleteDialogContentGiftCard, >({ - id, + ids, open, onClose, onConfirm, confirmButtonState, singleDeletion, - selectedItemsCount: listSelectedItemsCount, - listElements, + selectedRowIds, giftCards, giftCard, loading, @@ -62,7 +60,7 @@ function GiftCardDeleteDialogContent< const [isConsentChecked, setConsentChecked] = useState(false); - const selectedItemsCount = listSelectedItemsCount || SINGLE; + const selectedItemsCount = selectedRowIds?.length || SINGLE; useEffect(() => { if (!open) { @@ -75,17 +73,17 @@ function GiftCardDeleteDialogContent< return false; } - return listElements?.some(hasSelectedGiftCardBalance); + return selectedRowIds?.some(hasSelectedGiftCardBalance); }; const hasSelectedGiftCardBalance = (id: string) => { const card = giftCards?.find(getById(id)) || giftCard; - return card?.currentBalance?.amount > 0; + return (card?.currentBalance?.amount ?? 0) > 0; }; const deletingCardsWithBalance = singleDeletion - ? hasSelectedGiftCardBalance(id) + ? hasSelectedGiftCardBalance(ids?.[0] ?? "") : hasSelectedAnyGiftCardsWithBalance(); const submitEnabled = deletingCardsWithBalance ? isConsentChecked : true; diff --git a/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx b/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx index 43e2fb35627..375c17736f6 100644 --- a/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx +++ b/src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx @@ -1,5 +1,4 @@ import { ActionDialogProps } from "@dashboard/components/ActionDialog"; -import { useGiftCardListDialogs } from "@dashboard/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider"; import { useGiftCardList } from "@dashboard/giftCards/GiftCardsList/providers/GiftCardListProvider"; import { GIFT_CARD_LIST_QUERY } from "@dashboard/giftCards/GiftCardsList/queries"; import { DialogProps } from "@dashboard/types"; @@ -21,14 +20,12 @@ const GiftCardDeleteDialog: React.FC = ({ refetchQueries = [], }) => { const listProps = useGiftCardList(); - const { giftCards, loading, selectedItemsCount } = listProps; + const { giftCards, loading, selectedRowIds, clearRowSelection } = listProps; - const { id } = useGiftCardListDialogs(); - - const singleDeletion = !!id || selectedItemsCount === SINGLE; + const singleDeletion = selectedRowIds.length === SINGLE; const { onDeleteGiftCard, deleteGiftCardOpts } = useGiftCardSingleDelete({ - id, + id: selectedRowIds[0], onClose, refetchQueries: [GIFT_CARD_LIST_QUERY, ...refetchQueries], }); @@ -42,13 +39,19 @@ const GiftCardDeleteDialog: React.FC = ({ const dialogProps: Pick< ActionDialogProps, "onConfirm" | "confirmButtonState" - > = !!id + > = singleDeletion ? { - onConfirm: onDeleteGiftCard, + onConfirm: () => { + onDeleteGiftCard(); + clearRowSelection(); + }, confirmButtonState: deleteGiftCardOpts?.status, } : { - onConfirm: onBulkDeleteGiftCards, + onConfirm: () => { + onBulkDeleteGiftCards(); + clearRowSelection(); + }, confirmButtonState: bulkDeleteGiftCardOpts?.status, }; @@ -56,7 +59,7 @@ const GiftCardDeleteDialog: React.FC = ({ { const errors = data?.giftCardBulkDelete?.errors; - if (!errors.length) { + if (!errors?.length) { notify({ status: "success", text: intl.formatMessage(messages.deleteSuccessAlertText, { - selectedItemsCount, + selectedItemsCount: selectedRowIds.length, }), }); onClose(); - resetSelectedItems(); + clearRowSelection(); return; } @@ -59,7 +54,7 @@ const useGiftCardBulkDelete = ({ }); const onBulkDeleteGiftCards = () => - bulkDeleteGiftCard({ variables: { ids: listElements } }); + bulkDeleteGiftCard({ variables: { ids: selectedRowIds } }); return { onBulkDeleteGiftCards, diff --git a/src/graphql/hooks.generated.ts b/src/graphql/hooks.generated.ts index 0881ad74dcd..144869870ee 100644 --- a/src/graphql/hooks.generated.ts +++ b/src/graphql/hooks.generated.ts @@ -8206,77 +8206,6 @@ export function useGiftCardAddNoteMutation(baseOptions?: ApolloReactHooks.Mutati export type GiftCardAddNoteMutationHookResult = ReturnType; export type GiftCardAddNoteMutationResult = Apollo.MutationResult; export type GiftCardAddNoteMutationOptions = Apollo.BaseMutationOptions; -export const GiftCardDetailsDocument = gql` - query GiftCardDetails($id: ID!) { - giftCard(id: $id) { - ...GiftCardData - events { - ...GiftCardEvent - } - } -} - ${GiftCardDataFragmentDoc} -${GiftCardEventFragmentDoc}`; - -/** - * __useGiftCardDetailsQuery__ - * - * To run a query within a React component, call `useGiftCardDetailsQuery` and pass it any options that fit your needs. - * When your component renders, `useGiftCardDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGiftCardDetailsQuery({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useGiftCardDetailsQuery(baseOptions: ApolloReactHooks.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useQuery(GiftCardDetailsDocument, options); - } -export function useGiftCardDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useLazyQuery(GiftCardDetailsDocument, options); - } -export type GiftCardDetailsQueryHookResult = ReturnType; -export type GiftCardDetailsLazyQueryHookResult = ReturnType; -export type GiftCardDetailsQueryResult = Apollo.QueryResult; -export const GiftCardCurrenciesDocument = gql` - query GiftCardCurrencies { - giftCardCurrencies -} - `; - -/** - * __useGiftCardCurrenciesQuery__ - * - * To run a query within a React component, call `useGiftCardCurrenciesQuery` and pass it any options that fit your needs. - * When your component renders, `useGiftCardCurrenciesQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGiftCardCurrenciesQuery({ - * variables: { - * }, - * }); - */ -export function useGiftCardCurrenciesQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useQuery(GiftCardCurrenciesDocument, options); - } -export function useGiftCardCurrenciesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useLazyQuery(GiftCardCurrenciesDocument, options); - } -export type GiftCardCurrenciesQueryHookResult = ReturnType; -export type GiftCardCurrenciesLazyQueryHookResult = ReturnType; -export type GiftCardCurrenciesQueryResult = Apollo.QueryResult; export const GiftCardBulkActivateDocument = gql` mutation GiftCardBulkActivate($ids: [ID!]!) { giftCardBulkActivate(ids: $ids) { @@ -8349,6 +8278,77 @@ export function useGiftCardBulkDeactivateMutation(baseOptions?: ApolloReactHooks export type GiftCardBulkDeactivateMutationHookResult = ReturnType; export type GiftCardBulkDeactivateMutationResult = Apollo.MutationResult; export type GiftCardBulkDeactivateMutationOptions = Apollo.BaseMutationOptions; +export const GiftCardDetailsDocument = gql` + query GiftCardDetails($id: ID!) { + giftCard(id: $id) { + ...GiftCardData + events { + ...GiftCardEvent + } + } +} + ${GiftCardDataFragmentDoc} +${GiftCardEventFragmentDoc}`; + +/** + * __useGiftCardDetailsQuery__ + * + * To run a query within a React component, call `useGiftCardDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useGiftCardDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGiftCardDetailsQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useGiftCardDetailsQuery(baseOptions: ApolloReactHooks.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useQuery(GiftCardDetailsDocument, options); + } +export function useGiftCardDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useLazyQuery(GiftCardDetailsDocument, options); + } +export type GiftCardDetailsQueryHookResult = ReturnType; +export type GiftCardDetailsLazyQueryHookResult = ReturnType; +export type GiftCardDetailsQueryResult = Apollo.QueryResult; +export const GiftCardCurrenciesDocument = gql` + query GiftCardCurrencies { + giftCardCurrencies +} + `; + +/** + * __useGiftCardCurrenciesQuery__ + * + * To run a query within a React component, call `useGiftCardCurrenciesQuery` and pass it any options that fit your needs. + * When your component renders, `useGiftCardCurrenciesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGiftCardCurrenciesQuery({ + * variables: { + * }, + * }); + */ +export function useGiftCardCurrenciesQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useQuery(GiftCardCurrenciesDocument, options); + } +export function useGiftCardCurrenciesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useLazyQuery(GiftCardCurrenciesDocument, options); + } +export type GiftCardCurrenciesQueryHookResult = ReturnType; +export type GiftCardCurrenciesLazyQueryHookResult = ReturnType; +export type GiftCardCurrenciesQueryResult = Apollo.QueryResult; export const DeleteGiftCardDocument = gql` mutation DeleteGiftCard($id: ID!) { giftCardDelete(id: $id) { diff --git a/src/graphql/types.generated.ts b/src/graphql/types.generated.ts index 568c0aa8877..71fb527e83b 100644 --- a/src/graphql/types.generated.ts +++ b/src/graphql/types.generated.ts @@ -9439,18 +9439,6 @@ export type GiftCardAddNoteMutationVariables = Exact<{ export type GiftCardAddNoteMutation = { __typename: 'Mutation', giftCardAddNote: { __typename: 'GiftCardAddNote', errors: Array<{ __typename: 'GiftCardError', code: GiftCardErrorCode, field: string | null, message: string | null }>, giftCard: { __typename: 'GiftCard', last4CodeChars: string, boughtInChannel: string | null, usedByEmail: string | null, createdByEmail: string | null, created: any, expiryDate: any | null, lastUsedOn: any | null, isActive: boolean, id: string, createdBy: { __typename: 'User', id: string, firstName: string, lastName: string } | null, product: { __typename: 'Product', id: string, name: string } | null, usedBy: { __typename: 'User', id: string, firstName: string, lastName: string } | null, app: { __typename: 'App', id: string, name: string | null } | null, initialBalance: { __typename: 'Money', amount: number, currency: string }, currentBalance: { __typename: 'Money', amount: number, currency: string }, tags: Array<{ __typename: 'GiftCardTag', name: string }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }> } | null, event: { __typename: 'GiftCardEvent', expiryDate: any | null, oldExpiryDate: any | null, id: string, date: any | null, type: GiftCardEventsEnum | null, message: string | null, email: string | null, orderId: string | null, orderNumber: string | null, tags: Array | null, oldTags: Array | null, user: { __typename: 'User', email: string, id: string, firstName: string, lastName: string } | null, app: { __typename: 'App', id: string, name: string | null } | null, balance: { __typename: 'GiftCardEventBalance', initialBalance: { __typename: 'Money', amount: number, currency: string } | null, currentBalance: { __typename: 'Money', amount: number, currency: string }, oldInitialBalance: { __typename: 'Money', amount: number, currency: string } | null, oldCurrentBalance: { __typename: 'Money', amount: number, currency: string } | null } | null } | null } | null }; -export type GiftCardDetailsQueryVariables = Exact<{ - id: Scalars['ID']; -}>; - - -export type GiftCardDetailsQuery = { __typename: 'Query', giftCard: { __typename: 'GiftCard', last4CodeChars: string, boughtInChannel: string | null, usedByEmail: string | null, createdByEmail: string | null, created: any, expiryDate: any | null, lastUsedOn: any | null, isActive: boolean, id: string, events: Array<{ __typename: 'GiftCardEvent', expiryDate: any | null, oldExpiryDate: any | null, id: string, date: any | null, type: GiftCardEventsEnum | null, message: string | null, email: string | null, orderId: string | null, orderNumber: string | null, tags: Array | null, oldTags: Array | null, user: { __typename: 'User', email: string, id: string, firstName: string, lastName: string } | null, app: { __typename: 'App', id: string, name: string | null } | null, balance: { __typename: 'GiftCardEventBalance', initialBalance: { __typename: 'Money', amount: number, currency: string } | null, currentBalance: { __typename: 'Money', amount: number, currency: string }, oldInitialBalance: { __typename: 'Money', amount: number, currency: string } | null, oldCurrentBalance: { __typename: 'Money', amount: number, currency: string } | null } | null }>, createdBy: { __typename: 'User', id: string, firstName: string, lastName: string } | null, product: { __typename: 'Product', id: string, name: string } | null, usedBy: { __typename: 'User', id: string, firstName: string, lastName: string } | null, app: { __typename: 'App', id: string, name: string | null } | null, initialBalance: { __typename: 'Money', amount: number, currency: string }, currentBalance: { __typename: 'Money', amount: number, currency: string }, tags: Array<{ __typename: 'GiftCardTag', name: string }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }> } | null }; - -export type GiftCardCurrenciesQueryVariables = Exact<{ [key: string]: never; }>; - - -export type GiftCardCurrenciesQuery = { __typename: 'Query', giftCardCurrencies: Array }; - export type GiftCardBulkActivateMutationVariables = Exact<{ ids: Array | Scalars['ID']; }>; @@ -9465,6 +9453,18 @@ export type GiftCardBulkDeactivateMutationVariables = Exact<{ export type GiftCardBulkDeactivateMutation = { __typename: 'Mutation', giftCardBulkDeactivate: { __typename: 'GiftCardBulkDeactivate', count: number, errors: Array<{ __typename: 'GiftCardError', code: GiftCardErrorCode, field: string | null, message: string | null }> } | null }; +export type GiftCardDetailsQueryVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type GiftCardDetailsQuery = { __typename: 'Query', giftCard: { __typename: 'GiftCard', last4CodeChars: string, boughtInChannel: string | null, usedByEmail: string | null, createdByEmail: string | null, created: any, expiryDate: any | null, lastUsedOn: any | null, isActive: boolean, id: string, events: Array<{ __typename: 'GiftCardEvent', expiryDate: any | null, oldExpiryDate: any | null, id: string, date: any | null, type: GiftCardEventsEnum | null, message: string | null, email: string | null, orderId: string | null, orderNumber: string | null, tags: Array | null, oldTags: Array | null, user: { __typename: 'User', email: string, id: string, firstName: string, lastName: string } | null, app: { __typename: 'App', id: string, name: string | null } | null, balance: { __typename: 'GiftCardEventBalance', initialBalance: { __typename: 'Money', amount: number, currency: string } | null, currentBalance: { __typename: 'Money', amount: number, currency: string }, oldInitialBalance: { __typename: 'Money', amount: number, currency: string } | null, oldCurrentBalance: { __typename: 'Money', amount: number, currency: string } | null } | null }>, createdBy: { __typename: 'User', id: string, firstName: string, lastName: string } | null, product: { __typename: 'Product', id: string, name: string } | null, usedBy: { __typename: 'User', id: string, firstName: string, lastName: string } | null, app: { __typename: 'App', id: string, name: string | null } | null, initialBalance: { __typename: 'Money', amount: number, currency: string }, currentBalance: { __typename: 'Money', amount: number, currency: string }, tags: Array<{ __typename: 'GiftCardTag', name: string }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }> } | null }; + +export type GiftCardCurrenciesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GiftCardCurrenciesQuery = { __typename: 'Query', giftCardCurrencies: Array }; + export type DeleteGiftCardMutationVariables = Exact<{ id: Scalars['ID']; }>; diff --git a/src/hooks/useFilterPresets/useFilterPresets.ts b/src/hooks/useFilterPresets/useFilterPresets.ts index 6995b94e730..f64648e21e3 100644 --- a/src/hooks/useFilterPresets/useFilterPresets.ts +++ b/src/hooks/useFilterPresets/useFilterPresets.ts @@ -4,11 +4,23 @@ import { getActiveTabIndexAfterTabDelete, getNextUniqueTabName, } from "@dashboard/products/views/ProductList/utils"; -import { StorageUtils } from "@dashboard/utils/filters"; +import { GetFilterTabsOutput, StorageUtils } from "@dashboard/utils/filters"; import { prepareQs } from "@dashboard/utils/filters/qs"; import { stringify } from "qs"; import { useState } from "react"; +export interface UseFilterPresets { + presetIdToDelete: number | null; + setPresetIdToDelete: React.Dispatch>; + presets: GetFilterTabsOutput; + selectedPreset: number | undefined; + onPresetChange: (index: number) => void; + onPresetDelete: () => void; + onPresetSave: (data: SaveFilterTabDialogFormData) => void; + onPresetUpdate: (tabName: string) => void; + hasPresetsChange: () => boolean; +} + export const useFilterPresets = < T extends { activeTab?: string; action?: string }, >({ @@ -21,7 +33,7 @@ export const useFilterPresets = < reset: () => void; getUrl: () => string; storageUtils: StorageUtils; -}) => { +}): UseFilterPresets => { const navigate = useNavigator(); const baseUrl = getUrl(); const [presetIdToDelete, setPresetIdToDelete] = useState(null); diff --git a/src/hooks/useListSettings.ts b/src/hooks/useListSettings.ts index 58b4952f00a..8d2945ce91b 100644 --- a/src/hooks/useListSettings.ts +++ b/src/hooks/useListSettings.ts @@ -10,7 +10,7 @@ export interface UseListSettings { settings: ListSettings; updateListSettings: >( key: T, - value: ListSettings[T], + value: ListSettings[T], ) => void; } diff --git a/src/hooks/useRowSelection.ts b/src/hooks/useRowSelection.ts index b00939da5c6..5b0286d0ab9 100644 --- a/src/hooks/useRowSelection.ts +++ b/src/hooks/useRowSelection.ts @@ -1,7 +1,16 @@ import { Pagination } from "@dashboard/types"; import { useEffect, useRef, useState } from "react"; -export const useRowSelection = (paginationParams?: Pagination) => { +export interface UseRowSelection { + selectedRowIds: string[]; + setSelectedRowIds: React.Dispatch>; + clearRowSelection: () => void; + setClearDatagridRowSelectionCallback: (callback: () => void) => void; +} + +export const useRowSelection = ( + paginationParams?: Pagination, +): UseRowSelection => { const [selectedRowIds, setSelectedRowIds] = useState([]); // Keep reference to clear datagrid selection function diff --git a/src/index.css b/src/index.css index 7fcfbc95b10..40abad87c5f 100644 --- a/src/index.css +++ b/src/index.css @@ -45,6 +45,10 @@ body { background: none !important; } +.remove-content-padding-top .MuiCardContent-root { + padding-top: 0 !important; +} + [id*="ScrollableDialog"] .infinite-scroll-component { overflow: hidden !important; } diff --git a/src/types.ts b/src/types.ts index ba750b0fbf7..2e7fad4f533 100644 --- a/src/types.ts +++ b/src/types.ts @@ -43,7 +43,7 @@ export enum ListViews { WAREHOUSE_LIST = "WAREHOUSE_LIST", WEBHOOK_LIST = "WEBHOOK_LIST", TRANSLATION_ATTRIBUTE_VALUE_LIST = "TRANSLATION_ATTRIBUTE_VALUE_LIST", - GIFT_CARD_LIST = " GIFT_CARD_LIST", + GIFT_CARD_LIST = "GIFT_CARD_LIST", } export interface ListProps {