Introduce datagrid in grift cards list (#3894)

Co-authored-by: wojteknowacki <124166231+wojteknowacki@users.noreply.github.com>
This commit is contained in:
Paweł Chyła 2023-07-19 15:31:33 +02:00 committed by GitHub
parent 52f58eb00a
commit ce1854b2ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 1222 additions and 958 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---
Introduce datagrid on gift card list page

View file

@ -2,6 +2,7 @@
import faker from "faker"; import faker from "faker";
import { GIFT_CARD_LIST } from "../../../elements/catalog/giftCard/giftCardList"; import { GIFT_CARD_LIST } from "../../../elements/catalog/giftCard/giftCardList";
import { urlList } from "../../../fixtures/urlList";
import { completeCheckout } from "../../../support/api/requests/Checkout"; import { completeCheckout } from "../../../support/api/requests/Checkout";
import { import {
createGiftCard, createGiftCard,
@ -18,10 +19,7 @@ import {
} from "../../../support/api/utils/ordersUtils"; } from "../../../support/api/utils/ordersUtils";
import * as productsUtils from "../../../support/api/utils/products/productsUtils"; import * as productsUtils from "../../../support/api/utils/products/productsUtils";
import { updateTaxConfigurationForChannel } from "../../../support/api/utils/taxesUtils"; import { updateTaxConfigurationForChannel } from "../../../support/api/utils/taxesUtils";
import { import { giftCardsPage } from "../../../support/pages";
changeGiftCardActiveStatus,
enterAndSelectGiftCards,
} from "../../../support/pages/catalog/giftCardPage";
describe("As a admin I want to use enabled gift card in checkout", () => { describe("As a admin I want to use enabled gift card in checkout", () => {
const startsWith = "ActivateGiftCards"; const startsWith = "ActivateGiftCards";
@ -84,7 +82,7 @@ describe("As a admin I want to use enabled gift card in checkout", () => {
giftCardDeactivate(giftCard.id); giftCardDeactivate(giftCard.id);
}) })
.then(() => { .then(() => {
changeGiftCardActiveStatus(giftCard.id); giftCardsPage.changeGiftCardActiveStatus(giftCard.id);
dataForCheckout.voucherCode = giftCard.code; dataForCheckout.voucherCode = giftCard.code;
purchaseProductWithPromoCode(dataForCheckout); purchaseProductWithPromoCode(dataForCheckout);
}) })
@ -114,7 +112,7 @@ describe("As a admin I want to use enabled gift card in checkout", () => {
createGiftCard(giftCardData) createGiftCard(giftCardData)
.then(giftCardResp => { .then(giftCardResp => {
giftCard = giftCardResp; giftCard = giftCardResp;
changeGiftCardActiveStatus(giftCard.id); giftCardsPage.changeGiftCardActiveStatus(giftCard.id);
dataForCheckout.voucherCode = giftCard.code; dataForCheckout.voucherCode = giftCard.code;
createCheckoutWithDisabledGiftCard(dataForCheckout); 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", "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"] }, { tags: ["@giftCard", "@allEnv", "@stable"] },
() => { () => {
const firstGiftCardName = `${startsWith}${faker.datatype.number()}`; const firstGiftCardTag = faker.datatype.number();
const secondGiftCardName = `${startsWith}${faker.datatype.number()}`; const secondGiftCardTag = faker.datatype.number();
const amount = 10; const amount = 10;
let firstGiftCard; let firstGiftCard;
let firstGiftCardCode;
let secondGiftCard; let secondGiftCard;
let secondGiftCardCode;
cy.addAliasToGraphRequest("GiftCardBulkDeactivate");
createGiftCard({ createGiftCard({
tag: firstGiftCardName, tag: firstGiftCardTag,
amount, amount,
currency: "USD", currency: "USD",
}) })
.then(giftCard => { .then(giftCard => {
firstGiftCard = giftCard; firstGiftCard = giftCard;
firstGiftCardCode = firstGiftCard.code;
createGiftCard({ createGiftCard({
tag: secondGiftCardName, tag: secondGiftCardTag,
amount, amount,
currency: "USD", currency: "USD",
}); });
}) })
.then(giftCard => { .then(giftCard => {
secondGiftCard = giftCard; secondGiftCard = giftCard;
secondGiftCardCode = secondGiftCard.code;
cy.visit(
giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [
firstGiftCardTag,
secondGiftCardTag,
]),
);
enterAndSelectGiftCards([firstGiftCard.id, secondGiftCard.id]); giftCardsPage.selectGiftCardOnListView(secondGiftCardCode);
cy.addAliasToGraphRequest("GiftCardBulkDeactivate") giftCardsPage.selectGiftCardOnListView(firstGiftCardCode);
.get(GIFT_CARD_LIST.deactivateGiftCardButton) giftCardsPage.clickDeactivateButton();
.click() cy.waitForRequestAndCheckIfNoErrors(
.waitForRequestAndCheckIfNoErrors("@GiftCardBulkDeactivate") "@GiftCardBulkDeactivate",
.confirmationMessageShouldAppear(); ).confirmationMessageShouldAppear();
dataForCheckout.voucherCode = firstGiftCard.code; dataForCheckout.voucherCode = firstGiftCard.code;
createCheckoutWithDisabledGiftCard(dataForCheckout); createCheckoutWithDisabledGiftCard(dataForCheckout);
dataForCheckout.voucherCode = secondGiftCard.code; 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", "should be able to enable several gift cards on gift card list page and use it in checkout. TC: SALEOR_1012",
{ tags: ["@giftCard", "@allEnv"] }, { tags: ["@giftCard", "@allEnv"] },
() => { () => {
const firstGiftCardName = `${startsWith}${faker.datatype.number()}`;
const secondGiftCardName = `${startsWith}${faker.datatype.number()}`;
const amount = 10; const amount = 10;
const expectedOrderPrice = shippingPrice + productPrice - amount; const expectedOrderPrice = shippingPrice + productPrice - amount;
const firstGiftCardTag = faker.datatype.number();
const secondGiftCardTag = faker.datatype.number();
let firstGiftCard; let firstGiftCard;
let firstGiftCardCode;
let secondGiftCard; let secondGiftCard;
let secondGiftCardCode;
cy.addAliasToGraphRequest("GiftCardBulkActivate");
createGiftCard({ createGiftCard({
tag: firstGiftCardName, tag: firstGiftCardTag,
amount, amount,
currency: "USD", currency: "USD",
isActive: false, isActive: false,
}) })
.then(giftCard => { .then(giftCard => {
firstGiftCard = giftCard; firstGiftCard = giftCard;
firstGiftCardCode = giftCard.code;
createGiftCard({ createGiftCard({
tag: secondGiftCardName, tag: secondGiftCardTag,
amount, amount,
currency: "USD", currency: "USD",
isActive: false, isActive: false,
@ -216,9 +228,16 @@ describe("As a admin I want to use enabled gift card in checkout", () => {
}) })
.then(giftCard => { .then(giftCard => {
secondGiftCard = giftCard; secondGiftCard = giftCard;
enterAndSelectGiftCards([firstGiftCard.id, secondGiftCard.id]); secondGiftCardCode = giftCard.code;
cy.addAliasToGraphRequest("GiftCardBulkActivate") cy.visit(
.get(GIFT_CARD_LIST.activateGiftCardButton) giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [
firstGiftCardTag,
secondGiftCardTag,
]),
);
giftCardsPage.selectGiftCardOnListView(secondGiftCardCode);
giftCardsPage.selectGiftCardOnListView(firstGiftCardCode);
cy.get(GIFT_CARD_LIST.activateGiftCardButton)
.click() .click()
.waitForRequestAndCheckIfNoErrors("@GiftCardBulkActivate") .waitForRequestAndCheckIfNoErrors("@GiftCardBulkActivate")
.confirmationMessageShouldAppear(); .confirmationMessageShouldAppear();

View file

@ -3,10 +3,7 @@
import faker from "faker"; import faker from "faker";
import { GIFT_CARD_LIST } from "../../../elements/catalog/giftCard/giftCardList"; import { urlList } from "../../../fixtures/urlList";
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 { TEST_ADMIN_USER } from "../../../fixtures/users"; import { TEST_ADMIN_USER } from "../../../fixtures/users";
import { createGiftCard } from "../../../support/api/requests/GiftCard"; import { createGiftCard } from "../../../support/api/requests/GiftCard";
import { import {
@ -14,7 +11,7 @@ import {
updatePlugin, updatePlugin,
} from "../../../support/api/requests/Plugins"; } from "../../../support/api/requests/Plugins";
import { getMailWithGiftCardExportWithAttachment } from "../../../support/api/utils/users"; 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", () => { describe("As an admin I want to export gift card", () => {
const startsWith = "updateGCard"; 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", "should be able to export several gift cards to csv file. TC: SALEOR_1010",
{ tags: ["@giftCard", "@allEnv", "@stable"] }, { tags: ["@giftCard", "@allEnv", "@stable"] },
() => { () => {
const giftCard01 = `${startsWith}${faker.datatype.number()}`; const firstGiftCardTag = faker.datatype.number();
const giftCard02 = `${startsWith}${faker.datatype.number()}`; const secondGiftCardTag = faker.datatype.number();
const exportId = `${faker.datatype.number()}`; const exportId = `${faker.datatype.number()}`;
let giftCard01hash; let firstGiftCardId;
let giftCard02hash; let secondGiftCardId;
let firstGiftCardCode;
let secondGiftCardCode;
updatePlugin( updatePlugin(
"mirumee.notifications.admin_email", "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`, `Your exported {{ data_type }} data #${exportId} is ready`,
); );
createGiftCard({ createGiftCard({
tag: giftCard01, tag: firstGiftCardTag,
amount: 5, amount: 5,
currency: "THB", currency: "THB",
}) })
.then(hash => { .then(firstGiftCard => {
giftCard01hash = hash.id; firstGiftCardId = firstGiftCard.id;
firstGiftCardCode = firstGiftCard.code;
createGiftCard({ createGiftCard({
tag: giftCard02, tag: secondGiftCardTag,
amount: 10, amount: 10,
currency: "THB", currency: "THB",
}); });
}) })
.then(hash2 => { .then(secondGiftCard => {
giftCard02hash = hash2.id; secondGiftCardCode = secondGiftCard.code;
enterAndSelectGiftCards([giftCard01hash, giftCard02hash]); secondGiftCardId = secondGiftCard.id;
cy cy.visit(
.get(ASSIGN_ELEMENTS_SELECTORS.checkbox) giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [
.first() firstGiftCardTag,
.check() secondGiftCardTag,
.should("be.checked") ]),
.get(GIFT_CARD_LIST.selectedAmount) );
.contains("Selected 2 items") giftCardsPage.selectGiftCardOnListView(secondGiftCardCode);
.should("be.visible") giftCardsPage.selectGiftCardOnListView(firstGiftCardCode);
.get(BUTTON_SELECTORS.showMoreButton) giftCardsPage.openExportGiftCardsDialog();
.click({ force: true }) giftCardsPage.selectSelectedRecordsButton();
.get(GIFT_CARD_SHOW_MORE.exportCodesMenu) giftCardsPage.selectExportAsCSVButton();
.click() cy.clickSubmitButton();
.get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.csv)
.click()
.get(BUTTON_SELECTORS.submit)
.click().confirmationMessageShouldDisappear;
getMailWithGiftCardExportWithAttachment( getMailWithGiftCardExportWithAttachment(
TEST_ADMIN_USER.email, TEST_ADMIN_USER.email,
`Your exported gift cards data #${exportId} is ready`, `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", "should be able to export several gift cards to xlsx file. TC: SALEOR_1014",
{ tags: ["@giftCard", "@allEnv", "@stable"] }, { tags: ["@giftCard", "@allEnv", "@stable"] },
() => { () => {
const giftCard01 = `${startsWith}${faker.datatype.number()}`; const firstGiftCardTag = faker.datatype.number();
const giftCard02 = `${startsWith}${faker.datatype.number()}`; const secondGiftCardTag = faker.datatype.number();
const exportId = `${faker.datatype.number()}`; const exportId = `${faker.datatype.number()}`;
let giftCard01hash; let firstGiftCardId;
let giftCard02hash; let secondGiftCardId;
let firstGiftCardCode;
let secondGiftCardCode;
updatePlugin( updatePlugin(
"mirumee.notifications.admin_email", "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`, `Your exported {{ data_type }} data #${exportId} is ready`,
); );
createGiftCard({ createGiftCard({
tag: giftCard01, tag: firstGiftCardTag,
amount: 5, amount: 5,
currency: "THB", currency: "THB",
}) })
.then(hash => { .then(firstGiftCard => {
giftCard01hash = hash.id; firstGiftCardId = firstGiftCard.id;
firstGiftCardCode = firstGiftCard.code;
createGiftCard({ createGiftCard({
tag: giftCard02, tag: secondGiftCardTag,
amount: 10, amount: 10,
currency: "THB", currency: "THB",
}); });
}) })
.then(hash2 => { .then(secondGiftCard => {
giftCard02hash = hash2.id; secondGiftCardId = secondGiftCard.id;
enterAndSelectGiftCards([giftCard01hash, giftCard02hash]); secondGiftCardCode = secondGiftCard.code;
cy cy.visit(
.get(ASSIGN_ELEMENTS_SELECTORS.checkbox) giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [
.first() firstGiftCardTag,
.check() secondGiftCardTag,
.should("be.checked") ]),
.get(GIFT_CARD_LIST.selectedAmount) );
.contains("Selected 2 items") giftCardsPage.selectGiftCardOnListView(secondGiftCardCode);
.should("be.visible") giftCardsPage.selectGiftCardOnListView(firstGiftCardCode);
.get(BUTTON_SELECTORS.showMoreButton) giftCardsPage.openExportGiftCardsDialog();
.click({ force: true }) giftCardsPage.selectSelectedRecordsButton();
.get(GIFT_CARD_SHOW_MORE.exportCodesMenu) giftCardsPage.selectExportAsXLSXButton();
.click() cy.clickSubmitButton();
.get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.xlsx)
.click()
.get(BUTTON_SELECTORS.submit)
.click().confirmationMessageShouldDisappear;
getMailWithGiftCardExportWithAttachment( getMailWithGiftCardExportWithAttachment(
TEST_ADMIN_USER.email, TEST_ADMIN_USER.email,
`Your exported gift cards data #${exportId} is ready`, `Your exported gift cards data #${exportId} is ready`,

View file

@ -3,17 +3,16 @@
import faker from "faker"; import faker from "faker";
import { GIFT_CARD_LIST } from "../../../elements/catalog/giftCard/giftCardList";
import { GIFT_CARD_UPDATE } from "../../../elements/catalog/giftCard/giftCardUpdate"; 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 { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors";
import { giftCardDetailsUrl } from "../../../fixtures/urlList"; import { MESSAGES } from "../../../fixtures";
import { giftCardDetailsUrl, urlList } from "../../../fixtures/urlList";
import { import {
createGiftCard, createGiftCard,
getGiftCardWithId, getGiftCardWithId,
} from "../../../support/api/requests/GiftCard"; } from "../../../support/api/requests/GiftCard";
import { formatDate } from "../../../support/formatData/formatDate"; 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", () => { describe("As an admin I want to update gift card", () => {
const startsWith = "updateGCard"; 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", "should be able to delete several gift cards. TC: SALEOR_1011",
{ tags: ["@giftCard", "@allEnv", "@stable"] }, { tags: ["@giftCard", "@allEnv", "@stable"] },
() => { () => {
const giftCard01 = `${startsWith}${faker.datatype.number()}`; const firstGiftCardTag = faker.datatype.number();
const giftCard02 = `${startsWith}${faker.datatype.number()}`; const secondGiftCardTag = faker.datatype.number();
let giftCard01hash; let firstGiftCardId;
let giftCard02hash; let secondGiftCardId;
let firstGiftCardCode;
let secondGiftCardCode;
cy.addAliasToGraphRequest("BulkDeleteGiftCard");
createGiftCard({ createGiftCard({
tag: giftCard01, tag: firstGiftCardTag,
amount: 3, amount: 3,
currency: "THB", currency: "THB",
}) })
.then(hash => { .then(firstGiftCard => {
giftCard01hash = hash.id; firstGiftCardId = firstGiftCard.id;
firstGiftCardCode = firstGiftCard.code;
createGiftCard({ createGiftCard({
tag: giftCard02, tag: secondGiftCardTag,
amount: 7, amount: 7,
currency: "THB", currency: "THB",
}); });
}) })
.then(hash2 => { .then(secondGiftCard => {
giftCard02hash = hash2.id; secondGiftCardCode = secondGiftCard.code;
enterAndSelectGiftCards([giftCard01hash, giftCard02hash]); secondGiftCardId = secondGiftCard.id;
cy.get(ASSIGN_ELEMENTS_SELECTORS.checkbox) cy.visit(
.first() giftCardsPage.getUrlWithFilteredTags(urlList.giftCards, [
.check() firstGiftCardTag,
.should("be.checked") secondGiftCardTag,
.get(GIFT_CARD_LIST.selectedAmount) ]),
.contains("Selected 2 items") );
.should("be.visible") giftCardsPage.selectGiftCardOnListView(secondGiftCardCode);
.get(BUTTON_SELECTORS.deleteItemsButton) giftCardsPage.selectGiftCardOnListView(firstGiftCardCode);
.first() giftCardsPage.bulkDeleteRecords();
.click() cy.waitForRequestAndCheckIfNoErrors("@BulkDeleteGiftCard");
.get(GIFT_CARD_UPDATE.consentCheckbox) cy.contains(MESSAGES.noGiftCardsFound).should("be.visible");
.click() getGiftCardWithId(firstGiftCardId).should("be.null");
.get(BUTTON_SELECTORS.submit) getGiftCardWithId(secondGiftCardId).should("be.null");
.click()
.get(ASSIGN_ELEMENTS_SELECTORS.checkbox)
.should("not.be.visible");
getGiftCardWithId(giftCard01.id).should("be.null");
getGiftCardWithId(giftCard02.id).should("be.null");
}); });
}, },
); );

View file

@ -6,4 +6,5 @@ export const GIFT_CARD_SHOW_MORE = {
csv: 'input[value="CSV"]', csv: 'input[value="CSV"]',
xlsx: 'input[value="XLSX"]', xlsx: 'input[value="XLSX"]',
}, },
exportSelectedRecords: '[data-test-id="IDS"]',
}; };

View file

@ -21,4 +21,5 @@ export const BUTTON_SELECTORS = {
'[data-test-id = "delete-selected-elements-icon"]', '[data-test-id = "delete-selected-elements-icon"]',
dialogBackButton: '[data-test-id="back"]', dialogBackButton: '[data-test-id="back"]',
expandMetadataButton: '[data-test-id="expand"]', expandMetadataButton: '[data-test-id="expand"]',
bulkDeleteButton: '[data-test-id="bulk-delete-button"]',
}; };

View file

@ -2,6 +2,7 @@ export const SHARED_ELEMENTS = {
body: "body", body: "body",
header: "[data-test-id='page-header']", header: "[data-test-id='page-header']",
progressBar: '[role="progressbar"]', progressBar: '[role="progressbar"]',
rowNumberOption: "[data-test-id='rowNumberOption']",
circularProgress: '[class*="CircularProgress-circle"]', circularProgress: '[class*="CircularProgress-circle"]',
autocompleteCircle: '[class*="arrowInnerContainer"]', autocompleteCircle: '[class*="arrowInnerContainer"]',
dataGridTable: "[data-testid='data-grid-canvas']", dataGridTable: "[data-testid='data-grid-canvas']",

View file

@ -3,4 +3,5 @@ export const MESSAGES = {
confirmProductsDeletion: "Are you sure you want to delete 2 products?", confirmProductsDeletion: "Are you sure you want to delete 2 products?",
invalidEmailAddress: "Enter a valid email address.", invalidEmailAddress: "Enter a valid email address.",
slugMustBeUnique: "Slug must be unique", slugMustBeUnique: "Slug must be unique",
noGiftCardsFound: "No gift cards found",
}; };

View file

@ -40,6 +40,7 @@ export function getGiftCards(first) {
node{ node{
displayCode displayCode
id id
code
isActive isActive
expiryDate expiryDate
tags{ tags{
@ -159,3 +160,17 @@ export function deleteGiftCard(giftCardId) {
}`; }`;
return cy.sendRequestWithQuery(mutation); 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);
}
});
}

View file

@ -156,7 +156,7 @@ export function getMailWithGiftCardExportWithAttachment(
} else { } else {
cy.mpGetMailsBySubject(subject).then(mailsWithSubject => { cy.mpGetMailsBySubject(subject).then(mailsWithSubject => {
if (!mailsWithSubject.length) { if (!mailsWithSubject.length) {
cy.wait(10000); cy.wait(5000);
getMailWithGiftCardExportWithAttachment( getMailWithGiftCardExportWithAttachment(
email, email,
subject, subject,

View file

@ -1,8 +1,10 @@
import { SHARED_ELEMENTS } from "../../../elements";
import { GIFT_CARD_DIALOG } from "../../../elements/catalog/giftCard/giftCardDialog"; import { GIFT_CARD_DIALOG } from "../../../elements/catalog/giftCard/giftCardDialog";
import { import {
GIFT_CARD_LIST, GIFT_CARD_LIST,
giftCardRow giftCardRow,
} from "../../../elements/catalog/giftCard/giftCardList"; } 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 { GIFT_CARD_UPDATE } from "../../../elements/catalog/giftCard/giftCardUpdate";
import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors";
import { giftCardDetailsUrl, urlList } from "../../../fixtures/urlList"; import { giftCardDetailsUrl, urlList } from "../../../fixtures/urlList";
@ -11,7 +13,7 @@ export function openAndFillUpCreateGiftCardDialog({
note, note,
tag, tag,
amount, amount,
currency currency,
}) { }) {
cy.visit(urlList.giftCards) cy.visit(urlList.giftCards)
.get(GIFT_CARD_LIST.issueCardButton) .get(GIFT_CARD_LIST.issueCardButton)
@ -39,7 +41,7 @@ export function saveGiftCard() {
} }
export const expiryPeriods = { export const expiryPeriods = {
MONTH: GIFT_CARD_DIALOG.expirationOptions.expiryPeriodMonthType MONTH: GIFT_CARD_DIALOG.expirationOptions.expiryPeriodMonthType,
}; };
export function setExpiryPeriod(amount, period) { export function setExpiryPeriod(amount, period) {
@ -77,6 +79,20 @@ export function selectGiftCard(giftCardId) {
.find(GIFT_CARD_LIST.selectGiftCardCheckbox) .find(GIFT_CARD_LIST.selectGiftCardCheckbox)
.click(); .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) { export function enterAndSelectGiftCards(giftCardsIds) {
const alias = "GiftCardList"; const alias = "GiftCardList";
@ -86,6 +102,40 @@ export function enterAndSelectGiftCards(giftCardsIds) {
elementsGraphqlAlias: alias, elementsGraphqlAlias: alias,
elementsName: "giftCards", elementsName: "giftCards",
elementsIds: giftCardsIds, 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;
}

View file

@ -40,3 +40,4 @@ export * as channelsPage from "./channelsPage";
export * as pagesPage from "./pagesPage"; export * as pagesPage from "./pagesPage";
export * as columnPickerPage from "./columnPicker"; export * as columnPickerPage from "./columnPicker";
export * as pageDetailsPage from "./pageDetailsPage"; export * as pageDetailsPage from "./pageDetailsPage";
export * as giftCardsPage from "./catalog/giftCardPage";

View file

@ -790,6 +790,9 @@
"context": "product", "context": "product",
"string": "Stock quantity" "string": "Stock quantity"
}, },
"3a5wL8": {
"string": "Active"
},
"3bQz2o": { "3bQz2o": {
"context": "bulk issue gift cards success alert title", "context": "bulk issue gift cards success alert title",
"string": "Gift Cards Issued" "string": "Gift Cards Issued"
@ -1283,6 +1286,10 @@
"7Chrsf": { "7Chrsf": {
"string": "Passwords do not match" "string": "Passwords do not match"
}, },
"7EDqed": {
"context": "tab name",
"string": "All gift cards"
},
"7FL+WZ": { "7FL+WZ": {
"context": "export products to csv file, button", "context": "export products to csv file, button",
"string": "Export Products" "string": "Export Products"
@ -5765,6 +5772,9 @@
"context": "volume units types", "context": "volume units types",
"string": "Volume" "string": "Volume"
}, },
"d68yq7": {
"string": "Delete gift cards"
},
"d7dT8o": { "d7dT8o": {
"context": "button, sets granted refund amount in input", "context": "button, sets granted refund amount in input",
"string": "Set max" "string": "Set max"
@ -6207,6 +6217,10 @@
"context": "caption", "context": "caption",
"string": "If enabled, attribute will be accessible to customers." "string": "If enabled, attribute will be accessible to customers."
}, },
"h2hEKV": {
"context": "search gift card placeholder",
"string": "Search gift cards, e.g {exampleGiftCardCode}"
},
"h5r9+x": { "h5r9+x": {
"context": "sort shipping methods by zone, section header", "context": "sort shipping methods by zone, section header",
"string": "Shipping By Zone" "string": "Shipping By Zone"
@ -6819,10 +6833,6 @@
"context": "money", "context": "money",
"string": "from {money}" "string": "from {money}"
}, },
"labkPK": {
"context": "search gift card placeholder",
"string": "Search Gift Cards, e.g {exampleGiftCardCode}"
},
"lct0qd": { "lct0qd": {
"string": "This list shows all attributes that will be assigned to pages that have this page type assigned." "string": "This list shows all attributes that will be assigned to pages that have this page type assigned."
}, },

View file

@ -136,6 +136,7 @@ export const defaultListSettings: AppListViewSettings = {
}, },
[ListViews.GIFT_CARD_LIST]: { [ListViews.GIFT_CARD_LIST]: {
rowNumber: PAGINATE_BY, rowNumber: PAGINATE_BY,
columns: ["giftCardCode", "status", "tag", "product", "usedBy", "balance"],
}, },
}; };

View file

@ -1,4 +1,3 @@
// @ts-strict-ignore
import { ConfirmButton } from "@dashboard/components/ConfirmButton"; import { ConfirmButton } from "@dashboard/components/ConfirmButton";
import { Task } from "@dashboard/containers/BackgroundTasks/types"; import { Task } from "@dashboard/containers/BackgroundTasks/types";
import { import {
@ -45,10 +44,10 @@ const GiftCardExportDialog: React.FC<
const { const {
loading: loadingGiftCardList, loading: loadingGiftCardList,
totalCount: filteredGiftCardsCount, totalCount: filteredGiftCardsCount,
listElements, selectedRowIds,
} = useGiftCardList(); } = useGiftCardList();
const selectedIds = idsToExport ?? listElements; const selectedIds = idsToExport ?? selectedRowIds;
const { data: allGiftCardsCountData, loading: loadingGiftCardCount } = const { data: allGiftCardsCountData, loading: loadingGiftCardCount } =
useGiftCardTotalCountQuery(); useGiftCardTotalCountQuery();
@ -59,14 +58,14 @@ const GiftCardExportDialog: React.FC<
onCompleted: data => { onCompleted: data => {
const errors = data?.exportGiftCards?.errors; const errors = data?.exportGiftCards?.errors;
if (!errors.length) { if (!errors?.length) {
notify({ notify({
text: intl.formatMessage(messages.successAlertDescription), text: intl.formatMessage(messages.successAlertDescription),
title: intl.formatMessage(messages.successAlertTitle), title: intl.formatMessage(messages.successAlertTitle),
}); });
queue(Task.EXPORT, { queue(Task.EXPORT, {
id: data.exportGiftCards.exportFile.id, id: data?.exportGiftCards?.exportFile?.id,
}); });
onClose(); onClose();
@ -91,7 +90,7 @@ const GiftCardExportDialog: React.FC<
: exportSettingsInitialFormData, : exportSettingsInitialFormData,
handleSubmit, handleSubmit,
); );
const allGiftCardsCount = allGiftCardsCountData?.giftCards?.totalCount; const allGiftCardsCount = allGiftCardsCountData?.giftCards?.totalCount ?? 0;
const exportScopeLabels = { const exportScopeLabels = {
allItems: intl.formatMessage( allItems: intl.formatMessage(
@ -111,7 +110,7 @@ const GiftCardExportDialog: React.FC<
description: "export selected items to csv file", description: "export selected items to csv file",
}, },
{ {
number: listElements.length, number: selectedRowIds.length,
}, },
), ),
}; };
@ -126,7 +125,9 @@ const GiftCardExportDialog: React.FC<
{!loading && ( {!loading && (
<> <>
<ExportDialogSettings <ExportDialogSettings
errors={exportGiftCardsOpts?.data?.exportGiftCards?.errors} errors={
exportGiftCardsOpts?.data?.exportGiftCards?.errors ?? []
}
onChange={change} onChange={change}
selectedItems={selectedIds?.length} selectedItems={selectedIds?.length}
data={data} data={data}

View file

@ -4,7 +4,7 @@ import { ConfirmButton } from "@saleor/macaw-ui";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/messages";
import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails";
import useGiftCardActivateToggle from "./hooks/useGiftCardActivateToggle"; import useGiftCardActivateToggle from "./hooks/useGiftCardActivateToggle";

View file

@ -31,3 +31,25 @@ export const giftCardTimelineNoteAdd = gql`
} }
} }
`; `;
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
}
}
`;

View file

@ -1,4 +1,3 @@
// @ts-strict-ignore
import { ConfirmButton } from "@dashboard/components/ConfirmButton"; import { ConfirmButton } from "@dashboard/components/ConfirmButton";
import { IMessage } from "@dashboard/components/messages"; import { IMessage } from "@dashboard/components/messages";
import { import {
@ -10,55 +9,54 @@ import { getByIds } from "@dashboard/orders/components/OrderReturnPage/utils";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { useGiftCardList } from "../../providers/GiftCardListProvider"; import { bulkEnableDisableSectionMessages as messages } from "../messages";
import { GIFT_CARD_LIST_QUERY } from "../../queries"; import { useGiftCardList } from "../providers/GiftCardListProvider";
import { bulkEnableDisableSectionMessages as messages } from "./messages"; import { GIFT_CARD_LIST_QUERY } from "../queries";
const BulkEnableDisableSection: React.FC = () => { export const GiftCardListBulkActions: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const notify = useNotifier(); const notify = useNotifier();
const { listElements: ids, reset, giftCards } = useGiftCardList(); const { selectedRowIds, clearRowSelection, giftCards } = useGiftCardList();
const hasAnyEnabledCardsSelected = giftCards const hasAnyEnabledCardsSelected = giftCards
.filter(getByIds(ids)) .filter(getByIds(selectedRowIds))
.some(({ isActive }) => isActive); .some(({ isActive }) => isActive);
const areAllSelectedCardsActive = giftCards const areAllSelectedCardsActive = giftCards
.filter(getByIds(ids)) .filter(getByIds(selectedRowIds))
.every(({ isActive }) => isActive); .every(({ isActive }) => isActive);
const hasAnyDisabledCardsSelected = giftCards const hasAnyDisabledCardsSelected = giftCards
.filter(getByIds(ids)) .filter(getByIds(selectedRowIds))
.some(({ isActive }) => !isActive); .some(({ isActive }) => !isActive);
const areAllSelectedCardsDisabled = giftCards const areAllSelectedCardsDisabled = giftCards
.filter(getByIds(ids)) .filter(getByIds(selectedRowIds))
.every(({ isActive }) => !isActive); .every(({ isActive }) => !isActive);
const [activateGiftCards, activateGiftCardsOpts] = const [activateGiftCards, activateGiftCardsOpts] =
useGiftCardBulkActivateMutation({ useGiftCardBulkActivateMutation({
onCompleted: data => { onCompleted: data => {
const { errors, count } = data?.giftCardBulkActivate; const notifierData: IMessage = !!data?.giftCardBulkActivate?.errors
?.length
const notifierData: IMessage = !!errors?.length
? { ? {
status: "error", status: "error",
text: intl.formatMessage(messages.errorActivateAlertText, { text: intl.formatMessage(messages.errorActivateAlertText, {
count, count: data?.giftCardBulkActivate?.count,
}), }),
} }
: { : {
status: "success", status: "success",
text: intl.formatMessage(messages.successActivateAlertText, { text: intl.formatMessage(messages.successActivateAlertText, {
count, count: data?.giftCardBulkActivate?.count,
}), }),
}; };
notify(notifierData); notify(notifierData);
if (!errors.length) { if (!data?.giftCardBulkActivate?.errors?.length) {
reset(); clearRowSelection();
} }
}, },
refetchQueries: [GIFT_CARD_LIST_QUERY], refetchQueries: [GIFT_CARD_LIST_QUERY],
@ -67,36 +65,39 @@ const BulkEnableDisableSection: React.FC = () => {
const [deactivateGiftCards, deactivateGiftCardsOpts] = const [deactivateGiftCards, deactivateGiftCardsOpts] =
useGiftCardBulkDeactivateMutation({ useGiftCardBulkDeactivateMutation({
onCompleted: data => { onCompleted: data => {
const { errors, count } = data?.giftCardBulkDeactivate; const notifierData: IMessage = !!data?.giftCardBulkDeactivate?.errors
?.length
const notifierData: IMessage = !!errors?.length
? { ? {
status: "error", status: "error",
text: intl.formatMessage(messages.errorDeactivateAlertText, { text: intl.formatMessage(messages.errorDeactivateAlertText, {
count, count: data?.giftCardBulkDeactivate?.count,
}), }),
} }
: { : {
status: "success", status: "success",
text: intl.formatMessage(messages.successDeactivateAlertText, { text: intl.formatMessage(messages.successDeactivateAlertText, {
count, count: data?.giftCardBulkDeactivate?.count,
}), }),
}; };
notify(notifierData); notify(notifierData);
if (!errors.length) { if (!data?.giftCardBulkDeactivate?.errors?.length) {
reset(); clearRowSelection();
} }
}, },
refetchQueries: [GIFT_CARD_LIST_QUERY], refetchQueries: [GIFT_CARD_LIST_QUERY],
}); });
const handleActivateGiftCards = () => const handleActivateGiftCards = async () => {
activateGiftCards({ variables: { ids } }); await activateGiftCards({ variables: { ids: selectedRowIds } });
clearRowSelection();
};
const handleDeactivateGiftCards = () => const handleDeactivateGiftCards = async () => {
deactivateGiftCards({ variables: { ids } }); await deactivateGiftCards({ variables: { ids: selectedRowIds } });
clearRowSelection();
};
const isSelectionMixed = const isSelectionMixed =
hasAnyEnabledCardsSelected && hasAnyDisabledCardsSelected; hasAnyEnabledCardsSelected && hasAnyDisabledCardsSelected;
@ -126,5 +127,3 @@ const BulkEnableDisableSection: React.FC = () => {
</> </>
); );
}; };
export default BulkEnableDisableSection;

View file

@ -0,0 +1 @@
export * from "./GiftCardListBulkActions";

View file

@ -1,15 +1,15 @@
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
import React from "react"; import React from "react";
import GiftCardListSearchAndFilters from "./GiftCardListSearchAndFilters";
import { GiftCardsListDatagrid } from "./GiftCardsListDatagrid";
import GiftCardsListHeader from "./GiftCardsListHeader"; import GiftCardsListHeader from "./GiftCardsListHeader";
import GiftCardsListOrderInfoCard from "./GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard"; import GiftCardsListOrderInfoCard from "./GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard";
import GiftCardsListTable from "./GiftCardsListTable";
const GiftCardsListPage: React.FC = () => ( const GiftCardsListPage: React.FC = () => (
<> <>
<GiftCardsListHeader /> <GiftCardsListHeader />
<GiftCardsListTable /> <GiftCardListSearchAndFilters />
<VerticalSpacer spacing={2} /> <GiftCardsListDatagrid />
<GiftCardsListOrderInfoCard /> <GiftCardsListOrderInfoCard />
</> </>
); );

View file

@ -1,52 +1,52 @@
// @ts-strict-ignore // @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 DeleteFilterTabDialog from "@dashboard/components/DeleteFilterTabDialog";
import FilterBar from "@dashboard/components/FilterBar"; import SaveFilterTabDialog from "@dashboard/components/SaveFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData,
} from "@dashboard/components/SaveFilterTabDialog";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config";
import { giftCardListUrl } from "@dashboard/giftCards/urls";
import { useGiftCardCurrenciesQuery } from "@dashboard/graphql"; import { useGiftCardCurrenciesQuery } from "@dashboard/graphql";
import { getSearchFetchMoreProps } from "@dashboard/hooks/makeTopLevelSearch/utils"; import { getSearchFetchMoreProps } from "@dashboard/hooks/makeTopLevelSearch/utils";
import useLocalStorage from "@dashboard/hooks/useLocalStorage"; import useLocalStorage from "@dashboard/hooks/useLocalStorage";
import useNavigator from "@dashboard/hooks/useNavigator";
import { maybe } from "@dashboard/misc";
import useCustomerSearch from "@dashboard/searches/useCustomerSearch"; import useCustomerSearch from "@dashboard/searches/useCustomerSearch";
import useGiftCardTagsSearch from "@dashboard/searches/useGiftCardTagsSearch"; import useGiftCardTagsSearch from "@dashboard/searches/useGiftCardTagsSearch";
import useProductSearch from "@dashboard/searches/useProductSearch"; import useProductSearch from "@dashboard/searches/useProductSearch";
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
import { mapEdgesToItems } from "@dashboard/utils/maps"; import { mapEdgesToItems } from "@dashboard/utils/maps";
import { Box } from "@saleor/macaw-ui/next";
import compact from "lodash/compact"; import compact from "lodash/compact";
import React from "react"; 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 { useGiftCardListDialogs } from "../providers/GiftCardListDialogsProvider";
import { useGiftCardList } from "../providers/GiftCardListProvider"; import { useGiftCardList } from "../providers/GiftCardListProvider";
import { GiftCardListActionParamsEnum } from "../types"; import { GiftCardListActionParamsEnum } from "../types";
import {
createFilterStructure,
deleteFilterTab,
getActiveFilters,
getFilterOpts,
getFilterQueryParam,
getFiltersCurrentTab,
getFilterTabs,
saveFilterTab,
} from "./filters";
import { import {
giftCardListFilterErrorMessages as errorMessages, giftCardListFilterErrorMessages as errorMessages,
giftCardListSearchAndFiltersMessages as messages, giftCardListSearchAndFiltersMessages as messages,
} from "./messages"; } from "./messages";
const GiftCardListSearchAndFilters: React.FC = () => { const GiftCardListSearchAndFilters: React.FC = () => {
const navigate = useNavigator();
const intl = useIntl(); const intl = useIntl();
const [selectedChannel] = useLocalStorage("channel", ""); 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 } = const { onClose, openDeleteDialog } = useGiftCardListDialogs();
useGiftCardListDialogs();
const defaultSearchVariables = { const defaultSearchVariables = {
variables: { ...DEFAULT_INITIAL_SEARCH_DATA, first: 5 }, variables: { ...DEFAULT_INITIAL_SEARCH_DATA, first: 5 },
@ -109,75 +109,53 @@ const GiftCardListSearchAndFilters: React.FC = () => {
const filterStructure = createFilterStructure(intl, filterOpts); 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 ( return (
<> <>
<FilterBar <ListFilters
errorMessages={{ errorMessages={{
initialBalanceAmount: errorMessages.balanceAmount, initialBalanceAmount: errorMessages.balanceAmount,
initialBalanceCurrency: errorMessages.balanceCurrency, initialBalanceCurrency: errorMessages.balanceCurrency,
currentBalanceAmount: errorMessages.balanceAmount, currentBalanceAmount: errorMessages.balanceAmount,
currentBalanceCurrency: errorMessages.balanceCurrency, currentBalanceCurrency: errorMessages.balanceCurrency,
}} }}
tabs={tabs.map(tab => tab.name)} currencySymbol={selectedChannelData?.currencyCode || ""}
currentTab={currentTab}
filterStructure={filterStructure}
initialSearch={params?.query || ""} initialSearch={params?.query || ""}
onAll={resetFilters}
onFilterChange={changeFilters} onFilterChange={changeFilters}
onSearchChange={handleSearchChange} onSearchChange={handleSearchChange}
onTabChange={handleTabChange} filterStructure={filterStructure}
onTabDelete={openSearchDeleteDialog}
onTabSave={openSearchSaveDialog}
searchPlaceholder={intl.formatMessage(messages.searchPlaceholder, { searchPlaceholder={intl.formatMessage(messages.searchPlaceholder, {
exampleGiftCardCode: "21F1-39DY-V4U2", exampleGiftCardCode: "21F1-39DY-V4U2",
})} })}
allTabLabel={intl.formatMessage(messages.defaultTabLabel)} actions={
<Box display="flex" gap={4}>
{selectedRowIds.length > 0 && (
<>
<GiftCardListBulkActions />
<BulkDeleteButton onClick={openDeleteDialog}>
<FormattedMessage
defaultMessage="Delete gift cards"
id="d68yq7"
/> />
</BulkDeleteButton>
</>
)}
</Box>
}
/>
<SaveFilterTabDialog <SaveFilterTabDialog
open={params.action === GiftCardListActionParamsEnum.SAVE_SEARCH} open={params.action === GiftCardListActionParamsEnum.SAVE_SEARCH}
confirmButtonState="default" confirmButtonState="default"
onClose={onClose} onClose={onClose}
onSubmit={handleTabSave} onSubmit={onPresetSave}
/> />
<DeleteFilterTabDialog <DeleteFilterTabDialog
open={params.action === GiftCardListActionParamsEnum.DELETE_SEARCH} open={params.action === GiftCardListActionParamsEnum.DELETE_SEARCH}
confirmButtonState="default" confirmButtonState="default"
onClose={onClose} onClose={onClose}
onSubmit={handleTabDelete} onSubmit={onPresetDelete}
tabName={maybe(() => tabs[currentTab - 1].name, "...")} tabName={presets[presetIdToDelete - 1]?.name ?? "..."}
/> />
</> </>
); );

View file

@ -15,8 +15,8 @@ export const giftCardListFilterErrorMessages = defineMessages({
export const giftCardListSearchAndFiltersMessages = defineMessages({ export const giftCardListSearchAndFiltersMessages = defineMessages({
searchPlaceholder: { searchPlaceholder: {
id: "labkPK", id: "h2hEKV",
defaultMessage: "Search Gift Cards, e.g {exampleGiftCardCode}", defaultMessage: "Search gift cards, e.g {exampleGiftCardCode}",
description: "search gift card placeholder", description: "search gift card placeholder",
}, },
defaultTabLabel: { defaultTabLabel: {

View file

@ -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 (
<DatagridChangeStateContext.Provider value={datagridState}>
<Datagrid
readonly
loading={loading}
rowMarkers="checkbox"
columnSelect="single"
hasRowHover={true}
onColumnMoved={handlers.onMove}
onColumnResize={handlers.onResize}
verticalBorder={col => 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={() => (
<ColumnPicker
staticColumns={staticColumns}
selectedColumns={selectedColumns}
onToggle={handlers.onToggle}
/>
)}
/>
<Box paddingX={6}>
<TablePagination
hasNextPage={paginationValues?.hasNextPage ?? false}
nextHref={paginationValues.nextHref}
hasPreviousPage={paginationValues?.hasPreviousPage ?? false}
prevHref={paginationValues?.prevHref}
component="div"
settings={settings}
onUpdateListSettings={updateListSettings}
/>
</Box>
</DatagridChangeStateContext.Provider>
);
};

View file

@ -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<GiftCardUrlSortField>,
): 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<GiftCardListQuery["giftCards"]>["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<GiftCardBase & { isActive: boolean }>,
) => {
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"]
];
}

View file

@ -0,0 +1 @@
export * from "./GiftCardsListDatagrid";

View file

@ -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",
},
});

View file

@ -1,27 +1,86 @@
import { TopNav } from "@dashboard/components/AppLayout/TopNav"; import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import { Button } from "@dashboard/components/Button"; import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect";
import CardMenu, { CardMenuItem } from "@dashboard/components/CardMenu";
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import { Box, Button, ChevronRightIcon } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { giftCardSettingsUrl } from "../../urls"; import { giftCardSettingsUrl } from "../../urls";
import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages"; import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages";
import { useGiftCardListDialogs } from "../providers/GiftCardListDialogsProvider"; import { useGiftCardListDialogs } from "../providers/GiftCardListDialogsProvider";
import { useGiftCardList } from "../providers/GiftCardListProvider";
import GiftCardsListHeaderAlert from "./GiftCardsListHeaderAlert"; import GiftCardsListHeaderAlert from "./GiftCardsListHeaderAlert";
const GiftCardsListHeader: React.FC = () => { const GiftCardsListHeader: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const navigate = useNavigator(); const navigate = useNavigator();
const { openCreateDialog, openBulkCreateDialog, openExportDialog } = const {
useGiftCardListDialogs(); openCreateDialog,
openBulkCreateDialog,
openExportDialog,
openSearchDeleteDialog,
openSearchSaveDialog,
} = useGiftCardListDialogs();
const {
hasPresetsChange,
selectedPreset,
presets,
onPresetUpdate,
setPresetIdToDelete,
onPresetChange,
resetFilters,
isFilterPresetOpen,
setFilterPresetOpen,
} = useGiftCardList();
const openSettings = () => navigate(giftCardSettingsUrl); const openSettings = () => navigate(giftCardSettingsUrl);
const menuItems: CardMenuItem[] = [ return (
<>
<TopNav
withoutBorder
isAlignToRight={false}
title={intl.formatMessage(sectionNames.giftCards)}
>
<Box
__flex={1}
display="flex"
justifyContent="space-between"
alignItems="center"
>
<Box display="flex">
<Box marginX={3} display="flex" alignItems="center">
<ChevronRightIcon />
</Box>
<FilterPresetsSelect
presetsChanged={hasPresetsChange()}
onSelect={onPresetChange}
onRemove={(id: number) => {
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",
})}
/>
</Box>
<Box display="flex" alignItems="center" gap={2}>
<TopNav.Menu
items={[
{ {
label: intl.formatMessage(messages.settings), label: intl.formatMessage(messages.settings),
testId: "settingsMenuItem", testId: "settingsMenuItem",
@ -37,13 +96,9 @@ const GiftCardsListHeader: React.FC = () => {
testId: "exportCodesMenuItem", testId: "exportCodesMenuItem",
onSelect: openExportDialog, onSelect: openExportDialog,
}, },
]; ]}
data-test-id="menu"
return ( />
<>
<TopNav title={intl.formatMessage(sectionNames.giftCards)}>
<CardMenu menuItems={menuItems} data-test-id="menu" />
<HorizontalSpacer spacing={2} />
<Button <Button
variant="primary" variant="primary"
onClick={openCreateDialog} onClick={openCreateDialog}
@ -51,6 +106,8 @@ const GiftCardsListHeader: React.FC = () => {
> >
{intl.formatMessage(messages.issueButtonLabel)} {intl.formatMessage(messages.issueButtonLabel)}
</Button> </Button>
</Box>
</Box>
</TopNav> </TopNav>
<GiftCardsListHeaderAlert /> <GiftCardsListHeaderAlert />
</> </>

View file

@ -1,4 +1,3 @@
// @ts-strict-ignore
import { useGiftCardProductsCountQuery } from "@dashboard/graphql"; import { useGiftCardProductsCountQuery } from "@dashboard/graphql";
import useLocalStorage from "@dashboard/hooks/useLocalStorage"; import useLocalStorage from "@dashboard/hooks/useLocalStorage";
import { Alert } from "@saleor/macaw-ui"; import { Alert } from "@saleor/macaw-ui";
@ -20,9 +19,9 @@ const GiftCardsListHeaderAlert: React.FC = () => {
}); });
const giftCardProductTypesExist = const giftCardProductTypesExist =
giftCardProductsCount?.giftCardProductTypes.totalCount > 0; (giftCardProductsCount?.giftCardProductTypes?.totalCount ?? 0) > 0;
const giftCardProductsExist = const giftCardProductsExist =
giftCardProductsCount?.giftCardProducts.totalCount > 0; (giftCardProductsCount?.giftCardProducts?.totalCount ?? 0) > 0;
const showNoGiftCardProductsAlert = const showNoGiftCardProductsAlert =
!giftCardProductsCountLoading && !giftCardProductsCountLoading &&
@ -34,7 +33,7 @@ const GiftCardsListHeaderAlert: React.FC = () => {
title={intl.formatMessage(messages.noGiftCardsAlertTitle)} title={intl.formatMessage(messages.noGiftCardsAlertTitle)}
variant="warning" variant="warning"
close={false} close={false}
className="remove-icon-background" className="remove-icon-background remove-content-padding-top "
> >
<GiftCardsListHeaderAlertContent <GiftCardsListHeaderAlertContent
giftCardProductTypesExist={giftCardProductTypesExist} giftCardProductTypesExist={giftCardProductTypesExist}

View file

@ -2,22 +2,24 @@ import Link from "@dashboard/components/Link";
import { ProductTypeKindEnum } from "@dashboard/graphql"; import { ProductTypeKindEnum } from "@dashboard/graphql";
import { productAddUrl } from "@dashboard/products/urls"; import { productAddUrl } from "@dashboard/products/urls";
import { productTypeAddUrl } from "@dashboard/productTypes/urls"; import { productTypeAddUrl } from "@dashboard/productTypes/urls";
import { sprinkles } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages"; import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages";
import { useHeaderStyles as useStyles } from "../styles";
interface GiftCardsListHeaderAlertContentProps { interface GiftCardsListHeaderAlertContentProps {
giftCardProductTypesExist: boolean; giftCardProductTypesExist: boolean;
giftCardProductsExist: boolean; giftCardProductsExist: boolean;
} }
const alertLinkClassName = sprinkles({
fontSize: "captionSmall",
});
const GiftCardsListHeaderAlertContent: React.FC< const GiftCardsListHeaderAlertContent: React.FC<
GiftCardsListHeaderAlertContentProps GiftCardsListHeaderAlertContentProps
> = ({ giftCardProductTypesExist, giftCardProductsExist }) => { > = ({ giftCardProductTypesExist, giftCardProductsExist }) => {
const classes = useStyles({});
const giftCardProductTypeUrl = productTypeAddUrl({ const giftCardProductTypeUrl = productTypeAddUrl({
kind: ProductTypeKindEnum.GIFT_CARD, kind: ProductTypeKindEnum.GIFT_CARD,
}); });
@ -30,7 +32,7 @@ const GiftCardsListHeaderAlertContent: React.FC<
{...messages.noGiftCardsProductTypes} {...messages.noGiftCardsProductTypes}
values={{ values={{
createGiftCardProductType: ( createGiftCardProductType: (
<Link href={giftCardProductTypeUrl} className={classes.alertLink}> <Link href={giftCardProductTypeUrl} className={alertLinkClassName}>
<FormattedMessage {...messages.createGiftCardProductType} /> <FormattedMessage {...messages.createGiftCardProductType} />
</Link> </Link>
), ),
@ -47,7 +49,7 @@ const GiftCardsListHeaderAlertContent: React.FC<
createGiftCardProduct: ( createGiftCardProduct: (
<Link <Link
href={giftCardCreateGiftCardProductUrl} href={giftCardCreateGiftCardProductUrl}
className={classes.alertLink} className={alertLinkClassName}
> >
<FormattedMessage {...messages.createGiftCardProduct} /> <FormattedMessage {...messages.createGiftCardProduct} />
</Link> </Link>

View file

@ -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;

View file

@ -1,7 +1,7 @@
import Link from "@dashboard/components/Link"; import Link from "@dashboard/components/Link";
import { orderGiftCardBoughtPath } from "@dashboard/orders/urls"; import { orderGiftCardBoughtPath } from "@dashboard/orders/urls";
import { Typography } from "@material-ui/core";
import { Alert } from "@saleor/macaw-ui"; import { Alert } from "@saleor/macaw-ui";
import { Text } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
@ -9,7 +9,7 @@ import { giftCardListOrderCardMessages as messages } from "./messages";
const GiftCardsListOrderInfoCard: React.FC = () => ( const GiftCardsListOrderInfoCard: React.FC = () => (
<Alert variant="info" close={false}> <Alert variant="info" close={false}>
<Typography> <Text variant="caption" size="large">
<FormattedMessage <FormattedMessage
{...messages.giftCardOrderInfoMessage} {...messages.giftCardOrderInfoMessage}
values={{ values={{
@ -18,7 +18,7 @@ const GiftCardsListOrderInfoCard: React.FC = () => (
), ),
}} }}
/> />
</Typography> </Text>
</Alert> </Alert>
); );

View file

@ -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 (
<Card>
<GiftCardListSearchAndFilters />
<ResponsiveTable>
<GiftCardsListTableHeader isCurrencySelected={isCurrencySelected} />
<GiftCardsListTableFooter />
<TableBody>
{renderCollection(
giftCards,
giftCard => {
if (!giftCard) {
return (
<>
<TableCell padding="checkbox">
<Checkbox />
</TableCell>
<TableCell className={classes.skeleton} colSpan={5}>
<Skeleton />
</TableCell>
<TableCell className={classes.colDelete}>
<DeleteIconButton />
</TableCell>
</>
);
}
const {
id,
last4CodeChars,
usedBy,
usedByEmail,
tags,
product,
currentBalance,
} = giftCard;
return (
<TableRowLink
href={giftCardUrl(id)}
className={classes.row}
key={id}
hover={!!giftCard}
data-test-id={"gift-card-row-" + id}
>
<TableCell padding="checkbox">
<Checkbox
data-test-id="select-gift-card-checkbox"
disabled={!giftCard}
disableClickPropagation
checked={isSelected(id)}
onChange={() => toggle(id)}
/>
</TableCell>
<TableCell className={classes.colCardCode}>
<div className={classes.cardCodeContainer}>
<Typography>
{intl.formatMessage(messages.codeEndingWithLabel, {
last4CodeChars,
})}
</Typography>
<>
<HorizontalSpacer spacing={2} />
<GiftCardStatusChip giftCard={giftCard} />
</>
</div>
</TableCell>
<TableCell>
<Typography>{getTagCellText(tags)}</Typography>
</TableCell>
<TableCell>
{product ? (
<TableButtonWrapper>
<PillLink
className={classes.pill}
component={RouterLink}
to={productUrl(product?.id)}
onClick={event => {
event.stopPropagation();
navigate(productUrl(product?.id));
}}
>
{product?.name}
</PillLink>
</TableButtonWrapper>
) : (
PLACEHOLDER
)}
</TableCell>
<TableCell>
{usedBy ? (
<TableButtonWrapper>
<Link href={customerUrl(usedBy?.id)}>
{`${usedBy?.firstName} ${usedBy?.lastName}`}
</Link>
</TableButtonWrapper>
) : (
<Typography noWrap>
{usedByEmail || PLACEHOLDER}
</Typography>
)}
</TableCell>
<TableCell align="right" className={classes.colBalance}>
<Money money={currentBalance} />
</TableCell>
<TableCell className={classes.colDelete}>
<DeleteIconButton
onClick={event => {
event.stopPropagation();
openDeleteDialog(id);
}}
/>
</TableCell>
</TableRowLink>
);
},
() => (
<TableRowLink>
<TableCell colSpan={numberOfColumns}>
<FormattedMessage {...messages.noGiftCardsFound} />
</TableCell>
</TableRowLink>
),
)}
</TableBody>
</ResponsiveTable>
</Card>
);
};
export default GiftCardsListTable;

View file

@ -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 (
<TableFooter>
<TableRowLink>
<TablePagination
{...paginationValues}
settings={settings}
colSpan={numberOfColumns}
onUpdateListSettings={updateListSettings}
/>
</TableRowLink>
</TableFooter>
);
};
export default GiftCardsListTableFooter;

View file

@ -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<GiftCardsListTableHeaderProps> = ({
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: <b>{filterLabels.currencyLabel.defaultMessage}</b>,
}),
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { title, ...headerTooltipItemProps } = headerTooltipItem;
return (
<>
<colgroup>
<col />
<col className={classes.colCardCode} />
<col className={classes.colBase} />
<col className={classes.colProduct} />
<col className={classes.colBase} />
<col className={classes.colBalance} />
<col className={classes.colDelete} />
</colgroup>
<TableHead
disabled={loading}
colSpan={numberOfColumns}
selected={listElements.length}
items={giftCards}
toggleAll={toggleAll}
toolbar={
<div className={classes.toolbar}>
<BulkEnableDisableSection />
<DeleteIconButton onClick={() => openDeleteDialog()} />
</div>
}
>
{headerItems.map(({ title, options, onClick, direction }) => (
<TableCellHeader
{...options}
onClick={onClick}
direction={direction}
key={title.defaultMessage}
>
<Label text={intl.formatMessage(title)} size={LabelSizes.md} />
</TableCellHeader>
))}
<TooltipTableCellHeader {...headerTooltipItemProps}>
<Label
text={intl.formatMessage(headerTooltipItem.title)}
size={LabelSizes.md}
/>
</TooltipTableCellHeader>
<TableCell className={classes.colDelete} />
</TableHead>
</>
);
};
export default GiftCardsListTableHeader;

View file

@ -1,2 +0,0 @@
export * from "./GiftCardsListTableHeader";
export { default } from "./GiftCardsListTableHeader";

View file

@ -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",
},
});

View file

@ -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
}
}
`;

View file

@ -1,2 +0,0 @@
export * from "./GiftCardsListTable";
export { default } from "./GiftCardsListTable";

View file

@ -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;
};

View file

@ -26,12 +26,12 @@ import {
} from "@dashboard/utils/maps"; } from "@dashboard/utils/maps";
import { defineMessages, IntlShape } from "react-intl"; import { defineMessages, IntlShape } from "react-intl";
import { GiftCardListUrlQueryParams } from "../types";
import { import {
GiftCardListFilterKeys, GiftCardListFilterKeys,
GiftCardListFilterOpts, GiftCardListFilterOpts,
GiftCardListUrlFilters, GiftCardListUrlFilters,
GiftCardListUrlFiltersEnum, GiftCardListUrlFiltersEnum,
GiftCardListUrlQueryParams,
GiftCardStatusFilterEnum, GiftCardStatusFilterEnum,
SearchWithFetchMoreProps, SearchWithFetchMoreProps,
} from "./types"; } from "./types";
@ -337,8 +337,7 @@ export function createFilterStructure(
]; ];
} }
export const { deleteFilterTab, getFilterTabs, saveFilterTab } = export const storageUtils = createFilterTabUtils<string>(GIFT_CARD_FILTERS_KEY);
createFilterTabUtils<GiftCardListUrlFilters>(GIFT_CARD_FILTERS_KEY);
export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } = export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
createFilterUtils<GiftCardListUrlQueryParams, GiftCardListUrlFilters>( createFilterUtils<GiftCardListUrlQueryParams, GiftCardListUrlFilters>(

View file

@ -111,3 +111,45 @@ export const giftCardUpdateFormMessages = defineMessages({
description: "invalid date in expirydate field content", 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",
},
});

View file

@ -1,4 +1,3 @@
// @ts-strict-ignore
import GiftCardListPageDeleteDialog from "@dashboard/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog"; import GiftCardListPageDeleteDialog from "@dashboard/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog";
import GiftCardBulkCreateDialog from "@dashboard/giftCards/GiftCardBulkCreateDialog"; import GiftCardBulkCreateDialog from "@dashboard/giftCards/GiftCardBulkCreateDialog";
import GiftCardCreateDialogContent from "@dashboard/giftCards/GiftCardCreateDialog"; import GiftCardCreateDialogContent from "@dashboard/giftCards/GiftCardCreateDialog";
@ -32,10 +31,19 @@ export interface GiftCardListDialogsConsumerProps {
} }
export const GiftCardListDialogsContext = export const GiftCardListDialogsContext =
createContext<GiftCardListDialogsConsumerProps>(null); createContext<GiftCardListDialogsConsumerProps | null>(null);
export const useGiftCardListDialogs = () => export const useGiftCardListDialogs = () => {
useContext(GiftCardListDialogsContext); 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< const GiftCardListDialogsProvider: React.FC<
GiftCardListDialogsProviderProps GiftCardListDialogsProviderProps
@ -57,8 +65,8 @@ const GiftCardListDialogsProvider: React.FC<
const isDialogOpen = (type: GiftCardListActionParamsEnum) => const isDialogOpen = (type: GiftCardListActionParamsEnum) =>
params?.action === type; params?.action === type;
const handleDeleteDialogOpen = (id?: string) => { const handleDeleteDialogOpen = () => {
openDialog(DELETE, id ? { id } : undefined); openDialog(DELETE);
}; };
const openSearchDeleteDialog = () => const openSearchDeleteDialog = () =>
@ -75,7 +83,7 @@ const GiftCardListDialogsProvider: React.FC<
openSearchSaveDialog, openSearchSaveDialog,
openSearchDeleteDialog, openSearchDeleteDialog,
onClose, onClose,
id, id: id ?? "",
}; };
return ( return (

View file

@ -1,5 +1,5 @@
// @ts-strict-ignore
import { ApolloError } from "@apollo/client"; import { ApolloError } from "@apollo/client";
import { IFilter } from "@dashboard/components/Filter";
import { ExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; import { ExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types";
import { getExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/utils"; import { getExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/utils";
import { giftCardListUrl } from "@dashboard/giftCards/urls"; import { giftCardListUrl } from "@dashboard/giftCards/urls";
@ -8,9 +8,10 @@ import {
GiftCardListQueryVariables, GiftCardListQueryVariables,
useGiftCardListQuery, useGiftCardListQuery,
} from "@dashboard/graphql"; } from "@dashboard/graphql";
import useBulkActions, { import {
UseBulkActionsProps, UseFilterPresets,
} from "@dashboard/hooks/useBulkActions"; useFilterPresets,
} from "@dashboard/hooks/useFilterPresets";
import useListSettings, { import useListSettings, {
UseListSettings, UseListSettings,
} from "@dashboard/hooks/useListSettings"; } from "@dashboard/hooks/useListSettings";
@ -22,13 +23,28 @@ import {
PageInfo, PageInfo,
PaginationState, PaginationState,
} from "@dashboard/hooks/usePaginator"; } from "@dashboard/hooks/usePaginator";
import {
UseRowSelection,
useRowSelection,
} from "@dashboard/hooks/useRowSelection";
import { ListViews, SortPage } from "@dashboard/types"; import { ListViews, SortPage } from "@dashboard/types";
import createFilterHandlers from "@dashboard/utils/handlers/filterHandlers";
import createSortHandler from "@dashboard/utils/handlers/sortHandler"; import createSortHandler from "@dashboard/utils/handlers/sortHandler";
import { mapEdgesToItems } from "@dashboard/utils/maps"; import { mapEdgesToItems } from "@dashboard/utils/maps";
import { getSortParams } from "@dashboard/utils/sort"; 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 { import {
GiftCardListColummns, GiftCardListColummns,
GiftCardListUrlQueryParams, GiftCardListUrlQueryParams,
@ -44,25 +60,42 @@ interface GiftCardsListProviderProps {
} }
export interface GiftCardsListConsumerProps export interface GiftCardsListConsumerProps
extends UseBulkActionsProps, extends UseFilterPresets,
UseRowSelection,
UseListSettings<GiftCardListColummns>, UseListSettings<GiftCardListColummns>,
SortPage<GiftCardUrlSortField> { SortPage<GiftCardUrlSortField> {
giftCards: Array< giftCards: Array<
ExtendedGiftCard<GiftCardListQuery["giftCards"]["edges"][0]["node"]> ExtendedGiftCard<
NonNullable<GiftCardListQuery["giftCards"]>["edges"][0]["node"]
>
>; >;
pageInfo: PageInfo; pageInfo?: PageInfo;
loading: boolean; loading: boolean;
params: GiftCardListUrlQueryParams; params: GiftCardListUrlQueryParams;
paginationState: PaginationState; paginationState: PaginationState;
numberOfColumns: number; numberOfColumns: number;
totalCount: number; totalCount: number;
selectedItemsCount: number; changeFilters: (filter: IFilter<any>) => void;
resetFilters: () => void;
handleSearchChange: (query: string) => void;
isFilterPresetOpen: boolean;
setFilterPresetOpen: Dispatch<SetStateAction<boolean>>;
} }
export const GiftCardsListContext = export const GiftCardsListContext =
createContext<GiftCardsListConsumerProps>(null); createContext<GiftCardsListConsumerProps | null>(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<GiftCardsListProviderProps> = ({ export const GiftCardsListProvider: React.FC<GiftCardsListProviderProps> = ({
children, children,
@ -71,9 +104,25 @@ export const GiftCardsListProvider: React.FC<GiftCardsListProviderProps> = ({
const navigate = useNavigator(); const navigate = useNavigator();
const notify = useNotifier(); 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 } = const { updateListSettings, settings } =
useListSettings<GiftCardListColummns>(ListViews.GIFT_CARD_LIST); useListSettings<GiftCardListColummns>(ListViews.GIFT_CARD_LIST);
@ -110,7 +159,8 @@ export const GiftCardsListProvider: React.FC<GiftCardsListProviderProps> = ({
handleError: handleGiftCardListError, handleError: handleGiftCardListError,
}); });
const giftCards = mapEdgesToItems(data?.giftCards)?.map(getExtendedGiftCard); const giftCards =
mapEdgesToItems(data?.giftCards)?.map(getExtendedGiftCard) ?? [];
const providerValues: GiftCardsListConsumerProps = { const providerValues: GiftCardsListConsumerProps = {
onSort: handleSort, onSort: handleSort,
@ -118,18 +168,20 @@ export const GiftCardsListProvider: React.FC<GiftCardsListProviderProps> = ({
giftCards, giftCards,
totalCount: data?.giftCards?.totalCount || 0, totalCount: data?.giftCards?.totalCount || 0,
loading, loading,
isSelected, clearRowSelection,
listElements, ...rowSelectionUtils,
reset, ...filterUtils,
toggleAll,
toggle,
selectedItemsCount: listElements.length,
pageInfo: data?.giftCards?.pageInfo, pageInfo: data?.giftCards?.pageInfo,
paginationState, paginationState,
params, params,
settings, settings,
updateListSettings, updateListSettings,
numberOfColumns, numberOfColumns,
changeFilters,
resetFilters,
handleSearchChange,
isFilterPresetOpen,
setFilterPresetOpen,
}; };
return ( return (

View file

@ -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" },
);

View file

@ -1,14 +1,19 @@
import { import {
ActiveTab, ActiveTab,
AutocompleteFilterOpts,
Dialog, Dialog,
FetchMoreProps,
FilterOpts,
Filters,
FiltersWithMultipleValues,
MinMax,
Pagination, Pagination,
Search, Search,
SearchProps,
SingleAction, SingleAction,
Sort, Sort,
} from "@dashboard/types"; } from "@dashboard/types";
import { GiftCardListUrlFilters } from "./GiftCardListSearchAndFilters/types";
export type GiftCardListColummns = export type GiftCardListColummns =
| "giftCardCode" | "giftCardCode"
| "tag" | "tag"
@ -40,3 +45,51 @@ export type GiftCardListUrlQueryParams = Pagination &
GiftCardUrlSort & GiftCardUrlSort &
ActiveTab & ActiveTab &
Search; 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<GiftCardListUrlFiltersEnum> &
FiltersWithMultipleValues<GiftCardListUrlFiltersWithMultipleValuesEnum>;
export interface GiftCardListFilterOpts {
tag: FilterOpts<string[]> & AutocompleteFilterOpts;
currency: FilterOpts<string> & AutocompleteFilterOpts;
product: FilterOpts<string[]> & AutocompleteFilterOpts;
usedBy: FilterOpts<string[]> & AutocompleteFilterOpts;
initialBalanceAmount: FilterOpts<MinMax>;
currentBalanceAmount: FilterOpts<MinMax>;
status: FilterOpts<string>;
}
export type SearchWithFetchMoreProps = FetchMoreProps & Search & SearchProps;
export enum GiftCardStatusFilterEnum {
enabled = "enabled",
disabled = "disabled",
}

View file

@ -1,6 +1,8 @@
import CardMenu, { CardMenuItem } from "@dashboard/components/CardMenu"; import CardMenu, { CardMenuItem } from "@dashboard/components/CardMenu";
import { bulkEnableDisableSectionMessages } from "@dashboard/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; import {
import { giftCardsListTableMessages } from "@dashboard/giftCards/GiftCardsList/messages"; bulkEnableDisableSectionMessages,
giftCardsListTableMessages,
} from "@dashboard/giftCards/GiftCardsList/messages";
import useGiftCardActivateToggle from "@dashboard/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle"; import useGiftCardActivateToggle from "@dashboard/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle";
import { ExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types"; import { ExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types";
import { CustomerGiftCardFragment } from "@dashboard/graphql"; import { CustomerGiftCardFragment } from "@dashboard/graphql";

View file

@ -1,4 +1,3 @@
// @ts-strict-ignore
import ActionDialog, { import ActionDialog, {
ActionDialogProps, ActionDialogProps,
} from "@dashboard/components/ActionDialog"; } from "@dashboard/components/ActionDialog";
@ -34,10 +33,10 @@ export interface GiftCardDeleteDialogContentProps<
Partial< Partial<
Pick< Pick<
GiftCardsListConsumerProps, GiftCardsListConsumerProps,
"listElements" | "selectedItemsCount" | "giftCards" | "loading" "selectedRowIds" | "giftCards" | "loading"
> >
> { > {
id?: string; ids?: string[];
giftCard?: TGiftCard; giftCard?: TGiftCard;
singleDeletion: boolean; singleDeletion: boolean;
} }
@ -45,14 +44,13 @@ export interface GiftCardDeleteDialogContentProps<
function GiftCardDeleteDialogContent< function GiftCardDeleteDialogContent<
TGiftCard extends DeleteDialogContentGiftCard, TGiftCard extends DeleteDialogContentGiftCard,
>({ >({
id, ids,
open, open,
onClose, onClose,
onConfirm, onConfirm,
confirmButtonState, confirmButtonState,
singleDeletion, singleDeletion,
selectedItemsCount: listSelectedItemsCount, selectedRowIds,
listElements,
giftCards, giftCards,
giftCard, giftCard,
loading, loading,
@ -62,7 +60,7 @@ function GiftCardDeleteDialogContent<
const [isConsentChecked, setConsentChecked] = useState(false); const [isConsentChecked, setConsentChecked] = useState(false);
const selectedItemsCount = listSelectedItemsCount || SINGLE; const selectedItemsCount = selectedRowIds?.length || SINGLE;
useEffect(() => { useEffect(() => {
if (!open) { if (!open) {
@ -75,17 +73,17 @@ function GiftCardDeleteDialogContent<
return false; return false;
} }
return listElements?.some(hasSelectedGiftCardBalance); return selectedRowIds?.some(hasSelectedGiftCardBalance);
}; };
const hasSelectedGiftCardBalance = (id: string) => { const hasSelectedGiftCardBalance = (id: string) => {
const card = giftCards?.find(getById(id)) || giftCard; const card = giftCards?.find(getById(id)) || giftCard;
return card?.currentBalance?.amount > 0; return (card?.currentBalance?.amount ?? 0) > 0;
}; };
const deletingCardsWithBalance = singleDeletion const deletingCardsWithBalance = singleDeletion
? hasSelectedGiftCardBalance(id) ? hasSelectedGiftCardBalance(ids?.[0] ?? "")
: hasSelectedAnyGiftCardsWithBalance(); : hasSelectedAnyGiftCardsWithBalance();
const submitEnabled = deletingCardsWithBalance ? isConsentChecked : true; const submitEnabled = deletingCardsWithBalance ? isConsentChecked : true;

View file

@ -1,5 +1,4 @@
import { ActionDialogProps } from "@dashboard/components/ActionDialog"; import { ActionDialogProps } from "@dashboard/components/ActionDialog";
import { useGiftCardListDialogs } from "@dashboard/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider";
import { useGiftCardList } from "@dashboard/giftCards/GiftCardsList/providers/GiftCardListProvider"; import { useGiftCardList } from "@dashboard/giftCards/GiftCardsList/providers/GiftCardListProvider";
import { GIFT_CARD_LIST_QUERY } from "@dashboard/giftCards/GiftCardsList/queries"; import { GIFT_CARD_LIST_QUERY } from "@dashboard/giftCards/GiftCardsList/queries";
import { DialogProps } from "@dashboard/types"; import { DialogProps } from "@dashboard/types";
@ -21,14 +20,12 @@ const GiftCardDeleteDialog: React.FC<GiftCardDeleteDialogProps> = ({
refetchQueries = [], refetchQueries = [],
}) => { }) => {
const listProps = useGiftCardList(); const listProps = useGiftCardList();
const { giftCards, loading, selectedItemsCount } = listProps; const { giftCards, loading, selectedRowIds, clearRowSelection } = listProps;
const { id } = useGiftCardListDialogs(); const singleDeletion = selectedRowIds.length === SINGLE;
const singleDeletion = !!id || selectedItemsCount === SINGLE;
const { onDeleteGiftCard, deleteGiftCardOpts } = useGiftCardSingleDelete({ const { onDeleteGiftCard, deleteGiftCardOpts } = useGiftCardSingleDelete({
id, id: selectedRowIds[0],
onClose, onClose,
refetchQueries: [GIFT_CARD_LIST_QUERY, ...refetchQueries], refetchQueries: [GIFT_CARD_LIST_QUERY, ...refetchQueries],
}); });
@ -42,13 +39,19 @@ const GiftCardDeleteDialog: React.FC<GiftCardDeleteDialogProps> = ({
const dialogProps: Pick< const dialogProps: Pick<
ActionDialogProps, ActionDialogProps,
"onConfirm" | "confirmButtonState" "onConfirm" | "confirmButtonState"
> = !!id > = singleDeletion
? { ? {
onConfirm: onDeleteGiftCard, onConfirm: () => {
onDeleteGiftCard();
clearRowSelection();
},
confirmButtonState: deleteGiftCardOpts?.status, confirmButtonState: deleteGiftCardOpts?.status,
} }
: { : {
onConfirm: onBulkDeleteGiftCards, onConfirm: () => {
onBulkDeleteGiftCards();
clearRowSelection();
},
confirmButtonState: bulkDeleteGiftCardOpts?.status, confirmButtonState: bulkDeleteGiftCardOpts?.status,
}; };
@ -56,7 +59,7 @@ const GiftCardDeleteDialog: React.FC<GiftCardDeleteDialogProps> = ({
<GiftCardDeleteDialogContent <GiftCardDeleteDialogContent
{...listProps} {...listProps}
{...dialogProps} {...dialogProps}
id={id} ids={selectedRowIds}
open={open} open={open}
onClose={onClose} onClose={onClose}
singleDeletion={singleDeletion} singleDeletion={singleDeletion}

View file

@ -1,4 +1,3 @@
// @ts-strict-ignore
import { useGiftCardList } from "@dashboard/giftCards/GiftCardsList/providers/GiftCardListProvider"; import { useGiftCardList } from "@dashboard/giftCards/GiftCardsList/providers/GiftCardListProvider";
import { import {
BulkDeleteGiftCardMutation, BulkDeleteGiftCardMutation,
@ -26,27 +25,23 @@ const useGiftCardBulkDelete = ({
const notify = useNotifier(); const notify = useNotifier();
const intl = useIntl(); const intl = useIntl();
const { const { selectedRowIds, clearRowSelection } = useGiftCardList();
listElements,
selectedItemsCount,
reset: resetSelectedItems,
} = useGiftCardList();
const [bulkDeleteGiftCard, bulkDeleteGiftCardOpts] = const [bulkDeleteGiftCard, bulkDeleteGiftCardOpts] =
useBulkDeleteGiftCardMutation({ useBulkDeleteGiftCardMutation({
onCompleted: data => { onCompleted: data => {
const errors = data?.giftCardBulkDelete?.errors; const errors = data?.giftCardBulkDelete?.errors;
if (!errors.length) { if (!errors?.length) {
notify({ notify({
status: "success", status: "success",
text: intl.formatMessage(messages.deleteSuccessAlertText, { text: intl.formatMessage(messages.deleteSuccessAlertText, {
selectedItemsCount, selectedItemsCount: selectedRowIds.length,
}), }),
}); });
onClose(); onClose();
resetSelectedItems(); clearRowSelection();
return; return;
} }
@ -59,7 +54,7 @@ const useGiftCardBulkDelete = ({
}); });
const onBulkDeleteGiftCards = () => const onBulkDeleteGiftCards = () =>
bulkDeleteGiftCard({ variables: { ids: listElements } }); bulkDeleteGiftCard({ variables: { ids: selectedRowIds } });
return { return {
onBulkDeleteGiftCards, onBulkDeleteGiftCards,

View file

@ -8206,77 +8206,6 @@ export function useGiftCardAddNoteMutation(baseOptions?: ApolloReactHooks.Mutati
export type GiftCardAddNoteMutationHookResult = ReturnType<typeof useGiftCardAddNoteMutation>; export type GiftCardAddNoteMutationHookResult = ReturnType<typeof useGiftCardAddNoteMutation>;
export type GiftCardAddNoteMutationResult = Apollo.MutationResult<Types.GiftCardAddNoteMutation>; export type GiftCardAddNoteMutationResult = Apollo.MutationResult<Types.GiftCardAddNoteMutation>;
export type GiftCardAddNoteMutationOptions = Apollo.BaseMutationOptions<Types.GiftCardAddNoteMutation, Types.GiftCardAddNoteMutationVariables>; export type GiftCardAddNoteMutationOptions = Apollo.BaseMutationOptions<Types.GiftCardAddNoteMutation, Types.GiftCardAddNoteMutationVariables>;
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<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>(GiftCardDetailsDocument, options);
}
export function useGiftCardDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>(GiftCardDetailsDocument, options);
}
export type GiftCardDetailsQueryHookResult = ReturnType<typeof useGiftCardDetailsQuery>;
export type GiftCardDetailsLazyQueryHookResult = ReturnType<typeof useGiftCardDetailsLazyQuery>;
export type GiftCardDetailsQueryResult = Apollo.QueryResult<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>;
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<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>(GiftCardCurrenciesDocument, options);
}
export function useGiftCardCurrenciesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>(GiftCardCurrenciesDocument, options);
}
export type GiftCardCurrenciesQueryHookResult = ReturnType<typeof useGiftCardCurrenciesQuery>;
export type GiftCardCurrenciesLazyQueryHookResult = ReturnType<typeof useGiftCardCurrenciesLazyQuery>;
export type GiftCardCurrenciesQueryResult = Apollo.QueryResult<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>;
export const GiftCardBulkActivateDocument = gql` export const GiftCardBulkActivateDocument = gql`
mutation GiftCardBulkActivate($ids: [ID!]!) { mutation GiftCardBulkActivate($ids: [ID!]!) {
giftCardBulkActivate(ids: $ids) { giftCardBulkActivate(ids: $ids) {
@ -8349,6 +8278,77 @@ export function useGiftCardBulkDeactivateMutation(baseOptions?: ApolloReactHooks
export type GiftCardBulkDeactivateMutationHookResult = ReturnType<typeof useGiftCardBulkDeactivateMutation>; export type GiftCardBulkDeactivateMutationHookResult = ReturnType<typeof useGiftCardBulkDeactivateMutation>;
export type GiftCardBulkDeactivateMutationResult = Apollo.MutationResult<Types.GiftCardBulkDeactivateMutation>; export type GiftCardBulkDeactivateMutationResult = Apollo.MutationResult<Types.GiftCardBulkDeactivateMutation>;
export type GiftCardBulkDeactivateMutationOptions = Apollo.BaseMutationOptions<Types.GiftCardBulkDeactivateMutation, Types.GiftCardBulkDeactivateMutationVariables>; export type GiftCardBulkDeactivateMutationOptions = Apollo.BaseMutationOptions<Types.GiftCardBulkDeactivateMutation, Types.GiftCardBulkDeactivateMutationVariables>;
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<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>(GiftCardDetailsDocument, options);
}
export function useGiftCardDetailsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>(GiftCardDetailsDocument, options);
}
export type GiftCardDetailsQueryHookResult = ReturnType<typeof useGiftCardDetailsQuery>;
export type GiftCardDetailsLazyQueryHookResult = ReturnType<typeof useGiftCardDetailsLazyQuery>;
export type GiftCardDetailsQueryResult = Apollo.QueryResult<Types.GiftCardDetailsQuery, Types.GiftCardDetailsQueryVariables>;
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<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>(GiftCardCurrenciesDocument, options);
}
export function useGiftCardCurrenciesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>(GiftCardCurrenciesDocument, options);
}
export type GiftCardCurrenciesQueryHookResult = ReturnType<typeof useGiftCardCurrenciesQuery>;
export type GiftCardCurrenciesLazyQueryHookResult = ReturnType<typeof useGiftCardCurrenciesLazyQuery>;
export type GiftCardCurrenciesQueryResult = Apollo.QueryResult<Types.GiftCardCurrenciesQuery, Types.GiftCardCurrenciesQueryVariables>;
export const DeleteGiftCardDocument = gql` export const DeleteGiftCardDocument = gql`
mutation DeleteGiftCard($id: ID!) { mutation DeleteGiftCard($id: ID!) {
giftCardDelete(id: $id) { giftCardDelete(id: $id) {

View file

@ -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<string> | null, oldTags: Array<string> | 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 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<string> | null, oldTags: Array<string> | 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<string> | null, oldTags: Array<string> | 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<string> };
export type GiftCardBulkActivateMutationVariables = Exact<{ export type GiftCardBulkActivateMutationVariables = Exact<{
ids: Array<Scalars['ID']> | Scalars['ID']; ids: Array<Scalars['ID']> | 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 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<string> | null, oldTags: Array<string> | 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<string> };
export type DeleteGiftCardMutationVariables = Exact<{ export type DeleteGiftCardMutationVariables = Exact<{
id: Scalars['ID']; id: Scalars['ID'];
}>; }>;

View file

@ -4,11 +4,23 @@ import {
getActiveTabIndexAfterTabDelete, getActiveTabIndexAfterTabDelete,
getNextUniqueTabName, getNextUniqueTabName,
} from "@dashboard/products/views/ProductList/utils"; } 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 { prepareQs } from "@dashboard/utils/filters/qs";
import { stringify } from "qs"; import { stringify } from "qs";
import { useState } from "react"; import { useState } from "react";
export interface UseFilterPresets {
presetIdToDelete: number | null;
setPresetIdToDelete: React.Dispatch<React.SetStateAction<number | null>>;
presets: GetFilterTabsOutput<string>;
selectedPreset: number | undefined;
onPresetChange: (index: number) => void;
onPresetDelete: () => void;
onPresetSave: (data: SaveFilterTabDialogFormData) => void;
onPresetUpdate: (tabName: string) => void;
hasPresetsChange: () => boolean;
}
export const useFilterPresets = < export const useFilterPresets = <
T extends { activeTab?: string; action?: string }, T extends { activeTab?: string; action?: string },
>({ >({
@ -21,7 +33,7 @@ export const useFilterPresets = <
reset: () => void; reset: () => void;
getUrl: () => string; getUrl: () => string;
storageUtils: StorageUtils<string>; storageUtils: StorageUtils<string>;
}) => { }): UseFilterPresets => {
const navigate = useNavigator(); const navigate = useNavigator();
const baseUrl = getUrl(); const baseUrl = getUrl();
const [presetIdToDelete, setPresetIdToDelete] = useState<number | null>(null); const [presetIdToDelete, setPresetIdToDelete] = useState<number | null>(null);

View file

@ -10,7 +10,7 @@ export interface UseListSettings<TColumns extends string = string> {
settings: ListSettings<TColumns>; settings: ListSettings<TColumns>;
updateListSettings: <T extends keyof ListSettings<TColumns>>( updateListSettings: <T extends keyof ListSettings<TColumns>>(
key: T, key: T,
value: ListSettings<TColumns>[T], value: ListSettings[T],
) => void; ) => void;
} }

View file

@ -1,7 +1,16 @@
import { Pagination } from "@dashboard/types"; import { Pagination } from "@dashboard/types";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
export const useRowSelection = (paginationParams?: Pagination) => { export interface UseRowSelection {
selectedRowIds: string[];
setSelectedRowIds: React.Dispatch<React.SetStateAction<string[]>>;
clearRowSelection: () => void;
setClearDatagridRowSelectionCallback: (callback: () => void) => void;
}
export const useRowSelection = (
paginationParams?: Pagination,
): UseRowSelection => {
const [selectedRowIds, setSelectedRowIds] = useState<string[]>([]); const [selectedRowIds, setSelectedRowIds] = useState<string[]>([]);
// Keep reference to clear datagrid selection function // Keep reference to clear datagrid selection function

View file

@ -45,6 +45,10 @@ body {
background: none !important; background: none !important;
} }
.remove-content-padding-top .MuiCardContent-root {
padding-top: 0 !important;
}
[id*="ScrollableDialog"] .infinite-scroll-component { [id*="ScrollableDialog"] .infinite-scroll-component {
overflow: hidden !important; overflow: hidden !important;
} }

View file

@ -43,7 +43,7 @@ export enum ListViews {
WAREHOUSE_LIST = "WAREHOUSE_LIST", WAREHOUSE_LIST = "WAREHOUSE_LIST",
WEBHOOK_LIST = "WEBHOOK_LIST", WEBHOOK_LIST = "WEBHOOK_LIST",
TRANSLATION_ATTRIBUTE_VALUE_LIST = "TRANSLATION_ATTRIBUTE_VALUE_LIST", TRANSLATION_ATTRIBUTE_VALUE_LIST = "TRANSLATION_ATTRIBUTE_VALUE_LIST",
GIFT_CARD_LIST = " GIFT_CARD_LIST", GIFT_CARD_LIST = "GIFT_CARD_LIST",
} }
export interface ListProps<TColumns extends string = string> { export interface ListProps<TColumns extends string = string> {