Merge pull request #716 from mirumee/feature/variants-reorder
Add variants reordering possibility
This commit is contained in:
commit
734c9c0dc6
22 changed files with 1594 additions and 371 deletions
|
@ -41,6 +41,7 @@ All notable, unreleased changes to this project will be documented in this file.
|
||||||
- Restyle side menu - #697 by @dominik-zeglen
|
- Restyle side menu - #697 by @dominik-zeglen
|
||||||
- Add error info when fetching taxes - #701 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 return to previous page on screen size change - #710 by @orzechdev
|
||||||
|
- Add variants reordering possibility - #716 by @orzechdev
|
||||||
|
|
||||||
## 2.10.1
|
## 2.10.1
|
||||||
|
|
||||||
|
|
|
@ -5897,6 +5897,10 @@
|
||||||
"context": "webhook events",
|
"context": "webhook events",
|
||||||
"string": "Expand or restrict webhooks permissions to register certain events in Saleor system."
|
"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": {
|
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_3345061702": {
|
||||||
"context": "event",
|
"context": "event",
|
||||||
"string": "Order fully paid"
|
"string": "Order fully paid"
|
||||||
|
|
|
@ -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.")
|
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
|
productSetAvailabilityForPurchase(isAvailable: Boolean!, productId: ID!, startDate: Date): ProductSetAvailabilityForPurchase
|
||||||
productImageCreate(input: ProductImageCreateInput!): ProductImageCreate
|
productImageCreate(input: ProductImageCreateInput!): ProductImageCreate
|
||||||
|
productVariantReorder(moves: [ReorderInput]!, productId: ID!): ProductVariantReorder
|
||||||
productImageDelete(id: ID!): ProductImageDelete
|
productImageDelete(id: ID!): ProductImageDelete
|
||||||
productImageBulkDelete(ids: [ID]!): ProductImageBulkDelete
|
productImageBulkDelete(ids: [ID]!): ProductImageBulkDelete
|
||||||
productImageReorder(imagesIds: [ID]!, productId: ID!): ProductImageReorder
|
productImageReorder(imagesIds: [ID]!, productId: ID!): ProductImageReorder
|
||||||
|
@ -2999,6 +3000,7 @@ enum OrderErrorCode {
|
||||||
REQUIRED
|
REQUIRED
|
||||||
SHIPPING_METHOD_NOT_APPLICABLE
|
SHIPPING_METHOD_NOT_APPLICABLE
|
||||||
SHIPPING_METHOD_REQUIRED
|
SHIPPING_METHOD_REQUIRED
|
||||||
|
TAX_ERROR
|
||||||
UNIQUE
|
UNIQUE
|
||||||
VOID_INACTIVE_PAYMENT
|
VOID_INACTIVE_PAYMENT
|
||||||
ZERO_QUANTITY
|
ZERO_QUANTITY
|
||||||
|
@ -4174,6 +4176,12 @@ input ProductVariantInput {
|
||||||
weight: WeightScalar
|
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 {
|
type ProductVariantStocksCreate {
|
||||||
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
|
||||||
productVariant: ProductVariant
|
productVariant: ProductVariant
|
||||||
|
@ -5600,6 +5608,7 @@ enum WebhookEventTypeEnum {
|
||||||
INVOICE_SENT
|
INVOICE_SENT
|
||||||
CUSTOMER_CREATED
|
CUSTOMER_CREATED
|
||||||
PRODUCT_CREATED
|
PRODUCT_CREATED
|
||||||
|
PRODUCT_UPDATED
|
||||||
CHECKOUT_QUANTITY_CHANGED
|
CHECKOUT_QUANTITY_CHANGED
|
||||||
CHECKOUT_CREATED
|
CHECKOUT_CREATED
|
||||||
CHECKOUT_UPDATED
|
CHECKOUT_UPDATED
|
||||||
|
@ -5622,6 +5631,7 @@ enum WebhookSampleEventTypeEnum {
|
||||||
INVOICE_SENT
|
INVOICE_SENT
|
||||||
CUSTOMER_CREATED
|
CUSTOMER_CREATED
|
||||||
PRODUCT_CREATED
|
PRODUCT_CREATED
|
||||||
|
PRODUCT_UPDATED
|
||||||
CHECKOUT_QUANTITY_CHANGED
|
CHECKOUT_QUANTITY_CHANGED
|
||||||
CHECKOUT_CREATED
|
CHECKOUT_CREATED
|
||||||
CHECKOUT_UPDATED
|
CHECKOUT_UPDATED
|
||||||
|
|
|
@ -15,6 +15,7 @@ interface ImageUploadProps {
|
||||||
isActiveClassName?: string;
|
isActiveClassName?: string;
|
||||||
iconContainerClassName?: string;
|
iconContainerClassName?: string;
|
||||||
iconContainerActiveClassName?: string;
|
iconContainerActiveClassName?: string;
|
||||||
|
hideUploadIcon?: boolean;
|
||||||
onImageUpload: (file: FileList) => void;
|
onImageUpload: (file: FileList) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ export const ImageUpload: React.FC<ImageUploadProps> = props => {
|
||||||
iconContainerActiveClassName,
|
iconContainerActiveClassName,
|
||||||
iconContainerClassName,
|
iconContainerClassName,
|
||||||
isActiveClassName,
|
isActiveClassName,
|
||||||
|
hideUploadIcon,
|
||||||
onImageUpload
|
onImageUpload
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -82,6 +84,7 @@ export const ImageUpload: React.FC<ImageUploadProps> = props => {
|
||||||
[isActiveClassName]: isDragActive
|
[isActiveClassName]: isDragActive
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
{!hideUploadIcon && (
|
||||||
<div
|
<div
|
||||||
className={classNames(iconContainerClassName, {
|
className={classNames(iconContainerClassName, {
|
||||||
[iconContainerActiveClassName]: isDragActive
|
[iconContainerActiveClassName]: isDragActive
|
||||||
|
@ -100,6 +103,7 @@ export const ImageUpload: React.FC<ImageUploadProps> = props => {
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{children && children({ isDragActive })}
|
{children && children({ isDragActive })}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -83,9 +83,6 @@ const useStyles = makeStyles(
|
||||||
imageUploadActive: {
|
imageUploadActive: {
|
||||||
zIndex: 1
|
zIndex: 1
|
||||||
},
|
},
|
||||||
imageUploadIcon: {
|
|
||||||
display: "none"
|
|
||||||
},
|
|
||||||
imageUploadIconActive: {
|
imageUploadIconActive: {
|
||||||
display: "block"
|
display: "block"
|
||||||
},
|
},
|
||||||
|
@ -253,7 +250,7 @@ const ProductImages: React.FC<ProductImagesProps> = props => {
|
||||||
className={classes.imageUpload}
|
className={classes.imageUpload}
|
||||||
isActiveClassName={classes.imageUploadActive}
|
isActiveClassName={classes.imageUploadActive}
|
||||||
disableClick={true}
|
disableClick={true}
|
||||||
iconContainerClassName={classes.imageUploadIcon}
|
hideUploadIcon={true}
|
||||||
iconContainerActiveClassName={classes.imageUploadIconActive}
|
iconContainerActiveClassName={classes.imageUploadIconActive}
|
||||||
onImageUpload={handleImageUpload}
|
onImageUpload={handleImageUpload}
|
||||||
>
|
>
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { sectionNames } from "@saleor/intl";
|
||||||
import { maybe } from "@saleor/misc";
|
import { maybe } from "@saleor/misc";
|
||||||
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
||||||
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
|
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 createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||||
|
@ -73,6 +73,7 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
fetchCollections: (query: string) => void;
|
fetchCollections: (query: string) => void;
|
||||||
onVariantsAdd: () => void;
|
onVariantsAdd: () => void;
|
||||||
onVariantShow: (id: string) => () => void;
|
onVariantShow: (id: string) => () => void;
|
||||||
|
onVariantReorder: ReorderAction;
|
||||||
onImageDelete: (id: string) => () => void;
|
onImageDelete: (id: string) => () => void;
|
||||||
onBack?();
|
onBack?();
|
||||||
onDelete();
|
onDelete();
|
||||||
|
@ -120,6 +121,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
onVariantAdd,
|
onVariantAdd,
|
||||||
onVariantsAdd,
|
onVariantsAdd,
|
||||||
onVariantShow,
|
onVariantShow,
|
||||||
|
onVariantReorder,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -302,6 +304,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
onRowClick={onVariantShow}
|
onRowClick={onVariantShow}
|
||||||
onVariantAdd={onVariantAdd}
|
onVariantAdd={onVariantAdd}
|
||||||
onVariantsAdd={onVariantsAdd}
|
onVariantsAdd={onVariantsAdd}
|
||||||
|
onVariantReorder={onVariantReorder}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
isChecked={isChecked}
|
isChecked={isChecked}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import useFormset, {
|
||||||
} from "@saleor/hooks/useFormset";
|
} from "@saleor/hooks/useFormset";
|
||||||
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
|
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
|
||||||
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
||||||
|
import { ReorderAction } from "@saleor/types";
|
||||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
@ -56,6 +57,7 @@ interface ProductVariantCreatePageProps {
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onSubmit: (data: ProductVariantCreatePageSubmitData) => void;
|
onSubmit: (data: ProductVariantCreatePageSubmitData) => void;
|
||||||
onVariantClick: (variantId: string) => void;
|
onVariantClick: (variantId: string) => void;
|
||||||
|
onVariantReorder: ReorderAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
|
@ -69,7 +71,8 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
weightUnit,
|
weightUnit,
|
||||||
onBack,
|
onBack,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onVariantClick
|
onVariantClick,
|
||||||
|
onVariantReorder
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const attributeInput = React.useMemo(
|
const attributeInput = React.useMemo(
|
||||||
|
@ -131,6 +134,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||||
return onVariantClick(variantId);
|
return onVariantClick(variantId);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onReorder={onVariantReorder}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Card from "@material-ui/core/Card";
|
import Card from "@material-ui/core/Card";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import TableBody from "@material-ui/core/TableBody";
|
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
|
import {
|
||||||
|
SortableTableBody,
|
||||||
|
SortableTableRow
|
||||||
|
} from "@saleor/components/SortableTable";
|
||||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||||
|
import { ReorderAction } from "@saleor/types";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
@ -26,6 +30,7 @@ const useStyles = makeStyles(
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
},
|
},
|
||||||
tabActive: {
|
tabActive: {
|
||||||
|
"& > td:first-child": {
|
||||||
"&:before": {
|
"&:before": {
|
||||||
background: theme.palette.primary.main,
|
background: theme.palette.primary.main,
|
||||||
content: '""',
|
content: '""',
|
||||||
|
@ -37,6 +42,7 @@ const useStyles = makeStyles(
|
||||||
},
|
},
|
||||||
position: "relative"
|
position: "relative"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
{ name: "ProductVariantNavigation" }
|
{ name: "ProductVariantNavigation" }
|
||||||
);
|
);
|
||||||
|
@ -49,10 +55,18 @@ interface ProductVariantNavigationProps {
|
||||||
| ProductVariantCreateData_product_variants[];
|
| ProductVariantCreateData_product_variants[];
|
||||||
onAdd?: () => void;
|
onAdd?: () => void;
|
||||||
onRowClick: (variantId: string) => void;
|
onRowClick: (variantId: string) => void;
|
||||||
|
onReorder: ReorderAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props => {
|
const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props => {
|
||||||
const { current, fallbackThumbnail, variants, onAdd, onRowClick } = props;
|
const {
|
||||||
|
current,
|
||||||
|
fallbackThumbnail,
|
||||||
|
variants,
|
||||||
|
onAdd,
|
||||||
|
onRowClick,
|
||||||
|
onReorder
|
||||||
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -66,18 +80,18 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<ResponsiveTable>
|
<ResponsiveTable>
|
||||||
<TableBody>
|
<SortableTableBody onSortEnd={onReorder}>
|
||||||
{renderCollection(variants, variant => (
|
{renderCollection(variants, (variant, variantIndex) => (
|
||||||
<TableRow
|
<SortableTableRow
|
||||||
hover={!!variant}
|
hover={!!variant}
|
||||||
key={variant ? variant.id : "skeleton"}
|
key={variant ? variant.id : "skeleton"}
|
||||||
className={classes.link}
|
index={variantIndex}
|
||||||
|
className={classNames(classes.link, {
|
||||||
|
[classes.tabActive]: variant && variant.id === current
|
||||||
|
})}
|
||||||
onClick={variant ? () => onRowClick(variant.id) : undefined}
|
onClick={variant ? () => onRowClick(variant.id) : undefined}
|
||||||
>
|
>
|
||||||
<TableCellAvatar
|
<TableCellAvatar
|
||||||
className={classNames({
|
|
||||||
[classes.tabActive]: variant && variant.id === current
|
|
||||||
})}
|
|
||||||
thumbnail={maybe(
|
thumbnail={maybe(
|
||||||
() => variant.images[0].url,
|
() => variant.images[0].url,
|
||||||
fallbackThumbnail
|
fallbackThumbnail
|
||||||
|
@ -86,11 +100,11 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
|
||||||
<TableCell className={classes.colName}>
|
<TableCell className={classes.colName}>
|
||||||
{variant ? variant.name || variant.sku : <Skeleton />}
|
{variant ? variant.name || variant.sku : <Skeleton />}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</SortableTableRow>
|
||||||
))}
|
))}
|
||||||
{onAdd ? (
|
{onAdd ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={2}>
|
<TableCell colSpan={3}>
|
||||||
<Button color="primary" onClick={onAdd}>
|
<Button color="primary" onClick={onAdd}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Add variant"
|
defaultMessage="Add variant"
|
||||||
|
@ -102,7 +116,7 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
|
||||||
) : (
|
) : (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCellAvatar className={classes.tabActive} thumbnail={null} />
|
<TableCellAvatar className={classes.tabActive} thumbnail={null} />
|
||||||
<TableCell className={classes.colName}>
|
<TableCell className={classes.colName} colSpan={2}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="New Variant"
|
defaultMessage="New Variant"
|
||||||
description="variant name"
|
description="variant name"
|
||||||
|
@ -110,7 +124,7 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</SortableTableBody>
|
||||||
</ResponsiveTable>
|
</ResponsiveTable>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
getAttributeInputFromVariant,
|
getAttributeInputFromVariant,
|
||||||
getStockInputFromVariant
|
getStockInputFromVariant
|
||||||
} from "@saleor/products/utils/data";
|
} from "@saleor/products/utils/data";
|
||||||
|
import { ReorderAction } from "@saleor/types";
|
||||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||||
import { diff } from "fast-array-diff";
|
import { diff } from "fast-array-diff";
|
||||||
|
@ -60,6 +61,7 @@ interface ProductVariantPageProps {
|
||||||
placeholderImage?: string;
|
placeholderImage?: string;
|
||||||
header: string;
|
header: string;
|
||||||
warehouses: WarehouseFragment[];
|
warehouses: WarehouseFragment[];
|
||||||
|
onVariantReorder: ReorderAction;
|
||||||
onAdd();
|
onAdd();
|
||||||
onBack();
|
onBack();
|
||||||
onDelete();
|
onDelete();
|
||||||
|
@ -82,7 +84,8 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
||||||
onDelete,
|
onDelete,
|
||||||
onImageSelect,
|
onImageSelect,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onVariantClick
|
onVariantClick,
|
||||||
|
onVariantReorder
|
||||||
}) => {
|
}) => {
|
||||||
const attributeInput = React.useMemo(
|
const attributeInput = React.useMemo(
|
||||||
() => getAttributeInputFromVariant(variant),
|
() => getAttributeInputFromVariant(variant),
|
||||||
|
@ -188,6 +191,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
||||||
return onVariantClick(variantId);
|
return onVariantClick(variantId);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onReorder={onVariantReorder}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -3,9 +3,7 @@ import Card from "@material-ui/core/Card";
|
||||||
import CardContent from "@material-ui/core/CardContent";
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
import Hidden from "@material-ui/core/Hidden";
|
import Hidden from "@material-ui/core/Hidden";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import TableBody from "@material-ui/core/TableBody";
|
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Checkbox from "@saleor/components/Checkbox";
|
import Checkbox from "@saleor/components/Checkbox";
|
||||||
|
@ -14,13 +12,17 @@ import Money from "@saleor/components/Money";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
|
import {
|
||||||
|
SortableTableBody,
|
||||||
|
SortableTableRow
|
||||||
|
} from "@saleor/components/SortableTable";
|
||||||
import TableHead from "@saleor/components/TableHead";
|
import TableHead from "@saleor/components/TableHead";
|
||||||
import { ProductVariant_costPrice } from "@saleor/fragments/types/ProductVariant";
|
import { ProductVariant_costPrice } from "@saleor/fragments/types/ProductVariant";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
|
import { FormattedMessage, IntlShape, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe, renderCollection } from "../../../misc";
|
import { maybe, renderCollection } from "../../../misc";
|
||||||
import { ListActions } from "../../../types";
|
import { ListActions, ReorderAction } from "../../../types";
|
||||||
import {
|
import {
|
||||||
ProductDetails_product_variants,
|
ProductDetails_product_variants,
|
||||||
ProductDetails_product_variants_stocks_warehouse
|
ProductDetails_product_variants_stocks_warehouse
|
||||||
|
@ -171,12 +173,13 @@ interface ProductVariantsProps extends ListActions {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
variants: ProductDetails_product_variants[];
|
variants: ProductDetails_product_variants[];
|
||||||
fallbackPrice?: ProductVariant_costPrice;
|
fallbackPrice?: ProductVariant_costPrice;
|
||||||
|
onVariantReorder: ReorderAction;
|
||||||
onRowClick: (id: string) => () => void;
|
onRowClick: (id: string) => () => void;
|
||||||
onVariantAdd?();
|
onVariantAdd?();
|
||||||
onVariantsAdd?();
|
onVariantsAdd?();
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberOfColumns = 5;
|
const numberOfColumns = 6;
|
||||||
|
|
||||||
export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
const {
|
const {
|
||||||
|
@ -186,6 +189,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
onRowClick,
|
onRowClick,
|
||||||
onVariantAdd,
|
onVariantAdd,
|
||||||
onVariantsAdd,
|
onVariantsAdd,
|
||||||
|
onVariantReorder,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -266,6 +270,7 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
items={variants}
|
items={variants}
|
||||||
toggleAll={toggleAll}
|
toggleAll={toggleAll}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
|
dragRows
|
||||||
>
|
>
|
||||||
<TableCell className={classes.colName}>
|
<TableCell className={classes.colName}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
@ -291,8 +296,8 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<SortableTableBody onSortEnd={onVariantReorder}>
|
||||||
{renderCollection(variants, variant => {
|
{renderCollection(variants, (variant, variantIndex) => {
|
||||||
const isSelected = variant ? isChecked(variant.id) : false;
|
const isSelected = variant ? isChecked(variant.id) : false;
|
||||||
const numAvailable =
|
const numAvailable =
|
||||||
variant && variant.stocks
|
variant && variant.stocks
|
||||||
|
@ -303,11 +308,12 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<SortableTableRow
|
||||||
selected={isSelected}
|
selected={isSelected}
|
||||||
hover={!!variant}
|
hover={!!variant}
|
||||||
onClick={onRowClick(variant.id)}
|
onClick={onRowClick(variant.id)}
|
||||||
key={variant ? variant.id : "skeleton"}
|
key={variant ? variant.id : "skeleton"}
|
||||||
|
index={variantIndex || 0}
|
||||||
className={classes.link}
|
className={classes.link}
|
||||||
>
|
>
|
||||||
<TableCell padding="checkbox">
|
<TableCell padding="checkbox">
|
||||||
|
@ -354,10 +360,10 @@ export const ProductVariants: React.FC<ProductVariantsProps> = props => {
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</SortableTableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</TableBody>
|
</SortableTableBody>
|
||||||
</ResponsiveTable>
|
</ResponsiveTable>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -53,6 +53,10 @@ import {
|
||||||
ProductVariantBulkDelete,
|
ProductVariantBulkDelete,
|
||||||
ProductVariantBulkDeleteVariables
|
ProductVariantBulkDeleteVariables
|
||||||
} from "./types/ProductVariantBulkDelete";
|
} from "./types/ProductVariantBulkDelete";
|
||||||
|
import {
|
||||||
|
ProductVariantReorder,
|
||||||
|
ProductVariantReorderVariables
|
||||||
|
} from "./types/ProductVariantReorder";
|
||||||
import {
|
import {
|
||||||
SimpleProductUpdate,
|
SimpleProductUpdate,
|
||||||
SimpleProductUpdateVariables
|
SimpleProductUpdateVariables
|
||||||
|
@ -628,3 +632,22 @@ export const useProductSetAvailabilityForPurchase = makeMutation<
|
||||||
ProductSetAvailabilityForPurchase,
|
ProductSetAvailabilityForPurchase,
|
||||||
ProductSetAvailabilityForPurchaseVariables
|
ProductSetAvailabilityForPurchaseVariables
|
||||||
>(productSetAvailabilityForPurchase);
|
>(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);
|
||||||
|
|
236
src/products/types/ProductVariantReorder.ts
Normal file
236
src/products/types/ProductVariantReorder.ts
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import {
|
||||||
useProductSetAvailabilityForPurchase,
|
useProductSetAvailabilityForPurchase,
|
||||||
useProductUpdateMutation,
|
useProductUpdateMutation,
|
||||||
useProductVariantBulkDeleteMutation,
|
useProductVariantBulkDeleteMutation,
|
||||||
|
useProductVariantReorderMutation,
|
||||||
useSimpleProductUpdateMutation
|
useSimpleProductUpdateMutation
|
||||||
} from "@saleor/products/mutations";
|
} from "@saleor/products/mutations";
|
||||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||||
|
@ -52,7 +53,8 @@ import {
|
||||||
import {
|
import {
|
||||||
createImageReorderHandler,
|
createImageReorderHandler,
|
||||||
createImageUploadHandler,
|
createImageUploadHandler,
|
||||||
createUpdateHandler
|
createUpdateHandler,
|
||||||
|
createVariantReorderHandler
|
||||||
} from "./handlers";
|
} from "./handlers";
|
||||||
|
|
||||||
interface ProductUpdateProps {
|
interface ProductUpdateProps {
|
||||||
|
@ -232,12 +234,22 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
||||||
reorderProductImages({ variables })
|
reorderProductImages({ variables })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
reorderProductVariants,
|
||||||
|
reorderProductVariantsOpts
|
||||||
|
] = useProductVariantReorderMutation({});
|
||||||
|
|
||||||
|
const handleVariantReorder = createVariantReorderHandler(product, variables =>
|
||||||
|
reorderProductVariants({ variables })
|
||||||
|
);
|
||||||
|
|
||||||
const disableFormSave =
|
const disableFormSave =
|
||||||
createProductImageOpts.loading ||
|
createProductImageOpts.loading ||
|
||||||
deleteProductOpts.loading ||
|
deleteProductOpts.loading ||
|
||||||
reorderProductImagesOpts.loading ||
|
reorderProductImagesOpts.loading ||
|
||||||
updateProductOpts.loading ||
|
updateProductOpts.loading ||
|
||||||
productAvailabilityOpts.loading ||
|
productAvailabilityOpts.loading ||
|
||||||
|
reorderProductVariantsOpts.loading ||
|
||||||
loading;
|
loading;
|
||||||
|
|
||||||
const formTransitionState = getMutationState(
|
const formTransitionState = getMutationState(
|
||||||
|
@ -289,6 +301,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
||||||
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
|
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
|
||||||
onVariantShow={variantId => () =>
|
onVariantShow={variantId => () =>
|
||||||
navigate(productVariantEditUrl(product.id, variantId))}
|
navigate(productVariantEditUrl(product.id, variantId))}
|
||||||
|
onVariantReorder={handleVariantReorder}
|
||||||
onImageUpload={handleImageUpload}
|
onImageUpload={handleImageUpload}
|
||||||
onImageEdit={handleImageEdit}
|
onImageEdit={handleImageEdit}
|
||||||
onImageDelete={handleImageDelete}
|
onImageDelete={handleImageDelete}
|
||||||
|
|
|
@ -14,6 +14,9 @@ import {
|
||||||
ProductUpdate,
|
ProductUpdate,
|
||||||
ProductUpdateVariables
|
ProductUpdateVariables
|
||||||
} from "@saleor/products/types/ProductUpdate";
|
} 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 {
|
import {
|
||||||
SimpleProductUpdate,
|
SimpleProductUpdate,
|
||||||
SimpleProductUpdateVariables
|
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
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import ProductVariantPage, {
|
||||||
ProductVariantPageSubmitData
|
ProductVariantPageSubmitData
|
||||||
} from "../components/ProductVariantPage";
|
} from "../components/ProductVariantPage";
|
||||||
import {
|
import {
|
||||||
|
useProductVariantReorderMutation,
|
||||||
useVariantDeleteMutation,
|
useVariantDeleteMutation,
|
||||||
useVariantImageAssignMutation,
|
useVariantImageAssignMutation,
|
||||||
useVariantImageUnassignMutation,
|
useVariantImageUnassignMutation,
|
||||||
|
@ -36,6 +37,7 @@ import {
|
||||||
ProductVariantEditUrlQueryParams
|
ProductVariantEditUrlQueryParams
|
||||||
} from "../urls";
|
} from "../urls";
|
||||||
import { mapFormsetStockToStockInput } from "../utils/data";
|
import { mapFormsetStockToStockInput } from "../utils/data";
|
||||||
|
import { createVariantReorderHandler } from "./ProductUpdate/handlers";
|
||||||
|
|
||||||
interface ProductUpdateProps {
|
interface ProductUpdateProps {
|
||||||
variantId: string;
|
variantId: string;
|
||||||
|
@ -120,12 +122,23 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
|
||||||
return <NotFoundPage onBack={handleBack} />;
|
return <NotFoundPage onBack={handleBack} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [
|
||||||
|
reorderProductVariants,
|
||||||
|
reorderProductVariantsOpts
|
||||||
|
] = useProductVariantReorderMutation({});
|
||||||
|
|
||||||
|
const handleVariantReorder = createVariantReorderHandler(
|
||||||
|
variant?.product,
|
||||||
|
variables => reorderProductVariants({ variables })
|
||||||
|
);
|
||||||
|
|
||||||
const disableFormSave =
|
const disableFormSave =
|
||||||
loading ||
|
loading ||
|
||||||
deleteVariantOpts.loading ||
|
deleteVariantOpts.loading ||
|
||||||
updateVariantOpts.loading ||
|
updateVariantOpts.loading ||
|
||||||
assignImageOpts.loading ||
|
assignImageOpts.loading ||
|
||||||
unassignImageOpts.loading;
|
unassignImageOpts.loading ||
|
||||||
|
reorderProductVariantsOpts.loading;
|
||||||
|
|
||||||
const handleImageSelect = (id: string) => () => {
|
const handleImageSelect = (id: string) => () => {
|
||||||
if (variant) {
|
if (variant) {
|
||||||
|
@ -202,6 +215,7 @@ export const ProductVariant: React.FC<ProductUpdateProps> = ({
|
||||||
onVariantClick={variantId => {
|
onVariantClick={variantId => {
|
||||||
navigate(productVariantEditUrl(productId, variantId));
|
navigate(productVariantEditUrl(productId, variantId));
|
||||||
}}
|
}}
|
||||||
|
onVariantReorder={handleVariantReorder}
|
||||||
/>
|
/>
|
||||||
<ProductVariantDeleteDialog
|
<ProductVariantDeleteDialog
|
||||||
confirmButtonState={deleteVariantOpts.status}
|
confirmButtonState={deleteVariantOpts.status}
|
||||||
|
|
|
@ -17,9 +17,13 @@ import { decimal, weight } from "../../misc";
|
||||||
import ProductVariantCreatePage, {
|
import ProductVariantCreatePage, {
|
||||||
ProductVariantCreatePageSubmitData
|
ProductVariantCreatePageSubmitData
|
||||||
} from "../components/ProductVariantCreatePage";
|
} from "../components/ProductVariantCreatePage";
|
||||||
import { useVariantCreateMutation } from "../mutations";
|
import {
|
||||||
|
useProductVariantReorderMutation,
|
||||||
|
useVariantCreateMutation
|
||||||
|
} from "../mutations";
|
||||||
import { useProductVariantCreateQuery } from "../queries";
|
import { useProductVariantCreateQuery } from "../queries";
|
||||||
import { productListUrl, productUrl, productVariantEditUrl } from "../urls";
|
import { productListUrl, productUrl, productVariantEditUrl } from "../urls";
|
||||||
|
import { createVariantReorderHandler } from "./ProductUpdate/handlers";
|
||||||
|
|
||||||
interface ProductVariantCreateProps {
|
interface ProductVariantCreateProps {
|
||||||
productId: string;
|
productId: string;
|
||||||
|
@ -69,6 +73,15 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
||||||
return <NotFoundPage onBack={() => navigate(productListUrl())} />;
|
return <NotFoundPage onBack={() => navigate(productListUrl())} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [
|
||||||
|
reorderProductVariants,
|
||||||
|
reorderProductVariantsOpts
|
||||||
|
] = useProductVariantReorderMutation({});
|
||||||
|
|
||||||
|
const handleVariantReorder = createVariantReorderHandler(product, variables =>
|
||||||
|
reorderProductVariants({ variables })
|
||||||
|
);
|
||||||
|
|
||||||
const handleBack = () => navigate(productUrl(productId));
|
const handleBack = () => navigate(productUrl(productId));
|
||||||
const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => {
|
const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => {
|
||||||
const result = await variantCreate({
|
const result = await variantCreate({
|
||||||
|
@ -104,7 +117,10 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
||||||
const handleVariantClick = (id: string) =>
|
const handleVariantClick = (id: string) =>
|
||||||
navigate(productVariantEditUrl(productId, id));
|
navigate(productVariantEditUrl(productId, id));
|
||||||
|
|
||||||
const disableForm = productLoading || variantCreateResult.loading;
|
const disableForm =
|
||||||
|
productLoading ||
|
||||||
|
variantCreateResult.loading ||
|
||||||
|
reorderProductVariantsOpts.loading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -126,6 +142,7 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onVariantClick={handleVariantClick}
|
onVariantClick={handleVariantClick}
|
||||||
|
onVariantReorder={handleVariantReorder}
|
||||||
saveButtonBarState={variantCreateResult.status}
|
saveButtonBarState={variantCreateResult.status}
|
||||||
warehouses={
|
warehouses={
|
||||||
warehouses.data?.warehouses.edges.map(edge => edge.node) || []
|
warehouses.data?.warehouses.edges.map(edge => edge.node) || []
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -34,6 +34,7 @@ const props: ProductUpdatePageProps = {
|
||||||
onImageUpload: () => undefined,
|
onImageUpload: () => undefined,
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
onVariantAdd: () => undefined,
|
onVariantAdd: () => undefined,
|
||||||
|
onVariantReorder: () => undefined,
|
||||||
onVariantShow: () => undefined,
|
onVariantShow: () => undefined,
|
||||||
onVariantsAdd: () => undefined,
|
onVariantsAdd: () => undefined,
|
||||||
placeholderImage,
|
placeholderImage,
|
||||||
|
|
|
@ -23,6 +23,7 @@ storiesOf("Views / Products / Create product variant", module)
|
||||||
onBack={() => undefined}
|
onBack={() => undefined}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
onVariantClick={undefined}
|
onVariantClick={undefined}
|
||||||
|
onVariantReorder={() => undefined}
|
||||||
saveButtonBarState="default"
|
saveButtonBarState="default"
|
||||||
warehouses={warehouseList}
|
warehouses={warehouseList}
|
||||||
/>
|
/>
|
||||||
|
@ -54,6 +55,7 @@ storiesOf("Views / Products / Create product variant", module)
|
||||||
onBack={() => undefined}
|
onBack={() => undefined}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
onVariantClick={undefined}
|
onVariantClick={undefined}
|
||||||
|
onVariantReorder={() => undefined}
|
||||||
saveButtonBarState="default"
|
saveButtonBarState="default"
|
||||||
warehouses={warehouseList}
|
warehouses={warehouseList}
|
||||||
/>
|
/>
|
||||||
|
@ -69,6 +71,7 @@ storiesOf("Views / Products / Create product variant", module)
|
||||||
onBack={() => undefined}
|
onBack={() => undefined}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
onVariantClick={undefined}
|
onVariantClick={undefined}
|
||||||
|
onVariantReorder={() => undefined}
|
||||||
saveButtonBarState="default"
|
saveButtonBarState="default"
|
||||||
warehouses={warehouseList}
|
warehouses={warehouseList}
|
||||||
/>
|
/>
|
||||||
|
@ -87,6 +90,7 @@ storiesOf("Views / Products / Create product variant", module)
|
||||||
onBack={() => undefined}
|
onBack={() => undefined}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
onVariantClick={undefined}
|
onVariantClick={undefined}
|
||||||
|
onVariantReorder={() => undefined}
|
||||||
saveButtonBarState="default"
|
saveButtonBarState="default"
|
||||||
warehouses={warehouseList}
|
warehouses={warehouseList}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -24,6 +24,7 @@ storiesOf("Views / Products / Product variant details", module)
|
||||||
onImageSelect={() => undefined}
|
onImageSelect={() => undefined}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
onVariantClick={() => undefined}
|
onVariantClick={() => undefined}
|
||||||
|
onVariantReorder={() => undefined}
|
||||||
saveButtonBarState="default"
|
saveButtonBarState="default"
|
||||||
warehouses={warehouseList}
|
warehouses={warehouseList}
|
||||||
/>
|
/>
|
||||||
|
@ -41,6 +42,7 @@ storiesOf("Views / Products / Product variant details", module)
|
||||||
onImageSelect={() => undefined}
|
onImageSelect={() => undefined}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
onVariantClick={() => undefined}
|
onVariantClick={() => undefined}
|
||||||
|
onVariantReorder={() => undefined}
|
||||||
saveButtonBarState="default"
|
saveButtonBarState="default"
|
||||||
warehouses={warehouseList}
|
warehouses={warehouseList}
|
||||||
/>
|
/>
|
||||||
|
@ -56,6 +58,7 @@ storiesOf("Views / Products / Product variant details", module)
|
||||||
onImageSelect={() => undefined}
|
onImageSelect={() => undefined}
|
||||||
onSubmit={() => undefined}
|
onSubmit={() => undefined}
|
||||||
onVariantClick={() => undefined}
|
onVariantClick={() => undefined}
|
||||||
|
onVariantReorder={() => undefined}
|
||||||
saveButtonBarState="default"
|
saveButtonBarState="default"
|
||||||
errors={[
|
errors={[
|
||||||
{
|
{
|
||||||
|
|
|
@ -548,6 +548,7 @@ export enum OrderErrorCode {
|
||||||
REQUIRED = "REQUIRED",
|
REQUIRED = "REQUIRED",
|
||||||
SHIPPING_METHOD_NOT_APPLICABLE = "SHIPPING_METHOD_NOT_APPLICABLE",
|
SHIPPING_METHOD_NOT_APPLICABLE = "SHIPPING_METHOD_NOT_APPLICABLE",
|
||||||
SHIPPING_METHOD_REQUIRED = "SHIPPING_METHOD_REQUIRED",
|
SHIPPING_METHOD_REQUIRED = "SHIPPING_METHOD_REQUIRED",
|
||||||
|
TAX_ERROR = "TAX_ERROR",
|
||||||
UNIQUE = "UNIQUE",
|
UNIQUE = "UNIQUE",
|
||||||
VOID_INACTIVE_PAYMENT = "VOID_INACTIVE_PAYMENT",
|
VOID_INACTIVE_PAYMENT = "VOID_INACTIVE_PAYMENT",
|
||||||
ZERO_QUANTITY = "ZERO_QUANTITY",
|
ZERO_QUANTITY = "ZERO_QUANTITY",
|
||||||
|
@ -905,6 +906,7 @@ export enum WebhookEventTypeEnum {
|
||||||
ORDER_FULLY_PAID = "ORDER_FULLY_PAID",
|
ORDER_FULLY_PAID = "ORDER_FULLY_PAID",
|
||||||
ORDER_UPDATED = "ORDER_UPDATED",
|
ORDER_UPDATED = "ORDER_UPDATED",
|
||||||
PRODUCT_CREATED = "PRODUCT_CREATED",
|
PRODUCT_CREATED = "PRODUCT_CREATED",
|
||||||
|
PRODUCT_UPDATED = "PRODUCT_UPDATED",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WebhookSortField {
|
export enum WebhookSortField {
|
||||||
|
|
|
@ -68,6 +68,10 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
|
||||||
defaultMessage: "Product created",
|
defaultMessage: "Product created",
|
||||||
description: "event"
|
description: "event"
|
||||||
}),
|
}),
|
||||||
|
[WebhookEventTypeEnum.PRODUCT_UPDATED]: intl.formatMessage({
|
||||||
|
defaultMessage: "Product updated",
|
||||||
|
description: "event"
|
||||||
|
}),
|
||||||
[WebhookEventTypeEnum.CHECKOUT_QUANTITY_CHANGED]: intl.formatMessage({
|
[WebhookEventTypeEnum.CHECKOUT_QUANTITY_CHANGED]: intl.formatMessage({
|
||||||
defaultMessage: "Changed quantity in checkout",
|
defaultMessage: "Changed quantity in checkout",
|
||||||
description: "event"
|
description: "event"
|
||||||
|
|
Loading…
Reference in a new issue