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",
"string": "Attributes"
},
"src_dot_attributes_dot_attributeSlugUnique": {
"string": "Attribute with this slug already exists"
},
"src_dot_attributes_dot_attributeValueAlreadyExists": {
"string": "This value already exists within this attribute"
},
"src_dot_attributes_dot_components_dot_AttributeBulkDeleteDialog_dot_1184518529": {
"context": "dialog content",
"string": "{counter,plural,one{Are you sure you want to delete this attribute?} other{Are you sure you want to delete {displayQuantity} attributes?}}"
@ -477,10 +483,6 @@
"src_dot_attributes_dot_views_dot_AttributeCreate_dot_11941964": {
"string": "Successfully created attribute"
},
"src_dot_attributes_dot_views_dot_AttributeCreate_dot_354316953": {
"context": "attribute value edit error",
"string": "A value named {name} already exists"
},
"src_dot_attributes_dot_views_dot_AttributeDetails_dot_423042761": {
"context": "attribute value deleted",
"string": "Value deleted"
@ -836,9 +838,6 @@
"src_dot_collections_dot_views_dot_1152429477": {
"string": "Deleted collection"
},
"src_dot_collections_dot_views_dot_1597339737": {
"string": "Created collection"
},
"src_dot_collections_dot_views_dot_2001540731": {
"string": "Added product to collection"
},
@ -2722,9 +2721,6 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_1056718390": {
"string": "Payment successfully captured"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_1096896329": {
"string": "Could not fulfill items"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_1435191432": {
"string": "Draft order successfully finalized"
},
@ -2734,30 +2730,15 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_1475565380": {
"string": "Fulfillment successfully updated"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_149745214": {
"string": "Could not finalize draft"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_1632861387": {
"string": "Order line updated"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_1636370257": {
"string": "Order marked as paid"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_1708579411": {
"string": "Could not update shipping method"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_1852833563": {
"string": "Could not mark order as paid"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_1999598492": {
"string": "Order line added"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_2205960666": {
"string": "Could not delete order line"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_2714957902": {
"string": "Could not add note"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_2808777264": {
"string": "Order successfully cancelled"
},
@ -2770,27 +2751,12 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_3367579693": {
"string": "Order successfully updated"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_3991286734": {
"context": "notification",
"string": "Payment not refunded: {errorMessage}"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_4199953867": {
"string": "Could not cancel fulfillment"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_4245651107": {
"string": "Items successfully fulfilled"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_487150696": {
"string": "Could not update order line"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_553600482": {
"string": "Could not update fulfillment"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_557535634": {
"string": "Payment not captured: {errorMessage}"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_562214451": {
"string": "Could not create order line"
"src_dot_orders_dot_views_dot_OrderDetails_dot_580490159": {
"context": "window title",
"string": "Order #{orderNumber}"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_617145655": {
"string": "Shipping method successfully updated"
@ -2798,6 +2764,10 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_667274681": {
"string": "Payment successfully refunded"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_694622335": {
"context": "window title",
"string": "Draft Order #{orderNumber}"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_927945225": {
"string": "Fulfillment successfully cancelled"
},
@ -2893,9 +2863,6 @@
"context": "header",
"string": "Create Page"
},
"src_dot_pages_dot_views_dot_1457312643": {
"string": "Removed page"
},
"src_dot_pages_dot_views_dot_2680158037": {
"string": "Successfully created new page"
},
@ -4009,6 +3976,14 @@
"context": "shipping method has no value limits",
"string": "There are no value limits"
},
"src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_price": {
"context": "error message",
"string": "Maximum price cannot be lower than minimum"
},
"src_dot_shipping_dot_components_dot_ShippingZoneRateDialog_dot_weight": {
"context": "error message",
"string": "Maximum weight cannot be lower than minimum"
},
"src_dot_shipping_dot_components_dot_ShippingZoneRates_dot_1134347598": {
"context": "shipping method price",
"string": "Price"
@ -4236,9 +4211,6 @@
"src_dot_siteSettings_dot_components_dot_SiteSettingsPage_dot_866304242": {
"string": "This where you will find all of the settings determining your stores e-mails. You can determine main email address and some of the contents of your emails."
},
"src_dot_siteSettings_dot_views_dot_2276194921": {
"string": "Could not delete authorization key: {errorMessage}"
},
"src_dot_somethingWentWrong": {
"string": "Saleor ran into an unexpected problem"
},
@ -4745,6 +4717,98 @@
"context": "button",
"string": "Upload image"
},
"src_dot_utils_dot_errors_dot_alreadyExists": {
"context": "add authorization key error",
"string": "Authorization key with this type already exists"
},
"src_dot_utils_dot_errors_dot_attributeAlreadyAssigned": {
"string": "This attribute has already been assigned to this product type"
},
"src_dot_utils_dot_errors_dot_attributeCannotBeAssigned": {
"string": "This attribute cannot be assigned to this product type"
},
"src_dot_utils_dot_errors_dot_attributeVariantsDisabled": {
"string": "Variants are disabled in this product type"
},
"src_dot_utils_dot_errors_dot_billingNotSet": {
"context": "error message",
"string": "Billing address is not set"
},
"src_dot_utils_dot_errors_dot_cannotCancelFulfillment": {
"context": "error message",
"string": "This fulfillment cannot be cancelled"
},
"src_dot_utils_dot_errors_dot_cannotCancelOrder": {
"context": "error message",
"string": "This order cannot be cancelled"
},
"src_dot_utils_dot_errors_dot_cannotFulfillLine": {
"context": "error message",
"string": "Not enough items to fulfill"
},
"src_dot_utils_dot_errors_dot_cannotRefund": {
"context": "error message",
"string": "Manual payments can not be refunded"
},
"src_dot_utils_dot_errors_dot_cannotVoid": {
"context": "error message",
"string": "Only pre-authorized payments can be voided"
},
"src_dot_utils_dot_errors_dot_captureInactive": {
"context": "error message",
"string": "Only pre-authorized payments can be captured"
},
"src_dot_utils_dot_errors_dot_graphqlError": {
"string": "API error"
},
"src_dot_utils_dot_errors_dot_invalid": {
"string": "Invalid value"
},
"src_dot_utils_dot_errors_dot_invalidPassword": {
"string": "Invalid password"
},
"src_dot_utils_dot_errors_dot_noShippingAddress": {
"context": "error message",
"string": "Cannot choose a shipping method for an order without the shipping address"
},
"src_dot_utils_dot_errors_dot_notEditable": {
"context": "error message",
"string": "Only draft orders can be edited"
},
"src_dot_utils_dot_errors_dot_passwordNumeric": {
"string": "Password cannot be entirely numeric"
},
"src_dot_utils_dot_errors_dot_paymentMissing": {
"context": "error message",
"string": "There's no payment associated with the order"
},
"src_dot_utils_dot_errors_dot_shippingNotApplicable": {
"context": "error message",
"string": "Shipping method is not valid for chosen shipping address"
},
"src_dot_utils_dot_errors_dot_shippingRequired": {
"context": "error message",
"string": "Shipping method is required for this order"
},
"src_dot_utils_dot_errors_dot_skuUnique": {
"context": "bulk variant create error",
"string": "SKUs must be unique"
},
"src_dot_utils_dot_errors_dot_tooCommon": {
"string": "This password is too commonly used"
},
"src_dot_utils_dot_errors_dot_tooShort": {
"string": "This password is too short"
},
"src_dot_utils_dot_errors_dot_tooSimilar": {
"string": "These passwords are too similar"
},
"src_dot_utils_dot_errors_dot_unknownError": {
"string": "Unknown error"
},
"src_dot_utils_dot_errors_dot_variantNoDigitalContent": {
"string": "This variant does not have any digital content"
},
"src_dot_vouchers": {
"context": "vouchers section name",
"string": "Vouchers"
@ -4757,6 +4821,10 @@
"context": "header",
"string": "Create Webhook"
},
"src_dot_webhooks_dot_components_dot_WebhookDeleteDialog_dot_216945727": {
"context": "delete webhook",
"string": "Are you sure you want to delete this webhook?"
},
"src_dot_webhooks_dot_components_dot_WebhookDeleteDialog_dot_2297471173": {
"context": "delete webhook",
"string": "Are you sure you want to delete {name}?"
@ -4856,6 +4924,10 @@
"context": "section header",
"string": "Webhook Status"
},
"src_dot_webhooks_dot_components_dot_WebhooksDetailsPage_dot_1595053355": {
"context": "header",
"string": "Unnamed Webhook Details"
},
"src_dot_webhooks_dot_components_dot_WebhooksDetailsPage_dot_408706360": {
"context": "header",
"string": "{webhookName} Details"
@ -4890,6 +4962,9 @@
"context": "user action bar",
"string": "Action"
},
"src_dot_webhooks_dot_components_dot_WebhooksList_dot_618422799": {
"string": "Unnamed webhook"
},
"src_dot_webhooks_dot_components_dot_WebhooksList_dot_636461959": {
"context": "webhook name",
"string": "Name"

View file

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

View file

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

View file

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

View file

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

View file

@ -14,8 +14,9 @@ import Form from "@saleor/components/Form";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { getFormErrors } from "@saleor/utils/errors";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import { getAttributeValueErrorMessage } from "@saleor/attributes/errors";
import { AttributeDetails_attribute_values } from "../../types/AttributeDetails";
export interface AttributeValueEditDialogFormData {
@ -25,7 +26,7 @@ export interface AttributeValueEditDialogProps {
attributeValue: AttributeDetails_attribute_values | null;
confirmButtonState: ConfirmButtonTransitionState;
disabled: boolean;
errors: UserError[];
errors: ProductErrorFragment[];
open: boolean;
onSubmit: (data: AttributeValueEditDialogFormData) => void;
onClose: () => void;
@ -45,6 +46,7 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
name: maybe(() => attributeValue.name, "")
};
const errors = useModalDialogErrors(apiErrors, open);
const formErrors = getFormErrors(["name"], errors);
return (
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
@ -68,9 +70,12 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
<TextField
autoFocus
disabled={disabled}
error={!!getFieldError(errors, "name")}
error={!!formErrors.name}
fullWidth
helperText={getFieldError(errors, "name")?.message}
helperText={getAttributeValueErrorMessage(
formErrors.name,
intl
)}
name={"name" as keyof AttributeValueEditDialogFormData}
label={intl.formatMessage({
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
} from "./types/AttributeValueUpdate";
export const productErrorFragment = gql`
fragment ProductErrorFragment on ProductError {
code
field
}
`;
const attributeBulkDelete = gql`
${productErrorFragment}
mutation AttributeBulkDelete($ids: [ID!]!) {
attributeBulkDelete(ids: $ids) {
errors {
field
message
errors: productErrors {
...ProductErrorFragment
}
}
}
@ -51,11 +58,11 @@ export const AttributeBulkDeleteMutation = TypedMutation<
>(attributeBulkDelete);
const attributeDelete = gql`
${productErrorFragment}
mutation AttributeDelete($id: ID!) {
attributeDelete(id: $id) {
errors {
field
message
errors: productErrors {
...ProductErrorFragment
}
}
}
@ -67,15 +74,15 @@ export const AttributeDeleteMutation = TypedMutation<
export const attributeUpdateMutation = gql`
${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeUpdate($id: ID!, $input: AttributeUpdateInput!) {
attributeUpdate(id: $id, input: $input) {
errors {
field
message
}
attribute {
...AttributeDetailsFragment
}
errors: productErrors {
...ProductErrorFragment
}
}
}
`;
@ -86,15 +93,15 @@ export const AttributeUpdateMutation = TypedMutation<
const attributeValueDelete = gql`
${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueDelete($id: ID!) {
attributeValueDelete(id: $id) {
errors {
field
message
}
attribute {
...AttributeDetailsFragment
}
errors: productErrors {
...ProductErrorFragment
}
}
}
`;
@ -105,15 +112,15 @@ export const AttributeValueDeleteMutation = TypedMutation<
export const attributeValueUpdateMutation = gql`
${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueUpdate($id: ID!, $input: AttributeValueCreateInput!) {
attributeValueUpdate(id: $id, input: $input) {
errors {
field
message
}
attribute {
...AttributeDetailsFragment
}
errors: productErrors {
...ProductErrorFragment
}
}
}
`;
@ -124,15 +131,15 @@ export const AttributeValueUpdateMutation = TypedMutation<
export const attributeValueCreateMutation = gql`
${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeValueCreate($id: ID!, $input: AttributeValueCreateInput!) {
attributeValueCreate(attribute: $id, input: $input) {
errors {
field
message
}
attribute {
...AttributeDetailsFragment
}
errors: productErrors {
...ProductErrorFragment
}
}
}
`;
@ -143,15 +150,15 @@ export const AttributeValueCreateMutation = TypedMutation<
export const attributeCreateMutation = gql`
${attributeDetailsFragment}
${productErrorFragment}
mutation AttributeCreate($input: AttributeCreateInput!) {
attributeCreate(input: $input) {
errors {
field
message
}
attribute {
...AttributeDetailsFragment
}
errors: productErrors {
...ProductErrorFragment
}
}
}
`;
@ -161,18 +168,18 @@ export const AttributeCreateMutation = TypedMutation<
>(attributeCreateMutation);
const attributeValueReorderMutation = gql`
${productErrorFragment}
mutation AttributeValueReorder($id: ID!, $move: ReorderInput!) {
attributeReorderValues(attributeId: $id, moves: [$move]) {
errors {
field
message
}
attribute {
id
values {
id
}
}
errors: productErrors {
...ProductErrorFragment
}
}
}
`;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -99,7 +99,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
);
if (backgroundImageError) {
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 useDateLocalize from "@saleor/hooks/useDateLocalize";
import { commonMessages, sectionNames } from "@saleor/intl";
import { UserError } from "../../../types";
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
import CollectionDetails from "../CollectionDetails/CollectionDetails";
import { CollectionImage } from "../CollectionImage/CollectionImage";
@ -37,7 +37,7 @@ export interface CollectionCreatePageFormData {
export interface CollectionCreatePageProps {
disabled: boolean;
errors: UserError[];
errors: ProductErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void;
onSubmit: (data: CollectionCreatePageFormData) => void;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -30,7 +30,7 @@ const Story: React.FC<Partial<
MultiAutocompleteSelectFieldProps & {
enableLoadMore: boolean;
}
>> = ({ allowCustomValues, enableLoadMore }) => {
>> = ({ enableLoadMore, ...rest }) => {
const { change, data: countries } = useMultiAutocomplete([suggestions[0]]);
return (
@ -49,7 +49,7 @@ const Story: React.FC<Partial<
loading={loading}
hasMore={enableLoadMore ? hasMore : false}
onFetchMore={enableLoadMore ? onFetchMore : undefined}
allowCustomValues={allowCustomValues}
{...rest}
/>
)}
</ChoiceProvider>
@ -84,4 +84,5 @@ storiesOf("Generics / Multiple select with autocomplete", module)
.add("interactive with custom option", () => (
<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> {
allowCustomValues?: boolean;
displayValues: MultiAutocompleteChoiceType[];
error?: boolean;
name: string;
choices: MultiAutocompleteChoiceType[];
value: string[];
@ -70,19 +71,16 @@ export interface MultiAutocompleteSelectFieldProps
onChange: (event: React.ChangeEvent<any>) => void;
}
const DebounceAutocomplete: React.ComponentType<
DebounceProps<string>
> = Debounce;
const DebounceAutocomplete: React.ComponentType<DebounceProps<
string
>> = Debounce;
const MultiAutocompleteSelectFieldComponent: React.FC<
MultiAutocompleteSelectFieldProps
> = props => {
const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFieldProps> = props => {
const {
allowCustomValues,
choices,
displayValues,
error,
hasMore,
helperText,
label,
@ -147,6 +145,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<
id: undefined,
onClick: toggleMenu
}}
error={error}
helperText={helperText}
label={label}
fullWidth={true}
@ -191,9 +190,11 @@ const MultiAutocompleteSelectFieldComponent: React.FC<
);
};
const MultiAutocompleteSelectField: React.FC<
MultiAutocompleteSelectFieldProps
> = ({ choices, fetchChoices, ...props }) => {
const MultiAutocompleteSelectField: React.FC<MultiAutocompleteSelectFieldProps> = ({
choices,
fetchChoices,
...props
}) => {
const [query, setQuery] = React.useState("");
if (fetchChoices) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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 */
// This file was automatically generated and should not be edited.
import { AccountErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: BulkRemoveCustomers
// ====================================================
export interface BulkRemoveCustomers_customerBulkDelete_errors {
__typename: "Error";
__typename: "AccountError";
code: AccountErrorCode;
field: string | null;
message: string | null;
}
export interface BulkRemoveCustomers_customerBulkDelete {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface CreateCustomer_customerCreate_errors {
__typename: "Error";
__typename: "AccountError";
code: AccountErrorCode;
field: string | null;
message: string | null;
}
export interface CreateCustomer_customerCreate_user {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface CreateCustomerAddress_addressCreate_errors {
__typename: "Error";
__typename: "AccountError";
code: AccountErrorCode;
field: string | null;
message: string | null;
}
export interface CreateCustomerAddress_addressCreate_address_country {

View file

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

View file

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

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface SetCustomerDefaultAddress_addressSetDefault_errors {
__typename: "Error";
__typename: "AccountError";
code: AccountErrorCode;
field: string | null;
message: string | null;
}
export interface SetCustomerDefaultAddress_addressSetDefault_user_addresses_country {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface UpdateCustomer_customerUpdate_errors {
__typename: "Error";
__typename: "AccountError";
code: AccountErrorCode;
field: string | null;
message: string | null;
}
export interface UpdateCustomer_customerUpdate_user_defaultShippingAddress_country {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface UpdateCustomerAddress_addressUpdate_errors {
__typename: "Error";
__typename: "AccountError";
code: AccountErrorCode;
field: string | null;
message: string | null;
}
export interface UpdateCustomerAddress_addressUpdate_address_country {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,15 +8,16 @@ import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Grid from "@saleor/components/Grid";
import { commonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors";
import { UserError } from "../../../types";
import { getFormErrors } from "@saleor/utils/errors";
import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { FormData } from "../VoucherDetailsPage";
interface VoucherDatesProps {
data: FormData;
defaultCurrency: string;
disabled: boolean;
errors: UserError[];
errors: DiscountErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -28,6 +29,8 @@ const VoucherDates = ({
}: VoucherDatesProps) => {
const intl = useIntl();
const formErrors = getFormErrors(["startDate", "endDate"], errors);
return (
<Card>
<CardTitle
@ -40,8 +43,8 @@ const VoucherDates = ({
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!getFieldError(errors, "startDate")}
helperText={getFieldError(errors, "startDate")?.message}
error={!!formErrors.startDate}
helperText={getDiscountErrorMessage(formErrors.startDate, intl)}
name={"startDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)}
@ -54,8 +57,8 @@ const VoucherDates = ({
/>
<TextField
disabled={disabled}
error={!!getFieldError(errors, "startDate")}
helperText={getFieldError(errors, "startDate")?.message}
error={!!formErrors.startDate}
helperText={getDiscountErrorMessage(formErrors.startDate, intl)}
name={"startTime" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startHour)}
@ -80,8 +83,8 @@ const VoucherDates = ({
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!getFieldError(errors, "endDate")}
helperText={getFieldError(errors, "endDate")?.message}
error={!!formErrors.endDate}
helperText={getDiscountErrorMessage(formErrors.endDate, intl)}
name={"endDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)}
@ -94,8 +97,8 @@ const VoucherDates = ({
/>
<TextField
disabled={disabled}
error={!!getFieldError(errors, "endDate")}
helperText={getFieldError(errors, "endDate")?.message}
error={!!formErrors.endDate}
helperText={getDiscountErrorMessage(formErrors.endDate, intl)}
name={"endTime" as keyof FormData}
onChange={onChange}
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 { RequirementsPicker } from "@saleor/discounts/types";
import { sectionNames } from "@saleor/intl";
import { DiscountErrorFragment } from "@saleor/discounts/types/DiscountErrorFragment";
import { maybe, splitDateTime } from "../../../misc";
import { ListProps, TabListActions, UserError } from "../../../types";
import { ListProps, TabListActions } from "../../../types";
import {
DiscountValueTypeEnum,
VoucherTypeEnum
@ -72,7 +73,7 @@ export interface VoucherDetailsPageProps
> {
activeTab: VoucherDetailsPageTab;
defaultCurrency: string;
errors: UserError[];
errors: DiscountErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState;
voucher: VoucherDetails_voucher;
onBack: () => void;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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 */
// 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
// ====================================================
export interface SaleCataloguesAdd_saleCataloguesAdd_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface SaleCataloguesAdd_saleCataloguesAdd_sale_products_edges_node_productType {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface SaleCataloguesRemove_saleCataloguesRemove_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface SaleCataloguesRemove_saleCataloguesRemove_sale_products_edges_node_productType {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface SaleCreate_saleCreate_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface SaleCreate_saleCreate_sale {

View file

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

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface SaleUpdate_saleUpdate_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface SaleUpdate_saleUpdate_sale {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface VoucherCataloguesAdd_voucherCataloguesAdd_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface VoucherCataloguesAdd_voucherCataloguesAdd_voucher_countries {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface VoucherCataloguesRemove_voucherCataloguesRemove_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface VoucherCataloguesRemove_voucherCataloguesRemove_voucher_countries {

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface VoucherCreate_voucherCreate_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface VoucherCreate_voucherCreate_voucher_countries {

View file

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

View file

@ -2,16 +2,16 @@
/* eslint-disable */
// 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
// ====================================================
export interface VoucherUpdate_voucherUpdate_errors {
__typename: "Error";
__typename: "DiscountError";
code: DiscountErrorCode;
field: string | null;
message: string | null;
}
export interface VoucherUpdate_voucherUpdate_voucher_countries {

View file

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

View file

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

View file

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

View file

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

View file

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

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