diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 8fb6d37a0..fcf473bdc 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -292,6 +292,12 @@ "context": "attributes section name", "string": "Attributes" }, + "src_dot_attributes_dot_attributeSlugUnique": { + "string": "Attribute with this slug already exists" + }, + "src_dot_attributes_dot_attributeValueAlreadyExists": { + "string": "This value already exists within this attribute" + }, "src_dot_attributes_dot_components_dot_AttributeBulkDeleteDialog_dot_1184518529": { "context": "dialog content", "string": "{counter,plural,one{Are you sure you want to delete this attribute?} other{Are you sure you want to delete {displayQuantity} attributes?}}" @@ -477,10 +483,6 @@ "src_dot_attributes_dot_views_dot_AttributeCreate_dot_11941964": { "string": "Successfully created attribute" }, - "src_dot_attributes_dot_views_dot_AttributeCreate_dot_354316953": { - "context": "attribute value edit error", - "string": "A value named {name} already exists" - }, "src_dot_attributes_dot_views_dot_AttributeDetails_dot_423042761": { "context": "attribute value deleted", "string": "Value deleted" @@ -836,9 +838,6 @@ "src_dot_collections_dot_views_dot_1152429477": { "string": "Deleted collection" }, - "src_dot_collections_dot_views_dot_1597339737": { - "string": "Created collection" - }, "src_dot_collections_dot_views_dot_2001540731": { "string": "Added product to collection" }, @@ -2722,9 +2721,6 @@ "src_dot_orders_dot_views_dot_OrderDetails_dot_1056718390": { "string": "Payment successfully captured" }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_1096896329": { - "string": "Could not fulfill items" - }, "src_dot_orders_dot_views_dot_OrderDetails_dot_1435191432": { "string": "Draft order successfully finalized" }, @@ -2734,30 +2730,15 @@ "src_dot_orders_dot_views_dot_OrderDetails_dot_1475565380": { "string": "Fulfillment successfully updated" }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_149745214": { - "string": "Could not finalize draft" - }, "src_dot_orders_dot_views_dot_OrderDetails_dot_1632861387": { "string": "Order line updated" }, "src_dot_orders_dot_views_dot_OrderDetails_dot_1636370257": { "string": "Order marked as paid" }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_1708579411": { - "string": "Could not update shipping method" - }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_1852833563": { - "string": "Could not mark order as paid" - }, "src_dot_orders_dot_views_dot_OrderDetails_dot_1999598492": { "string": "Order line added" }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_2205960666": { - "string": "Could not delete order line" - }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_2714957902": { - "string": "Could not add note" - }, "src_dot_orders_dot_views_dot_OrderDetails_dot_2808777264": { "string": "Order successfully cancelled" }, @@ -2770,27 +2751,12 @@ "src_dot_orders_dot_views_dot_OrderDetails_dot_3367579693": { "string": "Order successfully updated" }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_3991286734": { - "context": "notification", - "string": "Payment not refunded: {errorMessage}" - }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_4199953867": { - "string": "Could not cancel fulfillment" - }, "src_dot_orders_dot_views_dot_OrderDetails_dot_4245651107": { "string": "Items successfully fulfilled" }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_487150696": { - "string": "Could not update order line" - }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_553600482": { - "string": "Could not update fulfillment" - }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_557535634": { - "string": "Payment not captured: {errorMessage}" - }, - "src_dot_orders_dot_views_dot_OrderDetails_dot_562214451": { - "string": "Could not create order line" + "src_dot_orders_dot_views_dot_OrderDetails_dot_580490159": { + "context": "window title", + "string": "Order #{orderNumber}" }, "src_dot_orders_dot_views_dot_OrderDetails_dot_617145655": { "string": "Shipping method successfully updated" @@ -2798,6 +2764,10 @@ "src_dot_orders_dot_views_dot_OrderDetails_dot_667274681": { "string": "Payment successfully refunded" }, + "src_dot_orders_dot_views_dot_OrderDetails_dot_694622335": { + "context": "window title", + "string": "Draft Order #{orderNumber}" + }, "src_dot_orders_dot_views_dot_OrderDetails_dot_927945225": { "string": "Fulfillment successfully cancelled" }, @@ -2893,9 +2863,6 @@ "context": "header", "string": "Create Page" }, - "src_dot_pages_dot_views_dot_1457312643": { - "string": "Removed page" - }, "src_dot_pages_dot_views_dot_2680158037": { "string": "Successfully created new page" }, @@ -4009,6 +3976,14 @@ "context": "shipping method has no value limits", "string": "There are no value limits" }, + "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_price": { + "context": "error message", + "string": "Maximum price cannot be lower than minimum" + }, + "src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_weight": { + "context": "error message", + "string": "Maximum weight cannot be lower than minimum" + }, "src_dot_shipping_dot_components_dot_ShippingZoneRates_dot_1134347598": { "context": "shipping method price", "string": "Price" @@ -4236,9 +4211,6 @@ "src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_866304242": { "string": "This where you will find all of the settings determining your stores e-mails. You can determine main email address and some of the contents of your emails." }, - "src_dot_siteSettings_dot_views_dot_2276194921": { - "string": "Could not delete authorization key: {errorMessage}" - }, "src_dot_somethingWentWrong": { "string": "Saleor ran into an unexpected problem" }, @@ -4745,6 +4717,98 @@ "context": "button", "string": "Upload image" }, + "src_dot_utils_dot_errors_dot_alreadyExists": { + "context": "add authorization key error", + "string": "Authorization key with this type already exists" + }, + "src_dot_utils_dot_errors_dot_attributeAlreadyAssigned": { + "string": "This attribute has already been assigned to this product type" + }, + "src_dot_utils_dot_errors_dot_attributeCannotBeAssigned": { + "string": "This attribute cannot be assigned to this product type" + }, + "src_dot_utils_dot_errors_dot_attributeVariantsDisabled": { + "string": "Variants are disabled in this product type" + }, + "src_dot_utils_dot_errors_dot_billingNotSet": { + "context": "error message", + "string": "Billing address is not set" + }, + "src_dot_utils_dot_errors_dot_cannotCancelFulfillment": { + "context": "error message", + "string": "This fulfillment cannot be cancelled" + }, + "src_dot_utils_dot_errors_dot_cannotCancelOrder": { + "context": "error message", + "string": "This order cannot be cancelled" + }, + "src_dot_utils_dot_errors_dot_cannotFulfillLine": { + "context": "error message", + "string": "Not enough items to fulfill" + }, + "src_dot_utils_dot_errors_dot_cannotRefund": { + "context": "error message", + "string": "Manual payments can not be refunded" + }, + "src_dot_utils_dot_errors_dot_cannotVoid": { + "context": "error message", + "string": "Only pre-authorized payments can be voided" + }, + "src_dot_utils_dot_errors_dot_captureInactive": { + "context": "error message", + "string": "Only pre-authorized payments can be captured" + }, + "src_dot_utils_dot_errors_dot_graphqlError": { + "string": "API error" + }, + "src_dot_utils_dot_errors_dot_invalid": { + "string": "Invalid value" + }, + "src_dot_utils_dot_errors_dot_invalidPassword": { + "string": "Invalid password" + }, + "src_dot_utils_dot_errors_dot_noShippingAddress": { + "context": "error message", + "string": "Cannot choose a shipping method for an order without the shipping address" + }, + "src_dot_utils_dot_errors_dot_notEditable": { + "context": "error message", + "string": "Only draft orders can be edited" + }, + "src_dot_utils_dot_errors_dot_passwordNumeric": { + "string": "Password cannot be entirely numeric" + }, + "src_dot_utils_dot_errors_dot_paymentMissing": { + "context": "error message", + "string": "There's no payment associated with the order" + }, + "src_dot_utils_dot_errors_dot_shippingNotApplicable": { + "context": "error message", + "string": "Shipping method is not valid for chosen shipping address" + }, + "src_dot_utils_dot_errors_dot_shippingRequired": { + "context": "error message", + "string": "Shipping method is required for this order" + }, + "src_dot_utils_dot_errors_dot_skuUnique": { + "context": "bulk variant create error", + "string": "SKUs must be unique" + }, + "src_dot_utils_dot_errors_dot_tooCommon": { + "string": "This password is too commonly used" + }, + "src_dot_utils_dot_errors_dot_tooShort": { + "string": "This password is too short" + }, + "src_dot_utils_dot_errors_dot_tooSimilar": { + "string": "These passwords are too similar" + }, + "src_dot_utils_dot_errors_dot_unknownError": { + "string": "Unknown error" + }, + "src_dot_utils_dot_errors_dot_variantNoDigitalContent": { + "string": "This variant does not have any digital content" + }, "src_dot_vouchers": { "context": "vouchers section name", "string": "Vouchers" @@ -4757,6 +4821,10 @@ "context": "header", "string": "Create Webhook" }, + "src_dot_webhooks_dot_components_dot_WebhookDeleteDialog_dot_216945727": { + "context": "delete webhook", + "string": "Are you sure you want to delete this webhook?" + }, "src_dot_webhooks_dot_components_dot_WebhookDeleteDialog_dot_2297471173": { "context": "delete webhook", "string": "Are you sure you want to delete {name}?" @@ -4856,6 +4924,10 @@ "context": "section header", "string": "Webhook Status" }, + "src_dot_webhooks_dot_components_dot_WebhooksDetailsPage_dot_1595053355": { + "context": "header", + "string": "Unnamed Webhook Details" + }, "src_dot_webhooks_dot_components_dot_WebhooksDetailsPage_dot_408706360": { "context": "header", "string": "{webhookName} Details" @@ -4890,6 +4962,9 @@ "context": "user action bar", "string": "Action" }, + "src_dot_webhooks_dot_components_dot_WebhooksList_dot_618422799": { + "string": "Unnamed webhook" + }, "src_dot_webhooks_dot_components_dot_WebhooksList_dot_636461959": { "context": "webhook name", "string": "Name" diff --git a/schema.graphql b/schema.graphql index 0a4ab2fe4..49c6bdb44 100644 --- a/schema.graphql +++ b/schema.graphql @@ -493,6 +493,13 @@ type BulkProductError { index: Int } +type BulkStockError { + field: String + message: String + code: ProductErrorCode! + index: Int +} + input CatalogueInput { products: [ID] categories: [ID] @@ -1502,6 +1509,20 @@ input DigitalContentUrlCreateInput { content: ID! } +type DiscountError { + field: String + message: String + code: DiscountErrorCode! +} + +enum DiscountErrorCode { + ALREADY_EXISTS + INVALID + NOT_FOUND + REQUIRED + UNIQUE +} + enum DiscountStatusEnum { ACTIVE EXPIRED @@ -2158,10 +2179,6 @@ type Mutation { deleteWarehouse(id: ID!): WarehouseDelete assignWarehouseShippingZone(id: ID!, shippingZoneIds: [ID!]!): WarehouseShippingZoneAssign unassignWarehouseShippingZone(id: ID!, shippingZoneIds: [ID!]!): WarehouseShippingZoneUnassign - createStock(input: StockInput!): StockCreate - updateStock(id: ID!, input: StockInput!): StockUpdate - deleteStock(id: ID!): StockDelete - bulkDeleteStock(ids: [ID]!): StockBulkDelete authorizationKeyAdd(input: AuthorizationKeyInput!, keyType: AuthorizationKeyType!): AuthorizationKeyAdd authorizationKeyDelete(keyType: AuthorizationKeyType!): AuthorizationKeyDelete staffNotificationRecipientCreate(input: StaffNotificationRecipientInput!): StaffNotificationRecipientCreate @@ -2253,6 +2270,9 @@ type Mutation { productVariantDelete(id: ID!): ProductVariantDelete productVariantBulkCreate(product: ID!, variants: [ProductVariantBulkCreateInput]!): ProductVariantBulkCreate productVariantBulkDelete(ids: [ID]!): ProductVariantBulkDelete + productVariantStocksCreate(stocks: [StockInput!]!, variantId: ID!): ProductVariantStocksCreate + productVariantStocksDelete(variantId: ID!, warehouseIds: [ID!]): ProductVariantStocksDelete + productVariantStocksUpdate(stocks: [StockInput!]!, variantId: ID!): ProductVariantStocksUpdate productVariantUpdate(id: ID!, input: ProductVariantInput!): ProductVariantUpdate productVariantTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ProductVariantTranslate productVariantUpdateMetadata(id: ID!, input: MetaInput!): ProductVariantUpdateMeta @deprecated(reason: "Will be removed in Saleor 2.11. Use the `UpdateMetadata` mutation instead.") @@ -2444,8 +2464,8 @@ type Order implements Node & ObjectWithMetadata { voucher: Voucher giftCards: [GiftCard] discount: Money - discountName: String! - translatedDiscountName: String! + discountName: String + translatedDiscountName: String displayGrossPrices: Boolean! customerNote: String! weight: Weight @@ -2489,7 +2509,7 @@ type OrderAddNote { } input OrderAddNoteInput { - message: String + message: String! } type OrderBulkCancel { @@ -3218,6 +3238,7 @@ input ProductCreateInput { quantity: Int trackInventory: Boolean productType: ID! + stocks: [StockInput!] } type ProductDelete { @@ -3255,6 +3276,7 @@ input ProductFilterInput { attributes: [AttributeInput] stockAvailability: StockAvailability productType: ID + stocks: ProductStockFilterInput search: String minimalPrice: PriceRangeInput productTypes: [ID] @@ -3355,6 +3377,11 @@ type ProductPricingInfo { priceRangeLocalCurrency: TaxedMoneyRange } +input ProductStockFilterInput { + warehouseIds: [ID!] + quantity: IntRangeInput +} + type ProductTranslatableContent implements Node { id: ID! seoTitle: String @@ -3549,7 +3576,7 @@ type ProductVariant implements Node & ObjectWithMetadata { images: [ProductImage] translation(languageCode: LanguageCodeEnum!): ProductVariantTranslation digitalContent: DigitalContent - stock(country: String): [Stock] + stocks(countryCode: CountryCode): [Stock] } type ProductVariantBulkCreate { @@ -3613,6 +3640,7 @@ input ProductVariantCreateInput { trackInventory: Boolean weight: WeightScalar product: ID! + stocks: [StockInput!] } type ProductVariantDelete { @@ -3631,6 +3659,24 @@ input ProductVariantInput { weight: WeightScalar } +type ProductVariantStocksCreate { + errors: [Error!]! + productVariant: ProductVariant + bulkStockErrors: [BulkStockError!]! +} + +type ProductVariantStocksDelete { + errors: [Error!]! + productVariant: ProductVariant + stockErrors: [StockError!]! +} + +type ProductVariantStocksUpdate { + errors: [Error!]! + productVariant: ProductVariant + bulkStockErrors: [BulkStockError!]! +} + type ProductVariantTranslatableContent implements Node { id: ID! name: String! @@ -3784,6 +3830,7 @@ type Sale implements Node { type SaleAddCatalogues { errors: [Error!]! sale: Sale + discountErrors: [DiscountError!]! } type SaleBulkDelete { @@ -3804,11 +3851,13 @@ type SaleCountableEdge { type SaleCreate { errors: [Error!]! + discountErrors: [DiscountError!]! sale: Sale } type SaleDelete { errors: [Error!]! + discountErrors: [DiscountError!]! sale: Sale } @@ -3833,6 +3882,7 @@ input SaleInput { type SaleRemoveCatalogues { errors: [Error!]! sale: Sale + discountErrors: [DiscountError!]! } enum SaleSortField { @@ -3873,6 +3923,7 @@ enum SaleType { type SaleUpdate { errors: [Error!]! + discountErrors: [DiscountError!]! sale: Sale } @@ -3888,15 +3939,15 @@ input SeoInput { type ServiceAccount implements Node & ObjectWithMetadata { id: ID! + name: String created: DateTime isActive: Boolean + permissions: [PermissionDisplay] tokens: [ServiceAccountToken] privateMetadata: [MetadataItem]! metadata: [MetadataItem]! privateMeta: [MetaStore]! @deprecated(reason: "DEPRECATED: Will be removed in Saleor 2.11. use the `privetaMetadata` field instead. ") meta: [MetaStore]! @deprecated(reason: "DEPRECATED: Will be removed in Saleor 2.11. use the `metadata` field instead. ") - permissions: [PermissionDisplay] - name: String } type ServiceAccountClearPrivateMeta { @@ -4341,12 +4392,6 @@ enum StockAvailability { OUT_OF_STOCK } -type StockBulkDelete { - errors: [Error!]! - count: Int! - stockError: [StockError!]! -} - type StockCountableConnection { pageInfo: PageInfo! edges: [StockCountableEdge!]! @@ -4358,18 +4403,13 @@ type StockCountableEdge { cursor: String! } -type StockCreate { - errors: [Error!]! - stockErrors: [StockError!]! - stock: Stock +type StockError { + field: String + message: String + code: StockErrorCode! } -type StockDelete { - errors: [Error!]! - stock: Stock -} - -enum StockErorrCode { +enum StockErrorCode { ALREADY_EXISTS GRAPHQL_ERROR INVALID @@ -4378,12 +4418,6 @@ enum StockErorrCode { UNIQUE } -type StockError { - field: String - message: String - code: StockErorrCode! -} - input StockFilterInput { quantity: Float quantityAllocated: Float @@ -4391,17 +4425,10 @@ input StockFilterInput { } input StockInput { - productVariant: ID! warehouse: ID! quantity: Int } -type StockUpdate { - errors: [Error!]! - stockError: [StockError!]! - stock: Stock -} - enum TaxRateType { ACCOMMODATION ADMISSION_TO_CULTURAL_EVENTS @@ -4693,6 +4720,7 @@ type Voucher implements Node { type VoucherAddCatalogues { errors: [Error!]! voucher: Voucher + discountErrors: [DiscountError!]! } type VoucherBulkDelete { @@ -4713,11 +4741,13 @@ type VoucherCountableEdge { type VoucherCreate { errors: [Error!]! + discountErrors: [DiscountError!]! voucher: Voucher } type VoucherDelete { errors: [Error!]! + discountErrors: [DiscountError!]! voucher: Voucher } @@ -4757,6 +4787,7 @@ input VoucherInput { type VoucherRemoveCatalogues { errors: [Error!]! voucher: Voucher + discountErrors: [DiscountError!]! } enum VoucherSortField { @@ -4800,6 +4831,7 @@ enum VoucherTypeEnum { type VoucherUpdate { errors: [Error!]! + discountErrors: [DiscountError!]! voucher: Voucher } diff --git a/src/attributes/components/AttributeDetails/AttributeDetails.tsx b/src/attributes/components/AttributeDetails/AttributeDetails.tsx index a0eb97cfe..26b3ea201 100644 --- a/src/attributes/components/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/components/AttributeDetails/AttributeDetails.tsx @@ -10,16 +10,17 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import FormSpacer from "@saleor/components/FormSpacer"; import SingleSelectField from "@saleor/components/SingleSelectField"; import { commonMessages } from "@saleor/intl"; -import { UserError } from "@saleor/types"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; -import { getFieldError } from "@saleor/utils/errors"; +import { getProductErrorMessage, getFormErrors } from "@saleor/utils/errors"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { AttributePageFormData } from "../AttributePage"; +import { getAttributeSlugErrorMessage } from "../../errors"; export interface AttributeDetailsProps { canChangeType: boolean; data: AttributePageFormData; disabled: boolean; - errors: UserError[]; + errors: ProductErrorFragment[]; onChange: (event: React.ChangeEvent) => void; } @@ -48,6 +49,8 @@ const AttributeDetails: React.FC = ({ } ]; + const formErrors = getFormErrors(["name", "slug", "inputType"], errors); + return ( = ({ = ({ placeholder={slugify(data.name).toLowerCase()} fullWidth helperText={ - getFieldError(errors, "slug")?.message || + getAttributeSlugErrorMessage(formErrors.slug, intl) || intl.formatMessage({ defaultMessage: "This is used internally. Make sure you don’t use spaces", @@ -93,8 +96,8 @@ const AttributeDetails: React.FC = ({ void; diff --git a/src/attributes/components/AttributeProperties/AttributeProperties.tsx b/src/attributes/components/AttributeProperties/AttributeProperties.tsx index 4e12f1d29..1ad3fcd36 100644 --- a/src/attributes/components/AttributeProperties/AttributeProperties.tsx +++ b/src/attributes/components/AttributeProperties/AttributeProperties.tsx @@ -11,14 +11,14 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import FormSpacer from "@saleor/components/FormSpacer"; import Hr from "@saleor/components/Hr"; import { commonMessages } from "@saleor/intl"; -import { UserError } from "@saleor/types"; -import { getFieldError } from "@saleor/utils/errors"; +import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { AttributePageFormData } from "../AttributePage"; export interface AttributePropertiesProps { data: AttributePageFormData; disabled: boolean; - errors: UserError[]; + errors: ProductErrorFragment[]; onChange: (event: React.ChangeEvent) => void; } @@ -30,6 +30,8 @@ const AttributeProperties: React.FC = ({ }) => { const intl = useIntl(); + const formErrors = getFormErrors(["storefrontSearchPosition"], errors); + return ( @@ -86,11 +88,12 @@ const AttributeProperties: React.FC = ({ {data.filterableInStorefront && ( void; onClose: () => void; @@ -45,6 +46,7 @@ const AttributeValueEditDialog: React.FC = ({ name: maybe(() => attributeValue.name, "") }; const errors = useModalDialogErrors(apiErrors, open); + const formErrors = getFormErrors(["name"], errors); return ( @@ -68,9 +70,12 @@ const AttributeValueEditDialog: React.FC = ({ (attributeBulkDelete); const attributeDelete = gql` + ${productErrorFragment} mutation AttributeDelete($id: ID!) { attributeDelete(id: $id) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } @@ -67,15 +74,15 @@ export const AttributeDeleteMutation = TypedMutation< export const attributeUpdateMutation = gql` ${attributeDetailsFragment} + ${productErrorFragment} mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) { attributeUpdate(id: $id, input: $input) { - errors { - field - message - } attribute { ...AttributeDetailsFragment } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -86,15 +93,15 @@ export const AttributeUpdateMutation = TypedMutation< const attributeValueDelete = gql` ${attributeDetailsFragment} + ${productErrorFragment} mutation AttributeValueDelete($id: ID!) { attributeValueDelete(id: $id) { - errors { - field - message - } attribute { ...AttributeDetailsFragment } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -105,15 +112,15 @@ export const AttributeValueDeleteMutation = TypedMutation< export const attributeValueUpdateMutation = gql` ${attributeDetailsFragment} + ${productErrorFragment} mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) { attributeValueUpdate(id: $id, input: $input) { - errors { - field - message - } attribute { ...AttributeDetailsFragment } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -124,15 +131,15 @@ export const AttributeValueUpdateMutation = TypedMutation< export const attributeValueCreateMutation = gql` ${attributeDetailsFragment} + ${productErrorFragment} mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) { attributeValueCreate(attribute: $id, input: $input) { - errors { - field - message - } attribute { ...AttributeDetailsFragment } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -143,15 +150,15 @@ export const AttributeValueCreateMutation = TypedMutation< export const attributeCreateMutation = gql` ${attributeDetailsFragment} + ${productErrorFragment} mutation AttributeCreate($input: AttributeCreateInput!) { attributeCreate(input: $input) { - errors { - field - message - } attribute { ...AttributeDetailsFragment } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -161,18 +168,18 @@ export const AttributeCreateMutation = TypedMutation< >(attributeCreateMutation); const attributeValueReorderMutation = gql` + ${productErrorFragment} mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) { attributeReorderValues(attributeId: $id, moves: [$move]) { - errors { - field - message - } attribute { id values { id } } + errors: productErrors { + ...ProductErrorFragment + } } } `; diff --git a/src/attributes/types/AttributeBulkDelete.ts b/src/attributes/types/AttributeBulkDelete.ts index 6d910e56c..259901950 100644 --- a/src/attributes/types/AttributeBulkDelete.ts +++ b/src/attributes/types/AttributeBulkDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: AttributeBulkDelete // ==================================================== export interface AttributeBulkDelete_attributeBulkDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface AttributeBulkDelete_attributeBulkDelete { diff --git a/src/attributes/types/AttributeCreate.ts b/src/attributes/types/AttributeCreate.ts index 5db772935..3c7db619b 100644 --- a/src/attributes/types/AttributeCreate.ts +++ b/src/attributes/types/AttributeCreate.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; +import { AttributeCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeCreate // ==================================================== -export interface AttributeCreate_attributeCreate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface AttributeCreate_attributeCreate_attribute_values { __typename: "AttributeValue"; id: string; @@ -37,10 +31,16 @@ export interface AttributeCreate_attributeCreate_attribute { values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null; } +export interface AttributeCreate_attributeCreate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface AttributeCreate_attributeCreate { __typename: "AttributeCreate"; - errors: AttributeCreate_attributeCreate_errors[]; attribute: AttributeCreate_attributeCreate_attribute | null; + errors: AttributeCreate_attributeCreate_errors[]; } export interface AttributeCreate { diff --git a/src/attributes/types/AttributeDelete.ts b/src/attributes/types/AttributeDelete.ts index e2cd94ff7..a0273fdac 100644 --- a/src/attributes/types/AttributeDelete.ts +++ b/src/attributes/types/AttributeDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: AttributeDelete // ==================================================== export interface AttributeDelete_attributeDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface AttributeDelete_attributeDelete { diff --git a/src/attributes/types/AttributeUpdate.ts b/src/attributes/types/AttributeUpdate.ts index 3529454ca..51dedb082 100644 --- a/src/attributes/types/AttributeUpdate.ts +++ b/src/attributes/types/AttributeUpdate.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeUpdateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; +import { AttributeUpdateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeUpdate // ==================================================== -export interface AttributeUpdate_attributeUpdate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface AttributeUpdate_attributeUpdate_attribute_values { __typename: "AttributeValue"; id: string; @@ -37,10 +31,16 @@ export interface AttributeUpdate_attributeUpdate_attribute { values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null; } +export interface AttributeUpdate_attributeUpdate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface AttributeUpdate_attributeUpdate { __typename: "AttributeUpdate"; - errors: AttributeUpdate_attributeUpdate_errors[]; attribute: AttributeUpdate_attributeUpdate_attribute | null; + errors: AttributeUpdate_attributeUpdate_errors[]; } export interface AttributeUpdate { diff --git a/src/attributes/types/AttributeValueCreate.ts b/src/attributes/types/AttributeValueCreate.ts index c11325fb3..9f02505d2 100644 --- a/src/attributes/types/AttributeValueCreate.ts +++ b/src/attributes/types/AttributeValueCreate.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; +import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeValueCreate // ==================================================== -export interface AttributeValueCreate_attributeValueCreate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface AttributeValueCreate_attributeValueCreate_attribute_values { __typename: "AttributeValue"; id: string; @@ -37,10 +31,16 @@ export interface AttributeValueCreate_attributeValueCreate_attribute { values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null; } +export interface AttributeValueCreate_attributeValueCreate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface AttributeValueCreate_attributeValueCreate { __typename: "AttributeValueCreate"; - errors: AttributeValueCreate_attributeValueCreate_errors[]; attribute: AttributeValueCreate_attributeValueCreate_attribute | null; + errors: AttributeValueCreate_attributeValueCreate_errors[]; } export interface AttributeValueCreate { diff --git a/src/attributes/types/AttributeValueDelete.ts b/src/attributes/types/AttributeValueDelete.ts index 8737ed336..978e938b3 100644 --- a/src/attributes/types/AttributeValueDelete.ts +++ b/src/attributes/types/AttributeValueDelete.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; +import { AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeValueDelete // ==================================================== -export interface AttributeValueDelete_attributeValueDelete_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface AttributeValueDelete_attributeValueDelete_attribute_values { __typename: "AttributeValue"; id: string; @@ -37,10 +31,16 @@ export interface AttributeValueDelete_attributeValueDelete_attribute { values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null; } +export interface AttributeValueDelete_attributeValueDelete_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface AttributeValueDelete_attributeValueDelete { __typename: "AttributeValueDelete"; - errors: AttributeValueDelete_attributeValueDelete_errors[]; attribute: AttributeValueDelete_attributeValueDelete_attribute | null; + errors: AttributeValueDelete_attributeValueDelete_errors[]; } export interface AttributeValueDelete { diff --git a/src/attributes/types/AttributeValueReorder.ts b/src/attributes/types/AttributeValueReorder.ts index 31a50ba08..e33438c50 100644 --- a/src/attributes/types/AttributeValueReorder.ts +++ b/src/attributes/types/AttributeValueReorder.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { ReorderInput } from "./../../types/globalTypes"; +import { ReorderInput, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeValueReorder // ==================================================== -export interface AttributeValueReorder_attributeReorderValues_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface AttributeValueReorder_attributeReorderValues_attribute_values { __typename: "AttributeValue"; id: string; @@ -25,10 +19,16 @@ export interface AttributeValueReorder_attributeReorderValues_attribute { values: (AttributeValueReorder_attributeReorderValues_attribute_values | null)[] | null; } +export interface AttributeValueReorder_attributeReorderValues_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface AttributeValueReorder_attributeReorderValues { __typename: "AttributeReorderValues"; - errors: AttributeValueReorder_attributeReorderValues_errors[]; attribute: AttributeValueReorder_attributeReorderValues_attribute | null; + errors: AttributeValueReorder_attributeReorderValues_errors[]; } export interface AttributeValueReorder { diff --git a/src/attributes/types/AttributeValueUpdate.ts b/src/attributes/types/AttributeValueUpdate.ts index 90bd6561d..564f7a691 100644 --- a/src/attributes/types/AttributeValueUpdate.ts +++ b/src/attributes/types/AttributeValueUpdate.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType } from "./../../types/globalTypes"; +import { AttributeValueCreateInput, AttributeInputTypeEnum, AttributeValueType, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: AttributeValueUpdate // ==================================================== -export interface AttributeValueUpdate_attributeValueUpdate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface AttributeValueUpdate_attributeValueUpdate_attribute_values { __typename: "AttributeValue"; id: string; @@ -37,10 +31,16 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute { values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null; } +export interface AttributeValueUpdate_attributeValueUpdate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface AttributeValueUpdate_attributeValueUpdate { __typename: "AttributeValueUpdate"; - errors: AttributeValueUpdate_attributeValueUpdate_errors[]; attribute: AttributeValueUpdate_attributeValueUpdate_attribute | null; + errors: AttributeValueUpdate_attributeValueUpdate_errors[]; } export interface AttributeValueUpdate { diff --git a/src/attributes/types/ProductErrorFragment.ts b/src/attributes/types/ProductErrorFragment.ts new file mode 100644 index 000000000..2f4d1fccb --- /dev/null +++ b/src/attributes/types/ProductErrorFragment.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ProductErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ProductErrorFragment +// ==================================================== + +export interface ProductErrorFragment { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} diff --git a/src/attributes/views/AttributeCreate/AttributeCreate.tsx b/src/attributes/views/AttributeCreate/AttributeCreate.tsx index cf700c858..7ece9730e 100644 --- a/src/attributes/views/AttributeCreate/AttributeCreate.tsx +++ b/src/attributes/views/AttributeCreate/AttributeCreate.tsx @@ -5,7 +5,7 @@ import slugify from "slugify"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { maybe } from "@saleor/misc"; -import { ReorderEvent, UserError } from "@saleor/types"; +import { ReorderEvent } from "@saleor/types"; import { add, isSelected, @@ -14,6 +14,8 @@ import { updateAtIndex } from "@saleor/utils/lists"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; +import { ProductErrorCode } from "@saleor/types/globalTypes"; import AttributePage from "../../components/AttributePage"; import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; import AttributeValueEditDialog, { @@ -33,6 +35,12 @@ interface AttributeDetailsProps { params: AttributeAddUrlQueryParams; } +const attributeValueAlreadyExistsError: ProductErrorFragment = { + __typename: "ProductError", + code: ProductErrorCode.ALREADY_EXISTS, + field: "name" +}; + function areValuesEqual( a: AttributeValueEditDialogFormData, b: AttributeValueEditDialogFormData @@ -48,7 +56,9 @@ const AttributeDetails: React.FC = ({ params }) => { const [values, setValues] = React.useState< AttributeValueEditDialogFormData[] >([]); - const [valueErrors, setValueErrors] = React.useState([]); + const [valueErrors, setValueErrors] = React.useState( + [] + ); const id = params.id ? parseInt(params.id, 0) : undefined; @@ -57,6 +67,8 @@ const AttributeDetails: React.FC = ({ params }) => { AttributeAddUrlQueryParams >(navigate, attributeAddUrl, params); + React.useEffect(() => setValueErrors([]), [params.action]); + const handleValueDelete = () => { setValues(remove(values[params.id], values, areValuesEqual)); closeModal(); @@ -73,20 +85,7 @@ const AttributeDetails: React.FC = ({ params }) => { }; const handleValueUpdate = (input: AttributeValueEditDialogFormData) => { if (isSelected(input, values, areValuesEqual)) { - setValueErrors([ - { - field: "name", - message: intl.formatMessage( - { - defaultMessage: "A value named {name} already exists", - description: "attribute value edit error" - }, - { - name: input.name - } - ) - } - ]); + setValueErrors([attributeValueAlreadyExistsError]); } else { setValues(updateAtIndex(input, values, id)); closeModal(); @@ -94,20 +93,7 @@ const AttributeDetails: React.FC = ({ params }) => { }; const handleValueCreate = (input: AttributeValueEditDialogFormData) => { if (isSelected(input, values, areValuesEqual)) { - setValueErrors([ - { - field: "name", - message: intl.formatMessage( - { - defaultMessage: "A value named {name} already exists", - description: "attribute value edit error" - }, - { - name: input.name - } - ) - } - ]); + setValueErrors([attributeValueAlreadyExistsError]); } else { setValues(add(input, values)); closeModal(); @@ -123,10 +109,7 @@ const AttributeDetails: React.FC = ({ params }) => { attributeCreateOpts.data.attributeCreate.errors, - [] - )} + errors={attributeCreateOpts.data?.attributeCreate.errors || []} onBack={() => navigate(attributeListUrl())} onDelete={undefined} onSubmit={input => diff --git a/src/attributes/views/AttributeDetails/AttributeDetails.tsx b/src/attributes/views/AttributeDetails/AttributeDetails.tsx index 3dcc9c8a4..2c00bb10d 100644 --- a/src/attributes/views/AttributeDetails/AttributeDetails.tsx +++ b/src/attributes/views/AttributeDetails/AttributeDetails.tsx @@ -8,6 +8,7 @@ import { maybe } from "@saleor/misc"; import { ReorderEvent } from "@saleor/types"; import { move } from "@saleor/utils/lists"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; +import { getProductErrorMessage } from "@saleor/utils/errors"; import AttributeDeleteDialog from "../../components/AttributeDeleteDialog"; import AttributePage from "../../components/AttributePage"; import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; @@ -95,7 +96,10 @@ const AttributeDetails: React.FC = ({ id, params }) => { const handleValueReorderMutation = (data: AttributeValueReorder) => { if (data.attributeReorderValues.errors.length !== 0) { notify({ - text: data.attributeReorderValues.errors[0].message + text: getProductErrorMessage( + data.attributeReorderValues.errors[0], + intl + ) }); } }; @@ -155,12 +159,10 @@ const AttributeDetails: React.FC = ({ id, params }) => { data.attribute)} disabled={loading} - errors={maybe( - () => - attributeUpdateOpts.data - .attributeUpdate.errors, - [] - )} + errors={ + attributeUpdateOpts.data + ?.attributeUpdate.errors || [] + } onBack={() => navigate(attributeListUrl()) } @@ -253,12 +255,10 @@ const AttributeDetails: React.FC = ({ id, params }) => { attributeValueCreateOpts.status } disabled={loading} - errors={maybe( - () => - attributeValueCreateOpts.data - .attributeValueCreate.errors, - [] - )} + errors={ + attributeValueCreateOpts.data + ?.attributeValueCreate.errors || [] + } open={params.action === "add-value"} onClose={closeModal} onSubmit={input => @@ -280,12 +280,10 @@ const AttributeDetails: React.FC = ({ id, params }) => { attributeValueUpdateOpts.status } disabled={loading} - errors={maybe( - () => - attributeValueUpdateOpts.data - .attributeValueUpdate.errors, - [] - )} + errors={ + attributeValueUpdateOpts.data + ?.attributeValueUpdate.errors || [] + } open={params.action === "edit-value"} onClose={closeModal} onSubmit={input => diff --git a/src/auth/mutations.ts b/src/auth/mutations.ts index 5138d6c2f..60ca7b91b 100644 --- a/src/auth/mutations.ts +++ b/src/auth/mutations.ts @@ -1,5 +1,6 @@ import gql from "graphql-tag"; +import { accountFragmentError } from "@saleor/customers/mutations"; import { TypedMutation } from "../mutations"; import { RequestPasswordReset, @@ -64,11 +65,11 @@ export const TypedVerifyTokenMutation = TypedMutation< >(tokenVerifyMutation); export const requestPasswordReset = gql` + ${accountFragmentError} mutation RequestPasswordReset($email: String!, $redirectUrl: String!) { requestPasswordReset(email: $email, redirectUrl: $redirectUrl) { - errors { - field - message + errors: accountErrors { + ...AccountErrorFragment } } } @@ -79,14 +80,14 @@ export const RequestPasswordResetMutation = TypedMutation< >(requestPasswordReset); export const setPassword = gql` + ${accountFragmentError} ${fragmentUser} mutation SetPassword($email: String!, $password: String!, $token: String!) { setPassword(email: $email, password: $password, token: $token) { - token - errors { - field - message + errors: accountErrors { + ...AccountErrorFragment } + token user { ...User } diff --git a/src/auth/types/RequestPasswordReset.ts b/src/auth/types/RequestPasswordReset.ts index c011b0794..87ba8684a 100644 --- a/src/auth/types/RequestPasswordReset.ts +++ b/src/auth/types/RequestPasswordReset.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { AccountErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: RequestPasswordReset // ==================================================== export interface RequestPasswordReset_requestPasswordReset_errors { - __typename: "Error"; + __typename: "AccountError"; + code: AccountErrorCode; field: string | null; - message: string | null; } export interface RequestPasswordReset_requestPasswordReset { diff --git a/src/auth/types/SetPassword.ts b/src/auth/types/SetPassword.ts index 9a4c04509..805728d35 100644 --- a/src/auth/types/SetPassword.ts +++ b/src/auth/types/SetPassword.ts @@ -2,16 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { PermissionEnum } from "./../../types/globalTypes"; +import { AccountErrorCode, PermissionEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: SetPassword // ==================================================== export interface SetPassword_setPassword_errors { - __typename: "Error"; + __typename: "AccountError"; + code: AccountErrorCode; field: string | null; - message: string | null; } export interface SetPassword_setPassword_user_permissions { @@ -37,8 +37,8 @@ export interface SetPassword_setPassword_user { export interface SetPassword_setPassword { __typename: "SetPassword"; + errors: SetPassword_setPassword_errors[] | null; token: string | null; - errors: (SetPassword_setPassword_errors | null)[]; user: SetPassword_setPassword_user | null; } diff --git a/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx b/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx index 7f7036066..a47266f62 100644 --- a/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx +++ b/src/categories/components/CategoryCreatePage/CategoryCreatePage.tsx @@ -11,7 +11,7 @@ import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SeoForm from "@saleor/components/SeoForm"; import { sectionNames } from "@saleor/intl"; -import { UserError } from "../../../types"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import CategoryDetailsForm from "../../components/CategoryDetailsForm"; interface FormData { @@ -29,7 +29,7 @@ const initialData: FormData = { }; export interface CategoryCreatePageProps { - errors: UserError[]; + errors: ProductErrorFragment[]; disabled: boolean; saveButtonBarState: ConfirmButtonTransitionState; onSubmit(data: FormData); diff --git a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx index 5ffcefab8..615667541 100644 --- a/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx +++ b/src/categories/components/CategoryDetailsForm/CategoryDetailsForm.tsx @@ -9,8 +9,8 @@ import CardTitle from "@saleor/components/CardTitle"; import FormSpacer from "@saleor/components/FormSpacer"; import RichTextEditor from "@saleor/components/RichTextEditor"; import { commonMessages } from "@saleor/intl"; -import { UserError } from "@saleor/types"; -import { getFieldError } from "@saleor/utils/errors"; +import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { maybe } from "../../../misc"; import { CategoryDetails_category } from "../../types/CategoryDetails"; @@ -21,7 +21,7 @@ interface CategoryDetailsFormProps { description: RawDraftContentState; }; disabled: boolean; - errors: UserError[]; + errors: ProductErrorFragment[]; onChange: (event: React.ChangeEvent) => void; } @@ -34,6 +34,8 @@ export const CategoryDetailsForm: React.FC = ({ }) => { const intl = useIntl(); + const formErrors = getFormErrors(["name", "descriptionJson"], errors); + return ( = ({ disabled={disabled} value={data && data.name} onChange={onChange} - error={!!getFieldError(errors, "name")} - helperText={getFieldError(errors, "name")?.message} + error={!!formErrors.name} + helperText={getProductErrorMessage(formErrors.name, intl)} fullWidth /> { changeTab: (index: CategoryPageTab) => void; currentTab: CategoryPageTab; - errors: UserError[]; + errors: ProductErrorFragment[]; disabled: boolean; category: CategoryDetails_category; products: CategoryDetails_category_products_edges_node[]; diff --git a/src/categories/mutations.ts b/src/categories/mutations.ts index 7995b3c6f..a228faa8c 100644 --- a/src/categories/mutations.ts +++ b/src/categories/mutations.ts @@ -1,6 +1,7 @@ import gql from "graphql-tag"; import makeMutation from "@saleor/hooks/makeMutation"; +import { productErrorFragment } from "@saleor/attributes/mutations"; import { categoryDetailsFragment } from "./queries"; import { CategoryBulkDelete, @@ -20,11 +21,11 @@ import { } from "./types/CategoryUpdate"; export const categoryDeleteMutation = gql` + ${productErrorFragment} mutation CategoryDelete($id: ID!) { categoryDelete(id: $id) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } @@ -36,15 +37,15 @@ export const useCategoryDeleteMutation = makeMutation< export const categoryCreateMutation = gql` ${categoryDetailsFragment} + ${productErrorFragment} mutation CategoryCreate($parent: ID, $input: CategoryInput!) { categoryCreate(parent: $parent, input: $input) { - errors { - field - message - } category { ...CategoryDetailsFragment } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -55,15 +56,15 @@ export const useCategoryCreateMutation = makeMutation< export const categoryUpdateMutation = gql` ${categoryDetailsFragment} + ${productErrorFragment} mutation CategoryUpdate($id: ID!, $input: CategoryInput!) { categoryUpdate(id: $id, input: $input) { - errors { - field - message - } category { ...CategoryDetailsFragment } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -73,11 +74,11 @@ export const useCategoryUpdateMutation = makeMutation< >(categoryUpdateMutation); export const categoryBulkDeleteMutation = gql` + ${productErrorFragment} mutation CategoryBulkDelete($ids: [ID]!) { categoryBulkDelete(ids: $ids) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } diff --git a/src/categories/types/CategoryBulkDelete.ts b/src/categories/types/CategoryBulkDelete.ts index a5e6d54a9..9009acdcd 100644 --- a/src/categories/types/CategoryBulkDelete.ts +++ b/src/categories/types/CategoryBulkDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: CategoryBulkDelete // ==================================================== export interface CategoryBulkDelete_categoryBulkDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface CategoryBulkDelete_categoryBulkDelete { diff --git a/src/categories/types/CategoryCreate.ts b/src/categories/types/CategoryCreate.ts index 496872e41..0bcf34a74 100644 --- a/src/categories/types/CategoryCreate.ts +++ b/src/categories/types/CategoryCreate.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CategoryInput } from "./../../types/globalTypes"; +import { CategoryInput, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CategoryCreate // ==================================================== -export interface CategoryCreate_categoryCreate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface CategoryCreate_categoryCreate_category_backgroundImage { __typename: "Image"; alt: string | null; @@ -36,10 +30,16 @@ export interface CategoryCreate_categoryCreate_category { parent: CategoryCreate_categoryCreate_category_parent | null; } +export interface CategoryCreate_categoryCreate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface CategoryCreate_categoryCreate { __typename: "CategoryCreate"; - errors: CategoryCreate_categoryCreate_errors[]; category: CategoryCreate_categoryCreate_category | null; + errors: CategoryCreate_categoryCreate_errors[]; } export interface CategoryCreate { diff --git a/src/categories/types/CategoryDelete.ts b/src/categories/types/CategoryDelete.ts index 79634e78d..0b7fda57d 100644 --- a/src/categories/types/CategoryDelete.ts +++ b/src/categories/types/CategoryDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: CategoryDelete // ==================================================== export interface CategoryDelete_categoryDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface CategoryDelete_categoryDelete { diff --git a/src/categories/types/CategoryUpdate.ts b/src/categories/types/CategoryUpdate.ts index a4621ee45..8b04c1583 100644 --- a/src/categories/types/CategoryUpdate.ts +++ b/src/categories/types/CategoryUpdate.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CategoryInput } from "./../../types/globalTypes"; +import { CategoryInput, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CategoryUpdate // ==================================================== -export interface CategoryUpdate_categoryUpdate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface CategoryUpdate_categoryUpdate_category_backgroundImage { __typename: "Image"; alt: string | null; @@ -36,10 +30,16 @@ export interface CategoryUpdate_categoryUpdate_category { parent: CategoryUpdate_categoryUpdate_category_parent | null; } +export interface CategoryUpdate_categoryUpdate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface CategoryUpdate_categoryUpdate { __typename: "CategoryUpdate"; - errors: CategoryUpdate_categoryUpdate_errors[]; category: CategoryUpdate_categoryUpdate_category | null; + errors: CategoryUpdate_categoryUpdate_errors[]; } export interface CategoryUpdate { diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index 812b4dc7d..e33d91b96 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -99,7 +99,7 @@ export const CategoryDetails: React.FC = ({ ); if (backgroundImageError) { notify({ - text: backgroundImageError.message + text: intl.formatMessage(commonMessages.somethingWentWrong) }); } } diff --git a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx index c7680bbcc..c6a3acabb 100644 --- a/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx +++ b/src/collections/components/CollectionCreatePage/CollectionCreatePage.tsx @@ -17,7 +17,7 @@ import SeoForm from "@saleor/components/SeoForm"; import VisibilityCard from "@saleor/components/VisibilityCard"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; import { commonMessages, sectionNames } from "@saleor/intl"; -import { UserError } from "../../../types"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import CollectionDetails from "../CollectionDetails/CollectionDetails"; import { CollectionImage } from "../CollectionImage/CollectionImage"; @@ -37,7 +37,7 @@ export interface CollectionCreatePageFormData { export interface CollectionCreatePageProps { disabled: boolean; - errors: UserError[]; + errors: ProductErrorFragment[]; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; onSubmit: (data: CollectionCreatePageFormData) => void; diff --git a/src/collections/components/CollectionDetails/CollectionDetails.tsx b/src/collections/components/CollectionDetails/CollectionDetails.tsx index 5c219335c..c63bc1dea 100644 --- a/src/collections/components/CollectionDetails/CollectionDetails.tsx +++ b/src/collections/components/CollectionDetails/CollectionDetails.tsx @@ -10,8 +10,8 @@ import FormSpacer from "@saleor/components/FormSpacer"; import RichTextEditor from "@saleor/components/RichTextEditor"; import { commonMessages } from "@saleor/intl"; import { maybe } from "@saleor/misc"; -import { UserError } from "@saleor/types"; -import { getFieldError } from "@saleor/utils/errors"; +import { getProductErrorMessage, getFormErrors } from "@saleor/utils/errors"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { CollectionDetails_collection } from "../../types/CollectionDetails"; export interface CollectionDetailsProps { @@ -21,7 +21,7 @@ export interface CollectionDetailsProps { name: string; }; disabled: boolean; - errors: UserError[]; + errors: ProductErrorFragment[]; onChange: (event: React.ChangeEvent) => void; } @@ -34,6 +34,8 @@ const CollectionDetails: React.FC = ({ }) => { const intl = useIntl(); + const formErrors = getFormErrors(["name", "descriptionJson"], errors); + return ( = ({ disabled={disabled} value={data.name} onChange={onChange} - error={!!getFieldError(errors, "name")} - helperText={getFieldError(errors, "name")?.message} + error={!!formErrors.name} + helperText={getProductErrorMessage(formErrors.name, intl)} fullWidth /> JSON.parse(collection.descriptionJson))} label={intl.formatMessage(commonMessages.description)} name="description" diff --git a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx index 3d81c31e4..106ba3463 100644 --- a/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx +++ b/src/collections/components/CollectionDetailsPage/CollectionDetailsPage.tsx @@ -17,8 +17,9 @@ import SeoForm from "@saleor/components/SeoForm"; import VisibilityCard from "@saleor/components/VisibilityCard"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; import { sectionNames } from "@saleor/intl"; +import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment"; import { maybe } from "../../../misc"; -import { ListActions, PageListProps, UserError } from "../../../types"; +import { ListActions, PageListProps } from "../../../types"; import { CollectionDetails_collection } from "../../types/CollectionDetails"; import CollectionDetails from "../CollectionDetails/CollectionDetails"; import { CollectionImage } from "../CollectionImage/CollectionImage"; @@ -37,7 +38,7 @@ export interface CollectionDetailsPageFormData { export interface CollectionDetailsPageProps extends PageListProps, ListActions { collection: CollectionDetails_collection; - errors: UserError[]; + errors: ProductErrorFragment[]; isFeatured: boolean; saveButtonBarState: ConfirmButtonTransitionState; onBack: () => void; diff --git a/src/collections/containers/CollectionOperations.tsx b/src/collections/containers/CollectionOperations.tsx index fd40ff642..9a65c37f1 100644 --- a/src/collections/containers/CollectionOperations.tsx +++ b/src/collections/containers/CollectionOperations.tsx @@ -54,6 +54,7 @@ interface CollectionUpdateOperationsProps { >; }) => React.ReactNode; onUpdate: (data: CollectionUpdate) => void; + onUpdateWithCollection: (data: CollectionUpdateWithHomepage) => void; onProductAssign: (data: CollectionAssignProduct) => void; onProductUnassign: (data: UnassignCollectionProduct) => void; onRemove: (data: RemoveCollection) => void; @@ -62,6 +63,7 @@ interface CollectionUpdateOperationsProps { const CollectionOperations: React.FC = ({ children, onUpdate, + onUpdateWithCollection, onProductAssign, onProductUnassign, onRemove @@ -72,7 +74,9 @@ const CollectionOperations: React.FC = ({ {(...removeCollection) => ( {(...assignProduct) => ( - + {(...updateWithHomepage) => ( (createCollection); const removeCollection = gql` + ${productErrorFragment} mutation RemoveCollection($id: ID!) { collectionDelete(id: $id) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } @@ -165,6 +173,7 @@ export const TypedCollectionRemoveMutation = TypedMutation< >(removeCollection); const unassignCollectionProduct = gql` + ${productErrorFragment} mutation UnassignCollectionProduct( $collectionId: ID! $productIds: [ID]! @@ -177,10 +186,6 @@ const unassignCollectionProduct = gql` collectionId: $collectionId products: $productIds ) { - errors { - field - message - } collection { id products(first: $first, after: $after, before: $before, last: $last) { @@ -206,6 +211,9 @@ const unassignCollectionProduct = gql` } } } + errors: productErrors { + ...ProductErrorFragment + } } } `; @@ -215,11 +223,11 @@ export const TypedUnassignCollectionProductMutation = TypedMutation< >(unassignCollectionProduct); const collectionBulkDelete = gql` + ${productErrorFragment} mutation CollectionBulkDelete($ids: [ID]!) { collectionBulkDelete(ids: $ids) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } @@ -230,11 +238,11 @@ export const TypedCollectionBulkDelete = TypedMutation< >(collectionBulkDelete); const collectionBulkPublish = gql` + ${productErrorFragment} mutation CollectionBulkPublish($ids: [ID]!, $isPublished: Boolean!) { collectionBulkPublish(ids: $ids, isPublished: $isPublished) { - errors { - field - message + errors: productErrors { + ...ProductErrorFragment } } } diff --git a/src/collections/types/CollectionAssignProduct.ts b/src/collections/types/CollectionAssignProduct.ts index 0fbfa3103..a2f5dce38 100644 --- a/src/collections/types/CollectionAssignProduct.ts +++ b/src/collections/types/CollectionAssignProduct.ts @@ -2,16 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: CollectionAssignProduct // ==================================================== -export interface CollectionAssignProduct_collectionAddProducts_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_productType { __typename: "ProductType"; id: string; @@ -57,10 +53,16 @@ export interface CollectionAssignProduct_collectionAddProducts_collection { products: CollectionAssignProduct_collectionAddProducts_collection_products | null; } +export interface CollectionAssignProduct_collectionAddProducts_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface CollectionAssignProduct_collectionAddProducts { __typename: "CollectionAddProducts"; - errors: CollectionAssignProduct_collectionAddProducts_errors[]; collection: CollectionAssignProduct_collectionAddProducts_collection | null; + errors: CollectionAssignProduct_collectionAddProducts_errors[]; } export interface CollectionAssignProduct { diff --git a/src/collections/types/CollectionBulkDelete.ts b/src/collections/types/CollectionBulkDelete.ts index 84254bc4f..0214b943f 100644 --- a/src/collections/types/CollectionBulkDelete.ts +++ b/src/collections/types/CollectionBulkDelete.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: CollectionBulkDelete // ==================================================== export interface CollectionBulkDelete_collectionBulkDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface CollectionBulkDelete_collectionBulkDelete { diff --git a/src/collections/types/CollectionBulkPublish.ts b/src/collections/types/CollectionBulkPublish.ts index 24739b592..542a2eaa5 100644 --- a/src/collections/types/CollectionBulkPublish.ts +++ b/src/collections/types/CollectionBulkPublish.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: CollectionBulkPublish // ==================================================== export interface CollectionBulkPublish_collectionBulkPublish_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface CollectionBulkPublish_collectionBulkPublish { diff --git a/src/collections/types/CollectionUpdate.ts b/src/collections/types/CollectionUpdate.ts index 95f216696..42f43cfd5 100644 --- a/src/collections/types/CollectionUpdate.ts +++ b/src/collections/types/CollectionUpdate.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CollectionInput } from "./../../types/globalTypes"; +import { CollectionInput, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CollectionUpdate // ==================================================== -export interface CollectionUpdate_collectionUpdate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface CollectionUpdate_collectionUpdate_collection_backgroundImage { __typename: "Image"; alt: string | null; @@ -32,10 +26,16 @@ export interface CollectionUpdate_collectionUpdate_collection { seoTitle: string | null; } +export interface CollectionUpdate_collectionUpdate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface CollectionUpdate_collectionUpdate { __typename: "CollectionUpdate"; - errors: CollectionUpdate_collectionUpdate_errors[]; collection: CollectionUpdate_collectionUpdate_collection | null; + errors: CollectionUpdate_collectionUpdate_errors[]; } export interface CollectionUpdate { diff --git a/src/collections/types/CollectionUpdateWithHomepage.ts b/src/collections/types/CollectionUpdateWithHomepage.ts index 71c2fd396..a51f43b1e 100644 --- a/src/collections/types/CollectionUpdateWithHomepage.ts +++ b/src/collections/types/CollectionUpdateWithHomepage.ts @@ -2,16 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CollectionInput } from "./../../types/globalTypes"; +import { CollectionInput, ShopErrorCode, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CollectionUpdateWithHomepage // ==================================================== export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_errors { - __typename: "Error"; + __typename: "ShopError"; + code: ShopErrorCode; field: string | null; - message: string | null; } export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_shop_homepageCollection { @@ -30,12 +30,6 @@ export interface CollectionUpdateWithHomepage_homepageCollectionUpdate { shop: CollectionUpdateWithHomepage_homepageCollectionUpdate_shop | null; } -export interface CollectionUpdateWithHomepage_collectionUpdate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface CollectionUpdateWithHomepage_collectionUpdate_collection_backgroundImage { __typename: "Image"; alt: string | null; @@ -54,10 +48,16 @@ export interface CollectionUpdateWithHomepage_collectionUpdate_collection { seoTitle: string | null; } +export interface CollectionUpdateWithHomepage_collectionUpdate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface CollectionUpdateWithHomepage_collectionUpdate { __typename: "CollectionUpdate"; - errors: CollectionUpdateWithHomepage_collectionUpdate_errors[]; collection: CollectionUpdateWithHomepage_collectionUpdate_collection | null; + errors: CollectionUpdateWithHomepage_collectionUpdate_errors[]; } export interface CollectionUpdateWithHomepage { diff --git a/src/collections/types/CreateCollection.ts b/src/collections/types/CreateCollection.ts index de5dc26e9..2a5ea5d9d 100644 --- a/src/collections/types/CreateCollection.ts +++ b/src/collections/types/CreateCollection.ts @@ -2,18 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { CollectionCreateInput } from "./../../types/globalTypes"; +import { CollectionCreateInput, ProductErrorCode } from "./../../types/globalTypes"; // ==================================================== // GraphQL mutation operation: CreateCollection // ==================================================== -export interface CreateCollection_collectionCreate_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface CreateCollection_collectionCreate_collection_backgroundImage { __typename: "Image"; alt: string | null; @@ -32,10 +26,16 @@ export interface CreateCollection_collectionCreate_collection { seoTitle: string | null; } +export interface CreateCollection_collectionCreate_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface CreateCollection_collectionCreate { __typename: "CollectionCreate"; - errors: CreateCollection_collectionCreate_errors[]; collection: CreateCollection_collectionCreate_collection | null; + errors: CreateCollection_collectionCreate_errors[]; } export interface CreateCollection { diff --git a/src/collections/types/RemoveCollection.ts b/src/collections/types/RemoveCollection.ts index 878406a25..588349acb 100644 --- a/src/collections/types/RemoveCollection.ts +++ b/src/collections/types/RemoveCollection.ts @@ -2,14 +2,16 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: RemoveCollection // ==================================================== export interface RemoveCollection_collectionDelete_errors { - __typename: "Error"; + __typename: "ProductError"; + code: ProductErrorCode; field: string | null; - message: string | null; } export interface RemoveCollection_collectionDelete { diff --git a/src/collections/types/ShopErrorFragment.ts b/src/collections/types/ShopErrorFragment.ts new file mode 100644 index 000000000..ae705f05f --- /dev/null +++ b/src/collections/types/ShopErrorFragment.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ShopErrorCode } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL fragment: ShopErrorFragment +// ==================================================== + +export interface ShopErrorFragment { + __typename: "ShopError"; + code: ShopErrorCode; + field: string | null; +} diff --git a/src/collections/types/UnassignCollectionProduct.ts b/src/collections/types/UnassignCollectionProduct.ts index 054ca97f3..7104a9c85 100644 --- a/src/collections/types/UnassignCollectionProduct.ts +++ b/src/collections/types/UnassignCollectionProduct.ts @@ -2,16 +2,12 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. +import { ProductErrorCode } from "./../../types/globalTypes"; + // ==================================================== // GraphQL mutation operation: UnassignCollectionProduct // ==================================================== -export interface UnassignCollectionProduct_collectionRemoveProducts_errors { - __typename: "Error"; - field: string | null; - message: string | null; -} - export interface UnassignCollectionProduct_collectionRemoveProducts_collection_products_edges_node_productType { __typename: "ProductType"; id: string; @@ -57,10 +53,16 @@ export interface UnassignCollectionProduct_collectionRemoveProducts_collection { products: UnassignCollectionProduct_collectionRemoveProducts_collection_products | null; } +export interface UnassignCollectionProduct_collectionRemoveProducts_errors { + __typename: "ProductError"; + code: ProductErrorCode; + field: string | null; +} + export interface UnassignCollectionProduct_collectionRemoveProducts { __typename: "CollectionRemoveProducts"; - errors: UnassignCollectionProduct_collectionRemoveProducts_errors[]; collection: UnassignCollectionProduct_collectionRemoveProducts_collection | null; + errors: UnassignCollectionProduct_collectionRemoveProducts_errors[]; } export interface UnassignCollectionProduct { diff --git a/src/collections/views/CollectionCreate.tsx b/src/collections/views/CollectionCreate.tsx index 8db1fcda5..345243306 100644 --- a/src/collections/views/CollectionCreate.tsx +++ b/src/collections/views/CollectionCreate.tsx @@ -4,7 +4,7 @@ import { useIntl } from "react-intl"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; -import { maybe } from "../../misc"; +import { commonMessages } from "@saleor/intl"; import { CollectionCreateInput } from "../../types/globalTypes"; import CollectionCreatePage from "../components/CollectionCreatePage/CollectionCreatePage"; import { TypedCollectionCreateMutation } from "../mutations"; @@ -19,9 +19,7 @@ export const CollectionCreate: React.FC = () => { const handleCollectionCreateSuccess = (data: CreateCollection) => { if (data.collectionCreate.errors.length === 0) { notify({ - text: intl.formatMessage({ - defaultMessage: "Created collection" - }) + text: intl.formatMessage(commonMessages.savedChanges) }); navigate(collectionUrl(data.collectionCreate.collection.id)); } else { @@ -31,7 +29,7 @@ export const CollectionCreate: React.FC = () => { ); if (backgroundImageError) { notify({ - text: backgroundImageError.message + text: intl.formatMessage(commonMessages.somethingWentWrong) }); } } @@ -47,10 +45,7 @@ export const CollectionCreate: React.FC = () => { })} /> createCollectionOpts.data.collectionCreate.errors, - [] - )} + errors={createCollectionOpts.data?.collectionCreate.errors || []} onBack={() => navigate(collectionListUrl())} disabled={createCollectionOpts.loading} onSubmit={formData => diff --git a/src/collections/views/CollectionDetails.tsx b/src/collections/views/CollectionDetails.tsx index ed11232cb..f5cbe6b93 100644 --- a/src/collections/views/CollectionDetails.tsx +++ b/src/collections/views/CollectionDetails.tsx @@ -35,6 +35,7 @@ import { CollectionUrlQueryParams, CollectionUrlDialog } from "../urls"; +import { CollectionUpdateWithHomepage } from "../types/CollectionUpdateWithHomepage"; interface CollectionDetailsProps { id: string; @@ -88,11 +89,18 @@ export const CollectionDetails: React.FC = ({ ); if (backgroundImageError) { notify({ - text: backgroundImageError.message + text: intl.formatMessage(commonMessages.somethingWentWrong) }); } } }; + const handleCollectioUpdateWithHomepage = ( + data: CollectionUpdateWithHomepage + ) => { + if (data.homepageCollectionUpdate.errors.length === 0) { + handleCollectionUpdate(data); + } + }; const handleProductAssign = (data: CollectionAssignProduct) => { if (data.collectionAddProducts.errors.length === 0) { @@ -130,6 +138,7 @@ export const CollectionDetails: React.FC = ({ return ( = ({ onAdd={() => openModal("assign")} onBack={handleBack} disabled={loading} - collection={maybe(() => data.collection)} + collection={data?.collection} errors={ updateCollection.opts?.data?.collectionUpdate.errors || [] } diff --git a/src/components/AddressEdit/AddressEdit.tsx b/src/components/AddressEdit/AddressEdit.tsx index 96ce090c1..041bbd99d 100644 --- a/src/components/AddressEdit/AddressEdit.tsx +++ b/src/components/AddressEdit/AddressEdit.tsx @@ -1,12 +1,15 @@ import { makeStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; import React from "react"; -import { useIntl } from "react-intl"; +import { useIntl, IntlShape } from "react-intl"; import { AddressTypeInput } from "@saleor/customers/types"; import { commonMessages } from "@saleor/intl"; -import { UserError } from "@saleor/types"; -import { getFieldError } from "@saleor/utils/errors"; +import { getFormErrors } from "@saleor/utils/errors"; +import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment"; +import getAccountErrorMessage from "@saleor/utils/errors/account"; +import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment"; +import getOrderErrorMessage from "@saleor/utils/errors/order"; import FormSpacer from "../FormSpacer"; import SingleAutocompleteSelectField, { SingleAutocompleteChoiceType @@ -28,11 +31,22 @@ interface AddressEditProps { countryDisplayValue: string; data: AddressTypeInput; disabled?: boolean; - errors: UserError[]; + errors: Array; onChange(event: React.ChangeEvent); onCountryChange(event: React.ChangeEvent); } +function getErrorMessage( + err: AccountErrorFragment | OrderErrorFragment, + intl: IntlShape +): string { + if (err?.__typename === "AccountError") { + return getAccountErrorMessage(err, intl); + } + + return getOrderErrorMessage(err, intl); +} + const AddressEdit: React.FC = props => { const { countries, @@ -43,18 +57,36 @@ const AddressEdit: React.FC = props => { onChange, onCountryChange } = props; - const classes = useStyles(props); + const classes = useStyles(props); const intl = useIntl(); + const formFields: Array = [ + "city", + "cityArea", + "country", + "countryArea", + "firstName", + "lastName", + "companyName", + "phone", + "postalCode", + "streetAddress1", + "streetAddress2" + ]; + const formErrors = getFormErrors< + keyof AddressTypeInput, + AccountErrorFragment | OrderErrorFragment + >(formFields, errors); + return ( <>
= props => {
= props => {
= props => {
= props => { = props => { = props => {
= props => {
= props => { = props => {
= props => { + - - - - -
-); +}) => { + const intl = useIntl(); + + return ( + + + + + + + + + {errors.length > 0 && ( + <> + + {errors.map(err => ( + + {getOrderErrorMessage(err, intl)} + + ))} + + )} + + + + + + + + + ); +}; OrderPaymentVoidDialog.displayName = "OrderPaymentVoidDialog"; export default OrderPaymentVoidDialog; diff --git a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx index 14a4f77b0..69f097112 100644 --- a/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx +++ b/src/orders/components/OrderProductAddDialog/OrderProductAddDialog.tsx @@ -3,6 +3,7 @@ import CircularProgress from "@material-ui/core/CircularProgress"; import Dialog from "@material-ui/core/Dialog"; import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; +import DialogContentText from "@material-ui/core/DialogContentText"; import DialogTitle from "@material-ui/core/DialogTitle"; import { makeStyles } from "@material-ui/core/styles"; import TableBody from "@material-ui/core/TableBody"; @@ -24,6 +25,10 @@ import useSearchQuery from "@saleor/hooks/useSearchQuery"; import { buttonMessages } from "@saleor/intl"; import { maybe, renderCollection } from "@saleor/misc"; import { FetchMoreProps } from "@saleor/types"; +import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment"; +import getOrderErrorMessage from "@saleor/utils/errors/order"; +import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; +import FormSpacer from "@saleor/components/FormSpacer"; import { SearchOrderVariant_search_edges_node, SearchOrderVariant_search_edges_node_variants @@ -50,7 +55,8 @@ const useStyles = makeStyles( alignItems: "center", display: "flex", height: theme.spacing(3), - justifyContent: "center" + justifyContent: "center", + marginTop: theme.spacing(3) }, overflow: { overflowY: "visible" @@ -79,8 +85,9 @@ type SetVariantsAction = ( data: SearchOrderVariant_search_edges_node_variants[] ) => void; -interface OrderProductAddDialogProps extends FetchMoreProps { +export interface OrderProductAddDialogProps extends FetchMoreProps { confirmButtonState: ConfirmButtonTransitionState; + errors: OrderErrorFragment[]; open: boolean; products: SearchOrderVariant_search_edges_node[]; onClose: () => void; @@ -154,6 +161,7 @@ const onVariantAdd = ( const OrderProductAddDialog: React.FC = props => { const { confirmButtonState, + errors: apiErrors, open, loading, hasMore, @@ -163,13 +171,14 @@ const OrderProductAddDialog: React.FC = props => { onClose, onSubmit } = props; - const classes = useStyles(props); + const classes = useStyles(props); const intl = useIntl(); const [query, onQueryChange] = useSearchQuery(onFetch); const [variants, setVariants] = React.useState< SearchOrderVariant_search_edges_node_variants[] >([]); + const errors = useModalDialogErrors(apiErrors, open); const selectedVariantsToProductsMap = products ? products.map(product => @@ -323,6 +332,16 @@ const OrderProductAddDialog: React.FC = props => { + {errors.length > 0 && ( + <> + + {errors.map(err => ( + + {getOrderErrorMessage(err, intl)} + + ))} + + )} + + + + + + +`; + exports[`Storyshots Generics / Multiple select with autocomplete interactive with load more 1`] = `
`; +exports[`Storyshots Orders / OrderCancelDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderCustomer default 1`] = `
`; +exports[`Storyshots Orders / OrderDraftCancelDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderDraftFinalizeDialog default 1`] = `
`; +exports[`Storyshots Orders / OrderDraftFinalizeDialog with errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderDraftFinalizeDialog with warnings 1`] = `
`; +exports[`Storyshots Orders / OrderFulfillmentCancelDialog error 1`] = ` +
+`; + exports[`Storyshots Orders / OrderFulfillmentDialog default 1`] = `
`; +exports[`Storyshots Orders / OrderFulfillmentDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderFulfillmentTrackingDialog default 1`] = `
`; +exports[`Storyshots Orders / OrderFulfillmentTrackingDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderHistory default 1`] = `
`; +exports[`Storyshots Orders / OrderMarkAsPaidDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderPaymentDialog capture payment 1`] = `
`; +exports[`Storyshots Orders / OrderPaymentDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderPaymentDialog refund payment 1`] = `
`; +exports[`Storyshots Orders / OrderPaymentVoidDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderProductAddDialog default 1`] = `
`; +exports[`Storyshots Orders / OrderProductAddDialog errors 1`] = ` +
+`; + exports[`Storyshots Orders / OrderShippingMethodEditDialog default 1`] = `
`; +exports[`Storyshots Orders / OrderShippingMethodEditDialog errors 1`] = ` +
+`; + exports[`Storyshots Product types / ProductTypeDeleteDialog default 1`] = `
- Generic form error + Invalid value

- Generic form error + Invalid value

- Generic form error + Invalid value

`; +exports[`Storyshots Views / Categories / Create category form errors 1`] = ` +
+ +
+
+
+ Create New Category +
+
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+
+ +
+ + +
+

+ This field is required +

+
+
+
+
+
+
+ Category Description +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ This field is required +
+
+
+
+
+
+
+ + Search Engine Preview + +
+ +
+
+
+
+
+
+ Add search engine title and description to make this category easier to find +
+
+
+
+
+ +
+`; + exports[`Storyshots Views / Categories / Update category default 1`] = `
`; +exports[`Storyshots Views / Categories / Update category form errors 1`] = ` +
+
+
+
+
+ Coffees +
+
+
+
+
+
+
+ + General Informations + +
+
+
+
+
+
+
+ +
+ + +
+

+ This field is required +

+
+
+
+
+
+
+ Category Description +
+
+ +
+
+
+
+
+
+ + + bold + + +
+
+
+
+ + + italic + + +
+
+
+
+ + + strikethrough + + +
+
+

+
+ + + h1 + + +
+

+

+
+ + + h2 + + +
+

+

+
+ + + h3 + + +
+

+
+
+ + + blockquote + + +
+
+
    +
  • +
    + + + ul + + +
    +
  • +
+
    +
  1. +
    + + + ol + + +
    +
  2. +
+
+
+ + + link + + +
+
+
+
+
+
+
+
+
+ This field is required +
+
+
+
+
+
+
+ + Background Image (optional) + +
+ + +
+
+
+
+
+
+
+
+ +
+
+ Alt text +
+
+
+
+
+ +
+ +