From cd88102af7ba16741980faf0ae9455f753dc1d2a Mon Sep 17 00:00:00 2001 From: AlicjaSzu Date: Fri, 18 Sep 2020 15:01:00 +0200 Subject: [PATCH 1/9] fix product availability date (#696) * VisibilityCard - fix product availability date * revert changes in VisibilityCard, refetch data on successfully updated availability * fix availability in ProductCreate and ProductUpdate * ProductUpdate - remove unneeded refetch * create getProductAvailabilityVariables handler --- .../VisibilityCard/VisibilityCard.tsx | 5 ++- src/products/mutations.ts | 5 +++ .../ProductSetAvailabilityForPurchase.ts | 8 ++++ src/products/utils/handlers.ts | 27 +++++++++++++ src/products/views/ProductCreate.tsx | 40 +++++++++++++++++-- .../views/ProductUpdate/ProductUpdate.tsx | 9 ++++- src/products/views/ProductUpdate/handlers.ts | 20 ++++------ 7 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/components/VisibilityCard/VisibilityCard.tsx b/src/components/VisibilityCard/VisibilityCard.tsx index 4207baff5..a97754e83 100644 --- a/src/components/VisibilityCard/VisibilityCard.tsx +++ b/src/components/VisibilityCard/VisibilityCard.tsx @@ -241,7 +241,10 @@ export const VisibilityCard: React.FC = props => { const { value } = e.target; if (!value) { onChange({ - target: { name: "availableForPurchase", value: null } + target: { + name: "availableForPurchase", + value: null + } }); } return onChange(e); diff --git a/src/products/mutations.ts b/src/products/mutations.ts index 12d6d4de3..663f6cad1 100644 --- a/src/products/mutations.ts +++ b/src/products/mutations.ts @@ -611,6 +611,11 @@ const productSetAvailabilityForPurchase = gql` productId: $productId startDate: $startDate ) { + product { + id + availableForPurchase + isAvailableForPurchase + } errors: productErrors { ...ProductErrorFragment message diff --git a/src/products/types/ProductSetAvailabilityForPurchase.ts b/src/products/types/ProductSetAvailabilityForPurchase.ts index cc8cb6557..8a5fdeebb 100644 --- a/src/products/types/ProductSetAvailabilityForPurchase.ts +++ b/src/products/types/ProductSetAvailabilityForPurchase.ts @@ -8,6 +8,13 @@ import { ProductErrorCode } from "./../../types/globalTypes"; // GraphQL mutation operation: ProductSetAvailabilityForPurchase // ==================================================== +export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_product { + __typename: "Product"; + id: string; + availableForPurchase: any | null; + isAvailableForPurchase: boolean | null; +} + export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_errors { __typename: "ProductError"; code: ProductErrorCode; @@ -17,6 +24,7 @@ export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurc export interface ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase { __typename: "ProductSetAvailabilityForPurchase"; + product: ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_product | null; errors: ProductSetAvailabilityForPurchase_productSetAvailabilityForPurchase_errors[]; } diff --git a/src/products/utils/handlers.ts b/src/products/utils/handlers.ts index cba4b3018..e6ae599b5 100644 --- a/src/products/utils/handlers.ts +++ b/src/products/utils/handlers.ts @@ -116,3 +116,30 @@ export function createProductTypeSelectHandler( ); }; } + +interface ProductAvailabilityArgs { + availableForPurchase: string | null; + isAvailableForPurchase: boolean; + productId: string; +} + +export function getProductAvailabilityVariables({ + isAvailableForPurchase, + availableForPurchase, + productId +}: ProductAvailabilityArgs) { + const isAvailable = + availableForPurchase && !isAvailableForPurchase + ? true + : isAvailableForPurchase; + + return { + isAvailable, + productId, + startDate: isAvailableForPurchase + ? null + : availableForPurchase !== "" + ? availableForPurchase + : null + }; +} diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 24d06a9da..f59685bff 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -3,6 +3,7 @@ import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; +import { getProductAvailabilityVariables } from "@saleor/products/utils/handlers"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; @@ -19,7 +20,10 @@ import { decimal, weight } from "../../misc"; import ProductCreatePage, { ProductCreatePageSubmitData } from "../components/ProductCreatePage"; -import { useProductCreateMutation } from "../mutations"; +import { + useProductCreateMutation, + useProductSetAvailabilityForPurchase +} from "../mutations"; import { productListUrl, productUrl } from "../urls"; export const ProductCreateView: React.FC = () => { @@ -59,6 +63,18 @@ export const ProductCreateView: React.FC = () => { const handleBack = () => navigate(productListUrl()); + const [ + setProductAvailability, + productAvailabilityOpts + ] = useProductSetAvailabilityForPurchase({ + onCompleted: data => { + const errors = data?.productSetAvailabilityForPurchase?.errors; + if (errors?.length === 0) { + navigate(productUrl(data.productSetAvailabilityForPurchase.product.id)); + } + } + }); + const [productCreate, productCreateOpts] = useProductCreateMutation({ onCompleted: data => { if (data.productCreate.errors.length === 0) { @@ -68,7 +84,6 @@ export const ProductCreateView: React.FC = () => { defaultMessage: "Product created" }) }); - navigate(productUrl(data.productCreate.product.id)); } } }); @@ -100,11 +115,28 @@ export const ProductCreateView: React.FC = () => { warehouse: stock.id })), trackInventory: formData.trackInventory, + visibleInListings: formData.visibleInListings, weight: weight(formData.weight) } }); - return result.data.productCreate?.product?.id || null; + const productId = result.data.productCreate?.product?.id; + + if (productId) { + const { isAvailableForPurchase, availableForPurchase } = formData; + + const variables = getProductAvailabilityVariables({ + availableForPurchase, + isAvailableForPurchase, + productId + }); + + setProductAvailability({ + variables + }); + } + + return productId || null; }; const handleSubmit = createMetadataCreateHandler( handleCreate, @@ -128,7 +160,7 @@ export const ProductCreateView: React.FC = () => { collections={(searchCollectionOpts.data?.search.edges || []).map( edge => edge.node )} - disabled={productCreateOpts.loading} + disabled={productCreateOpts.loading || productAvailabilityOpts.loading} errors={productCreateOpts.data?.productCreate.errors || []} fetchCategories={searchCategory} fetchCollections={searchCollection} diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index d3915ceab..a5f9c4e57 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -10,6 +10,7 @@ import useBulkActions from "@saleor/hooks/useBulkActions"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { commonMessages } from "@saleor/intl"; import { useProductDeleteMutation, @@ -177,6 +178,12 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onCompleted: data => { const errors = data?.productSetAvailabilityForPurchase?.errors; if (errors?.length === 0) { + const updatedProduct = data?.productSetAvailabilityForPurchase?.product; + setProduct(product => ({ + ...product, + availableForPurchase: updatedProduct.availableForPurchase, + isAvailableForPurchase: updatedProduct.isAvailableForPurchase + })); notify({ status: "success", text: intl.formatMessage({ @@ -195,7 +202,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { const handleBack = () => navigate(productListUrl()); - const product = data?.product; + const [product, setProduct] = useStateFromProps(data?.product); if (product === null) { return ; diff --git a/src/products/views/ProductUpdate/handlers.ts b/src/products/views/ProductUpdate/handlers.ts index f1ac03ad9..1d1cdd56f 100644 --- a/src/products/views/ProductUpdate/handlers.ts +++ b/src/products/views/ProductUpdate/handlers.ts @@ -19,6 +19,7 @@ import { SimpleProductUpdateVariables } from "@saleor/products/types/SimpleProductUpdate"; import { mapFormsetStockToStockInput } from "@saleor/products/utils/data"; +import { getProductAvailabilityVariables } from "@saleor/products/utils/handlers"; import { ReorderEvent } from "@saleor/types"; import { MutationFetchResult } from "react-apollo"; import { arrayMove } from "react-sortable-hoc"; @@ -91,20 +92,13 @@ export function createUpdateHandler( isAvailableForPurchase !== product.isAvailableForPurchase || availableForPurchase !== product.availableForPurchase ) { - const isAvailable = - availableForPurchase && !isAvailableForPurchase - ? true - : isAvailableForPurchase; - - const availabilityResult = await setProductAvailability({ - isAvailable, - productId: product.id, - startDate: isAvailableForPurchase - ? null - : availableForPurchase !== "" - ? availableForPurchase - : null + const variables = getProductAvailabilityVariables({ + availableForPurchase, + isAvailableForPurchase, + productId: product.id }); + + const availabilityResult = await setProductAvailability(variables); errors = [ ...errors, ...availabilityResult.data.productSetAvailabilityForPurchase.errors From 45d33c392081dc418ef3cc7d2b67d8fda522c458 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Fri, 18 Sep 2020 15:08:50 +0200 Subject: [PATCH 2/9] Fix return to previous page on screen size change (#710) * Fix header responsiveness * Update changelog --- CHANGELOG.md | 1 + src/components/AppHeader/AppHeader.tsx | 2 +- src/components/AppLayout/AppLayout.tsx | 86 +++++++++++++++----------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4d457413..7b14159dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ All notable, unreleased changes to this project will be documented in this file. - Update schema with PositiveDecimal type - #695 by @AlicjaSzu - Restyle side menu - #697 by @dominik-zeglen - Add error info when fetching taxes - #701 by @dominik-zeglen +- Fix return to previous page on screen size change - #710 by @orzechdev ## 2.10.1 diff --git a/src/components/AppHeader/AppHeader.tsx b/src/components/AppHeader/AppHeader.tsx index 7f85264dd..d38731269 100644 --- a/src/components/AppHeader/AppHeader.tsx +++ b/src/components/AppHeader/AppHeader.tsx @@ -34,7 +34,7 @@ const useStyles = makeStyles( marginTop: theme.spacing(0.5), transition: theme.transitions.duration.standard + "ms", [theme.breakpoints.down("sm")]: { - margin: theme.spacing(4, 0, 3, 0) + margin: theme.spacing(4, 0, 0, 0) } }, skeleton: { diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index 891ef3f09..703a33a2c 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -56,12 +56,24 @@ const useStyles = makeStyles( marginRight: theme.spacing(2) }, header: { + display: "grid", + gridTemplateAreas: `"headerAnchor headerToolbar"`, + [theme.breakpoints.down("sm")]: { + gridTemplateAreas: `"headerToolbar" + "headerAnchor"` + }, + marginBottom: theme.spacing(3) + }, + headerAnchor: { + gridArea: "headerAnchor" + }, + headerToolbar: { + display: "flex", + gridArea: "headerToolbar", + height: 40, [theme.breakpoints.down("sm")]: { height: "auto" - }, - display: "flex", - height: 40, - marginBottom: theme.spacing(3) + } }, root: { @@ -162,39 +174,43 @@ const AppLayout: React.FC = ({ children }) => {
- {isMdUp &&
} - {!isMdUp && ( - - )} -
-
- - setNavigatorVisibility(true)} - /> - - navigate(staffMemberDetailsUrl(user.id)) - } - user={user} - /> +
+
+ {!isMdUp && ( + + )} +
+
+ + setNavigatorVisibility(true)} + /> + + navigate(staffMemberDetailsUrl(user.id)) + } + user={user} + /> +
- {!isMdUp &&
}
From f7936fa3fc2ad5763699ab55876d338226ae14f9 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Fri, 18 Sep 2020 16:30:50 +0200 Subject: [PATCH 3/9] Fix avatar change button --- assets/images/photo-icon.svg | 8 +---- .../StaffProperties/StaffProperties.tsx | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/assets/images/photo-icon.svg b/assets/images/photo-icon.svg index 57ee59835..d9662bedc 100644 --- a/assets/images/photo-icon.svg +++ b/assets/images/photo-icon.svg @@ -1,9 +1,3 @@ - - - - - - - + diff --git a/src/staff/components/StaffProperties/StaffProperties.tsx b/src/staff/components/StaffProperties/StaffProperties.tsx index 1a2b10f63..3cdc2f628 100644 --- a/src/staff/components/StaffProperties/StaffProperties.tsx +++ b/src/staff/components/StaffProperties/StaffProperties.tsx @@ -34,6 +34,14 @@ const useStyles = makeStyles( position: "relative", width: 120 }, + avatarActionText: { + "&:hover": { + textDecoration: "underline" + }, + color: "#fff", + cursor: "pointer", + fontSize: 12 + }, avatarDefault: { "& div": { color: "#fff", @@ -47,17 +55,10 @@ const useStyles = makeStyles( width: 120 }, avatarHover: { - "& p": { - "&:hover": { - textDecoration: "underline" - }, - color: theme.palette.primary.main, - cursor: "pointer", - fontSize: 12, - fontWeight: 500 - }, background: "#00000080", borderRadius: "100%", + fontSize: 12, + fontWeight: 500, height: 120, opacity: 0, padding: theme.spacing(2.5, 0), @@ -155,13 +156,19 @@ const StaffProperties: React.FC = props => { {canEditAvatar && (
- + - + Date: Fri, 18 Sep 2020 16:35:30 +0200 Subject: [PATCH 4/9] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b14159dc..7632161ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ All notable, unreleased changes to this project will be documented in this file. - Restyle side menu - #697 by @dominik-zeglen - Add error info when fetching taxes - #701 by @dominik-zeglen - Fix return to previous page on screen size change - #710 by @orzechdev +- Fix avatar change button - #719 by @orzechdev ## 2.10.1 From e474f90a8efe3678b5af1ca709208361f00a9f6a Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Fri, 18 Sep 2020 16:56:22 +0200 Subject: [PATCH 5/9] Update test snapshots --- src/storybook/__snapshots__/Stories.test.ts.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index af14341ef..9ad778532 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -178586,12 +178586,12 @@ exports[`Storyshots Views / Staff / Staff member details himself 1`] = ` class="isvg pending" />
Change photo
Delete photo
From 88b8950408733eb94ef93964912dbfe999377ce0 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Thu, 17 Sep 2020 13:31:09 +0200 Subject: [PATCH 6/9] Add drag-and-drop to allow variants reordering --- src/components/ImageUpload/ImageUpload.tsx | 38 ++++++------ .../ProductImages/ProductImages.tsx | 5 +- .../ProductUpdatePage/ProductUpdatePage.tsx | 5 +- .../ProductVariantCreatePage.tsx | 6 +- .../ProductVariantNavigation.tsx | 58 ++++++++++++------- .../ProductVariantPage/ProductVariantPage.tsx | 6 +- .../ProductVariants/ProductVariants.tsx | 24 +++++--- .../views/ProductUpdate/ProductUpdate.tsx | 1 + src/products/views/ProductVariant.tsx | 1 + src/products/views/ProductVariantCreate.tsx | 1 + .../stories/products/ProductUpdatePage.tsx | 1 + .../products/ProductVariantCreatePage.tsx | 4 ++ .../stories/products/ProductVariantPage.tsx | 3 + 13 files changed, 98 insertions(+), 55 deletions(-) diff --git a/src/components/ImageUpload/ImageUpload.tsx b/src/components/ImageUpload/ImageUpload.tsx index f0b1d18a2..65530d2fe 100644 --- a/src/components/ImageUpload/ImageUpload.tsx +++ b/src/components/ImageUpload/ImageUpload.tsx @@ -15,6 +15,7 @@ interface ImageUploadProps { isActiveClassName?: string; iconContainerClassName?: string; iconContainerActiveClassName?: string; + hideUploadIcon?: boolean; onImageUpload: (file: FileList) => void; } @@ -66,6 +67,7 @@ export const ImageUpload: React.FC = props => { iconContainerActiveClassName, iconContainerClassName, isActiveClassName, + hideUploadIcon, onImageUpload } = props; @@ -82,24 +84,26 @@ export const ImageUpload: React.FC = props => { [isActiveClassName]: isDragActive })} > -
- - - - + - -
+ + + + +
+ )}
{children && children({ isDragActive })} diff --git a/src/products/components/ProductImages/ProductImages.tsx b/src/products/components/ProductImages/ProductImages.tsx index bc3c750ca..2890342cd 100644 --- a/src/products/components/ProductImages/ProductImages.tsx +++ b/src/products/components/ProductImages/ProductImages.tsx @@ -83,9 +83,6 @@ const useStyles = makeStyles( imageUploadActive: { zIndex: 1 }, - imageUploadIcon: { - display: "none" - }, imageUploadIconActive: { display: "block" }, @@ -253,7 +250,7 @@ const ProductImages: React.FC = props => { className={classes.imageUpload} isActiveClassName={classes.imageUploadActive} disableClick={true} - iconContainerClassName={classes.imageUploadIcon} + hideUploadIcon={true} iconContainerActiveClassName={classes.imageUploadIconActive} onImageUpload={handleImageUpload} > diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index d3be9d79e..ad8f27270 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -18,7 +18,7 @@ import { sectionNames } from "@saleor/intl"; import { maybe } from "@saleor/misc"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; -import { FetchMoreProps, ListActions } from "@saleor/types"; +import { FetchMoreProps, ListActions, ReorderAction } from "@saleor/types"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; @@ -73,6 +73,7 @@ export interface ProductUpdatePageProps extends ListActions { fetchCollections: (query: string) => void; onVariantsAdd: () => void; onVariantShow: (id: string) => () => void; + onVariantReorder: ReorderAction; onImageDelete: (id: string) => () => void; onBack?(); onDelete(); @@ -120,6 +121,7 @@ export const ProductUpdatePage: React.FC = ({ onVariantAdd, onVariantsAdd, onVariantShow, + onVariantReorder, isChecked, selected, toggle, @@ -302,6 +304,7 @@ export const ProductUpdatePage: React.FC = ({ onRowClick={onVariantShow} onVariantAdd={onVariantAdd} onVariantsAdd={onVariantsAdd} + onVariantReorder={onVariantReorder} toolbar={toolbar} isChecked={isChecked} selected={selected} diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index ac79fad42..1237d8b98 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -14,6 +14,7 @@ import useFormset, { } from "@saleor/hooks/useFormset"; import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data"; import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses"; +import { ReorderAction } from "@saleor/types"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; import { useIntl } from "react-intl"; @@ -56,6 +57,7 @@ interface ProductVariantCreatePageProps { onBack: () => void; onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onVariantClick: (variantId: string) => void; + onVariantReorder: ReorderAction; } const ProductVariantCreatePage: React.FC = ({ @@ -69,7 +71,8 @@ const ProductVariantCreatePage: React.FC = ({ weightUnit, onBack, onSubmit, - onVariantClick + onVariantClick, + onVariantReorder }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -131,6 +134,7 @@ const ProductVariantCreatePage: React.FC = ({ return onVariantClick(variantId); } }} + onReorder={onVariantReorder} />
diff --git a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx index 89f0e7d55..249f03af6 100644 --- a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx +++ b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx @@ -1,13 +1,17 @@ import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; 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 CardTitle from "@saleor/components/CardTitle"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import Skeleton from "@saleor/components/Skeleton"; +import { + SortableTableBody, + SortableTableRow +} from "@saleor/components/SortableTable"; import TableCellAvatar from "@saleor/components/TableCellAvatar"; +import { ReorderAction } from "@saleor/types"; import classNames from "classnames"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -26,16 +30,18 @@ const useStyles = makeStyles( cursor: "pointer" }, tabActive: { - "&:before": { - background: theme.palette.primary.main, - content: '""', - height: "100%", - left: 0, - position: "absolute", - top: 0, - width: 2 - }, - position: "relative" + "& > td:first-child": { + "&:before": { + background: theme.palette.primary.main, + content: '""', + height: "100%", + left: 0, + position: "absolute", + top: 0, + width: 2 + }, + position: "relative" + } } }), { name: "ProductVariantNavigation" } @@ -49,10 +55,18 @@ interface ProductVariantNavigationProps { | ProductVariantCreateData_product_variants[]; onAdd?: () => void; onRowClick: (variantId: string) => void; + onReorder: ReorderAction; } const ProductVariantNavigation: React.FC = props => { - const { current, fallbackThumbnail, variants, onAdd, onRowClick } = props; + const { + current, + fallbackThumbnail, + variants, + onAdd, + onRowClick, + onReorder + } = props; const classes = useStyles(props); const intl = useIntl(); @@ -66,18 +80,18 @@ const ProductVariantNavigation: React.FC = props })} /> - - {renderCollection(variants, variant => ( - + {renderCollection(variants, (variant, variantIndex) => ( + onRowClick(variant.id) : undefined} > variant.images[0].url, fallbackThumbnail @@ -86,11 +100,11 @@ const ProductVariantNavigation: React.FC = props {variant ? variant.name || variant.sku : } - + ))} {onAdd ? ( - +
diff --git a/src/products/components/ProductVariants/ProductVariants.tsx b/src/products/components/ProductVariants/ProductVariants.tsx index 86ee81aa4..90c798629 100644 --- a/src/products/components/ProductVariants/ProductVariants.tsx +++ b/src/products/components/ProductVariants/ProductVariants.tsx @@ -3,9 +3,7 @@ import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import Hidden from "@material-ui/core/Hidden"; 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 Typography from "@material-ui/core/Typography"; import CardTitle from "@saleor/components/CardTitle"; import Checkbox from "@saleor/components/Checkbox"; @@ -14,13 +12,17 @@ import Money from "@saleor/components/Money"; import ResponsiveTable from "@saleor/components/ResponsiveTable"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import Skeleton from "@saleor/components/Skeleton"; +import { + SortableTableBody, + SortableTableRow +} from "@saleor/components/SortableTable"; import TableHead from "@saleor/components/TableHead"; import { ProductVariant_costPrice } from "@saleor/fragments/types/ProductVariant"; import React from "react"; import { FormattedMessage, IntlShape, useIntl } from "react-intl"; import { maybe, renderCollection } from "../../../misc"; -import { ListActions } from "../../../types"; +import { ListActions, ReorderAction } from "../../../types"; import { ProductDetails_product_variants, ProductDetails_product_variants_stocks_warehouse @@ -171,12 +173,13 @@ interface ProductVariantsProps extends ListActions { disabled: boolean; variants: ProductDetails_product_variants[]; fallbackPrice?: ProductVariant_costPrice; + onVariantReorder: ReorderAction; onRowClick: (id: string) => () => void; onVariantAdd?(); onVariantsAdd?(); } -const numberOfColumns = 5; +const numberOfColumns = 6; export const ProductVariants: React.FC = props => { const { @@ -186,6 +189,7 @@ export const ProductVariants: React.FC = props => { onRowClick, onVariantAdd, onVariantsAdd, + onVariantReorder, isChecked, selected, toggle, @@ -266,6 +270,7 @@ export const ProductVariants: React.FC = props => { items={variants} toggleAll={toggleAll} toolbar={toolbar} + dragRows > = props => { /> - - {renderCollection(variants, variant => { + + {renderCollection(variants, (variant, variantIndex) => { const isSelected = variant ? isChecked(variant.id) : false; const numAvailable = variant && variant.stocks @@ -303,11 +308,12 @@ export const ProductVariants: React.FC = props => { : null; return ( - @@ -354,10 +360,10 @@ export const ProductVariants: React.FC = props => { ) )} - + ); })} - + )} diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index a5f9c4e57..33b12df25 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -289,6 +289,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => navigate(productVariantEditUrl(product.id, variantId))} + onVariantReorder={() => undefined} // TODO: ... onImageUpload={handleImageUpload} onImageEdit={handleImageEdit} onImageDelete={handleImageDelete} diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index bce7b327b..36fac92c9 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -202,6 +202,7 @@ export const ProductVariant: React.FC = ({ onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} + onVariantReorder={() => undefined} // TODO: ... /> = ({ onBack={handleBack} onSubmit={handleSubmit} onVariantClick={handleVariantClick} + onVariantReorder={() => undefined} // TODO: ... saveButtonBarState={variantCreateResult.status} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index 7c00bcf01..7682f8369 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -34,6 +34,7 @@ const props: ProductUpdatePageProps = { onImageUpload: () => undefined, onSubmit: () => undefined, onVariantAdd: () => undefined, + onVariantReorder: () => undefined, onVariantShow: () => undefined, onVariantsAdd: () => undefined, placeholderImage, diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index fee28f4a7..d8578724d 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -23,6 +23,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -54,6 +55,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -69,6 +71,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -87,6 +90,7 @@ storiesOf("Views / Products / Create product variant", module) onBack={() => undefined} onSubmit={() => undefined} onVariantClick={undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index f4d50e4ca..cca477a73 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -24,6 +24,7 @@ storiesOf("Views / Products / Product variant details", module) onImageSelect={() => undefined} onSubmit={() => undefined} onVariantClick={() => undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -41,6 +42,7 @@ storiesOf("Views / Products / Product variant details", module) onImageSelect={() => undefined} onSubmit={() => undefined} onVariantClick={() => undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} /> @@ -56,6 +58,7 @@ storiesOf("Views / Products / Product variant details", module) onImageSelect={() => undefined} onSubmit={() => undefined} onVariantClick={() => undefined} + onVariantReorder={() => undefined} saveButtonBarState="default" errors={[ { From ffaf56a51b7cb27ab431ce69f29418e55213388e Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Thu, 17 Sep 2020 16:37:33 +0200 Subject: [PATCH 7/9] Handle variants reordering --- locale/defaultMessages.json | 4 + schema.graphql | 10 + .../ProductVariantNavigation.tsx | 2 +- src/products/mutations.ts | 23 ++ src/products/types/ProductVariantReorder.ts | 236 ++++++++++++++++++ .../views/ProductUpdate/ProductUpdate.tsx | 16 +- src/products/views/ProductUpdate/handlers.ts | 21 ++ src/products/views/ProductVariant.tsx | 17 +- src/products/views/ProductVariantCreate.tsx | 22 +- src/types/globalTypes.ts | 2 + .../WebhookEvents/WebhookEvents.tsx | 4 + 11 files changed, 349 insertions(+), 8 deletions(-) create mode 100644 src/products/types/ProductVariantReorder.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 105c9bf12..5c00097a1 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -5897,6 +5897,10 @@ "context": "webhook events", "string": "Expand or restrict webhooks permissions to register certain events in Saleor system." }, + "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3316426878": { + "context": "event", + "string": "Product updated" + }, "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3345061702": { "context": "event", "string": "Order fully paid" diff --git a/schema.graphql b/schema.graphql index e2953571b..abe05671b 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2640,6 +2640,7 @@ type Mutation { productClearPrivateMetadata(id: ID!, input: MetaPath!): ProductClearPrivateMeta @deprecated(reason: "Use the `deletePrivateMetadata` mutation instead. This field will be removed after 2020-07-31.") productSetAvailabilityForPurchase(isAvailable: Boolean!, productId: ID!, startDate: Date): ProductSetAvailabilityForPurchase productImageCreate(input: ProductImageCreateInput!): ProductImageCreate + productVariantReorder(moves: [ReorderInput]!, productId: ID!): ProductVariantReorder productImageDelete(id: ID!): ProductImageDelete productImageBulkDelete(ids: [ID]!): ProductImageBulkDelete productImageReorder(imagesIds: [ID]!, productId: ID!): ProductImageReorder @@ -2999,6 +3000,7 @@ enum OrderErrorCode { REQUIRED SHIPPING_METHOD_NOT_APPLICABLE SHIPPING_METHOD_REQUIRED + TAX_ERROR UNIQUE VOID_INACTIVE_PAYMENT ZERO_QUANTITY @@ -4174,6 +4176,12 @@ input ProductVariantInput { weight: WeightScalar } +type ProductVariantReorder { + errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") + product: Product + productErrors: [ProductError!]! +} + type ProductVariantStocksCreate { errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.") productVariant: ProductVariant @@ -5600,6 +5608,7 @@ enum WebhookEventTypeEnum { INVOICE_SENT CUSTOMER_CREATED PRODUCT_CREATED + PRODUCT_UPDATED CHECKOUT_QUANTITY_CHANGED CHECKOUT_CREATED CHECKOUT_UPDATED @@ -5622,6 +5631,7 @@ enum WebhookSampleEventTypeEnum { INVOICE_SENT CUSTOMER_CREATED PRODUCT_CREATED + PRODUCT_UPDATED CHECKOUT_QUANTITY_CHANGED CHECKOUT_CREATED CHECKOUT_UPDATED diff --git a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx index 249f03af6..39ac8e6b2 100644 --- a/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx +++ b/src/products/components/ProductVariantNavigation/ProductVariantNavigation.tsx @@ -116,7 +116,7 @@ const ProductVariantNavigation: React.FC = props ) : ( - + (productSetAvailabilityForPurchase); + +const productVariantReorder = gql` + ${productErrorFragment} + ${productFragmentDetails} + mutation ProductVariantReorder($move: ReorderInput!, $productId: ID!) { + productVariantReorder(moves: [$move], productId: $productId) { + errors: productErrors { + ...ProductErrorFragment + } + product { + ...Product + } + } + } +`; +export const useProductVariantReorderMutation = makeMutation< + ProductVariantReorder, + ProductVariantReorderVariables +>(productVariantReorder); diff --git a/src/products/types/ProductVariantReorder.ts b/src/products/types/ProductVariantReorder.ts new file mode 100644 index 000000000..8c5f1741f --- /dev/null +++ b/src/products/types/ProductVariantReorder.ts @@ -0,0 +1,236 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ReorderInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ProductVariantReorder +// ==================================================== + +export interface ProductVariantReorder_productVariantReorder_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_attributes_attribute { + __typename: "Attribute"; + id: string; + slug: string | null; + name: string | null; + inputType: AttributeInputTypeEnum | null; + valueRequired: boolean; + values: (ProductVariantReorder_productVariantReorder_product_attributes_attribute_values | null)[] | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_attributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_attributes { + __typename: "SelectedAttribute"; + attribute: ProductVariantReorder_productVariantReorder_product_attributes_attribute; + values: (ProductVariantReorder_productVariantReorder_product_attributes_values | null)[]; +} + +export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values { + __typename: "AttributeValue"; + id: string; + name: string | null; + slug: string | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_productType_variantAttributes { + __typename: "Attribute"; + id: string; + name: string | null; + values: (ProductVariantReorder_productVariantReorder_product_productType_variantAttributes_values | null)[] | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_productType { + __typename: "ProductType"; + id: string; + variantAttributes: (ProductVariantReorder_productVariantReorder_product_productType_variantAttributes | null)[] | null; + name: string; + hasVariants: boolean; +} + +export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start { + __typename: "TaxedMoney"; + gross: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start_gross; +} + +export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop_gross { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop { + __typename: "TaxedMoney"; + gross: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop_gross; +} + +export interface ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted { + __typename: "TaxedMoneyRange"; + start: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_start | null; + stop: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted_stop | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_pricing { + __typename: "ProductPricingInfo"; + priceRangeUndiscounted: ProductVariantReorder_productVariantReorder_product_pricing_priceRangeUndiscounted | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_metadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_privateMetadata { + __typename: "MetadataItem"; + key: string; + value: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_category { + __typename: "Category"; + id: string; + name: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_collections { + __typename: "Collection"; + id: string; + name: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_margin { + __typename: "Margin"; + start: number | null; + stop: number | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_purchaseCost_start { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_purchaseCost_stop { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_purchaseCost { + __typename: "MoneyRange"; + start: ProductVariantReorder_productVariantReorder_product_purchaseCost_start | null; + stop: ProductVariantReorder_productVariantReorder_product_purchaseCost_stop | null; +} + +export interface ProductVariantReorder_productVariantReorder_product_images { + __typename: "ProductImage"; + id: string; + alt: string; + sortOrder: number | null; + url: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_variants_price { + __typename: "Money"; + amount: number; + currency: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_variants_stocks_warehouse { + __typename: "Warehouse"; + id: string; + name: string; +} + +export interface ProductVariantReorder_productVariantReorder_product_variants_stocks { + __typename: "Stock"; + id: string; + quantity: number; + quantityAllocated: number; + warehouse: ProductVariantReorder_productVariantReorder_product_variants_stocks_warehouse; +} + +export interface ProductVariantReorder_productVariantReorder_product_variants { + __typename: "ProductVariant"; + id: string; + sku: string; + name: string; + price: ProductVariantReorder_productVariantReorder_product_variants_price | null; + margin: number | null; + stocks: (ProductVariantReorder_productVariantReorder_product_variants_stocks | null)[] | null; + trackInventory: boolean; +} + +export interface ProductVariantReorder_productVariantReorder_product_weight { + __typename: "Weight"; + unit: WeightUnitsEnum; + value: number; +} + +export interface ProductVariantReorder_productVariantReorder_product { + __typename: "Product"; + id: string; + attributes: ProductVariantReorder_productVariantReorder_product_attributes[]; + productType: ProductVariantReorder_productVariantReorder_product_productType; + pricing: ProductVariantReorder_productVariantReorder_product_pricing | null; + metadata: (ProductVariantReorder_productVariantReorder_product_metadata | null)[]; + privateMetadata: (ProductVariantReorder_productVariantReorder_product_privateMetadata | null)[]; + name: string; + descriptionJson: any; + seoTitle: string | null; + seoDescription: string | null; + category: ProductVariantReorder_productVariantReorder_product_category | null; + collections: (ProductVariantReorder_productVariantReorder_product_collections | null)[] | null; + margin: ProductVariantReorder_productVariantReorder_product_margin | null; + purchaseCost: ProductVariantReorder_productVariantReorder_product_purchaseCost | null; + isAvailableForPurchase: boolean | null; + isAvailable: boolean | null; + isPublished: boolean; + chargeTaxes: boolean; + publicationDate: any | null; + images: (ProductVariantReorder_productVariantReorder_product_images | null)[] | null; + variants: (ProductVariantReorder_productVariantReorder_product_variants | null)[] | null; + weight: ProductVariantReorder_productVariantReorder_product_weight | null; + availableForPurchase: any | null; + visibleInListings: boolean; +} + +export interface ProductVariantReorder_productVariantReorder { + __typename: "ProductVariantReorder"; + errors: ProductVariantReorder_productVariantReorder_errors[]; + product: ProductVariantReorder_productVariantReorder_product | null; +} + +export interface ProductVariantReorder { + productVariantReorder: ProductVariantReorder_productVariantReorder | null; +} + +export interface ProductVariantReorderVariables { + move: ReorderInput; + productId: string; +} diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 33b12df25..8aeef201c 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -20,6 +20,7 @@ import { useProductSetAvailabilityForPurchase, useProductUpdateMutation, useProductVariantBulkDeleteMutation, + useProductVariantReorderMutation, useSimpleProductUpdateMutation } from "@saleor/products/mutations"; import useCategorySearch from "@saleor/searches/useCategorySearch"; @@ -52,7 +53,8 @@ import { import { createImageReorderHandler, createImageUploadHandler, - createUpdateHandler + createUpdateHandler, + createVariantReorderHandler } from "./handlers"; interface ProductUpdateProps { @@ -232,12 +234,22 @@ export const ProductUpdate: React.FC = ({ id, params }) => { reorderProductImages({ variables }) ); + const [ + reorderProductVariants, + reorderProductVariantsOpts + ] = useProductVariantReorderMutation({}); + + const handleVariantReorder = createVariantReorderHandler(product, variables => + reorderProductVariants({ variables }) + ); + const disableFormSave = createProductImageOpts.loading || deleteProductOpts.loading || reorderProductImagesOpts.loading || updateProductOpts.loading || productAvailabilityOpts.loading || + reorderProductVariantsOpts.loading || loading; const formTransitionState = getMutationState( @@ -289,7 +301,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => navigate(productVariantEditUrl(product.id, variantId))} - onVariantReorder={() => undefined} // TODO: ... + onVariantReorder={handleVariantReorder} onImageUpload={handleImageUpload} onImageEdit={handleImageEdit} onImageDelete={handleImageDelete} diff --git a/src/products/views/ProductUpdate/handlers.ts b/src/products/views/ProductUpdate/handlers.ts index 1d1cdd56f..6e3de1180 100644 --- a/src/products/views/ProductUpdate/handlers.ts +++ b/src/products/views/ProductUpdate/handlers.ts @@ -14,6 +14,9 @@ import { ProductUpdate, ProductUpdateVariables } from "@saleor/products/types/ProductUpdate"; +import { ProductVariantCreateData_product } from "@saleor/products/types/ProductVariantCreateData"; +import { ProductVariantDetails_productVariant_product } from "@saleor/products/types/ProductVariantDetails"; +import { ProductVariantReorderVariables } from "@saleor/products/types/ProductVariantReorder"; import { SimpleProductUpdate, SimpleProductUpdateVariables @@ -134,3 +137,21 @@ export function createImageReorderHandler( }); }; } + +export function createVariantReorderHandler( + product: + | ProductDetails_product + | ProductVariantDetails_productVariant_product + | ProductVariantCreateData_product, + reorderProductVariants: (variables: ProductVariantReorderVariables) => void +) { + return ({ newIndex, oldIndex }: ReorderEvent) => { + reorderProductVariants({ + move: { + id: product.variants[oldIndex].id, + sortOrder: newIndex - oldIndex + }, + productId: product.id + }); + }; +} diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index 36fac92c9..d090a2f70 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -21,6 +21,7 @@ import ProductVariantPage, { ProductVariantPageSubmitData } from "../components/ProductVariantPage"; import { + useProductVariantReorderMutation, useVariantDeleteMutation, useVariantImageAssignMutation, useVariantImageUnassignMutation, @@ -36,6 +37,7 @@ import { ProductVariantEditUrlQueryParams } from "../urls"; import { mapFormsetStockToStockInput } from "../utils/data"; +import { createVariantReorderHandler } from "./ProductUpdate/handlers"; interface ProductUpdateProps { variantId: string; @@ -120,12 +122,23 @@ export const ProductVariant: React.FC = ({ return ; } + const [ + reorderProductVariants, + reorderProductVariantsOpts + ] = useProductVariantReorderMutation({}); + + const handleVariantReorder = createVariantReorderHandler( + variant?.product, + variables => reorderProductVariants({ variables }) + ); + const disableFormSave = loading || deleteVariantOpts.loading || updateVariantOpts.loading || assignImageOpts.loading || - unassignImageOpts.loading; + unassignImageOpts.loading || + reorderProductVariantsOpts.loading; const handleImageSelect = (id: string) => () => { if (variant) { @@ -202,7 +215,7 @@ export const ProductVariant: React.FC = ({ onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} - onVariantReorder={() => undefined} // TODO: ... + onVariantReorder={handleVariantReorder} /> = ({ return navigate(productListUrl())} />; } + const [ + reorderProductVariants, + reorderProductVariantsOpts + ] = useProductVariantReorderMutation({}); + + const handleVariantReorder = createVariantReorderHandler(product, variables => + reorderProductVariants({ variables }) + ); + const handleBack = () => navigate(productUrl(productId)); const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => { const result = await variantCreate({ @@ -104,7 +117,10 @@ export const ProductVariant: React.FC = ({ const handleVariantClick = (id: string) => navigate(productVariantEditUrl(productId, id)); - const disableForm = productLoading || variantCreateResult.loading; + const disableForm = + productLoading || + variantCreateResult.loading || + reorderProductVariantsOpts.loading; return ( <> @@ -126,7 +142,7 @@ export const ProductVariant: React.FC = ({ onBack={handleBack} onSubmit={handleSubmit} onVariantClick={handleVariantClick} - onVariantReorder={() => undefined} // TODO: ... + onVariantReorder={handleVariantReorder} saveButtonBarState={variantCreateResult.status} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 7430a65da..5d626b4df 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -548,6 +548,7 @@ export enum OrderErrorCode { REQUIRED = "REQUIRED", SHIPPING_METHOD_NOT_APPLICABLE = "SHIPPING_METHOD_NOT_APPLICABLE", SHIPPING_METHOD_REQUIRED = "SHIPPING_METHOD_REQUIRED", + TAX_ERROR = "TAX_ERROR", UNIQUE = "UNIQUE", VOID_INACTIVE_PAYMENT = "VOID_INACTIVE_PAYMENT", ZERO_QUANTITY = "ZERO_QUANTITY", @@ -905,6 +906,7 @@ export enum WebhookEventTypeEnum { ORDER_FULLY_PAID = "ORDER_FULLY_PAID", ORDER_UPDATED = "ORDER_UPDATED", PRODUCT_CREATED = "PRODUCT_CREATED", + PRODUCT_UPDATED = "PRODUCT_UPDATED", } export enum WebhookSortField { diff --git a/src/webhooks/components/WebhookEvents/WebhookEvents.tsx b/src/webhooks/components/WebhookEvents/WebhookEvents.tsx index 7bae16ebb..8578fa752 100644 --- a/src/webhooks/components/WebhookEvents/WebhookEvents.tsx +++ b/src/webhooks/components/WebhookEvents/WebhookEvents.tsx @@ -68,6 +68,10 @@ const WebhookEvents: React.FC = ({ defaultMessage: "Product created", description: "event" }), + [WebhookEventTypeEnum.PRODUCT_UPDATED]: intl.formatMessage({ + defaultMessage: "Product updated", + description: "event" + }), [WebhookEventTypeEnum.CHECKOUT_QUANTITY_CHANGED]: intl.formatMessage({ defaultMessage: "Changed quantity in checkout", description: "event" From b39183da9327563b78632aff4cc343ec890dc8d1 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Thu, 17 Sep 2020 16:44:07 +0200 Subject: [PATCH 8/9] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b14159dc..5416f8bed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ All notable, unreleased changes to this project will be documented in this file. - Restyle side menu - #697 by @dominik-zeglen - Add error info when fetching taxes - #701 by @dominik-zeglen - Fix return to previous page on screen size change - #710 by @orzechdev +- Add variants reordering possibility - #716 by @orzechdev ## 2.10.1 From 8d9162cd1d2b03b91d5fa976069881860d28d227 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Thu, 17 Sep 2020 17:04:47 +0200 Subject: [PATCH 9/9] Update test snapshots --- .../__snapshots__/Stories.test.ts.snap | 1460 +++++++++++++---- 1 file changed, 1149 insertions(+), 311 deletions(-) diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index af14341ef..2dee6d7ea 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -15259,6 +15259,48 @@ exports[`Storyshots Views / Apps / Webhooks / Create webhook default 1`] = `
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
New Variant @@ -140878,6 +141177,43 @@ exports[`Storyshots Views / Products / Create product variant default 1`] = ` + + + @@ -140906,6 +141242,43 @@ exports[`Storyshots Views / Products / Create product variant default 1`] = ` + + + @@ -140981,6 +141354,7 @@ exports[`Storyshots Views / Products / Create product variant default 1`] = ` New Variant @@ -141698,6 +142072,43 @@ exports[`Storyshots Views / Products / Create product variant when loading data + + + @@ -141784,6 +142195,7 @@ exports[`Storyshots Views / Products / Create product variant when loading data New Variant @@ -142446,6 +142858,43 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] = + + + @@ -142474,6 +142923,43 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] = + + + @@ -142549,6 +143035,7 @@ exports[`Storyshots Views / Products / Create product variant with errors 1`] = New Variant @@ -143899,56 +144386,7 @@ Ctrl + K"
-
- - -
- Drop here to upload -
-
-
+ />
@@ -144390,7 +144828,11 @@ Ctrl + K" class="MuiTableRow-root-id MuiTableRow-head-id" > + + + + @@ -144506,6 +144985,43 @@ Ctrl + K" + + + @@ -146171,56 +146687,7 @@ Ctrl + K"
-
- - -
- Drop here to upload -
-
-
+ />
@@ -146403,7 +146870,11 @@ Ctrl + K" class="MuiTableRow-root-id MuiTableRow-head-id" > + + + + @@ -146519,6 +147027,43 @@ Ctrl + K" + + + @@ -148173,56 +148718,7 @@ Ctrl + K"
-
- - -
- Drop here to upload -
-
-
+ />
@@ -150593,56 +151089,7 @@ Ctrl + K"
-
- - -
- Drop here to upload -
-
-
+ />
@@ -153147,56 +153594,7 @@ Ctrl + K"
-
- - -
- Drop here to upload -
-
-
+ />
@@ -153638,7 +154036,11 @@ Ctrl + K" class="MuiTableRow-root-id MuiTableRow-head-id" > + + + + @@ -153754,6 +154193,43 @@ Ctrl + K" + + + @@ -157112,7 +157588,11 @@ Ctrl + K" class="MuiTableRow-root-id MuiTableRow-head-id" > + + + + @@ -157228,6 +157745,43 @@ Ctrl + K" + + + @@ -158882,56 +159436,7 @@ Ctrl + K"
-
- - -
- Drop here to upload -
-
-
+ />
@@ -167375,10 +167880,47 @@ exports[`Storyshots Views / Products / Product variant details attribute errors class="MuiTableBody-root-id" > + + +
+ + + @@ -167433,6 +168012,43 @@ exports[`Storyshots Views / Products / Product variant details attribute errors + + + @@ -167461,6 +168077,43 @@ exports[`Storyshots Views / Products / Product variant details attribute errors + + + @@ -167491,7 +168144,7 @@ exports[`Storyshots Views / Products / Product variant details attribute errors >