Exit form fixes (#1889)
* Add onBeforeUnload handler to prevent accidental refresh * Update button messages * Fix exit form not working after submit * Make onBeforeUnload disable if env is development * Fix onClose * Remove internal date time field state * Update messages and dialog * Prevent navigation on 400 error * Add submit disabled ref in exit form * Update exit form dialog for disabled save * Update confirmLeave forms to set ref if save is disabled * Remove unused error handling * Remove explicit ref type * Remove unused import * Fix disabled type * Add disable check function to generic forms * Add custom isDisabled method to sale and voucher forms * Add default isDisabled functions to confirmLeave forms * Update tests * Remove unused code * Rebase fixes + update tests * Refactor form and useform * Refactor disabling forms * Change "saveDisabled" name to "isSaveDisabled" for improved readability * Change "isDisabled" function to "checkIfSaveIsDisabled" * Update exit form disabling conditions for zone rates forms
This commit is contained in:
parent
7576afed4e
commit
a5ac6bb92e
59 changed files with 717 additions and 390 deletions
|
@ -2231,19 +2231,23 @@
|
|||
},
|
||||
"src_dot_components_dot_Form_dot_cancelButton": {
|
||||
"context": "ExitFormPrompt cancel button",
|
||||
"string": "leave without saving"
|
||||
"string": "Discard changes"
|
||||
},
|
||||
"src_dot_components_dot_Form_dot_confirmButton": {
|
||||
"context": "ExitFormPrompt confirm button",
|
||||
"string": "save & continue"
|
||||
"string": "Save changes"
|
||||
},
|
||||
"src_dot_components_dot_Form_dot_description": {
|
||||
"context": "ExitFormPrompt description",
|
||||
"string": "You have unsaved changes on this view. What would you like to do with them?"
|
||||
"src_dot_components_dot_Form_dot_continueEditingButton": {
|
||||
"context": "ExitFormPrompt continue editing button",
|
||||
"string": "Continue editing"
|
||||
},
|
||||
"src_dot_components_dot_Form_dot_title": {
|
||||
"context": "ExitFormPrompt title",
|
||||
"string": "Are you sure you want to leave?"
|
||||
"string": "Would you like to save changes?"
|
||||
},
|
||||
"src_dot_components_dot_Form_dot_unableToSaveTitle": {
|
||||
"context": "ExitFormPrompt title",
|
||||
"string": "You have unsaved changes"
|
||||
},
|
||||
"src_dot_components_dot_ImageUpload_dot_1731007575": {
|
||||
"context": "image upload",
|
||||
|
|
|
@ -56,8 +56,13 @@ const CustomAppCreatePage: React.FC<CustomAppCreatePageProps> = props => {
|
|||
const permissionsError = getAppErrorMessage(formErrors.permissions, intl);
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ data, change, hasChanged, submit }) => (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data, change, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.apps)}
|
||||
|
@ -96,7 +101,7 @@ const CustomAppCreatePage: React.FC<CustomAppCreatePageProps> = props => {
|
|||
/>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -101,8 +101,13 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ data, change, hasChanged, submit }) => (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data, change, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.apps)}
|
||||
|
@ -182,7 +187,7 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -156,12 +156,17 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={handleSubmit}>
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={handleSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({
|
||||
change,
|
||||
set,
|
||||
data,
|
||||
hasChanged,
|
||||
isSaveDisabled,
|
||||
submit,
|
||||
errors,
|
||||
setError,
|
||||
|
@ -239,7 +244,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -31,8 +31,8 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<CategoryCreateForm onSubmit={onSubmit}>
|
||||
{({ data, change, handlers, submit, hasChanged }) => (
|
||||
<CategoryCreateForm onSubmit={onSubmit} disabled={disabled}>
|
||||
{({ data, change, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.categories)}
|
||||
|
@ -74,7 +74,7 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
|
|||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
</div>
|
||||
</Container>
|
||||
|
|
|
@ -33,6 +33,7 @@ export interface UseCategoryCreateFormResult
|
|||
export interface CategoryCreateFormProps {
|
||||
children: (props: UseCategoryCreateFormResult) => React.ReactNode;
|
||||
onSubmit: (data: CategoryCreateData) => Promise<any[]>;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const initialData: CategoryCreateFormData = {
|
||||
|
@ -45,7 +46,8 @@ const initialData: CategoryCreateFormData = {
|
|||
};
|
||||
|
||||
function useCategoryCreateForm(
|
||||
onSubmit: (data: CategoryCreateData) => Promise<any[]>
|
||||
onSubmit: (data: CategoryCreateData) => Promise<any[]>,
|
||||
disabled: boolean
|
||||
): UseCategoryCreateFormResult {
|
||||
const {
|
||||
handleChange,
|
||||
|
@ -53,7 +55,8 @@ function useCategoryCreateForm(
|
|||
hasChanged,
|
||||
triggerChange,
|
||||
setChanged,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = useForm(initialData, undefined, { confirmLeave: true });
|
||||
|
||||
const handleFormSubmit = useHandleFormSubmit({
|
||||
|
@ -87,6 +90,9 @@ function useCategoryCreateForm(
|
|||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const isSaveDisabled = disabled || !hasChanged;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data: getData(),
|
||||
|
@ -95,15 +101,17 @@ function useCategoryCreateForm(
|
|||
changeMetadata
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
const CategoryCreateForm: React.FC<CategoryCreateFormProps> = ({
|
||||
children,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
disabled
|
||||
}) => {
|
||||
const props = useCategoryCreateForm(onSubmit);
|
||||
const props = useCategoryCreateForm(onSubmit, disabled);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -92,8 +92,12 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<CategoryUpdateForm category={category} onSubmit={onSubmit}>
|
||||
{({ data, change, handlers, submit, hasChanged }) => (
|
||||
<CategoryUpdateForm
|
||||
category={category}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data, change, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.categories)}
|
||||
|
@ -217,7 +221,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
|||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
|
|
|
@ -38,6 +38,7 @@ export interface CategoryUpdateFormProps {
|
|||
children: (props: UseCategoryUpdateFormResult) => React.ReactNode;
|
||||
category: CategoryDetailsFragment;
|
||||
onSubmit: (data: CategoryUpdateData) => Promise<any[]>;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const getInitialData = (category?: CategoryDetailsFragment) => ({
|
||||
|
@ -52,7 +53,8 @@ const getInitialData = (category?: CategoryDetailsFragment) => ({
|
|||
|
||||
function useCategoryUpdateForm(
|
||||
category: CategoryDetailsFragment,
|
||||
onSubmit: (data: CategoryUpdateData) => Promise<any[]>
|
||||
onSubmit: (data: CategoryUpdateData) => Promise<any[]>,
|
||||
disabled: boolean
|
||||
): UseCategoryUpdateFormResult {
|
||||
const {
|
||||
handleChange,
|
||||
|
@ -60,7 +62,8 @@ function useCategoryUpdateForm(
|
|||
triggerChange,
|
||||
hasChanged,
|
||||
setChanged,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = useForm(getInitialData(category), undefined, { confirmLeave: true });
|
||||
|
||||
const handleFormSubmit = useHandleFormSubmit({
|
||||
|
@ -101,6 +104,9 @@ function useCategoryUpdateForm(
|
|||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const isSaveDisabled = disabled || !hasChanged;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data: getData(),
|
||||
|
@ -109,16 +115,18 @@ function useCategoryUpdateForm(
|
|||
changeMetadata
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
const CategoryUpdateForm: React.FC<CategoryUpdateFormProps> = ({
|
||||
children,
|
||||
category,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
disabled
|
||||
}) => {
|
||||
const props = useCategoryUpdateForm(category, onSubmit);
|
||||
const props = useCategoryUpdateForm(category, onSubmit, disabled);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import ShippingZonesCard from "@saleor/channels/components/ShippingZonesCard/ShippingZonesCard";
|
||||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Form, { FormDataWithOpts } from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Savebar from "@saleor/components/Savebar";
|
||||
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||
|
@ -94,9 +94,25 @@ const ChannelDetailsPage = function<TErrors>({
|
|||
!shippingZonesToDisplay.some(({ id }) => id === searchedZoneId)
|
||||
);
|
||||
|
||||
const checkIfSaveIsDisabled = (data: FormDataWithOpts<FormData>) => {
|
||||
const formDisabled =
|
||||
!data.name ||
|
||||
!data.slug ||
|
||||
!data.currencyCode ||
|
||||
!data.defaultCountry ||
|
||||
!(data.name.trim().length > 0);
|
||||
|
||||
return disabled || formDisabled || !data.hasChanged;
|
||||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave onSubmit={onSubmit} initial={initialData}>
|
||||
{({ change, data, hasChanged, submit, set }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
onSubmit={onSubmit}
|
||||
initial={initialData}
|
||||
checkIfSaveIsDisabled={checkIfSaveIsDisabled}
|
||||
>
|
||||
{({ change, data, submit, set, isSaveDisabled }) => {
|
||||
const handleCurrencyCodeSelect = createSingleAutocompleteSelectHandler(
|
||||
change,
|
||||
setSelectedCurrencyCode,
|
||||
|
@ -147,13 +163,6 @@ const ChannelDetailsPage = function<TErrors>({
|
|||
);
|
||||
};
|
||||
|
||||
const formDisabled =
|
||||
!data.name ||
|
||||
!data.slug ||
|
||||
!data.currencyCode ||
|
||||
!data.defaultCountry ||
|
||||
!(data.name.trim().length > 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
|
@ -197,7 +206,7 @@ const ChannelDetailsPage = function<TErrors>({
|
|||
onSubmit={submit}
|
||||
onDelete={onDelete}
|
||||
state={saveButtonBarState}
|
||||
disabled={disabled || formDisabled || !onSubmit || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -54,8 +54,9 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
|
|||
onSubmit={onSubmit}
|
||||
currentChannels={currentChannels}
|
||||
setChannels={onChannelsChange}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, handlers, hasChanged, submit }) => (
|
||||
{({ change, data, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.collections)}
|
||||
|
@ -156,7 +157,7 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
|
|||
</Grid>
|
||||
<Savebar
|
||||
state={saveButtonBarState}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
|
|
|
@ -49,6 +49,7 @@ export interface CollectionCreateFormProps {
|
|||
setChannels: (data: ChannelCollectionData[]) => void;
|
||||
children: (props: UseCollectionCreateFormResult) => React.ReactNode;
|
||||
onSubmit: (data: CollectionCreateData) => SubmitPromise;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const getInitialData = (
|
||||
|
@ -71,7 +72,8 @@ const getInitialData = (
|
|||
function useCollectionCreateForm(
|
||||
currentChannels: ChannelCollectionData[],
|
||||
setChannels: (data: ChannelCollectionData[]) => void,
|
||||
onSubmit: (data: CollectionCreateData) => SubmitPromise
|
||||
onSubmit: (data: CollectionCreateData) => SubmitPromise,
|
||||
disabled: boolean
|
||||
): UseCollectionCreateFormResult {
|
||||
const {
|
||||
handleChange,
|
||||
|
@ -79,7 +81,8 @@ function useCollectionCreateForm(
|
|||
triggerChange,
|
||||
setChanged,
|
||||
hasChanged,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = useForm(getInitialData(currentChannels), undefined, {
|
||||
confirmLeave: true,
|
||||
formId: COLLECTION_CREATE_FORM_ID
|
||||
|
@ -122,6 +125,9 @@ function useCollectionCreateForm(
|
|||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const isSaveDisabled = disabled || !hasChanged;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data: getData(),
|
||||
|
@ -131,7 +137,8 @@ function useCollectionCreateForm(
|
|||
changeMetadata
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -139,9 +146,15 @@ const CollectionCreateForm: React.FC<CollectionCreateFormProps> = ({
|
|||
currentChannels,
|
||||
setChannels,
|
||||
children,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
disabled
|
||||
}) => {
|
||||
const props = useCollectionCreateForm(currentChannels, setChannels, onSubmit);
|
||||
const props = useCollectionCreateForm(
|
||||
currentChannels,
|
||||
setChannels,
|
||||
onSubmit,
|
||||
disabled
|
||||
);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -73,8 +73,10 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
|
|||
currentChannels={currentChannels}
|
||||
setChannels={onChannelsChange}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
hasChannelChanged={hasChannelChanged}
|
||||
>
|
||||
{({ change, data, handlers, hasChanged, submit }) => (
|
||||
{({ change, data, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.collections)}
|
||||
|
@ -150,7 +152,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
|
|||
</Grid>
|
||||
<Savebar
|
||||
state={saveButtonBarState}
|
||||
disabled={disabled || (!hasChanged && !hasChannelChanged)}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onDelete={onCollectionRemove}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -48,6 +48,8 @@ export interface CollectionUpdateFormProps {
|
|||
currentChannels: ChannelCollectionData[];
|
||||
setChannels: (data: ChannelCollectionData[]) => void;
|
||||
onSubmit: (data: CollectionUpdateData) => Promise<any[]>;
|
||||
disabled: boolean;
|
||||
hasChannelChanged: boolean;
|
||||
}
|
||||
|
||||
const getInitialData = (
|
||||
|
@ -68,7 +70,9 @@ function useCollectionUpdateForm(
|
|||
collection: CollectionDetailsFragment,
|
||||
currentChannels: ChannelCollectionData[],
|
||||
setChannels: (data: ChannelCollectionData[]) => void,
|
||||
onSubmit: (data: CollectionUpdateData) => Promise<any[]>
|
||||
onSubmit: (data: CollectionUpdateData) => Promise<any[]>,
|
||||
disabled: boolean,
|
||||
hasChannelChanged: boolean
|
||||
): UseCollectionUpdateFormResult {
|
||||
const {
|
||||
handleChange,
|
||||
|
@ -76,7 +80,8 @@ function useCollectionUpdateForm(
|
|||
triggerChange,
|
||||
setChanged,
|
||||
hasChanged,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = useForm(getInitialData(collection, currentChannels), undefined, {
|
||||
confirmLeave: true,
|
||||
formId: COLLECTION_DETAILS_FORM_ID
|
||||
|
@ -126,6 +131,9 @@ function useCollectionUpdateForm(
|
|||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const isSaveDisabled = disabled || (!hasChanged && !hasChannelChanged);
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data: getData(),
|
||||
|
@ -135,7 +143,8 @@ function useCollectionUpdateForm(
|
|||
changeMetadata
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -144,13 +153,17 @@ const CollectionUpdateForm: React.FC<CollectionUpdateFormProps> = ({
|
|||
currentChannels,
|
||||
setChannels,
|
||||
children,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
disabled,
|
||||
hasChannelChanged
|
||||
}) => {
|
||||
const props = useCollectionUpdateForm(
|
||||
collection,
|
||||
currentChannels,
|
||||
setChannels,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
disabled,
|
||||
hasChannelChanged
|
||||
);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
|
|
|
@ -6,8 +6,8 @@ import {
|
|||
ProductErrorWithAttributesFragment
|
||||
} from "@saleor/graphql";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { DateTime, joinDateTime, splitDateTime } from "@saleor/misc";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { joinDateTime, splitDateTime } from "@saleor/misc";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
type DateTimeFieldProps = Omit<TextFieldProps, "label" | "error"> & {
|
||||
|
@ -21,14 +21,11 @@ export const DateTimeField: React.FC<DateTimeFieldProps> = ({
|
|||
error,
|
||||
name,
|
||||
onChange,
|
||||
value: initialValue
|
||||
value
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [value, setValue] = useState<DateTime>(
|
||||
initialValue ? splitDateTime(initialValue) : { date: "", time: "" }
|
||||
);
|
||||
|
||||
useEffect(() => onChange(joinDateTime(value.date, value.time)), [value]);
|
||||
const parsedValue = value ? splitDateTime(value) : { date: "", time: "" };
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -41,10 +38,11 @@ export const DateTimeField: React.FC<DateTimeFieldProps> = ({
|
|||
name={`${name}:date`}
|
||||
onChange={event => {
|
||||
const date = event.target.value;
|
||||
setValue(value => ({ ...value, date }));
|
||||
|
||||
onChange(joinDateTime(date, parsedValue.time));
|
||||
}}
|
||||
type="date"
|
||||
value={value.date}
|
||||
value={parsedValue.date}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
<TextField
|
||||
|
@ -56,10 +54,11 @@ export const DateTimeField: React.FC<DateTimeFieldProps> = ({
|
|||
name={`${name}:time`}
|
||||
onChange={event => {
|
||||
const time = event.target.value;
|
||||
setValue(value => ({ ...value, time }));
|
||||
|
||||
onChange(joinDateTime(parsedValue.date, time));
|
||||
}}
|
||||
type="time"
|
||||
value={value.time}
|
||||
value={parsedValue.time}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { Button, Dialog, DialogContent, makeStyles } from "@material-ui/core";
|
||||
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
|
||||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { exitFormPromptMessages as messages } from "./messages";
|
||||
|
||||
|
@ -19,6 +18,12 @@ const useStyles = makeStyles(
|
|||
buttonsContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "flex-end"
|
||||
},
|
||||
dialogContent: {
|
||||
"@media (min-width: 800px)": {
|
||||
minWidth: 500
|
||||
},
|
||||
paddingTop: 0
|
||||
}
|
||||
}),
|
||||
{ name: "ExitFormPrompt" }
|
||||
|
@ -29,24 +34,27 @@ interface ExitFormDialogProps {
|
|||
onClose: () => void;
|
||||
onLeave: () => void;
|
||||
isOpen: boolean;
|
||||
isSubmitDisabled: boolean;
|
||||
}
|
||||
|
||||
const ExitFormDialog: React.FC<ExitFormDialogProps> = ({
|
||||
onSubmit,
|
||||
onLeave,
|
||||
onClose,
|
||||
isOpen
|
||||
isOpen,
|
||||
isSubmitDisabled
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Dialog className={classes.container} open={isOpen}>
|
||||
<CardTitle title={intl.formatMessage(messages.title)} onClose={onClose} />
|
||||
<DialogContent>
|
||||
<FormattedMessage {...messages.description} />
|
||||
<CardSpacer />
|
||||
<CardSpacer />
|
||||
<Dialog className={classes.container} open={isOpen} onClose={onClose}>
|
||||
<CardTitle
|
||||
title={intl.formatMessage(
|
||||
isSubmitDisabled ? messages.unableToSaveTitle : messages.title
|
||||
)}
|
||||
/>
|
||||
<DialogContent className={classes.dialogContent}>
|
||||
<div className={classes.buttonsContainer}>
|
||||
<Button onClick={onLeave}>
|
||||
{intl.formatMessage(messages.cancelButton)}
|
||||
|
@ -55,10 +63,14 @@ const ExitFormDialog: React.FC<ExitFormDialogProps> = ({
|
|||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={onSubmit}
|
||||
onClick={isSubmitDisabled ? onClose : onSubmit}
|
||||
data-test-id="save-and-continue"
|
||||
>
|
||||
{intl.formatMessage(messages.confirmButton)}
|
||||
{intl.formatMessage(
|
||||
isSubmitDisabled
|
||||
? messages.continueEditingButton
|
||||
: messages.confirmButton
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import { isInDevelopment } from "@saleor/misc";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useHistory } from "react-router";
|
||||
import useRouter from "use-react-router";
|
||||
|
||||
import ExitFormDialog from "./ExitFormDialog";
|
||||
import useBeforeUnload from "./useBeforeUnload";
|
||||
|
||||
export interface ExitFormDialogData {
|
||||
setIsDirty: (id: symbol, isDirty: boolean) => void;
|
||||
|
@ -12,6 +14,7 @@ export interface ExitFormDialogData {
|
|||
shouldBlockNavigation: () => boolean;
|
||||
setIsSubmitting: (value: boolean) => void;
|
||||
submit: () => SubmitPromise;
|
||||
setIsSubmitDisabled: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export type SubmitFn = (dataOrEvent?: any) => SubmitPromise<any[]>;
|
||||
|
@ -37,7 +40,8 @@ export const ExitFormDialogContext = React.createContext<ExitFormDialogData>({
|
|||
setExitDialogSubmitRef: () => undefined,
|
||||
shouldBlockNavigation: () => false,
|
||||
setIsSubmitting: () => undefined,
|
||||
submit: () => Promise.resolve([])
|
||||
submit: () => Promise.resolve([]),
|
||||
setIsSubmitDisabled: () => undefined
|
||||
});
|
||||
|
||||
const defaultValues = {
|
||||
|
@ -56,6 +60,11 @@ export function useExitFormDialogProvider() {
|
|||
const { history: routerHistory } = useRouter();
|
||||
|
||||
const [showDialog, setShowDialog] = useState(defaultValues.showDialog);
|
||||
const isSubmitDisabled = useRef(false);
|
||||
|
||||
const setIsSubmitDisabled = (status: boolean) => {
|
||||
isSubmitDisabled.current = status;
|
||||
};
|
||||
|
||||
const isSubmitting = useRef(defaultValues.isSubmitting);
|
||||
const formsData = useRef<FormsData>({});
|
||||
|
@ -209,10 +218,11 @@ export function useExitFormDialogProvider() {
|
|||
const errors = await Promise.all(
|
||||
getDirtyFormsSubmitFn().map(submitFn => submitFn())
|
||||
);
|
||||
const isError = errors.flat().some(errors => errors);
|
||||
|
||||
setIsSubmitting(false);
|
||||
|
||||
const isError = errors.flat().some(errors => errors);
|
||||
|
||||
if (!isError) {
|
||||
continueNavigation();
|
||||
}
|
||||
|
@ -239,7 +249,8 @@ export function useExitFormDialogProvider() {
|
|||
setEnableExitDialog,
|
||||
setExitDialogSubmitRef: setSubmitRef,
|
||||
setIsSubmitting,
|
||||
submit: handleSubmit
|
||||
submit: handleSubmit,
|
||||
setIsSubmitDisabled
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -247,7 +258,9 @@ export function useExitFormDialogProvider() {
|
|||
showDialog,
|
||||
handleSubmit,
|
||||
handleLeave,
|
||||
handleClose
|
||||
handleClose,
|
||||
shouldBlockNav,
|
||||
isSubmitDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -257,9 +270,20 @@ const ExitFormDialogProvider = ({ children }) => {
|
|||
handleLeave,
|
||||
handleSubmit,
|
||||
providerData,
|
||||
showDialog
|
||||
showDialog,
|
||||
shouldBlockNav,
|
||||
isSubmitDisabled
|
||||
} = useExitFormDialogProvider();
|
||||
|
||||
useBeforeUnload(e => {
|
||||
// If form is dirty and user does a refresh,
|
||||
// the browser will ask about unsaved changes
|
||||
if (shouldBlockNav() && !isInDevelopment) {
|
||||
e.preventDefault();
|
||||
e.returnValue = "";
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<ExitFormDialogContext.Provider value={providerData}>
|
||||
<ExitFormDialog
|
||||
|
@ -267,6 +291,7 @@ const ExitFormDialogProvider = ({ children }) => {
|
|||
onSubmit={handleSubmit}
|
||||
onLeave={handleLeave}
|
||||
onClose={handleClose}
|
||||
isSubmitDisabled={isSubmitDisabled.current}
|
||||
/>
|
||||
{children}
|
||||
</ExitFormDialogContext.Provider>
|
||||
|
|
|
@ -3,6 +3,13 @@ import React from "react";
|
|||
|
||||
import { FormId } from "./ExitFormDialogProvider";
|
||||
|
||||
export type FormDataWithOpts<TData> = TData &
|
||||
Pick<UseFormResult<TData>, "hasChanged">;
|
||||
|
||||
export type CheckIfSaveIsDisabledFnType<T> = (
|
||||
data: FormDataWithOpts<T>
|
||||
) => boolean;
|
||||
|
||||
export interface FormProps<TData, TErrors>
|
||||
extends Omit<React.HTMLProps<HTMLFormElement>, "onSubmit"> {
|
||||
children: (props: UseFormResult<TData>) => React.ReactNode;
|
||||
|
@ -11,6 +18,7 @@ export interface FormProps<TData, TErrors>
|
|||
resetOnSubmit?: boolean;
|
||||
onSubmit?: (data: TData) => SubmitPromise<TErrors[]> | void;
|
||||
formId?: FormId;
|
||||
checkIfSaveIsDisabled?: CheckIfSaveIsDisabledFnType<TData>;
|
||||
}
|
||||
|
||||
function Form<TData, Terrors>({
|
||||
|
@ -20,9 +28,16 @@ function Form<TData, Terrors>({
|
|||
onSubmit,
|
||||
confirmLeave = false,
|
||||
formId,
|
||||
checkIfSaveIsDisabled,
|
||||
disabled,
|
||||
...rest
|
||||
}: FormProps<TData, Terrors>) {
|
||||
const renderProps = useForm(initial, onSubmit, { confirmLeave, formId });
|
||||
const renderProps = useForm(initial, onSubmit, {
|
||||
confirmLeave,
|
||||
formId,
|
||||
checkIfSaveIsDisabled,
|
||||
disabled
|
||||
});
|
||||
|
||||
function handleSubmit(event?: React.FormEvent<any>, cb?: () => void) {
|
||||
const { reset, submit } = renderProps;
|
||||
|
|
|
@ -2,20 +2,23 @@ import { defineMessages } from "react-intl";
|
|||
|
||||
export const exitFormPromptMessages = defineMessages({
|
||||
title: {
|
||||
defaultMessage: "Are you sure you want to leave?",
|
||||
defaultMessage: "Would you like to save changes?",
|
||||
description: "ExitFormPrompt title"
|
||||
},
|
||||
description: {
|
||||
defaultMessage:
|
||||
"You have unsaved changes on this view. What would you like to do with them?",
|
||||
description: "ExitFormPrompt description"
|
||||
unableToSaveTitle: {
|
||||
defaultMessage: "You have unsaved changes",
|
||||
description: "ExitFormPrompt title"
|
||||
},
|
||||
cancelButton: {
|
||||
defaultMessage: "leave without saving",
|
||||
defaultMessage: "Discard changes",
|
||||
description: "ExitFormPrompt cancel button"
|
||||
},
|
||||
confirmButton: {
|
||||
defaultMessage: "save & continue",
|
||||
defaultMessage: "Save changes",
|
||||
description: "ExitFormPrompt confirm button"
|
||||
},
|
||||
continueEditingButton: {
|
||||
defaultMessage: "Continue editing",
|
||||
description: "ExitFormPrompt continue editing button"
|
||||
}
|
||||
});
|
||||
|
|
19
src/components/Form/useBeforeUnload.ts
Normal file
19
src/components/Form/useBeforeUnload.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
|
||||
const useBeforeUnload = fn => {
|
||||
const cb = useRef(fn);
|
||||
|
||||
useEffect(() => {
|
||||
cb.current = fn;
|
||||
}, [fn]);
|
||||
|
||||
useEffect(() => {
|
||||
const onBeforeUnload = (...args) => cb.current?.(...args);
|
||||
|
||||
window.addEventListener("beforeunload", onBeforeUnload);
|
||||
|
||||
return () => window.removeEventListener("beforeunload", onBeforeUnload);
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default useBeforeUnload;
|
|
@ -1,4 +1,4 @@
|
|||
import { useContext, useRef } from "react";
|
||||
import React, { useContext, useRef } from "react";
|
||||
|
||||
import {
|
||||
ExitFormDialogContext,
|
||||
|
@ -8,7 +8,14 @@ import {
|
|||
} from "./ExitFormDialogProvider";
|
||||
|
||||
export interface UseExitFormDialogResult
|
||||
extends Omit<ExitFormDialogData, "setIsDirty" | "setExitDialogSubmitRef">,
|
||||
extends Pick<
|
||||
ExitFormDialogData,
|
||||
| "setEnableExitDialog"
|
||||
| "shouldBlockNavigation"
|
||||
| "setIsSubmitting"
|
||||
| "setIsSubmitDisabled"
|
||||
| "submit"
|
||||
>,
|
||||
WithFormId {
|
||||
setIsDirty: (isDirty: boolean) => void;
|
||||
setExitDialogSubmitRef: (submitFn: SubmitFn) => void;
|
||||
|
@ -16,19 +23,29 @@ export interface UseExitFormDialogResult
|
|||
|
||||
export interface UseExitFormDialogProps {
|
||||
formId: symbol;
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
export const useExitFormDialog = (
|
||||
{ formId }: UseExitFormDialogProps = { formId: undefined }
|
||||
{ formId, isDisabled }: UseExitFormDialogProps = { formId: undefined }
|
||||
): UseExitFormDialogResult => {
|
||||
const id = useRef(formId || Symbol()).current;
|
||||
|
||||
const { setIsDirty, setExitDialogSubmitRef, ...rest } = useContext(
|
||||
ExitFormDialogContext
|
||||
);
|
||||
const exitDialogProps = useContext(ExitFormDialogContext);
|
||||
const {
|
||||
setIsDirty,
|
||||
setIsSubmitDisabled,
|
||||
setExitDialogSubmitRef
|
||||
} = exitDialogProps;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isDisabled !== undefined) {
|
||||
setIsSubmitDisabled(isDisabled);
|
||||
}
|
||||
}, [isDisabled]);
|
||||
|
||||
return {
|
||||
...rest,
|
||||
...exitDialogProps,
|
||||
formId: id,
|
||||
setIsDirty: (value: boolean) => setIsDirty(id, value),
|
||||
setExitDialogSubmitRef: (submitFn: SubmitFn) =>
|
||||
|
|
|
@ -135,8 +135,13 @@ const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={handleSubmit}>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={handleSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit }) => {
|
||||
const handleCountrySelect = createSingleAutocompleteSelectHandler(
|
||||
change,
|
||||
setCountryDisplayName,
|
||||
|
@ -182,7 +187,7 @@ const CustomerCreatePage: React.FC<CustomerCreatePageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBar}
|
||||
onSubmit={submit}
|
||||
onCancel={onBack}
|
||||
|
|
|
@ -80,8 +80,13 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
|
|||
} = useMetadataChangeTrigger();
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit }) => {
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -136,7 +141,7 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBar}
|
||||
onSubmit={submit}
|
||||
onCancel={onBack}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { validateSalePrice } from "@saleor/channels/utils";
|
|||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import ChannelsAvailabilityCard from "@saleor/components/ChannelsAvailabilityCard";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Form, { FormDataWithOpts } from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Metadata, { MetadataFormData } from "@saleor/components/Metadata";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
|
@ -81,23 +81,26 @@ const SaleCreatePage: React.FC<SaleCreatePageProps> = ({
|
|||
privateMetadata: []
|
||||
};
|
||||
|
||||
const checkIfSaveIsDisabled = (data: FormDataWithOpts<FormData>) =>
|
||||
data.channelListings?.some(channel => validateSalePrice(data, channel)) ||
|
||||
disabled ||
|
||||
!data.hasChanged;
|
||||
|
||||
return (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
formId={SALE_CREATE_FORM_ID}
|
||||
checkIfSaveIsDisabled={checkIfSaveIsDisabled}
|
||||
>
|
||||
{({ change, data, hasChanged, submit, triggerChange }) => {
|
||||
{({ change, data, submit, triggerChange, isSaveDisabled }) => {
|
||||
const handleChannelChange = createSaleChannelsChangeHandler(
|
||||
data.channelListings,
|
||||
onChannelsChange,
|
||||
triggerChange,
|
||||
data.type
|
||||
);
|
||||
const formDisabled = data.channelListings?.some(channel =>
|
||||
validateSalePrice(data, channel)
|
||||
);
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -152,7 +155,7 @@ const SaleCreatePage: React.FC<SaleCreatePageProps> = ({
|
|||
<Metadata data={data} onChange={changeMetadata} />
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || formDisabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { ChannelSaleData, validateSalePrice } from "@saleor/channels/utils";
|
|||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import ChannelsAvailabilityCard from "@saleor/components/ChannelsAvailabilityCard";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Form, { FormDataWithOpts } from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Metadata, { MetadataFormData } from "@saleor/components/Metadata";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
|
@ -156,23 +156,29 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
|||
metadata: sale?.metadata.map(mapMetadataItemToInput),
|
||||
privateMetadata: sale?.privateMetadata.map(mapMetadataItemToInput)
|
||||
};
|
||||
|
||||
const checkIfSaveIsDisabled = (
|
||||
data: FormDataWithOpts<SaleDetailsPageFormData>
|
||||
) =>
|
||||
data.channelListings?.some(channel => validateSalePrice(data, channel)) ||
|
||||
disabled ||
|
||||
(!data.hasChanged && !hasChannelChanged);
|
||||
|
||||
return (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
formId={SALE_UPDATE_FORM_ID}
|
||||
checkIfSaveIsDisabled={checkIfSaveIsDisabled}
|
||||
>
|
||||
{({ change, data, hasChanged, submit, triggerChange }) => {
|
||||
{({ change, data, submit, triggerChange, isSaveDisabled }) => {
|
||||
const handleChannelChange = createSaleChannelsChangeHandler(
|
||||
data.channelListings,
|
||||
onChannelsChange,
|
||||
triggerChange,
|
||||
data.type
|
||||
);
|
||||
const formDisabled = data.channelListings?.some(channel =>
|
||||
validateSalePrice(data, channel)
|
||||
);
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -370,9 +376,7 @@ const SaleDetailsPage: React.FC<SaleDetailsPageProps> = ({
|
|||
<Metadata data={data} onChange={changeMetadata} />
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={
|
||||
disabled || formDisabled || (!hasChanged && !hasChannelChanged)
|
||||
}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onDelete={onRemove}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { ChannelVoucherData } from "@saleor/channels/utils";
|
|||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import ChannelsAvailabilityCard from "@saleor/components/ChannelsAvailabilityCard";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Form, { FormDataWithOpts } from "@saleor/components/Form";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Metadata from "@saleor/components/Metadata";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
|
@ -91,14 +91,26 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
|
|||
privateMetadata: []
|
||||
};
|
||||
|
||||
const checkIfSaveIsDisabled = (data: FormDataWithOpts<FormData>) =>
|
||||
(data.discountType.toString() !== "SHIPPING" &&
|
||||
data.channelListings?.some(
|
||||
channel =>
|
||||
validatePrice(channel.discountValue) ||
|
||||
(data.requirementsPicker === RequirementsPicker.ORDER &&
|
||||
validatePrice(channel.minSpent))
|
||||
)) ||
|
||||
disabled ||
|
||||
(!data.hasChanged && hasChannelChanged);
|
||||
|
||||
return (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
formId={VOUCHER_CREATE_FORM_ID}
|
||||
checkIfSaveIsDisabled={checkIfSaveIsDisabled}
|
||||
>
|
||||
{({ change, data, hasChanged, submit, triggerChange, set }) => {
|
||||
{({ change, data, submit, triggerChange, set, isSaveDisabled }) => {
|
||||
const handleDiscountTypeChange = createDiscountTypeChangeHandler(
|
||||
change
|
||||
);
|
||||
|
@ -107,14 +119,6 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
|
|||
onChannelsChange,
|
||||
triggerChange
|
||||
);
|
||||
const formDisabled =
|
||||
data.discountType.toString() !== "SHIPPING" &&
|
||||
data.channelListings?.some(
|
||||
channel =>
|
||||
validatePrice(channel.discountValue) ||
|
||||
(data.requirementsPicker === RequirementsPicker.ORDER &&
|
||||
validatePrice(channel.minSpent))
|
||||
);
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -199,9 +203,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
|
|||
<Metadata data={data} onChange={changeMetadata} />
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={
|
||||
disabled || formDisabled || (!hasChanged && !hasChannelChanged)
|
||||
}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { CheckIfSaveIsDisabledFnType } from "@saleor/components/Form";
|
||||
import { FormId } from "@saleor/components/Form/ExitFormDialogProvider";
|
||||
import {
|
||||
useExitFormDialog,
|
||||
|
@ -25,9 +26,11 @@ export type FormErrors<T> = {
|
|||
[field in keyof T]?: string | React.ReactNode;
|
||||
};
|
||||
|
||||
export interface UseFormOpts {
|
||||
export interface UseFormOpts<T> {
|
||||
confirmLeave: boolean;
|
||||
formId?: FormId;
|
||||
checkIfSaveIsDisabled?: CheckIfSaveIsDisabledFnType<T>;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface UseFormResult<TData>
|
||||
|
@ -42,6 +45,7 @@ export interface UseFormResult<TData>
|
|||
errors: FormErrors<TData>;
|
||||
setError: (name: keyof TData, error: string | React.ReactNode) => void;
|
||||
clearErrors: (name?: keyof TData | Array<keyof TData>) => void;
|
||||
setIsSubmitDisabled: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export interface CommonUseFormResult<TData> {
|
||||
|
@ -49,6 +53,7 @@ export interface CommonUseFormResult<TData> {
|
|||
change: FormChange;
|
||||
hasChanged: boolean;
|
||||
submit: (dataOrEvent?: any) => SubmitPromise<any[]>;
|
||||
isSaveDisabled?: boolean;
|
||||
}
|
||||
|
||||
export interface CommonUseFormResultWithHandlers<TData, THandlers>
|
||||
|
@ -84,9 +89,14 @@ function handleRefresh<T extends FormData>(
|
|||
function useForm<T extends FormData, TErrors>(
|
||||
initialData: T,
|
||||
onSubmit?: (data: T) => SubmitPromise<TErrors[]> | void,
|
||||
opts: UseFormOpts = { confirmLeave: false, formId: undefined }
|
||||
opts: UseFormOpts<T> = { confirmLeave: false, formId: undefined }
|
||||
): UseFormResult<T> {
|
||||
const { confirmLeave, formId: propsFormId } = opts;
|
||||
const {
|
||||
confirmLeave,
|
||||
formId: propsFormId,
|
||||
checkIfSaveIsDisabled,
|
||||
disabled
|
||||
} = opts;
|
||||
const [hasChanged, setChanged] = useState(false);
|
||||
const [errors, setErrors] = useState<FormErrors<T>>({});
|
||||
const [data, setData] = useStateFromProps(initialData, {
|
||||
|
@ -94,12 +104,33 @@ function useForm<T extends FormData, TErrors>(
|
|||
onRefresh: newData => handleRefresh(data, newData, handleSetChanged)
|
||||
});
|
||||
|
||||
const basicFormDisableConditions = () => !hasChanged || disabled;
|
||||
|
||||
const isSaveDisabled = () => {
|
||||
if (checkIfSaveIsDisabled) {
|
||||
return checkIfSaveIsDisabled({
|
||||
...data,
|
||||
hasChanged
|
||||
});
|
||||
}
|
||||
|
||||
if (disabled !== undefined) {
|
||||
return basicFormDisableConditions();
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const {
|
||||
setIsDirty: setIsFormDirtyInExitDialog,
|
||||
setExitDialogSubmitRef,
|
||||
setEnableExitDialog,
|
||||
setIsSubmitDisabled,
|
||||
formId
|
||||
} = useExitFormDialog({ formId: propsFormId });
|
||||
} = useExitFormDialog({
|
||||
formId: propsFormId,
|
||||
isDisabled: isSaveDisabled()
|
||||
});
|
||||
|
||||
const handleFormSubmit = useHandleFormSubmit({
|
||||
formId,
|
||||
|
@ -216,7 +247,9 @@ function useForm<T extends FormData, TErrors>(
|
|||
toggleValue,
|
||||
handleChange,
|
||||
triggerChange: handleSetChanged,
|
||||
setChanged: handleSetChanged
|
||||
setChanged: handleSetChanged,
|
||||
setIsSubmitDisabled,
|
||||
isSaveDisabled: isSaveDisabled()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -35,14 +35,14 @@ function useHandleFormSubmit<TData, TErrors>({
|
|||
|
||||
const errors = await result;
|
||||
|
||||
setIsSubmitting(false);
|
||||
|
||||
if (errors?.length === 0) {
|
||||
setChanged(false);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
setIsSubmitting(false);
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
|
|
@ -522,3 +522,6 @@ export const combinedMultiAutocompleteChoices = (
|
|||
selected: MultiAutocompleteChoiceType[],
|
||||
choices: MultiAutocompleteChoiceType[]
|
||||
) => uniqBy([...selected, ...choices], "value");
|
||||
|
||||
export const isInDevelopment =
|
||||
!process.env.NODE_ENV || process.env.NODE_ENV === "development";
|
||||
|
|
|
@ -67,8 +67,9 @@ const OrderRefundPage: React.FC<OrderRefundPageProps> = props => {
|
|||
order={order}
|
||||
defaultType={defaultType}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data, handlers, change, submit }) => {
|
||||
{({ data, handlers, change, submit, isSaveDisabled }) => {
|
||||
const isProductRefund = data.type === OrderRefundType.PRODUCTS;
|
||||
|
||||
return (
|
||||
|
@ -156,7 +157,7 @@ const OrderRefundPage: React.FC<OrderRefundPageProps> = props => {
|
|||
}
|
||||
data={data}
|
||||
order={order}
|
||||
disabled={disabled}
|
||||
disabled={isSaveDisabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
onRefund={submit}
|
||||
|
|
|
@ -57,6 +57,7 @@ interface OrderRefundFormProps {
|
|||
order: OrderRefundDataQuery["order"];
|
||||
defaultType: OrderRefundType;
|
||||
onSubmit: (data: OrderRefundSubmitData) => SubmitPromise;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
function getOrderRefundPageFormData(
|
||||
|
@ -73,7 +74,8 @@ function getOrderRefundPageFormData(
|
|||
function useOrderRefundForm(
|
||||
order: OrderRefundDataQuery["order"],
|
||||
defaultType: OrderRefundType,
|
||||
onSubmit: (data: OrderRefundSubmitData) => SubmitPromise
|
||||
onSubmit: (data: OrderRefundSubmitData) => SubmitPromise,
|
||||
disabled: boolean
|
||||
): UseOrderRefundFormResult {
|
||||
const {
|
||||
handleChange,
|
||||
|
@ -81,12 +83,15 @@ function useOrderRefundForm(
|
|||
hasChanged,
|
||||
triggerChange,
|
||||
data: formData,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = useForm(getOrderRefundPageFormData(defaultType), undefined, {
|
||||
confirmLeave: true
|
||||
});
|
||||
|
||||
const { setExitDialogSubmitRef } = useExitFormDialog();
|
||||
const { setExitDialogSubmitRef } = useExitFormDialog({
|
||||
formId
|
||||
});
|
||||
|
||||
const refundedProductQuantities = useFormset<null, string>(
|
||||
order?.lines
|
||||
|
@ -188,7 +193,8 @@ function useOrderRefundForm(
|
|||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const disabled = !order;
|
||||
const isSaveDisabled = disabled || !order;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
|
@ -201,7 +207,8 @@ function useOrderRefundForm(
|
|||
setMaximalRefundedProductQuantities: handleMaximalRefundedProductQuantitiesSet
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -209,9 +216,10 @@ const OrderRefundForm: React.FC<OrderRefundFormProps> = ({
|
|||
children,
|
||||
order,
|
||||
defaultType,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
disabled
|
||||
}) => {
|
||||
const props = useOrderRefundForm(order, defaultType, onSubmit);
|
||||
const props = useOrderRefundForm(order, defaultType, onSubmit, disabled);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -45,110 +45,97 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => {
|
|||
const intl = useIntl();
|
||||
return (
|
||||
<OrderRefundForm order={order} onSubmit={onSubmit}>
|
||||
{({ data, handlers, change, submit }) => {
|
||||
const {
|
||||
fulfilledItemsQuantities,
|
||||
waitingItemsQuantities,
|
||||
unfulfilledItemsQuantities
|
||||
} = data;
|
||||
|
||||
const hasAnyItemsSelected =
|
||||
fulfilledItemsQuantities.some(({ value }) => !!value) ||
|
||||
waitingItemsQuantities.some(({ value }) => !!value) ||
|
||||
unfulfilledItemsQuantities.some(({ value }) => !!value);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(messages.appTitle, {
|
||||
orderNumber: order?.number
|
||||
})}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
title={intl.formatMessage(messages.pageTitle, {
|
||||
orderNumber: order?.number
|
||||
})}
|
||||
/>
|
||||
<Grid>
|
||||
<div>
|
||||
{!!data.unfulfilledItemsQuantities.length && (
|
||||
<>
|
||||
{({ data, handlers, change, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(messages.appTitle, {
|
||||
orderNumber: order?.number
|
||||
})}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
title={intl.formatMessage(messages.pageTitle, {
|
||||
orderNumber: order?.number
|
||||
})}
|
||||
/>
|
||||
<Grid>
|
||||
<div>
|
||||
{!!data.unfulfilledItemsQuantities.length && (
|
||||
<>
|
||||
<ItemsCard
|
||||
errors={errors}
|
||||
order={order}
|
||||
lines={getUnfulfilledLines(order)}
|
||||
itemsQuantities={data.unfulfilledItemsQuantities}
|
||||
itemsSelections={data.itemsToBeReplaced}
|
||||
onChangeQuantity={handlers.changeUnfulfiledItemsQuantity}
|
||||
onSetMaxQuantity={
|
||||
handlers.handleSetMaximalUnfulfiledItemsQuantities
|
||||
}
|
||||
onChangeSelected={handlers.changeItemsToBeReplaced}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
{renderCollection(
|
||||
getWaitingFulfillments(order),
|
||||
({ id, lines }) => (
|
||||
<React.Fragment key={id}>
|
||||
<ItemsCard
|
||||
errors={errors}
|
||||
order={order}
|
||||
lines={getUnfulfilledLines(order)}
|
||||
itemsQuantities={data.unfulfilledItemsQuantities}
|
||||
fulfilmentId={id}
|
||||
lines={getParsedLines(lines)}
|
||||
itemsQuantities={data.waitingItemsQuantities}
|
||||
itemsSelections={data.itemsToBeReplaced}
|
||||
onChangeQuantity={handlers.changeUnfulfiledItemsQuantity}
|
||||
onSetMaxQuantity={
|
||||
handlers.handleSetMaximalUnfulfiledItemsQuantities
|
||||
}
|
||||
onChangeQuantity={handlers.changeWaitingItemsQuantity}
|
||||
onSetMaxQuantity={handlers.handleSetMaximalItemsQuantities(
|
||||
id
|
||||
)}
|
||||
onChangeSelected={handlers.changeItemsToBeReplaced}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
{renderCollection(
|
||||
getWaitingFulfillments(order),
|
||||
({ id, lines }) => (
|
||||
<React.Fragment key={id}>
|
||||
<ItemsCard
|
||||
errors={errors}
|
||||
order={order}
|
||||
fulfilmentId={id}
|
||||
lines={getParsedLines(lines)}
|
||||
itemsQuantities={data.waitingItemsQuantities}
|
||||
itemsSelections={data.itemsToBeReplaced}
|
||||
onChangeQuantity={handlers.changeWaitingItemsQuantity}
|
||||
onSetMaxQuantity={handlers.handleSetMaximalItemsQuantities(
|
||||
id
|
||||
)}
|
||||
onChangeSelected={handlers.changeItemsToBeReplaced}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</React.Fragment>
|
||||
)
|
||||
)}
|
||||
{renderCollection(
|
||||
getFulfilledFulfillemnts(order),
|
||||
({ id, lines }) => (
|
||||
<React.Fragment key={id}>
|
||||
<ItemsCard
|
||||
errors={errors}
|
||||
order={order}
|
||||
fulfilmentId={id}
|
||||
lines={getParsedLines(lines)}
|
||||
itemsQuantities={data.fulfilledItemsQuantities}
|
||||
itemsSelections={data.itemsToBeReplaced}
|
||||
onChangeQuantity={handlers.changeFulfiledItemsQuantity}
|
||||
onSetMaxQuantity={handlers.handleSetMaximalItemsQuantities(
|
||||
id
|
||||
)}
|
||||
onChangeSelected={handlers.changeItemsToBeReplaced}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</React.Fragment>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<OrderAmount
|
||||
allowNoRefund
|
||||
isReturn
|
||||
amountData={getReturnProductsAmountValues(order, data)}
|
||||
data={data}
|
||||
order={order}
|
||||
disableSubmitButton={!hasAnyItemsSelected}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
onRefund={submit}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}}
|
||||
</React.Fragment>
|
||||
)
|
||||
)}
|
||||
{renderCollection(
|
||||
getFulfilledFulfillemnts(order),
|
||||
({ id, lines }) => (
|
||||
<React.Fragment key={id}>
|
||||
<ItemsCard
|
||||
errors={errors}
|
||||
order={order}
|
||||
fulfilmentId={id}
|
||||
lines={getParsedLines(lines)}
|
||||
itemsQuantities={data.fulfilledItemsQuantities}
|
||||
itemsSelections={data.itemsToBeReplaced}
|
||||
onChangeQuantity={handlers.changeFulfiledItemsQuantity}
|
||||
onSetMaxQuantity={handlers.handleSetMaximalItemsQuantities(
|
||||
id
|
||||
)}
|
||||
onChangeSelected={handlers.changeItemsToBeReplaced}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</React.Fragment>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<OrderAmount
|
||||
allowNoRefund
|
||||
isReturn
|
||||
amountData={getReturnProductsAmountValues(order, data)}
|
||||
data={data}
|
||||
order={order}
|
||||
disableSubmitButton={isSaveDisabled}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
onRefund={submit}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</Container>
|
||||
)}
|
||||
</OrderRefundForm>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -85,12 +85,15 @@ function useOrderReturnForm(
|
|||
hasChanged,
|
||||
data: formData,
|
||||
triggerChange,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = useForm(getOrderRefundPageFormData(), undefined, {
|
||||
confirmLeave: true
|
||||
});
|
||||
|
||||
const { setExitDialogSubmitRef } = useExitFormDialog();
|
||||
const { setExitDialogSubmitRef } = useExitFormDialog({
|
||||
formId
|
||||
});
|
||||
|
||||
const unfulfiledItemsQuantites = useFormset<LineItemData, number>(
|
||||
getOrderUnfulfilledLines(order).map(getParsedLineData({ initialValue: 0 }))
|
||||
|
@ -241,6 +244,14 @@ function useOrderReturnForm(
|
|||
};
|
||||
}
|
||||
|
||||
const hasAnyItemsSelected =
|
||||
fulfiledItemsQuatities.data.some(({ value }) => !!value) ||
|
||||
waitingItemsQuantities.data.some(({ value }) => !!value) ||
|
||||
unfulfiledItemsQuantites.data.some(({ value }) => !!value);
|
||||
|
||||
const isSaveDisabled = !hasAnyItemsSelected;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
|
@ -259,7 +270,8 @@ function useOrderReturnForm(
|
|||
handleSetMaximalUnfulfiledItemsQuantities
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -43,8 +43,9 @@ const OrderSettingsPage: React.FC<OrderSettingsPageProps> = props => {
|
|||
orderSettings={orderSettings}
|
||||
shop={shop}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data, submit, hasChanged, change }) => (
|
||||
{({ data, submit, change, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.orders)}
|
||||
|
@ -73,7 +74,7 @@ const OrderSettingsPage: React.FC<OrderSettingsPageProps> = props => {
|
|||
<Savebar
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
|
|
|
@ -2,7 +2,10 @@ import {
|
|||
OrderSettingsFragment,
|
||||
ShopOrderSettingsFragment
|
||||
} from "@saleor/graphql";
|
||||
import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm";
|
||||
import useForm, {
|
||||
CommonUseFormResult,
|
||||
SubmitPromise
|
||||
} from "@saleor/hooks/useForm";
|
||||
import useHandleFormSubmit from "@saleor/hooks/useHandleFormSubmit";
|
||||
import React from "react";
|
||||
|
||||
|
@ -13,18 +16,15 @@ export interface OrderSettingsFormData {
|
|||
automaticallyFulfillNonShippableGiftCard: boolean;
|
||||
}
|
||||
|
||||
export interface UseOrderSettingsFormResult {
|
||||
change: FormChange;
|
||||
data: OrderSettingsFormData;
|
||||
hasChanged: boolean;
|
||||
submit: () => SubmitPromise<any[]>;
|
||||
}
|
||||
|
||||
export type UseOrderSettingsFormResult = CommonUseFormResult<
|
||||
OrderSettingsFormData
|
||||
>;
|
||||
export interface OrderSettingsFormProps {
|
||||
children: (props: UseOrderSettingsFormResult) => React.ReactNode;
|
||||
orderSettings: OrderSettingsFragment;
|
||||
shop: ShopOrderSettingsFragment;
|
||||
onSubmit: (data: OrderSettingsFormData) => SubmitPromise;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
function getOrderSeettingsFormData(
|
||||
|
@ -44,15 +44,19 @@ function getOrderSeettingsFormData(
|
|||
function useOrderSettingsForm(
|
||||
orderSettings: OrderSettingsFragment,
|
||||
shop: ShopOrderSettingsFragment,
|
||||
onSubmit: (data: OrderSettingsFormData) => SubmitPromise
|
||||
onSubmit: (data: OrderSettingsFormData) => SubmitPromise,
|
||||
disabled: boolean
|
||||
): UseOrderSettingsFormResult {
|
||||
const { data, handleChange, formId, hasChanged, setChanged } = useForm(
|
||||
getOrderSeettingsFormData(orderSettings, shop),
|
||||
undefined,
|
||||
{
|
||||
confirmLeave: true
|
||||
}
|
||||
);
|
||||
const {
|
||||
data,
|
||||
handleChange,
|
||||
formId,
|
||||
hasChanged,
|
||||
setChanged,
|
||||
setIsSubmitDisabled
|
||||
} = useForm(getOrderSeettingsFormData(orderSettings, shop), undefined, {
|
||||
confirmLeave: true
|
||||
});
|
||||
|
||||
const handleFormSubmit = useHandleFormSubmit({
|
||||
formId,
|
||||
|
@ -61,12 +65,15 @@ function useOrderSettingsForm(
|
|||
});
|
||||
|
||||
const submit = () => handleFormSubmit(data);
|
||||
const isSaveDisabled = disabled || !hasChanged;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -74,9 +81,10 @@ const OrderSettingsForm: React.FC<OrderSettingsFormProps> = ({
|
|||
children,
|
||||
orderSettings,
|
||||
shop,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
disabled
|
||||
}) => {
|
||||
const props = useOrderSettingsForm(orderSettings, shop, onSubmit);
|
||||
const props = useOrderSettingsForm(orderSettings, shop, onSubmit, disabled);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -58,8 +58,13 @@ const PageTypeCreatePage: React.FC<PageTypeCreatePageProps> = props => {
|
|||
} = useMetadataChangeTrigger();
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={formInitialData} onSubmit={onSubmit}>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={formInitialData}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, submit, isSaveDisabled }) => {
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -103,7 +108,7 @@ const PageTypeCreatePage: React.FC<PageTypeCreatePageProps> = props => {
|
|||
<Savebar
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
|
|
|
@ -110,8 +110,13 @@ const PageTypeDetailsPage: React.FC<PageTypeDetailsPageProps> = props => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={formInitialData} onSubmit={handleSubmit}>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={formInitialData}
|
||||
onSubmit={handleSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit }) => {
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -174,7 +179,7 @@ const PageTypeDetailsPage: React.FC<PageTypeDetailsPageProps> = props => {
|
|||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
|
|
|
@ -135,8 +135,9 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
|
|||
fetchMoreReferenceProducts={fetchMoreReferenceProducts}
|
||||
assignReferencesAttributeId={assignReferencesAttributeId}
|
||||
onSubmit={onSubmit}
|
||||
disabled={loading}
|
||||
>
|
||||
{({ change, data, valid, handlers, hasChanged, submit }) => (
|
||||
{({ change, data, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.pages)}
|
||||
|
@ -242,7 +243,7 @@ const PageDetailsPage: React.FC<PageDetailsPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={loading || !hasChanged || !valid}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onDelete={page === null ? undefined : onRemove}
|
||||
|
|
|
@ -97,6 +97,7 @@ export interface PageFormProps extends UsePageFormOpts {
|
|||
children: (props: UsePageUpdateFormResult) => React.ReactNode;
|
||||
page: PageDetailsFragment;
|
||||
onSubmit: (data: PageData) => SubmitPromise;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const getInitialFormData = (page?: PageDetailsFragment): PageFormData => ({
|
||||
|
@ -114,6 +115,7 @@ const getInitialFormData = (page?: PageDetailsFragment): PageFormData => ({
|
|||
function usePageForm(
|
||||
page: PageDetailsFragment,
|
||||
onSubmit: (data: PageData) => SubmitPromise,
|
||||
disabled: boolean,
|
||||
opts: UsePageFormOpts
|
||||
): UsePageUpdateFormResult {
|
||||
const pageExists = page !== null;
|
||||
|
@ -138,7 +140,7 @@ function usePageForm(
|
|||
confirmLeave: true
|
||||
});
|
||||
|
||||
const { setExitDialogSubmitRef } = useExitFormDialog({
|
||||
const { setExitDialogSubmitRef, setIsSubmitDisabled } = useExitFormDialog({
|
||||
formId
|
||||
});
|
||||
|
||||
|
@ -238,6 +240,9 @@ function usePageForm(
|
|||
|
||||
const valid = pageExists || !!opts.selectedPageType;
|
||||
|
||||
const isSaveDisabled = disabled || !hasChanged || !valid;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data: getData(),
|
||||
|
@ -255,7 +260,8 @@ function usePageForm(
|
|||
selectPageType: handlePageTypeSelect
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -263,9 +269,10 @@ const PageForm: React.FC<PageFormProps> = ({
|
|||
children,
|
||||
page,
|
||||
onSubmit,
|
||||
disabled,
|
||||
...rest
|
||||
}) => {
|
||||
const props = usePageForm(page, onSubmit, rest);
|
||||
const props = usePageForm(page, onSubmit, disabled, rest);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -55,8 +55,13 @@ const PermissionGroupCreatePage: React.FC<PermissionGroupCreatePageProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ data, change, submit, hasChanged }) => (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data, change, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
{intl.formatMessage(sectionNames.permissionGroups)}
|
||||
|
@ -95,7 +100,7 @@ const PermissionGroupCreatePage: React.FC<PermissionGroupCreatePageProps> = ({
|
|||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
</div>
|
||||
</Container>
|
||||
|
|
|
@ -75,8 +75,9 @@ const PluginsDetailsPage: React.FC<PluginsDetailsPageProps> = ({
|
|||
initial={initialFormData()}
|
||||
onSubmit={onSubmit}
|
||||
key={selectedChannelId}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data, hasChanged, submit, set }) => {
|
||||
{({ data, submit, set, isSaveDisabled }) => {
|
||||
const onChange = (event: ChangeEvent) => {
|
||||
const { name, value } = event.target;
|
||||
const newData = {
|
||||
|
@ -159,7 +160,7 @@ const PluginsDetailsPage: React.FC<PluginsDetailsPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -91,8 +91,13 @@ const ProductTypeCreatePage: React.FC<ProductTypeCreatePageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialData} onSubmit={onSubmit}>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialData}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit }) => {
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
const changeKind = makeProductTypeKindChangeHandler(
|
||||
|
@ -145,7 +150,7 @@ const ProductTypeCreatePage: React.FC<ProductTypeCreatePageProps> = ({
|
|||
<Savebar
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
|
|
|
@ -156,8 +156,13 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Form initial={formInitialData} onSubmit={handleSubmit} confirmLeave>
|
||||
{({ change, data, hasChanged, submit, setChanged }) => {
|
||||
<Form
|
||||
initial={formInitialData}
|
||||
onSubmit={handleSubmit}
|
||||
confirmLeave
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit, setChanged }) => {
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -256,7 +261,7 @@ const ProductTypeDetailsPage: React.FC<ProductTypeDetailsPageProps> = ({
|
|||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
|
|
|
@ -202,16 +202,9 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
fetchReferenceProducts={fetchReferenceProducts}
|
||||
fetchMoreReferenceProducts={fetchMoreReferenceProducts}
|
||||
assignReferencesAttributeId={assignReferencesAttributeId}
|
||||
loading={loading}
|
||||
>
|
||||
{({
|
||||
change,
|
||||
data,
|
||||
formErrors,
|
||||
disabled: formDisabled,
|
||||
handlers,
|
||||
hasChanged,
|
||||
submit
|
||||
}) => {
|
||||
{({ change, data, formErrors, handlers, submit, isSaveDisabled }) => {
|
||||
// Comparing explicitly to false because `hasVariants` can be undefined
|
||||
const isSimpleProduct = data.productType?.hasVariants === false;
|
||||
|
||||
|
@ -367,7 +360,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={loading || !onSubmit || formDisabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
{canOpenAssignReferencesAttributeDialog && (
|
||||
<AssignAttributeValueDialog
|
||||
|
|
|
@ -163,11 +163,13 @@ export interface ProductCreateFormProps extends UseProductCreateFormOpts {
|
|||
children: (props: UseProductCreateFormResult) => React.ReactNode;
|
||||
initial?: Partial<ProductCreateFormData>;
|
||||
onSubmit: (data: ProductCreateData) => SubmitPromise;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
function useProductCreateForm(
|
||||
initial: Partial<ProductCreateFormData>,
|
||||
onSubmit: (data: ProductCreateData) => SubmitPromise,
|
||||
loading: boolean,
|
||||
opts: UseProductCreateFormOpts
|
||||
): UseProductCreateFormResult {
|
||||
const intl = useIntl();
|
||||
|
@ -231,10 +233,6 @@ function useProductCreateForm(
|
|||
triggerChange
|
||||
});
|
||||
|
||||
const { setExitDialogSubmitRef } = useExitFormDialog({
|
||||
formId: PRODUCT_CREATE_FORM_ID
|
||||
});
|
||||
|
||||
const {
|
||||
makeChangeHandler: makeMetadataChangeHandler
|
||||
} = useMetadataChangeTrigger();
|
||||
|
@ -357,6 +355,10 @@ function useProductCreateForm(
|
|||
|
||||
const submit = () => handleFormSubmit(data);
|
||||
|
||||
const { setExitDialogSubmitRef, setIsSubmitDisabled } = useExitFormDialog({
|
||||
formId: PRODUCT_CREATE_FORM_ID
|
||||
});
|
||||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const shouldEnableSave = () => {
|
||||
|
@ -387,12 +389,15 @@ function useProductCreateForm(
|
|||
return true;
|
||||
};
|
||||
|
||||
const disabled = !shouldEnableSave();
|
||||
const isSaveEnabled = !shouldEnableSave();
|
||||
|
||||
const isSaveDisabled = loading || !onSubmit || isSaveEnabled || !hasChanged;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
disabled,
|
||||
disabled: isSaveEnabled,
|
||||
formErrors: form.errors,
|
||||
handlers: {
|
||||
addStock: handleStockAdd,
|
||||
|
@ -416,7 +421,8 @@ function useProductCreateForm(
|
|||
selectTaxRate: handleTaxTypeSelect
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -424,9 +430,10 @@ const ProductCreateForm: React.FC<ProductCreateFormProps> = ({
|
|||
children,
|
||||
initial,
|
||||
onSubmit,
|
||||
loading,
|
||||
...rest
|
||||
}) => {
|
||||
const props = useProductCreateForm(initial || {}, onSubmit, rest);
|
||||
const props = useProductCreateForm(initial || {}, onSubmit, loading, rest);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -284,16 +284,10 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
|||
fetchReferenceProducts={fetchReferenceProducts}
|
||||
fetchMoreReferenceProducts={fetchMoreReferenceProducts}
|
||||
assignReferencesAttributeId={assignReferencesAttributeId}
|
||||
disabled={disabled}
|
||||
hasChannelChanged={hasChannelChanged}
|
||||
>
|
||||
{({
|
||||
change,
|
||||
data,
|
||||
formErrors,
|
||||
disabled: formDisabled,
|
||||
handlers,
|
||||
hasChanged,
|
||||
submit
|
||||
}) => (
|
||||
{({ change, data, formErrors, handlers, submit, isSaveDisabled }) => (
|
||||
<>
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
|
@ -508,9 +502,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
|||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={
|
||||
disabled || formDisabled || (!hasChanged && !hasChannelChanged)
|
||||
}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
{canOpenAssignReferencesAttributeDialog && (
|
||||
<AssignAttributeValueDialog
|
||||
|
|
|
@ -157,7 +157,6 @@ export interface UseProductUpdateFormResult
|
|||
ProductUpdateData,
|
||||
ProductUpdateHandlers
|
||||
> {
|
||||
disabled: boolean;
|
||||
formErrors: FormErrors<ProductUpdateSubmitData>;
|
||||
}
|
||||
|
||||
|
@ -193,6 +192,8 @@ export interface ProductUpdateFormProps extends UseProductUpdateFormOpts {
|
|||
children: (props: UseProductUpdateFormResult) => React.ReactNode;
|
||||
product: ProductFragment;
|
||||
onSubmit: (data: ProductUpdateSubmitData) => SubmitPromise;
|
||||
disabled: boolean;
|
||||
hasChannelChanged: boolean;
|
||||
}
|
||||
|
||||
const getStocksData = (
|
||||
|
@ -222,6 +223,8 @@ const getStocksData = (
|
|||
function useProductUpdateForm(
|
||||
product: ProductFragment,
|
||||
onSubmit: (data: ProductUpdateSubmitData) => SubmitPromise,
|
||||
disabled: boolean,
|
||||
hasChannelChanged: boolean,
|
||||
opts: UseProductUpdateFormOpts
|
||||
): UseProductUpdateFormResult {
|
||||
const intl = useIntl();
|
||||
|
@ -244,7 +247,8 @@ function useProductUpdateForm(
|
|||
toggleValue,
|
||||
data: formData,
|
||||
setChanged,
|
||||
hasChanged
|
||||
hasChanged,
|
||||
setIsSubmitDisabled
|
||||
} = form;
|
||||
|
||||
const attributes = useFormset(getAttributeInputFromProduct(product));
|
||||
|
@ -435,12 +439,15 @@ function useProductUpdateForm(
|
|||
return true;
|
||||
};
|
||||
|
||||
const disabled = !shouldEnableSave();
|
||||
const isSaveEnabled = !shouldEnableSave();
|
||||
|
||||
const isSaveDisabled =
|
||||
disabled || isSaveEnabled || (!hasChanged && !hasChannelChanged);
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
disabled,
|
||||
formErrors: form.errors,
|
||||
handlers: {
|
||||
addStock: handleStockAdd,
|
||||
|
@ -464,7 +471,8 @@ function useProductUpdateForm(
|
|||
selectTaxRate: handleTaxTypeSelect
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -472,9 +480,17 @@ const ProductUpdateForm: React.FC<ProductUpdateFormProps> = ({
|
|||
children,
|
||||
product,
|
||||
onSubmit,
|
||||
disabled,
|
||||
hasChannelChanged,
|
||||
...rest
|
||||
}) => {
|
||||
const props = useProductUpdateForm(product, onSubmit, rest);
|
||||
const props = useProductUpdateForm(
|
||||
product,
|
||||
onSubmit,
|
||||
disabled,
|
||||
hasChannelChanged,
|
||||
rest
|
||||
);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -149,15 +149,9 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
|||
fetchReferenceProducts={fetchReferenceProducts}
|
||||
fetchMoreReferenceProducts={fetchMoreReferenceProducts}
|
||||
assignReferencesAttributeId={assignReferencesAttributeId}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({
|
||||
change,
|
||||
data,
|
||||
formErrors,
|
||||
disabled: formDisabled,
|
||||
handlers,
|
||||
submit
|
||||
}) => (
|
||||
{({ change, data, formErrors, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>{product?.name}</Backlink>
|
||||
<PageHeader title={header} />
|
||||
|
@ -258,7 +252,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || formDisabled || !onSubmit}
|
||||
disabled={isSaveDisabled}
|
||||
labels={{
|
||||
confirm: intl.formatMessage(messages.saveVariant),
|
||||
delete: intl.formatMessage(messages.deleteVariant)
|
||||
|
|
|
@ -94,6 +94,7 @@ export interface ProductVariantCreateFormProps
|
|||
children: (props: UseProductVariantCreateFormResult) => React.ReactNode;
|
||||
product: ProductVariantCreateDataQuery["product"];
|
||||
onSubmit: (data: ProductVariantCreateData) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const initial: ProductVariantCreateFormData = {
|
||||
|
@ -113,6 +114,7 @@ const initial: ProductVariantCreateFormData = {
|
|||
function useProductVariantCreateForm(
|
||||
product: ProductVariantCreateDataQuery["product"],
|
||||
onSubmit: (data: ProductVariantCreateData) => void,
|
||||
disabled: boolean,
|
||||
opts: UseProductVariantCreateFormOpts
|
||||
): UseProductVariantCreateFormResult {
|
||||
const intl = useIntl();
|
||||
|
@ -126,7 +128,8 @@ function useProductVariantCreateForm(
|
|||
handleChange,
|
||||
hasChanged,
|
||||
data: formData,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = form;
|
||||
|
||||
const attributes = useFormset(attributeInput);
|
||||
|
@ -227,13 +230,18 @@ function useProductVariantCreateForm(
|
|||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const formDisabled =
|
||||
data.isPreorder &&
|
||||
data.hasPreorderEndDate &&
|
||||
!!form.errors.preorderEndDateTime;
|
||||
|
||||
const isSaveDisabled = disabled || formDisabled || !onSubmit;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
disabled:
|
||||
data.isPreorder &&
|
||||
data.hasPreorderEndDate &&
|
||||
!!form.errors.preorderEndDateTime,
|
||||
disabled,
|
||||
formErrors: form.errors,
|
||||
handlers: {
|
||||
addStock: handleStockAdd,
|
||||
|
@ -250,7 +258,8 @@ function useProductVariantCreateForm(
|
|||
selectAttributeReference: handleAttributeReferenceChange
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -258,9 +267,10 @@ const ProductVariantCreateForm: React.FC<ProductVariantCreateFormProps> = ({
|
|||
children,
|
||||
product,
|
||||
onSubmit,
|
||||
disabled,
|
||||
...rest
|
||||
}) => {
|
||||
const props = useProductVariantCreateForm(product, onSubmit, rest);
|
||||
const props = useProductVariantCreateForm(product, onSubmit, disabled, rest);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -218,16 +218,9 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
fetchReferenceProducts={fetchReferenceProducts}
|
||||
fetchMoreReferenceProducts={fetchMoreReferenceProducts}
|
||||
assignReferencesAttributeId={assignReferencesAttributeId}
|
||||
loading={loading}
|
||||
>
|
||||
{({
|
||||
change,
|
||||
data,
|
||||
formErrors,
|
||||
disabled: formDisabled,
|
||||
handlers,
|
||||
hasChanged,
|
||||
submit
|
||||
}) => {
|
||||
{({ change, data, formErrors, isSaveDisabled, handlers, submit }) => {
|
||||
const nonSelectionAttributes = data.attributes.filter(
|
||||
byAttributeScope(VariantAttributeScope.NOT_VARIANT_SELECTION)
|
||||
);
|
||||
|
@ -373,7 +366,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={loading || formDisabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
|
|
|
@ -130,12 +130,14 @@ export interface ProductVariantUpdateFormProps
|
|||
extends UseProductVariantUpdateFormOpts {
|
||||
children: (props: UseProductVariantUpdateFormResult) => React.ReactNode;
|
||||
variant: ProductVariantFragment;
|
||||
loading: boolean;
|
||||
onSubmit: (data: ProductVariantUpdateSubmitData) => SubmitPromise;
|
||||
}
|
||||
|
||||
function useProductVariantUpdateForm(
|
||||
variant: ProductVariantFragment,
|
||||
onSubmit: (data: ProductVariantUpdateSubmitData) => SubmitPromise,
|
||||
loading: boolean,
|
||||
opts: UseProductVariantUpdateFormOpts
|
||||
): UseProductVariantUpdateFormResult {
|
||||
const intl = useIntl();
|
||||
|
@ -180,7 +182,8 @@ function useProductVariantUpdateForm(
|
|||
data: formData,
|
||||
setChanged,
|
||||
hasChanged,
|
||||
formId
|
||||
formId,
|
||||
setIsSubmitDisabled
|
||||
} = form;
|
||||
|
||||
const { setExitDialogSubmitRef } = useExitFormDialog({
|
||||
|
@ -332,6 +335,9 @@ function useProductVariantUpdateForm(
|
|||
|
||||
useEffect(() => setExitDialogSubmitRef(submit), [submit]);
|
||||
|
||||
const isSaveDisabled = loading || disabled || !hasChanged;
|
||||
setIsSubmitDisabled(isSaveDisabled);
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
|
@ -353,7 +359,8 @@ function useProductVariantUpdateForm(
|
|||
selectAttributeReference: handleAttributeReferenceChange
|
||||
},
|
||||
hasChanged,
|
||||
submit
|
||||
submit,
|
||||
isSaveDisabled
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -361,9 +368,10 @@ const ProductVariantUpdateForm: React.FC<ProductVariantUpdateFormProps> = ({
|
|||
children,
|
||||
variant,
|
||||
onSubmit,
|
||||
loading,
|
||||
...rest
|
||||
}) => {
|
||||
const props = useProductVariantUpdateForm(variant, onSubmit, rest);
|
||||
const props = useProductVariantUpdateForm(variant, onSubmit, loading, rest);
|
||||
|
||||
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||
};
|
||||
|
|
|
@ -122,10 +122,11 @@ export function createHandler(
|
|||
};
|
||||
|
||||
const result = await productCreate(productVariables);
|
||||
|
||||
let hasErrors = errors.length > 0;
|
||||
|
||||
const hasVariants = productType.hasVariants;
|
||||
const productId = result.data.productCreate.product?.id;
|
||||
const hasVariants = productType?.hasVariants;
|
||||
const productId = result?.data?.productCreate?.product?.id;
|
||||
|
||||
if (!productId) {
|
||||
return { errors };
|
||||
|
|
|
@ -66,8 +66,13 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ change, data, hasChanged, submit }) => (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit }) => (
|
||||
<>
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
|
@ -105,7 +110,7 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
|
|
|
@ -124,8 +124,13 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
|
|||
} = useMetadataChangeTrigger();
|
||||
|
||||
return (
|
||||
<Form initial={initialForm} onSubmit={onSubmit} confirmLeave>
|
||||
{({ change, data, hasChanged, submit, toggleValue }) => {
|
||||
<Form
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
confirmLeave
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit, toggleValue }) => {
|
||||
const handleWarehouseChange = createMultiAutocompleteSelectHandler(
|
||||
toggleValue,
|
||||
setWarehouseDisplayValues,
|
||||
|
@ -215,7 +220,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ChannelShippingData } from "@saleor/channels/utils";
|
|||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import ChannelsAvailabilityCard from "@saleor/components/ChannelsAvailabilityCard";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Form, { FormDataWithOpts } from "@saleor/components/Form";
|
||||
import { WithFormId } from "@saleor/components/Form/ExitFormDialogProvider";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
|
@ -86,22 +86,30 @@ export const ShippingZoneRatesCreatePage: React.FC<ShippingZoneRatesCreatePagePr
|
|||
type: null
|
||||
};
|
||||
|
||||
const checkIfSaveIsDisabled = (
|
||||
data: FormDataWithOpts<ShippingZoneRateCommonFormData>
|
||||
) => {
|
||||
const formDisabled = data.channelListings?.some(channel =>
|
||||
validatePrice(channel.price)
|
||||
);
|
||||
|
||||
return disabled || formDisabled || (!data.hasChanged && !hasChannelChanged);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
formId={formId}
|
||||
checkIfSaveIsDisabled={checkIfSaveIsDisabled}
|
||||
>
|
||||
{({ change, data, hasChanged, submit, triggerChange, set }) => {
|
||||
{({ change, data, isSaveDisabled, submit, triggerChange, set }) => {
|
||||
const handleChannelsChange = createChannelsChangeHandler(
|
||||
shippingChannels,
|
||||
onChannelsChange,
|
||||
triggerChange
|
||||
);
|
||||
const formDisabled = data.channelListings?.some(channel =>
|
||||
validatePrice(channel.price)
|
||||
);
|
||||
const onDescriptionChange = (description: OutputData) => {
|
||||
set({ description });
|
||||
triggerChange();
|
||||
|
@ -181,9 +189,7 @@ export const ShippingZoneRatesCreatePage: React.FC<ShippingZoneRatesCreatePagePr
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={
|
||||
disabled || formDisabled || (!hasChanged && !hasChannelChanged)
|
||||
}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ChannelShippingData } from "@saleor/channels/utils";
|
|||
import CardSpacer from "@saleor/components/CardSpacer";
|
||||
import ChannelsAvailabilityCard from "@saleor/components/ChannelsAvailabilityCard";
|
||||
import Container from "@saleor/components/Container";
|
||||
import Form from "@saleor/components/Form";
|
||||
import Form, { FormDataWithOpts } from "@saleor/components/Form";
|
||||
import { WithFormId } from "@saleor/components/Form/ExitFormDialogProvider";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import Metadata from "@saleor/components/Metadata/Metadata";
|
||||
|
@ -112,30 +112,38 @@ export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
|
|||
makeChangeHandler: makeMetadataChangeHandler
|
||||
} = useMetadataChangeTrigger();
|
||||
|
||||
const checkIfSaveIsDisabled = (
|
||||
data: FormDataWithOpts<ShippingZoneRateUpdateFormData>
|
||||
) => {
|
||||
const formDisabled = data.channelListings?.some(channel =>
|
||||
validatePrice(channel.price)
|
||||
);
|
||||
const formIsUnchanged =
|
||||
!data.hasChanged && !hasChannelChanged && !havePostalCodesChanged;
|
||||
|
||||
return disabled || formDisabled || formIsUnchanged;
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
formId={formId}
|
||||
checkIfSaveIsDisabled={checkIfSaveIsDisabled}
|
||||
>
|
||||
{({ change, data, hasChanged, submit, set, triggerChange }) => {
|
||||
{({ change, data, isSaveDisabled, submit, set, triggerChange }) => {
|
||||
const handleChannelsChange = createChannelsChangeHandler(
|
||||
shippingChannels,
|
||||
onChannelsChange,
|
||||
triggerChange
|
||||
);
|
||||
const formDisabled = data.channelListings?.some(channel =>
|
||||
validatePrice(channel.price)
|
||||
);
|
||||
const onDescriptionChange = (description: OutputData) => {
|
||||
set({ description });
|
||||
triggerChange();
|
||||
};
|
||||
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
const formIsUnchanged =
|
||||
!hasChanged && !hasChannelChanged && !havePostalCodesChanged;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
|
@ -212,7 +220,7 @@ export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || formDisabled || formIsUnchanged}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -134,8 +134,9 @@ const SiteSettingsPage: React.FC<SiteSettingsPageProps> = props => {
|
|||
return submitFunc(data);
|
||||
}}
|
||||
confirmLeave
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, hasChanged, submit }) => {
|
||||
{({ change, data, isSaveDisabled, submit }) => {
|
||||
const countryChoices = mapCountriesToChoices(shop?.countries || []);
|
||||
const handleCountryChange = createSingleAutocompleteSelectHandler(
|
||||
change,
|
||||
|
@ -201,7 +202,7 @@ const SiteSettingsPage: React.FC<SiteSettingsPageProps> = props => {
|
|||
</Grid>
|
||||
<Savebar
|
||||
state={saveButtonBarState}
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
|
|
|
@ -101,8 +101,13 @@ const StaffDetailsPage: React.FC<StaffDetailsPageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ data: formData, change, hasChanged, submit, toggleValue }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ data: formData, change, isSaveDisabled, submit, toggleValue }) => {
|
||||
const permissionGroupsChange = createMultiAutocompleteSelectHandler(
|
||||
toggleValue,
|
||||
setPermissionGroupsDisplayValues,
|
||||
|
@ -187,7 +192,7 @@ const StaffDetailsPage: React.FC<StaffDetailsPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -46,8 +46,13 @@ const CountryListPage: React.FC<CountryListPageProps> = ({
|
|||
showGross: maybe(() => shop.displayGrossPrices, false)
|
||||
};
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ change, data, hasChanged, submit }) => (
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={onSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit }) => (
|
||||
<>
|
||||
<Container>
|
||||
<Backlink onClick={onBack}>
|
||||
|
@ -77,7 +82,7 @@ const CountryListPage: React.FC<CountryListPageProps> = ({
|
|||
</Grid>
|
||||
</Container>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onSubmit={submit}
|
||||
|
|
|
@ -84,8 +84,13 @@ const WarehouseDetailsPage: React.FC<WarehouseDetailsPageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Form confirmLeave initial={initialForm} onSubmit={handleSubmit}>
|
||||
{({ change, data, hasChanged, submit, set }) => {
|
||||
<Form
|
||||
confirmLeave
|
||||
initial={initialForm}
|
||||
onSubmit={handleSubmit}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, isSaveDisabled, submit, set }) => {
|
||||
const countryChoices = mapCountriesToChoices(countries);
|
||||
const handleCountryChange = createSingleAutocompleteSelectHandler(
|
||||
change,
|
||||
|
@ -134,7 +139,7 @@ const WarehouseDetailsPage: React.FC<WarehouseDetailsPageProps> = ({
|
|||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={disabled || !hasChanged}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
|
|
Loading…
Reference in a new issue