Allow to copy and go to product details on order details datagrid (#3744)

This commit is contained in:
Paweł Chyła 2023-06-20 10:29:28 +02:00 committed by GitHub
parent 3508852b4d
commit 2db64cd135
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 230 additions and 71 deletions

View file

@ -287,7 +287,7 @@ describe("Orders", () => {
address, address,
}).then(unconfirmedOrderResponse => { }).then(unconfirmedOrderResponse => {
cy.visit(urlList.orders + `${unconfirmedOrderResponse.order.id}`); cy.visit(urlList.orders + `${unconfirmedOrderResponse.order.id}`);
deleteProductFromGridTableOnIndex(0); deleteProductFromGridTableOnIndex(1);
cy.contains(MESSAGES.noProductFound).should("be.visible"); cy.contains(MESSAGES.noProductFound).should("be.visible");
cy.get(ORDERS_SELECTORS.productDeleteFromRowButton).should("not.exist"); cy.get(ORDERS_SELECTORS.productDeleteFromRowButton).should("not.exist");
}); });

View file

@ -91,7 +91,7 @@ describe("Tests for pages", () => {
`should create page with ${attributeType} attribute`, `should create page with ${attributeType} attribute`,
{ tags: ["@pages", "@allEnv", "@stable"] }, { tags: ["@pages", "@allEnv", "@stable"] },
() => { () => {
const randomName = `${startsWith}${faker.datatype.number()}`; const randomName = `AAA-${startsWith}${faker.datatype.number()}`;
const attributeValues = [attributeValuesOnPage[attributeType]]; const attributeValues = [attributeValuesOnPage[attributeType]];
createAttribute({ createAttribute({
name: randomName, name: randomName,

View file

@ -121,7 +121,7 @@ describe("As an admin I should be able to create product", () => {
fillUpPriceList(prices.sellingPrice); fillUpPriceList(prices.sellingPrice);
fillUpPriceList(prices.costPrice, priceInputLists.costPrice); fillUpPriceList(prices.costPrice, priceInputLists.costPrice);
cy.get(PRODUCT_DETAILS.skuInput) cy.get(PRODUCT_DETAILS.skuInput)
.type(randomName) .type(randomName, { force: true })
.addAliasToGraphRequest("ProductDetails") .addAliasToGraphRequest("ProductDetails")
.get(BUTTON_SELECTORS.confirm) .get(BUTTON_SELECTORS.confirm)
.click() .click()

View file

@ -121,7 +121,8 @@ describe("Updating products without sku", () => {
.get(SHARED_ELEMENTS.skeleton) .get(SHARED_ELEMENTS.skeleton)
.should("not.exist") .should("not.exist")
.get(VARIANTS_SELECTORS.skuTextField) .get(VARIANTS_SELECTORS.skuTextField)
.type(sku) .scrollIntoView()
.type(sku, { force: true })
.addAliasToGraphRequest("VariantUpdate") .addAliasToGraphRequest("VariantUpdate")
.get(BUTTON_SELECTORS.confirm) .get(BUTTON_SELECTORS.confirm)
.click() .click()
@ -157,7 +158,7 @@ describe("Updating products without sku", () => {
.get(SHARED_ELEMENTS.skeleton) .get(SHARED_ELEMENTS.skeleton)
.should("not.exist") .should("not.exist")
.get(VARIANTS_SELECTORS.skuTextField) .get(VARIANTS_SELECTORS.skuTextField)
.type(sku) .type(sku, { force: true })
.addAliasToGraphRequest("VariantUpdate") .addAliasToGraphRequest("VariantUpdate")
.get(BUTTON_SELECTORS.confirm) .get(BUTTON_SELECTORS.confirm)
.click() .click()

View file

@ -1,6 +1,6 @@
export const AVAILABLE_CHANNELS_FORM = { export const AVAILABLE_CHANNELS_FORM = {
manageChannelsButton: "[data-test-id='channels-availability-manage-button']", manageChannelsButton: "[data-test-id='channels-availability-manage-button']",
assignedChannels: "[data-test-id='expand-icon']", assignedChannels: "[data-test-id='channel-availability-item']",
publishedRadioButtons: "[name*='isPublished'] > ", publishedRadioButtons: "[name*='isPublished'] > ",
availableForPurchaseRadioButtons: "[id*='isAvailableForPurchase']", availableForPurchaseRadioButtons: "[id*='isAvailableForPurchase']",
radioButtonsValueTrue: "[value='true']", radioButtonsValueTrue: "[value='true']",

View file

@ -9,16 +9,17 @@ export const ORDERS_SELECTORS = {
orderFulfillmentFrame: "[data-test-id='order-fulfillment']", orderFulfillmentFrame: "[data-test-id='order-fulfillment']",
refundButton: '[data-test-id="refund-button"]', refundButton: '[data-test-id="refund-button"]',
fulfillMenuButton: '[data-test-id="fulfill-menu"]', fulfillMenuButton: '[data-test-id="fulfill-menu"]',
priceCellFirstRowOrderDetails: "[id='glide-cell-4-0']", priceCellFirstRowOrderDetails: "[id='glide-cell-5-0']",
productNameSecondRowOrderDetails: "[id='glide-cell-1-1']", productNameSecondRowOrderDetails: "[id='glide-cell-1-1']",
quantityCellFirstRowOrderDetails: "[id='glide-cell-3-0']", quantityCellFirstRowOrderDetails: "[id='glide-cell-4-0']",
gridClip: "[class='clip-region']",
discountFixedPriceButton: '[data-test-id="FIXED"]', discountFixedPriceButton: '[data-test-id="FIXED"]',
discountAmountField: '[data-test-id="price-field"]', discountAmountField: '[data-test-id="price-field"]',
discountReasonField: '[data-test-id="discount-reason"]', discountReasonField: '[data-test-id="discount-reason"]',
orderSummarySubtotalPriceRow: '[data-test-id="order-subtotal-price"]', orderSummarySubtotalPriceRow: '[data-test-id="order-subtotal-price"]',
orderSummaryTotalPriceRow: '[data-test-id="order-total-price"]', orderSummaryTotalPriceRow: '[data-test-id="order-total-price"]',
dataGridTable: "[data-testid='data-grid-canvas']", dataGridTable: "[data-testid='data-grid-canvas']",
productDeleteFromRowButton: "[data-test-id='row-action-button']", productDeleteFromRowButton: "[data-test-id='delete-order-line']",
markAsPaidButton: '[data-test-id="markAsPaidButton"]', markAsPaidButton: '[data-test-id="markAsPaidButton"]',
grantRefundButton: '[data-test-id="grantRefundButton"]', grantRefundButton: '[data-test-id="grantRefundButton"]',
transactionReferenceInput: '[data-test-id="transaction-reference-input"]', transactionReferenceInput: '[data-test-id="transaction-reference-input"]',

View file

@ -84,7 +84,7 @@ export function fillUpAllCommonFieldsInCreateAndUpdate({
export function fillUpProductGeneralInfo({ name, description, rating }) { export function fillUpProductGeneralInfo({ name, description, rating }) {
return cy return cy
.get(PRODUCT_DETAILS.productNameInput) .get(PRODUCT_DETAILS.productNameInput)
.click() .click({ force: true })
.clearAndType(name) .clearAndType(name)
.get(PRODUCT_DETAILS.descriptionInput) .get(PRODUCT_DETAILS.descriptionInput)
.clearAndType(description) .clearAndType(description)

View file

@ -1,5 +1,6 @@
import { import {
ADD_PRODUCT_TO_ORDER_DIALOG, ADD_PRODUCT_TO_ORDER_DIALOG,
BUTTON_SELECTORS,
CHANNEL_FORM_SELECTORS, CHANNEL_FORM_SELECTORS,
DRAFT_ORDER_SELECTORS, DRAFT_ORDER_SELECTORS,
ORDERS_SELECTORS, ORDERS_SELECTORS,
@ -32,22 +33,31 @@ export function applyFixedLineDiscountForProduct(
.waitForRequestAndCheckIfNoErrors("@OrderLineDiscountUpdate"); .waitForRequestAndCheckIfNoErrors("@OrderLineDiscountUpdate");
} }
export function changeQuantityOfProducts() { export function changeQuantityOfProducts() {
cy.addAliasToGraphRequest("OrderLineUpdate");
cy.get(ORDERS_SELECTORS.dataGridTable).should("be.visible"); cy.get(ORDERS_SELECTORS.dataGridTable).should("be.visible");
cy.get(ORDERS_SELECTORS.quantityCellFirstRowOrderDetails) cy.get(ORDERS_SELECTORS.quantityCellFirstRowOrderDetails)
.dblclick({ force: true }) .click({ force: true })
.type("2", { force: true }); .wait(200)
cy.addAliasToGraphRequest("OrderLineUpdate") .click({ force: true })
.wait(1000);
cy.get(ORDERS_SELECTORS.gridClip)
.find("input")
.clear({ force: true })
.type("2");
// grid expects focus to be dismissed from cell - because of that extra action needed which blur focus from cell (other more elegant build in actions was not working) // grid expects focus to be dismissed from cell - because of that extra action needed which blur focus from cell (other more elegant build in actions was not working)
.get(SHARED_ELEMENTS.pageHeader) cy.get(SHARED_ELEMENTS.pageHeader)
.click() .click()
.waitForRequestAndCheckIfNoErrors("@OrderLineUpdate"); .waitForRequestAndCheckIfNoErrors("@OrderLineUpdate");
} }
export function deleteProductFromGridTableOnIndex(trIndex = 0) { export function deleteProductFromGridTableOnIndex(trIndex = 0) {
cy.get(ORDERS_SELECTORS.dataGridTable).should("be.visible"); cy.get(ORDERS_SELECTORS.dataGridTable).should("be.visible");
cy.addAliasToGraphRequest("OrderLineDelete") cy.addAliasToGraphRequest("OrderLineDelete")
.get(ORDERS_SELECTORS.productDeleteFromRowButton) .get(BUTTON_SELECTORS.showMoreButton)
.eq(trIndex) .eq(trIndex)
.click() .click()
.get(ORDERS_SELECTORS.productDeleteFromRowButton)
.click()
.wait("@OrderLineDelete"); .wait("@OrderLineDelete");
} }
export function addNewProductToOrder(productIndex = 0, variantIndex = 0) { export function addNewProductToOrder(productIndex = 0, variantIndex = 0) {

View file

@ -61,6 +61,7 @@ export function addBooleanAttributeValue() {
export function addNumericAttributeValue(attributeValue) { export function addNumericAttributeValue(attributeValue) {
cy.get(PAGE_DETAILS_SELECTORS.numericAttributeValueInput).type( cy.get(PAGE_DETAILS_SELECTORS.numericAttributeValueInput).type(
attributeValue, attributeValue,
{ force: true },
); );
} }

View file

@ -3632,6 +3632,9 @@
"context": "button", "context": "button",
"string": "Create Channel" "string": "Create Channel"
}, },
"OK5+Fh": {
"string": "Variant"
},
"OKGd/k": { "OKGd/k": {
"context": "order history message", "context": "order history message",
"string": "Order was created from draft" "string": "Order was created from draft"
@ -4652,6 +4655,9 @@
"context": "section header", "context": "section header",
"string": "Coming Soon" "string": "Coming Soon"
}, },
"VYK2nN": {
"string": "Product details"
},
"VZsE96": { "VZsE96": {
"string": "Collection Name" "string": "Collection Name"
}, },

14
package-lock.json generated
View file

@ -27,7 +27,7 @@
"@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/lab": "^4.0.0-alpha.61",
"@material-ui/styles": "^4.11.4", "@material-ui/styles": "^4.11.4",
"@reach/auto-id": "^0.16.0", "@reach/auto-id": "^0.16.0",
"@saleor/macaw-ui": "0.8.0-pre.90", "@saleor/macaw-ui": "0.8.0-pre.96",
"@saleor/sdk": "0.6.0", "@saleor/sdk": "0.6.0",
"@sentry/react": "^6.0.0", "@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6", "@types/faker": "^5.1.6",
@ -7950,9 +7950,9 @@
} }
}, },
"node_modules/@saleor/macaw-ui": { "node_modules/@saleor/macaw-ui": {
"version": "0.8.0-pre.90", "version": "0.8.0-pre.96",
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.90.tgz", "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.96.tgz",
"integrity": "sha512-V3x2HR/piQOaH+X/5OTFZPNJmW0HONUspXSAKfwckMPBiZ4upOg+zbqCaUvkq8Bb+qGD0J4FTwptQuW89LvrCw==", "integrity": "sha512-uPVodJvUcmtz+pXAGZUZ87TZl77bx2Qt1y2jKLqZTzQGeKmqDwRDPCb0Qcb4TI7fDN93W0hFqpGIpoZm5DcbVQ==",
"dependencies": { "dependencies": {
"@dessert-box/react": "^0.4.0", "@dessert-box/react": "^0.4.0",
"@floating-ui/react-dom-interactions": "^0.5.0", "@floating-ui/react-dom-interactions": "^0.5.0",
@ -41317,9 +41317,9 @@
} }
}, },
"@saleor/macaw-ui": { "@saleor/macaw-ui": {
"version": "0.8.0-pre.90", "version": "0.8.0-pre.96",
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.90.tgz", "resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.96.tgz",
"integrity": "sha512-V3x2HR/piQOaH+X/5OTFZPNJmW0HONUspXSAKfwckMPBiZ4upOg+zbqCaUvkq8Bb+qGD0J4FTwptQuW89LvrCw==", "integrity": "sha512-uPVodJvUcmtz+pXAGZUZ87TZl77bx2Qt1y2jKLqZTzQGeKmqDwRDPCb0Qcb4TI7fDN93W0hFqpGIpoZm5DcbVQ==",
"requires": { "requires": {
"@dessert-box/react": "^0.4.0", "@dessert-box/react": "^0.4.0",
"@floating-ui/react-dom-interactions": "^0.5.0", "@floating-ui/react-dom-interactions": "^0.5.0",

View file

@ -34,7 +34,7 @@
"@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/lab": "^4.0.0-alpha.61",
"@material-ui/styles": "^4.11.4", "@material-ui/styles": "^4.11.4",
"@reach/auto-id": "^0.16.0", "@reach/auto-id": "^0.16.0",
"@saleor/macaw-ui": "0.8.0-pre.90", "@saleor/macaw-ui": "0.8.0-pre.96",
"@saleor/sdk": "0.6.0", "@saleor/sdk": "0.6.0",
"@sentry/react": "^6.0.0", "@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6", "@types/faker": "^5.1.6",

View file

@ -35,6 +35,8 @@ export interface CardMenuProps {
outlined?: boolean; outlined?: boolean;
Icon?: React.ElementType<{}>; Icon?: React.ElementType<{}>;
IconButtonProps?: IconButtonProps; IconButtonProps?: IconButtonProps;
autoFocusItem?: boolean;
showMenuIcon?: boolean;
} }
const useStyles = makeStyles( const useStyles = makeStyles(
@ -77,6 +79,8 @@ const CardMenu: React.FC<CardMenuProps> = props => {
outlined, outlined,
Icon: icon, Icon: icon,
IconButtonProps = {}, IconButtonProps = {},
autoFocusItem = true,
showMenuIcon = false,
...rest ...rest
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -171,7 +175,7 @@ const CardMenu: React.FC<CardMenuProps> = props => {
<Paper className={classes.paper} elevation={8}> <Paper className={classes.paper} elevation={8}>
<ClickAwayListener onClickAway={handleClose}> <ClickAwayListener onClickAway={handleClose}>
<MenuList <MenuList
autoFocusItem={open} autoFocusItem={autoFocusItem && open}
id="menu-list-grow" id="menu-list-grow"
onKeyDown={handleListKeyDown} onKeyDown={handleListKeyDown}
> >
@ -198,7 +202,9 @@ const CardMenu: React.FC<CardMenuProps> = props => {
<CircularProgress size={24} /> <CircularProgress size={24} />
</> </>
) : ( ) : (
<Typography>{menuItem.label}</Typography> <Typography>
{showMenuIcon && menuItem.Icon} {menuItem.label}
</Typography>
)} )}
</div> </div>
</MenuItem> </MenuItem>

View file

@ -37,6 +37,8 @@ export const RowActions = ({ menuItems, disabled }: RowActionsProps) => {
) : ( ) : (
<CardMenu <CardMenu
disabled={disabled} disabled={disabled}
autoFocusItem={false}
showMenuIcon={true}
Icon={MoreHorizontalIcon} Icon={MoreHorizontalIcon}
IconButtonProps={{ IconButtonProps={{
className: classes.ghostIcon, className: classes.ghostIcon,

View file

@ -17,6 +17,8 @@ interface NumberCellProps {
export type NumberCell = CustomCell<NumberCellProps>; export type NumberCell = CustomCell<NumberCellProps>;
const onlyDigitsRegExp = /^\d+$/;
const NumberCellEdit: ReturnType<ProvideEditorCallback<NumberCell>> = ({ const NumberCellEdit: ReturnType<ProvideEditorCallback<NumberCell>> = ({
value: cell, value: cell,
onChange, onChange,
@ -69,8 +71,14 @@ export const numberCellRenderer = (
}, },
}), }),
}), }),
onPaste: (value, data) => ({ onPaste: (value, data) => {
if (!onlyDigitsRegExp.test(value)) {
return undefined;
}
return {
...data, ...data,
value: value ? parseFloat(value) : numberCellEmptyValue, value: value ? parseFloat(value) : numberCellEmptyValue,
}), };
},
}); });

View file

@ -123,7 +123,6 @@ const useStyles = makeStyles(
border: `1px solid ${vars.colors.border.neutralHighlight}`, border: `1px solid ${vars.colors.border.neutralHighlight}`,
borderLeft: "none", borderLeft: "none",
borderRight: "none", borderRight: "none",
cursor: "pointer",
color: vars.colors.foreground.iconNeutralPlain, color: vars.colors.foreground.iconNeutralPlain,
marginLeft: -1, marginLeft: -1,
display: "flex", display: "flex",

View file

@ -84,6 +84,7 @@ export const fragmentOrderLine = gql`
} }
variant { variant {
id id
name
quantityAvailable quantityAvailable
preorder { preorder {
endDate endDate

View file

@ -1567,6 +1567,7 @@ export const OrderLineFragmentDoc = gql`
} }
variant { variant {
id id
name
quantityAvailable quantityAvailable
preorder { preorder {
endDate endDate

File diff suppressed because one or more lines are too long

View file

@ -6,11 +6,15 @@ import {
useDatagridChangeState, useDatagridChangeState,
} from "@dashboard/components/Datagrid/hooks/useDatagridChange"; } from "@dashboard/components/Datagrid/hooks/useDatagridChange";
import { OrderLineFragment } from "@dashboard/graphql"; import { OrderLineFragment } from "@dashboard/graphql";
import React from "react"; import { productPath } from "@dashboard/products/urls";
import { ExternalLinkIcon } from "@saleor/macaw-ui/next";
import React, { useCallback } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { Link } from "react-router-dom";
import { messages } from "../OrderListDatagrid/messages"; import { messages as orderMessages } from "../OrderListDatagrid/messages";
import { useColumns, useGetCellContent } from "./datagrid"; import { useColumns, useGetCellContent } from "./datagrid";
import { messages } from "./messages";
interface OrderDetailsDatagridProps { interface OrderDetailsDatagridProps {
lines: OrderLineFragment[]; lines: OrderLineFragment[];
@ -43,19 +47,36 @@ export const OrderDetailsDatagrid = ({
loading, loading,
}); });
const getMenuItems = useCallback(
index => [
{
label: intl.formatMessage(messages.productDetails),
Icon: (
<Link
to={productPath(lines[index].variant.product.id)}
target="_blank"
>
<ExternalLinkIcon />
</Link>
),
onSelect: () => false,
},
],
[intl, lines],
);
return ( return (
<DatagridChangeStateContext.Provider value={datagrid}> <DatagridChangeStateContext.Provider value={datagrid}>
<Datagrid <Datagrid
readonly
showEmptyDatagrid showEmptyDatagrid
rowMarkers="none" rowMarkers="none"
columnSelect="single" columnSelect="single"
freezeColumns={1} freezeColumns={1}
availableColumns={columns} availableColumns={columns}
emptyText={intl.formatMessage(messages.emptyText)} emptyText={intl.formatMessage(orderMessages.emptyText)}
getCellContent={getCellContent} getCellContent={getCellContent}
getCellError={() => false} getCellError={() => false}
menuItems={() => []} menuItems={getMenuItems}
rows={loading ? 1 : lines.length} rows={loading ? 1 : lines.length}
selectionActions={() => null} selectionActions={() => null}
onColumnResize={onColumnResize} onColumnResize={onColumnResize}

View file

@ -29,6 +29,11 @@ export const useColumns = (): AvailableColumn[] => {
title: intl.formatMessage(columnsMessages.sku), title: intl.formatMessage(columnsMessages.sku),
width: 150, width: 150,
}, },
{
id: "variantName",
title: intl.formatMessage(columnsMessages.variantName),
width: 150,
},
{ {
id: "quantity", id: "quantity",
title: intl.formatMessage(columnsMessages.quantity), title: intl.formatMessage(columnsMessages.quantity),
@ -82,21 +87,26 @@ export const useGetCellContent = ({
return thumbnailCell( return thumbnailCell(
rowData?.productName ?? "", rowData?.productName ?? "",
rowData.thumbnail?.url ?? "", rowData.thumbnail?.url ?? "",
readonyOptions,
); );
case "sku": case "sku":
return readonlyTextCell(rowData.productSku ?? "", false); return readonlyTextCell(rowData.productSku ?? "", false);
case "variantName":
return readonlyTextCell(rowData.variant.name ?? "", false);
case "quantity": case "quantity":
return readonlyTextCell(rowData.quantity.toString(), false); return readonlyTextCell(rowData.quantity.toString(), false);
case "price": case "price":
return moneyCell( return moneyCell(
rowData.unitPrice.gross.amount, rowData.unitPrice.gross.amount,
rowData.unitPrice.gross.currency, rowData.unitPrice.gross.currency,
readonyOptions,
); );
case "total": case "total":
return moneyCell( return moneyCell(
rowData.totalPrice.gross.amount, rowData.totalPrice.gross.amount,
rowData.totalPrice.gross.currency, rowData.totalPrice.gross.currency,
readonyOptions,
); );
default: default:
@ -108,3 +118,8 @@ export const useGetCellContent = ({
return getCellContent; return getCellContent;
}; };
const readonyOptions: Partial<GridCell> = {
allowOverlay: false,
readonly: true,
};

View file

@ -6,6 +6,10 @@ export const columnsMessages = defineMessages({
defaultMessage: "Product", defaultMessage: "Product",
description: "product name", description: "product name",
}, },
variantName: {
id: "OK5+Fh",
defaultMessage: "Variant",
},
sku: { sku: {
id: "8J81ri", id: "8J81ri",
defaultMessage: "SKU", defaultMessage: "SKU",
@ -27,3 +31,10 @@ export const columnsMessages = defineMessages({
description: "order line total price", description: "order line total price",
}, },
}); });
export const messages = defineMessages({
productDetails: {
id: "VYK2nN",
defaultMessage: "Product details",
},
});

View file

@ -7,9 +7,16 @@ import {
useDatagridChangeState, useDatagridChangeState,
} from "@dashboard/components/Datagrid/hooks/useDatagridChange"; } from "@dashboard/components/Datagrid/hooks/useDatagridChange";
import { OrderDetailsFragment, OrderErrorFragment } from "@dashboard/graphql"; import { OrderDetailsFragment, OrderErrorFragment } from "@dashboard/graphql";
import { TrashBinIcon } from "@saleor/macaw-ui/next"; import { productUrl } from "@dashboard/products/urls";
import {
Box,
ExternalLinkIcon,
sprinkles,
TrashBinIcon,
} from "@saleor/macaw-ui/next";
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { Link } from "react-router-dom";
import { FormData } from "../OrderDraftDetailsProducts/OrderDraftDetailsProducts"; import { FormData } from "../OrderDraftDetailsProducts/OrderDraftDetailsProducts";
import { useColumns, useGetCellContent } from "./datagrid"; import { useColumns, useGetCellContent } from "./datagrid";
@ -54,8 +61,39 @@ export const OrderDraftDetailsDatagrid = ({
const getMenuItems = useCallback( const getMenuItems = useCallback(
index => [ index => [
{ {
label: intl.formatMessage(messages.deleteOrder), label: "",
Icon: <TrashBinIcon />, Icon: (
<Link
to={productUrl(lines[index]?.variant.product.id)}
target="_blank"
className={sprinkles({
display: "flex",
alignItems: "center",
gap: 2,
})}
>
<ExternalLinkIcon />
{intl.formatMessage(messages.productDetails)}
</Link>
),
onSelect: () => false,
},
{
label: "",
Icon: (
<Box
data-test-id="delete-order-line"
as="span"
color="iconCriticalDefault"
display="flex"
alignItems="center"
__marginLeft="-2px"
gap={2}
>
<TrashBinIcon />
{intl.formatMessage(messages.deleteOrder)}
</Box>
),
onSelect: () => { onSelect: () => {
onOrderLineRemove(lines[index].id); onOrderLineRemove(lines[index].id);
}, },
@ -73,8 +111,8 @@ export const OrderDraftDetailsDatagrid = ({
updates.map(({ data, column, row }) => { updates.map(({ data, column, row }) => {
const orderId = lines[row].id; const orderId = lines[row].id;
if (column === "quantity" && data !== "") { if (column === "quantity" && data.value !== "") {
return onOrderLineChange(orderId, { quantity: data }); return onOrderLineChange(orderId, { quantity: data.value });
} }
}), }),
); );

View file

@ -1,9 +1,9 @@
import { import {
moneyCell, moneyCell,
moneyDiscountedCell, moneyDiscountedCell,
numberCell,
readonlyTextCell, readonlyTextCell,
tagsCell, tagsCell,
textCell,
thumbnailCell, thumbnailCell,
} from "@dashboard/components/Datagrid/customCells/cells"; } from "@dashboard/components/Datagrid/customCells/cells";
import { GetCellContentOpts } from "@dashboard/components/Datagrid/Datagrid"; import { GetCellContentOpts } from "@dashboard/components/Datagrid/Datagrid";
@ -43,6 +43,11 @@ export const useColumns = () => {
title: "SKU", title: "SKU",
width: 150, width: 150,
}, },
{
id: "variantName",
title: intl.formatMessage(columnsMessages.variantName),
width: 150,
},
{ {
id: "quantity", id: "quantity",
title: intl.formatMessage(columnsMessages.quantity), title: intl.formatMessage(columnsMessages.quantity),
@ -121,7 +126,7 @@ export const useGetCellContent = ({
}, },
); );
case "quantity": case "quantity":
return textCell(change || rowData.quantity.toString()); return numberCell(change?.value || rowData.quantity);
case "price": case "price":
return moneyDiscountedCell( return moneyDiscountedCell(
{ {
@ -149,6 +154,8 @@ export const useGetCellContent = ({
); );
case "sku": case "sku":
return readonlyTextCell(rowData?.productSku ?? "", false); return readonlyTextCell(rowData?.productSku ?? "", false);
case "variantName":
return readonlyTextCell(rowData.variant.name ?? "", false);
case "total": case "total":
return moneyCell( return moneyCell(
rowData.totalPrice.gross.amount, rowData.totalPrice.gross.amount,

View file

@ -10,6 +10,10 @@ export const columnsMessages = defineMessages({
defaultMessage: "Quantity", defaultMessage: "Quantity",
description: "quantity of ordered products", description: "quantity of ordered products",
}, },
variantName: {
id: "OK5+Fh",
defaultMessage: "Variant",
},
price: { price: {
id: "32dfzI", id: "32dfzI",
defaultMessage: "Price", defaultMessage: "Price",
@ -27,6 +31,10 @@ export const messages = defineMessages({
id: "Q1Uzbb", id: "Q1Uzbb",
defaultMessage: "No products found", defaultMessage: "No products found",
}, },
productDetails: {
id: "VYK2nN",
defaultMessage: "Product details",
},
deleteOrder: { deleteOrder: {
id: "LKD6fB", id: "LKD6fB",
defaultMessage: "Remove product", defaultMessage: "Remove product",

View file

@ -1289,6 +1289,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
variant: { variant: {
__typename: "ProductVariant", __typename: "ProductVariant",
id: "dsfsfuhb", id: "dsfsfuhb",
name: "XS",
quantityAvailable: 10, quantityAvailable: 10,
preorder: null, preorder: null,
product: { product: {
@ -1415,6 +1416,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
variant: { variant: {
__typename: "ProductVariant", __typename: "ProductVariant",
id: "dsfsfuhb", id: "dsfsfuhb",
name: "XS",
quantityAvailable: 10, quantityAvailable: 10,
preorder: null, preorder: null,
product: { product: {
@ -1547,6 +1549,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
variant: { variant: {
__typename: "ProductVariant", __typename: "ProductVariant",
id: "dsfsfuhb", id: "dsfsfuhb",
name: "Soft",
quantityAvailable: 10, quantityAvailable: 10,
preorder: null, preorder: null,
product: { product: {
@ -1656,6 +1659,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
variant: { variant: {
__typename: "ProductVariant", __typename: "ProductVariant",
id: "dsfsfuhb", id: "dsfsfuhb",
name: "XXL",
quantityAvailable: 10, quantityAvailable: 10,
preorder: null, preorder: null,
product: { product: {
@ -1930,6 +1934,7 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
variant: { variant: {
__typename: "ProductVariant", __typename: "ProductVariant",
id: "dsfsfuhb", id: "dsfsfuhb",
name: "Hard",
quantityAvailable: 10, quantityAvailable: 10,
preorder: null, preorder: null,
product: { product: {
@ -2039,6 +2044,7 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
variant: { variant: {
__typename: "ProductVariant", __typename: "ProductVariant",
id: "dsfsfuhb", id: "dsfsfuhb",
name: "15-1337",
quantityAvailable: 10, quantityAvailable: 10,
preorder: null, preorder: null,
product: { product: {

View file

@ -538,6 +538,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Milk 1",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
__typename: "ProductVariant", __typename: "ProductVariant",
@ -647,6 +648,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Milk 1",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -756,6 +758,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
name: "Milk 2",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -871,6 +874,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Milk 1",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -985,6 +989,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Milk 1",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -1099,6 +1104,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
name: "Milk 2",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -1213,6 +1219,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Milk 3",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -1327,6 +1334,7 @@ describe("Get the total value of all replaced products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Milk 3",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -1575,6 +1583,7 @@ describe("Get the total value of all selected products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Digital Book",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -1684,6 +1693,7 @@ describe("Get the total value of all selected products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Digital Book",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -1793,6 +1803,7 @@ describe("Get the total value of all selected products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
name: "Digital Book",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -1908,6 +1919,7 @@ describe("Get the total value of all selected products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Digital Book",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -2022,6 +2034,7 @@ describe("Get the total value of all selected products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Digital Book",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -2136,6 +2149,7 @@ describe("Get the total value of all selected products", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
name: "Digital Book",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -2378,6 +2392,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Saleor Demo Product",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -2492,6 +2507,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3", id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
name: "Saleor Demo Product",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [
@ -2606,6 +2622,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
], ],
variant: { variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2", id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
name: "Saleor Demo Product",
quantityAvailable: 50, quantityAvailable: 50,
preorder: null, preorder: null,
stocks: [ stocks: [