From b774cc900226d25751dac93940fe5d53efbc9937 Mon Sep 17 00:00:00 2001 From: AlicjaSzu Date: Wed, 2 Dec 2020 12:35:02 +0100 Subject: [PATCH] Excluded Products in shipping view (#866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Clean up stories * Add missing props * Add zip codes section (#861) * Add zip code listing * Add list wrapping * Update snapshots * Set up API data * Fix lgtm warning * Update snapshots * Run Actions on all PR * Checks on PR * Test envs on PR * Cleanup action on PR * Update messages Co-authored-by: Krzysztof Wolski * Allow zip codes to be assigned to shipping method * Add zip code deletion (#871) * Add zip code range dialog * Fix path management * Use query params to handle modal actions * Allow zip codes to be assigned to shipping method * Make params optional * Fix types * Clean up urls * Add zip code range delete action * Update snapshots and messages * Update schema * Refresh zip code list after assigning them * Update types and snapshots * Update snapshots * Fix error message, checkbox default value (#880) * Fix error message, checkbox default value * Update snapshots * Update schema and types * Update stories * add excluded products section in shipping methods views * create UnassignDialog component * use priceRangeFragment in shipping queries * remove unneeded price from ShippingMethodAddProductsDialog * update messages in ShippingMethodProducts * updates after rebase * update snapshots, fix lint errors * fix ShippingMethodProductsAddDialog * update snapshots * small fix in ShippingMethodProducts * update snapshots after rebase * add handleClose func in ShippingMethodProductsAddDialog * Fix metadata not showing in category update * update snapshots again * update ShippingMethodProductsAddDialog * updates after rebase * update Price and Weight rates views Co-authored-by: dominik-zeglen Co-authored-by: Krzysztof Wolski Co-authored-by: Tomasz Szymański Co-authored-by: Magdalena Markusik --- locale/defaultMessages.json | 52 +- .../components/CategoryUpdatePage/form.tsx | 2 +- src/fragments/shipping.ts | 30 + ...ppingMethodWithExcludedProductsFragment.ts | 105 + .../ShippingMethodProducts.stories.tsx | 34 + .../ShippingMethodProducts.tsx | 171 + .../ShippingMethodProducts/index.ts | 2 + ...hippingMethodProductsAddDialog.stories.tsx | 24 + .../ShippingMethodProductsAddDialog.tsx | 242 + .../ShippingMethodProductsAddDialog/index.ts | 2 + .../ShippingZoneRatesCreatePage.stories.tsx | 87 + .../ShippingZoneRatesCreatePage.tsx | 190 + .../ShippingZoneRatesCreatePage/index.ts | 2 + .../ShippingZoneRatesPage.stories.tsx | 31 +- .../ShippingZoneRatesPage.tsx | 52 +- .../UnassignDialog/UnassignDialog.stories.tsx | 17 + .../UnassignDialog/UnassignDialog.tsx | 48 + .../components/UnassignDialog/index.ts | 2 + src/shipping/fixtures.ts | 109 +- src/shipping/mutations.ts | 43 + src/shipping/queries.ts | 23 +- .../types/ShippingPriceExcludeProduct.ts | 29 + .../ShippingPriceRemoveProductFromExclude.ts | 29 + src/shipping/types/ShippingZone.ts | 36 + src/shipping/urls.ts | 16 +- .../PriceRatesCreate/PriceRatesCreate.tsx | 5 +- .../PriceRatesUpdate/PriceRatesUpdate.tsx | 116 +- .../views/ShippingZoneDetails/index.tsx | 6 +- .../WeightRatesCreate/WeightRatesCreate.tsx | 5 +- .../WeightRatesUpdate/WeightRatesUpdate.tsx | 116 +- .../__snapshots__/Stories.test.ts.snap | 6627 ++++++++++++++--- src/types/globalTypes.ts | 4 + 32 files changed, 7170 insertions(+), 1087 deletions(-) create mode 100644 src/fragments/types/ShippingMethodWithExcludedProductsFragment.ts create mode 100644 src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.stories.tsx create mode 100644 src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.tsx create mode 100644 src/shipping/components/ShippingMethodProducts/index.ts create mode 100644 src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.stories.tsx create mode 100644 src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.tsx create mode 100644 src/shipping/components/ShippingMethodProductsAddDialog/index.ts create mode 100644 src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx create mode 100644 src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx create mode 100644 src/shipping/components/ShippingZoneRatesCreatePage/index.ts create mode 100644 src/shipping/components/UnassignDialog/UnassignDialog.stories.tsx create mode 100644 src/shipping/components/UnassignDialog/UnassignDialog.tsx create mode 100644 src/shipping/components/UnassignDialog/index.ts create mode 100644 src/shipping/types/ShippingPriceExcludeProduct.ts create mode 100644 src/shipping/types/ShippingPriceRemoveProductFromExclude.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index f6bfa6f617c..b5c5490e28b 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -5349,6 +5349,33 @@ "context": "column title", "string": "Channel name" }, + "src_dot_shipping_dot_components_dot_ShippingMethodProductsAddDialog_dot_2850255786": { + "string": "Search Products" + }, + "src_dot_shipping_dot_components_dot_ShippingMethodProductsAddDialog_dot_353369701": { + "string": "No products matching given query" + }, + "src_dot_shipping_dot_components_dot_ShippingMethodProductsAddDialog_dot_3756118423": { + "context": "dialog header", + "string": "Assign Products" + }, + "src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_1737533260": { + "string": "No Products" + }, + "src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_1781179817": { + "context": "section header", + "string": "Excluded Products" + }, + "src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_2100305525": { + "context": "button", + "string": "Assign products" + }, + "src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_2697405188": { + "string": "Product Name" + }, + "src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_4190792473": { + "string": "Actions" + }, "src_dot_shipping_dot_components_dot_ShippingRateZipCodeRangeRemoveDialog_dot_1083561409": { "string": "Are you sure you want to remove this ZIP-code rule?" }, @@ -5427,17 +5454,20 @@ "src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_579967655": { "string": "Shipping rate name" }, - "src_dot_shipping_dot_components_dot_ShippingZoneRatesPage_dot_1161979494": { + "src_dot_shipping_dot_components_dot_ShippingZoneRatesCreatePage_dot_1161979494": { "context": "page title", "string": "Price Rate Create" }, - "src_dot_shipping_dot_components_dot_ShippingZoneRatesPage_dot_1325966144": { + "src_dot_shipping_dot_components_dot_ShippingZoneRatesCreatePage_dot_1325966144": { "string": "Shipping" }, - "src_dot_shipping_dot_components_dot_ShippingZoneRatesPage_dot_3538551526": { + "src_dot_shipping_dot_components_dot_ShippingZoneRatesCreatePage_dot_3538551526": { "context": "page title", "string": "Weight Rate Create" }, + "src_dot_shipping_dot_components_dot_ShippingZoneRatesPage_dot_1325966144": { + "string": "Shipping" + }, "src_dot_shipping_dot_components_dot_ShippingZoneRates_dot_1134347598": { "context": "shipping method price", "string": "Price" @@ -5554,6 +5584,14 @@ "src_dot_shipping_dot_components_dot_ShippingZonesList_dot_655374584": { "string": "No shipping zones found" }, + "src_dot_shipping_dot_components_dot_UnassignDialog_dot_1203193503": { + "context": "dialog header", + "string": "Unassign Products From Shipping" + }, + "src_dot_shipping_dot_components_dot_UnassignDialog_dot_3215481647": { + "context": "dialog content", + "string": "{counter,plural,one{Are you sure you want to unassign this product?} other{Are you sure you want to unassign {displayQuantity} products?}}" + }, "src_dot_shipping_dot_invalid": { "context": "error message", "string": "Value is invalid" @@ -5587,6 +5625,10 @@ "src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_3823295269": { "string": "Manage Channel Availability" }, + "src_dot_shipping_dot_views_dot_PriceRatesUpdate_dot_870815507": { + "context": "unassign products from shipping method, button", + "string": "Unassign" + }, "src_dot_shipping_dot_views_dot_ShippingZoneDetails_dot_1010705153": { "context": "dialog header", "string": "Delete Shipping Zone" @@ -5609,6 +5651,10 @@ "src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_3014453080": { "string": "Manage Channels Availability" }, + "src_dot_shipping_dot_views_dot_WeightRatesUpdate_dot_870815507": { + "context": "unassign products from shipping method, button", + "string": "Unassign" + }, "src_dot_shipping_dot_weight": { "context": "error message", "string": "Maximum weight cannot be lower than minimum" diff --git a/src/categories/components/CategoryUpdatePage/form.tsx b/src/categories/components/CategoryUpdatePage/form.tsx index 809b5a851a9..c2fdb854dc3 100644 --- a/src/categories/components/CategoryUpdatePage/form.tsx +++ b/src/categories/components/CategoryUpdatePage/form.tsx @@ -75,9 +75,9 @@ function useCategoryUpdateForm( // Need to make it function to always have description.current up to date const getData = (): CategoryUpdateData => ({ ...form.data, - ...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified), description: description.current }); + const getSubmitData = (): CategoryUpdateData => ({ ...getData(), ...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified) diff --git a/src/fragments/shipping.ts b/src/fragments/shipping.ts index 87aae776f05..2a957a2e402 100644 --- a/src/fragments/shipping.ts +++ b/src/fragments/shipping.ts @@ -56,6 +56,36 @@ export const shippingMethodFragment = gql` } } `; +export const shippingMethodWithExcludedProductsFragment = gql` + ${fragmentMoney} + ${shippingMethodFragment} + fragment ShippingMethodWithExcludedProductsFragment on ShippingMethod { + ...ShippingMethodFragment + excludedProducts( + before: $before + after: $after + first: $first + last: $last + ) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + edges { + node { + id + name + thumbnail { + url + } + } + } + } + } +`; + export const shippingZoneDetailsFragment = gql` ${shippingZoneFragment} ${shippingMethodFragment} diff --git a/src/fragments/types/ShippingMethodWithExcludedProductsFragment.ts b/src/fragments/types/ShippingMethodWithExcludedProductsFragment.ts new file mode 100644 index 00000000000..83a534ac263 --- /dev/null +++ b/src/fragments/types/ShippingMethodWithExcludedProductsFragment.ts @@ -0,0 +1,105 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ShippingMethodWithExcludedProductsFragment +// ==================================================== + +export interface ShippingMethodWithExcludedProductsFragment_zipCodeRules { + __typename: "ShippingMethodZipCodeRule"; + id: string; + start: string | null; + end: string | null; +} + +export interface ShippingMethodWithExcludedProductsFragment_minimumOrderWeight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface ShippingMethodWithExcludedProductsFragment_maximumOrderWeight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface ShippingMethodWithExcludedProductsFragment_channelListings_channel { + __typename: "Channel"; + id: string; + name: string; + currencyCode: string; +} + +export interface ShippingMethodWithExcludedProductsFragment_channelListings_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodWithExcludedProductsFragment_channelListings_minimumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodWithExcludedProductsFragment_channelListings_maximumOrderPrice { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ShippingMethodWithExcludedProductsFragment_channelListings { + __typename: "ShippingMethodChannelListing"; + id: string; + channel: ShippingMethodWithExcludedProductsFragment_channelListings_channel; + price: ShippingMethodWithExcludedProductsFragment_channelListings_price | null; + minimumOrderPrice: ShippingMethodWithExcludedProductsFragment_channelListings_minimumOrderPrice | null; + maximumOrderPrice: ShippingMethodWithExcludedProductsFragment_channelListings_maximumOrderPrice | null; +} + +export interface ShippingMethodWithExcludedProductsFragment_excludedProducts_pageInfo { + __typename: "PageInfo"; + hasNextPage: boolean; + hasPreviousPage: boolean; + endCursor: string | null; + startCursor: string | null; +} + +export interface ShippingMethodWithExcludedProductsFragment_excludedProducts_edges_node_thumbnail { + __typename: "Image"; + url: string; +} + +export interface ShippingMethodWithExcludedProductsFragment_excludedProducts_edges_node { + __typename: "Product"; + id: string; + name: string; + thumbnail: ShippingMethodWithExcludedProductsFragment_excludedProducts_edges_node_thumbnail | null; +} + +export interface ShippingMethodWithExcludedProductsFragment_excludedProducts_edges { + __typename: "ProductCountableEdge"; + node: ShippingMethodWithExcludedProductsFragment_excludedProducts_edges_node; +} + +export interface ShippingMethodWithExcludedProductsFragment_excludedProducts { + __typename: "ProductCountableConnection"; + pageInfo: ShippingMethodWithExcludedProductsFragment_excludedProducts_pageInfo; + edges: ShippingMethodWithExcludedProductsFragment_excludedProducts_edges[]; +} + +export interface ShippingMethodWithExcludedProductsFragment { + __typename: "ShippingMethod"; + id: string; + zipCodeRules: (ShippingMethodWithExcludedProductsFragment_zipCodeRules | null)[] | null; + minimumOrderWeight: ShippingMethodWithExcludedProductsFragment_minimumOrderWeight | null; + maximumOrderWeight: ShippingMethodWithExcludedProductsFragment_maximumOrderWeight | null; + name: string; + type: ShippingMethodTypeEnum | null; + channelListings: ShippingMethodWithExcludedProductsFragment_channelListings[] | null; + excludedProducts: ShippingMethodWithExcludedProductsFragment_excludedProducts | null; +} diff --git a/src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.stories.tsx b/src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.stories.tsx new file mode 100644 index 00000000000..c21d4f9d645 --- /dev/null +++ b/src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.stories.tsx @@ -0,0 +1,34 @@ +import { shippingZone } from "@saleor/shipping/fixtures"; +import Decorator from "@saleor/storybook//Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ShippingMethodProducts, { + ShippingMethodProductsProps +} from "./ShippingMethodProducts"; + +const products = shippingZone.shippingMethods[0].excludedProducts.edges.map( + edge => edge.node +); + +const props: ShippingMethodProductsProps = { + disabled: false, + isChecked: () => undefined, + onNextPage: () => undefined, + onPreviousPage: () => undefined, + onProductAssign: () => undefined, + onProductUnassign: () => undefined, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false + }, + products, + selected: products.length, + toggle: () => undefined, + toggleAll: () => undefined, + toolbar: () => undefined +}; + +storiesOf("Shipping / ShippingMethodProducts", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.tsx b/src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.tsx new file mode 100644 index 00000000000..d734994c456 --- /dev/null +++ b/src/shipping/components/ShippingMethodProducts/ShippingMethodProducts.tsx @@ -0,0 +1,171 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import IconButton from "@material-ui/core/IconButton"; +import { makeStyles } from "@material-ui/core/styles"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableFooter from "@material-ui/core/TableFooter"; +import TableRow from "@material-ui/core/TableRow"; +import Typography from "@material-ui/core/Typography"; +import DeleteIcon from "@material-ui/icons/Delete"; +import CardTitle from "@saleor/components/CardTitle"; +import Checkbox from "@saleor/components/Checkbox"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import Skeleton from "@saleor/components/Skeleton"; +import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import TableHead from "@saleor/components/TableHead"; +import TablePagination from "@saleor/components/TablePagination"; +import { renderCollection } from "@saleor/misc"; +import { ShippingZone_shippingZone_shippingMethods_excludedProducts_edges_node } from "@saleor/shipping/types/ShippingZone"; +import { ListActions, ListProps } from "@saleor/types"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +const useStyles = makeStyles( + theme => ({ + colAction: { + "&:last-child": { + paddingRight: theme.spacing(3) + }, + textAlign: "right", + width: 100 + }, + colName: { + width: "auto" + }, + colProductName: { + paddingLeft: 0 + }, + table: { + tableLayout: "fixed" + } + }), + { name: "ShippingMethodProducts" } +); + +export interface ShippingMethodProductsProps + extends Pick>, + ListActions { + products: ShippingZone_shippingZone_shippingMethods_excludedProducts_edges_node[]; + onProductAssign: () => void; + onProductUnassign: (ids: string[]) => void; +} + +const numberOfColumns = 3; + +const ShippingMethodProducts: React.FC = props => { + const { + disabled, + pageInfo, + products, + onNextPage, + onPreviousPage, + onProductAssign, + onProductUnassign, + isChecked, + selected, + toggle, + toggleAll, + toolbar + } = props; + + const classes = useStyles(props); + const intl = useIntl(); + + return ( + + + + + } + /> + + {!!products?.length && ( + <> + + + + + + + + + + + + + + + )} + + {products?.length === 0 ? ( + + + + + + ) : ( + renderCollection(products, product => { + const isSelected = product ? isChecked(product.id) : false; + return ( + + + toggle(product.id)} + /> + + + {product?.name ? ( + {product.name} + ) : ( + + )} + + + onProductUnassign([product.id])}> + + + + + ); + }) + )} + + + + ); +}; +ShippingMethodProducts.displayName = "ShippingMethodProducts"; +export default ShippingMethodProducts; diff --git a/src/shipping/components/ShippingMethodProducts/index.ts b/src/shipping/components/ShippingMethodProducts/index.ts new file mode 100644 index 00000000000..d1a52c4a712 --- /dev/null +++ b/src/shipping/components/ShippingMethodProducts/index.ts @@ -0,0 +1,2 @@ +export * from "./ShippingMethodProducts"; +export { default } from "./ShippingMethodProducts"; diff --git a/src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.stories.tsx b/src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.stories.tsx new file mode 100644 index 00000000000..46d20d2692b --- /dev/null +++ b/src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.stories.tsx @@ -0,0 +1,24 @@ +import { products } from "@saleor/shipping/fixtures"; +import Decorator from "@saleor/storybook//Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ShippingMethodProductsAddDialog, { + ShippingMethodProductsAddDialogProps +} from "./ShippingMethodProductsAddDialog"; + +const props: ShippingMethodProductsAddDialogProps = { + confirmButtonState: "default", + hasMore: false, + loading: false, + onClose: () => undefined, + onFetch: () => undefined, + onFetchMore: () => undefined, + onSubmit: () => undefined, + open: true, + products +}; + +storiesOf("Shipping / ShippingMethodProductsAddDialog", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.tsx b/src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.tsx new file mode 100644 index 00000000000..5d381b069a7 --- /dev/null +++ b/src/shipping/components/ShippingMethodProductsAddDialog/ShippingMethodProductsAddDialog.tsx @@ -0,0 +1,242 @@ +import Button from "@material-ui/core/Button"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import { makeStyles } from "@material-ui/core/styles"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableRow from "@material-ui/core/TableRow"; +import TextField from "@material-ui/core/TextField"; +import Checkbox from "@saleor/components/Checkbox"; +import ConfirmButton, { + ConfirmButtonTransitionState +} from "@saleor/components/ConfirmButton"; +import ResponsiveTable from "@saleor/components/ResponsiveTable"; +import Skeleton from "@saleor/components/Skeleton"; +import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import useSearchQuery from "@saleor/hooks/useSearchQuery"; +import { buttonMessages } from "@saleor/intl"; +import { renderCollection } from "@saleor/misc"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; +import { ShippingPriceExcludeProduct } from "@saleor/shipping/types/ShippingPriceExcludeProduct"; +import { FetchMoreProps } from "@saleor/types"; +import React from "react"; +import { MutationFetchResult } from "react-apollo"; +import InfiniteScroll from "react-infinite-scroller"; +import { FormattedMessage, useIntl } from "react-intl"; + +const useStyles = makeStyles( + theme => ({ + avatar: { + paddingLeft: 0, + width: 64 + }, + colName: { + paddingLeft: 0 + }, + content: { + overflowY: "scroll" + }, + loadMoreLoaderContainer: { + alignItems: "center", + display: "flex", + height: theme.spacing(3), + justifyContent: "center", + marginTop: theme.spacing(3) + }, + overflow: { + overflowY: "visible" + }, + productCheckboxCell: { + "&:first-child": { + paddingLeft: 0, + paddingRight: 0 + } + } + }), + { name: "ShippingMethodProductsAddDialog" } +); + +export interface ShippingMethodProductsAddDialogProps extends FetchMoreProps { + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + products: SearchProducts_search_edges_node[]; + onClose: () => void; + onFetch: (query: string) => void; + onSubmit: ( + ids: string[] + ) => Promise>; +} + +const handleProductAssign = ( + product: SearchProducts_search_edges_node, + isSelected: boolean, + selectedProducts: SearchProducts_search_edges_node[], + setSelectedProducts: (data: SearchProducts_search_edges_node[]) => void +) => { + if (isSelected) { + setSelectedProducts( + selectedProducts.filter( + selectedProduct => selectedProduct.id !== product.id + ) + ); + } else { + setSelectedProducts([...selectedProducts, product]); + } +}; + +const ShippingMethodProductsAddDialog: React.FC = props => { + const { + confirmButtonState, + open, + loading, + hasMore, + products, + onFetch, + onFetchMore, + onClose, + onSubmit + } = props; + + const classes = useStyles(props); + const intl = useIntl(); + const [query, onQueryChange, resetQuery] = useSearchQuery(onFetch); + const [selectedProducts, setSelectedProducts] = React.useState< + SearchProducts_search_edges_node[] + >([]); + + const handleSubmit = () => { + onSubmit(selectedProducts.map(product => product.id)).then(() => { + setSelectedProducts([]); + resetQuery(); + }); + }; + + const handleClose = () => { + onClose(); + setSelectedProducts([]); + resetQuery(); + }; + + return ( + + + + + + + }} + /> + + + + + + } + threshold={10} + > + + + {renderCollection( + products, + (product, productIndex) => { + const isSelected = selectedProducts.some( + selectedProduct => selectedProduct.id === product.id + ); + return ( + + + + {product && ( + + handleProductAssign( + product, + isSelected, + selectedProducts, + setSelectedProducts + ) + } + /> + )} + + + + {product?.name || } + + + + ); + }, + () => ( + + + + + + ) + )} + + + + + + + + + + + + ); +}; +ShippingMethodProductsAddDialog.displayName = "ShippingMethodProductsAddDialog"; +export default ShippingMethodProductsAddDialog; diff --git a/src/shipping/components/ShippingMethodProductsAddDialog/index.ts b/src/shipping/components/ShippingMethodProductsAddDialog/index.ts new file mode 100644 index 00000000000..6049335c643 --- /dev/null +++ b/src/shipping/components/ShippingMethodProductsAddDialog/index.ts @@ -0,0 +1,2 @@ +export * from "./ShippingMethodProductsAddDialog"; +export { default } from "./ShippingMethodProductsAddDialog"; diff --git a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx new file mode 100644 index 00000000000..8652232f112 --- /dev/null +++ b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.stories.tsx @@ -0,0 +1,87 @@ +import Decorator from "@saleor/storybook//Decorator"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import ShippingZoneRatesCreatePage, { + ShippingZoneRatesCreatePageProps +} from "./ShippingZoneRatesCreatePage"; + +const channels = [ + { + currency: "USD", + id: "1", + maxValue: "10", + minValue: "0", + name: "channel", + price: "5" + }, + { + currency: "USD", + id: "2", + maxValue: "20", + minValue: "1", + name: "test", + price: "6" + } +]; + +const defaultChannels = [ + { + currency: "USD", + id: "1", + maxValue: "", + minValue: "", + name: "channel", + price: "" + } +]; + +const props: ShippingZoneRatesCreatePageProps = { + allChannelsCount: 3, + channelErrors: [], + disabled: false, + errors: [], + onBack: () => undefined, + onChannelsChange: () => undefined, + onDelete: () => undefined, + onSubmit: () => undefined, + onZipCodeAssign: () => undefined, + onZipCodeUnassign: () => undefined, + openChannelsModal: () => undefined, + saveButtonBarState: "default", + shippingChannels: defaultChannels, + variant: ShippingMethodTypeEnum.PRICE, + zipCodes: [ + { + __typename: "ShippingMethodZipCodeRule", + end: "51-200", + id: "1", + start: "51-220" + }, + { + __typename: "ShippingMethodZipCodeRule", + end: "31-101", + id: "1", + start: "44-205" + } + ] +}; + +storiesOf("Shipping / ShippingZoneRatesCreatePage page", module) + .addDecorator(Decorator) + .add("create price", () => ) + .add("loading", () => ( + + )) + .add("create weight", () => ( + + )); diff --git a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx new file mode 100644 index 00000000000..c390d76e1d3 --- /dev/null +++ b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx @@ -0,0 +1,190 @@ +import { ChannelShippingData } from "@saleor/channels/utils"; +import AppHeader from "@saleor/components/AppHeader"; +import CardSpacer from "@saleor/components/CardSpacer"; +import ChannelsAvailability from "@saleor/components/ChannelsAvailability"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Container from "@saleor/components/Container"; +import Form from "@saleor/components/Form"; +import Grid from "@saleor/components/Grid"; +import PageHeader from "@saleor/components/PageHeader"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { ShippingChannelsErrorFragment } from "@saleor/fragments/types/ShippingChannelsErrorFragment"; +import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; +import { ShippingMethodFragment_zipCodeRules } from "@saleor/fragments/types/ShippingMethodFragment"; +import { validatePrice } from "@saleor/products/utils/validation"; +import OrderValue from "@saleor/shipping/components/OrderValue"; +import OrderWeight from "@saleor/shipping/components/OrderWeight"; +import PricingCard from "@saleor/shipping/components/PricingCard"; +import ShippingZoneInfo from "@saleor/shipping/components/ShippingZoneInfo"; +import { createChannelsChangeHandler } from "@saleor/shipping/handlers"; +import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import ShippingZoneZipCodes, { + ZipCodeInclusion +} from "../ShippingZoneZipCodes"; + +export interface FormData { + channelListings: ChannelShippingData[]; + includeZipCodes: ZipCodeInclusion; + name: string; + noLimits: boolean; + minValue: string; + maxValue: string; + type: ShippingMethodTypeEnum; +} + +export interface ShippingZoneRatesCreatePageProps { + allChannelsCount?: number; + shippingChannels: ChannelShippingData[]; + disabled: boolean; + hasChannelChanged?: boolean; + zipCodes?: ShippingMethodFragment_zipCodeRules[]; + channelErrors: ShippingChannelsErrorFragment[]; + errors: ShippingErrorFragment[]; + saveButtonBarState: ConfirmButtonTransitionState; + onBack: () => void; + onDelete?: () => void; + onSubmit: (data: FormData) => void; + onZipCodeAssign: () => void; + onZipCodeUnassign: (id: string) => void; + onChannelsChange: (data: ChannelShippingData[]) => void; + openChannelsModal: () => void; + variant: ShippingMethodTypeEnum; +} + +export const ShippingZoneRatesCreatePage: React.FC = ({ + allChannelsCount, + shippingChannels, + channelErrors, + disabled, + errors, + hasChannelChanged, + onBack, + onDelete, + onSubmit, + onChannelsChange, + onZipCodeAssign, + onZipCodeUnassign, + openChannelsModal, + saveButtonBarState, + variant, + zipCodes +}) => { + const intl = useIntl(); + const isPriceVariant = variant === ShippingMethodTypeEnum.PRICE; + const initialForm: FormData = { + channelListings: shippingChannels, + includeZipCodes: ZipCodeInclusion.Include, + maxValue: "", + minValue: "", + name: "", + noLimits: false, + type: null + }; + + return ( +
+ {({ change, data, hasChanged, submit, triggerChange }) => { + const handleChannelsChange = createChannelsChangeHandler( + shippingChannels, + onChannelsChange, + triggerChange + ); + const formDisabled = data.channelListings?.some(channel => + validatePrice(channel.price) + ); + + return ( + + + + + + +
+ + + {isPriceVariant ? ( + + ) : ( + + )} + + + + undefined} + onZipCodeRangeAdd={onZipCodeAssign} + zipCodes={zipCodes} + /> +
+
+ ({ + id: channel.id, + name: channel.name + }))} + openModal={openChannelsModal} + /> +
+
+ +
+ ); + }} +
+ ); +}; + +export default ShippingZoneRatesCreatePage; diff --git a/src/shipping/components/ShippingZoneRatesCreatePage/index.ts b/src/shipping/components/ShippingZoneRatesCreatePage/index.ts new file mode 100644 index 00000000000..0a6b39e93b3 --- /dev/null +++ b/src/shipping/components/ShippingZoneRatesCreatePage/index.ts @@ -0,0 +1,2 @@ +export * from "./ShippingZoneRatesCreatePage"; +export { default } from "./ShippingZoneRatesCreatePage"; diff --git a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx index 2f1ed4686fc..a56ea4b8547 100644 --- a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx +++ b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.stories.tsx @@ -43,31 +43,26 @@ const props: ShippingZoneRatesPageProps = { channelErrors: [], disabled: false, errors: [], + isChecked: () => undefined, onBack: () => undefined, onChannelsChange: () => undefined, onDelete: () => undefined, + onNextPage: () => undefined, + onPreviousPage: () => undefined, + onProductAssign: () => undefined, + onProductUnassign: () => undefined, onSubmit: () => undefined, onZipCodeAssign: () => undefined, onZipCodeUnassign: () => undefined, openChannelsModal: () => undefined, - rate: null, + rate: shippingZone.shippingMethods[0], saveButtonBarState: "default", + selected: 0, shippingChannels: defaultChannels, - variant: ShippingMethodTypeEnum.PRICE, - zipCodes: [ - { - __typename: "ShippingMethodZipCodeRule", - end: "51-200", - id: "1", - start: "51-220" - }, - { - __typename: "ShippingMethodZipCodeRule", - end: "31-101", - id: "1", - start: "44-205" - } - ] + toggle: () => undefined, + toggleAll: () => undefined, + toolbar: () => undefined, + variant: ShippingMethodTypeEnum.PRICE }; storiesOf("Views / Shipping / Shipping rate", module) @@ -95,10 +90,6 @@ storiesOf("Views / Shipping / Shipping rate", module) - )) - .add("no zip codes", () => ( - )); diff --git a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx index d411d7c7871..980a15bd915 100644 --- a/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx +++ b/src/shipping/components/ShippingZoneRatesPage/ShippingZoneRatesPage.tsx @@ -10,19 +10,18 @@ import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { ShippingChannelsErrorFragment } from "@saleor/fragments/types/ShippingChannelsErrorFragment"; import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; -import { - ShippingMethodFragment, - ShippingMethodFragment_zipCodeRules -} from "@saleor/fragments/types/ShippingMethodFragment"; import { validatePrice } from "@saleor/products/utils/validation"; import OrderValue from "@saleor/shipping/components/OrderValue"; import OrderWeight from "@saleor/shipping/components/OrderWeight"; import PricingCard from "@saleor/shipping/components/PricingCard"; +import ShippingMethodProducts from "@saleor/shipping/components/ShippingMethodProducts"; import ShippingZoneInfo from "@saleor/shipping/components/ShippingZoneInfo"; import { createChannelsChangeHandler } from "@saleor/shipping/handlers"; +import { ShippingZone_shippingZone_shippingMethods } from "@saleor/shipping/types/ShippingZone"; +import { ListActions, ListProps } from "@saleor/types"; import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { FormattedMessage } from "react-intl"; import ShippingZoneZipCodes, { ZipCodeInclusion @@ -38,13 +37,14 @@ export interface FormData { type: ShippingMethodTypeEnum; } -export interface ShippingZoneRatesPageProps { +export interface ShippingZoneRatesPageProps + extends Pick>, + ListActions { allChannelsCount?: number; shippingChannels: ChannelShippingData[]; disabled: boolean; hasChannelChanged?: boolean; - rate: ShippingMethodFragment | null; - zipCodes?: ShippingMethodFragment_zipCodeRules[]; + rate: ShippingZone_shippingZone_shippingMethods; channelErrors: ShippingChannelsErrorFragment[]; errors: ShippingErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; @@ -55,6 +55,8 @@ export interface ShippingZoneRatesPageProps { onZipCodeUnassign: (id: string) => void; onChannelsChange: (data: ChannelShippingData[]) => void; openChannelsModal: () => void; + onProductAssign: () => void; + onProductUnassign: (ids: string[]) => void; variant: ShippingMethodTypeEnum; } @@ -71,13 +73,14 @@ export const ShippingZoneRatesPage: React.FC = ({ onChannelsChange, onZipCodeAssign, onZipCodeUnassign, + onProductAssign, + onProductUnassign, openChannelsModal, rate, saveButtonBarState, variant, - zipCodes + ...listProps }) => { - const intl = useIntl(); const isPriceVariant = variant === ShippingMethodTypeEnum.PRICE; const initialForm: FormData = { channelListings: shippingChannels, @@ -89,8 +92,6 @@ export const ShippingZoneRatesPage: React.FC = ({ type: rate?.type || null }; - const rateExists = rate !== null; - return (
{({ change, data, hasChanged, submit, triggerChange }) => { @@ -108,20 +109,7 @@ export const ShippingZoneRatesPage: React.FC = ({ - +
= ({ onZipCodeDelete={onZipCodeUnassign} onZipCodeInclusionChange={() => undefined} onZipCodeRangeAdd={onZipCodeAssign} - zipCodes={rateExists ? rate?.zipCodeRules : zipCodes} + zipCodes={rate?.zipCodeRules} + /> + + edge.node + )} + onProductAssign={onProductAssign} + onProductUnassign={onProductUnassign} + disabled={disabled} + {...listProps} />
diff --git a/src/shipping/components/UnassignDialog/UnassignDialog.stories.tsx b/src/shipping/components/UnassignDialog/UnassignDialog.stories.tsx new file mode 100644 index 00000000000..f18f170a3bf --- /dev/null +++ b/src/shipping/components/UnassignDialog/UnassignDialog.stories.tsx @@ -0,0 +1,17 @@ +import Decorator from "@saleor/storybook//Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import UnassignDialog, { UnassignDialogProps } from "./UnassignDialog"; + +const props: UnassignDialogProps = { + closeModal: () => undefined, + confirmButtonState: "default", + idsLength: 2, + onConfirm: () => undefined, + open: true +}; + +storiesOf("Shipping / UnassignDialog", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/shipping/components/UnassignDialog/UnassignDialog.tsx b/src/shipping/components/UnassignDialog/UnassignDialog.tsx new file mode 100644 index 00000000000..ca2f71a5a86 --- /dev/null +++ b/src/shipping/components/UnassignDialog/UnassignDialog.tsx @@ -0,0 +1,48 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +export interface UnassignDialogProps { + open: boolean; + confirmButtonState: ConfirmButtonTransitionState; + idsLength: number; + closeModal: () => void; + onConfirm: () => void; +} + +export const UnassignDialog: React.FC = ({ + closeModal, + confirmButtonState, + idsLength, + onConfirm, + open +}) => { + const intl = useIntl(); + return ( + + + {idsLength} + }} + /> + + + ); +}; + +export default UnassignDialog; diff --git a/src/shipping/components/UnassignDialog/index.ts b/src/shipping/components/UnassignDialog/index.ts new file mode 100644 index 00000000000..57f90cddb2c --- /dev/null +++ b/src/shipping/components/UnassignDialog/index.ts @@ -0,0 +1,2 @@ +export * from "./UnassignDialog"; +export { default } from "./UnassignDialog"; diff --git a/src/shipping/fixtures.ts b/src/shipping/fixtures.ts index 7045d9e0ba8..57a4635173d 100644 --- a/src/shipping/fixtures.ts +++ b/src/shipping/fixtures.ts @@ -1,5 +1,6 @@ -import { ShippingZoneDetailsFragment } from "@saleor/fragments/types/ShippingZoneDetailsFragment"; import { ShippingZoneFragment } from "@saleor/fragments/types/ShippingZoneFragment"; +import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts"; +import { ShippingZone_shippingZone } from "@saleor/shipping/types/ShippingZone"; import { ShippingMethodTypeEnum, WeightUnitsEnum } from "../types/globalTypes"; @@ -1285,7 +1286,7 @@ export const shippingZones: ShippingZoneFragment[] = [ } ]; -export const shippingZone: ShippingZoneDetailsFragment = { +export const shippingZone: ShippingZone_shippingZone = { __typename: "ShippingZone", countries: [ { @@ -1577,6 +1578,30 @@ export const shippingZone: ShippingZoneDetailsFragment = { } } ], + excludedProducts: { + __typename: "ProductCountableConnection", + edges: [ + { + __typename: "ProductCountableEdge", + node: { + __typename: "Product", + id: "1", + name: "Apple Juice", + thumbnail: { + __typename: "Image", + url: "" + } + } + } + ], + pageInfo: { + __typename: "PageInfo", + endCursor: "", + hasNextPage: false, + hasPreviousPage: false, + startCursor: "" + } + }, id: "U2hpcHBpbmdNZXRob2Q6NA==", maximumOrderWeight: { __typename: "Weight", @@ -1614,6 +1639,30 @@ export const shippingZone: ShippingZoneDetailsFragment = { { __typename: "ShippingMethod", channelListings: [], + excludedProducts: { + __typename: "ProductCountableConnection", + edges: [ + { + __typename: "ProductCountableEdge", + node: { + __typename: "Product", + id: "1", + name: "Apple Juice", + thumbnail: { + __typename: "Image", + url: "" + } + } + } + ], + pageInfo: { + __typename: "PageInfo", + endCursor: "", + hasNextPage: false, + hasPreviousPage: false, + startCursor: "" + } + }, id: "U2hpcHBpbmdNZXRob2Q6Mw==", maximumOrderWeight: null, minimumOrderWeight: { @@ -1647,6 +1696,30 @@ export const shippingZone: ShippingZoneDetailsFragment = { { __typename: "ShippingMethod", channelListings: [], + excludedProducts: { + __typename: "ProductCountableConnection", + edges: [ + { + __typename: "ProductCountableEdge", + node: { + __typename: "Product", + id: "1", + name: "Apple Juice", + thumbnail: { + __typename: "Image", + url: "" + } + } + } + ], + pageInfo: { + __typename: "PageInfo", + endCursor: "", + hasNextPage: false, + hasPreviousPage: false, + startCursor: "" + } + }, id: "U2hpcHBpbmdNZXRob2Q6Mg==", maximumOrderWeight: null, minimumOrderWeight: { @@ -1680,6 +1753,17 @@ export const shippingZone: ShippingZoneDetailsFragment = { { __typename: "ShippingMethod", channelListings: [], + excludedProducts: { + __typename: "ProductCountableConnection", + edges: [], + pageInfo: { + __typename: "PageInfo", + endCursor: "", + hasNextPage: false, + hasPreviousPage: false, + startCursor: "" + } + }, id: "U2hpcHBpbmdNZXRob2Q6MQ==", maximumOrderWeight: null, minimumOrderWeight: { @@ -1724,3 +1808,24 @@ export const shippingZone: ShippingZoneDetailsFragment = { } ] }; + +export const products: SearchProducts_search_edges_node[] = [ + { + __typename: "Product", + id: "1", + name: "Apple Juice", + thumbnail: { + __typename: "Image", + url: "" + } + }, + { + __typename: "Product", + id: "2", + name: "Banana Juice", + thumbnail: { + __typename: "Image", + url: "" + } + } +]; diff --git a/src/shipping/mutations.ts b/src/shipping/mutations.ts index 35c0f943825..8874db55751 100644 --- a/src/shipping/mutations.ts +++ b/src/shipping/mutations.ts @@ -47,6 +47,14 @@ import { ShippingMethodZipCodeRangeUnassign, ShippingMethodZipCodeRangeUnassignVariables } from "./types/ShippingMethodZipCodeRangeUnassign"; +import { + ShippingPriceExcludeProduct, + ShippingPriceExcludeProductVariables +} from "./types/ShippingPriceExcludeProduct"; +import { + ShippingPriceRemoveProductFromExclude, + ShippingPriceRemoveProductFromExcludeVariables +} from "./types/ShippingPriceRemoveProductFromExclude"; import { UpdateDefaultWeightUnit, UpdateDefaultWeightUnitVariables @@ -297,3 +305,38 @@ export const useShippingMethodZipCodeRangeUnassign = makeMutation< ShippingMethodZipCodeRangeUnassign, ShippingMethodZipCodeRangeUnassignVariables >(shippingMethodZipCodeRulesDelete); + +export const shippingPriceExcludeProducts = gql` + ${shippingErrorFragment} + mutation ShippingPriceExcludeProduct( + $id: ID! + $input: ShippingPriceExcludeProductsInput! + ) { + shippingPriceExcludeProducts(id: $id, input: $input) { + errors: shippingErrors { + ...ShippingErrorFragment + } + } + } +`; + +export const useShippingPriceExcludeProduct = makeMutation< + ShippingPriceExcludeProduct, + ShippingPriceExcludeProductVariables +>(shippingPriceExcludeProducts); + +export const shippingPriceRemoveProductsFromExclude = gql` + ${shippingErrorFragment} + mutation ShippingPriceRemoveProductFromExclude($id: ID!, $products: [ID]!) { + shippingPriceRemoveProductFromExclude(id: $id, products: $products) { + errors: shippingErrors { + ...ShippingErrorFragment + } + } + } +`; + +export const useShippingPriceRemoveProductsFromExclude = makeMutation< + ShippingPriceRemoveProductFromExclude, + ShippingPriceRemoveProductFromExcludeVariables +>(shippingPriceRemoveProductsFromExclude); diff --git a/src/shipping/queries.ts b/src/shipping/queries.ts index 6f4eb71b376..5d288d90ce0 100644 --- a/src/shipping/queries.ts +++ b/src/shipping/queries.ts @@ -1,6 +1,6 @@ import { pageInfoFragment } from "@saleor/fragments/pageInfo"; import { - shippingZoneDetailsFragment, + shippingMethodWithExcludedProductsFragment, shippingZoneFragment } from "@saleor/fragments/shipping"; import makeQuery from "@saleor/hooks/makeQuery"; @@ -36,10 +36,25 @@ export const useShippingZoneList = makeQuery< >(shippingZones); const shippingZone = gql` - ${shippingZoneDetailsFragment} - query ShippingZone($id: ID!) { + ${shippingZoneFragment} + ${shippingMethodWithExcludedProductsFragment} + query ShippingZone( + $id: ID! + $before: String + $after: String + $first: Int + $last: Int + ) { shippingZone(id: $id) { - ...ShippingZoneDetailsFragment + ...ShippingZoneFragment + default + shippingMethods { + ...ShippingMethodWithExcludedProductsFragment + } + warehouses { + id + name + } } } `; diff --git a/src/shipping/types/ShippingPriceExcludeProduct.ts b/src/shipping/types/ShippingPriceExcludeProduct.ts new file mode 100644 index 00000000000..cf70df077e7 --- /dev/null +++ b/src/shipping/types/ShippingPriceExcludeProduct.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ShippingPriceExcludeProductsInput, ShippingErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ShippingPriceExcludeProduct +// ==================================================== + +export interface ShippingPriceExcludeProduct_shippingPriceExcludeProducts_errors { + __typename: "ShippingError"; + code: ShippingErrorCode; + field: string | null; +} + +export interface ShippingPriceExcludeProduct_shippingPriceExcludeProducts { + __typename: "ShippingPriceExcludeProducts"; + errors: ShippingPriceExcludeProduct_shippingPriceExcludeProducts_errors[]; +} + +export interface ShippingPriceExcludeProduct { + shippingPriceExcludeProducts: ShippingPriceExcludeProduct_shippingPriceExcludeProducts | null; +} + +export interface ShippingPriceExcludeProductVariables { + id: string; + input: ShippingPriceExcludeProductsInput; +} diff --git a/src/shipping/types/ShippingPriceRemoveProductFromExclude.ts b/src/shipping/types/ShippingPriceRemoveProductFromExclude.ts new file mode 100644 index 00000000000..1c55c5bf479 --- /dev/null +++ b/src/shipping/types/ShippingPriceRemoveProductFromExclude.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ShippingErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ShippingPriceRemoveProductFromExclude +// ==================================================== + +export interface ShippingPriceRemoveProductFromExclude_shippingPriceRemoveProductFromExclude_errors { + __typename: "ShippingError"; + code: ShippingErrorCode; + field: string | null; +} + +export interface ShippingPriceRemoveProductFromExclude_shippingPriceRemoveProductFromExclude { + __typename: "ShippingPriceRemoveProductFromExclude"; + errors: ShippingPriceRemoveProductFromExclude_shippingPriceRemoveProductFromExclude_errors[]; +} + +export interface ShippingPriceRemoveProductFromExclude { + shippingPriceRemoveProductFromExclude: ShippingPriceRemoveProductFromExclude_shippingPriceRemoveProductFromExclude | null; +} + +export interface ShippingPriceRemoveProductFromExcludeVariables { + id: string; + products: (string | null)[]; +} diff --git a/src/shipping/types/ShippingZone.ts b/src/shipping/types/ShippingZone.ts index 04bb699058b..3d9724c8030 100644 --- a/src/shipping/types/ShippingZone.ts +++ b/src/shipping/types/ShippingZone.ts @@ -67,6 +67,37 @@ export interface ShippingZone_shippingZone_shippingMethods_channelListings { maximumOrderPrice: ShippingZone_shippingZone_shippingMethods_channelListings_maximumOrderPrice | null; } +export interface ShippingZone_shippingZone_shippingMethods_excludedProducts_pageInfo { + __typename: "PageInfo"; + hasNextPage: boolean; + hasPreviousPage: boolean; + endCursor: string | null; + startCursor: string | null; +} + +export interface ShippingZone_shippingZone_shippingMethods_excludedProducts_edges_node_thumbnail { + __typename: "Image"; + url: string; +} + +export interface ShippingZone_shippingZone_shippingMethods_excludedProducts_edges_node { + __typename: "Product"; + id: string; + name: string; + thumbnail: ShippingZone_shippingZone_shippingMethods_excludedProducts_edges_node_thumbnail | null; +} + +export interface ShippingZone_shippingZone_shippingMethods_excludedProducts_edges { + __typename: "ProductCountableEdge"; + node: ShippingZone_shippingZone_shippingMethods_excludedProducts_edges_node; +} + +export interface ShippingZone_shippingZone_shippingMethods_excludedProducts { + __typename: "ProductCountableConnection"; + pageInfo: ShippingZone_shippingZone_shippingMethods_excludedProducts_pageInfo; + edges: ShippingZone_shippingZone_shippingMethods_excludedProducts_edges[]; +} + export interface ShippingZone_shippingZone_shippingMethods { __typename: "ShippingMethod"; id: string; @@ -76,6 +107,7 @@ export interface ShippingZone_shippingZone_shippingMethods { name: string; type: ShippingMethodTypeEnum | null; channelListings: ShippingZone_shippingZone_shippingMethods_channelListings[] | null; + excludedProducts: ShippingZone_shippingZone_shippingMethods_excludedProducts | null; } export interface ShippingZone_shippingZone_warehouses { @@ -100,4 +132,8 @@ export interface ShippingZone { export interface ShippingZoneVariables { id: string; + before?: string | null; + after?: string | null; + first?: number | null; + last?: number | null; } diff --git a/src/shipping/urls.ts b/src/shipping/urls.ts index 2421f18a99a..2f0fda05a0d 100644 --- a/src/shipping/urls.ts +++ b/src/shipping/urls.ts @@ -26,20 +26,30 @@ export type ShippingZoneUrlDialog = | "remove" | "remove-rate" | "unassign-country"; + +export type ShippingMethodActions = "assign-product" | "unassign-product"; + export type ShippingZoneUrlQueryParams = Dialog & SingleAction & Partial<{ type: ShippingMethodTypeEnum; - }>; + }> & + Pagination; export const shippingZoneUrl = ( id: string, params?: ShippingZoneUrlQueryParams ) => shippingZonePath(encodeURIComponent(id)) + "?" + stringifyQs(params); type ZipCodeRangeActions = "add-range" | "remove-range"; -export type ShippingRateUrlDialog = ZipCodeRangeActions | "remove"; +export type ShippingRateUrlDialog = + | ZipCodeRangeActions + | "remove" + | ShippingMethodActions; + export type ShippingRateUrlQueryParams = Dialog & - SingleAction; + SingleAction & + BulkAction & + Pagination; export type ShippingRateCreateUrlDialog = ZipCodeRangeActions; export type ShippingRateCreateUrlQueryParams = Dialog< ShippingRateCreateUrlDialog diff --git a/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx b/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx index 3ce8f048c45..05a481de856 100644 --- a/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx +++ b/src/shipping/views/PriceRatesCreate/PriceRatesCreate.tsx @@ -7,7 +7,7 @@ import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import { sectionNames } from "@saleor/intl"; import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; -import ShippingZoneRatesPage from "@saleor/shipping/components/ShippingZoneRatesPage"; +import ShippingZoneRatesCreatePage from "@saleor/shipping/components/ShippingZoneRatesCreatePage"; import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; import { useShippingRateCreator } from "@saleor/shipping/handlers"; import { @@ -114,7 +114,7 @@ export const PriceRatesCreate: React.FC = ({ /> )} - = ({ onBack={handleBack} errors={errors} channelErrors={channelErrors} - rate={null} zipCodes={zipCodes} openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} diff --git a/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx b/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx index b83a646eaaa..deeb4f552c6 100644 --- a/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx +++ b/src/shipping/views/PriceRatesUpdate/PriceRatesUpdate.tsx @@ -1,3 +1,4 @@ +import Button from "@material-ui/core/Button"; import { useChannelsList } from "@saleor/channels/queries"; import { createShippingChannelsFromRate, @@ -5,17 +6,26 @@ import { } from "@saleor/channels/utils"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { PAGINATE_BY } from "@saleor/config"; +import useBulkActions from "@saleor/hooks/useBulkActions"; import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; +import usePaginator, { + createPaginationState +} from "@saleor/hooks/usePaginator"; import { sectionNames } from "@saleor/intl"; import { commonMessages } from "@saleor/intl"; +import useProductSearch from "@saleor/searches/useProductSearch"; import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; +import ShippingMethodProductsAddDialog from "@saleor/shipping/components/ShippingMethodProductsAddDialog"; import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; import ShippingZoneRatesPage, { FormData } from "@saleor/shipping/components/ShippingZoneRatesPage"; import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; +import UnassignDialog from "@saleor/shipping/components/UnassignDialog"; import { getShippingMethodChannelVariables, getUpdateShippingPriceRateVariables @@ -23,9 +33,9 @@ import { import { useShippingMethodChannelListingUpdate, useShippingMethodZipCodeRangeAssign, - useShippingMethodZipCodeRangeUnassign -} from "@saleor/shipping/mutations"; -import { + useShippingMethodZipCodeRangeUnassign, + useShippingPriceExcludeProduct, + useShippingPriceRemoveProductsFromExclude, useShippingRateDelete, useShippingRateUpdate } from "@saleor/shipping/mutations"; @@ -39,7 +49,7 @@ import { import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import React from "react"; -import { useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; export interface PriceRatesUpdateProps { id: string; @@ -55,11 +65,19 @@ export const PriceRatesUpdate: React.FC = ({ const navigate = useNavigator(); const notify = useNotifier(); const intl = useIntl(); + const paginate = usePaginator(); - const { data, loading } = useShippingZone({ + const paginationState = createPaginationState(PAGINATE_BY, params); + + const { data, loading, refetch } = useShippingZone({ displayLoader: true, - variables: { id } + variables: { id, ...paginationState } }); + const { + loadMore, + search: productsSearch, + result: productsSearchOpts + } = useProductSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); const [openModal, closeModal] = createDialogActionHandlers< ShippingRateUrlDialog, @@ -69,6 +87,17 @@ export const PriceRatesUpdate: React.FC = ({ const rate = data?.shippingZone?.shippingMethods.find( rate => rate.id === rateId ); + + const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( + [] + ); + + const { loadNextPage, loadPreviousPage, pageInfo } = paginate( + rate?.excludedProducts.pageInfo, + paginationState, + params + ); + const { data: channelsData } = useChannelsList({}); const [ @@ -98,6 +127,7 @@ export const PriceRatesUpdate: React.FC = ({ } } }); + const [ unassignZipCodeRange, unassignZipCodeRangeOpts @@ -108,11 +138,32 @@ export const PriceRatesUpdate: React.FC = ({ status: "success", text: intl.formatMessage(commonMessages.savedChanges) }); + } + } + }); + + const [ + unassignProduct, + unassignProductOpts + ] = useShippingPriceRemoveProductsFromExclude({ + onCompleted: data => { + if (data.shippingPriceRemoveProductFromExclude.errors.length === 0) { + handleSuccess(); + refetch(); closeModal(); } } }); + const [assignProduct, assignProductOpts] = useShippingPriceExcludeProduct({ + onCompleted: data => { + if (data.shippingPriceExcludeProducts.errors.length === 0) { + handleSuccess(); + refetch(); + closeModal(); + } + } + }); const shippingChannels = createShippingChannelsFromRate( rate?.channelListings ); @@ -168,6 +219,18 @@ export const PriceRatesUpdate: React.FC = ({ } }; + const handleProductAssign = (ids: string[]) => + assignProduct({ + variables: { id: rateId, input: { products: ids } } + }); + + const handleProductUnassign = (ids: string[]) => { + unassignProduct({ + variables: { id: rateId, products: ids } + }); + reset(); + }; + const handleBack = () => navigate(shippingZoneUrl(id)); return ( @@ -203,13 +266,35 @@ export const PriceRatesUpdate: React.FC = ({ open={params.action === "remove"} name={rate?.name} /> + handleProductUnassign(listElements)} + /> + edge.node) + .filter(suggestedProduct => suggestedProduct.id)} + onClose={closeModal} + onFetch={productsSearch} + onFetchMore={loadMore} + onSubmit={handleProductAssign} + /> = ({ } openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} + onProductUnassign={handleProductUnassign} + onProductAssign={() => openModal("assign-product")} variant={ShippingMethodTypeEnum.PRICE} + isChecked={isSelected} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + pageInfo={pageInfo} + toolbar={ + + } onZipCodeAssign={() => openModal("add-range")} onZipCodeUnassign={id => openModal("remove-range", { diff --git a/src/shipping/views/ShippingZoneDetails/index.tsx b/src/shipping/views/ShippingZoneDetails/index.tsx index 501b81cbead..33a66b20014 100644 --- a/src/shipping/views/ShippingZoneDetails/index.tsx +++ b/src/shipping/views/ShippingZoneDetails/index.tsx @@ -3,8 +3,10 @@ import ActionDialog from "@saleor/components/ActionDialog"; import useAppChannel from "@saleor/components/AppLayout/AppChannelContext"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { PAGINATE_BY } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; +import { createPaginationState } from "@saleor/hooks/usePaginator"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; import useWarehouseSearch from "@saleor/searches/useWarehouseSearch"; @@ -53,6 +55,8 @@ const ShippingZoneDetails: React.FC = ({ const intl = useIntl(); const shop = useShop(); + const paginationState = createPaginationState(PAGINATE_BY, params); + const { result: searchWarehousesOpts, loadMore, search } = useWarehouseSearch( { variables: DEFAULT_INITIAL_SEARCH_DATA @@ -61,7 +65,7 @@ const ShippingZoneDetails: React.FC = ({ const { data, loading } = useShippingZone({ displayLoader: true, - variables: { id } + variables: { id, ...paginationState } }); const { channel } = useAppChannel(); diff --git a/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx b/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx index 16bbd992d25..fc16a78e345 100644 --- a/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx +++ b/src/shipping/views/WeightRatesCreate/WeightRatesCreate.tsx @@ -10,7 +10,7 @@ import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import { sectionNames } from "@saleor/intl"; import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; -import ShippingZoneRatesPage from "@saleor/shipping/components/ShippingZoneRatesPage"; +import ShippingZoneRatesCreatePage from "@saleor/shipping/components/ShippingZoneRatesCreatePage"; import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; import { useShippingRateCreator } from "@saleor/shipping/handlers"; import { @@ -117,7 +117,7 @@ export const WeightRatesCreate: React.FC = ({ toggleAll={toggleAllChannels} /> )} - = ({ onBack={handleBack} errors={errors} channelErrors={channelErrors} - rate={null} zipCodes={zipCodes} openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} diff --git a/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx b/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx index 2e6cbdb8060..7216582b49d 100644 --- a/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx +++ b/src/shipping/views/WeightRatesUpdate/WeightRatesUpdate.tsx @@ -1,3 +1,4 @@ +import Button from "@material-ui/core/Button"; import { useChannelsList } from "@saleor/channels/queries"; import { createShippingChannelsFromRate, @@ -5,28 +6,39 @@ import { } from "@saleor/channels/utils"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { PAGINATE_BY } from "@saleor/config"; +import useBulkActions from "@saleor/hooks/useBulkActions"; import useChannels from "@saleor/hooks/useChannels"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; +import usePaginator, { + createPaginationState +} from "@saleor/hooks/usePaginator"; import { sectionNames } from "@saleor/intl"; import { commonMessages } from "@saleor/intl"; +import useProductSearch from "@saleor/searches/useProductSearch"; import DeleteShippingRateDialog from "@saleor/shipping/components/DeleteShippingRateDialog"; +import ShippingMethodProductsAddDialog from "@saleor/shipping/components/ShippingMethodProductsAddDialog"; import ShippingRateZipCodeRangeRemoveDialog from "@saleor/shipping/components/ShippingRateZipCodeRangeRemoveDialog"; import ShippingZoneRatesPage, { FormData } from "@saleor/shipping/components/ShippingZoneRatesPage"; import ShippingZoneZipCodeRangeDialog from "@saleor/shipping/components/ShippingZoneZipCodeRangeDialog"; +import UnassignDialog from "@saleor/shipping/components/UnassignDialog"; import { getShippingMethodChannelVariables, getUpdateShippingWeightRateVariables } from "@saleor/shipping/handlers"; import { + useShippingMethodChannelListingUpdate, useShippingMethodZipCodeRangeAssign, useShippingMethodZipCodeRangeUnassign, + useShippingPriceExcludeProduct, + useShippingPriceRemoveProductsFromExclude, useShippingRateDelete, useShippingRateUpdate } from "@saleor/shipping/mutations"; -import { useShippingMethodChannelListingUpdate } from "@saleor/shipping/mutations"; import { useShippingZone } from "@saleor/shipping/queries"; import { ShippingRateUrlDialog, @@ -37,7 +49,7 @@ import { import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import React from "react"; -import { useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; export interface WeightRatesUpdateProps { id: string; @@ -53,10 +65,13 @@ export const WeightRatesUpdate: React.FC = ({ const navigate = useNavigator(); const notify = useNotifier(); const intl = useIntl(); + const paginate = usePaginator(); - const { data, loading } = useShippingZone({ + const paginationState = createPaginationState(PAGINATE_BY, params); + + const { data, loading, refetch } = useShippingZone({ displayLoader: true, - variables: { id } + variables: { id, ...paginationState } }); const [openModal, closeModal] = createDialogActionHandlers< @@ -64,15 +79,55 @@ export const WeightRatesUpdate: React.FC = ({ ShippingRateUrlQueryParams >(navigate, params => shippingWeightRatesEditUrl(id, rateId, params), params); + const { + loadMore, + search: productsSearch, + result: productsSearchOpts + } = useProductSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); + const rate = data?.shippingZone?.shippingMethods.find( rate => rate.id === rateId ); + const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( + [] + ); + + const { loadNextPage, loadPreviousPage, pageInfo } = paginate( + rate?.excludedProducts.pageInfo, + paginationState, + params + ); + const { data: channelsData } = useChannelsList({}); const [ updateShippingMethodChannelListing, updateShippingMethodChannelListingOpts ] = useShippingMethodChannelListingUpdate({}); + + const [ + unassignProduct, + unassignProductOpts + ] = useShippingPriceRemoveProductsFromExclude({ + onCompleted: data => { + if (data.shippingPriceRemoveProductFromExclude.errors.length === 0) { + handleSuccess(); + refetch(); + closeModal(); + } + } + }); + + const [assignProduct, assignProductOpts] = useShippingPriceExcludeProduct({ + onCompleted: data => { + if (data.shippingPriceExcludeProducts.errors.length === 0) { + handleSuccess(); + refetch(); + closeModal(); + } + } + }); + const shippingChannels = createShippingChannelsFromRate( rate?.channelListings ); @@ -166,6 +221,18 @@ export const WeightRatesUpdate: React.FC = ({ } }; + const handleProductAssign = (ids: string[]) => + assignProduct({ + variables: { id: rateId, input: { products: ids } } + }); + + const handleProductUnassign = (ids: string[]) => { + unassignProduct({ + variables: { id: rateId, products: ids } + }); + reset(); + }; + const handleBack = () => navigate(shippingZoneUrl(id)); return ( @@ -201,13 +268,35 @@ export const WeightRatesUpdate: React.FC = ({ open={params.action === "remove"} name={rate?.name} /> + handleProductUnassign(listElements)} + /> + edge.node) + .filter(suggestedProduct => suggestedProduct.id)} + onClose={closeModal} + onFetch={productsSearch} + onFetchMore={loadMore} + onSubmit={handleProductAssign} + /> = ({ } openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} + onProductUnassign={handleProductUnassign} + onProductAssign={() => openModal("assign-product")} variant={ShippingMethodTypeEnum.WEIGHT} + isChecked={isSelected} + selected={listElements.length} + toggle={toggle} + toggleAll={toggleAll} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + pageInfo={pageInfo} + toolbar={ + + } onZipCodeAssign={() => openModal("add-range")} onZipCodeUnassign={id => openModal("remove-range", { diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index a37ba2ea1b1..897a7136084 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -13873,6 +13873,2835 @@ exports[`Storyshots Shipping / Pricing Card loading 1`] = `
`; +exports[`Storyshots Shipping / ShippingMethodProducts default 1`] = ` +
+
+
+ + Excluded Products + +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+ Selected 1 items +
+
+
+
+
+ + + + + + + +
+
+
+
+ Apple Juice +
+
+
+
+ +
+
+
+
+`; + +exports[`Storyshots Shipping / ShippingMethodProductsAddDialog default 1`] = ` +
+`; + +exports[`Storyshots Shipping / ShippingZoneRatesCreatePage page create price 1`] = ` +
+ +
+
+
+ Price Rate Create +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order Value + +
+
+
+
+
+
+ +
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+
+ + + + + + + + + + + + + + + +
+ + Channel name + + + + Min. value + + + + Max. value + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + ZIP-Codes + +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+ 2 ZIP-Code ranges +
+
+ +
+ 51-220 - 51-200 + + +
+ 44-205 - 31-101 + + +
+
+
+
+
+
+
+ + Availability + +
+
+
+
+
+
+ Available at 1 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Shipping / ShippingZoneRatesCreatePage page create weight 1`] = ` +
+
+
+
+
+ Weight Rate Create +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order Weight + +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ test +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + ZIP-Codes + +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+ 2 ZIP-Code ranges +
+
+ +
+ 51-220 - 51-200 + + +
+ 44-205 - 31-101 + + +
+
+
+
+
+
+
+ + Availability + +
+
+
+
+
+
+ Available at 2 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+ test +
+
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Shipping / ShippingZoneRatesCreatePage page loading 1`] = ` +
+
+
+
+
+ Price Rate Create +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ + Order Value + +
+
+
+
+
+
+ +
+ Channels that don’t have assigned discounts will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+
+ + + + + + + + + + + + + + + +
+ + Channel name + + + + Min. value + + + + Max. value + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+ Channels that don’t have assigned prices will use their parent channel to define the price. Price will be converted to channel’s currency +
+
+ + + + + + + + + + + + + +
+ + Channel name + + + + Price + +
+
+ channel +
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+
+ + ZIP-Codes + +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+ 2 ZIP-Code ranges +
+
+ +
+ 51-220 - 51-200 + + +
+ 44-205 - 31-101 + + +
+
+
+
+
+
+
+ + Availability + +
+
+
+
+
+
+ Available at 1 out of 3 channels +
+
+
+
+
+ channel +
+
+
+
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Shipping / UnassignDialog default 1`] = ` +
+`; + exports[`Storyshots SiteSettings / Add key dialog default 1`] = `
- - ‌ - +
+ 1 Field +
+ +
+
+ + + + + + + + + + + +