Merge pull request #452 from mirumee/next/use-form-error-codes

Use translated error messages based on error codes
This commit is contained in:
Dominik Żegleń 2020-03-27 11:40:12 +01:00 committed by GitHub
commit 680dd8baa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
310 changed files with 8039 additions and 2247 deletions

View file

@ -292,6 +292,12 @@
"context": "attributes section name", "context": "attributes section name",
"string": "Attributes" "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": { "src_dot_attributes_dot_components_dot_AttributeBulkDeleteDialog_dot_1184518529": {
"context": "dialog content", "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?}}" "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": { "src_dot_attributes_dot_views_dot_AttributeCreate_dot_11941964": {
"string": "Successfully created attribute" "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": { "src_dot_attributes_dot_views_dot_AttributeDetails_dot_423042761": {
"context": "attribute value deleted", "context": "attribute value deleted",
"string": "Value deleted" "string": "Value deleted"
@ -836,9 +838,6 @@
"src_dot_collections_dot_views_dot_1152429477": { "src_dot_collections_dot_views_dot_1152429477": {
"string": "Deleted collection" "string": "Deleted collection"
}, },
"src_dot_collections_dot_views_dot_1597339737": {
"string": "Created collection"
},
"src_dot_collections_dot_views_dot_2001540731": { "src_dot_collections_dot_views_dot_2001540731": {
"string": "Added product to collection" "string": "Added product to collection"
}, },
@ -2722,9 +2721,6 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_1056718390": { "src_dot_orders_dot_views_dot_OrderDetails_dot_1056718390": {
"string": "Payment successfully captured" "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": { "src_dot_orders_dot_views_dot_OrderDetails_dot_1435191432": {
"string": "Draft order successfully finalized" "string": "Draft order successfully finalized"
}, },
@ -2734,30 +2730,15 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_1475565380": { "src_dot_orders_dot_views_dot_OrderDetails_dot_1475565380": {
"string": "Fulfillment successfully updated" "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": { "src_dot_orders_dot_views_dot_OrderDetails_dot_1632861387": {
"string": "Order line updated" "string": "Order line updated"
}, },
"src_dot_orders_dot_views_dot_OrderDetails_dot_1636370257": { "src_dot_orders_dot_views_dot_OrderDetails_dot_1636370257": {
"string": "Order marked as paid" "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": { "src_dot_orders_dot_views_dot_OrderDetails_dot_1999598492": {
"string": "Order line added" "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": { "src_dot_orders_dot_views_dot_OrderDetails_dot_2808777264": {
"string": "Order successfully cancelled" "string": "Order successfully cancelled"
}, },
@ -2770,27 +2751,12 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_3367579693": { "src_dot_orders_dot_views_dot_OrderDetails_dot_3367579693": {
"string": "Order successfully updated" "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": { "src_dot_orders_dot_views_dot_OrderDetails_dot_4245651107": {
"string": "Items successfully fulfilled" "string": "Items successfully fulfilled"
}, },
"src_dot_orders_dot_views_dot_OrderDetails_dot_487150696": { "src_dot_orders_dot_views_dot_OrderDetails_dot_580490159": {
"string": "Could not update order line" "context": "window title",
}, "string": "Order #{orderNumber}"
"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_617145655": { "src_dot_orders_dot_views_dot_OrderDetails_dot_617145655": {
"string": "Shipping method successfully updated" "string": "Shipping method successfully updated"
@ -2798,6 +2764,10 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_667274681": { "src_dot_orders_dot_views_dot_OrderDetails_dot_667274681": {
"string": "Payment successfully refunded" "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": { "src_dot_orders_dot_views_dot_OrderDetails_dot_927945225": {
"string": "Fulfillment successfully cancelled" "string": "Fulfillment successfully cancelled"
}, },
@ -2893,9 +2863,6 @@
"context": "header", "context": "header",
"string": "Create Page" "string": "Create Page"
}, },
"src_dot_pages_dot_views_dot_1457312643": {
"string": "Removed page"
},
"src_dot_pages_dot_views_dot_2680158037": { "src_dot_pages_dot_views_dot_2680158037": {
"string": "Successfully created new page" "string": "Successfully created new page"
}, },
@ -4009,6 +3976,14 @@
"context": "shipping method has no value limits", "context": "shipping method has no value limits",
"string": "There are 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": { "src_dot_shipping_dot_components_dot_ShippingZoneRates_dot_1134347598": {
"context": "shipping method price", "context": "shipping method price",
"string": "Price" "string": "Price"
@ -4236,9 +4211,6 @@
"src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_866304242": { "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." "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": { "src_dot_somethingWentWrong": {
"string": "Saleor ran into an unexpected problem" "string": "Saleor ran into an unexpected problem"
}, },
@ -4745,6 +4717,98 @@
"context": "button", "context": "button",
"string": "Upload image" "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": { "src_dot_vouchers": {
"context": "vouchers section name", "context": "vouchers section name",
"string": "Vouchers" "string": "Vouchers"
@ -4757,6 +4821,10 @@
"context": "header", "context": "header",
"string": "Create Webhook" "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": { "src_dot_webhooks_dot_components_dot_WebhookDeleteDialog_dot_2297471173": {
"context": "delete webhook", "context": "delete webhook",
"string": "Are you sure you want to delete {name}?" "string": "Are you sure you want to delete {name}?"
@ -4856,6 +4924,10 @@
"context": "section header", "context": "section header",
"string": "Webhook Status" "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": { "src_dot_webhooks_dot_components_dot_WebhooksDetailsPage_dot_408706360": {
"context": "header", "context": "header",
"string": "{webhookName} Details" "string": "{webhookName} Details"
@ -4890,6 +4962,9 @@
"context": "user action bar", "context": "user action bar",
"string": "Action" "string": "Action"
}, },
"src_dot_webhooks_dot_components_dot_WebhooksList_dot_618422799": {
"string": "Unnamed webhook"
},
"src_dot_webhooks_dot_components_dot_WebhooksList_dot_636461959": { "src_dot_webhooks_dot_components_dot_WebhooksList_dot_636461959": {
"context": "webhook name", "context": "webhook name",
"string": "Name" "string": "Name"

View file

@ -493,6 +493,13 @@ type BulkProductError {
index: Int index: Int
} }
type BulkStockError {
field: String
message: String
code: ProductErrorCode!
index: Int
}
input CatalogueInput { input CatalogueInput {
products: [ID] products: [ID]
categories: [ID] categories: [ID]
@ -1502,6 +1509,20 @@ input DigitalContentUrlCreateInput {
content: ID! content: ID!
} }
type DiscountError {
field: String
message: String
code: DiscountErrorCode!
}
enum DiscountErrorCode {
ALREADY_EXISTS
INVALID
NOT_FOUND
REQUIRED
UNIQUE
}
enum DiscountStatusEnum { enum DiscountStatusEnum {
ACTIVE ACTIVE
EXPIRED EXPIRED
@ -2158,10 +2179,6 @@ type Mutation {
deleteWarehouse(id: ID!): WarehouseDelete deleteWarehouse(id: ID!): WarehouseDelete
assignWarehouseShippingZone(id: ID!, shippingZoneIds: [ID!]!): WarehouseShippingZoneAssign assignWarehouseShippingZone(id: ID!, shippingZoneIds: [ID!]!): WarehouseShippingZoneAssign
unassignWarehouseShippingZone(id: ID!, shippingZoneIds: [ID!]!): WarehouseShippingZoneUnassign 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 authorizationKeyAdd(input: AuthorizationKeyInput!, keyType: AuthorizationKeyType!): AuthorizationKeyAdd
authorizationKeyDelete(keyType: AuthorizationKeyType!): AuthorizationKeyDelete authorizationKeyDelete(keyType: AuthorizationKeyType!): AuthorizationKeyDelete
staffNotificationRecipientCreate(input: StaffNotificationRecipientInput!): StaffNotificationRecipientCreate staffNotificationRecipientCreate(input: StaffNotificationRecipientInput!): StaffNotificationRecipientCreate
@ -2253,6 +2270,9 @@ type Mutation {
productVariantDelete(id: ID!): ProductVariantDelete productVariantDelete(id: ID!): ProductVariantDelete
productVariantBulkCreate(product: ID!, variants: [ProductVariantBulkCreateInput]!): ProductVariantBulkCreate productVariantBulkCreate(product: ID!, variants: [ProductVariantBulkCreateInput]!): ProductVariantBulkCreate
productVariantBulkDelete(ids: [ID]!): ProductVariantBulkDelete 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 productVariantUpdate(id: ID!, input: ProductVariantInput!): ProductVariantUpdate
productVariantTranslate(id: ID!, input: NameTranslationInput!, languageCode: LanguageCodeEnum!): ProductVariantTranslate 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.") 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 voucher: Voucher
giftCards: [GiftCard] giftCards: [GiftCard]
discount: Money discount: Money
discountName: String! discountName: String
translatedDiscountName: String! translatedDiscountName: String
displayGrossPrices: Boolean! displayGrossPrices: Boolean!
customerNote: String! customerNote: String!
weight: Weight weight: Weight
@ -2489,7 +2509,7 @@ type OrderAddNote {
} }
input OrderAddNoteInput { input OrderAddNoteInput {
message: String message: String!
} }
type OrderBulkCancel { type OrderBulkCancel {
@ -3218,6 +3238,7 @@ input ProductCreateInput {
quantity: Int quantity: Int
trackInventory: Boolean trackInventory: Boolean
productType: ID! productType: ID!
stocks: [StockInput!]
} }
type ProductDelete { type ProductDelete {
@ -3255,6 +3276,7 @@ input ProductFilterInput {
attributes: [AttributeInput] attributes: [AttributeInput]
stockAvailability: StockAvailability stockAvailability: StockAvailability
productType: ID productType: ID
stocks: ProductStockFilterInput
search: String search: String
minimalPrice: PriceRangeInput minimalPrice: PriceRangeInput
productTypes: [ID] productTypes: [ID]
@ -3355,6 +3377,11 @@ type ProductPricingInfo {
priceRangeLocalCurrency: TaxedMoneyRange priceRangeLocalCurrency: TaxedMoneyRange
} }
input ProductStockFilterInput {
warehouseIds: [ID!]
quantity: IntRangeInput
}
type ProductTranslatableContent implements Node { type ProductTranslatableContent implements Node {
id: ID! id: ID!
seoTitle: String seoTitle: String
@ -3549,7 +3576,7 @@ type ProductVariant implements Node & ObjectWithMetadata {
images: [ProductImage] images: [ProductImage]
translation(languageCode: LanguageCodeEnum!): ProductVariantTranslation translation(languageCode: LanguageCodeEnum!): ProductVariantTranslation
digitalContent: DigitalContent digitalContent: DigitalContent
stock(country: String): [Stock] stocks(countryCode: CountryCode): [Stock]
} }
type ProductVariantBulkCreate { type ProductVariantBulkCreate {
@ -3613,6 +3640,7 @@ input ProductVariantCreateInput {
trackInventory: Boolean trackInventory: Boolean
weight: WeightScalar weight: WeightScalar
product: ID! product: ID!
stocks: [StockInput!]
} }
type ProductVariantDelete { type ProductVariantDelete {
@ -3631,6 +3659,24 @@ input ProductVariantInput {
weight: WeightScalar 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 { type ProductVariantTranslatableContent implements Node {
id: ID! id: ID!
name: String! name: String!
@ -3784,6 +3830,7 @@ type Sale implements Node {
type SaleAddCatalogues { type SaleAddCatalogues {
errors: [Error!]! errors: [Error!]!
sale: Sale sale: Sale
discountErrors: [DiscountError!]!
} }
type SaleBulkDelete { type SaleBulkDelete {
@ -3804,11 +3851,13 @@ type SaleCountableEdge {
type SaleCreate { type SaleCreate {
errors: [Error!]! errors: [Error!]!
discountErrors: [DiscountError!]!
sale: Sale sale: Sale
} }
type SaleDelete { type SaleDelete {
errors: [Error!]! errors: [Error!]!
discountErrors: [DiscountError!]!
sale: Sale sale: Sale
} }
@ -3833,6 +3882,7 @@ input SaleInput {
type SaleRemoveCatalogues { type SaleRemoveCatalogues {
errors: [Error!]! errors: [Error!]!
sale: Sale sale: Sale
discountErrors: [DiscountError!]!
} }
enum SaleSortField { enum SaleSortField {
@ -3873,6 +3923,7 @@ enum SaleType {
type SaleUpdate { type SaleUpdate {
errors: [Error!]! errors: [Error!]!
discountErrors: [DiscountError!]!
sale: Sale sale: Sale
} }
@ -3888,15 +3939,15 @@ input SeoInput {
type ServiceAccount implements Node & ObjectWithMetadata { type ServiceAccount implements Node & ObjectWithMetadata {
id: ID! id: ID!
name: String
created: DateTime created: DateTime
isActive: Boolean isActive: Boolean
permissions: [PermissionDisplay]
tokens: [ServiceAccountToken] tokens: [ServiceAccountToken]
privateMetadata: [MetadataItem]! privateMetadata: [MetadataItem]!
metadata: [MetadataItem]! metadata: [MetadataItem]!
privateMeta: [MetaStore]! @deprecated(reason: "DEPRECATED: Will be removed in Saleor 2.11. use the `privetaMetadata` field instead. ") 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. ") meta: [MetaStore]! @deprecated(reason: "DEPRECATED: Will be removed in Saleor 2.11. use the `metadata` field instead. ")
permissions: [PermissionDisplay]
name: String
} }
type ServiceAccountClearPrivateMeta { type ServiceAccountClearPrivateMeta {
@ -4341,12 +4392,6 @@ enum StockAvailability {
OUT_OF_STOCK OUT_OF_STOCK
} }
type StockBulkDelete {
errors: [Error!]!
count: Int!
stockError: [StockError!]!
}
type StockCountableConnection { type StockCountableConnection {
pageInfo: PageInfo! pageInfo: PageInfo!
edges: [StockCountableEdge!]! edges: [StockCountableEdge!]!
@ -4358,18 +4403,13 @@ type StockCountableEdge {
cursor: String! cursor: String!
} }
type StockCreate { type StockError {
errors: [Error!]! field: String
stockErrors: [StockError!]! message: String
stock: Stock code: StockErrorCode!
} }
type StockDelete { enum StockErrorCode {
errors: [Error!]!
stock: Stock
}
enum StockErorrCode {
ALREADY_EXISTS ALREADY_EXISTS
GRAPHQL_ERROR GRAPHQL_ERROR
INVALID INVALID
@ -4378,12 +4418,6 @@ enum StockErorrCode {
UNIQUE UNIQUE
} }
type StockError {
field: String
message: String
code: StockErorrCode!
}
input StockFilterInput { input StockFilterInput {
quantity: Float quantity: Float
quantityAllocated: Float quantityAllocated: Float
@ -4391,17 +4425,10 @@ input StockFilterInput {
} }
input StockInput { input StockInput {
productVariant: ID!
warehouse: ID! warehouse: ID!
quantity: Int quantity: Int
} }
type StockUpdate {
errors: [Error!]!
stockError: [StockError!]!
stock: Stock
}
enum TaxRateType { enum TaxRateType {
ACCOMMODATION ACCOMMODATION
ADMISSION_TO_CULTURAL_EVENTS ADMISSION_TO_CULTURAL_EVENTS
@ -4693,6 +4720,7 @@ type Voucher implements Node {
type VoucherAddCatalogues { type VoucherAddCatalogues {
errors: [Error!]! errors: [Error!]!
voucher: Voucher voucher: Voucher
discountErrors: [DiscountError!]!
} }
type VoucherBulkDelete { type VoucherBulkDelete {
@ -4713,11 +4741,13 @@ type VoucherCountableEdge {
type VoucherCreate { type VoucherCreate {
errors: [Error!]! errors: [Error!]!
discountErrors: [DiscountError!]!
voucher: Voucher voucher: Voucher
} }
type VoucherDelete { type VoucherDelete {
errors: [Error!]! errors: [Error!]!
discountErrors: [DiscountError!]!
voucher: Voucher voucher: Voucher
} }
@ -4757,6 +4787,7 @@ input VoucherInput {
type VoucherRemoveCatalogues { type VoucherRemoveCatalogues {
errors: [Error!]! errors: [Error!]!
voucher: Voucher voucher: Voucher
discountErrors: [DiscountError!]!
} }
enum VoucherSortField { enum VoucherSortField {
@ -4800,6 +4831,7 @@ enum VoucherTypeEnum {
type VoucherUpdate { type VoucherUpdate {
errors: [Error!]! errors: [Error!]!
discountErrors: [DiscountError!]!
voucher: Voucher voucher: Voucher
} }

View file

@ -10,16 +10,17 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import SingleSelectField from "@saleor/components/SingleSelectField"; import SingleSelectField from "@saleor/components/SingleSelectField";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; 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 { AttributePageFormData } from "../AttributePage";
import { getAttributeSlugErrorMessage } from "../../errors";
export interface AttributeDetailsProps { export interface AttributeDetailsProps {
canChangeType: boolean; canChangeType: boolean;
data: AttributePageFormData; data: AttributePageFormData;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: ProductErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -48,6 +49,8 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
} }
]; ];
const formErrors = getFormErrors(["name", "slug", "inputType"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -56,21 +59,21 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
<CardContent> <CardContent>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "name")} error={!!formErrors.name}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Default Label", defaultMessage: "Default Label",
description: "attribute's label" description: "attribute's label"
})} })}
name={"name" as keyof AttributePageFormData} name={"name" as keyof AttributePageFormData}
fullWidth fullWidth
helperText={getFieldError(errors, "name")?.message} helperText={getProductErrorMessage(formErrors.name, intl)}
value={data.name} value={data.name}
onChange={onChange} onChange={onChange}
/> />
<FormSpacer /> <FormSpacer />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "slug")} error={!!formErrors.slug}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Attribute Code", defaultMessage: "Attribute Code",
description: "attribute's slug short code label" description: "attribute's slug short code label"
@ -79,7 +82,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
placeholder={slugify(data.name).toLowerCase()} placeholder={slugify(data.name).toLowerCase()}
fullWidth fullWidth
helperText={ helperText={
getFieldError(errors, "slug")?.message || getAttributeSlugErrorMessage(formErrors.slug, intl) ||
intl.formatMessage({ intl.formatMessage({
defaultMessage: defaultMessage:
"This is used internally. Make sure you dont use spaces", "This is used internally. Make sure you dont use spaces",
@ -93,8 +96,8 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({
<SingleSelectField <SingleSelectField
choices={inputTypeChoices} choices={inputTypeChoices}
disabled={disabled || !canChangeType} disabled={disabled || !canChangeType}
error={!!getFieldError(errors, "inputType")} error={!!formErrors.inputType}
hint={getFieldError(errors, "inputType")?.message} hint={getProductErrorMessage(formErrors.inputType, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Catalog Input type for Store Owner", defaultMessage: "Catalog Input type for Store Owner",
description: "attribute's editor component" description: "attribute's editor component"

View file

@ -12,8 +12,9 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { ReorderAction, UserError } from "@saleor/types"; import { ReorderAction } from "@saleor/types";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import { AttributeInputTypeEnum } from "@saleor/types/globalTypes";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { import {
AttributeDetailsFragment, AttributeDetailsFragment,
AttributeDetailsFragment_values AttributeDetailsFragment_values
@ -25,7 +26,7 @@ import AttributeValues from "../AttributeValues";
export interface AttributePageProps { export interface AttributePageProps {
attribute: AttributeDetailsFragment | null; attribute: AttributeDetailsFragment | null;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: ProductErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
values: AttributeDetailsFragment_values[]; values: AttributeDetailsFragment_values[];
onBack: () => void; onBack: () => void;

View file

@ -11,14 +11,14 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types"; import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { AttributePageFormData } from "../AttributePage"; import { AttributePageFormData } from "../AttributePage";
export interface AttributePropertiesProps { export interface AttributePropertiesProps {
data: AttributePageFormData; data: AttributePageFormData;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: ProductErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -30,6 +30,8 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["storefrontSearchPosition"], errors);
return ( return (
<Card> <Card>
<CardTitle title={intl.formatMessage(commonMessages.properties)} /> <CardTitle title={intl.formatMessage(commonMessages.properties)} />
@ -86,11 +88,12 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
{data.filterableInStorefront && ( {data.filterableInStorefront && (
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "storefrontSearchPosition")} error={!!formErrors.storefrontSearchPosition}
fullWidth fullWidth
helperText={ helperText={getProductErrorMessage(
getFieldError(errors, "storefrontSearchPosition")?.message formErrors.storefrontSearchPosition,
} intl
)}
name={"storefrontSearchPosition" as keyof AttributePageFormData} name={"storefrontSearchPosition" as keyof AttributePageFormData}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Position in faceted navigation", defaultMessage: "Position in faceted navigation",

View file

@ -14,8 +14,9 @@ import Form from "@saleor/components/Form";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { UserError } from "@saleor/types"; import { getFormErrors } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { getAttributeValueErrorMessage } from "@saleor/attributes/errors";
import { AttributeDetails_attribute_values } from "../../types/AttributeDetails"; import { AttributeDetails_attribute_values } from "../../types/AttributeDetails";
export interface AttributeValueEditDialogFormData { export interface AttributeValueEditDialogFormData {
@ -25,7 +26,7 @@ export interface AttributeValueEditDialogProps {
attributeValue: AttributeDetails_attribute_values | null; attributeValue: AttributeDetails_attribute_values | null;
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: ProductErrorFragment[];
open: boolean; open: boolean;
onSubmit: (data: AttributeValueEditDialogFormData) => void; onSubmit: (data: AttributeValueEditDialogFormData) => void;
onClose: () => void; onClose: () => void;
@ -45,6 +46,7 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
name: maybe(() => attributeValue.name, "") name: maybe(() => attributeValue.name, "")
}; };
const errors = useModalDialogErrors(apiErrors, open); const errors = useModalDialogErrors(apiErrors, open);
const formErrors = getFormErrors(["name"], errors);
return ( return (
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm"> <Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
@ -68,9 +70,12 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
<TextField <TextField
autoFocus autoFocus
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "name")} error={!!formErrors.name}
fullWidth fullWidth
helperText={getFieldError(errors, "name")?.message} helperText={getAttributeValueErrorMessage(
formErrors.name,
intl
)}
name={"name" as keyof AttributeValueEditDialogFormData} name={"name" as keyof AttributeValueEditDialogFormData}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Name", defaultMessage: "Name",

38
src/attributes/errors.ts Normal file
View file

@ -0,0 +1,38 @@
import { IntlShape, defineMessages } from "react-intl";
import { ProductErrorCode } from "@saleor/types/globalTypes";
import { getProductErrorMessage } from "@saleor/utils/errors";
import { ProductErrorFragment } from "./types/ProductErrorFragment";
const messages = defineMessages({
attributeSlugUnique: {
defaultMessage: "Attribute with this slug already exists"
},
attributeValueAlreadyExists: {
defaultMessage: "This value already exists within this attribute"
}
});
export function getAttributeSlugErrorMessage(
err: ProductErrorFragment,
intl: IntlShape
): string {
switch (err?.code) {
case ProductErrorCode.UNIQUE:
return intl.formatMessage(messages.attributeSlugUnique);
default:
return getProductErrorMessage(err, intl);
}
}
export function getAttributeValueErrorMessage(
err: ProductErrorFragment,
intl: IntlShape
): string {
switch (err?.code) {
case ProductErrorCode.ALREADY_EXISTS:
return intl.formatMessage(messages.attributeValueAlreadyExists);
default:
return getProductErrorMessage(err, intl);
}
}

View file

@ -35,12 +35,19 @@ import {
AttributeValueUpdateVariables AttributeValueUpdateVariables
} from "./types/AttributeValueUpdate"; } from "./types/AttributeValueUpdate";
export const productErrorFragment = gql`
fragment ProductErrorFragment on ProductError {
code
field
}
`;
const attributeBulkDelete = gql` const attributeBulkDelete = gql`
${productErrorFragment}
mutation AttributeBulkDelete($ids: [ID!]!) { mutation AttributeBulkDelete($ids: [ID!]!) {
attributeBulkDelete(ids: $ids) { attributeBulkDelete(ids: $ids) {
errors { errors: productErrors {
field ...ProductErrorFragment
message
} }
} }
} }
@ -51,11 +58,11 @@ export const AttributeBulkDeleteMutation = TypedMutation<
>(attributeBulkDelete); >(attributeBulkDelete);
const attributeDelete = gql` const attributeDelete = gql`
${productErrorFragment}
mutation AttributeDelete($id: ID!) { mutation AttributeDelete($id: ID!) {
attributeDelete(id: $id) { attributeDelete(id: $id) {
errors { errors: productErrors {
field ...ProductErrorFragment
message
} }
} }
} }
@ -67,15 +74,15 @@ export const AttributeDeleteMutation = TypedMutation<
export const attributeUpdateMutation = gql` export const attributeUpdateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) { mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) {
attributeUpdate(id: $id, input: $input) { attributeUpdate(id: $id, input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -86,15 +93,15 @@ export const AttributeUpdateMutation = TypedMutation<
const attributeValueDelete = gql` const attributeValueDelete = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueDelete($id: ID!) { mutation AttributeValueDelete($id: ID!) {
attributeValueDelete(id: $id) { attributeValueDelete(id: $id) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -105,15 +112,15 @@ export const AttributeValueDeleteMutation = TypedMutation<
export const attributeValueUpdateMutation = gql` export const attributeValueUpdateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) { mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) {
attributeValueUpdate(id: $id, input: $input) { attributeValueUpdate(id: $id, input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -124,15 +131,15 @@ export const AttributeValueUpdateMutation = TypedMutation<
export const attributeValueCreateMutation = gql` export const attributeValueCreateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) { mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) {
attributeValueCreate(attribute: $id, input: $input) { attributeValueCreate(attribute: $id, input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -143,15 +150,15 @@ export const AttributeValueCreateMutation = TypedMutation<
export const attributeCreateMutation = gql` export const attributeCreateMutation = gql`
${attributeDetailsFragment} ${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeCreate($input: AttributeCreateInput!) { mutation AttributeCreate($input: AttributeCreateInput!) {
attributeCreate(input: $input) { attributeCreate(input: $input) {
errors {
field
message
}
attribute { attribute {
...AttributeDetailsFragment ...AttributeDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -161,18 +168,18 @@ export const AttributeCreateMutation = TypedMutation<
>(attributeCreateMutation); >(attributeCreateMutation);
const attributeValueReorderMutation = gql` const attributeValueReorderMutation = gql`
${productErrorFragment}
mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) { mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) {
attributeReorderValues(attributeId: $id, moves: [$move]) { attributeReorderValues(attributeId: $id, moves: [$move]) {
errors {
field
message
}
attribute { attribute {
id id
values { values {
id id
} }
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeBulkDelete // GraphQL mutation operation: AttributeBulkDelete
// ==================================================== // ====================================================
export interface AttributeBulkDelete_attributeBulkDelete_errors { export interface AttributeBulkDelete_attributeBulkDelete_errors {
__typename: "Error"; __typename: "ProductError";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface AttributeBulkDelete_attributeBulkDelete { export interface AttributeBulkDelete_attributeBulkDelete {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: AttributeCreate
// ==================================================== // ====================================================
export interface AttributeCreate_attributeCreate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeCreate_attributeCreate_attribute_values { export interface AttributeCreate_attributeCreate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -37,10 +31,16 @@ export interface AttributeCreate_attributeCreate_attribute {
values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null; values: (AttributeCreate_attributeCreate_attribute_values | null)[] | null;
} }
export interface AttributeCreate_attributeCreate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeCreate_attributeCreate { export interface AttributeCreate_attributeCreate {
__typename: "AttributeCreate"; __typename: "AttributeCreate";
errors: AttributeCreate_attributeCreate_errors[];
attribute: AttributeCreate_attributeCreate_attribute | null; attribute: AttributeCreate_attributeCreate_attribute | null;
errors: AttributeCreate_attributeCreate_errors[];
} }
export interface AttributeCreate { export interface AttributeCreate {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AttributeDelete // GraphQL mutation operation: AttributeDelete
// ==================================================== // ====================================================
export interface AttributeDelete_attributeDelete_errors { export interface AttributeDelete_attributeDelete_errors {
__typename: "Error"; __typename: "ProductError";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface AttributeDelete_attributeDelete { export interface AttributeDelete_attributeDelete {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: AttributeUpdate
// ==================================================== // ====================================================
export interface AttributeUpdate_attributeUpdate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeUpdate_attributeUpdate_attribute_values { export interface AttributeUpdate_attributeUpdate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -37,10 +31,16 @@ export interface AttributeUpdate_attributeUpdate_attribute {
values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null; values: (AttributeUpdate_attributeUpdate_attribute_values | null)[] | null;
} }
export interface AttributeUpdate_attributeUpdate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeUpdate_attributeUpdate { export interface AttributeUpdate_attributeUpdate {
__typename: "AttributeUpdate"; __typename: "AttributeUpdate";
errors: AttributeUpdate_attributeUpdate_errors[];
attribute: AttributeUpdate_attributeUpdate_attribute | null; attribute: AttributeUpdate_attributeUpdate_attribute | null;
errors: AttributeUpdate_attributeUpdate_errors[];
} }
export interface AttributeUpdate { export interface AttributeUpdate {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: AttributeValueCreate
// ==================================================== // ====================================================
export interface AttributeValueCreate_attributeValueCreate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueCreate_attributeValueCreate_attribute_values { export interface AttributeValueCreate_attributeValueCreate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -37,10 +31,16 @@ export interface AttributeValueCreate_attributeValueCreate_attribute {
values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null; values: (AttributeValueCreate_attributeValueCreate_attribute_values | null)[] | null;
} }
export interface AttributeValueCreate_attributeValueCreate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueCreate_attributeValueCreate { export interface AttributeValueCreate_attributeValueCreate {
__typename: "AttributeValueCreate"; __typename: "AttributeValueCreate";
errors: AttributeValueCreate_attributeValueCreate_errors[];
attribute: AttributeValueCreate_attributeValueCreate_attribute | null; attribute: AttributeValueCreate_attributeValueCreate_attribute | null;
errors: AttributeValueCreate_attributeValueCreate_errors[];
} }
export interface AttributeValueCreate { export interface AttributeValueCreate {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: AttributeValueDelete
// ==================================================== // ====================================================
export interface AttributeValueDelete_attributeValueDelete_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueDelete_attributeValueDelete_attribute_values { export interface AttributeValueDelete_attributeValueDelete_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -37,10 +31,16 @@ export interface AttributeValueDelete_attributeValueDelete_attribute {
values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null; values: (AttributeValueDelete_attributeValueDelete_attribute_values | null)[] | null;
} }
export interface AttributeValueDelete_attributeValueDelete_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueDelete_attributeValueDelete { export interface AttributeValueDelete_attributeValueDelete {
__typename: "AttributeValueDelete"; __typename: "AttributeValueDelete";
errors: AttributeValueDelete_attributeValueDelete_errors[];
attribute: AttributeValueDelete_attributeValueDelete_attribute | null; attribute: AttributeValueDelete_attributeValueDelete_attribute | null;
errors: AttributeValueDelete_attributeValueDelete_errors[];
} }
export interface AttributeValueDelete { export interface AttributeValueDelete {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: AttributeValueReorder
// ==================================================== // ====================================================
export interface AttributeValueReorder_attributeReorderValues_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueReorder_attributeReorderValues_attribute_values { export interface AttributeValueReorder_attributeReorderValues_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -25,10 +19,16 @@ export interface AttributeValueReorder_attributeReorderValues_attribute {
values: (AttributeValueReorder_attributeReorderValues_attribute_values | null)[] | null; values: (AttributeValueReorder_attributeReorderValues_attribute_values | null)[] | null;
} }
export interface AttributeValueReorder_attributeReorderValues_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueReorder_attributeReorderValues { export interface AttributeValueReorder_attributeReorderValues {
__typename: "AttributeReorderValues"; __typename: "AttributeReorderValues";
errors: AttributeValueReorder_attributeReorderValues_errors[];
attribute: AttributeValueReorder_attributeReorderValues_attribute | null; attribute: AttributeValueReorder_attributeReorderValues_attribute | null;
errors: AttributeValueReorder_attributeReorderValues_errors[];
} }
export interface AttributeValueReorder { export interface AttributeValueReorder {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: AttributeValueUpdate
// ==================================================== // ====================================================
export interface AttributeValueUpdate_attributeValueUpdate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface AttributeValueUpdate_attributeValueUpdate_attribute_values { export interface AttributeValueUpdate_attributeValueUpdate_attribute_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
@ -37,10 +31,16 @@ export interface AttributeValueUpdate_attributeValueUpdate_attribute {
values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null; values: (AttributeValueUpdate_attributeValueUpdate_attribute_values | null)[] | null;
} }
export interface AttributeValueUpdate_attributeValueUpdate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface AttributeValueUpdate_attributeValueUpdate { export interface AttributeValueUpdate_attributeValueUpdate {
__typename: "AttributeValueUpdate"; __typename: "AttributeValueUpdate";
errors: AttributeValueUpdate_attributeValueUpdate_errors[];
attribute: AttributeValueUpdate_attributeValueUpdate_attribute | null; attribute: AttributeValueUpdate_attributeValueUpdate_attribute | null;
errors: AttributeValueUpdate_attributeValueUpdate_errors[];
} }
export interface AttributeValueUpdate { export interface AttributeValueUpdate {

View file

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

View file

@ -5,7 +5,7 @@ import slugify from "slugify";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { ReorderEvent, UserError } from "@saleor/types"; import { ReorderEvent } from "@saleor/types";
import { import {
add, add,
isSelected, isSelected,
@ -14,6 +14,8 @@ import {
updateAtIndex updateAtIndex
} from "@saleor/utils/lists"; } from "@saleor/utils/lists";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; 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 AttributePage from "../../components/AttributePage";
import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog";
import AttributeValueEditDialog, { import AttributeValueEditDialog, {
@ -33,6 +35,12 @@ interface AttributeDetailsProps {
params: AttributeAddUrlQueryParams; params: AttributeAddUrlQueryParams;
} }
const attributeValueAlreadyExistsError: ProductErrorFragment = {
__typename: "ProductError",
code: ProductErrorCode.ALREADY_EXISTS,
field: "name"
};
function areValuesEqual( function areValuesEqual(
a: AttributeValueEditDialogFormData, a: AttributeValueEditDialogFormData,
b: AttributeValueEditDialogFormData b: AttributeValueEditDialogFormData
@ -48,7 +56,9 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
const [values, setValues] = React.useState< const [values, setValues] = React.useState<
AttributeValueEditDialogFormData[] AttributeValueEditDialogFormData[]
>([]); >([]);
const [valueErrors, setValueErrors] = React.useState<UserError[]>([]); const [valueErrors, setValueErrors] = React.useState<ProductErrorFragment[]>(
[]
);
const id = params.id ? parseInt(params.id, 0) : undefined; const id = params.id ? parseInt(params.id, 0) : undefined;
@ -57,6 +67,8 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
AttributeAddUrlQueryParams AttributeAddUrlQueryParams
>(navigate, attributeAddUrl, params); >(navigate, attributeAddUrl, params);
React.useEffect(() => setValueErrors([]), [params.action]);
const handleValueDelete = () => { const handleValueDelete = () => {
setValues(remove(values[params.id], values, areValuesEqual)); setValues(remove(values[params.id], values, areValuesEqual));
closeModal(); closeModal();
@ -73,20 +85,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
}; };
const handleValueUpdate = (input: AttributeValueEditDialogFormData) => { const handleValueUpdate = (input: AttributeValueEditDialogFormData) => {
if (isSelected(input, values, areValuesEqual)) { if (isSelected(input, values, areValuesEqual)) {
setValueErrors([ setValueErrors([attributeValueAlreadyExistsError]);
{
field: "name",
message: intl.formatMessage(
{
defaultMessage: "A value named {name} already exists",
description: "attribute value edit error"
},
{
name: input.name
}
)
}
]);
} else { } else {
setValues(updateAtIndex(input, values, id)); setValues(updateAtIndex(input, values, id));
closeModal(); closeModal();
@ -94,20 +93,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
}; };
const handleValueCreate = (input: AttributeValueEditDialogFormData) => { const handleValueCreate = (input: AttributeValueEditDialogFormData) => {
if (isSelected(input, values, areValuesEqual)) { if (isSelected(input, values, areValuesEqual)) {
setValueErrors([ setValueErrors([attributeValueAlreadyExistsError]);
{
field: "name",
message: intl.formatMessage(
{
defaultMessage: "A value named {name} already exists",
description: "attribute value edit error"
},
{
name: input.name
}
)
}
]);
} else { } else {
setValues(add(input, values)); setValues(add(input, values));
closeModal(); closeModal();
@ -123,10 +109,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
<AttributePage <AttributePage
attribute={null} attribute={null}
disabled={false} disabled={false}
errors={maybe( errors={attributeCreateOpts.data?.attributeCreate.errors || []}
() => attributeCreateOpts.data.attributeCreate.errors,
[]
)}
onBack={() => navigate(attributeListUrl())} onBack={() => navigate(attributeListUrl())}
onDelete={undefined} onDelete={undefined}
onSubmit={input => onSubmit={input =>

View file

@ -8,6 +8,7 @@ import { maybe } from "@saleor/misc";
import { ReorderEvent } from "@saleor/types"; import { ReorderEvent } from "@saleor/types";
import { move } from "@saleor/utils/lists"; import { move } from "@saleor/utils/lists";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import { getProductErrorMessage } from "@saleor/utils/errors";
import AttributeDeleteDialog from "../../components/AttributeDeleteDialog"; import AttributeDeleteDialog from "../../components/AttributeDeleteDialog";
import AttributePage from "../../components/AttributePage"; import AttributePage from "../../components/AttributePage";
import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog"; import AttributeValueDeleteDialog from "../../components/AttributeValueDeleteDialog";
@ -95,7 +96,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
const handleValueReorderMutation = (data: AttributeValueReorder) => { const handleValueReorderMutation = (data: AttributeValueReorder) => {
if (data.attributeReorderValues.errors.length !== 0) { if (data.attributeReorderValues.errors.length !== 0) {
notify({ notify({
text: data.attributeReorderValues.errors[0].message text: getProductErrorMessage(
data.attributeReorderValues.errors[0],
intl
)
}); });
} }
}; };
@ -155,12 +159,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
<AttributePage <AttributePage
attribute={maybe(() => data.attribute)} attribute={maybe(() => data.attribute)}
disabled={loading} disabled={loading}
errors={maybe( errors={
() =>
attributeUpdateOpts.data attributeUpdateOpts.data
.attributeUpdate.errors, ?.attributeUpdate.errors || []
[] }
)}
onBack={() => onBack={() =>
navigate(attributeListUrl()) navigate(attributeListUrl())
} }
@ -253,12 +255,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValueCreateOpts.status attributeValueCreateOpts.status
} }
disabled={loading} disabled={loading}
errors={maybe( errors={
() =>
attributeValueCreateOpts.data attributeValueCreateOpts.data
.attributeValueCreate.errors, ?.attributeValueCreate.errors || []
[] }
)}
open={params.action === "add-value"} open={params.action === "add-value"}
onClose={closeModal} onClose={closeModal}
onSubmit={input => onSubmit={input =>
@ -280,12 +280,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValueUpdateOpts.status attributeValueUpdateOpts.status
} }
disabled={loading} disabled={loading}
errors={maybe( errors={
() =>
attributeValueUpdateOpts.data attributeValueUpdateOpts.data
.attributeValueUpdate.errors, ?.attributeValueUpdate.errors || []
[] }
)}
open={params.action === "edit-value"} open={params.action === "edit-value"}
onClose={closeModal} onClose={closeModal}
onSubmit={input => onSubmit={input =>

View file

@ -1,5 +1,6 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import { accountFragmentError } from "@saleor/customers/mutations";
import { TypedMutation } from "../mutations"; import { TypedMutation } from "../mutations";
import { import {
RequestPasswordReset, RequestPasswordReset,
@ -64,11 +65,11 @@ export const TypedVerifyTokenMutation = TypedMutation<
>(tokenVerifyMutation); >(tokenVerifyMutation);
export const requestPasswordReset = gql` export const requestPasswordReset = gql`
${accountFragmentError}
mutation RequestPasswordReset($email: String!, $redirectUrl: String!) { mutation RequestPasswordReset($email: String!, $redirectUrl: String!) {
requestPasswordReset(email: $email, redirectUrl: $redirectUrl) { requestPasswordReset(email: $email, redirectUrl: $redirectUrl) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
} }
} }
@ -79,14 +80,14 @@ export const RequestPasswordResetMutation = TypedMutation<
>(requestPasswordReset); >(requestPasswordReset);
export const setPassword = gql` export const setPassword = gql`
${accountFragmentError}
${fragmentUser} ${fragmentUser}
mutation SetPassword($email: String!, $password: String!, $token: String!) { mutation SetPassword($email: String!, $password: String!, $token: String!) {
setPassword(email: $email, password: $password, token: $token) { setPassword(email: $email, password: $password, token: $token) {
token errors: accountErrors {
errors { ...AccountErrorFragment
field
message
} }
token
user { user {
...User ...User
} }

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: RequestPasswordReset // GraphQL mutation operation: RequestPasswordReset
// ==================================================== // ====================================================
export interface RequestPasswordReset_requestPasswordReset_errors { export interface RequestPasswordReset_requestPasswordReset_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface RequestPasswordReset_requestPasswordReset { export interface RequestPasswordReset_requestPasswordReset {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: SetPassword
// ==================================================== // ====================================================
export interface SetPassword_setPassword_errors { export interface SetPassword_setPassword_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface SetPassword_setPassword_user_permissions { export interface SetPassword_setPassword_user_permissions {
@ -37,8 +37,8 @@ export interface SetPassword_setPassword_user {
export interface SetPassword_setPassword { export interface SetPassword_setPassword {
__typename: "SetPassword"; __typename: "SetPassword";
errors: SetPassword_setPassword_errors[] | null;
token: string | null; token: string | null;
errors: (SetPassword_setPassword_errors | null)[];
user: SetPassword_setPassword_user | null; user: SetPassword_setPassword_user | null;
} }

View file

@ -11,7 +11,7 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import SeoForm from "@saleor/components/SeoForm"; import SeoForm from "@saleor/components/SeoForm";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { UserError } from "../../../types"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import CategoryDetailsForm from "../../components/CategoryDetailsForm"; import CategoryDetailsForm from "../../components/CategoryDetailsForm";
interface FormData { interface FormData {
@ -29,7 +29,7 @@ const initialData: FormData = {
}; };
export interface CategoryCreatePageProps { export interface CategoryCreatePageProps {
errors: UserError[]; errors: ProductErrorFragment[];
disabled: boolean; disabled: boolean;
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
onSubmit(data: FormData); onSubmit(data: FormData);

View file

@ -9,8 +9,8 @@ import CardTitle from "@saleor/components/CardTitle";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import RichTextEditor from "@saleor/components/RichTextEditor"; import RichTextEditor from "@saleor/components/RichTextEditor";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types"; import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { CategoryDetails_category } from "../../types/CategoryDetails"; import { CategoryDetails_category } from "../../types/CategoryDetails";
@ -21,7 +21,7 @@ interface CategoryDetailsFormProps {
description: RawDraftContentState; description: RawDraftContentState;
}; };
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: ProductErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -34,6 +34,8 @@ export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["name", "descriptionJson"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -49,16 +51,16 @@ export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({
disabled={disabled} disabled={disabled}
value={data && data.name} value={data && data.name}
onChange={onChange} onChange={onChange}
error={!!getFieldError(errors, "name")} error={!!formErrors.name}
helperText={getFieldError(errors, "name")?.message} helperText={getProductErrorMessage(formErrors.name, intl)}
fullWidth fullWidth
/> />
</div> </div>
<FormSpacer /> <FormSpacer />
<RichTextEditor <RichTextEditor
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "descriptionJson")} error={!!formErrors.descriptionJson}
helperText={getFieldError(errors, "descriptionJson")?.message} helperText={getProductErrorMessage(formErrors.descriptionJson, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Category Description" defaultMessage: "Category Description"
})} })}

View file

@ -15,8 +15,9 @@ import SaveButtonBar from "@saleor/components/SaveButtonBar";
import SeoForm from "@saleor/components/SeoForm"; import SeoForm from "@saleor/components/SeoForm";
import { Tab, TabContainer } from "@saleor/components/Tab"; import { Tab, TabContainer } from "@saleor/components/Tab";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { TabListActions, UserError } from "../../../types"; import { TabListActions } from "../../../types";
import CategoryDetailsForm from "../../components/CategoryDetailsForm"; import CategoryDetailsForm from "../../components/CategoryDetailsForm";
import CategoryList from "../../components/CategoryList"; import CategoryList from "../../components/CategoryList";
import { import {
@ -44,7 +45,7 @@ export interface CategoryUpdatePageProps
extends TabListActions<"productListToolbar" | "subcategoryListToolbar"> { extends TabListActions<"productListToolbar" | "subcategoryListToolbar"> {
changeTab: (index: CategoryPageTab) => void; changeTab: (index: CategoryPageTab) => void;
currentTab: CategoryPageTab; currentTab: CategoryPageTab;
errors: UserError[]; errors: ProductErrorFragment[];
disabled: boolean; disabled: boolean;
category: CategoryDetails_category; category: CategoryDetails_category;
products: CategoryDetails_category_products_edges_node[]; products: CategoryDetails_category_products_edges_node[];

View file

@ -1,6 +1,7 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import makeMutation from "@saleor/hooks/makeMutation"; import makeMutation from "@saleor/hooks/makeMutation";
import { productErrorFragment } from "@saleor/attributes/mutations";
import { categoryDetailsFragment } from "./queries"; import { categoryDetailsFragment } from "./queries";
import { import {
CategoryBulkDelete, CategoryBulkDelete,
@ -20,11 +21,11 @@ import {
} from "./types/CategoryUpdate"; } from "./types/CategoryUpdate";
export const categoryDeleteMutation = gql` export const categoryDeleteMutation = gql`
${productErrorFragment}
mutation CategoryDelete($id: ID!) { mutation CategoryDelete($id: ID!) {
categoryDelete(id: $id) { categoryDelete(id: $id) {
errors { errors: productErrors {
field ...ProductErrorFragment
message
} }
} }
} }
@ -36,15 +37,15 @@ export const useCategoryDeleteMutation = makeMutation<
export const categoryCreateMutation = gql` export const categoryCreateMutation = gql`
${categoryDetailsFragment} ${categoryDetailsFragment}
${productErrorFragment}
mutation CategoryCreate($parent: ID, $input: CategoryInput!) { mutation CategoryCreate($parent: ID, $input: CategoryInput!) {
categoryCreate(parent: $parent, input: $input) { categoryCreate(parent: $parent, input: $input) {
errors {
field
message
}
category { category {
...CategoryDetailsFragment ...CategoryDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -55,15 +56,15 @@ export const useCategoryCreateMutation = makeMutation<
export const categoryUpdateMutation = gql` export const categoryUpdateMutation = gql`
${categoryDetailsFragment} ${categoryDetailsFragment}
${productErrorFragment}
mutation CategoryUpdate($id: ID!, $input: CategoryInput!) { mutation CategoryUpdate($id: ID!, $input: CategoryInput!) {
categoryUpdate(id: $id, input: $input) { categoryUpdate(id: $id, input: $input) {
errors {
field
message
}
category { category {
...CategoryDetailsFragment ...CategoryDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -73,11 +74,11 @@ export const useCategoryUpdateMutation = makeMutation<
>(categoryUpdateMutation); >(categoryUpdateMutation);
export const categoryBulkDeleteMutation = gql` export const categoryBulkDeleteMutation = gql`
${productErrorFragment}
mutation CategoryBulkDelete($ids: [ID]!) { mutation CategoryBulkDelete($ids: [ID]!) {
categoryBulkDelete(ids: $ids) { categoryBulkDelete(ids: $ids) {
errors { errors: productErrors {
field ...ProductErrorFragment
message
} }
} }
} }

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CategoryBulkDelete // GraphQL mutation operation: CategoryBulkDelete
// ==================================================== // ====================================================
export interface CategoryBulkDelete_categoryBulkDelete_errors { export interface CategoryBulkDelete_categoryBulkDelete_errors {
__typename: "Error"; __typename: "ProductError";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface CategoryBulkDelete_categoryBulkDelete { export interface CategoryBulkDelete_categoryBulkDelete {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: CategoryCreate
// ==================================================== // ====================================================
export interface CategoryCreate_categoryCreate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface CategoryCreate_categoryCreate_category_backgroundImage { export interface CategoryCreate_categoryCreate_category_backgroundImage {
__typename: "Image"; __typename: "Image";
alt: string | null; alt: string | null;
@ -36,10 +30,16 @@ export interface CategoryCreate_categoryCreate_category {
parent: CategoryCreate_categoryCreate_category_parent | null; parent: CategoryCreate_categoryCreate_category_parent | null;
} }
export interface CategoryCreate_categoryCreate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface CategoryCreate_categoryCreate { export interface CategoryCreate_categoryCreate {
__typename: "CategoryCreate"; __typename: "CategoryCreate";
errors: CategoryCreate_categoryCreate_errors[];
category: CategoryCreate_categoryCreate_category | null; category: CategoryCreate_categoryCreate_category | null;
errors: CategoryCreate_categoryCreate_errors[];
} }
export interface CategoryCreate { export interface CategoryCreate {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CategoryDelete // GraphQL mutation operation: CategoryDelete
// ==================================================== // ====================================================
export interface CategoryDelete_categoryDelete_errors { export interface CategoryDelete_categoryDelete_errors {
__typename: "Error"; __typename: "ProductError";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface CategoryDelete_categoryDelete { export interface CategoryDelete_categoryDelete {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: CategoryUpdate
// ==================================================== // ====================================================
export interface CategoryUpdate_categoryUpdate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface CategoryUpdate_categoryUpdate_category_backgroundImage { export interface CategoryUpdate_categoryUpdate_category_backgroundImage {
__typename: "Image"; __typename: "Image";
alt: string | null; alt: string | null;
@ -36,10 +30,16 @@ export interface CategoryUpdate_categoryUpdate_category {
parent: CategoryUpdate_categoryUpdate_category_parent | null; parent: CategoryUpdate_categoryUpdate_category_parent | null;
} }
export interface CategoryUpdate_categoryUpdate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface CategoryUpdate_categoryUpdate { export interface CategoryUpdate_categoryUpdate {
__typename: "CategoryUpdate"; __typename: "CategoryUpdate";
errors: CategoryUpdate_categoryUpdate_errors[];
category: CategoryUpdate_categoryUpdate_category | null; category: CategoryUpdate_categoryUpdate_category | null;
errors: CategoryUpdate_categoryUpdate_errors[];
} }
export interface CategoryUpdate { export interface CategoryUpdate {

View file

@ -99,7 +99,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
); );
if (backgroundImageError) { if (backgroundImageError) {
notify({ notify({
text: backgroundImageError.message text: intl.formatMessage(commonMessages.somethingWentWrong)
}); });
} }
} }

View file

@ -17,7 +17,7 @@ import SeoForm from "@saleor/components/SeoForm";
import VisibilityCard from "@saleor/components/VisibilityCard"; import VisibilityCard from "@saleor/components/VisibilityCard";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
import { commonMessages, sectionNames } from "@saleor/intl"; import { commonMessages, sectionNames } from "@saleor/intl";
import { UserError } from "../../../types"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import CollectionDetails from "../CollectionDetails/CollectionDetails"; import CollectionDetails from "../CollectionDetails/CollectionDetails";
import { CollectionImage } from "../CollectionImage/CollectionImage"; import { CollectionImage } from "../CollectionImage/CollectionImage";
@ -37,7 +37,7 @@ export interface CollectionCreatePageFormData {
export interface CollectionCreatePageProps { export interface CollectionCreatePageProps {
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: ProductErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;
onSubmit: (data: CollectionCreatePageFormData) => void; onSubmit: (data: CollectionCreatePageFormData) => void;

View file

@ -10,8 +10,8 @@ import FormSpacer from "@saleor/components/FormSpacer";
import RichTextEditor from "@saleor/components/RichTextEditor"; import RichTextEditor from "@saleor/components/RichTextEditor";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { UserError } from "@saleor/types"; import { getProductErrorMessage, getFormErrors } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { CollectionDetails_collection } from "../../types/CollectionDetails"; import { CollectionDetails_collection } from "../../types/CollectionDetails";
export interface CollectionDetailsProps { export interface CollectionDetailsProps {
@ -21,7 +21,7 @@ export interface CollectionDetailsProps {
name: string; name: string;
}; };
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: ProductErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -34,6 +34,8 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["name", "descriptionJson"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -49,14 +51,14 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
disabled={disabled} disabled={disabled}
value={data.name} value={data.name}
onChange={onChange} onChange={onChange}
error={!!getFieldError(errors, "name")} error={!!formErrors.name}
helperText={getFieldError(errors, "name")?.message} helperText={getProductErrorMessage(formErrors.name, intl)}
fullWidth fullWidth
/> />
<FormSpacer /> <FormSpacer />
<RichTextEditor <RichTextEditor
error={!!getFieldError(errors, "descriptionJson")} error={!!formErrors.descriptionJson}
helperText={getFieldError(errors, "descriptionJson")?.message} helperText={getProductErrorMessage(formErrors.descriptionJson, intl)}
initial={maybe(() => JSON.parse(collection.descriptionJson))} initial={maybe(() => JSON.parse(collection.descriptionJson))}
label={intl.formatMessage(commonMessages.description)} label={intl.formatMessage(commonMessages.description)}
name="description" name="description"

View file

@ -17,8 +17,9 @@ import SeoForm from "@saleor/components/SeoForm";
import VisibilityCard from "@saleor/components/VisibilityCard"; import VisibilityCard from "@saleor/components/VisibilityCard";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { ListActions, PageListProps, UserError } from "../../../types"; import { ListActions, PageListProps } from "../../../types";
import { CollectionDetails_collection } from "../../types/CollectionDetails"; import { CollectionDetails_collection } from "../../types/CollectionDetails";
import CollectionDetails from "../CollectionDetails/CollectionDetails"; import CollectionDetails from "../CollectionDetails/CollectionDetails";
import { CollectionImage } from "../CollectionImage/CollectionImage"; import { CollectionImage } from "../CollectionImage/CollectionImage";
@ -37,7 +38,7 @@ export interface CollectionDetailsPageFormData {
export interface CollectionDetailsPageProps extends PageListProps, ListActions { export interface CollectionDetailsPageProps extends PageListProps, ListActions {
collection: CollectionDetails_collection; collection: CollectionDetails_collection;
errors: UserError[]; errors: ProductErrorFragment[];
isFeatured: boolean; isFeatured: boolean;
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;

View file

@ -54,6 +54,7 @@ interface CollectionUpdateOperationsProps {
>; >;
}) => React.ReactNode; }) => React.ReactNode;
onUpdate: (data: CollectionUpdate) => void; onUpdate: (data: CollectionUpdate) => void;
onUpdateWithCollection: (data: CollectionUpdateWithHomepage) => void;
onProductAssign: (data: CollectionAssignProduct) => void; onProductAssign: (data: CollectionAssignProduct) => void;
onProductUnassign: (data: UnassignCollectionProduct) => void; onProductUnassign: (data: UnassignCollectionProduct) => void;
onRemove: (data: RemoveCollection) => void; onRemove: (data: RemoveCollection) => void;
@ -62,6 +63,7 @@ interface CollectionUpdateOperationsProps {
const CollectionOperations: React.FC<CollectionUpdateOperationsProps> = ({ const CollectionOperations: React.FC<CollectionUpdateOperationsProps> = ({
children, children,
onUpdate, onUpdate,
onUpdateWithCollection,
onProductAssign, onProductAssign,
onProductUnassign, onProductUnassign,
onRemove onRemove
@ -72,7 +74,9 @@ const CollectionOperations: React.FC<CollectionUpdateOperationsProps> = ({
{(...removeCollection) => ( {(...removeCollection) => (
<TypedCollectionAssignProductMutation onCompleted={onProductAssign}> <TypedCollectionAssignProductMutation onCompleted={onProductAssign}>
{(...assignProduct) => ( {(...assignProduct) => (
<TypedCollectionUpdateWithHomepageMutation onCompleted={onUpdate}> <TypedCollectionUpdateWithHomepageMutation
onCompleted={onUpdateWithCollection}
>
{(...updateWithHomepage) => ( {(...updateWithHomepage) => (
<TypedUnassignCollectionProductMutation <TypedUnassignCollectionProductMutation
onCompleted={onProductUnassign} onCompleted={onProductUnassign}

View file

@ -1,5 +1,6 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import { productErrorFragment } from "@saleor/attributes/mutations";
import { TypedMutation } from "../mutations"; import { TypedMutation } from "../mutations";
import { import {
collectionDetailsFragment, collectionDetailsFragment,
@ -38,17 +39,24 @@ import {
UnassignCollectionProductVariables UnassignCollectionProductVariables
} from "./types/UnassignCollectionProduct"; } from "./types/UnassignCollectionProduct";
export const ShopErrorFragment = gql`
fragment ShopErrorFragment on ShopError {
code
field
}
`;
const collectionUpdate = gql` const collectionUpdate = gql`
${collectionDetailsFragment} ${collectionDetailsFragment}
${productErrorFragment}
mutation CollectionUpdate($id: ID!, $input: CollectionInput!) { mutation CollectionUpdate($id: ID!, $input: CollectionInput!) {
collectionUpdate(id: $id, input: $input) { collectionUpdate(id: $id, input: $input) {
errors {
field
message
}
collection { collection {
...CollectionDetailsFragment ...CollectionDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -59,15 +67,16 @@ export const TypedCollectionUpdateMutation = TypedMutation<
const collectionUpdateWithHomepage = gql` const collectionUpdateWithHomepage = gql`
${collectionDetailsFragment} ${collectionDetailsFragment}
${productErrorFragment}
${ShopErrorFragment}
mutation CollectionUpdateWithHomepage( mutation CollectionUpdateWithHomepage(
$id: ID! $id: ID!
$input: CollectionInput! $input: CollectionInput!
$homepageId: ID $homepageId: ID
) { ) {
homepageCollectionUpdate(collection: $homepageId) { homepageCollectionUpdate(collection: $homepageId) {
errors { errors: shopErrors {
field ...ShopErrorFragment
message
} }
shop { shop {
homepageCollection { homepageCollection {
@ -76,13 +85,12 @@ const collectionUpdateWithHomepage = gql`
} }
} }
collectionUpdate(id: $id, input: $input) { collectionUpdate(id: $id, input: $input) {
errors {
field
message
}
collection { collection {
...CollectionDetailsFragment ...CollectionDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -93,6 +101,7 @@ export const TypedCollectionUpdateWithHomepageMutation = TypedMutation<
const assignCollectionProduct = gql` const assignCollectionProduct = gql`
${collectionProductFragment} ${collectionProductFragment}
${productErrorFragment}
mutation CollectionAssignProduct( mutation CollectionAssignProduct(
$collectionId: ID! $collectionId: ID!
$productIds: [ID!]! $productIds: [ID!]!
@ -102,10 +111,6 @@ const assignCollectionProduct = gql`
$before: String $before: String
) { ) {
collectionAddProducts(collectionId: $collectionId, products: $productIds) { collectionAddProducts(collectionId: $collectionId, products: $productIds) {
errors {
field
message
}
collection { collection {
id id
products(first: $first, after: $after, before: $before, last: $last) { products(first: $first, after: $after, before: $before, last: $last) {
@ -122,6 +127,9 @@ const assignCollectionProduct = gql`
} }
} }
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -132,15 +140,15 @@ export const TypedCollectionAssignProductMutation = TypedMutation<
const createCollection = gql` const createCollection = gql`
${collectionDetailsFragment} ${collectionDetailsFragment}
${productErrorFragment}
mutation CreateCollection($input: CollectionCreateInput!) { mutation CreateCollection($input: CollectionCreateInput!) {
collectionCreate(input: $input) { collectionCreate(input: $input) {
errors {
field
message
}
collection { collection {
...CollectionDetailsFragment ...CollectionDetailsFragment
} }
errors: productErrors {
...ProductErrorFragment
}
} }
} }
`; `;
@ -150,11 +158,11 @@ export const TypedCollectionCreateMutation = TypedMutation<
>(createCollection); >(createCollection);
const removeCollection = gql` const removeCollection = gql`
${productErrorFragment}
mutation RemoveCollection($id: ID!) { mutation RemoveCollection($id: ID!) {
collectionDelete(id: $id) { collectionDelete(id: $id) {
errors { errors: productErrors {
field ...ProductErrorFragment
message
} }
} }
} }
@ -165,6 +173,7 @@ export const TypedCollectionRemoveMutation = TypedMutation<
>(removeCollection); >(removeCollection);
const unassignCollectionProduct = gql` const unassignCollectionProduct = gql`
${productErrorFragment}
mutation UnassignCollectionProduct( mutation UnassignCollectionProduct(
$collectionId: ID! $collectionId: ID!
$productIds: [ID]! $productIds: [ID]!
@ -177,10 +186,6 @@ const unassignCollectionProduct = gql`
collectionId: $collectionId collectionId: $collectionId
products: $productIds products: $productIds
) { ) {
errors {
field
message
}
collection { collection {
id id
products(first: $first, after: $after, before: $before, last: $last) { 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); >(unassignCollectionProduct);
const collectionBulkDelete = gql` const collectionBulkDelete = gql`
${productErrorFragment}
mutation CollectionBulkDelete($ids: [ID]!) { mutation CollectionBulkDelete($ids: [ID]!) {
collectionBulkDelete(ids: $ids) { collectionBulkDelete(ids: $ids) {
errors { errors: productErrors {
field ...ProductErrorFragment
message
} }
} }
} }
@ -230,11 +238,11 @@ export const TypedCollectionBulkDelete = TypedMutation<
>(collectionBulkDelete); >(collectionBulkDelete);
const collectionBulkPublish = gql` const collectionBulkPublish = gql`
${productErrorFragment}
mutation CollectionBulkPublish($ids: [ID]!, $isPublished: Boolean!) { mutation CollectionBulkPublish($ids: [ID]!, $isPublished: Boolean!) {
collectionBulkPublish(ids: $ids, isPublished: $isPublished) { collectionBulkPublish(ids: $ids, isPublished: $isPublished) {
errors { errors: productErrors {
field ...ProductErrorFragment
message
} }
} }
} }

View file

@ -2,16 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CollectionAssignProduct // 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 { export interface CollectionAssignProduct_collectionAddProducts_collection_products_edges_node_productType {
__typename: "ProductType"; __typename: "ProductType";
id: string; id: string;
@ -57,10 +53,16 @@ export interface CollectionAssignProduct_collectionAddProducts_collection {
products: CollectionAssignProduct_collectionAddProducts_collection_products | null; products: CollectionAssignProduct_collectionAddProducts_collection_products | null;
} }
export interface CollectionAssignProduct_collectionAddProducts_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface CollectionAssignProduct_collectionAddProducts { export interface CollectionAssignProduct_collectionAddProducts {
__typename: "CollectionAddProducts"; __typename: "CollectionAddProducts";
errors: CollectionAssignProduct_collectionAddProducts_errors[];
collection: CollectionAssignProduct_collectionAddProducts_collection | null; collection: CollectionAssignProduct_collectionAddProducts_collection | null;
errors: CollectionAssignProduct_collectionAddProducts_errors[];
} }
export interface CollectionAssignProduct { export interface CollectionAssignProduct {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CollectionBulkDelete // GraphQL mutation operation: CollectionBulkDelete
// ==================================================== // ====================================================
export interface CollectionBulkDelete_collectionBulkDelete_errors { export interface CollectionBulkDelete_collectionBulkDelete_errors {
__typename: "Error"; __typename: "ProductError";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface CollectionBulkDelete_collectionBulkDelete { export interface CollectionBulkDelete_collectionBulkDelete {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CollectionBulkPublish // GraphQL mutation operation: CollectionBulkPublish
// ==================================================== // ====================================================
export interface CollectionBulkPublish_collectionBulkPublish_errors { export interface CollectionBulkPublish_collectionBulkPublish_errors {
__typename: "Error"; __typename: "ProductError";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface CollectionBulkPublish_collectionBulkPublish { export interface CollectionBulkPublish_collectionBulkPublish {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: CollectionUpdate
// ==================================================== // ====================================================
export interface CollectionUpdate_collectionUpdate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface CollectionUpdate_collectionUpdate_collection_backgroundImage { export interface CollectionUpdate_collectionUpdate_collection_backgroundImage {
__typename: "Image"; __typename: "Image";
alt: string | null; alt: string | null;
@ -32,10 +26,16 @@ export interface CollectionUpdate_collectionUpdate_collection {
seoTitle: string | null; seoTitle: string | null;
} }
export interface CollectionUpdate_collectionUpdate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface CollectionUpdate_collectionUpdate { export interface CollectionUpdate_collectionUpdate {
__typename: "CollectionUpdate"; __typename: "CollectionUpdate";
errors: CollectionUpdate_collectionUpdate_errors[];
collection: CollectionUpdate_collectionUpdate_collection | null; collection: CollectionUpdate_collectionUpdate_collection | null;
errors: CollectionUpdate_collectionUpdate_errors[];
} }
export interface CollectionUpdate { export interface CollectionUpdate {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: CollectionUpdateWithHomepage
// ==================================================== // ====================================================
export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_errors { export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_errors {
__typename: "Error"; __typename: "ShopError";
code: ShopErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_shop_homepageCollection { export interface CollectionUpdateWithHomepage_homepageCollectionUpdate_shop_homepageCollection {
@ -30,12 +30,6 @@ export interface CollectionUpdateWithHomepage_homepageCollectionUpdate {
shop: CollectionUpdateWithHomepage_homepageCollectionUpdate_shop | null; shop: CollectionUpdateWithHomepage_homepageCollectionUpdate_shop | null;
} }
export interface CollectionUpdateWithHomepage_collectionUpdate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface CollectionUpdateWithHomepage_collectionUpdate_collection_backgroundImage { export interface CollectionUpdateWithHomepage_collectionUpdate_collection_backgroundImage {
__typename: "Image"; __typename: "Image";
alt: string | null; alt: string | null;
@ -54,10 +48,16 @@ export interface CollectionUpdateWithHomepage_collectionUpdate_collection {
seoTitle: string | null; seoTitle: string | null;
} }
export interface CollectionUpdateWithHomepage_collectionUpdate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface CollectionUpdateWithHomepage_collectionUpdate { export interface CollectionUpdateWithHomepage_collectionUpdate {
__typename: "CollectionUpdate"; __typename: "CollectionUpdate";
errors: CollectionUpdateWithHomepage_collectionUpdate_errors[];
collection: CollectionUpdateWithHomepage_collectionUpdate_collection | null; collection: CollectionUpdateWithHomepage_collectionUpdate_collection | null;
errors: CollectionUpdateWithHomepage_collectionUpdate_errors[];
} }
export interface CollectionUpdateWithHomepage { export interface CollectionUpdateWithHomepage {

View file

@ -2,18 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // 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 // GraphQL mutation operation: CreateCollection
// ==================================================== // ====================================================
export interface CreateCollection_collectionCreate_errors {
__typename: "Error";
field: string | null;
message: string | null;
}
export interface CreateCollection_collectionCreate_collection_backgroundImage { export interface CreateCollection_collectionCreate_collection_backgroundImage {
__typename: "Image"; __typename: "Image";
alt: string | null; alt: string | null;
@ -32,10 +26,16 @@ export interface CreateCollection_collectionCreate_collection {
seoTitle: string | null; seoTitle: string | null;
} }
export interface CreateCollection_collectionCreate_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface CreateCollection_collectionCreate { export interface CreateCollection_collectionCreate {
__typename: "CollectionCreate"; __typename: "CollectionCreate";
errors: CreateCollection_collectionCreate_errors[];
collection: CreateCollection_collectionCreate_collection | null; collection: CreateCollection_collectionCreate_collection | null;
errors: CreateCollection_collectionCreate_errors[];
} }
export interface CreateCollection { export interface CreateCollection {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: RemoveCollection // GraphQL mutation operation: RemoveCollection
// ==================================================== // ====================================================
export interface RemoveCollection_collectionDelete_errors { export interface RemoveCollection_collectionDelete_errors {
__typename: "Error"; __typename: "ProductError";
code: ProductErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface RemoveCollection_collectionDelete { export interface RemoveCollection_collectionDelete {

View file

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

View file

@ -2,16 +2,12 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: UnassignCollectionProduct // 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 { export interface UnassignCollectionProduct_collectionRemoveProducts_collection_products_edges_node_productType {
__typename: "ProductType"; __typename: "ProductType";
id: string; id: string;
@ -57,10 +53,16 @@ export interface UnassignCollectionProduct_collectionRemoveProducts_collection {
products: UnassignCollectionProduct_collectionRemoveProducts_collection_products | null; products: UnassignCollectionProduct_collectionRemoveProducts_collection_products | null;
} }
export interface UnassignCollectionProduct_collectionRemoveProducts_errors {
__typename: "ProductError";
code: ProductErrorCode;
field: string | null;
}
export interface UnassignCollectionProduct_collectionRemoveProducts { export interface UnassignCollectionProduct_collectionRemoveProducts {
__typename: "CollectionRemoveProducts"; __typename: "CollectionRemoveProducts";
errors: UnassignCollectionProduct_collectionRemoveProducts_errors[];
collection: UnassignCollectionProduct_collectionRemoveProducts_collection | null; collection: UnassignCollectionProduct_collectionRemoveProducts_collection | null;
errors: UnassignCollectionProduct_collectionRemoveProducts_errors[];
} }
export interface UnassignCollectionProduct { export interface UnassignCollectionProduct {

View file

@ -4,7 +4,7 @@ import { useIntl } from "react-intl";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { maybe } from "../../misc"; import { commonMessages } from "@saleor/intl";
import { CollectionCreateInput } from "../../types/globalTypes"; import { CollectionCreateInput } from "../../types/globalTypes";
import CollectionCreatePage from "../components/CollectionCreatePage/CollectionCreatePage"; import CollectionCreatePage from "../components/CollectionCreatePage/CollectionCreatePage";
import { TypedCollectionCreateMutation } from "../mutations"; import { TypedCollectionCreateMutation } from "../mutations";
@ -19,9 +19,7 @@ export const CollectionCreate: React.FC = () => {
const handleCollectionCreateSuccess = (data: CreateCollection) => { const handleCollectionCreateSuccess = (data: CreateCollection) => {
if (data.collectionCreate.errors.length === 0) { if (data.collectionCreate.errors.length === 0) {
notify({ notify({
text: intl.formatMessage({ text: intl.formatMessage(commonMessages.savedChanges)
defaultMessage: "Created collection"
})
}); });
navigate(collectionUrl(data.collectionCreate.collection.id)); navigate(collectionUrl(data.collectionCreate.collection.id));
} else { } else {
@ -31,7 +29,7 @@ export const CollectionCreate: React.FC = () => {
); );
if (backgroundImageError) { if (backgroundImageError) {
notify({ notify({
text: backgroundImageError.message text: intl.formatMessage(commonMessages.somethingWentWrong)
}); });
} }
} }
@ -47,10 +45,7 @@ export const CollectionCreate: React.FC = () => {
})} })}
/> />
<CollectionCreatePage <CollectionCreatePage
errors={maybe( errors={createCollectionOpts.data?.collectionCreate.errors || []}
() => createCollectionOpts.data.collectionCreate.errors,
[]
)}
onBack={() => navigate(collectionListUrl())} onBack={() => navigate(collectionListUrl())}
disabled={createCollectionOpts.loading} disabled={createCollectionOpts.loading}
onSubmit={formData => onSubmit={formData =>

View file

@ -35,6 +35,7 @@ import {
CollectionUrlQueryParams, CollectionUrlQueryParams,
CollectionUrlDialog CollectionUrlDialog
} from "../urls"; } from "../urls";
import { CollectionUpdateWithHomepage } from "../types/CollectionUpdateWithHomepage";
interface CollectionDetailsProps { interface CollectionDetailsProps {
id: string; id: string;
@ -88,11 +89,18 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
); );
if (backgroundImageError) { if (backgroundImageError) {
notify({ 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) => { const handleProductAssign = (data: CollectionAssignProduct) => {
if (data.collectionAddProducts.errors.length === 0) { if (data.collectionAddProducts.errors.length === 0) {
@ -130,6 +138,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
return ( return (
<CollectionOperations <CollectionOperations
onUpdate={handleCollectionUpdate} onUpdate={handleCollectionUpdate}
onUpdateWithCollection={handleCollectioUpdateWithHomepage}
onProductAssign={handleProductAssign} onProductAssign={handleProductAssign}
onProductUnassign={handleProductUnassign} onProductUnassign={handleProductUnassign}
onRemove={handleCollectionRemove} onRemove={handleCollectionRemove}
@ -204,7 +213,7 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
onAdd={() => openModal("assign")} onAdd={() => openModal("assign")}
onBack={handleBack} onBack={handleBack}
disabled={loading} disabled={loading}
collection={maybe(() => data.collection)} collection={data?.collection}
errors={ errors={
updateCollection.opts?.data?.collectionUpdate.errors || [] updateCollection.opts?.data?.collectionUpdate.errors || []
} }

View file

@ -1,12 +1,15 @@
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl, IntlShape } from "react-intl";
import { AddressTypeInput } from "@saleor/customers/types"; import { AddressTypeInput } from "@saleor/customers/types";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types"; import { getFormErrors } from "@saleor/utils/errors";
import { getFieldError } 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 FormSpacer from "../FormSpacer";
import SingleAutocompleteSelectField, { import SingleAutocompleteSelectField, {
SingleAutocompleteChoiceType SingleAutocompleteChoiceType
@ -28,11 +31,22 @@ interface AddressEditProps {
countryDisplayValue: string; countryDisplayValue: string;
data: AddressTypeInput; data: AddressTypeInput;
disabled?: boolean; disabled?: boolean;
errors: UserError[]; errors: Array<AccountErrorFragment | OrderErrorFragment>;
onChange(event: React.ChangeEvent<any>); onChange(event: React.ChangeEvent<any>);
onCountryChange(event: React.ChangeEvent<any>); onCountryChange(event: React.ChangeEvent<any>);
} }
function getErrorMessage(
err: AccountErrorFragment | OrderErrorFragment,
intl: IntlShape
): string {
if (err?.__typename === "AccountError") {
return getAccountErrorMessage(err, intl);
}
return getOrderErrorMessage(err, intl);
}
const AddressEdit: React.FC<AddressEditProps> = props => { const AddressEdit: React.FC<AddressEditProps> = props => {
const { const {
countries, countries,
@ -43,18 +57,36 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
onChange, onChange,
onCountryChange onCountryChange
} = props; } = props;
const classes = useStyles(props);
const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const formFields: Array<keyof AddressTypeInput> = [
"city",
"cityArea",
"country",
"countryArea",
"firstName",
"lastName",
"companyName",
"phone",
"postalCode",
"streetAddress1",
"streetAddress2"
];
const formErrors = getFormErrors<
keyof AddressTypeInput,
AccountErrorFragment | OrderErrorFragment
>(formFields, errors);
return ( return (
<> <>
<div className={classes.root}> <div className={classes.root}>
<div> <div>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "firstName")} error={!!formErrors.firstName}
helperText={getFieldError(errors, "firstName")?.message} helperText={getErrorMessage(formErrors.firstName, intl)}
label={intl.formatMessage(commonMessages.firstName)} label={intl.formatMessage(commonMessages.firstName)}
name="firstName" name="firstName"
onChange={onChange} onChange={onChange}
@ -65,8 +97,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div> <div>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "lastName")} error={!!formErrors.lastName}
helperText={getFieldError(errors, "lastName")?.message} helperText={getErrorMessage(formErrors.lastName, intl)}
label={intl.formatMessage(commonMessages.lastName)} label={intl.formatMessage(commonMessages.lastName)}
name="lastName" name="lastName"
onChange={onChange} onChange={onChange}
@ -80,8 +112,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div> <div>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "companyName")} error={!!formErrors.companyName}
helperText={getFieldError(errors, "companyName")?.message} helperText={getErrorMessage(formErrors.companyName, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Company" defaultMessage: "Company"
})} })}
@ -94,9 +126,9 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div> <div>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "phone")} error={!!formErrors.phone}
fullWidth fullWidth
helperText={getFieldError(errors, "phone")?.message} helperText={getErrorMessage(formErrors.phone, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Phone" defaultMessage: "Phone"
})} })}
@ -109,8 +141,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<FormSpacer /> <FormSpacer />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "streetAddress1")} error={!!formErrors.streetAddress1}
helperText={getFieldError(errors, "streetAddress1")?.message} helperText={getErrorMessage(formErrors.streetAddress1, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Address line 1" defaultMessage: "Address line 1"
})} })}
@ -122,8 +154,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<FormSpacer /> <FormSpacer />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "streetAddress2")} error={!!formErrors.streetAddress2}
helperText={getFieldError(errors, "streetAddress2")?.message} helperText={getErrorMessage(formErrors.streetAddress2, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Address line 2" defaultMessage: "Address line 2"
})} })}
@ -137,8 +169,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div> <div>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "city")} error={!!formErrors.city}
helperText={getFieldError(errors, "city")?.message} helperText={getErrorMessage(formErrors.city, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "City" defaultMessage: "City"
})} })}
@ -151,8 +183,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div> <div>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "postalCode")} error={!!formErrors.postalCode}
helperText={getFieldError(errors, "postalCode")?.message} helperText={getErrorMessage(formErrors.postalCode, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "ZIP / Postal code" defaultMessage: "ZIP / Postal code"
})} })}
@ -170,8 +202,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<SingleAutocompleteSelectField <SingleAutocompleteSelectField
disabled={disabled} disabled={disabled}
displayValue={countryDisplayValue} displayValue={countryDisplayValue}
error={!!getFieldError(errors, "country")} error={!!formErrors.country}
helperText={getFieldError(errors, "country")?.message} helperText={getErrorMessage(formErrors.country, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Country" defaultMessage: "Country"
})} })}
@ -187,8 +219,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div> <div>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "countryArea")} error={!!formErrors.countryArea}
helperText={getFieldError(errors, "countryArea")?.message} helperText={getErrorMessage(formErrors.countryArea, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Country area" defaultMessage: "Country area"
})} })}

View file

@ -15,7 +15,7 @@ import { FormattedMessage } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import ResponsiveTable from "@saleor/components/ResponsiveTable"; import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { maybe, renderCollection } from "../../misc"; import { maybe, renderCollection, getStringOrPlaceholder } from "../../misc";
import { CountryFragment } from "../../taxes/types/CountryFragment"; import { CountryFragment } from "../../taxes/types/CountryFragment";
export interface CountryListProps { export interface CountryListProps {
@ -98,7 +98,7 @@ const CountryList: React.FC<CountryListProps> = props => {
<CardTitle <CardTitle
title={title} title={title}
toolbar={ toolbar={
<Button color="primary" onClick={onCountryAssign}> <Button color="primary" disabled={disabled} onClick={onCountryAssign}>
<FormattedMessage <FormattedMessage
defaultMessage="Assign countries" defaultMessage="Assign countries"
description="button" description="button"
@ -117,7 +117,7 @@ const CountryList: React.FC<CountryListProps> = props => {
defaultMessage="{number} Countries" defaultMessage="{number} Countries"
description="number of countries" description="number of countries"
values={{ values={{
number: maybe(() => countries.length.toString(), "...") number: getStringOrPlaceholder(countries?.length.toString())
}} }}
/> />
</TableCell> </TableCell>

View file

@ -30,7 +30,7 @@ const Story: React.FC<Partial<
MultiAutocompleteSelectFieldProps & { MultiAutocompleteSelectFieldProps & {
enableLoadMore: boolean; enableLoadMore: boolean;
} }
>> = ({ allowCustomValues, enableLoadMore }) => { >> = ({ enableLoadMore, ...rest }) => {
const { change, data: countries } = useMultiAutocomplete([suggestions[0]]); const { change, data: countries } = useMultiAutocomplete([suggestions[0]]);
return ( return (
@ -49,7 +49,7 @@ const Story: React.FC<Partial<
loading={loading} loading={loading}
hasMore={enableLoadMore ? hasMore : false} hasMore={enableLoadMore ? hasMore : false}
onFetchMore={enableLoadMore ? onFetchMore : undefined} onFetchMore={enableLoadMore ? onFetchMore : undefined}
allowCustomValues={allowCustomValues} {...rest}
/> />
)} )}
</ChoiceProvider> </ChoiceProvider>
@ -84,4 +84,5 @@ storiesOf("Generics / Multiple select with autocomplete", module)
.add("interactive with custom option", () => ( .add("interactive with custom option", () => (
<Story allowCustomValues={true} /> <Story allowCustomValues={true} />
)) ))
.add("interactive with load more", () => <Story enableLoadMore={true} />); .add("interactive with load more", () => <Story enableLoadMore={true} />)
.add("interactive with error", () => <Story error={true} />);

View file

@ -59,6 +59,7 @@ export interface MultiAutocompleteSelectFieldProps
extends Partial<FetchMoreProps> { extends Partial<FetchMoreProps> {
allowCustomValues?: boolean; allowCustomValues?: boolean;
displayValues: MultiAutocompleteChoiceType[]; displayValues: MultiAutocompleteChoiceType[];
error?: boolean;
name: string; name: string;
choices: MultiAutocompleteChoiceType[]; choices: MultiAutocompleteChoiceType[];
value: string[]; value: string[];
@ -70,19 +71,16 @@ export interface MultiAutocompleteSelectFieldProps
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const DebounceAutocomplete: React.ComponentType< const DebounceAutocomplete: React.ComponentType<DebounceProps<
DebounceProps<string> string
> = Debounce; >> = Debounce;
const MultiAutocompleteSelectFieldComponent: React.FC< const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFieldProps> = props => {
MultiAutocompleteSelectFieldProps
> = props => {
const { const {
allowCustomValues, allowCustomValues,
choices, choices,
displayValues, displayValues,
error,
hasMore, hasMore,
helperText, helperText,
label, label,
@ -147,6 +145,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<
id: undefined, id: undefined,
onClick: toggleMenu onClick: toggleMenu
}} }}
error={error}
helperText={helperText} helperText={helperText}
label={label} label={label}
fullWidth={true} fullWidth={true}
@ -191,9 +190,11 @@ const MultiAutocompleteSelectFieldComponent: React.FC<
); );
}; };
const MultiAutocompleteSelectField: React.FC< const MultiAutocompleteSelectField: React.FC<MultiAutocompleteSelectFieldProps> = ({
MultiAutocompleteSelectFieldProps choices,
> = ({ choices, fetchChoices, ...props }) => { fetchChoices,
...props
}) => {
const [query, setQuery] = React.useState(""); const [query, setQuery] = React.useState("");
if (fetchChoices) { if (fetchChoices) {

View file

@ -17,9 +17,9 @@ import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { UserError } from "@saleor/types";
import { AddressInput } from "@saleor/types/globalTypes"; import { AddressInput } from "@saleor/types/globalTypes";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
import { AddressTypeInput } from "../../types"; import { AddressTypeInput } from "../../types";
import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses"; import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses";
@ -30,7 +30,7 @@ export interface CustomerAddressDialogProps {
code: string; code: string;
label: string; label: string;
}>; }>;
errors: UserError[]; errors: AccountErrorFragment[];
open: boolean; open: boolean;
variant: "create" | "edit"; variant: "create" | "edit";
onClose: () => void; onClose: () => void;

View file

@ -9,7 +9,7 @@ import AddressEdit from "@saleor/components/AddressEdit";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { FormSpacer } from "@saleor/components/FormSpacer"; import { FormSpacer } from "@saleor/components/FormSpacer";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import { UserError } from "../../../types"; import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
import { AddressTypeInput } from "../../types"; import { AddressTypeInput } from "../../types";
const useStyles = makeStyles( const useStyles = makeStyles(
@ -26,7 +26,7 @@ export interface CustomerCreateAddressProps {
countryDisplayName: string; countryDisplayName: string;
data: AddressTypeInput; data: AddressTypeInput;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: AccountErrorFragment[];
onChange(event: React.ChangeEvent<any>); onChange(event: React.ChangeEvent<any>);
onCountryChange(event: React.ChangeEvent<any>); onCountryChange(event: React.ChangeEvent<any>);
} }

View file

@ -7,8 +7,9 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import { UserError } from "../../../types"; import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
import getAccountErrorMessage from "@saleor/utils/errors/account";
import { CustomerCreatePageFormData } from "../CustomerCreatePage"; import { CustomerCreatePageFormData } from "../CustomerCreatePage";
const useStyles = makeStyles( const useStyles = makeStyles(
@ -26,16 +27,21 @@ const useStyles = makeStyles(
export interface CustomerCreateDetailsProps { export interface CustomerCreateDetailsProps {
data: CustomerCreatePageFormData; data: CustomerCreatePageFormData;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: AccountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const CustomerCreateDetails: React.FC<CustomerCreateDetailsProps> = props => { const CustomerCreateDetails: React.FC<CustomerCreateDetailsProps> = props => {
const { data, disabled, errors, onChange } = props; const { data, disabled, errors, onChange } = props;
const classes = useStyles(props);
const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(
["customerFirstName", "customerLastName", "email"],
errors
);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -48,33 +54,39 @@ const CustomerCreateDetails: React.FC<CustomerCreateDetailsProps> = props => {
<div className={classes.root}> <div className={classes.root}>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "customerFirstName")} error={!!formErrors.customerFirstName}
fullWidth fullWidth
name="customerFirstName" name="customerFirstName"
label={intl.formatMessage(commonMessages.firstName)} label={intl.formatMessage(commonMessages.firstName)}
helperText={getFieldError(errors, "customerFirstName")?.message} helperText={getAccountErrorMessage(
formErrors.customerFirstName,
intl
)}
type="text" type="text"
value={data.customerFirstName} value={data.customerFirstName}
onChange={onChange} onChange={onChange}
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "customerLastName")} error={!!formErrors.customerLastName}
fullWidth fullWidth
name="customerLastName" name="customerLastName"
label={intl.formatMessage(commonMessages.lastName)} label={intl.formatMessage(commonMessages.lastName)}
helperText={getFieldError(errors, "customerLastName")?.message} helperText={getAccountErrorMessage(
formErrors.customerLastName,
intl
)}
type="text" type="text"
value={data.customerLastName} value={data.customerLastName}
onChange={onChange} onChange={onChange}
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "email")} error={!!formErrors.email}
fullWidth fullWidth
name="email" name="email"
label={intl.formatMessage(commonMessages.email)} label={intl.formatMessage(commonMessages.email)}
helperText={getFieldError(errors, "email")?.message} helperText={getAccountErrorMessage(formErrors.email, intl)}
type="email" type="email"
value={data.email} value={data.email}
onChange={onChange} onChange={onChange}
@ -84,5 +96,6 @@ const CustomerCreateDetails: React.FC<CustomerCreateDetailsProps> = props => {
</Card> </Card>
); );
}; };
CustomerCreateDetails.displayName = "CustomerCreateDetails"; CustomerCreateDetails.displayName = "CustomerCreateDetails";
export default CustomerCreateDetails; export default CustomerCreateDetails;

View file

@ -7,15 +7,16 @@ import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { FormSpacer } from "@saleor/components/FormSpacer"; import { FormSpacer } from "@saleor/components/FormSpacer";
import { UserError } from "@saleor/types"; import { getFormErrors } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import getAccountErrorMessage from "@saleor/utils/errors/account";
import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
export interface CustomerCreateNoteProps { export interface CustomerCreateNoteProps {
data: { data: {
note: string; note: string;
}; };
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: AccountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -27,6 +28,8 @@ const CustomerCreateNote: React.FC<CustomerCreateNoteProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["note"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -42,11 +45,11 @@ const CustomerCreateNote: React.FC<CustomerCreateNoteProps> = ({
<FormSpacer /> <FormSpacer />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "note")} error={!!formErrors.note}
fullWidth fullWidth
multiline multiline
name="note" name="note"
helperText={getFieldError(errors, "note")?.message} helperText={getAccountErrorMessage(formErrors.note, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Note", defaultMessage: "Note",
description: "note about customer" description: "note about customer"

View file

@ -13,7 +13,7 @@ import useAddressValidation from "@saleor/hooks/useAddressValidation";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { AddressInput } from "@saleor/types/globalTypes"; import { AddressInput } from "@saleor/types/globalTypes";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { UserError } from "../../../types"; import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
import { AddressTypeInput } from "../../types"; import { AddressTypeInput } from "../../types";
import { CustomerCreateData_shop_countries } from "../../types/CustomerCreateData"; import { CustomerCreateData_shop_countries } from "../../types/CustomerCreateData";
import CustomerCreateAddress from "../CustomerCreateAddress/CustomerCreateAddress"; import CustomerCreateAddress from "../CustomerCreateAddress/CustomerCreateAddress";
@ -52,7 +52,7 @@ const initialForm: CustomerCreatePageFormData & AddressTypeInput = {
export interface CustomerCreatePageProps { export interface CustomerCreatePageProps {
countries: CustomerCreateData_shop_countries[]; countries: CustomerCreateData_shop_countries[];
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: AccountErrorFragment[];
saveButtonBar: ConfirmButtonTransitionState; saveButtonBar: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;
onSubmit: (data: CustomerCreatePageSubmitData) => void; onSubmit: (data: CustomerCreatePageSubmitData) => void;

View file

@ -11,8 +11,9 @@ import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { UserError } from "@saleor/types"; import { getFormErrors } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import getAccountErrorMessage from "@saleor/utils/errors/account";
import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
import { CustomerDetails_user } from "../../types/CustomerDetails"; import { CustomerDetails_user } from "../../types/CustomerDetails";
const useStyles = makeStyles( const useStyles = makeStyles(
@ -40,16 +41,18 @@ export interface CustomerDetailsProps {
note: string; note: string;
}; };
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: AccountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const CustomerDetails: React.FC<CustomerDetailsProps> = props => { const CustomerDetails: React.FC<CustomerDetailsProps> = props => {
const { customer, data, disabled, errors, onChange } = props; const { customer, data, disabled, errors, onChange } = props;
const classes = useStyles(props);
const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["note"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -91,10 +94,10 @@ const CustomerDetails: React.FC<CustomerDetailsProps> = props => {
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "note")} error={!!formErrors.note}
fullWidth fullWidth
multiline multiline
helperText={getFieldError(errors, "note")?.message} helperText={getAccountErrorMessage(formErrors.note, intl)}
name="note" name="note"
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Note", defaultMessage: "Note",

View file

@ -10,8 +10,8 @@ import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
import { getUserName, maybe } from "../../../misc"; import { getUserName, maybe } from "../../../misc";
import { UserError } from "../../../types";
import { CustomerDetails_user } from "../../types/CustomerDetails"; import { CustomerDetails_user } from "../../types/CustomerDetails";
import CustomerAddresses from "../CustomerAddresses"; import CustomerAddresses from "../CustomerAddresses";
import CustomerDetails from "../CustomerDetails"; import CustomerDetails from "../CustomerDetails";
@ -30,7 +30,7 @@ export interface CustomerDetailsPageFormData {
export interface CustomerDetailsPageProps { export interface CustomerDetailsPageProps {
customer: CustomerDetails_user; customer: CustomerDetails_user;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: AccountErrorFragment[];
saveButtonBar: ConfirmButtonTransitionState; saveButtonBar: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;
onSubmit: (data: CustomerDetailsPageFormData) => void; onSubmit: (data: CustomerDetailsPageFormData) => void;

View file

@ -10,8 +10,9 @@ import CardTitle from "@saleor/components/CardTitle";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types"; import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import getAccountErrorMessage from "@saleor/utils/errors/account";
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ theme => ({
@ -35,16 +36,18 @@ export interface CustomerInfoProps {
email: string; email: string;
}; };
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: AccountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const CustomerInfo: React.FC<CustomerInfoProps> = props => { const CustomerInfo: React.FC<CustomerInfoProps> = props => {
const { data, disabled, errors, onChange } = props; const { data, disabled, errors, onChange } = props;
const classes = useStyles(props);
const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["firstName", "lastName", "email"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -62,9 +65,9 @@ const CustomerInfo: React.FC<CustomerInfoProps> = props => {
<Grid variant="uniform"> <Grid variant="uniform">
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "firstName")} error={!!formErrors.firstName}
fullWidth fullWidth
helperText={getFieldError(errors, "firstName")?.message} helperText={getAccountErrorMessage(formErrors.firstName, intl)}
name="firstName" name="firstName"
type="text" type="text"
label={intl.formatMessage(commonMessages.firstName)} label={intl.formatMessage(commonMessages.firstName)}
@ -73,9 +76,9 @@ const CustomerInfo: React.FC<CustomerInfoProps> = props => {
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "lastName")} error={!!formErrors.lastName}
fullWidth fullWidth
helperText={getFieldError(errors, "lastName")?.message} helperText={getAccountErrorMessage(formErrors.lastName, intl)}
name="lastName" name="lastName"
type="text" type="text"
label={intl.formatMessage(commonMessages.lastName)} label={intl.formatMessage(commonMessages.lastName)}
@ -92,9 +95,9 @@ const CustomerInfo: React.FC<CustomerInfoProps> = props => {
</Typography> </Typography>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "email")} error={!!formErrors.email}
fullWidth fullWidth
helperText={getFieldError(errors, "email")?.message} helperText={getAccountErrorMessage(formErrors.email, intl)}
name="email" name="email"
type="email" type="email"
label={intl.formatMessage(commonMessages.email)} label={intl.formatMessage(commonMessages.email)}

View file

@ -36,13 +36,20 @@ import {
UpdateCustomerAddressVariables UpdateCustomerAddressVariables
} from "./types/UpdateCustomerAddress"; } from "./types/UpdateCustomerAddress";
export const accountFragmentError = gql`
fragment AccountErrorFragment on AccountError {
code
field
}
`;
const updateCustomer = gql` const updateCustomer = gql`
${accountFragmentError}
${customerDetailsFragment} ${customerDetailsFragment}
mutation UpdateCustomer($id: ID!, $input: CustomerInput!) { mutation UpdateCustomer($id: ID!, $input: CustomerInput!) {
customerUpdate(id: $id, input: $input) { customerUpdate(id: $id, input: $input) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
user { user {
...CustomerDetailsFragment ...CustomerDetailsFragment
@ -56,11 +63,11 @@ export const TypedUpdateCustomerMutation = TypedMutation<
>(updateCustomer); >(updateCustomer);
const createCustomer = gql` const createCustomer = gql`
${accountFragmentError}
mutation CreateCustomer($input: UserCreateInput!) { mutation CreateCustomer($input: UserCreateInput!) {
customerCreate(input: $input) { customerCreate(input: $input) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
user { user {
id id
@ -74,11 +81,11 @@ export const TypedCreateCustomerMutation = TypedMutation<
>(createCustomer); >(createCustomer);
const removeCustomer = gql` const removeCustomer = gql`
${accountFragmentError}
mutation RemoveCustomer($id: ID!) { mutation RemoveCustomer($id: ID!) {
customerDelete(id: $id) { customerDelete(id: $id) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
} }
} }
@ -89,6 +96,7 @@ export const TypedRemoveCustomerMutation = TypedMutation<
>(removeCustomer); >(removeCustomer);
const setCustomerDefaultAddress = gql` const setCustomerDefaultAddress = gql`
${accountFragmentError}
${customerAddressesFragment} ${customerAddressesFragment}
mutation SetCustomerDefaultAddress( mutation SetCustomerDefaultAddress(
$addressId: ID! $addressId: ID!
@ -96,9 +104,8 @@ const setCustomerDefaultAddress = gql`
$type: AddressTypeEnum! $type: AddressTypeEnum!
) { ) {
addressSetDefault(addressId: $addressId, userId: $userId, type: $type) { addressSetDefault(addressId: $addressId, userId: $userId, type: $type) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
user { user {
...CustomerAddressesFragment ...CustomerAddressesFragment
@ -112,13 +119,13 @@ export const TypedSetCustomerDefaultAddressMutation = TypedMutation<
>(setCustomerDefaultAddress); >(setCustomerDefaultAddress);
const createCustomerAddress = gql` const createCustomerAddress = gql`
${accountFragmentError}
${customerAddressesFragment} ${customerAddressesFragment}
${fragmentAddress} ${fragmentAddress}
mutation CreateCustomerAddress($id: ID!, $input: AddressInput!) { mutation CreateCustomerAddress($id: ID!, $input: AddressInput!) {
addressCreate(userId: $id, input: $input) { addressCreate(userId: $id, input: $input) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
address { address {
...AddressFragment ...AddressFragment
@ -135,12 +142,12 @@ export const TypedCreateCustomerAddressMutation = TypedMutation<
>(createCustomerAddress); >(createCustomerAddress);
const updateCustomerAddress = gql` const updateCustomerAddress = gql`
${accountFragmentError}
${fragmentAddress} ${fragmentAddress}
mutation UpdateCustomerAddress($id: ID!, $input: AddressInput!) { mutation UpdateCustomerAddress($id: ID!, $input: AddressInput!) {
addressUpdate(id: $id, input: $input) { addressUpdate(id: $id, input: $input) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
address { address {
...AddressFragment ...AddressFragment
@ -154,12 +161,12 @@ export const TypedUpdateCustomerAddressMutation = TypedMutation<
>(updateCustomerAddress); >(updateCustomerAddress);
const removeCustomerAddress = gql` const removeCustomerAddress = gql`
${accountFragmentError}
${customerAddressesFragment} ${customerAddressesFragment}
mutation RemoveCustomerAddress($id: ID!) { mutation RemoveCustomerAddress($id: ID!) {
addressDelete(id: $id) { addressDelete(id: $id) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
user { user {
...CustomerAddressesFragment ...CustomerAddressesFragment
@ -173,11 +180,11 @@ export const TypedRemoveCustomerAddressMutation = TypedMutation<
>(removeCustomerAddress); >(removeCustomerAddress);
export const bulkRemoveCustomers = gql` export const bulkRemoveCustomers = gql`
${accountFragmentError}
mutation BulkRemoveCustomers($ids: [ID]!) { mutation BulkRemoveCustomers($ids: [ID]!) {
customerBulkDelete(ids: $ids) { customerBulkDelete(ids: $ids) {
errors { errors: accountErrors {
field ...AccountErrorFragment
message
} }
} }
} }

View file

@ -0,0 +1,15 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AccountErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: AccountErrorFragment
// ====================================================
export interface AccountErrorFragment {
__typename: "AccountError";
code: AccountErrorCode;
field: string | null;
}

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: BulkRemoveCustomers // GraphQL mutation operation: BulkRemoveCustomers
// ==================================================== // ====================================================
export interface BulkRemoveCustomers_customerBulkDelete_errors { export interface BulkRemoveCustomers_customerBulkDelete_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface BulkRemoveCustomers_customerBulkDelete { export interface BulkRemoveCustomers_customerBulkDelete {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { UserCreateInput } from "./../../types/globalTypes"; import { UserCreateInput, AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CreateCustomer // GraphQL mutation operation: CreateCustomer
// ==================================================== // ====================================================
export interface CreateCustomer_customerCreate_errors { export interface CreateCustomer_customerCreate_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface CreateCustomer_customerCreate_user { export interface CreateCustomer_customerCreate_user {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AddressInput } from "./../../types/globalTypes"; import { AddressInput, AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CreateCustomerAddress // GraphQL mutation operation: CreateCustomerAddress
// ==================================================== // ====================================================
export interface CreateCustomerAddress_addressCreate_errors { export interface CreateCustomerAddress_addressCreate_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface CreateCustomerAddress_addressCreate_address_country { export interface CreateCustomerAddress_addressCreate_address_country {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: RemoveCustomer // GraphQL mutation operation: RemoveCustomer
// ==================================================== // ====================================================
export interface RemoveCustomer_customerDelete_errors { export interface RemoveCustomer_customerDelete_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface RemoveCustomer_customerDelete { export interface RemoveCustomer_customerDelete {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: RemoveCustomerAddress // GraphQL mutation operation: RemoveCustomerAddress
// ==================================================== // ====================================================
export interface RemoveCustomerAddress_addressDelete_errors { export interface RemoveCustomerAddress_addressDelete_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface RemoveCustomerAddress_addressDelete_user_addresses_country { export interface RemoveCustomerAddress_addressDelete_user_addresses_country {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AddressTypeEnum } from "./../../types/globalTypes"; import { AddressTypeEnum, AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: SetCustomerDefaultAddress // GraphQL mutation operation: SetCustomerDefaultAddress
// ==================================================== // ====================================================
export interface SetCustomerDefaultAddress_addressSetDefault_errors { export interface SetCustomerDefaultAddress_addressSetDefault_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface SetCustomerDefaultAddress_addressSetDefault_user_addresses_country { export interface SetCustomerDefaultAddress_addressSetDefault_user_addresses_country {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { CustomerInput } from "./../../types/globalTypes"; import { CustomerInput, AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: UpdateCustomer // GraphQL mutation operation: UpdateCustomer
// ==================================================== // ====================================================
export interface UpdateCustomer_customerUpdate_errors { export interface UpdateCustomer_customerUpdate_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface UpdateCustomer_customerUpdate_user_defaultShippingAddress_country { export interface UpdateCustomer_customerUpdate_user_defaultShippingAddress_country {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AddressInput } from "./../../types/globalTypes"; import { AddressInput, AccountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: UpdateCustomerAddress // GraphQL mutation operation: UpdateCustomerAddress
// ==================================================== // ====================================================
export interface UpdateCustomerAddress_addressUpdate_errors { export interface UpdateCustomerAddress_addressUpdate_errors {
__typename: "Error"; __typename: "AccountError";
code: AccountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface UpdateCustomerAddress_addressUpdate_address_country { export interface UpdateCustomerAddress_addressUpdate_address_country {

View file

@ -41,20 +41,8 @@ export const CustomerCreate: React.FC<{}> = () => {
<CustomerCreatePage <CustomerCreatePage
countries={maybe(() => data.shop.countries, [])} countries={maybe(() => data.shop.countries, [])}
disabled={loading || createCustomerOpts.loading} disabled={loading || createCustomerOpts.loading}
errors={maybe(() => { errors={createCustomerOpts.data?.customerCreate.errors || []}
const errs = createCustomerOpts.data.customerCreate.errors; saveButtonBar={createCustomerOpts.status}
return errs.map(err =>
err.field.split(":").length > 1
? {
...err,
field: err.field.split(":")[1]
}
: err
);
}, [])}
saveButtonBar={
createCustomerOpts.loading ? "loading" : "default"
}
onBack={() => navigate(customerListUrl())} onBack={() => navigate(customerListUrl())}
onSubmit={formData => { onSubmit={formData => {
createCustomer({ createCustomer({

View file

@ -8,8 +8,9 @@ import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import { UserError } from "../../../types"; import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
interface DiscountDatesProps { interface DiscountDatesProps {
data: { data: {
@ -21,7 +22,7 @@ interface DiscountDatesProps {
}; };
defaultCurrency: string; defaultCurrency: string;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -33,6 +34,8 @@ const DiscountDates = ({
}: DiscountDatesProps) => { }: DiscountDatesProps) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["startDate", "endDate"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -45,8 +48,8 @@ const DiscountDates = ({
<Grid variant="uniform"> <Grid variant="uniform">
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "startDate")} error={!!formErrors.startDate}
helperText={getFieldError(errors, "startDate")?.message} helperText={getDiscountErrorMessage(formErrors.startDate, intl)}
name={"startDate" as keyof FormData} name={"startDate" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)} label={intl.formatMessage(commonMessages.startDate)}
@ -59,8 +62,8 @@ const DiscountDates = ({
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "startDate")} error={!!formErrors.startDate}
helperText={getFieldError(errors, "startDate")?.message} helperText={getDiscountErrorMessage(formErrors.startDate, intl)}
name={"startTime" as keyof FormData} name={"startTime" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.startHour)} label={intl.formatMessage(commonMessages.startHour)}
@ -85,8 +88,8 @@ const DiscountDates = ({
<Grid variant="uniform"> <Grid variant="uniform">
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "endDate")} error={!!formErrors.endDate}
helperText={getFieldError(errors, "endDate")?.message} helperText={getDiscountErrorMessage(formErrors.endDate, intl)}
name={"endDate" as keyof FormData} name={"endDate" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)} label={intl.formatMessage(commonMessages.endDate)}
@ -99,8 +102,8 @@ const DiscountDates = ({
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "endDate")} error={!!formErrors.endDate}
helperText={getFieldError(errors, "endDate")?.message} helperText={getDiscountErrorMessage(formErrors.endDate, intl)}
name={"endTime" as keyof FormData} name={"endTime" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.endHour)} label={intl.formatMessage(commonMessages.endHour)}

View file

@ -10,7 +10,7 @@ import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { UserError } from "../../../types"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { SaleType as SaleTypeEnum } from "../../../types/globalTypes"; import { SaleType as SaleTypeEnum } from "../../../types/globalTypes";
import DiscountDates from "../DiscountDates"; import DiscountDates from "../DiscountDates";
import SaleInfo from "../SaleInfo"; import SaleInfo from "../SaleInfo";
@ -30,7 +30,7 @@ export interface FormData {
export interface SaleCreatePageProps { export interface SaleCreatePageProps {
defaultCurrency: string; defaultCurrency: string;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;
onSubmit: (data: FormData) => void; onSubmit: (data: FormData) => void;

View file

@ -11,8 +11,9 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { Tab, TabContainer } from "@saleor/components/Tab"; import { Tab, TabContainer } from "@saleor/components/Tab";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { maybe, splitDateTime } from "../../../misc"; import { maybe, splitDateTime } from "../../../misc";
import { ListProps, TabListActions, UserError } from "../../../types"; import { ListProps, TabListActions } from "../../../types";
import { SaleType as SaleTypeEnum } from "../../../types/globalTypes"; import { SaleType as SaleTypeEnum } from "../../../types/globalTypes";
import { SaleDetails_sale } from "../../types/SaleDetails"; import { SaleDetails_sale } from "../../types/SaleDetails";
import DiscountCategories from "../DiscountCategories"; import DiscountCategories from "../DiscountCategories";
@ -55,7 +56,7 @@ export interface SaleDetailsPageProps
> { > {
activeTab: SaleDetailsPageTab; activeTab: SaleDetailsPageTab;
defaultCurrency: string; defaultCurrency: string;
errors: UserError[]; errors: DiscountErrorFragment[];
sale: SaleDetails_sale; sale: SaleDetails_sale;
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;

View file

@ -6,14 +6,15 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types"; import { getFormErrors } from "@saleor/utils/errors";
import { getFieldError } from "@saleor/utils/errors"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { FormData } from "../SaleDetailsPage"; import { FormData } from "../SaleDetailsPage";
export interface SaleInfoProps { export interface SaleInfoProps {
data: FormData; data: FormData;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -25,6 +26,8 @@ const SaleInfo: React.FC<SaleInfoProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["name"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -33,8 +36,8 @@ const SaleInfo: React.FC<SaleInfoProps> = ({
<CardContent> <CardContent>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "name")} error={!!formErrors.name}
helperText={getFieldError(errors, "name")?.message} helperText={getDiscountErrorMessage(formErrors.name, intl)}
name={"name" as keyof FormData} name={"name" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage({ label={intl.formatMessage({

View file

@ -6,16 +6,17 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { FormChange } from "@saleor/hooks/useForm"; import { FormChange } from "@saleor/hooks/useForm";
import { UserError } from "@saleor/types";
import { SaleType } from "@saleor/types/globalTypes"; import { SaleType } from "@saleor/types/globalTypes";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { FormData } from "../SaleDetailsPage"; import { FormData } from "../SaleDetailsPage";
export interface SaleValueProps { export interface SaleValueProps {
currencySymbol: string; currencySymbol: string;
data: FormData; data: FormData;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
onChange: FormChange; onChange: FormChange;
} }
@ -28,6 +29,8 @@ const SaleValue: React.FC<SaleValueProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["value"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -44,12 +47,12 @@ const SaleValue: React.FC<SaleValueProps> = ({
defaultMessage: "Discount Value", defaultMessage: "Discount Value",
description: "sale discount" description: "sale discount"
})} })}
error={!!getFieldError(errors, "value")} error={!!formErrors.value}
name="value" name="value"
InputProps={{ InputProps={{
endAdornment: data.type === SaleType.FIXED ? currencySymbol : "%" endAdornment: data.type === SaleType.FIXED ? currencySymbol : "%"
}} }}
helperText={getFieldError(errors, "value")?.message} helperText={getDiscountErrorMessage(formErrors.value, intl)}
value={data.value} value={data.value}
onChange={onChange} onChange={onChange}
/> />

View file

@ -10,7 +10,7 @@ import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { UserError } from "../../../types"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { import {
DiscountValueTypeEnum, DiscountValueTypeEnum,
VoucherTypeEnum VoucherTypeEnum
@ -21,8 +21,8 @@ import VoucherInfo from "../VoucherInfo";
import VoucherLimits from "../VoucherLimits"; import VoucherLimits from "../VoucherLimits";
import VoucherRequirements from "../VoucherRequirements"; import VoucherRequirements from "../VoucherRequirements";
import VoucherTypes from "../VoucherTypes"; import VoucherTypes from "../VoucherTypes";
import VoucherValue from "../VoucherValue"; import VoucherValue from "../VoucherValue";
export interface FormData { export interface FormData {
applyOncePerCustomer: boolean; applyOncePerCustomer: boolean;
applyOncePerOrder: boolean; applyOncePerOrder: boolean;
@ -45,7 +45,7 @@ export interface FormData {
export interface VoucherCreatePageProps { export interface VoucherCreatePageProps {
defaultCurrency: string; defaultCurrency: string;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void; onBack: () => void;
onSubmit: (data: FormData) => void; onSubmit: (data: FormData) => void;

View file

@ -8,15 +8,16 @@ import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import { UserError } from "../../../types"; import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { FormData } from "../VoucherDetailsPage"; import { FormData } from "../VoucherDetailsPage";
interface VoucherDatesProps { interface VoucherDatesProps {
data: FormData; data: FormData;
defaultCurrency: string; defaultCurrency: string;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -28,6 +29,8 @@ const VoucherDates = ({
}: VoucherDatesProps) => { }: VoucherDatesProps) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["startDate", "endDate"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -40,8 +43,8 @@ const VoucherDates = ({
<Grid variant="uniform"> <Grid variant="uniform">
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "startDate")} error={!!formErrors.startDate}
helperText={getFieldError(errors, "startDate")?.message} helperText={getDiscountErrorMessage(formErrors.startDate, intl)}
name={"startDate" as keyof FormData} name={"startDate" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)} label={intl.formatMessage(commonMessages.startDate)}
@ -54,8 +57,8 @@ const VoucherDates = ({
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "startDate")} error={!!formErrors.startDate}
helperText={getFieldError(errors, "startDate")?.message} helperText={getDiscountErrorMessage(formErrors.startDate, intl)}
name={"startTime" as keyof FormData} name={"startTime" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.startHour)} label={intl.formatMessage(commonMessages.startHour)}
@ -80,8 +83,8 @@ const VoucherDates = ({
<Grid variant="uniform"> <Grid variant="uniform">
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "endDate")} error={!!formErrors.endDate}
helperText={getFieldError(errors, "endDate")?.message} helperText={getDiscountErrorMessage(formErrors.endDate, intl)}
name={"endDate" as keyof FormData} name={"endDate" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)} label={intl.formatMessage(commonMessages.endDate)}
@ -94,8 +97,8 @@ const VoucherDates = ({
/> />
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "endDate")} error={!!formErrors.endDate}
helperText={getFieldError(errors, "endDate")?.message} helperText={getDiscountErrorMessage(formErrors.endDate, intl)}
name={"endTime" as keyof FormData} name={"endTime" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage(commonMessages.endHour)} label={intl.formatMessage(commonMessages.endHour)}

View file

@ -14,8 +14,9 @@ import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { Tab, TabContainer } from "@saleor/components/Tab"; import { Tab, TabContainer } from "@saleor/components/Tab";
import { RequirementsPicker } from "@saleor/discounts/types"; import { RequirementsPicker } from "@saleor/discounts/types";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { maybe, splitDateTime } from "../../../misc"; import { maybe, splitDateTime } from "../../../misc";
import { ListProps, TabListActions, UserError } from "../../../types"; import { ListProps, TabListActions } from "../../../types";
import { import {
DiscountValueTypeEnum, DiscountValueTypeEnum,
VoucherTypeEnum VoucherTypeEnum
@ -72,7 +73,7 @@ export interface VoucherDetailsPageProps
> { > {
activeTab: VoucherDetailsPageTab; activeTab: VoucherDetailsPageTab;
defaultCurrency: string; defaultCurrency: string;
errors: UserError[]; errors: DiscountErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
voucher: VoucherDetails_voucher; voucher: VoucherDetails_voucher;
onBack: () => void; onBack: () => void;

View file

@ -7,14 +7,15 @@ import { FormattedMessage, useIntl } from "react-intl";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { generateCode } from "../../../misc"; import { getFormErrors } from "@saleor/utils/errors";
import { UserError } from "../../../types"; import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { FormData } from "../VoucherDetailsPage"; import { FormData } from "../VoucherDetailsPage";
import { generateCode } from "../../../misc";
interface VoucherInfoProps { interface VoucherInfoProps {
data: FormData; data: FormData;
errors: UserError[]; errors: DiscountErrorFragment[];
disabled: boolean; disabled: boolean;
variant: "create" | "update"; variant: "create" | "update";
onChange: (event: any) => void; onChange: (event: any) => void;
@ -29,6 +30,8 @@ const VoucherInfo = ({
}: VoucherInfoProps) => { }: VoucherInfoProps) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["code"], errors);
const onGenerateCode = () => const onGenerateCode = () =>
onChange({ onChange({
target: { target: {
@ -55,9 +58,9 @@ const VoucherInfo = ({
<CardContent> <CardContent>
<TextField <TextField
disabled={variant === "update" || disabled} disabled={variant === "update" || disabled}
error={!!getFieldError(errors, "code")} error={!!formErrors.code}
fullWidth fullWidth
helperText={getFieldError(errors, "code")?.message} helperText={getDiscountErrorMessage(formErrors.code, intl)}
name={"code" as keyof FormData} name={"code" as keyof FormData}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Discount Code" defaultMessage: "Discount Code"

View file

@ -6,15 +6,16 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox"; import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import { UserError } from "../../../types"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { FormData } from "../VoucherDetailsPage"; import { FormData } from "../VoucherDetailsPage";
interface VoucherLimitsProps { interface VoucherLimitsProps {
data: FormData; data: FormData;
defaultCurrency: string; defaultCurrency: string;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -26,6 +27,8 @@ const VoucherLimits = ({
}: VoucherLimitsProps) => { }: VoucherLimitsProps) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["usageLimit"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -47,8 +50,8 @@ const VoucherLimits = ({
{data.hasUsageLimit && ( {data.hasUsageLimit && (
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "usageLimit")} error={!!formErrors.usageLimit}
helperText={getFieldError(errors, "usageLimit")?.message} helperText={getDiscountErrorMessage(formErrors.usageLimit, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Limit of Uses", defaultMessage: "Limit of Uses",
description: "voucher" description: "voucher"

View file

@ -8,15 +8,16 @@ import CardTitle from "@saleor/components/CardTitle";
import { FormSpacer } from "@saleor/components/FormSpacer"; import { FormSpacer } from "@saleor/components/FormSpacer";
import RadioGroupField from "@saleor/components/RadioGroupField"; import RadioGroupField from "@saleor/components/RadioGroupField";
import { RequirementsPicker } from "@saleor/discounts/types"; import { RequirementsPicker } from "@saleor/discounts/types";
import { UserError } from "@saleor/types"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { getFieldError } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { FormData } from "../VoucherDetailsPage"; import { FormData } from "../VoucherDetailsPage";
interface VoucherRequirementsProps { interface VoucherRequirementsProps {
data: FormData; data: FormData;
defaultCurrency: string; defaultCurrency: string;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: DiscountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -28,6 +29,11 @@ const VoucherRequirements = ({
}: VoucherRequirementsProps) => { }: VoucherRequirementsProps) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(
["minSpent", "minCheckoutItemsQuantity"],
errors
);
const minimalOrderValueText = intl.formatMessage({ const minimalOrderValueText = intl.formatMessage({
defaultMessage: "Minimal order value", defaultMessage: "Minimal order value",
description: "voucher requirement" description: "voucher requirement"
@ -77,8 +83,8 @@ const VoucherRequirements = ({
{data.requirementsPicker === RequirementsPicker.ORDER ? ( {data.requirementsPicker === RequirementsPicker.ORDER ? (
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "minSpent")} error={!!formErrors.minSpent}
helperText={getFieldError(errors, "minSpent")?.message} helperText={getDiscountErrorMessage(formErrors.minSpent, intl)}
label={minimalOrderValueText} label={minimalOrderValueText}
name={"minSpent" as keyof FormData} name={"minSpent" as keyof FormData}
value={data.minSpent} value={data.minSpent}
@ -88,10 +94,11 @@ const VoucherRequirements = ({
) : data.requirementsPicker === RequirementsPicker.ITEM ? ( ) : data.requirementsPicker === RequirementsPicker.ITEM ? (
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "minCheckoutItemsQuantity")} error={!!formErrors.minCheckoutItemsQuantity}
helperText={ helperText={getDiscountErrorMessage(
getFieldError(errors, "minCheckoutItemsQuantity")?.message formErrors.minCheckoutItemsQuantity,
} intl
)}
label={minimalQuantityText} label={minimalQuantityText}
name={"minCheckoutItemsQuantity" as keyof FormData} name={"minCheckoutItemsQuantity" as keyof FormData}
value={data.minCheckoutItemsQuantity} value={data.minCheckoutItemsQuantity}

View file

@ -6,14 +6,15 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import RadioGroupField from "@saleor/components/RadioGroupField"; import RadioGroupField from "@saleor/components/RadioGroupField";
import { getFieldError } from "@saleor/utils/errors"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { UserError } from "../../../types"; import { getFormErrors } from "@saleor/utils/errors";
import { DiscountValueTypeEnum } from "../../../types/globalTypes"; import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { FormData } from "../VoucherDetailsPage"; import { FormData } from "../VoucherDetailsPage";
import { DiscountValueTypeEnum } from "../../../types/globalTypes";
interface VoucherTypesProps { interface VoucherTypesProps {
data: FormData; data: FormData;
errors: UserError[]; errors: DiscountErrorFragment[];
disabled: boolean; disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -26,6 +27,8 @@ const VoucherTypes = ({
}: VoucherTypesProps) => { }: VoucherTypesProps) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["discountType"], errors);
const voucherTypeChoices = [ const voucherTypeChoices = [
{ {
label: intl.formatMessage({ label: intl.formatMessage({
@ -63,8 +66,8 @@ const VoucherTypes = ({
<RadioGroupField <RadioGroupField
choices={voucherTypeChoices} choices={voucherTypeChoices}
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "discountType")} error={!!formErrors.discountType}
hint={getFieldError(errors, "discountType")?.message} hint={getDiscountErrorMessage(formErrors.discountType, intl)}
name={"discountType" as keyof FormData} name={"discountType" as keyof FormData}
value={data.discountType} value={data.discountType}
onChange={onChange} onChange={onChange}

View file

@ -11,8 +11,9 @@ import { FormSpacer } from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import RadioGroupField from "@saleor/components/RadioGroupField"; import RadioGroupField from "@saleor/components/RadioGroupField";
import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice"; import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice";
import { getFieldError } from "@saleor/utils/errors"; import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { UserError } from "../../../types"; import { getFormErrors } from "@saleor/utils/errors";
import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { DiscountValueTypeEnum } from "../../../types/globalTypes"; import { DiscountValueTypeEnum } from "../../../types/globalTypes";
import { translateVoucherTypes } from "../../translations"; import { translateVoucherTypes } from "../../translations";
import { FormData } from "../VoucherDetailsPage"; import { FormData } from "../VoucherDetailsPage";
@ -20,7 +21,7 @@ import { FormData } from "../VoucherDetailsPage";
interface VoucherValueProps { interface VoucherValueProps {
data: FormData; data: FormData;
defaultCurrency: string; defaultCurrency: string;
errors: UserError[]; errors: DiscountErrorFragment[];
disabled: boolean; disabled: boolean;
variant: string; variant: string;
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
@ -48,6 +49,8 @@ const VoucherValue: React.FC<VoucherValueProps> = props => {
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["discountValue", "type"], errors);
const translatedVoucherTypes = translateVoucherTypes(intl); const translatedVoucherTypes = translateVoucherTypes(intl);
const voucherTypeChoices = Object.values(VoucherType).map(type => ({ const voucherTypeChoices = Object.values(VoucherType).map(type => ({
label: translatedVoucherTypes[type], label: translatedVoucherTypes[type],
@ -65,7 +68,7 @@ const VoucherValue: React.FC<VoucherValueProps> = props => {
<CardContent> <CardContent>
<TextFieldWithChoice <TextFieldWithChoice
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "discountValue")} error={!!formErrors.discountValue}
ChoiceProps={{ ChoiceProps={{
label: label:
data.discountType === DiscountValueTypeEnum.FIXED data.discountType === DiscountValueTypeEnum.FIXED
@ -74,7 +77,7 @@ const VoucherValue: React.FC<VoucherValueProps> = props => {
name: "discountType" as keyof FormData, name: "discountType" as keyof FormData,
values: null values: null
}} }}
helperText={getFieldError(errors, "discountValue")?.message} helperText={getDiscountErrorMessage(formErrors.discountValue, intl)}
name={"value" as keyof FormData} name={"value" as keyof FormData}
onChange={onChange} onChange={onChange}
label={intl.formatMessage({ label={intl.formatMessage({
@ -94,8 +97,8 @@ const VoucherValue: React.FC<VoucherValueProps> = props => {
<RadioGroupField <RadioGroupField
choices={voucherTypeChoices} choices={voucherTypeChoices}
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "type")} error={!!formErrors.type}
hint={getFieldError(errors, "type")?.message} hint={getDiscountErrorMessage(formErrors.type, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Voucher Specific Information" defaultMessage: "Voucher Specific Information"
})} })}

View file

@ -38,13 +38,20 @@ import { VoucherCreate, VoucherCreateVariables } from "./types/VoucherCreate";
import { VoucherDelete, VoucherDeleteVariables } from "./types/VoucherDelete"; import { VoucherDelete, VoucherDeleteVariables } from "./types/VoucherDelete";
import { VoucherUpdate, VoucherUpdateVariables } from "./types/VoucherUpdate"; import { VoucherUpdate, VoucherUpdateVariables } from "./types/VoucherUpdate";
const discountErrorFragment = gql`
fragment DiscountErrorFragment on DiscountError {
code
field
}
`;
const saleUpdate = gql` const saleUpdate = gql`
${discountErrorFragment}
${saleFragment} ${saleFragment}
mutation SaleUpdate($input: SaleInput!, $id: ID!) { mutation SaleUpdate($input: SaleInput!, $id: ID!) {
saleUpdate(id: $id, input: $input) { saleUpdate(id: $id, input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
sale { sale {
...SaleFragment ...SaleFragment
@ -57,6 +64,7 @@ export const TypedSaleUpdate = TypedMutation<SaleUpdate, SaleUpdateVariables>(
); );
const saleCataloguesAdd = gql` const saleCataloguesAdd = gql`
${discountErrorFragment}
${saleDetailsFragment} ${saleDetailsFragment}
mutation SaleCataloguesAdd( mutation SaleCataloguesAdd(
$input: CatalogueInput! $input: CatalogueInput!
@ -67,9 +75,8 @@ const saleCataloguesAdd = gql`
$last: Int $last: Int
) { ) {
saleCataloguesAdd(id: $id, input: $input) { saleCataloguesAdd(id: $id, input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
sale { sale {
...SaleDetailsFragment ...SaleDetailsFragment
@ -83,6 +90,7 @@ export const TypedSaleCataloguesAdd = TypedMutation<
>(saleCataloguesAdd); >(saleCataloguesAdd);
const saleCataloguesRemove = gql` const saleCataloguesRemove = gql`
${discountErrorFragment}
${saleDetailsFragment} ${saleDetailsFragment}
mutation SaleCataloguesRemove( mutation SaleCataloguesRemove(
$input: CatalogueInput! $input: CatalogueInput!
@ -93,9 +101,8 @@ const saleCataloguesRemove = gql`
$last: Int $last: Int
) { ) {
saleCataloguesRemove(id: $id, input: $input) { saleCataloguesRemove(id: $id, input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
sale { sale {
...SaleDetailsFragment ...SaleDetailsFragment
@ -109,12 +116,12 @@ export const TypedSaleCataloguesRemove = TypedMutation<
>(saleCataloguesRemove); >(saleCataloguesRemove);
const saleCreate = gql` const saleCreate = gql`
${discountErrorFragment}
${saleFragment} ${saleFragment}
mutation SaleCreate($input: SaleInput!) { mutation SaleCreate($input: SaleInput!) {
saleCreate(input: $input) { saleCreate(input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
sale { sale {
...SaleFragment ...SaleFragment
@ -127,11 +134,11 @@ export const TypedSaleCreate = TypedMutation<SaleCreate, SaleCreateVariables>(
); );
const saleDelete = gql` const saleDelete = gql`
${discountErrorFragment}
mutation SaleDelete($id: ID!) { mutation SaleDelete($id: ID!) {
saleDelete(id: $id) { saleDelete(id: $id) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
} }
} }
@ -156,12 +163,12 @@ export const TypedSaleBulkDelete = TypedMutation<
>(saleBulkDelete); >(saleBulkDelete);
const voucherUpdate = gql` const voucherUpdate = gql`
${discountErrorFragment}
${voucherFragment} ${voucherFragment}
mutation VoucherUpdate($input: VoucherInput!, $id: ID!) { mutation VoucherUpdate($input: VoucherInput!, $id: ID!) {
voucherUpdate(id: $id, input: $input) { voucherUpdate(id: $id, input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
voucher { voucher {
...VoucherFragment ...VoucherFragment
@ -175,6 +182,7 @@ export const TypedVoucherUpdate = TypedMutation<
>(voucherUpdate); >(voucherUpdate);
const voucherCataloguesAdd = gql` const voucherCataloguesAdd = gql`
${discountErrorFragment}
${voucherDetailsFragment} ${voucherDetailsFragment}
mutation VoucherCataloguesAdd( mutation VoucherCataloguesAdd(
$input: CatalogueInput! $input: CatalogueInput!
@ -185,9 +193,8 @@ const voucherCataloguesAdd = gql`
$last: Int $last: Int
) { ) {
voucherCataloguesAdd(id: $id, input: $input) { voucherCataloguesAdd(id: $id, input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
voucher { voucher {
...VoucherDetailsFragment ...VoucherDetailsFragment
@ -201,6 +208,7 @@ export const TypedVoucherCataloguesAdd = TypedMutation<
>(voucherCataloguesAdd); >(voucherCataloguesAdd);
const voucherCataloguesRemove = gql` const voucherCataloguesRemove = gql`
${discountErrorFragment}
${voucherDetailsFragment} ${voucherDetailsFragment}
mutation VoucherCataloguesRemove( mutation VoucherCataloguesRemove(
$input: CatalogueInput! $input: CatalogueInput!
@ -211,9 +219,8 @@ const voucherCataloguesRemove = gql`
$last: Int $last: Int
) { ) {
voucherCataloguesRemove(id: $id, input: $input) { voucherCataloguesRemove(id: $id, input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
voucher { voucher {
...VoucherDetailsFragment ...VoucherDetailsFragment
@ -227,12 +234,12 @@ export const TypedVoucherCataloguesRemove = TypedMutation<
>(voucherCataloguesRemove); >(voucherCataloguesRemove);
const voucherCreate = gql` const voucherCreate = gql`
${discountErrorFragment}
${voucherFragment} ${voucherFragment}
mutation VoucherCreate($input: VoucherInput!) { mutation VoucherCreate($input: VoucherInput!) {
voucherCreate(input: $input) { voucherCreate(input: $input) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
voucher { voucher {
...VoucherFragment ...VoucherFragment
@ -246,11 +253,11 @@ export const TypedVoucherCreate = TypedMutation<
>(voucherCreate); >(voucherCreate);
const voucherDelete = gql` const voucherDelete = gql`
${discountErrorFragment}
mutation VoucherDelete($id: ID!) { mutation VoucherDelete($id: ID!) {
voucherDelete(id: $id) { voucherDelete(id: $id) {
errors { errors: discountErrors {
field ...DiscountErrorFragment
message
} }
} }
} }

View file

@ -0,0 +1,15 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { DiscountErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: DiscountErrorFragment
// ====================================================
export interface DiscountErrorFragment {
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
}

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { CatalogueInput, SaleType } from "./../../types/globalTypes"; import { CatalogueInput, DiscountErrorCode, SaleType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: SaleCataloguesAdd // GraphQL mutation operation: SaleCataloguesAdd
// ==================================================== // ====================================================
export interface SaleCataloguesAdd_saleCataloguesAdd_errors { export interface SaleCataloguesAdd_saleCataloguesAdd_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_products_edges_node_productType { export interface SaleCataloguesAdd_saleCataloguesAdd_sale_products_edges_node_productType {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { CatalogueInput, SaleType } from "./../../types/globalTypes"; import { CatalogueInput, DiscountErrorCode, SaleType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: SaleCataloguesRemove // GraphQL mutation operation: SaleCataloguesRemove
// ==================================================== // ====================================================
export interface SaleCataloguesRemove_saleCataloguesRemove_errors { export interface SaleCataloguesRemove_saleCataloguesRemove_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_products_edges_node_productType { export interface SaleCataloguesRemove_saleCataloguesRemove_sale_products_edges_node_productType {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { SaleInput, SaleType } from "./../../types/globalTypes"; import { SaleInput, DiscountErrorCode, SaleType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: SaleCreate // GraphQL mutation operation: SaleCreate
// ==================================================== // ====================================================
export interface SaleCreate_saleCreate_errors { export interface SaleCreate_saleCreate_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface SaleCreate_saleCreate_sale { export interface SaleCreate_saleCreate_sale {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { DiscountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: SaleDelete // GraphQL mutation operation: SaleDelete
// ==================================================== // ====================================================
export interface SaleDelete_saleDelete_errors { export interface SaleDelete_saleDelete_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface SaleDelete_saleDelete { export interface SaleDelete_saleDelete {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { SaleInput, SaleType } from "./../../types/globalTypes"; import { SaleInput, DiscountErrorCode, SaleType } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: SaleUpdate // GraphQL mutation operation: SaleUpdate
// ==================================================== // ====================================================
export interface SaleUpdate_saleUpdate_errors { export interface SaleUpdate_saleUpdate_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface SaleUpdate_saleUpdate_sale { export interface SaleUpdate_saleUpdate_sale {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { CatalogueInput, DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes"; import { CatalogueInput, DiscountErrorCode, DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VoucherCataloguesAdd // GraphQL mutation operation: VoucherCataloguesAdd
// ==================================================== // ====================================================
export interface VoucherCataloguesAdd_voucherCataloguesAdd_errors { export interface VoucherCataloguesAdd_voucherCataloguesAdd_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_countries { export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_countries {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { CatalogueInput, DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes"; import { CatalogueInput, DiscountErrorCode, DiscountValueTypeEnum, VoucherTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VoucherCataloguesRemove // GraphQL mutation operation: VoucherCataloguesRemove
// ==================================================== // ====================================================
export interface VoucherCataloguesRemove_voucherCataloguesRemove_errors { export interface VoucherCataloguesRemove_voucherCataloguesRemove_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_countries { export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_countries {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { VoucherInput, DiscountValueTypeEnum } from "./../../types/globalTypes"; import { VoucherInput, DiscountErrorCode, DiscountValueTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VoucherCreate // GraphQL mutation operation: VoucherCreate
// ==================================================== // ====================================================
export interface VoucherCreate_voucherCreate_errors { export interface VoucherCreate_voucherCreate_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface VoucherCreate_voucherCreate_voucher_countries { export interface VoucherCreate_voucherCreate_voucher_countries {

View file

@ -2,14 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { DiscountErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VoucherDelete // GraphQL mutation operation: VoucherDelete
// ==================================================== // ====================================================
export interface VoucherDelete_voucherDelete_errors { export interface VoucherDelete_voucherDelete_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface VoucherDelete_voucherDelete { export interface VoucherDelete_voucherDelete {

View file

@ -2,16 +2,16 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { VoucherInput, DiscountValueTypeEnum } from "./../../types/globalTypes"; import { VoucherInput, DiscountErrorCode, DiscountValueTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VoucherUpdate // GraphQL mutation operation: VoucherUpdate
// ==================================================== // ====================================================
export interface VoucherUpdate_voucherUpdate_errors { export interface VoucherUpdate_voucherUpdate_errors {
__typename: "Error"; __typename: "DiscountError";
code: DiscountErrorCode;
field: string | null; field: string | null;
message: string | null;
} }
export interface VoucherUpdate_voucherUpdate_voucher_countries { export interface VoucherUpdate_voucherUpdate_voucher_countries {

View file

@ -1,27 +1,27 @@
import { useState } from "react"; import { useState } from "react";
import { useIntl } from "react-intl";
import { AddressTypeInput } from "@saleor/customers/types"; import { AddressTypeInput } from "@saleor/customers/types";
import { commonMessages } from "@saleor/intl";
import { transformFormToAddress } from "@saleor/misc"; import { transformFormToAddress } from "@saleor/misc";
import { UserError } from "@saleor/types"; import { AddressInput, AccountErrorCode } from "@saleor/types/globalTypes";
import { AddressInput } from "@saleor/types/globalTypes";
import { add, remove } from "@saleor/utils/lists"; import { add, remove } from "@saleor/utils/lists";
import { AccountErrorFragment } from "@saleor/customers/types/AccountErrorFragment";
interface UseAddressValidation<T> { interface UseAddressValidation<T> {
errors: UserError[]; errors: AccountErrorFragment[];
submit: (data: T & AddressTypeInput) => void; submit: (data: T & AddressTypeInput) => void;
} }
function useAddressValidation<T>( function useAddressValidation<T>(
onSubmit: (address: T & AddressInput) => void onSubmit: (address: T & AddressInput) => void
): UseAddressValidation<T> { ): UseAddressValidation<T> {
const intl = useIntl(); const [validationErrors, setValidationErrors] = useState<
const [validationErrors, setValidationErrors] = useState<UserError[]>([]); AccountErrorFragment[]
>([]);
const countryRequiredError = { const countryRequiredError: AccountErrorFragment = {
field: "country", __typename: "AccountError",
message: intl.formatMessage(commonMessages.requiredField) code: AccountErrorCode.REQUIRED,
field: "country"
}; };
return { return {

View file

@ -12,8 +12,9 @@ import ConfirmButton, {
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors"; import { MenuErrorFragment } from "@saleor/navigation/types/MenuErrorFragment";
import { UserError } from "@saleor/types"; import { getFormErrors } from "@saleor/utils/errors";
import getMenuErrorMessage from "@saleor/utils/errors/menu";
export interface MenuCreateDialogFormData { export interface MenuCreateDialogFormData {
name: string; name: string;
@ -22,7 +23,7 @@ export interface MenuCreateDialogFormData {
export interface MenuCreateDialogProps { export interface MenuCreateDialogProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: MenuErrorFragment[];
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
onConfirm: (data: MenuCreateDialogFormData) => void; onConfirm: (data: MenuCreateDialogFormData) => void;
@ -42,6 +43,8 @@ const MenuCreateDialog: React.FC<MenuCreateDialogProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["name"], errors);
return ( return (
<Dialog onClose={onClose} maxWidth="sm" fullWidth open={open}> <Dialog onClose={onClose} maxWidth="sm" fullWidth open={open}>
<DialogTitle> <DialogTitle>
@ -57,9 +60,9 @@ const MenuCreateDialog: React.FC<MenuCreateDialogProps> = ({
<DialogContent> <DialogContent>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!getFieldError(errors, "name")} error={!!formErrors.name}
fullWidth fullWidth
helperText={getFieldError(errors, "name")?.message} helperText={getMenuErrorMessage(formErrors.name, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Menu Title", defaultMessage: "Menu Title",
id: "menuCreateDialogMenuTitleLabel" id: "menuCreateDialogMenuTitleLabel"

View file

@ -10,6 +10,7 @@ import Form from "@saleor/components/Form";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { MenuErrorFragment } from "@saleor/navigation/types/MenuErrorFragment";
import { maybe } from "../../../misc"; import { maybe } from "../../../misc";
import { MenuDetails_menu } from "../../types/MenuDetails"; import { MenuDetails_menu } from "../../types/MenuDetails";
import { MenuItemType } from "../MenuItemDialog"; import { MenuItemType } from "../MenuItemDialog";
@ -28,6 +29,7 @@ export interface MenuDetailsSubmitData extends MenuDetailsFormData {
export interface MenuDetailsPageProps { export interface MenuDetailsPageProps {
saveButtonState: ConfirmButtonTransitionState; saveButtonState: ConfirmButtonTransitionState;
disabled: boolean; disabled: boolean;
errors: MenuErrorFragment[];
menu: MenuDetails_menu; menu: MenuDetails_menu;
onBack: () => void; onBack: () => void;
onDelete: () => void; onDelete: () => void;
@ -39,6 +41,7 @@ export interface MenuDetailsPageProps {
const MenuDetailsPage: React.FC<MenuDetailsPageProps> = ({ const MenuDetailsPage: React.FC<MenuDetailsPageProps> = ({
disabled, disabled,
errors,
menu, menu,
saveButtonState, saveButtonState,
onBack, onBack,
@ -98,6 +101,7 @@ const MenuDetailsPage: React.FC<MenuDetailsPageProps> = ({
<MenuProperties <MenuProperties
data={data} data={data}
disabled={disabled} disabled={disabled}
errors={errors}
onChange={change} onChange={change}
/> />
<CardSpacer /> <CardSpacer />

View file

@ -21,9 +21,10 @@ import { buttonMessages, sectionNames } from "@saleor/intl";
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories"; import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections"; import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages"; import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages";
import { UserError } from "@saleor/types";
import { getErrors, getFieldError } from "@saleor/utils/errors";
import { getMenuItemByValue, IMenu } from "@saleor/utils/menu"; import { getMenuItemByValue, IMenu } from "@saleor/utils/menu";
import { MenuErrorFragment } from "@saleor/navigation/types/MenuErrorFragment";
import { getFieldError, getFormErrors } from "@saleor/utils/errors";
import getMenuErrorMessage from "@saleor/utils/errors/menu";
export type MenuItemType = "category" | "collection" | "link" | "page"; export type MenuItemType = "category" | "collection" | "link" | "page";
export interface MenuItemData { export interface MenuItemData {
@ -38,7 +39,7 @@ export interface MenuItemDialogFormData extends MenuItemData {
export interface MenuItemDialogProps { export interface MenuItemDialogProps {
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
disabled: boolean; disabled: boolean;
errors: UserError[]; errors: MenuErrorFragment[];
initial?: MenuItemDialogFormData; initial?: MenuItemDialogFormData;
initialDisplayValue?: string; initialDisplayValue?: string;
loading: boolean; loading: boolean;
@ -112,7 +113,11 @@ const MenuItemDialog: React.FC<MenuItemDialogProps> = ({
initialDisplayValue initialDisplayValue
]); ]);
const mutationErrors = getErrors(errors); const mutationErrors = errors.filter(err => err.field === null);
const formErrors = getFormErrors(["name"], errors);
const idError = ["category", "collection", "page", "url"]
.map(field => getFieldError(errors, field))
.reduce((acc, err) => acc || err);
let options: IMenu = []; let options: IMenu = [];
@ -208,10 +213,6 @@ const MenuItemDialog: React.FC<MenuItemDialogProps> = ({
const handleSubmit = () => onSubmit(data); const handleSubmit = () => onSubmit(data);
const idError = ["category", "collection", "page", "url"]
.map(field => getFieldError(errors, field))
.reduce((acc, err) => acc || err);
return ( return (
<Dialog <Dialog
onClose={onClose} onClose={onClose}
@ -252,8 +253,8 @@ const MenuItemDialog: React.FC<MenuItemDialogProps> = ({
})) }))
} }
name="name" name="name"
error={!!getFieldError(errors, "name")} error={!!formErrors.name}
helperText={getFieldError(errors, "name")?.message} helperText={getMenuErrorMessage(formErrors.name, intl)}
/> />
<FormSpacer /> <FormSpacer />
<AutocompleteSelectMenu <AutocompleteSelectMenu
@ -269,7 +270,7 @@ const MenuItemDialog: React.FC<MenuItemDialogProps> = ({
loading={loading} loading={loading}
options={options} options={options}
error={!!idError} error={!!idError}
helperText={idError?.message} helperText={getMenuErrorMessage(idError, intl)}
placeholder={intl.formatMessage({ placeholder={intl.formatMessage({
defaultMessage: "Start typing to begin search...", defaultMessage: "Start typing to begin search...",
id: "menuItemDialogLinkPlaceholder" id: "menuItemDialogLinkPlaceholder"
@ -280,8 +281,8 @@ const MenuItemDialog: React.FC<MenuItemDialogProps> = ({
<> <>
<FormSpacer /> <FormSpacer />
{mutationErrors.map(err => ( {mutationErrors.map(err => (
<Typography key={err} color="error"> <Typography key={err.code} color="error">
{err} {getMenuErrorMessage(err, intl)}
</Typography> </Typography>
))} ))}
</> </>

View file

@ -6,21 +6,28 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { MenuErrorFragment } from "@saleor/navigation/types/MenuErrorFragment";
import { getFormErrors } from "@saleor/utils/errors";
import getMenuErrorMessage from "@saleor/utils/errors/menu";
import { MenuDetailsFormData } from "../MenuDetailsPage"; import { MenuDetailsFormData } from "../MenuDetailsPage";
export interface MenuPropertiesProps { export interface MenuPropertiesProps {
data: MenuDetailsFormData; data: MenuDetailsFormData;
disabled: boolean; disabled: boolean;
errors: MenuErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const MenuProperties: React.FC<MenuPropertiesProps> = ({ const MenuProperties: React.FC<MenuPropertiesProps> = ({
data, data,
disabled, disabled,
errors,
onChange onChange
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const formErrors = getFormErrors(["name"], errors);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -29,12 +36,14 @@ const MenuProperties: React.FC<MenuPropertiesProps> = ({
<CardContent> <CardContent>
<TextField <TextField
disabled={disabled} disabled={disabled}
error={!!formErrors.name}
name={"name" as keyof MenuDetailsFormData} name={"name" as keyof MenuDetailsFormData}
fullWidth fullWidth
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Menu Title", defaultMessage: "Menu Title",
id: "menuPropertiesMenuTitle" id: "menuPropertiesMenuTitle"
})} })}
helperText={getMenuErrorMessage(formErrors.name, intl)}
value={data.name} value={data.name}
onChange={onChange} onChange={onChange}
/> />

Some files were not shown because too many files have changed in this diff Show more