Merge pull request #410 from mirumee/ref/do-not-format-errors-in-forms

Do not store errors in form component
This commit is contained in:
Marcin Gębala 2020-02-25 15:54:27 +01:00 committed by GitHub
commit 16291446a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 2242 additions and 778 deletions

View file

@ -35,6 +35,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Use structurized JSON files instead of PO - #403 by @dominik-zeglen
- Remove PO files from repo and update translations #409 by @dominik-zeglen
- Add optional chaining and explicitely return "Not found" page - #408 by @dominik-zeglen
- Do not store errors in form component - #410 by @dominik-zeglen
## 2.0.0

View file

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

View file

@ -108,8 +108,8 @@ const AttributePage: React.FC<AttributePageProps> = ({
});
return (
<Form errors={errors} initial={initialForm} onSubmit={handleSubmit}>
{({ change, errors: formErrors, data, submit }) => (
<Form initial={initialForm} onSubmit={handleSubmit}>
{({ change, data, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.attributes)}
@ -130,7 +130,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
canChangeType={attribute === null}
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -146,7 +146,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
<div>
<AttributeProperties
data={data}
errors={formErrors}
errors={errors}
disabled={disabled}
onChange={change}
/>

View file

@ -11,13 +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 { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { AttributePageFormData } from "../AttributePage";
export interface AttributePropertiesProps {
data: AttributePageFormData;
disabled: boolean;
errors: FormErrors<"storefrontSearchPosition">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -85,9 +86,11 @@ const AttributeProperties: React.FC<AttributePropertiesProps> = ({
{data.filterableInStorefront && (
<TextField
disabled={disabled}
error={!!errors.storefrontSearchPosition}
error={!!getFieldError(errors, "storefrontSearchPosition")}
fullWidth
helperText={errors.storefrontSearchPosition}
helperText={
getFieldError(errors, "storefrontSearchPosition")?.message
}
name={"storefrontSearchPosition" as keyof AttributePageFormData}
label={intl.formatMessage({
defaultMessage: "Position in faceted navigation",

View file

@ -15,6 +15,7 @@ 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 { AttributeDetails_attribute_values } from "../../types/AttributeDetails";
export interface AttributeValueEditDialogFormData {
@ -60,16 +61,16 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
/>
)}
</DialogTitle>
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, submit }) => (
<>
<DialogContent>
<TextField
autoFocus
disabled={disabled}
error={!!formErrors.name}
error={!!getFieldError(errors, "name")}
fullWidth
helperText={formErrors.name}
helperText={getFieldError(errors, "name")?.message}
name={"name" as keyof AttributeValueEditDialogFormData}
label={intl.formatMessage({
defaultMessage: "Name",

View file

@ -40,18 +40,13 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
disabled,
onSubmit,
onBack,
errors: userErrors,
errors,
saveButtonBarState
}) => {
const intl = useIntl();
return (
<Form
onSubmit={onSubmit}
initial={initialData}
errors={userErrors}
confirmLeave
>
{({ data, change, errors, submit, hasChanged }) => (
<Form onSubmit={onSubmit} initial={initialData} confirmLeave>
{({ data, change, submit, hasChanged }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.categories)}

View file

@ -9,6 +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 { maybe } from "../../../misc";
import { CategoryDetails_category } from "../../types/CategoryDetails";
@ -19,7 +21,7 @@ interface CategoryDetailsFormProps {
description: RawDraftContentState;
};
disabled: boolean;
errors: { [key: string]: string };
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -47,16 +49,16 @@ export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({
disabled={disabled}
value={data && data.name}
onChange={onChange}
error={!!errors.name}
helperText={errors.name}
error={!!getFieldError(errors, "name")}
helperText={getFieldError(errors, "name")?.message}
fullWidth
/>
</div>
<FormSpacer />
<RichTextEditor
disabled={disabled}
error={!!errors.descriptionJson}
helperText={errors.descriptionJson}
error={!!getFieldError(errors, "descriptionJson")}
helperText={getFieldError(errors, "descriptionJson")?.message}
label={intl.formatMessage({
defaultMessage: "Category Description"
})}

View file

@ -75,7 +75,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
currentTab,
category,
disabled,
errors: userErrors,
errors,
pageInfo,
products,
saveButtonBarState,
@ -115,13 +115,8 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
seoTitle: ""
};
return (
<Form
onSubmit={onSubmit}
initial={initialData}
errors={userErrors}
confirmLeave
>
{({ data, change, errors, submit, hasChanged }) => (
<Form onSubmit={onSubmit} initial={initialData} confirmLeave>
{({ data, change, submit, hasChanged }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.categories)}

View file

@ -68,8 +68,8 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
const localizeDate = useDateLocalize();
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.collections)}
@ -85,7 +85,7 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
<CollectionDetails
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -147,7 +147,7 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
<CardContent>
<VisibilityCard
data={data}
errors={formErrors}
errors={errors}
disabled={disabled}
hiddenMessage={intl.formatMessage(
{

View file

@ -10,7 +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 { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { CollectionDetails_collection } from "../../types/CollectionDetails";
export interface CollectionDetailsProps {
@ -20,7 +21,7 @@ export interface CollectionDetailsProps {
name: string;
};
disabled: boolean;
errors: FormErrors<"descriptionJson" | "name">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -48,14 +49,14 @@ const CollectionDetails: React.FC<CollectionDetailsProps> = ({
disabled={disabled}
value={data.name}
onChange={onChange}
error={!!errors.name}
helperText={errors.name}
error={!!getFieldError(errors, "name")}
helperText={getFieldError(errors, "name")?.message}
fullWidth
/>
<FormSpacer />
<RichTextEditor
error={!!errors.descriptionJson}
helperText={errors.descriptionJson}
error={!!getFieldError(errors, "descriptionJson")}
helperText={getFieldError(errors, "descriptionJson")?.message}
initial={maybe(() => JSON.parse(collection.descriptionJson))}
label={intl.formatMessage(commonMessages.description)}
name="description"

View file

@ -18,7 +18,7 @@ import VisibilityCard from "@saleor/components/VisibilityCard";
import useDateLocalize from "@saleor/hooks/useDateLocalize";
import { sectionNames } from "@saleor/intl";
import { maybe } from "../../../misc";
import { ListActions, PageListProps } from "../../../types";
import { ListActions, PageListProps, UserError } from "../../../types";
import { CollectionDetails_collection } from "../../types/CollectionDetails";
import CollectionDetails from "../CollectionDetails/CollectionDetails";
import { CollectionImage } from "../CollectionImage/CollectionImage";
@ -37,6 +37,7 @@ export interface CollectionDetailsPageFormData {
export interface CollectionDetailsPageProps extends PageListProps, ListActions {
collection: CollectionDetails_collection;
errors: UserError[];
isFeatured: boolean;
saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void;
@ -50,6 +51,7 @@ export interface CollectionDetailsPageProps extends PageListProps, ListActions {
const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
collection,
disabled,
errors,
isFeatured,
saveButtonBarState,
onBack,
@ -77,7 +79,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
onSubmit={onSubmit}
confirmLeave
>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.collections)}
@ -89,7 +91,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
collection={collection}
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -124,7 +126,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
<div>
<VisibilityCard
data={data}
errors={formErrors}
errors={errors}
disabled={disabled}
hiddenMessage={intl.formatMessage(
{

View file

@ -205,6 +205,9 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
onBack={handleBack}
disabled={loading}
collection={maybe(() => data.collection)}
errors={
updateCollection.opts?.data?.collectionUpdate.errors || []
}
isFeatured={maybe(
() =>
data.shop.homepageCollection.id === data.collection.id,

View file

@ -5,7 +5,8 @@ import { useIntl } from "react-intl";
import { AddressTypeInput } from "@saleor/customers/types";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import FormSpacer from "../FormSpacer";
import SingleAutocompleteSelectField, {
SingleAutocompleteChoiceType
@ -27,7 +28,7 @@ interface AddressEditProps {
countryDisplayValue: string;
data: AddressTypeInput;
disabled?: boolean;
errors: FormErrors<keyof AddressTypeInput>;
errors: UserError[];
onChange(event: React.ChangeEvent<any>);
onCountryChange(event: React.ChangeEvent<any>);
}
@ -52,8 +53,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div>
<TextField
disabled={disabled}
error={!!errors.firstName}
helperText={errors.firstName}
error={!!getFieldError(errors, "firstName")}
helperText={getFieldError(errors, "firstName")?.message}
label={intl.formatMessage(commonMessages.firstName)}
name="firstName"
onChange={onChange}
@ -64,8 +65,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div>
<TextField
disabled={disabled}
error={!!errors.lastName}
helperText={errors.lastName}
error={!!getFieldError(errors, "lastName")}
helperText={getFieldError(errors, "lastName")?.message}
label={intl.formatMessage(commonMessages.lastName)}
name="lastName"
onChange={onChange}
@ -79,8 +80,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div>
<TextField
disabled={disabled}
error={!!errors.companyName}
helperText={errors.companyName}
error={!!getFieldError(errors, "companyName")}
helperText={getFieldError(errors, "companyName")?.message}
label={intl.formatMessage({
defaultMessage: "Company"
})}
@ -93,9 +94,9 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div>
<TextField
disabled={disabled}
error={!!errors.phone}
error={!!getFieldError(errors, "phone")}
fullWidth
helperText={errors.phone}
helperText={getFieldError(errors, "phone")?.message}
label={intl.formatMessage({
defaultMessage: "Phone"
})}
@ -108,8 +109,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.streetAddress1}
helperText={errors.streetAddress1}
error={!!getFieldError(errors, "streetAddress1")}
helperText={getFieldError(errors, "streetAddress1")?.message}
label={intl.formatMessage({
defaultMessage: "Address line 1"
})}
@ -121,8 +122,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.streetAddress2}
helperText={errors.streetAddress2}
error={!!getFieldError(errors, "streetAddress2")}
helperText={getFieldError(errors, "streetAddress2")?.message}
label={intl.formatMessage({
defaultMessage: "Address line 2"
})}
@ -136,8 +137,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div>
<TextField
disabled={disabled}
error={!!errors.city}
helperText={errors.city}
error={!!getFieldError(errors, "city")}
helperText={getFieldError(errors, "city")?.message}
label={intl.formatMessage({
defaultMessage: "City"
})}
@ -150,8 +151,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div>
<TextField
disabled={disabled}
error={!!errors.postalCode}
helperText={errors.postalCode}
error={!!getFieldError(errors, "postalCode")}
helperText={getFieldError(errors, "postalCode")?.message}
label={intl.formatMessage({
defaultMessage: "ZIP / Postal code"
})}
@ -169,8 +170,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<SingleAutocompleteSelectField
disabled={disabled}
displayValue={countryDisplayValue}
error={!!errors.country}
helperText={errors.country}
error={!!getFieldError(errors, "country")}
helperText={getFieldError(errors, "country")?.message}
label={intl.formatMessage({
defaultMessage: "Country"
})}
@ -186,8 +187,8 @@ const AddressEdit: React.FC<AddressEditProps> = props => {
<div>
<TextField
disabled={disabled}
error={!!errors.countryArea}
helperText={errors.countryArea}
error={!!getFieldError(errors, "countryArea")}
helperText={getFieldError(errors, "countryArea")?.message}
label={intl.formatMessage({
defaultMessage: "Country area"
})}

View file

@ -68,7 +68,7 @@ export const EditableTableCell: React.FC<EditableTableCellProps> = props => {
};
const [opened, setOpenStatus] = React.useState(focused);
const { change, data } = useForm({ value }, [], handleConfirm);
const { change, data } = useForm({ value }, handleConfirm);
const enable = () => setOpenStatus(true);
const disable = () => setOpenStatus(false);

View file

@ -1,20 +1,18 @@
import React from "react";
import useForm, { UseFormResult } from "@saleor/hooks/useForm";
import { UserError } from "@saleor/types";
export interface FormProps<T> {
children: (props: UseFormResult<T>) => React.ReactNode;
confirmLeave?: boolean;
errors?: UserError[];
initial?: T;
resetOnSubmit?: boolean;
onSubmit?: (data: T) => void;
}
function Form<T>(props: FormProps<T>) {
const { children, errors, initial, resetOnSubmit, onSubmit } = props;
const renderProps = useForm(initial, errors, onSubmit);
const { children, initial, resetOnSubmit, onSubmit } = props;
const renderProps = useForm(initial, onSubmit);
function handleSubmit(event?: React.FormEvent<any>, cb?: () => void) {
const { reset, submit } = renderProps;

View file

@ -8,7 +8,8 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import RadioSwitchField from "@saleor/components/RadioSwitchField";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { DateContext } from "../Date/DateContext";
import FormSpacer from "../FormSpacer";
@ -52,7 +53,7 @@ interface VisibilityCardProps {
isPublished: boolean;
publicationDate: string;
};
errors: FormErrors<"isPublished" | "publicationDate">;
errors: UserError[];
disabled?: boolean;
hiddenMessage: string;
onChange: (event: React.ChangeEvent<any>) => void;
@ -62,7 +63,6 @@ interface VisibilityCardProps {
export const VisibilityCard: React.FC<VisibilityCardProps> = props => {
const {
children,
data: { isPublished, publicationDate },
errors,
disabled,
@ -101,7 +101,7 @@ export const VisibilityCard: React.FC<VisibilityCardProps> = props => {
<CardContent>
<RadioSwitchField
disabled={disabled}
error={!!errors.isPublished}
error={!!getFieldError(errors, "isPublished")}
firstOptionLabel={
<>
<p className={classes.label}>
@ -140,7 +140,7 @@ export const VisibilityCard: React.FC<VisibilityCardProps> = props => {
)}
{isPublicationDate && (
<TextField
error={!!errors.publicationDate}
error={!!getFieldError(errors, "publicationDate")}
disabled={disabled}
label={intl.formatMessage({
defaultMessage: "Publish on",
@ -149,7 +149,7 @@ export const VisibilityCard: React.FC<VisibilityCardProps> = props => {
name="publicationDate"
type="date"
fullWidth={true}
helperText={errors.publicationDate}
helperText={getFieldError(errors, "publicationDate")?.message}
value={publicationDate ? publicationDate : ""}
onChange={onChange}
className={classes.date}
@ -160,10 +160,12 @@ export const VisibilityCard: React.FC<VisibilityCardProps> = props => {
)}
</>
)}
{errors.isPublished && (
{getFieldError(errors, "isPublished") && (
<>
<FormSpacer />
<Typography color="error">{errors.isPublished}</Typography>
<Typography color="error">
{getFieldError(errors, "isPublished")?.message}
</Typography>
</>
)}
<div className={classes.children}>{children}</div>

View file

@ -43,7 +43,10 @@ const styles = createStyles({
}
});
const CustomerAddressDialog = withStyles(styles, {})(
const CustomerAddressDialog = withStyles(
styles,
{}
)(
({
address,
classes,
@ -98,12 +101,8 @@ const CustomerAddressDialog = withStyles(styles, {})(
fullWidth
maxWidth="sm"
>
<Form
initial={initialForm}
errors={dialogErrors}
onSubmit={handleSubmit}
>
{({ change, data, errors: formErrors }) => {
<Form initial={initialForm} onSubmit={handleSubmit}>
{({ change, data }) => {
const handleCountrySelect = createSingleAutocompleteSelectHandler(
change,
setCountryDisplayName,
@ -130,7 +129,7 @@ const CustomerAddressDialog = withStyles(styles, {})(
countries={countryChoices}
data={data}
countryDisplayValue={countryDisplayName}
errors={formErrors}
errors={dialogErrors}
onChange={change}
onCountryChange={handleCountrySelect}
/>

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 { FormErrors } from "../../../types";
import { UserError } from "../../../types";
import { AddressTypeInput } from "../../types";
const useStyles = makeStyles(
@ -26,7 +26,7 @@ export interface CustomerCreateAddressProps {
countryDisplayName: string;
data: AddressTypeInput;
disabled: boolean;
errors: FormErrors<keyof AddressTypeInput>;
errors: UserError[];
onChange(event: React.ChangeEvent<any>);
onCountryChange(event: React.ChangeEvent<any>);
}

View file

@ -7,7 +7,8 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
import { getFieldError } from "@saleor/utils/errors";
import { UserError } from "../../../types";
import { CustomerCreatePageFormData } from "../CustomerCreatePage";
const useStyles = makeStyles(
@ -25,7 +26,7 @@ const useStyles = makeStyles(
export interface CustomerCreateDetailsProps {
data: CustomerCreatePageFormData;
disabled: boolean;
errors: FormErrors<"customerFirstName" | "customerLastName" | "email">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -47,33 +48,33 @@ const CustomerCreateDetails: React.FC<CustomerCreateDetailsProps> = props => {
<div className={classes.root}>
<TextField
disabled={disabled}
error={!!errors.customerFirstName}
error={!!getFieldError(errors, "customerFirstName")}
fullWidth
name="customerFirstName"
label={intl.formatMessage(commonMessages.firstName)}
helperText={errors.customerFirstName}
helperText={getFieldError(errors, "customerFirstName")?.message}
type="text"
value={data.customerFirstName}
onChange={onChange}
/>
<TextField
disabled={disabled}
error={!!errors.customerLastName}
error={!!getFieldError(errors, "customerLastName")}
fullWidth
name="customerLastName"
label={intl.formatMessage(commonMessages.lastName)}
helperText={errors.customerLastName}
helperText={getFieldError(errors, "customerLastName")?.message}
type="text"
value={data.customerLastName}
onChange={onChange}
/>
<TextField
disabled={disabled}
error={!!errors.email}
error={!!getFieldError(errors, "email")}
fullWidth
name="email"
label={intl.formatMessage(commonMessages.email)}
helperText={errors.email}
helperText={getFieldError(errors, "email")?.message}
type="email"
value={data.email}
onChange={onChange}

View file

@ -7,15 +7,15 @@ 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";
export interface CustomerCreateNoteProps {
data: {
note: string;
};
disabled: boolean;
errors: Partial<{
note: string;
}>;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -42,11 +42,11 @@ const CustomerCreateNote: React.FC<CustomerCreateNoteProps> = ({
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.note}
error={!!getFieldError(errors, "note")}
fullWidth
multiline
name="note"
helperText={errors.note}
helperText={getFieldError(errors, "note")?.message}
label={intl.formatMessage({
defaultMessage: "Note",
description: "note about customer"

View file

@ -61,7 +61,7 @@ export interface CustomerCreatePageProps {
const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
countries,
disabled,
errors,
errors: apiErrors,
saveButtonBar,
onBack,
onSubmit
@ -98,6 +98,8 @@ const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
})
);
const errors = [...apiErrors, ...validationErrors];
const handleSubmit = (
formData: CustomerCreatePageFormData & AddressTypeInput
) => {
@ -130,13 +132,8 @@ const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
};
return (
<Form
initial={initialForm}
onSubmit={handleSubmit}
errors={[...errors, ...validationErrors]}
confirmLeave
>
{({ change, data, errors: formErrors, hasChanged, submit }) => {
<Form initial={initialForm} onSubmit={handleSubmit} confirmLeave>
{({ change, data, hasChanged, submit }) => {
const handleCountrySelect = createSingleAutocompleteSelectHandler(
change,
setCountryDisplayName,
@ -159,7 +156,7 @@ const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
<CustomerCreateDetails
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -168,7 +165,7 @@ const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
countryDisplayName={countryDisplayName}
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
onCountryChange={handleCountrySelect}
/>
@ -176,7 +173,7 @@ const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
<CustomerCreateNote
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
</div>

View file

@ -11,7 +11,8 @@ import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Skeleton from "@saleor/components/Skeleton";
import { maybe } from "@saleor/misc";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { CustomerDetails_user } from "../../types/CustomerDetails";
const useStyles = makeStyles(
@ -39,7 +40,7 @@ export interface CustomerDetailsProps {
note: string;
};
disabled: boolean;
errors: FormErrors<"isActive" | "note">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -90,10 +91,10 @@ const CustomerDetails: React.FC<CustomerDetailsProps> = props => {
/>
<TextField
disabled={disabled}
error={!!errors.note}
error={!!getFieldError(errors, "note")}
fullWidth
multiline
helperText={errors.note}
helperText={getFieldError(errors, "note")?.message}
name="note"
label={intl.formatMessage({
defaultMessage: "Note",

View file

@ -56,7 +56,6 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
return (
<Form
errors={errors}
initial={{
email: maybe(() => customer.email, ""),
firstName: maybe(() => customer.firstName, ""),
@ -67,7 +66,7 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
onSubmit={onSubmit}
confirmLeave
>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.customers)}
@ -79,14 +78,14 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
customer={customer}
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
<CustomerInfo
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />

View file

@ -10,6 +10,8 @@ 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";
const useStyles = makeStyles(
theme => ({
@ -33,11 +35,7 @@ export interface CustomerInfoProps {
email: string;
};
disabled: boolean;
errors: {
firstName?: string;
lastName?: string;
email?: string;
};
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -64,9 +62,9 @@ const CustomerInfo: React.FC<CustomerInfoProps> = props => {
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!errors.firstName}
error={!!getFieldError(errors, "firstName")}
fullWidth
helperText={errors.firstName}
helperText={getFieldError(errors, "firstName")?.message}
name="firstName"
type="text"
label={intl.formatMessage(commonMessages.firstName)}
@ -75,9 +73,9 @@ const CustomerInfo: React.FC<CustomerInfoProps> = props => {
/>
<TextField
disabled={disabled}
error={!!errors.lastName}
error={!!getFieldError(errors, "lastName")}
fullWidth
helperText={errors.lastName}
helperText={getFieldError(errors, "lastName")?.message}
name="lastName"
type="text"
label={intl.formatMessage(commonMessages.lastName)}
@ -94,9 +92,9 @@ const CustomerInfo: React.FC<CustomerInfoProps> = props => {
</Typography>
<TextField
disabled={disabled}
error={!!errors.email}
error={!!getFieldError(errors, "email")}
fullWidth
helperText={errors.email}
helperText={getFieldError(errors, "email")?.message}
name="email"
type="email"
label={intl.formatMessage(commonMessages.email)}

View file

@ -8,7 +8,8 @@ import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Grid from "@saleor/components/Grid";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
import { getFieldError } from "@saleor/utils/errors";
import { UserError } from "../../../types";
interface DiscountDatesProps {
data: {
@ -20,7 +21,7 @@ interface DiscountDatesProps {
};
defaultCurrency: string;
disabled: boolean;
errors: FormErrors<"endDate" | "startDate">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -44,8 +45,8 @@ const DiscountDates = ({
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
error={!!getFieldError(errors, "startDate")}
helperText={getFieldError(errors, "startDate")?.message}
name={"startDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)}
@ -58,8 +59,8 @@ const DiscountDates = ({
/>
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
error={!!getFieldError(errors, "startDate")}
helperText={getFieldError(errors, "startDate")?.message}
name={"startTime" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startHour)}
@ -84,8 +85,8 @@ const DiscountDates = ({
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
error={!!getFieldError(errors, "endDate")}
helperText={getFieldError(errors, "endDate")?.message}
name={"endDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)}
@ -98,8 +99,8 @@ const DiscountDates = ({
/>
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
error={!!getFieldError(errors, "endDate")}
helperText={getFieldError(errors, "endDate")?.message}
name={"endTime" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endHour)}

View file

@ -57,8 +57,8 @@ const SaleCreatePage: React.FC<SaleCreatePageProps> = ({
value: ""
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.sales)}
@ -74,7 +74,7 @@ const SaleCreatePage: React.FC<SaleCreatePageProps> = ({
<SaleInfo
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -84,7 +84,7 @@ const SaleCreatePage: React.FC<SaleCreatePageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
</div>

View file

@ -121,8 +121,8 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
value: maybe(() => sale.value.toString(), "")
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.sales)}
@ -133,7 +133,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
<SaleInfo
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -143,7 +143,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
currencySymbol={defaultCurrency}
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -258,7 +258,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
</div>

View file

@ -6,14 +6,14 @@ 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 { FormData } from "../SaleDetailsPage";
export interface SaleInfoProps {
data: FormData;
disabled: boolean;
errors: {
name?: string;
};
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -33,8 +33,8 @@ const SaleInfo: React.FC<SaleInfoProps> = ({
<CardContent>
<TextField
disabled={disabled}
error={!!errors.name}
helperText={errors.name}
error={!!getFieldError(errors, "name")}
helperText={getFieldError(errors, "name")?.message}
name={"name" as keyof FormData}
onChange={onChange}
label={intl.formatMessage({

View file

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

View file

@ -81,8 +81,8 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.vouchers)}
@ -97,7 +97,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
<div>
<VoucherInfo
data={data}
errors={formErrors}
errors={errors}
disabled={disabled}
onChange={change}
variant="create"
@ -106,7 +106,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
<VoucherTypes
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
{data.discountType.toString() !== "SHIPPING" ? (
@ -114,7 +114,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
variant="create"
/>
@ -124,7 +124,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -132,7 +132,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -140,7 +140,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
</div>

View file

@ -8,14 +8,15 @@ import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Grid from "@saleor/components/Grid";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
import { getFieldError } from "@saleor/utils/errors";
import { UserError } from "../../../types";
import { FormData } from "../VoucherDetailsPage";
interface VoucherDatesProps {
data: FormData;
defaultCurrency: string;
disabled: boolean;
errors: FormErrors<"endDate" | "startDate">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -39,8 +40,8 @@ const VoucherDates = ({
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
error={!!getFieldError(errors, "startDate")}
helperText={getFieldError(errors, "startDate")?.message}
name={"startDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)}
@ -53,8 +54,8 @@ const VoucherDates = ({
/>
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
error={!!getFieldError(errors, "startDate")}
helperText={getFieldError(errors, "startDate")?.message}
name={"startTime" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startHour)}
@ -79,8 +80,8 @@ const VoucherDates = ({
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
error={!!getFieldError(errors, "endDate")}
helperText={getFieldError(errors, "endDate")?.message}
name={"endDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)}
@ -93,8 +94,8 @@ const VoucherDates = ({
/>
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
error={!!getFieldError(errors, "endDate")}
helperText={getFieldError(errors, "endDate")?.message}
name={"endTime" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endHour)}

View file

@ -166,8 +166,8 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.vouchers)}
@ -178,7 +178,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
<VoucherInfo
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
variant="update"
/>
@ -186,7 +186,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
<VoucherTypes
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -195,7 +195,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
variant="update"
/>
@ -337,7 +337,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -345,7 +345,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
@ -353,7 +353,7 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
errors={errors}
onChange={change}
/>
</div>

View file

@ -7,13 +7,14 @@ 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 { FormErrors } from "../../../types";
import { UserError } from "../../../types";
import { FormData } from "../VoucherDetailsPage";
interface VoucherInfoProps {
data: FormData;
errors: FormErrors<"code">;
errors: UserError[];
disabled: boolean;
variant: "create" | "update";
onChange: (event: any) => void;
@ -54,9 +55,9 @@ const VoucherInfo = ({
<CardContent>
<TextField
disabled={variant === "update" || disabled}
error={!!errors.code}
error={!!getFieldError(errors, "code")}
fullWidth
helperText={errors.code}
helperText={getFieldError(errors, "code")?.message}
name={"code" as keyof FormData}
label={intl.formatMessage({
defaultMessage: "Discount Code"

View file

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

View file

@ -8,14 +8,15 @@ 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 { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { FormData } from "../VoucherDetailsPage";
interface VoucherRequirementsProps {
data: FormData;
defaultCurrency: string;
disabled: boolean;
errors: FormErrors<"minSpent" | "minCheckoutItemsQuantity">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -76,8 +77,8 @@ const VoucherRequirements = ({
{data.requirementsPicker === RequirementsPicker.ORDER ? (
<TextField
disabled={disabled}
error={!!errors.minSpent}
helperText={errors.minSpent}
error={!!getFieldError(errors, "minSpent")}
helperText={getFieldError(errors, "minSpent")?.message}
label={minimalOrderValueText}
name={"minSpent" as keyof FormData}
value={data.minSpent}
@ -87,8 +88,10 @@ const VoucherRequirements = ({
) : data.requirementsPicker === RequirementsPicker.ITEM ? (
<TextField
disabled={disabled}
error={!!errors.minCheckoutItemsQuantity}
helperText={errors.minCheckoutItemsQuantity}
error={!!getFieldError(errors, "minCheckoutItemsQuantity")}
helperText={
getFieldError(errors, "minCheckoutItemsQuantity")?.message
}
label={minimalQuantityText}
name={"minCheckoutItemsQuantity" as keyof FormData}
value={data.minCheckoutItemsQuantity}

View file

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

View file

@ -11,7 +11,8 @@ 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 { FormErrors } from "../../../types";
import { getFieldError } from "@saleor/utils/errors";
import { UserError } from "../../../types";
import { DiscountValueTypeEnum } from "../../../types/globalTypes";
import { translateVoucherTypes } from "../../translations";
import { FormData } from "../VoucherDetailsPage";
@ -19,7 +20,7 @@ import { FormData } from "../VoucherDetailsPage";
interface VoucherValueProps {
data: FormData;
defaultCurrency: string;
errors: FormErrors<"discountValue" | "type">;
errors: UserError[];
disabled: boolean;
variant: string;
onChange: (event: React.ChangeEvent<any>) => void;
@ -64,7 +65,7 @@ const VoucherValue: React.FC<VoucherValueProps> = props => {
<CardContent>
<TextFieldWithChoice
disabled={disabled}
error={!!errors.discountValue}
error={!!getFieldError(errors, "discountValue")}
ChoiceProps={{
label:
data.discountType === DiscountValueTypeEnum.FIXED
@ -73,7 +74,7 @@ const VoucherValue: React.FC<VoucherValueProps> = props => {
name: "discountType" as keyof FormData,
values: null
}}
helperText={errors.discountValue}
helperText={getFieldError(errors, "discountValue")?.message}
name={"value" as keyof FormData}
onChange={onChange}
label={intl.formatMessage({
@ -93,8 +94,8 @@ const VoucherValue: React.FC<VoucherValueProps> = props => {
<RadioGroupField
choices={voucherTypeChoices}
disabled={disabled}
error={!!errors.type}
hint={errors.type}
error={!!getFieldError(errors, "type")}
hint={getFieldError(errors, "type")?.message}
label={intl.formatMessage({
defaultMessage: "Voucher Specific Information"
})}

View file

@ -1,7 +1,6 @@
import isEqual from "lodash-es/isEqual";
import { useState } from "react";
import { UserError } from "@saleor/types";
import { toggle } from "@saleor/utils/lists";
import useStateFromProps from "./useStateFromProps";
@ -17,7 +16,6 @@ export type FormChange = (event: ChangeEvent, cb?: () => void) => void;
export interface UseFormResult<T> {
change: FormChange;
data: T;
errors: Record<string, string>;
hasChanged: boolean;
reset: () => void;
set: (data: T) => void;
@ -26,21 +24,6 @@ export interface UseFormResult<T> {
toggleValue: FormChange;
}
function parseErrors(errors: UserError[]): Record<string, string> {
return errors
? errors.reduce(
(acc, curr) =>
curr.field
? {
...acc,
[curr.field.split(":")[0]]: curr.message
}
: acc,
{}
)
: {};
}
type FormData = Record<string, any | any[]>;
function merge<T extends FormData>(prevData: T, prevState: T, data: T): T {
@ -68,7 +51,6 @@ function handleRefresh<T extends FormData>(
function useForm<T extends FormData>(
initial: T,
errors: UserError[],
onSubmit: (data: T) => void
): UseFormResult<T> {
const [hasChanged, setChanged] = useState(false);
@ -135,7 +117,6 @@ function useForm<T extends FormData>(
return {
change,
data,
errors: parseErrors(errors),
hasChanged,
reset,
set,

View file

@ -12,6 +12,8 @@ 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";
export interface MenuCreateDialogFormData {
name: string;
@ -20,6 +22,7 @@ export interface MenuCreateDialogFormData {
export interface MenuCreateDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
disabled: boolean;
errors: UserError[];
open: boolean;
onClose: () => void;
onConfirm: (data: MenuCreateDialogFormData) => void;
@ -32,6 +35,7 @@ const initialForm: MenuCreateDialogFormData = {
const MenuCreateDialog: React.FC<MenuCreateDialogProps> = ({
confirmButtonState,
disabled,
errors,
onClose,
onConfirm,
open
@ -48,14 +52,14 @@ const MenuCreateDialog: React.FC<MenuCreateDialogProps> = ({
/>
</DialogTitle>
<Form initial={initialForm} onSubmit={onConfirm}>
{({ change, data, errors: formErrors, submit }) => (
{({ change, data, submit }) => (
<>
<DialogContent>
<TextField
disabled={disabled}
error={!!formErrors.name}
error={!!getFieldError(errors, "name")}
fullWidth
helperText={formErrors.name}
helperText={getFieldError(errors, "name")?.message}
label={intl.formatMessage({
defaultMessage: "Menu Title",
id: "menuCreateDialogMenuTitleLabel"

View file

@ -253,7 +253,7 @@ const MenuItemDialog: React.FC<MenuItemDialogProps> = ({
}
name="name"
error={!!getFieldError(errors, "name")}
helperText={getFieldError(errors, "name")}
helperText={getFieldError(errors, "name")?.message}
/>
<FormSpacer />
<AutocompleteSelectMenu
@ -269,7 +269,7 @@ const MenuItemDialog: React.FC<MenuItemDialogProps> = ({
loading={loading}
options={options}
error={!!idError}
helperText={idError}
helperText={idError?.message}
placeholder={intl.formatMessage({
defaultMessage: "Start typing to begin search...",
id: "menuItemDialogLinkPlaceholder"

View file

@ -174,6 +174,7 @@ const MenuList: React.FC<MenuListProps> = ({ params }) => {
open={params.action === "add"}
confirmButtonState={menuCreateOpts.status}
disabled={menuCreateOpts.loading}
errors={menuCreateOpts?.data?.menuCreate.errors || []}
onClose={closeModal}
onConfirm={formData =>
menuCreate({

View file

@ -80,8 +80,8 @@ const OrderAddressEditDialog: React.FC<OrderAddressEditDialogProps> = props => {
return (
<Dialog onClose={onClose} open={open} classes={{ paper: classes.overflow }}>
<Form initial={address} errors={dialogErrors} onSubmit={handleSubmit}>
{({ change, data, errors, submit }) => {
<Form initial={address} onSubmit={handleSubmit}>
{({ change, data, submit }) => {
const handleCountrySelect = createSingleAutocompleteSelectHandler(
change,
setCountryDisplayName,
@ -106,7 +106,7 @@ const OrderAddressEditDialog: React.FC<OrderAddressEditDialogProps> = props => {
countries={countryChoices}
countryDisplayValue={countryDisplayName}
data={data}
errors={errors}
errors={dialogErrors}
onChange={change}
onCountryChange={handleCountrySelect}
/>

View file

@ -70,8 +70,8 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
title: maybe(() => page.title, "")
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.pages)}
@ -91,7 +91,7 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
<PageInfo
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
page={page}
onChange={change}
/>
@ -119,13 +119,13 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
<PageSlug
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />
<VisibilityCard
data={data}
errors={formErrors}
errors={errors}
disabled={disabled}
hiddenMessage={intl.formatMessage(
{

View file

@ -9,15 +9,16 @@ import CardTitle from "@saleor/components/CardTitle";
import FormSpacer from "@saleor/components/FormSpacer";
import RichTextEditor from "@saleor/components/RichTextEditor";
import { commonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors";
import { maybe } from "../../../misc";
import { FormErrors } from "../../../types";
import { UserError } from "../../../types";
import { PageDetails_page } from "../../types/PageDetails";
import { FormData } from "../PageDetailsPage";
export interface PageInfoProps {
data: FormData;
disabled: boolean;
errors: FormErrors<"contentJson" | "title">;
errors: UserError[];
page: PageDetails_page;
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -45,9 +46,9 @@ const PageInfo: React.FC<PageInfoProps> = props => {
<CardContent>
<TextField
disabled={disabled}
error={!!errors.title}
error={!!getFieldError(errors, "title")}
fullWidth
helperText={errors.title}
helperText={getFieldError(errors, "title")?.message}
label={intl.formatMessage({
defaultMessage: "Title",
description: "page title"
@ -59,8 +60,8 @@ const PageInfo: React.FC<PageInfoProps> = props => {
<FormSpacer />
<RichTextEditor
disabled={disabled}
error={!!errors.contentJson}
helperText={errors.contentJson}
error={!!getFieldError(errors, "contentJson")}
helperText={getFieldError(errors, "contentJson")?.message}
initial={maybe(() => JSON.parse(page.contentJson))}
label={intl.formatMessage({
defaultMessage: "Content",

View file

@ -6,12 +6,14 @@ import { useIntl } from "react-intl";
import slugify from "slugify";
import CardTitle from "@saleor/components/CardTitle";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { FormData } from "../PageDetailsPage";
export interface PageSlugProps {
data: FormData;
disabled: boolean;
errors: Partial<Record<"slug", string>>;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -34,13 +36,13 @@ const PageSlug: React.FC<PageSlugProps> = ({
<TextField
name={"slug" as keyof FormData}
disabled={disabled}
error={!!errors.slug}
error={!!getFieldError(errors, "slug")}
label={intl.formatMessage({
defaultMessage: "Slug",
description: "page internal name"
})}
helperText={
errors.slug ||
getFieldError(errors, "slug")?.message ||
intl.formatMessage({
defaultMessage:
"If empty, URL will be autogenerated from Page Name"

View file

@ -4,17 +4,18 @@ import makeStyles from "@material-ui/core/styles/makeStyles";
import TextField from "@material-ui/core/TextField";
import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { ConfigurationTypeFieldEnum } from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
import { Plugin_plugin_configuration } from "@saleor/plugins/types/Plugin";
import { getFieldError } from "@saleor/utils/errors";
import { FormData } from "../PluginsDetailsPage";
interface PluginSettingsProps {
data: FormData;
errors: FormErrors<"name" | "configuration">;
errors: UserError[];
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
fields: Plugin_plugin_configuration[];
@ -84,7 +85,7 @@ const PluginSettings: React.FC<PluginSettingsProps> = ({
) : (
<TextField
disabled={disabled}
error={!!errors.name}
error={!!getFieldError(errors, "name")}
helperText={fieldData.helpText}
label={fieldData.label}
name={field.name}

View file

@ -77,8 +77,8 @@ const PluginsDetailsPage: React.FC<PluginsDetailsPageProps> = props => {
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ data, errors, hasChanged, submit, set, triggerChange }) => {
<Form initial={initialForm} onSubmit={onSubmit}>
{({ data, hasChanged, submit, set, triggerChange }) => {
const onChange = (event: ChangeEvent) => {
const { name, value } = event.target;
const newData = {

View file

@ -11,6 +11,8 @@ import Form from "@saleor/components/Form";
import { FormSpacer } from "@saleor/components/FormSpacer";
import ListField from "@saleor/components/ListField";
import { buttonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
export interface FormData {
name: string;
@ -22,10 +24,7 @@ export interface FormData {
export interface ProductTypeAttributeEditDialogProps {
disabled: boolean;
errors: Array<{
field: string;
message: string;
}>;
errors: UserError[];
name: string;
opened: boolean;
title: string;
@ -37,9 +36,16 @@ export interface ProductTypeAttributeEditDialogProps {
onConfirm: (data: FormData) => void;
}
const ProductTypeAttributeEditDialog: React.FC<
ProductTypeAttributeEditDialogProps
> = ({ disabled, errors, name, opened, title, values, onClose, onConfirm }) => {
const ProductTypeAttributeEditDialog: React.FC<ProductTypeAttributeEditDialogProps> = ({
disabled,
errors,
name,
opened,
title,
values,
onClose,
onConfirm
}) => {
const intl = useIntl();
const initialForm: FormData = {
@ -48,19 +54,19 @@ const ProductTypeAttributeEditDialog: React.FC<
};
return (
<Dialog onClose={onClose} open={opened}>
<Form errors={errors} initial={initialForm} onSubmit={onConfirm}>
{({ change, data, errors: formErrors }) => (
<Form initial={initialForm} onSubmit={onConfirm}>
{({ change, data }) => (
<>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<TextField
disabled={disabled}
error={!!formErrors.name}
error={!!getFieldError(errors, "name")}
fullWidth
label={intl.formatMessage({
defaultMessage: "Attribute name"
})}
helperText={formErrors.name}
helperText={getFieldError(errors, "name")?.message}
name="name"
value={data.name}
onChange={change}
@ -70,9 +76,9 @@ const ProductTypeAttributeEditDialog: React.FC<
autoComplete="off"
disabled={disabled}
error={
!!formErrors.values ||
!!formErrors.addValues ||
!!formErrors.removeValues
!!getFieldError(errors, "values") ||
!!getFieldError(errors, "addValues") ||
!!getFieldError(errors, "removeValues")
}
fullWidth
name="values"
@ -80,9 +86,9 @@ const ProductTypeAttributeEditDialog: React.FC<
defaultMessage: "Attribute values"
})}
helperText={
formErrors.values ||
formErrors.addValues ||
formErrors.removeValues
getFieldError(errors, "values") ||
getFieldError(errors, "addValues") ||
getFieldError(errors, "removeValues")
}
values={data.values}
onChange={change}

View file

@ -70,13 +70,8 @@ const ProductTypeCreatePage: React.FC<ProductTypeCreatePageProps> = ({
const [taxTypeDisplayName, setTaxTypeDisplayName] = useStateFromProps("");
return (
<Form
errors={errors}
initial={formInitialData}
onSubmit={onSubmit}
confirmLeave
>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={formInitialData} onSubmit={onSubmit} confirmLeave>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.productTypes)}
@ -87,7 +82,7 @@ const ProductTypeCreatePage: React.FC<ProductTypeCreatePageProps> = ({
<ProductTypeDetails
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />

View file

@ -7,7 +7,8 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
const useStyles = makeStyles(
{
@ -23,7 +24,7 @@ interface ProductTypeDetailsProps {
name: string;
};
disabled: boolean;
errors: FormErrors<"name">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -41,9 +42,9 @@ const ProductTypeDetails: React.FC<ProductTypeDetailsProps> = props => {
<CardContent>
<TextField
disabled={disabled}
error={!!errors.name}
error={!!getFieldError(errors, "name")}
fullWidth
helperText={errors.name}
helperText={getFieldError(errors, "name")?.message}
label={intl.formatMessage({
defaultMessage: "Product Type Name"
})}

View file

@ -123,13 +123,8 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
weight: maybe(() => productType.weight.value)
};
return (
<Form
errors={errors}
initial={formInitialData}
onSubmit={onSubmit}
confirmLeave
>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={formInitialData} onSubmit={onSubmit} confirmLeave>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.productTypes)}
@ -140,7 +135,7 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
<ProductTypeDetails
data={data}
disabled={disabled}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />

View file

@ -90,7 +90,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
disabled,
categories: categoryChoiceList,
collections: collectionChoiceList,
errors: userErrors,
errors,
fetchCategories,
fetchCollections,
fetchMoreCategories,
@ -162,21 +162,8 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
});
return (
<Form
onSubmit={handleSubmit}
errors={userErrors}
initial={initialData}
confirmLeave
>
{({
change,
data,
errors,
hasChanged,
submit,
triggerChange,
toggleValue
}) => {
<Form onSubmit={handleSubmit} initial={initialData} confirmLeave>
{({ change, data, hasChanged, submit, triggerChange, toggleValue }) => {
const handleCollectionSelect = createMultiAutocompleteSelectHandler(
toggleValue,
setSelectedCollections,

View file

@ -9,6 +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";
interface ProductDetailsFormProps {
data: {
@ -16,7 +18,7 @@ interface ProductDetailsFormProps {
name: string;
};
disabled?: boolean;
errors: { [key: string]: string };
errors: UserError[];
// Draftail isn't controlled - it needs only initial input
// because it's autosaving on its own.
// Ref https://github.com/mirumee/saleor/issues/4470
@ -40,8 +42,8 @@ export const ProductDetailsForm: React.FC<ProductDetailsFormProps> = ({
/>
<CardContent>
<TextField
error={!!errors.name}
helperText={errors.name}
error={!!getFieldError(errors, "name")}
helperText={getFieldError(errors, "name")?.message}
disabled={disabled}
fullWidth
label={intl.formatMessage({
@ -55,8 +57,8 @@ export const ProductDetailsForm: React.FC<ProductDetailsFormProps> = ({
<FormSpacer />
<RichTextEditor
disabled={disabled}
error={!!errors.descriptionJson}
helperText={errors.descriptionJson}
error={!!getFieldError(errors, "descriptionJson")}
helperText={getFieldError(errors, "descriptionJson")?.message}
initial={initialDescription}
label={intl.formatMessage(commonMessages.description)}
name="description"

View file

@ -17,7 +17,8 @@ import SingleAutocompleteSelectField, {
} from "@saleor/components/SingleAutocompleteSelectField";
import { ChangeEvent } from "@saleor/hooks/useForm";
import { maybe } from "@saleor/misc";
import { FetchMoreProps, FormErrors } from "@saleor/types";
import { FetchMoreProps, UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
interface ProductType {
hasVariants: boolean;
@ -53,7 +54,7 @@ interface ProductOrganizationProps {
productType?: string;
};
disabled: boolean;
errors: FormErrors<"productType" | "category">;
errors: UserError[];
productType?: ProductType;
productTypeInputDisplayValue?: string;
productTypes?: SingleAutocompleteChoiceType[];
@ -73,7 +74,6 @@ const ProductOrganization: React.FC<ProductOrganizationProps> = props => {
canChangeType,
categories,
categoryInputDisplayValue,
collections,
collectionsInputDisplayValue,
data,
@ -108,8 +108,8 @@ const ProductOrganization: React.FC<ProductOrganizationProps> = props => {
{canChangeType ? (
<SingleAutocompleteSelectField
displayValue={productTypeInputDisplayValue}
error={!!errors.productType}
helperText={errors.productType}
error={!!getFieldError(errors, "productType")}
helperText={getFieldError(errors, "productType")?.message}
name="productType"
disabled={disabled}
label={intl.formatMessage({
@ -154,8 +154,8 @@ const ProductOrganization: React.FC<ProductOrganizationProps> = props => {
<FormSpacer />
<SingleAutocompleteSelectField
displayValue={categoryInputDisplayValue}
error={!!errors.category}
helperText={errors.category}
error={!!getFieldError(errors, "category")}
helperText={getFieldError(errors, "category")?.message}
disabled={disabled}
label={intl.formatMessage({
defaultMessage: "Category"

View file

@ -6,6 +6,8 @@ import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { maybe } from "../../../misc";
import { ProductDetails_product } from "../../types/ProductDetails";
@ -26,7 +28,7 @@ interface ProductStockProps {
stockQuantity: number;
};
disabled: boolean;
errors: { [key: string]: string };
errors: UserError[];
product: ProductDetails_product;
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -56,8 +58,8 @@ const ProductStock: React.FC<ProductStockProps> = props => {
})}
value={data.sku}
onChange={onChange}
error={!!errors.sku}
helperText={errors.sku}
error={!!getFieldError(errors, "sku")}
helperText={getFieldError(errors, "sku")?.message}
/>
<TextField
disabled={disabled}

View file

@ -85,7 +85,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
disabled,
categories: categoryChoiceList,
collections: collectionChoiceList,
errors: userErrors,
errors,
fetchCategories,
fetchCollections,
fetchMoreCategories,
@ -152,21 +152,8 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
});
return (
<Form
onSubmit={handleSubmit}
errors={userErrors}
initial={initialData}
confirmLeave
>
{({
change,
data,
errors,
hasChanged,
submit,
triggerChange,
toggleValue
}) => {
<Form onSubmit={handleSubmit} initial={initialData} confirmLeave>
{({ change, data, hasChanged, submit, triggerChange, toggleValue }) => {
const handleCollectionSelect = createMultiAutocompleteSelectHandler(
toggleValue,
setSelectedCollections,

View file

@ -51,7 +51,7 @@ interface ProductVariantCreatePageProps {
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
currencySymbol,
errors: apiErrors,
errors,
loading,
header,
product,
@ -93,8 +93,8 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
});
return (
<Form initial={initialForm} errors={apiErrors} onSubmit={handleSubmit}>
{({ change, data, errors, hasChanged, submit, triggerChange }) => {
<Form initial={initialForm} onSubmit={handleSubmit}>
{({ change, data, hasChanged, submit, triggerChange }) => {
const handleAttributeChange: FormsetChange = (id, value) => {
changeAttributeData(id, value);
triggerChange();
@ -120,7 +120,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
<ProductVariantAttributes
attributes={attributes}
disabled={loading}
errors={apiErrors}
errors={errors}
onChange={handleAttributeChange}
/>
<CardSpacer />

View file

@ -53,7 +53,7 @@ interface ProductVariantPageProps {
}
const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
errors: apiErrors,
errors,
loading,
header,
placeholderImage,
@ -109,13 +109,8 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
{maybe(() => variant.product.name)}
</AppHeader>
<PageHeader title={header} />
<Form
initial={initialForm}
errors={apiErrors}
onSubmit={handleSubmit}
confirmLeave
>
{({ change, data, errors, hasChanged, submit, triggerChange }) => {
<Form initial={initialForm} onSubmit={handleSubmit} confirmLeave>
{({ change, data, hasChanged, submit, triggerChange }) => {
const handleAttributeChange: FormsetChange = (id, value) => {
changeAttributeData(id, value);
triggerChange();
@ -143,7 +138,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
<ProductVariantAttributes
attributes={attributes}
disabled={loading}
errors={apiErrors}
errors={errors}
onChange={handleAttributeChange}
/>
<CardSpacer />

View file

@ -6,6 +6,8 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import PriceField from "@saleor/components/PriceField";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
const useStyles = makeStyles(
theme => ({
@ -22,7 +24,7 @@ interface ProductVariantPriceProps {
currencySymbol?: string;
priceOverride?: string;
costPrice?: string;
errors: { [key: string]: string };
errors: UserError[];
loading?: boolean;
onChange(event: any);
}
@ -52,19 +54,18 @@ const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
<div className={classes.grid}>
<div>
<PriceField
error={!!errors.price_override}
error={!!getFieldError(errors, "price_override")}
name="priceOverride"
label={intl.formatMessage({
defaultMessage: "Selling price override"
})}
hint={
errors.price_override
? errors.price_override
: intl.formatMessage({
defaultMessage: "Optional",
description: "optional field",
id: "productVariantPriceOptionalPriceOverrideField"
})
getFieldError(errors, "price_override")?.message ||
intl.formatMessage({
defaultMessage: "Optional",
description: "optional field",
id: "productVariantPriceOptionalPriceOverrideField"
})
}
value={priceOverride}
currencySymbol={currencySymbol}
@ -74,19 +75,18 @@ const ProductVariantPrice: React.FC<ProductVariantPriceProps> = props => {
</div>
<div>
<PriceField
error={!!errors.cost_price}
error={!!getFieldError(errors, "cost_price")}
name="costPrice"
label={intl.formatMessage({
defaultMessage: "Cost price override"
})}
hint={
errors.cost_price
? errors.cost_price
: intl.formatMessage({
defaultMessage: "Optional",
description: "optional field",
id: "productVariantPriceOptionalCostPriceField"
})
getFieldError(errors, "cost_price")?.message ||
intl.formatMessage({
defaultMessage: "Optional",
description: "optional field",
id: "productVariantPriceOptionalCostPriceField"
})
}
value={costPrice}
currencySymbol={currencySymbol}

View file

@ -6,6 +6,8 @@ import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
const useStyles = makeStyles(
theme => ({
@ -19,10 +21,7 @@ const useStyles = makeStyles(
);
interface ProductVariantStockProps {
errors: {
quantity?: string;
sku?: string;
};
errors: UserError[];
sku: string;
quantity: string;
stockAllocated?: number;
@ -48,7 +47,7 @@ const ProductVariantStock: React.FC<ProductVariantStockProps> = props => {
<div className={classes.grid}>
<div>
<TextField
error={!!errors.quantity}
error={!!getFieldError(errors, "quantity")}
name="quantity"
value={quantity}
label={intl.formatMessage({
@ -56,8 +55,8 @@ const ProductVariantStock: React.FC<ProductVariantStockProps> = props => {
description: "product variant stock"
})}
helperText={
errors.quantity
? errors.quantity
getFieldError(errors, "quantity")
? getFieldError(errors, "quantity")
: !!stockAllocated
? intl.formatMessage(
{
@ -77,8 +76,8 @@ const ProductVariantStock: React.FC<ProductVariantStockProps> = props => {
</div>
<div>
<TextField
error={!!errors.sku}
helperText={errors.sku}
error={!!getFieldError(errors, "sku")}
helperText={getFieldError(errors, "sku")?.message}
name="sku"
value={sku}
label={intl.formatMessage({

View file

@ -35,7 +35,7 @@ export interface ServiceCreatePageProps {
const ServiceCreatePage: React.FC<ServiceCreatePageProps> = props => {
const {
disabled,
errors: formErrors,
errors,
permissions,
saveButtonBarState,
onBack,
@ -50,13 +50,8 @@ const ServiceCreatePage: React.FC<ServiceCreatePageProps> = props => {
permissions: []
};
return (
<Form
errors={formErrors}
initial={initialForm}
onSubmit={onSubmit}
confirmLeave
>
{({ data, change, errors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit} confirmLeave>
{({ data, change, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.serviceAccounts)}

View file

@ -48,7 +48,7 @@ const ServiceDetailsPage: React.FC<ServiceDetailsPageProps> = props => {
const {
apiUri,
disabled,
errors: formErrors,
errors,
permissions,
saveButtonBarState,
service,
@ -79,13 +79,8 @@ const ServiceDetailsPage: React.FC<ServiceDetailsPageProps> = props => {
permissions: maybe(() => service.permissions, []).map(perm => perm.code)
};
return (
<Form
errors={formErrors}
initial={initialForm}
onSubmit={onSubmit}
confirmLeave
>
{({ data, change, errors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit} confirmLeave>
{({ data, change, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.serviceAccounts)}

View file

@ -6,14 +6,15 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { FormChange } from "@saleor/hooks/useForm";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
export interface ServiceInfoProps {
data: {
name: string;
};
disabled: boolean;
errors: FormErrors<"name">;
errors: UserError[];
onChange: FormChange;
}
@ -32,12 +33,12 @@ const ServiceInfo: React.FC<ServiceInfoProps> = props => {
<CardContent>
<TextField
disabled={disabled}
error={!!errors.name}
error={!!getFieldError(errors, "name")}
label={intl.formatMessage({
defaultMessage: "Account Name",
description: "service account"
})}
helperText={errors.name}
helperText={getFieldError(errors, "name")?.message}
fullWidth
name="name"
value={data.name}

View file

@ -50,8 +50,8 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<>
<Container>
<AppHeader onBack={onBack}>
@ -67,7 +67,7 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
<div>
<ShippingZoneInfo
data={data}
errors={formErrors}
errors={errors}
onChange={change}
/>
<CardSpacer />

View file

@ -57,11 +57,11 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
const intl = useIntl();
const initialForm: FormData = {
name: maybe(() => shippingZone.name, "")
name: shippingZone?.name || ""
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged, submit }) => (
<Container>
<AppHeader onBack={onBack}>
<FormattedMessage defaultMessage="Shipping" />
@ -69,11 +69,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
<PageHeader title={maybe(() => shippingZone.name)} />
<Grid>
<div>
<ShippingZoneInfo
data={data}
errors={formErrors}
onChange={change}
/>
<ShippingZoneInfo data={data} errors={errors} onChange={change} />
<CardSpacer />
<CountryList
countries={maybe(() => shippingZone.countries)}

View file

@ -6,12 +6,13 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
import { getFieldError } from "@saleor/utils/errors";
import { UserError } from "../../../types";
import { FormData } from "../ShippingZoneDetailsPage";
export interface ShippingZoneInfoProps {
data: FormData;
errors: FormErrors<"name">;
errors: UserError[];
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -29,9 +30,9 @@ const ShippingZoneInfo: React.FC<ShippingZoneInfoProps> = ({
/>
<CardContent>
<TextField
error={!!errors.name}
error={!!getFieldError(errors, "name")}
fullWidth
helperText={errors.name}
helperText={getFieldError(errors, "name")?.message}
label={intl.formatMessage({
defaultMessage: "Shipping Zone Name"
})}

View file

@ -18,8 +18,9 @@ import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr";
import Skeleton from "@saleor/components/Skeleton";
import { buttonMessages } from "@saleor/intl";
import { getFieldError } from "@saleor/utils/errors";
import { maybe } from "../../../misc";
import { FormErrors, UserError } from "../../../types";
import { UserError } from "../../../types";
import { ShippingMethodTypeEnum } from "../../../types/globalTypes";
import { ShippingZoneDetailsFragment_shippingMethods } from "../../types/ShippingZoneDetailsFragment";
@ -109,243 +110,233 @@ const ShippingZoneRateDialog: React.FC<ShippingZoneRateDialogProps> = props => {
return (
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors: formErrors, hasChanged }) => {
const typedFormErrors: FormErrors<
| "minimumOrderPrice"
| "minimumOrderWeight"
| "maximumOrderPrice"
| "maximumOrderWeight"
| "price"
| "name"
> = formErrors;
return (
<>
<DialogTitle>
{variant === ShippingMethodTypeEnum.PRICE
? action === "create"
? intl.formatMessage({
defaultMessage: "Add Price Rate",
description: "dialog header"
})
: intl.formatMessage({
defaultMessage: "Edit Price Rate",
description: "dialog header"
})
: action === "create"
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged }) => (
<>
<DialogTitle>
{variant === ShippingMethodTypeEnum.PRICE
? action === "create"
? intl.formatMessage({
defaultMessage: "Add Weight Rate",
description:
"add weight based shipping method, dialog header"
defaultMessage: "Add Price Rate",
description: "dialog header"
})
: intl.formatMessage({
defaultMessage: "Edit Weight Rate",
description:
"edit weight based shipping method, dialog header"
})}
</DialogTitle>
<DialogContent>
<TextField
disabled={disabled}
error={!!typedFormErrors.name}
fullWidth
helperText={
typedFormErrors.name ||
intl.formatMessage({
defaultMessage:
"This will be shown to customers at checkout"
defaultMessage: "Edit Price Rate",
description: "dialog header"
})
}
label={intl.formatMessage({
defaultMessage: "Rate Name",
description: "shipping method name"
: action === "create"
? intl.formatMessage({
defaultMessage: "Add Weight Rate",
description:
"add weight based shipping method, dialog header"
})
: intl.formatMessage({
defaultMessage: "Edit Weight Rate",
description:
"edit weight based shipping method, dialog header"
})}
name={"name" as keyof FormData}
value={data.name}
onChange={change}
/>
</DialogContent>
<Hr />
<DialogContent>
{!!variant ? (
<>
<Typography
className={classes.subheading}
variant="subtitle1"
>
{variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage: "Value range",
description: "order price range"
})
: intl.formatMessage({
defaultMessage: "Weight range",
description: "order weight range"
})}
</Typography>
<ControlledCheckbox
name={"noLimits" as keyof FormData}
label={
<>
<FormattedMessage
defaultMessage="There are no value limits"
description="shipping method has no value limits"
/>
<Typography variant="caption">
{variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage:
"This rate will apply to all orders of all prices"
})
: intl.formatMessage({
defaultMessage:
"This rate will apply to all orders of all weights"
})}
</Typography>
</>
}
checked={data.noLimits}
onChange={change}
disabled={disabled}
/>
{!data.noLimits && (
<>
<FormSpacer />
<div className={classes.grid}>
<TextField
disabled={disabled}
error={
variant === ShippingMethodTypeEnum.PRICE
? !!typedFormErrors.minimumOrderPrice
: !!typedFormErrors.minimumOrderWeight
}
fullWidth
helperText={
variant === ShippingMethodTypeEnum.PRICE
? typedFormErrors.minimumOrderPrice
: typedFormErrors.minimumOrderWeight
}
label={
variant === ShippingMethodTypeEnum.PRICE
? typedFormErrors.minimumOrderPrice ||
intl.formatMessage({
defaultMessage: "Minimal Order Value"
})
: typedFormErrors.minimumOrderWeight ||
intl.formatMessage({
defaultMessage: "Minimal Order Weight"
})
}
name={"minValue" as keyof FormData}
type="number"
value={data.minValue}
onChange={change}
/>
<TextField
disabled={disabled}
error={
variant === ShippingMethodTypeEnum.PRICE
? !!typedFormErrors.maximumOrderPrice
: !!typedFormErrors.maximumOrderWeight
}
fullWidth
helperText={
variant === ShippingMethodTypeEnum.PRICE
? typedFormErrors.maximumOrderPrice
: typedFormErrors.maximumOrderWeight
}
label={
variant === ShippingMethodTypeEnum.PRICE
? typedFormErrors.maximumOrderPrice ||
intl.formatMessage({
defaultMessage: "Maximal Order Value"
})
: typedFormErrors.maximumOrderWeight ||
intl.formatMessage({
defaultMessage: "Maximal Order Weight"
})
}
name={"maxValue" as keyof FormData}
type="number"
value={data.maxValue}
onChange={change}
/>
</div>
</>
)}
</>
) : (
<Skeleton />
)}
</DialogContent>
<Hr />
<DialogContent>
<Typography className={classes.subheading} variant="subtitle1">
<FormattedMessage
defaultMessage="Rate"
description="shipping method"
/>
</Typography>
<ControlledCheckbox
name={"isFree" as keyof FormData}
label={intl.formatMessage({
defaultMessage: "This is free shipping",
description: "shipping method, switch button"
})}
checked={data.isFree}
onChange={change}
disabled={disabled}
/>
{!data.isFree && (
<>
<FormSpacer />
<div className={classes.grid}>
<TextField
disabled={disabled}
error={!!typedFormErrors.price}
fullWidth
helperText={typedFormErrors.price}
label={intl.formatMessage({
defaultMessage: "Rate Price",
description: "shipping method price"
</DialogTitle>
<DialogContent>
<TextField
disabled={disabled}
error={!!getFieldError(errors, "name")}
fullWidth
helperText={
getFieldError(errors, "name") ||
intl.formatMessage({
defaultMessage:
"This will be shown to customers at checkout"
})
}
label={intl.formatMessage({
defaultMessage: "Rate Name",
description: "shipping method name"
})}
name={"name" as keyof FormData}
value={data.name}
onChange={change}
/>
</DialogContent>
<Hr />
<DialogContent>
{!!variant ? (
<>
<Typography
className={classes.subheading}
variant="subtitle1"
>
{variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage: "Value range",
description: "order price range"
})
: intl.formatMessage({
defaultMessage: "Weight range",
description: "order weight range"
})}
name={"price" as keyof FormData}
type="number"
value={data.price}
onChange={change}
InputProps={{
endAdornment: defaultCurrency
}}
/>
</div>
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
disabled={disabled || !hasChanged}
transitionState={confirmButtonState}
color="primary"
variant="contained"
type="submit"
>
{action === "create"
? intl.formatMessage({
defaultMessage: "Create rate",
description: "button"
})
: intl.formatMessage({
defaultMessage: "Update rate",
description: "button"
</Typography>
<ControlledCheckbox
name={"noLimits" as keyof FormData}
label={
<>
<FormattedMessage
defaultMessage="There are no value limits"
description="shipping method has no value limits"
/>
<Typography variant="caption">
{variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage:
"This rate will apply to all orders of all prices"
})
: intl.formatMessage({
defaultMessage:
"This rate will apply to all orders of all weights"
})}
</Typography>
</>
}
checked={data.noLimits}
onChange={change}
disabled={disabled}
/>
{!data.noLimits && (
<>
<FormSpacer />
<div className={classes.grid}>
<TextField
disabled={disabled}
error={
variant === ShippingMethodTypeEnum.PRICE
? !!getFieldError(errors, "minimumOrderPrice")
: !!getFieldError(errors, "minimumOrderWeight")
}
fullWidth
helperText={
variant === ShippingMethodTypeEnum.PRICE
? getFieldError(errors, "minimumOrderPrice")
: getFieldError(errors, "minimumOrderWeight")
}
label={
variant === ShippingMethodTypeEnum.PRICE
? getFieldError(errors, "minimumOrderPrice") ||
intl.formatMessage({
defaultMessage: "Minimal Order Value"
})
: getFieldError(errors, "minimumOrderWeight") ||
intl.formatMessage({
defaultMessage: "Minimal Order Weight"
})
}
name={"minValue" as keyof FormData}
type="number"
value={data.minValue}
onChange={change}
/>
<TextField
disabled={disabled}
error={
variant === ShippingMethodTypeEnum.PRICE
? !!getFieldError(errors, "maximumOrderPrice")
: !!getFieldError(errors, "maximumOrderWeight")
}
fullWidth
helperText={
variant === ShippingMethodTypeEnum.PRICE
? getFieldError(errors, "maximumOrderPrice")
: getFieldError(errors, "maximumOrderWeight")
}
label={
variant === ShippingMethodTypeEnum.PRICE
? getFieldError(errors, "maximumOrderPrice") ||
intl.formatMessage({
defaultMessage: "Maximal Order Value"
})
: getFieldError(errors, "maximumOrderWeight") ||
intl.formatMessage({
defaultMessage: "Maximal Order Weight"
})
}
name={"maxValue" as keyof FormData}
type="number"
value={data.maxValue}
onChange={change}
/>
</div>
</>
)}
</>
) : (
<Skeleton />
)}
</DialogContent>
<Hr />
<DialogContent>
<Typography className={classes.subheading} variant="subtitle1">
<FormattedMessage
defaultMessage="Rate"
description="shipping method"
/>
</Typography>
<ControlledCheckbox
name={"isFree" as keyof FormData}
label={intl.formatMessage({
defaultMessage: "This is free shipping",
description: "shipping method, switch button"
})}
checked={data.isFree}
onChange={change}
disabled={disabled}
/>
{!data.isFree && (
<>
<FormSpacer />
<div className={classes.grid}>
<TextField
disabled={disabled}
error={!!getFieldError(errors, "price")}
fullWidth
helperText={getFieldError(errors, "price")?.message}
label={intl.formatMessage({
defaultMessage: "Rate Price",
description: "shipping method price"
})}
</ConfirmButton>
</DialogActions>
</>
);
}}
name={"price" as keyof FormData}
type="number"
value={data.price}
onChange={change}
InputProps={{
endAdornment: defaultCurrency
}}
/>
</div>
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
disabled={disabled || !hasChanged}
transitionState={confirmButtonState}
color="primary"
variant="contained"
type="submit"
>
{action === "create"
? intl.formatMessage({
defaultMessage: "Create rate",
description: "button"
})
: intl.formatMessage({
defaultMessage: "Update rate",
description: "button"
})}
</ConfirmButton>
</DialogActions>
</>
)}
</Form>
</Dialog>
);

View file

@ -11,16 +11,16 @@ import Grid from "@saleor/components/Grid";
import SingleAutocompleteSelectField, {
SingleAutocompleteChoiceType
} from "@saleor/components/SingleAutocompleteSelectField";
import { AddressTypeInput } from "@saleor/customers/types";
import { ChangeEvent } from "@saleor/hooks/useForm";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { SiteSettingsPageFormData } from "../SiteSettingsPage";
interface SiteSettingsAddressProps {
countries: SingleAutocompleteChoiceType[];
data: SiteSettingsPageFormData;
displayCountry: string;
errors: FormErrors<keyof AddressTypeInput>;
errors: UserError[];
disabled: boolean;
onChange: (event: ChangeEvent) => void;
onCountryChange: (event: ChangeEvent) => void;
@ -60,8 +60,8 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
<CardContent>
<TextField
disabled={disabled}
error={!!errors.companyName}
helperText={errors.companyName}
error={!!getFieldError(errors, "companyName")}
helperText={getFieldError(errors, "companyName")?.message}
label={intl.formatMessage({
defaultMessage: "Company"
})}
@ -73,8 +73,8 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.streetAddress1}
helperText={errors.streetAddress1}
error={!!getFieldError(errors, "streetAddress1")}
helperText={getFieldError(errors, "streetAddress1")?.message}
label={intl.formatMessage({
defaultMessage: "Address line 1"
})}
@ -86,8 +86,8 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.streetAddress2}
helperText={errors.streetAddress2}
error={!!getFieldError(errors, "streetAddress2")}
helperText={getFieldError(errors, "streetAddress2")?.message}
label={intl.formatMessage({
defaultMessage: "Address line 2"
})}
@ -100,8 +100,8 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
<Grid>
<TextField
disabled={disabled}
error={!!errors.city}
helperText={errors.city}
error={!!getFieldError(errors, "city")}
helperText={getFieldError(errors, "city")?.message}
label={intl.formatMessage({
defaultMessage: "City"
})}
@ -112,8 +112,8 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
/>
<TextField
disabled={disabled}
error={!!errors.postalCode}
helperText={errors.postalCode}
error={!!getFieldError(errors, "postalCode")}
helperText={getFieldError(errors, "postalCode")?.message}
label={intl.formatMessage({
defaultMessage: "ZIP / Postal code"
})}
@ -128,8 +128,8 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
<SingleAutocompleteSelectField
disabled={disabled}
displayValue={displayCountry}
error={!!errors.country}
helperText={errors.country}
error={!!getFieldError(errors, "country")}
helperText={getFieldError(errors, "country")?.message}
label={intl.formatMessage({
defaultMessage: "Country"
})}
@ -143,8 +143,8 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
/>
<TextField
disabled={disabled}
error={!!errors.countryArea}
helperText={errors.countryArea}
error={!!getFieldError(errors, "companyArea")}
helperText={getFieldError(errors, "companyArea")?.message}
label={intl.formatMessage({
defaultMessage: "Country area"
})}
@ -157,9 +157,9 @@ const SiteSettingsAddress: React.FC<SiteSettingsAddressProps> = props => {
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.phone}
error={!!getFieldError(errors, "phone")}
fullWidth
helperText={errors.phone}
helperText={getFieldError(errors, "phone")?.message}
label={intl.formatMessage({
defaultMessage: "Phone"
})}

View file

@ -7,15 +7,13 @@ import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import FormSpacer from "@saleor/components/FormSpacer";
import { commonMessages } from "@saleor/intl";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { SiteSettingsPageFormData } from "../SiteSettingsPage";
interface SiteSettingsDetailsProps {
data: SiteSettingsPageFormData;
errors: Partial<{
description: string;
domain: string;
name: string;
}>;
errors: UserError[];
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -36,14 +34,14 @@ const SiteSettingsDetails: React.FC<SiteSettingsDetailsProps> = ({
<CardContent>
<TextField
disabled={disabled}
error={!!errors.name}
error={!!getFieldError(errors, "name")}
fullWidth
name="name"
label={intl.formatMessage({
defaultMessage: "Name of your store"
})}
helperText={
errors.name ||
getFieldError(errors, "name")?.message ||
intl.formatMessage({
defaultMessage:
"Name of your store is shown on tab in web browser"
@ -55,27 +53,27 @@ const SiteSettingsDetails: React.FC<SiteSettingsDetailsProps> = ({
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.domain}
error={!!getFieldError(errors, "domain")}
fullWidth
name="domain"
label={intl.formatMessage({
defaultMessage: "URL of your online store"
})}
helperText={errors.domain}
helperText={getFieldError(errors, "domain")?.message}
value={data.domain}
onChange={onChange}
/>
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.domain}
error={!!getFieldError(errors, "description")}
fullWidth
name="description"
label={intl.formatMessage({
defaultMessage: "Store Description"
})}
helperText={
errors.description ||
getFieldError(errors, "description")?.message ||
intl.formatMessage({
defaultMessage:
"Store description is shown on taskbar after your store name"

View file

@ -7,10 +7,12 @@ import TextField from "@material-ui/core/TextField";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Form, { FormProps } from "@saleor/components/Form";
import Form from "@saleor/components/Form";
import { FormSpacer } from "@saleor/components/FormSpacer";
import SingleSelectField from "@saleor/components/SingleSelectField";
import { buttonMessages } from "@saleor/intl";
import { UserError, DialogProps } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
import { authorizationKeyTypes } from "../../../misc";
import { AuthorizationKeyType } from "../../../types/globalTypes";
@ -20,13 +22,10 @@ export interface SiteSettingsKeyDialogForm {
type: AuthorizationKeyType;
}
export interface SiteSettingsKeyDialogProps
extends Pick<
FormProps<SiteSettingsKeyDialogForm>,
Exclude<keyof FormProps<SiteSettingsKeyDialogForm>, "children">
> {
open: boolean;
onClose: () => void;
export interface SiteSettingsKeyDialogProps extends DialogProps {
errors: UserError[];
initial: SiteSettingsKeyDialogForm;
onSubmit: (data: SiteSettingsKeyDialogForm) => void;
}
const SiteSettingsKeyDialog: React.FC<SiteSettingsKeyDialogProps> = ({
@ -40,8 +39,8 @@ const SiteSettingsKeyDialog: React.FC<SiteSettingsKeyDialogProps> = ({
return (
<Dialog onClose={onClose} maxWidth="xs" open={open}>
<Form initial={initial} onSubmit={onSubmit} errors={errors}>
{({ change, data, errors }) => (
<Form initial={initial} onSubmit={onSubmit}>
{({ change, data }) => (
<>
<DialogTitle>
<FormattedMessage
@ -55,37 +54,37 @@ const SiteSettingsKeyDialog: React.FC<SiteSettingsKeyDialogProps> = ({
label: authorizationKeyTypes[key],
value: key
}))}
error={!!errors.keyType}
error={!!getFieldError(errors, "keyType")}
label={intl.formatMessage({
defaultMessage: "Authentication type",
description: "authentication provider name"
})}
hint={errors.keyType}
hint={getFieldError(errors, "keyType")?.message}
name="type"
onChange={change}
value={data.type}
/>
<FormSpacer />
<TextField
error={!!errors.key}
error={!!getFieldError(errors, "key")}
fullWidth
label={intl.formatMessage({
defaultMessage: "Key",
description: "authentication provider API key"
})}
helperText={errors.key}
helperText={getFieldError(errors, "key")?.message}
name="key"
onChange={change}
value={data.key}
/>
<FormSpacer />
<TextField
error={!!errors.password}
error={!!getFieldError(errors, "password")}
fullWidth
label={intl.formatMessage({
defaultMessage: "Password"
})}
helperText={errors.password}
helperText={getFieldError(errors, "password")?.message}
name="password"
onChange={change}
value={data.password}

View file

@ -9,7 +9,8 @@ import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr";
import { FormErrors } from "@saleor/types";
import { UserError } from "@saleor/types";
import { getFieldError } from "@saleor/utils/errors";
export interface SiteSettingsMailingFormData {
defaultMailSenderName: string;
@ -18,11 +19,7 @@ export interface SiteSettingsMailingFormData {
}
interface SiteSettingsMailingProps {
data: SiteSettingsMailingFormData;
errors: FormErrors<
| "defaultMailSenderAddress"
| "defaultMailSenderName"
| "customerSetPasswordUrl"
>;
errors: UserError[];
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -68,27 +65,29 @@ const SiteSettingsMailing: React.FC<SiteSettingsMailingProps> = props => {
</Typography>
<TextField
disabled={disabled}
error={!!errors.defaultMailSenderAddress}
error={!!getFieldError(errors, "defaultMailSenderAddress")}
fullWidth
name="defaultMailSenderAddress"
label={intl.formatMessage({
defaultMessage: "Mailing email address"
})}
helperText={errors.defaultMailSenderAddress}
helperText={
getFieldError(errors, "defaultMailSenderAddress")?.message
}
value={data.defaultMailSenderAddress}
onChange={onChange}
/>
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.defaultMailSenderName}
error={!!getFieldError(errors, "defaultMailSenderName")}
fullWidth
name="defaultMailSenderName"
label={intl.formatMessage({
defaultMessage: "Mailing email sender"
})}
helperText={
errors.defaultMailSenderName ||
getFieldError(errors, "defaultMailSenderName")?.message ||
intl.formatMessage({
defaultMessage: 'This will be visible as "from" name',
description: "email sender"
@ -102,7 +101,7 @@ const SiteSettingsMailing: React.FC<SiteSettingsMailingProps> = props => {
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.customerSetPasswordUrl}
error={!!getFieldError(errors, "customerSetPasswordUrl")}
fullWidth
name="customerSetPasswordUrl"
label={intl.formatMessage({
@ -112,7 +111,7 @@ const SiteSettingsMailing: React.FC<SiteSettingsMailingProps> = props => {
defaultMessage: "URL address"
})}
helperText={
errors.customerSetPasswordUrl ||
getFieldError(errors, "customerSetPasswordUrl")?.message ||
intl.formatMessage({
defaultMessage:
"This URL will be used as a main URL for password resets. It will be sent via email."

View file

@ -127,9 +127,10 @@ const SiteSettingsPage: React.FC<SiteSettingsPageProps> = props => {
name: maybe(() => shop.name, "")
};
const formErrors = [...errors, ...validationErrors];
return (
<Form
errors={[...errors, ...validationErrors]}
initial={initialForm}
onSubmit={data => {
const submitFunc = areAddressInputFieldsModified(data)
@ -139,8 +140,7 @@ const SiteSettingsPage: React.FC<SiteSettingsPageProps> = props => {
}}
confirmLeave
>
{({ change, data, errors: formErrors, hasChanged, submit }) => {
const siteFormErrors = { ...formErrors };
{({ change, data, hasChanged, submit }) => {
const countryChoices = mapCountriesToChoices(
maybe(() => shop.countries, [])
);
@ -169,7 +169,7 @@ const SiteSettingsPage: React.FC<SiteSettingsPageProps> = props => {
</div>
<SiteSettingsDetails
data={data}
errors={siteFormErrors}
errors={formErrors}
disabled={disabled}
onChange={change}
/>
@ -187,7 +187,7 @@ const SiteSettingsPage: React.FC<SiteSettingsPageProps> = props => {
</div>
<SiteSettingsMailing
data={data}
errors={siteFormErrors}
errors={formErrors}
disabled={disabled}
onChange={change}
/>
@ -208,7 +208,7 @@ const SiteSettingsPage: React.FC<SiteSettingsPageProps> = props => {
data={data}
displayCountry={displayCountry}
countries={countryChoices}
errors={siteFormErrors}
errors={formErrors}
disabled={disabled}
onChange={change}
onCountryChange={handleCountryChange}

View file

@ -16,6 +16,8 @@ import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Form from "@saleor/components/Form";
import FormSpacer from "@saleor/components/FormSpacer";
import { buttonMessages, commonMessages } from "@saleor/intl";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import { getFieldError } from "@saleor/utils/errors";
import { UserError } from "../../../types";
export interface FormData {
@ -65,13 +67,14 @@ interface StaffAddMemberDialogProps {
const StaffAddMemberDialog: React.FC<StaffAddMemberDialogProps> = props => {
const { confirmButtonState, errors, open, onClose, onConfirm } = props;
const classes = useStyles(props);
const dialogErrors = useModalDialogErrors(errors, open);
const intl = useIntl();
return (
<Dialog onClose={onClose} open={open}>
<Form errors={errors} initial={initialForm} onSubmit={onConfirm}>
{({ change, data, errors: formErrors, hasChanged }) => (
<Form initial={initialForm} onSubmit={onConfirm}>
{({ change, data, hasChanged }) => (
<>
<DialogTitle>
<FormattedMessage
@ -82,8 +85,8 @@ const StaffAddMemberDialog: React.FC<StaffAddMemberDialogProps> = props => {
<DialogContent>
<div className={classes.textFieldGrid}>
<TextField
error={!!formErrors.firstName}
helperText={formErrors.firstName}
error={!!getFieldError(dialogErrors, "firstName")}
helperText={getFieldError(dialogErrors, "firstName")?.message}
label={intl.formatMessage(commonMessages.firstName)}
name="firstName"
type="text"
@ -91,8 +94,8 @@ const StaffAddMemberDialog: React.FC<StaffAddMemberDialogProps> = props => {
onChange={change}
/>
<TextField
error={!!formErrors.lastName}
helperText={formErrors.lastName}
error={!!getFieldError(dialogErrors, "lastName")}
helperText={getFieldError(dialogErrors, "lastName")?.message}
label={intl.formatMessage(commonMessages.lastName)}
name="lastName"
type="text"
@ -102,9 +105,9 @@ const StaffAddMemberDialog: React.FC<StaffAddMemberDialogProps> = props => {
</div>
<FormSpacer />
<TextField
error={!!formErrors.email}
error={!!getFieldError(dialogErrors, "email")}
fullWidth
helperText={formErrors.email}
helperText={getFieldError(dialogErrors, "email")?.message}
label={intl.formatMessage(commonMessages.email)}
name="email"
type="email"

View file

@ -15,6 +15,7 @@ import ConfirmButton, {
} from "@saleor/components/ConfirmButton";
import FormSpacer from "@saleor/components/FormSpacer";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import { getFieldError } from "@saleor/utils/errors";
interface StaffPasswordResetDialogFormData {
newPassword: string;
@ -33,13 +34,13 @@ const initialForm: StaffPasswordResetDialogFormData = {
const StaffPasswordResetDialog: React.FC<StaffPasswordResetDialogProps> = ({
confirmButtonState,
errors: apiErrors,
errors,
open,
onClose,
onSubmit
}) => {
const intl = useIntl();
const dialogErrors = useModalDialogErrors(apiErrors, open);
const dialogErrors = useModalDialogErrors(errors, open);
return (
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
@ -49,14 +50,14 @@ const StaffPasswordResetDialog: React.FC<StaffPasswordResetDialogProps> = ({
description="dialog header"
/>
</DialogTitle>
<Form errors={dialogErrors} initial={initialForm} onSubmit={onSubmit}>
{({ change, data, errors, submit }) => (
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, submit }) => (
<>
<DialogContent>
<TextField
error={!!errors.oldPassword}
error={!!getFieldError(dialogErrors, "oldPassword")}
fullWidth
helperText={errors.oldPassword}
helperText={getFieldError(dialogErrors, "oldPassword")?.message}
label={intl.formatMessage({
defaultMessage: "Previous Password",
description: "input label"
@ -67,10 +68,10 @@ const StaffPasswordResetDialog: React.FC<StaffPasswordResetDialogProps> = ({
/>
<FormSpacer />
<TextField
error={!!errors.newPassword}
error={!!getFieldError(dialogErrors, "newPassword")}
fullWidth
helperText={
errors.newPassword ||
getFieldError(dialogErrors, "newPassword") ||
intl.formatMessage({
defaultMessage:
"New password must be at least 8 characters long"

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@ import React from "react";
import placeholderCollectionImage from "@assets/images/block1.jpg";
import placeholderProductImage from "@assets/images/placeholder60x60.png";
import { formError } from "@saleor/storybook/misc";
import CollectionDetailsPage, {
CollectionDetailsPageProps
} from "../../../collections/components/CollectionDetailsPage";
@ -21,6 +22,7 @@ const props: Omit<CollectionDetailsPageProps, "classes"> = {
...pageListProps.default,
collection,
disabled: false,
errors: [],
isFeatured: true,
onBack: () => undefined,
onCollectionRemove: () => undefined,
@ -37,6 +39,14 @@ storiesOf("Views / Collections / Collection details", module)
.add("loading", () => (
<CollectionDetailsPage {...props} collection={undefined} disabled={true} />
))
.add("form errors", () => (
<CollectionDetailsPage
{...props}
errors={["name", "descriptionJson", "publicationDate", "isPublished"].map(
formError
)}
/>
))
.add("no products", () => (
<CollectionDetailsPage
{...props}

View file

@ -20,7 +20,7 @@ storiesOf("Generics / AddressEdit", module)
>
<CardContent>
<AddressEdit
errors={{}}
errors={[]}
data={transformAddressToForm(customer.defaultBillingAddress)}
countries={countries.map(c => ({
label: c.label,

View file

@ -1,6 +1,7 @@
import { storiesOf } from "@storybook/react";
import React from "react";
import { formError } from "@saleor/storybook/misc";
import MenuCreateDialog, {
MenuCreateDialogProps
} from "../../../navigation/components/MenuCreateDialog";
@ -9,6 +10,7 @@ import Decorator from "../../Decorator";
const props: MenuCreateDialogProps = {
confirmButtonState: "default",
disabled: false,
errors: [],
onClose: () => undefined,
onConfirm: () => undefined,
open: true
@ -19,4 +21,7 @@ storiesOf("Navigation / Menu create", module)
.add("default", () => <MenuCreateDialog {...props} />)
.add("loading", () => (
<MenuCreateDialog {...props} disabled={true} confirmButtonState="loading" />
))
.add("form errors", () => (
<MenuCreateDialog {...props} errors={["name"].map(formError)} />
));

View file

@ -1,6 +1,7 @@
import { storiesOf } from "@storybook/react";
import React from "react";
import { formError } from "@saleor/storybook/misc";
import ProductTypeAttributeEditDialog, {
ProductTypeAttributeEditDialogProps
} from "../../../productTypes/components/ProductTypeAttributeEditDialog";
@ -32,10 +33,6 @@ storiesOf("Product types / Edit attribute", module)
.add("form errors", () => (
<ProductTypeAttributeEditDialog
{...props}
// errors={["name", "values"].map(field => formError(field))}
errors={["name", "values"].map(field => ({
field,
message: "Generic error"
}))}
errors={["name", "values"].map(field => formError(field))}
/>
));

View file

@ -16,6 +16,7 @@ const props: SiteSettingsKeyDialogProps = {
type: AuthorizationKeyType.FACEBOOK
},
onClose: () => undefined,
onSubmit: () => undefined,
open: true
};

View file

@ -6,7 +6,7 @@ import { IFilter } from "./components/Filter";
import { MultiAutocompleteChoiceType } from "./components/MultiAutocompleteSelectField";
export interface UserError {
field: string;
field: string | null;
message: string;
}
@ -116,8 +116,6 @@ export interface Node {
id: string;
}
export type FormErrors<TKeys extends string> = Partial<Record<TKeys, string>>;
export type Pagination = Partial<{
after: string;
before: string;

View file

@ -1,10 +1,7 @@
import { maybe } from "@saleor/misc";
import { UserError } from "@saleor/types";
export function getFieldError(errors: UserError[], field: string): string {
const err = errors.find(err => err.field === field);
return maybe(() => err.message);
export function getFieldError(errors: UserError[], field: string): UserError {
return errors.find(err => err.field === field);
}
export function getErrors(errors: UserError[]): string[] {

View file

@ -40,7 +40,7 @@ export interface WebhookCreatePageProps {
const WebhookCreatePage: React.FC<WebhookCreatePageProps> = ({
disabled,
errors: apiErrors,
errors,
saveButtonBarState,
services,
fetchServiceAccounts,
@ -70,8 +70,8 @@ const WebhookCreatePage: React.FC<WebhookCreatePageProps> = ({
);
return (
<Form errors={apiErrors} initial={initialForm} onSubmit={onSubmit}>
{({ data, errors, hasChanged, submit, change }) => {
<Form initial={initialForm} onSubmit={onSubmit}>
{({ data, hasChanged, submit, change }) => {
const handleServiceSelect = createSingleAutocompleteSelectHandler(
change,
setSelectedServiceAcccount,
@ -93,11 +93,10 @@ const WebhookCreatePage: React.FC<WebhookCreatePageProps> = ({
<WebhookInfo
data={data}
disabled={disabled}
errors={errors}
fetchServiceAccounts={fetchServiceAccounts}
serviceDisplayValue={selectedServiceAcccount}
services={servicesChoiceList}
fetchServiceAccounts={fetchServiceAccounts}
apiErrors={apiErrors}
errors={errors}
serviceOnChange={handleServiceSelect}
onChange={change}
/>

View file

@ -14,17 +14,16 @@ import SingleAutocompleteSelectField, {
} from "@saleor/components/SingleAutocompleteSelectField";
import { ChangeEvent } from "@saleor/hooks/useForm";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "@saleor/types";
import { WebhookCreate_webhookCreate_webhookErrors } from "@saleor/webhooks/types/WebhookCreate";
import { getFieldError } from "@saleor/utils/errors";
import { FormData } from "../WebhooksDetailsPage";
interface WebhookInfoProps {
apiErrors: WebhookCreate_webhookCreate_webhookErrors[];
data: FormData;
disabled: boolean;
errors: WebhookCreate_webhookCreate_webhookErrors[];
serviceDisplayValue: string;
services: SingleAutocompleteChoiceType[];
errors: FormErrors<"name" | "targetUrl" | "secretKey">;
onChange: (event: React.ChangeEvent<any>) => void;
serviceOnChange: (event: ChangeEvent) => void;
fetchServiceAccounts: (data: string) => void;
@ -45,7 +44,6 @@ const useStyles = makeStyles(
);
const WebhookInfo: React.FC<WebhookInfoProps> = ({
apiErrors,
data,
disabled,
services,
@ -58,7 +56,7 @@ const WebhookInfo: React.FC<WebhookInfoProps> = ({
const classes = useStyles({});
const intl = useIntl();
const serviceAccountsError =
apiErrors.filter(error => error.field === null).length > 0;
errors.filter(error => error.field === null).length > 0;
return (
<Card>
@ -74,8 +72,8 @@ const WebhookInfo: React.FC<WebhookInfoProps> = ({
</Typography>
<TextField
disabled={disabled}
error={!!errors.name}
helperText={errors.name}
error={!!getFieldError(errors, "name")}
helperText={getFieldError(errors, "name")?.message}
label={intl.formatMessage({
defaultMessage: "Webhook Name",
description: "webhook"
@ -117,11 +115,14 @@ const WebhookInfo: React.FC<WebhookInfoProps> = ({
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.targetUrl}
helperText={intl.formatMessage({
defaultMessage: "This URL will receive webhook POST requests",
description: "webhook target url help text"
})}
error={!!getFieldError(errors, "targetUrl")}
helperText={
getFieldError(errors, "targetUrl")?.message ||
intl.formatMessage({
defaultMessage: "This URL will receive webhook POST requests",
description: "webhook target url help text"
})
}
label={intl.formatMessage({
defaultMessage: "Target URL",
description: "webhook"
@ -134,12 +135,15 @@ const WebhookInfo: React.FC<WebhookInfoProps> = ({
<FormSpacer />
<TextField
disabled={disabled}
error={!!errors.secretKey}
helperText={intl.formatMessage({
defaultMessage:
"secret key is used to create a hash signature with each payload. *optional field",
description: "webhook secret key help text"
})}
error={!!getFieldError(errors, "secretKey")}
helperText={
getFieldError(errors, "secretKey")?.message ||
intl.formatMessage({
defaultMessage:
"secret key is used to create a hash signature with each payload. *optional field",
description: "webhook secret key help text"
})
}
label={intl.formatMessage({
defaultMessage: "Secrect Key",
description: "webhook"

View file

@ -45,7 +45,7 @@ export interface WebhooksDetailsPageProps {
const WebhooksDetailsPage: React.FC<WebhooksDetailsPageProps> = ({
disabled,
errors: apiErrors,
errors,
webhook,
saveButtonBarState,
services,
@ -81,8 +81,8 @@ const WebhooksDetailsPage: React.FC<WebhooksDetailsPageProps> = ({
[]
);
return (
<Form errors={apiErrors} initial={initialForm} onSubmit={onSubmit}>
{({ data, errors, hasChanged, submit, change }) => {
<Form initial={initialForm} onSubmit={onSubmit}>
{({ data, hasChanged, submit, change }) => {
const handleServiceSelect = createSingleAutocompleteSelectHandler(
change,
setSelectedServiceAcccounts,
@ -107,7 +107,6 @@ const WebhooksDetailsPage: React.FC<WebhooksDetailsPageProps> = ({
<Grid>
<div>
<WebhookInfo
apiErrors={apiErrors}
data={data}
disabled={disabled}
serviceDisplayValue={selectedServiceAcccounts}