Handle variants reordering

This commit is contained in:
Dawid Tarasiuk 2020-09-17 16:37:33 +02:00
parent 88b8950408
commit ffaf56a51b
11 changed files with 349 additions and 8 deletions

View file

@ -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"

View file

@ -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

View file

@ -116,7 +116,7 @@ const ProductVariantNavigation: React.FC<ProductVariantNavigationProps> = props
) : (
<TableRow>
<TableCellAvatar className={classes.tabActive} thumbnail={null} />
<TableCell className={classes.colName}>
<TableCell className={classes.colName} colSpan={2}>
<FormattedMessage
defaultMessage="New Variant"
description="variant name"

View file

@ -53,6 +53,10 @@ import {
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
} from "./types/ProductVariantBulkDelete";
import {
ProductVariantReorder,
ProductVariantReorderVariables
} from "./types/ProductVariantReorder";
import {
SimpleProductUpdate,
SimpleProductUpdateVariables
@ -628,3 +632,22 @@ export const useProductSetAvailabilityForPurchase = makeMutation<
ProductSetAvailabilityForPurchase,
ProductSetAvailabilityForPurchaseVariables
>(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);

View 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;
}

View file

@ -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<ProductUpdateProps> = ({ 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<ProductUpdateProps> = ({ id, params }) => {
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
onVariantShow={variantId => () =>
navigate(productVariantEditUrl(product.id, variantId))}
onVariantReorder={() => undefined} // TODO: ...
onVariantReorder={handleVariantReorder}
onImageUpload={handleImageUpload}
onImageEdit={handleImageEdit}
onImageDelete={handleImageDelete}

View file

@ -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
});
};
}

View file

@ -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<ProductUpdateProps> = ({
return <NotFoundPage onBack={handleBack} />;
}
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<ProductUpdateProps> = ({
onVariantClick={variantId => {
navigate(productVariantEditUrl(productId, variantId));
}}
onVariantReorder={() => undefined} // TODO: ...
onVariantReorder={handleVariantReorder}
/>
<ProductVariantDeleteDialog
confirmButtonState={deleteVariantOpts.status}

View file

@ -17,9 +17,13 @@ import { decimal, weight } from "../../misc";
import ProductVariantCreatePage, {
ProductVariantCreatePageSubmitData
} from "../components/ProductVariantCreatePage";
import { useVariantCreateMutation } from "../mutations";
import {
useProductVariantReorderMutation,
useVariantCreateMutation
} from "../mutations";
import { useProductVariantCreateQuery } from "../queries";
import { productListUrl, productUrl, productVariantEditUrl } from "../urls";
import { createVariantReorderHandler } from "./ProductUpdate/handlers";
interface ProductVariantCreateProps {
productId: string;
@ -69,6 +73,15 @@ export const ProductVariant: React.FC<ProductVariantCreateProps> = ({
return <NotFoundPage onBack={() => 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<ProductVariantCreateProps> = ({
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<ProductVariantCreateProps> = ({
onBack={handleBack}
onSubmit={handleSubmit}
onVariantClick={handleVariantClick}
onVariantReorder={() => undefined} // TODO: ...
onVariantReorder={handleVariantReorder}
saveButtonBarState={variantCreateResult.status}
warehouses={
warehouses.data?.warehouses.edges.map(edge => edge.node) || []

View file

@ -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 {

View file

@ -68,6 +68,10 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
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"