Merge pull request #1066 from mirumee/SALEOR-2547/update-adding-draft-order-products-per-channel (#1070)
Update adding draft order products per channel
This commit is contained in:
parent
bddfa2c4af
commit
9d6cc99103
53 changed files with 807 additions and 1677 deletions
|
@ -3938,8 +3938,8 @@
|
|||
"src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_2850255786": {
|
||||
"string": "Search Products"
|
||||
},
|
||||
"src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_353369701": {
|
||||
"string": "No products matching given query"
|
||||
"src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_3284796469": {
|
||||
"string": "No products available in order channel matching given query"
|
||||
},
|
||||
"src_dot_orders_dot_components_dot_OrderProductsCardElements_dot_1134347598": {
|
||||
"context": "product price",
|
||||
|
@ -5310,6 +5310,10 @@
|
|||
"context": "button",
|
||||
"string": "Delete Variant"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_pricingCardSubtitle": {
|
||||
"context": "variant pricing section subtitle",
|
||||
"string": "There is no channel to define prices for. You need to first add variant to channels to define prices."
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantCreatePage_dot_saveVariant": {
|
||||
"context": "button",
|
||||
"string": "Save variant"
|
||||
|
@ -5462,6 +5466,18 @@
|
|||
"context": "variant name",
|
||||
"string": "New Variant"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantPage_dot_itemSubtitleHidden": {
|
||||
"context": "VariantDetailsChannelsAvailabilityCard item subtitle hidden",
|
||||
"string": "Hidden"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantPage_dot_itemSubtitlePublished": {
|
||||
"context": "VariantDetailsChannelsAvailabilityCard item subtitle published",
|
||||
"string": "Published since {publicationDate}"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantPage_dot_noItemsAvailable": {
|
||||
"context": "VariantDetailsChannelsAvailabilityCard no items available",
|
||||
"string": "This variant is not available at any of the channels"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantPage_dot_nonSelectionAttributes": {
|
||||
"context": "attributes, section header",
|
||||
"string": "Variant Attributes"
|
||||
|
@ -5470,6 +5486,14 @@
|
|||
"context": "attributes, section header",
|
||||
"string": "Variant Selection Attributes"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantPage_dot_subtitle": {
|
||||
"context": "VariantDetailsChannelsAvailabilityCard subtitle",
|
||||
"string": "Available in {publishedInChannelsCount} out of {availableChannelsCount}"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantPage_dot_title": {
|
||||
"context": "VariantDetailsChannelsAvailabilityCard title",
|
||||
"string": "Availability"
|
||||
},
|
||||
"src_dot_products_dot_components_dot_ProductVariantPrice_dot_1099355007": {
|
||||
"context": "product pricing, section header",
|
||||
"string": "Pricing"
|
||||
|
|
|
@ -272,6 +272,7 @@ export const fragmentOrderDetails = gql`
|
|||
id
|
||||
name
|
||||
currencyCode
|
||||
slug
|
||||
}
|
||||
isPaid
|
||||
}
|
||||
|
|
|
@ -298,6 +298,8 @@ export const fragmentVariant = gql`
|
|||
url
|
||||
}
|
||||
channelListings {
|
||||
publicationDate
|
||||
isPublished
|
||||
channel {
|
||||
id
|
||||
name
|
||||
|
|
|
@ -471,6 +471,7 @@ export interface OrderDetailsFragment_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDetailsFragment {
|
||||
|
|
|
@ -189,6 +189,8 @@ export interface ProductVariant_product_channelListings_pricing {
|
|||
|
||||
export interface ProductVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: ProductVariant_product_channelListings_channel;
|
||||
pricing: ProductVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -363,7 +363,7 @@ const OrderProductAddDialog: React.FC<OrderProductAddDialogProps> = props => {
|
|||
() => (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>
|
||||
<FormattedMessage defaultMessage="No products matching given query" />
|
||||
<FormattedMessage defaultMessage="No products available in order channel matching given query" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
|
|
@ -814,6 +814,7 @@ export const order = (placeholder: string): OrderDetails_order => ({
|
|||
canFinalize: true,
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
slug: "channel-default",
|
||||
currencyCode: "USD",
|
||||
id: "123454",
|
||||
isActive: true,
|
||||
|
@ -1371,6 +1372,7 @@ export const draftOrder = (placeholder: string): OrderDetails_order => ({
|
|||
canFinalize: true,
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
slug: "channel-default",
|
||||
currencyCode: "USD",
|
||||
id: "123454",
|
||||
isActive: true,
|
||||
|
|
|
@ -164,8 +164,18 @@ export const useOrderQuery = makeQuery<OrderDetails, OrderDetailsVariables>(
|
|||
);
|
||||
|
||||
export const searchOrderVariant = gql`
|
||||
query SearchOrderVariant($first: Int!, $query: String!, $after: String) {
|
||||
search: products(first: $first, after: $after, filter: { search: $query }) {
|
||||
query SearchOrderVariant(
|
||||
$channel: String!
|
||||
$first: Int!
|
||||
$query: String!
|
||||
$after: String
|
||||
) {
|
||||
search: products(
|
||||
first: $first
|
||||
after: $after
|
||||
filter: { search: $query }
|
||||
channel: $channel
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
|
|
|
@ -479,6 +479,7 @@ export interface FulfillOrder_orderFulfill_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface FulfillOrder_orderFulfill_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderCancel_orderCancel_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderCancel_orderCancel_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderCapture_orderCapture_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderCapture_orderCapture_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderConfirm_orderConfirm_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderConfirm_orderConfirm_order {
|
||||
|
|
|
@ -471,6 +471,7 @@ export interface OrderDetails_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDetails_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderDiscountAdd_orderDiscountAdd_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDiscountAdd_orderDiscountAdd_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderDiscountDelete_orderDiscountDelete_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDiscountDelete_orderDiscountDelete_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderDiscountUpdate_orderDiscountUpdate_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDiscountUpdate_orderDiscountUpdate_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDraftCancel_draftOrderDelete_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDraftFinalize_draftOrderComplete_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderDraftUpdate_draftOrderUpdate_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order {
|
||||
|
|
|
@ -572,6 +572,7 @@ export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_o
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderLineDelete_orderLineDelete_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderLineDelete_orderLineDelete_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderLineDiscountRemove_orderLineDiscountRemove_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderLineDiscountRemove_orderLineDiscountRemove_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderLineDiscountUpdate_orderLineDiscountUpdate_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderLineUpdate_orderLineUpdate_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderLineUpdate_orderLineUpdate_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderLinesAdd_orderLinesCreate_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderLinesAdd_orderLinesCreate_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderMarkAsPaid_orderMarkAsPaid_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderRefund_orderRefund_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderRefund_orderRefund_order {
|
||||
|
|
|
@ -485,6 +485,7 @@ export interface OrderShippingMethodUpdate_orderUpdateShipping_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderShippingMethodUpdate_orderUpdateShipping_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderUpdate_orderUpdate_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderUpdate_orderUpdate_order {
|
||||
|
|
|
@ -477,6 +477,7 @@ export interface OrderVoid_orderVoid_order_channel {
|
|||
id: string;
|
||||
name: string;
|
||||
currencyCode: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface OrderVoid_orderVoid_order {
|
||||
|
|
|
@ -72,6 +72,7 @@ export interface SearchOrderVariant {
|
|||
}
|
||||
|
||||
export interface SearchOrderVariantVariables {
|
||||
channel: string;
|
||||
first: number;
|
||||
query: string;
|
||||
after?: string | null;
|
||||
|
|
|
@ -61,7 +61,7 @@ export const OrderDraftDetails: React.FC<OrderDraftDetailsProps> = ({
|
|||
search: variantSearch,
|
||||
result: variantSearchOpts
|
||||
} = useOrderVariantSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
variables: { ...DEFAULT_INITIAL_SEARCH_DATA, channel: order.channel.slug }
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
|
@ -89,7 +89,7 @@ export const OrderUnconfirmedDetails: React.FC<OrderUnconfirmedDetailsProps> = (
|
|||
search: variantSearch,
|
||||
result: variantSearchOpts
|
||||
} = useOrderVariantSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
variables: { ...DEFAULT_INITIAL_SEARCH_DATA, channel: order.channel.slug }
|
||||
});
|
||||
const warehouses = useWarehouseList({
|
||||
displayLoader: true,
|
||||
|
|
|
@ -16,7 +16,6 @@ import Grid from "@saleor/components/Grid";
|
|||
import Metadata from "@saleor/components/Metadata";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import { ProductChannelListingErrorFragment } from "@saleor/fragments/types/ProductChannelListingErrorFragment";
|
||||
import { ProductErrorWithAttributesFragment } from "@saleor/fragments/types/ProductErrorWithAttributesFragment";
|
||||
import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages";
|
||||
import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts";
|
||||
|
@ -51,12 +50,16 @@ const messages = defineMessages({
|
|||
saveVariant: {
|
||||
defaultMessage: "Save variant",
|
||||
description: "button"
|
||||
},
|
||||
pricingCardSubtitle: {
|
||||
defaultMessage:
|
||||
"There is no channel to define prices for. You need to first add variant to channels to define prices.",
|
||||
description: "variant pricing section subtitle"
|
||||
}
|
||||
});
|
||||
|
||||
interface ProductVariantCreatePageProps {
|
||||
channels: ChannelPriceData[];
|
||||
channelErrors: ProductChannelListingErrorFragment[] | undefined;
|
||||
disabled: boolean;
|
||||
errors: ProductErrorWithAttributesFragment[];
|
||||
header: string;
|
||||
|
@ -82,7 +85,6 @@ interface ProductVariantCreatePageProps {
|
|||
|
||||
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||
channels,
|
||||
channelErrors = [],
|
||||
disabled,
|
||||
errors,
|
||||
header,
|
||||
|
@ -209,15 +211,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
|||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantPrice
|
||||
ProductVariantChannelListings={data.channelListings.map(
|
||||
channel => ({
|
||||
...channel.data,
|
||||
...channel.value
|
||||
})
|
||||
)}
|
||||
errors={channelErrors}
|
||||
loading={disabled}
|
||||
onChange={handlers.changeChannels}
|
||||
disabledMessage={messages.pricingCardSubtitle}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductStocks
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
createFetchMoreReferencesHandler,
|
||||
createFetchReferencesHandler
|
||||
} from "@saleor/attributes/utils/handlers";
|
||||
import { ChannelPriceData, IChannelPriceArgs } from "@saleor/channels/utils";
|
||||
import { ChannelPriceData } from "@saleor/channels/utils";
|
||||
import { AttributeInput } from "@saleor/components/Attributes";
|
||||
import { MetadataFormData } from "@saleor/components/Metadata";
|
||||
import useForm, { FormChange } from "@saleor/hooks/useForm";
|
||||
|
@ -18,11 +18,6 @@ import useFormset, {
|
|||
} from "@saleor/hooks/useFormset";
|
||||
import { ProductVariantCreateData_product } from "@saleor/products/types/ProductVariantCreateData";
|
||||
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
|
||||
import { getChannelsInput } from "@saleor/products/utils/handlers";
|
||||
import {
|
||||
validateCostPrice,
|
||||
validatePrice
|
||||
} from "@saleor/products/utils/validation";
|
||||
import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages";
|
||||
import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts";
|
||||
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
||||
|
@ -38,7 +33,6 @@ export interface ProductVariantCreateFormData extends MetadataFormData {
|
|||
weight: string;
|
||||
}
|
||||
export interface ProductVariantCreateData extends ProductVariantCreateFormData {
|
||||
channelListings: FormsetData<ChannelPriceData, IChannelPriceArgs>;
|
||||
attributes: AttributeInput[];
|
||||
attributesWithNewFileValue: FormsetData<null, File>;
|
||||
stocks: ProductStockInput[];
|
||||
|
@ -58,10 +52,7 @@ export interface UseProductVariantCreateFormOpts {
|
|||
|
||||
export interface ProductVariantCreateHandlers
|
||||
extends Record<
|
||||
| "changeStock"
|
||||
| "selectAttribute"
|
||||
| "selectAttributeMultiple"
|
||||
| "changeChannels",
|
||||
"changeStock" | "selectAttribute" | "selectAttributeMultiple",
|
||||
FormsetChange
|
||||
>,
|
||||
Record<"selectAttributeReference", FormsetChange<string[]>>,
|
||||
|
@ -107,13 +98,11 @@ function useProductVariantCreateForm(
|
|||
const triggerChange = () => setChanged(true);
|
||||
|
||||
const attributeInput = getVariantAttributeInputFromProduct(product);
|
||||
const channelsInput = getChannelsInput(opts.currentChannels);
|
||||
|
||||
const form = useForm(initial);
|
||||
const attributes = useFormset(attributeInput);
|
||||
const attributesWithNewFileValue = useFormset<null, File>([]);
|
||||
const stocks = useFormset<ProductStockFormsetData, string>([]);
|
||||
const channels = useFormset(channelsInput);
|
||||
const {
|
||||
makeChangeHandler: makeMetadataChangeHandler
|
||||
} = useMetadataChangeTrigger();
|
||||
|
@ -179,16 +168,6 @@ function useProductVariantCreateForm(
|
|||
triggerChange();
|
||||
stocks.remove(id);
|
||||
};
|
||||
const handleChannelChange: FormsetChange = (id, value) => {
|
||||
channels.change(id, value);
|
||||
triggerChange();
|
||||
};
|
||||
|
||||
const disabled = channels?.data.some(
|
||||
channelData =>
|
||||
validatePrice(channelData.value.price) ||
|
||||
validateCostPrice(channelData.value.costPrice)
|
||||
);
|
||||
|
||||
const data: ProductVariantCreateData = {
|
||||
...form.data,
|
||||
|
@ -199,7 +178,6 @@ function useProductVariantCreateForm(
|
|||
opts.referenceProducts
|
||||
),
|
||||
attributesWithNewFileValue: attributesWithNewFileValue.data,
|
||||
channelListings: channels.data,
|
||||
stocks: stocks.data
|
||||
};
|
||||
|
||||
|
@ -208,10 +186,9 @@ function useProductVariantCreateForm(
|
|||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
disabled,
|
||||
disabled: false,
|
||||
handlers: {
|
||||
addStock: handleStockAdd,
|
||||
changeChannels: handleChannelChange,
|
||||
changeMetadata,
|
||||
changeStock: handleStockChange,
|
||||
deleteStock: handleStockDelete,
|
||||
|
|
|
@ -41,6 +41,7 @@ import ProductVariantUpdateForm, {
|
|||
ProductVariantUpdateHandlers,
|
||||
ProductVariantUpdateSubmitData
|
||||
} from "./form";
|
||||
import VariantDetailsChannelsAvailabilityCard from "./VariantDetailsChannelsAvailabilityCard";
|
||||
|
||||
const messages = defineMessages({
|
||||
nonSelectionAttributes: {
|
||||
|
@ -217,6 +218,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<VariantDetailsChannelsAvailabilityCard variant={variant} />
|
||||
<Attributes
|
||||
title={intl.formatMessage(messages.nonSelectionAttributes)}
|
||||
attributes={data.attributes.filter(
|
||||
|
@ -263,6 +265,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantPrice
|
||||
disabled={!variant}
|
||||
ProductVariantChannelListings={data.channelListings.map(
|
||||
channel => ({
|
||||
...channel.data,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { Card } from "@material-ui/core";
|
||||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { variantDetailsChannelsAvailabilityCardMessages as messages } from "../messages";
|
||||
|
||||
interface VariantDetailsChannelsAvailabilityCardContainerProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const VariantDetailsChannelsAvailabilityCardContainer: React.FC<VariantDetailsChannelsAvailabilityCardContainerProps> = ({
|
||||
children
|
||||
}) => (
|
||||
<>
|
||||
<Card>
|
||||
<CardTitle title={<FormattedMessage {...messages.title} />} />
|
||||
{children}
|
||||
</Card>
|
||||
<CardSpacer />
|
||||
</>
|
||||
);
|
||||
|
||||
export default VariantDetailsChannelsAvailabilityCardContainer;
|
|
@ -0,0 +1,180 @@
|
|||
import {
|
||||
CardContent,
|
||||
Divider,
|
||||
ExpansionPanel,
|
||||
ExpansionPanelSummary,
|
||||
makeStyles,
|
||||
Typography
|
||||
} from "@material-ui/core";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { ProductVariant } from "@saleor/fragments/types/ProductVariant";
|
||||
import useDateLocalize from "@saleor/hooks/useDateLocalize";
|
||||
import IconChevronDown from "@saleor/icons/ChevronDown";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { variantDetailsChannelsAvailabilityCardMessages as messages } from "../messages";
|
||||
import CardContainer from "./VariantDetailsChannelsAvailabilityCardContainer";
|
||||
|
||||
const useExpanderStyles = makeStyles(
|
||||
() => ({
|
||||
expanded: {},
|
||||
root: {
|
||||
boxShadow: "none",
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
|
||||
"&:before": {
|
||||
content: "none"
|
||||
},
|
||||
|
||||
"&$expanded": {
|
||||
margin: 0,
|
||||
border: "none"
|
||||
}
|
||||
}
|
||||
}),
|
||||
{ name: "VariantDetailsChannelsAvailabilityCardExpander" }
|
||||
);
|
||||
|
||||
const useSummaryStyles = makeStyles(
|
||||
theme => ({
|
||||
expanded: {},
|
||||
root: {
|
||||
width: "100%",
|
||||
border: "none",
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
minHeight: 0,
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingLeft: theme.spacing(3),
|
||||
paddingRight: theme.spacing(5.5),
|
||||
paddingBottom: theme.spacing(2),
|
||||
|
||||
"&$expanded": {
|
||||
minHeight: 0
|
||||
}
|
||||
},
|
||||
content: {
|
||||
margin: 0,
|
||||
|
||||
"&$expanded": {
|
||||
margin: 0
|
||||
}
|
||||
}
|
||||
}),
|
||||
{ name: "VariantDetailsChannelsAvailabilityCardExpanderSummary" }
|
||||
);
|
||||
|
||||
interface VariantDetailsChannelsAvailabilityCardProps {
|
||||
variant: ProductVariant;
|
||||
}
|
||||
|
||||
const VariantDetailsChannelsAvailabilityCard: React.FC<VariantDetailsChannelsAvailabilityCardProps> = ({
|
||||
variant
|
||||
}) => {
|
||||
const expanderClasses = useExpanderStyles({});
|
||||
const summaryClasses = useSummaryStyles({});
|
||||
const localizeDate = useDateLocalize();
|
||||
const intl = useIntl();
|
||||
|
||||
const getProductChannelListingByChannelId = (channelId: string) =>
|
||||
variant?.product.channelListings.find(
|
||||
({ channel }) => channel.id === channelId
|
||||
);
|
||||
|
||||
const getItemSubtitle = (channelId: string) => {
|
||||
const {
|
||||
isPublished,
|
||||
publicationDate
|
||||
} = getProductChannelListingByChannelId(channelId);
|
||||
|
||||
if (!isPublished) {
|
||||
return intl.formatMessage(messages.itemSubtitleHidden);
|
||||
}
|
||||
|
||||
return intl.formatMessage(messages.itemSubtitlePublished, {
|
||||
publicationDate: localizeDate(publicationDate)
|
||||
});
|
||||
};
|
||||
|
||||
if (!variant) {
|
||||
return (
|
||||
<CardContainer>
|
||||
<CardContent>
|
||||
<Skeleton />
|
||||
</CardContent>
|
||||
</CardContainer>
|
||||
);
|
||||
}
|
||||
|
||||
const { channelListings } = variant;
|
||||
|
||||
const isAvailableInAnyChannels = !!channelListings.length;
|
||||
|
||||
const variantChannelListingsChannelsIds = channelListings.map(
|
||||
({ channel: { id } }) => id
|
||||
);
|
||||
|
||||
const allAvailableChannelsListings = variant.product.channelListings.filter(
|
||||
({ channel }) => variantChannelListingsChannelsIds.includes(channel.id)
|
||||
);
|
||||
|
||||
const publishedInChannelsListings = allAvailableChannelsListings.filter(
|
||||
({ isPublished }) => isPublished
|
||||
);
|
||||
|
||||
if (!isAvailableInAnyChannels) {
|
||||
return (
|
||||
<CardContainer>
|
||||
<CardContent>
|
||||
<Typography variant="caption">
|
||||
{intl.formatMessage(messages.noItemsAvailable)}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CardContainer>
|
||||
<ExpansionPanel classes={expanderClasses}>
|
||||
<ExpansionPanelSummary
|
||||
expandIcon={<IconChevronDown />}
|
||||
classes={summaryClasses}
|
||||
data-test-id="channels-variant-availability-summary"
|
||||
>
|
||||
<>
|
||||
<Typography variant="caption">
|
||||
{intl.formatMessage(messages.subtitle, {
|
||||
publishedInChannelsCount: publishedInChannelsListings.length,
|
||||
availableChannelsCount: allAvailableChannelsListings.length
|
||||
})}
|
||||
</Typography>
|
||||
</>
|
||||
</ExpansionPanelSummary>
|
||||
|
||||
{channelListings.map(({ channel }) => (
|
||||
<>
|
||||
<Divider />
|
||||
<CardContent>
|
||||
<Typography
|
||||
data-test-id={`channels-variant-availability-item-title-${channel.id}`}
|
||||
>
|
||||
{channel.name}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
data-test-id={`channels-variant-availability-item-subtitle-${channel.id}`}
|
||||
>
|
||||
{getItemSubtitle(channel.id)}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</>
|
||||
))}
|
||||
</ExpansionPanel>
|
||||
</CardContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default VariantDetailsChannelsAvailabilityCard;
|
26
src/products/components/ProductVariantPage/messages.ts
Normal file
26
src/products/components/ProductVariantPage/messages.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const variantDetailsChannelsAvailabilityCardMessages = defineMessages({
|
||||
title: {
|
||||
defaultMessage: "Availability",
|
||||
description: "VariantDetailsChannelsAvailabilityCard title"
|
||||
},
|
||||
subtitle: {
|
||||
defaultMessage:
|
||||
"Available in {publishedInChannelsCount} out of {availableChannelsCount}",
|
||||
description: "VariantDetailsChannelsAvailabilityCard subtitle"
|
||||
},
|
||||
itemSubtitlePublished: {
|
||||
defaultMessage: "Published since {publicationDate}",
|
||||
description:
|
||||
"VariantDetailsChannelsAvailabilityCard item subtitle published"
|
||||
},
|
||||
itemSubtitleHidden: {
|
||||
defaultMessage: "Hidden",
|
||||
description: "VariantDetailsChannelsAvailabilityCard item subtitle hidden"
|
||||
},
|
||||
noItemsAvailable: {
|
||||
defaultMessage: "This variant is not available at any of the channels",
|
||||
description: "VariantDetailsChannelsAvailabilityCard no items available"
|
||||
}
|
||||
});
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from "@saleor/utils/errors";
|
||||
import getProductErrorMessage from "@saleor/utils/errors/product";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { FormattedMessage, MessageDescriptor, useIntl } from "react-intl";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
|
@ -62,25 +62,53 @@ const useStyles = makeStyles(
|
|||
);
|
||||
|
||||
interface ProductVariantPriceProps {
|
||||
ProductVariantChannelListings: ChannelData[];
|
||||
errors: ProductChannelListingErrorFragment[];
|
||||
ProductVariantChannelListings?: ChannelData[];
|
||||
errors?: ProductChannelListingErrorFragment[];
|
||||
loading?: boolean;
|
||||
onChange: (id: string, data: ChannelPriceArgs) => void;
|
||||
disabled?: boolean;
|
||||
onChange?: (id: string, data: ChannelPriceArgs) => void;
|
||||
disabledMessage?: MessageDescriptor;
|
||||
}
|
||||
|
||||
const numberOfColumns = 2;
|
||||
|
||||
const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
|
||||
const {
|
||||
disabled = false,
|
||||
errors = [],
|
||||
ProductVariantChannelListings,
|
||||
ProductVariantChannelListings = [],
|
||||
loading,
|
||||
onChange
|
||||
onChange,
|
||||
disabledMessage
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
const intl = useIntl();
|
||||
const formErrors = getFormChannelErrors(["price", "costPrice"], errors);
|
||||
|
||||
if (disabled || !ProductVariantChannelListings.length) {
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Pricing",
|
||||
description: "product pricing, section header"
|
||||
})}
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography variant="caption">
|
||||
{intl.formatMessage(
|
||||
disabledMessage || {
|
||||
defaultMessage: "There is no channel to define prices for",
|
||||
description: "variant pricing section subtitle",
|
||||
id: "product variant pricing card disabled subtitle"
|
||||
}
|
||||
)}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
|
|
|
@ -2817,6 +2817,8 @@ export const variant = (placeholderImage: string): ProductVariant => ({
|
|||
channelListings: [
|
||||
{
|
||||
__typename: "ProductChannelListing",
|
||||
isPublished: false,
|
||||
publicationDate: null,
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
currencyCode: "USD",
|
||||
|
@ -2848,6 +2850,8 @@ export const variant = (placeholderImage: string): ProductVariant => ({
|
|||
},
|
||||
{
|
||||
__typename: "ProductChannelListing",
|
||||
isPublished: true,
|
||||
publicationDate: "2022-01-21",
|
||||
channel: {
|
||||
__typename: "Channel",
|
||||
currencyCode: "USD",
|
||||
|
|
|
@ -189,6 +189,8 @@ export interface ProductVariantChannelListingUpdate_productVariantChannelListing
|
|||
|
||||
export interface ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings_channel;
|
||||
pricing: ProductVariantChannelListingUpdate_productVariantChannelListingUpdate_variant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -189,6 +189,8 @@ export interface ProductVariantDetails_productVariant_product_channelListings_pr
|
|||
|
||||
export interface ProductVariantDetails_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: ProductVariantDetails_productVariant_product_channelListings_channel;
|
||||
pricing: ProductVariantDetails_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -480,6 +480,8 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_product
|
|||
|
||||
export interface SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings_channel;
|
||||
pricing: SimpleProductUpdate_productVariantUpdate_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
@ -767,6 +769,8 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_p
|
|||
|
||||
export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings_channel;
|
||||
pricing: SimpleProductUpdate_productVariantStocksCreate_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
@ -1053,6 +1057,8 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_p
|
|||
|
||||
export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings_channel;
|
||||
pricing: SimpleProductUpdate_productVariantStocksDelete_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
@ -1340,6 +1346,8 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_p
|
|||
|
||||
export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings_channel;
|
||||
pricing: SimpleProductUpdate_productVariantStocksUpdate_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -196,6 +196,8 @@ export interface VariantCreate_productVariantCreate_productVariant_product_chann
|
|||
|
||||
export interface VariantCreate_productVariantCreate_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: VariantCreate_productVariantCreate_productVariant_product_channelListings_channel;
|
||||
pricing: VariantCreate_productVariantCreate_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -195,6 +195,8 @@ export interface VariantMediaAssign_variantMediaAssign_productVariant_product_ch
|
|||
|
||||
export interface VariantMediaAssign_variantMediaAssign_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: VariantMediaAssign_variantMediaAssign_productVariant_product_channelListings_channel;
|
||||
pricing: VariantMediaAssign_variantMediaAssign_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -195,6 +195,8 @@ export interface VariantMediaUnassign_variantMediaUnassign_productVariant_produc
|
|||
|
||||
export interface VariantMediaUnassign_variantMediaUnassign_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: VariantMediaUnassign_variantMediaUnassign_productVariant_product_channelListings_channel;
|
||||
pricing: VariantMediaUnassign_variantMediaUnassign_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -196,6 +196,8 @@ export interface VariantUpdate_productVariantUpdate_productVariant_product_chann
|
|||
|
||||
export interface VariantUpdate_productVariantUpdate_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: VariantUpdate_productVariantUpdate_productVariant_product_channelListings_channel;
|
||||
pricing: VariantUpdate_productVariantUpdate_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
@ -483,6 +485,8 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_product
|
|||
|
||||
export interface VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings {
|
||||
__typename: "ProductChannelListing";
|
||||
publicationDate: any | null;
|
||||
isPublished: boolean;
|
||||
channel: VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings_channel;
|
||||
pricing: VariantUpdate_productVariantStocksUpdate_productVariant_product_channelListings_pricing | null;
|
||||
}
|
||||
|
|
|
@ -10,11 +10,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
|
|||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import { useFileUploadMutation } from "@saleor/files/mutations";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import useShop from "@saleor/hooks/useShop";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { useProductVariantChannelListingUpdate } from "@saleor/products/mutations";
|
||||
import { ProductVariantChannelListingUpdate } from "@saleor/products/types/ProductVariantChannelListingUpdate";
|
||||
import usePageSearch from "@saleor/searches/usePageSearch";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler";
|
||||
|
@ -54,7 +50,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
|||
params
|
||||
}) => {
|
||||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const shop = useShop();
|
||||
const intl = useIntl();
|
||||
const warehouses = useWarehouseList({
|
||||
|
@ -63,20 +58,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
|||
first: 50
|
||||
}
|
||||
});
|
||||
const handleCreateSuccess = (data: ProductVariantChannelListingUpdate) => {
|
||||
if (data.productVariantChannelListingUpdate.errors.length === 0) {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
navigate(
|
||||
productVariantEditUrl(
|
||||
productId,
|
||||
data.productVariantChannelListingUpdate.variant.id
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const { data, loading: productLoading } = useProductVariantCreateQuery({
|
||||
displayLoader: true,
|
||||
|
@ -85,13 +66,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
|||
|
||||
const [uploadFile, uploadFileOpts] = useFileUploadMutation({});
|
||||
|
||||
const [
|
||||
updateChannels,
|
||||
updateChannelsOpts
|
||||
] = useProductVariantChannelListingUpdate({
|
||||
onCompleted: handleCreateSuccess
|
||||
});
|
||||
|
||||
const product = data?.product;
|
||||
|
||||
const channels: ChannelPriceData[] = product?.channelListings.map(
|
||||
|
@ -104,21 +78,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
|||
})
|
||||
);
|
||||
|
||||
const handleSubmitChannels = (
|
||||
data: ProductVariantCreateData,
|
||||
variantId: string
|
||||
) =>
|
||||
updateChannels({
|
||||
variables: {
|
||||
id: variantId,
|
||||
input: data.channelListings.map(listing => ({
|
||||
channelId: listing.id,
|
||||
costPrice: listing.value.costPrice || null,
|
||||
price: listing.value.price
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
const [variantCreate, variantCreateResult] = useVariantCreateMutation({});
|
||||
|
||||
const [updateMetadata] = useMetadataUpdate({});
|
||||
|
@ -170,9 +129,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
|||
}
|
||||
});
|
||||
const id = result.data?.productVariantCreate?.productVariant?.id;
|
||||
if (id) {
|
||||
await handleSubmitChannels(formData, id);
|
||||
}
|
||||
|
||||
return id || null;
|
||||
};
|
||||
|
@ -234,9 +190,6 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
|||
/>
|
||||
<ProductVariantCreatePage
|
||||
channels={channels}
|
||||
channelErrors={
|
||||
updateChannelsOpts?.data?.productVariantChannelListingUpdate?.errors
|
||||
}
|
||||
disabled={disableForm}
|
||||
errors={variantCreateResult.data?.productVariantCreate.errors || []}
|
||||
header={intl.formatMessage({
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -22,7 +22,6 @@ storiesOf("Views / Products / Create product variant", module)
|
|||
.add("default", () => (
|
||||
<ProductVariantCreatePage
|
||||
channels={channels}
|
||||
channelErrors={[]}
|
||||
weightUnit="kg"
|
||||
disabled={false}
|
||||
errors={[]}
|
||||
|
@ -44,7 +43,6 @@ storiesOf("Views / Products / Create product variant", module)
|
|||
.add("with errors", () => (
|
||||
<ProductVariantCreatePage
|
||||
channels={channels}
|
||||
channelErrors={[]}
|
||||
weightUnit="kg"
|
||||
disabled={false}
|
||||
errors={[
|
||||
|
@ -85,7 +83,6 @@ storiesOf("Views / Products / Create product variant", module)
|
|||
.add("when loading data", () => (
|
||||
<ProductVariantCreatePage
|
||||
channels={channels}
|
||||
channelErrors={[]}
|
||||
weightUnit="kg"
|
||||
disabled={true}
|
||||
errors={[]}
|
||||
|
@ -107,7 +104,6 @@ storiesOf("Views / Products / Create product variant", module)
|
|||
.add("add first variant", () => (
|
||||
<ProductVariantCreatePage
|
||||
channels={channels}
|
||||
channelErrors={[]}
|
||||
weightUnit="kg"
|
||||
disabled={false}
|
||||
errors={[]}
|
||||
|
@ -132,7 +128,6 @@ storiesOf("Views / Products / Create product variant", module)
|
|||
.add("no warehouses", () => (
|
||||
<ProductVariantCreatePage
|
||||
channels={channels}
|
||||
channelErrors={[]}
|
||||
weightUnit="kg"
|
||||
disabled={false}
|
||||
errors={[]}
|
||||
|
|
Loading…
Reference in a new issue