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

View file

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

View file

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

View file

@ -6,4 +6,5 @@ export const GIFT_CARD_SHOW_MORE = {
csv: 'input[value="CSV"]',
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"]',
dialogBackButton: '[data-test-id="back"]',
expandMetadataButton: '[data-test-id="expand"]',
bulkDeleteButton: '[data-test-id="bulk-delete-button"]',
};

View file

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

View file

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

View file

@ -40,6 +40,7 @@ export function getGiftCards(first) {
node{
displayCode
id
code
isActive
expiryDate
tags{
@ -159,3 +160,17 @@ export function deleteGiftCard(giftCardId) {
}`;
return cy.sendRequestWithQuery(mutation);
}
export function ensureGiftCardIsCreated(giftCardId, retries = 0) {
return getGiftCards(100, giftCardId).then(giftCardsListResponse => {
if (JSON.stringify(giftCardsListResponse).includes(giftCardId)) {
return;
} else if (retries > 4) {
throw new Error(
`Gift card with id: ${giftCardId} should be on list but could not find it. Retried for ${retries} times`,
);
} else {
cy.wait(5000);
ensureGiftCardIsAdded(giftCardId, retries + 1);
}
});
}

View file

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

View file

@ -1,8 +1,10 @@
import { SHARED_ELEMENTS } from "../../../elements";
import { GIFT_CARD_DIALOG } from "../../../elements/catalog/giftCard/giftCardDialog";
import {
GIFT_CARD_LIST,
giftCardRow
giftCardRow,
} from "../../../elements/catalog/giftCard/giftCardList";
import { GIFT_CARD_SHOW_MORE } from "../../../elements/catalog/giftCard/giftCardShowMore";
import { GIFT_CARD_UPDATE } from "../../../elements/catalog/giftCard/giftCardUpdate";
import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors";
import { giftCardDetailsUrl, urlList } from "../../../fixtures/urlList";
@ -11,7 +13,7 @@ export function openAndFillUpCreateGiftCardDialog({
note,
tag,
amount,
currency
currency,
}) {
cy.visit(urlList.giftCards)
.get(GIFT_CARD_LIST.issueCardButton)
@ -39,7 +41,7 @@ export function saveGiftCard() {
}
export const expiryPeriods = {
MONTH: GIFT_CARD_DIALOG.expirationOptions.expiryPeriodMonthType
MONTH: GIFT_CARD_DIALOG.expirationOptions.expiryPeriodMonthType,
};
export function setExpiryPeriod(amount, period) {
@ -77,6 +79,20 @@ export function selectGiftCard(giftCardId) {
.find(GIFT_CARD_LIST.selectGiftCardCheckbox)
.click();
}
export function selectGiftCardOnListView(giftCardCode) {
const splitCode = giftCardCode.split("-");
const lastCodeElement = splitCode.slice(-1).toString();
cy.log(lastCodeElement);
return cy
.contains(
`${SHARED_ELEMENTS.dataGridTable} table tbody tr`,
lastCodeElement,
)
.invoke("index")
.then(index => {
cy.clickGridCell(0, index);
});
}
export function enterAndSelectGiftCards(giftCardsIds) {
const alias = "GiftCardList";
@ -86,6 +102,40 @@ export function enterAndSelectGiftCards(giftCardsIds) {
elementsGraphqlAlias: alias,
elementsName: "giftCards",
elementsIds: giftCardsIds,
actionFunction: selectGiftCard
actionFunction: selectGiftCard,
});
}
export function openExportGiftCardsDialog() {
cy.get(BUTTON_SELECTORS.showMoreButton)
.click({ force: true })
.get(GIFT_CARD_SHOW_MORE.exportCodesMenu)
.click();
}
export function selectSelectedRecordsButton() {
cy.get(GIFT_CARD_SHOW_MORE.exportSelectedRecords).click();
}
export function selectExportAsCSVButton() {
cy.get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.csv).click();
}
export function selectExportAsXLSXButton() {
cy.get(GIFT_CARD_SHOW_MORE.exportAsRadioBtn.xlsx).click();
}
export function clickDeactivateButton() {
cy.get(GIFT_CARD_LIST.deactivateGiftCardButton).click();
}
export function bulkDeleteRecords() {
cy.get(BUTTON_SELECTORS.bulkDeleteButton)
.click()
.get(GIFT_CARD_UPDATE.consentCheckbox)
.click()
.get(BUTTON_SELECTORS.submit)
.click();
}
export function getUrlWithFilteredTags(url, tagsArray) {
const urlHelper = url + "?asc=true&sort=usedBy";
let tagsPath = "";
tagsArray.forEach((tag, index) => {
tagsPath += `&tag%5B${index}%5D=${tag}`;
});
return urlHelper + tagsPath;
}

View file

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

View file

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

View file

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

View file

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

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 GiftCardListSearchAndFilters from "./GiftCardListSearchAndFilters";
import { GiftCardsListDatagrid } from "./GiftCardsListDatagrid";
import GiftCardsListHeader from "./GiftCardsListHeader";
import GiftCardsListOrderInfoCard from "./GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard";
import GiftCardsListTable from "./GiftCardsListTable";
const GiftCardsListPage: React.FC = () => (
<>
<GiftCardsListHeader />
<GiftCardsListTable />
<VerticalSpacer spacing={2} />
<GiftCardListSearchAndFilters />
<GiftCardsListDatagrid />
<GiftCardsListOrderInfoCard />
</>
);

View file

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

View file

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

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 { Button } from "@dashboard/components/Button";
import CardMenu, { CardMenuItem } from "@dashboard/components/CardMenu";
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect";
import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl";
import { Box, Button, ChevronRightIcon } from "@saleor/macaw-ui/next";
import React from "react";
import { useIntl } from "react-intl";
import { giftCardSettingsUrl } from "../../urls";
import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages";
import { useGiftCardListDialogs } from "../providers/GiftCardListDialogsProvider";
import { useGiftCardList } from "../providers/GiftCardListProvider";
import GiftCardsListHeaderAlert from "./GiftCardsListHeaderAlert";
const GiftCardsListHeader: React.FC = () => {
const intl = useIntl();
const navigate = useNavigator();
const { openCreateDialog, openBulkCreateDialog, openExportDialog } =
useGiftCardListDialogs();
const {
openCreateDialog,
openBulkCreateDialog,
openExportDialog,
openSearchDeleteDialog,
openSearchSaveDialog,
} = useGiftCardListDialogs();
const {
hasPresetsChange,
selectedPreset,
presets,
onPresetUpdate,
setPresetIdToDelete,
onPresetChange,
resetFilters,
isFilterPresetOpen,
setFilterPresetOpen,
} = useGiftCardList();
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),
testId: "settingsMenuItem",
@ -37,13 +96,9 @@ const GiftCardsListHeader: React.FC = () => {
testId: "exportCodesMenuItem",
onSelect: openExportDialog,
},
];
return (
<>
<TopNav title={intl.formatMessage(sectionNames.giftCards)}>
<CardMenu menuItems={menuItems} data-test-id="menu" />
<HorizontalSpacer spacing={2} />
]}
data-test-id="menu"
/>
<Button
variant="primary"
onClick={openCreateDialog}
@ -51,6 +106,8 @@ const GiftCardsListHeader: React.FC = () => {
>
{intl.formatMessage(messages.issueButtonLabel)}
</Button>
</Box>
</Box>
</TopNav>
<GiftCardsListHeaderAlert />
</>

View file

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

View file

@ -2,22 +2,24 @@ import Link from "@dashboard/components/Link";
import { ProductTypeKindEnum } from "@dashboard/graphql";
import { productAddUrl } from "@dashboard/products/urls";
import { productTypeAddUrl } from "@dashboard/productTypes/urls";
import { sprinkles } from "@saleor/macaw-ui/next";
import React from "react";
import { FormattedMessage } from "react-intl";
import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages";
import { useHeaderStyles as useStyles } from "../styles";
interface GiftCardsListHeaderAlertContentProps {
giftCardProductTypesExist: boolean;
giftCardProductsExist: boolean;
}
const alertLinkClassName = sprinkles({
fontSize: "captionSmall",
});
const GiftCardsListHeaderAlertContent: React.FC<
GiftCardsListHeaderAlertContentProps
> = ({ giftCardProductTypesExist, giftCardProductsExist }) => {
const classes = useStyles({});
const giftCardProductTypeUrl = productTypeAddUrl({
kind: ProductTypeKindEnum.GIFT_CARD,
});
@ -30,7 +32,7 @@ const GiftCardsListHeaderAlertContent: React.FC<
{...messages.noGiftCardsProductTypes}
values={{
createGiftCardProductType: (
<Link href={giftCardProductTypeUrl} className={classes.alertLink}>
<Link href={giftCardProductTypeUrl} className={alertLinkClassName}>
<FormattedMessage {...messages.createGiftCardProductType} />
</Link>
),
@ -47,7 +49,7 @@ const GiftCardsListHeaderAlertContent: React.FC<
createGiftCardProduct: (
<Link
href={giftCardCreateGiftCardProductUrl}
className={classes.alertLink}
className={alertLinkClassName}
>
<FormattedMessage {...messages.createGiftCardProduct} />
</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 { orderGiftCardBoughtPath } from "@dashboard/orders/urls";
import { Typography } from "@material-ui/core";
import { Alert } from "@saleor/macaw-ui";
import { Text } from "@saleor/macaw-ui/next";
import React from "react";
import { FormattedMessage } from "react-intl";
@ -9,7 +9,7 @@ import { giftCardListOrderCardMessages as messages } from "./messages";
const GiftCardsListOrderInfoCard: React.FC = () => (
<Alert variant="info" close={false}>
<Typography>
<Text variant="caption" size="large">
<FormattedMessage
{...messages.giftCardOrderInfoMessage}
values={{
@ -18,7 +18,7 @@ const GiftCardsListOrderInfoCard: React.FC = () => (
),
}}
/>
</Typography>
</Text>
</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";
import { defineMessages, IntlShape } from "react-intl";
import { GiftCardListUrlQueryParams } from "../types";
import {
GiftCardListFilterKeys,
GiftCardListFilterOpts,
GiftCardListUrlFilters,
GiftCardListUrlFiltersEnum,
GiftCardListUrlQueryParams,
GiftCardStatusFilterEnum,
SearchWithFetchMoreProps,
} from "./types";
@ -337,8 +337,7 @@ export function createFilterStructure(
];
}
export const { deleteFilterTab, getFilterTabs, saveFilterTab } =
createFilterTabUtils<GiftCardListUrlFilters>(GIFT_CARD_FILTERS_KEY);
export const storageUtils = createFilterTabUtils<string>(GIFT_CARD_FILTERS_KEY);
export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
createFilterUtils<GiftCardListUrlQueryParams, GiftCardListUrlFilters>(

View file

@ -111,3 +111,45 @@ export const giftCardUpdateFormMessages = defineMessages({
description: "invalid date in expirydate field content",
},
});
export const bulkEnableDisableSectionMessages = defineMessages({
enableLabel: {
id: "hz+9ES",
defaultMessage: "Activate",
description: "bulk activate label",
},
disableLabel: {
id: "IzEVek",
defaultMessage: "Deactivate",
description: "bulk disable label",
},
deleteLabel: {
id: "qkt/Km",
defaultMessage: "Delete",
description: "bulk delete label",
},
successActivateAlertText: {
id: "IwEQvz",
defaultMessage:
"Successfully activated gift {count,plural,one{card} other{cards}}",
description: "success activate alert message",
},
successDeactivateAlertText: {
id: "SO56cv",
defaultMessage:
"Successfully deactivated gift {count,plural,one{card} other{cards}}",
description: "success deactivate alert message",
},
errorActivateAlertText: {
id: "KcsJKm",
defaultMessage:
"Error activating gift {count,plural,one{card} other{cards}}",
description: "error with activatation alert message",
},
errorDeactivateAlertText: {
id: "C90nKP",
defaultMessage:
"Errors deactivating gift {count,plural,one{card} other{cards}}",
description: "error with deactivatation alert message",
},
});

View file

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

View file

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

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 {
ActiveTab,
AutocompleteFilterOpts,
Dialog,
FetchMoreProps,
FilterOpts,
Filters,
FiltersWithMultipleValues,
MinMax,
Pagination,
Search,
SearchProps,
SingleAction,
Sort,
} from "@dashboard/types";
import { GiftCardListUrlFilters } from "./GiftCardListSearchAndFilters/types";
export type GiftCardListColummns =
| "giftCardCode"
| "tag"
@ -40,3 +45,51 @@ export type GiftCardListUrlQueryParams = Pagination &
GiftCardUrlSort &
ActiveTab &
Search;
export enum GiftCardListUrlFiltersEnum {
currency = "currency",
initialBalanceAmountFrom = "initialBalanceAmountFrom",
initialBalanceAmountTo = "initialBalanceAmountTo",
currentBalanceAmountFrom = "currentBalanceAmountFrom",
currentBalanceAmountTo = "currentBalanceAmountTo",
status = "status",
}
export enum GiftCardListUrlFiltersWithMultipleValuesEnum {
tag = "tag",
product = "product",
usedBy = "usedBy",
}
export enum GiftCardListFilterKeys {
currency = "currency",
balance = "balance",
initialBalance = "initialBalance",
currentBalance = "currentBalance",
initialBalanceAmount = "initialBalanceAmount",
currentBalanceAmount = "currentBalanceAmount",
tag = "tag",
product = "product",
usedBy = "usedBy",
status = "status",
}
export type GiftCardListUrlFilters = Filters<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 { bulkEnableDisableSectionMessages } from "@dashboard/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages";
import { giftCardsListTableMessages } from "@dashboard/giftCards/GiftCardsList/messages";
import {
bulkEnableDisableSectionMessages,
giftCardsListTableMessages,
} from "@dashboard/giftCards/GiftCardsList/messages";
import useGiftCardActivateToggle from "@dashboard/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/hooks/useGiftCardActivateToggle";
import { ExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/types";
import { CustomerGiftCardFragment } from "@dashboard/graphql";

View file

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

View file

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

View file

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

View file

@ -8206,77 +8206,6 @@ export function useGiftCardAddNoteMutation(baseOptions?: ApolloReactHooks.Mutati
export type GiftCardAddNoteMutationHookResult = ReturnType<typeof useGiftCardAddNoteMutation>;
export type GiftCardAddNoteMutationResult = Apollo.MutationResult<Types.GiftCardAddNoteMutation>;
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`
mutation GiftCardBulkActivate($ids: [ID!]!) {
giftCardBulkActivate(ids: $ids) {
@ -8349,6 +8278,77 @@ export function useGiftCardBulkDeactivateMutation(baseOptions?: ApolloReactHooks
export type GiftCardBulkDeactivateMutationHookResult = ReturnType<typeof useGiftCardBulkDeactivateMutation>;
export type GiftCardBulkDeactivateMutationResult = Apollo.MutationResult<Types.GiftCardBulkDeactivateMutation>;
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`
mutation DeleteGiftCard($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 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<{
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 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<{
id: Scalars['ID'];
}>;

View file

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

View file

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

View file

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

View file

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