Refactor leave action in product update
This commit is contained in:
parent
4aefafda9c
commit
b9bccd07f3
5 changed files with 56 additions and 50 deletions
|
@ -1,12 +1,14 @@
|
||||||
import useForm, { UseFormResult } from "@saleor/hooks/useForm";
|
import useForm, { UseFormResult } from "@saleor/hooks/useForm";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import LeaveScreenDialog from "../LeaveScreenDialog";
|
||||||
|
|
||||||
export interface FormProps<T> {
|
export interface FormProps<T> {
|
||||||
children: (props: UseFormResult<T>) => React.ReactNode;
|
children: (props: UseFormResult<T>) => React.ReactNode;
|
||||||
confirmLeave?: boolean;
|
confirmLeave?: boolean;
|
||||||
initial?: T;
|
initial?: T;
|
||||||
resetOnSubmit?: boolean;
|
resetOnSubmit?: boolean;
|
||||||
onSubmit?: (data: T) => void;
|
onSubmit?: (data: T, nextAction: () => void) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Form<T>(props: FormProps<T>) {
|
function Form<T>(props: FormProps<T>) {
|
||||||
|
@ -32,7 +34,18 @@ function Form<T>(props: FormProps<T>) {
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
return <form onSubmit={handleSubmit}>{children(renderProps)}</form>;
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
{children(renderProps)}
|
||||||
|
<LeaveScreenDialog
|
||||||
|
onSaveChanges={renderProps.submit}
|
||||||
|
onRejectChanges={renderProps.leaveAction}
|
||||||
|
onClose={() => renderProps.askToLeave(null)}
|
||||||
|
open={!!renderProps.leaveModal}
|
||||||
|
confirmButtonState="default"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Form.displayName = "Form";
|
Form.displayName = "Form";
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,9 @@ export interface ChangeEvent<TData = any> {
|
||||||
export type FormChange = (event: ChangeEvent, cb?: () => void) => void;
|
export type FormChange = (event: ChangeEvent, cb?: () => void) => void;
|
||||||
|
|
||||||
export interface UseFormResult<T> {
|
export interface UseFormResult<T> {
|
||||||
|
askToLeave: (action: () => void | null) => void;
|
||||||
|
leaveAction: () => void | null;
|
||||||
|
leaveModal: boolean;
|
||||||
change: FormChange;
|
change: FormChange;
|
||||||
data: T;
|
data: T;
|
||||||
hasChanged: boolean;
|
hasChanged: boolean;
|
||||||
|
@ -51,13 +54,15 @@ function handleRefresh<T extends FormData>(
|
||||||
|
|
||||||
function useForm<T extends FormData>(
|
function useForm<T extends FormData>(
|
||||||
initial: T,
|
initial: T,
|
||||||
onSubmit: (data: T) => void
|
onSubmit: (data: T, nextAction: () => void) => void
|
||||||
): UseFormResult<T> {
|
): UseFormResult<T> {
|
||||||
const [hasChanged, setChanged] = useState(false);
|
const [hasChanged, setChanged] = useState(false);
|
||||||
const [data, setData] = useStateFromProps(initial, {
|
const [data, setData] = useStateFromProps(initial, {
|
||||||
mergeFunc: merge,
|
mergeFunc: merge,
|
||||||
onRefresh: newData => handleRefresh(data, newData, setChanged)
|
onRefresh: newData => handleRefresh(data, newData, setChanged)
|
||||||
});
|
});
|
||||||
|
const [leaveModal, setLeaveModal] = useState<boolean>(false);
|
||||||
|
const [leaveAction, setLeaveAction] = useState<() => void>(null);
|
||||||
|
|
||||||
function toggleValue(event: ChangeEvent, cb?: () => void) {
|
function toggleValue(event: ChangeEvent, cb?: () => void) {
|
||||||
const { name, value } = event.target;
|
const { name, value } = event.target;
|
||||||
|
@ -107,17 +112,26 @@ function useForm<T extends FormData>(
|
||||||
}
|
}
|
||||||
|
|
||||||
function submit() {
|
function submit() {
|
||||||
return onSubmit(data);
|
onSubmit(data, leaveAction);
|
||||||
|
setLeaveModal(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function triggerChange() {
|
function triggerChange() {
|
||||||
setChanged(true);
|
setChanged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function askToLeave(action: () => void | null) {
|
||||||
|
setLeaveModal(() => !!action);
|
||||||
|
setLeaveAction(() => action);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
askToLeave,
|
||||||
change,
|
change,
|
||||||
data,
|
data,
|
||||||
hasChanged,
|
hasChanged,
|
||||||
|
leaveAction,
|
||||||
|
leaveModal,
|
||||||
reset,
|
reset,
|
||||||
set,
|
set,
|
||||||
submit,
|
submit,
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import Form from "@saleor/components/Form";
|
import Form from "@saleor/components/Form";
|
||||||
import Grid from "@saleor/components/Grid";
|
import Grid from "@saleor/components/Grid";
|
||||||
import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog";
|
|
||||||
import Metadata from "@saleor/components/Metadata/Metadata";
|
import Metadata from "@saleor/components/Metadata/Metadata";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||||
|
@ -57,8 +56,6 @@ import ProductStocks, { ProductStockInput } from "../ProductStocks";
|
||||||
import ProductTaxes from "../ProductTaxes";
|
import ProductTaxes from "../ProductTaxes";
|
||||||
import ProductVariants from "../ProductVariants";
|
import ProductVariants from "../ProductVariants";
|
||||||
|
|
||||||
export type ProductUpdatePageSubmitNextAction = "warehouse-configure";
|
|
||||||
|
|
||||||
export interface ProductUpdatePageProps extends ListActions {
|
export interface ProductUpdatePageProps extends ListActions {
|
||||||
defaultWeightUnit: string;
|
defaultWeightUnit: string;
|
||||||
errors: ProductErrorFragment[];
|
errors: ProductErrorFragment[];
|
||||||
|
@ -75,7 +72,6 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
warehouses: WarehouseFragment[];
|
warehouses: WarehouseFragment[];
|
||||||
taxTypes: TaxTypeFragment[];
|
taxTypes: TaxTypeFragment[];
|
||||||
submitNextAction?: ProductUpdatePageSubmitNextAction;
|
|
||||||
fetchCategories: (query: string) => void;
|
fetchCategories: (query: string) => void;
|
||||||
fetchCollections: (query: string) => void;
|
fetchCollections: (query: string) => void;
|
||||||
onVariantsAdd: () => void;
|
onVariantsAdd: () => void;
|
||||||
|
@ -88,13 +84,10 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
onImageReorder?(event: { oldIndex: number; newIndex: number });
|
onImageReorder?(event: { oldIndex: number; newIndex: number });
|
||||||
onImageUpload(file: File);
|
onImageUpload(file: File);
|
||||||
onSeoClick?();
|
onSeoClick?();
|
||||||
onSubmit?(
|
onSubmit?(data: ProductUpdatePageSubmitData, nextAction?: () => void);
|
||||||
data: ProductUpdatePageSubmitData,
|
|
||||||
nextAction?: ProductUpdatePageSubmitNextAction
|
|
||||||
);
|
|
||||||
onSubmitSkip?(nextAction?: ProductUpdatePageSubmitNextAction);
|
|
||||||
onVariantAdd?();
|
onVariantAdd?();
|
||||||
onSetDefaultVariant();
|
onSetDefaultVariant();
|
||||||
|
onWarehouseConfigure();
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData {
|
export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData {
|
||||||
|
@ -131,12 +124,12 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
onImageUpload,
|
onImageUpload,
|
||||||
onSeoClick,
|
onSeoClick,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onSubmitSkip,
|
|
||||||
onVariantAdd,
|
onVariantAdd,
|
||||||
onVariantsAdd,
|
onVariantsAdd,
|
||||||
onSetDefaultVariant,
|
onSetDefaultVariant,
|
||||||
onVariantShow,
|
onVariantShow,
|
||||||
onVariantReorder,
|
onVariantReorder,
|
||||||
|
onWarehouseConfigure,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -200,11 +193,10 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
value: taxType.taxCode
|
value: taxType.taxCode
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
const [modalWithAction, setModalWithAction] = React.useState<
|
const handleSubmit = (
|
||||||
ProductUpdatePageSubmitNextAction
|
data: ProductUpdatePageFormData,
|
||||||
>(null);
|
nextAction: () => void
|
||||||
|
) => {
|
||||||
const handleSubmit = (data: ProductUpdatePageFormData) => {
|
|
||||||
const metadata = isMetadataModified ? data.metadata : undefined;
|
const metadata = isMetadataModified ? data.metadata : undefined;
|
||||||
const privateMetadata = isPrivateMetadataModified
|
const privateMetadata = isPrivateMetadataModified
|
||||||
? data.privateMetadata
|
? data.privateMetadata
|
||||||
|
@ -221,7 +213,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
removeStocks: [],
|
removeStocks: [],
|
||||||
updateStocks: []
|
updateStocks: []
|
||||||
},
|
},
|
||||||
modalWithAction
|
nextAction
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const dataStocks = stocks.map(stock => stock.id);
|
const dataStocks = stocks.map(stock => stock.id);
|
||||||
|
@ -245,15 +237,22 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
!stockDiff.added.some(addedStock => addedStock === stock.id)
|
!stockDiff.added.some(addedStock => addedStock === stock.id)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
modalWithAction
|
nextAction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
setModalWithAction(null);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form onSubmit={handleSubmit} initial={initialData} confirmLeave>
|
<Form onSubmit={handleSubmit} initial={initialData} confirmLeave>
|
||||||
{({ change, data, hasChanged, submit, triggerChange, toggleValue }) => {
|
{({
|
||||||
|
change,
|
||||||
|
data,
|
||||||
|
hasChanged,
|
||||||
|
submit,
|
||||||
|
triggerChange,
|
||||||
|
toggleValue,
|
||||||
|
askToLeave
|
||||||
|
}) => {
|
||||||
const handleCollectionSelect = createMultiAutocompleteSelectHandler(
|
const handleCollectionSelect = createMultiAutocompleteSelectHandler(
|
||||||
toggleValue,
|
toggleValue,
|
||||||
setSelectedCollections,
|
setSelectedCollections,
|
||||||
|
@ -393,9 +392,9 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
}}
|
}}
|
||||||
onWarehouseConfigure={() => {
|
onWarehouseConfigure={() => {
|
||||||
if (disabled || !onSubmit || !hasChanged) {
|
if (disabled || !onSubmit || !hasChanged) {
|
||||||
onSubmitSkip("warehouse-configure");
|
onWarehouseConfigure();
|
||||||
} else {
|
} else {
|
||||||
setModalWithAction("warehouse-configure");
|
askToLeave(onWarehouseConfigure);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -487,17 +486,6 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
state={saveButtonBarState}
|
state={saveButtonBarState}
|
||||||
disabled={disabled || !hasChanged}
|
disabled={disabled || !hasChanged}
|
||||||
/>
|
/>
|
||||||
<LeaveScreenDialog
|
|
||||||
onSaveChanges={() => {
|
|
||||||
submit();
|
|
||||||
}}
|
|
||||||
onRejectChanges={() => {
|
|
||||||
onSubmitSkip("warehouse-configure");
|
|
||||||
}}
|
|
||||||
onClose={() => setModalWithAction(null)}
|
|
||||||
open={modalWithAction === "warehouse-configure"}
|
|
||||||
confirmButtonState={saveButtonBarState}
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,9 +38,7 @@ import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import { getMutationState, maybe } from "../../../misc";
|
import { getMutationState, maybe } from "../../../misc";
|
||||||
import ProductUpdatePage, {
|
import ProductUpdatePage from "../../components/ProductUpdatePage";
|
||||||
ProductUpdatePageSubmitNextAction
|
|
||||||
} from "../../components/ProductUpdatePage";
|
|
||||||
import { useProductDetails } from "../../queries";
|
import { useProductDetails } from "../../queries";
|
||||||
import { ProductImageCreateVariables } from "../../types/ProductImageCreate";
|
import { ProductImageCreateVariables } from "../../types/ProductImageCreate";
|
||||||
import { ProductUpdate as ProductUpdateMutationResult } from "../../types/ProductUpdate";
|
import { ProductUpdate as ProductUpdateMutationResult } from "../../types/ProductUpdate";
|
||||||
|
@ -281,14 +279,6 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmitNextAction = (
|
|
||||||
nextAction?: ProductUpdatePageSubmitNextAction
|
|
||||||
) => {
|
|
||||||
if (nextAction === "warehouse-configure") {
|
|
||||||
navigate(warehouseListPath);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle title={maybe(() => data.product.name)} />
|
<WindowTitle title={maybe(() => data.product.name)} />
|
||||||
|
@ -316,11 +306,11 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
||||||
onImageReorder={handleImageReorder}
|
onImageReorder={handleImageReorder}
|
||||||
onSubmit={async (data, nextAction) => {
|
onSubmit={async (data, nextAction) => {
|
||||||
const errors = await handleSubmit(data);
|
const errors = await handleSubmit(data);
|
||||||
if (errors?.length === 0) {
|
if (errors?.length === 0 && nextAction) {
|
||||||
handleSubmitNextAction(nextAction);
|
nextAction();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSubmitSkip={handleSubmitNextAction}
|
onWarehouseConfigure={() => navigate(warehouseListPath)}
|
||||||
onVariantAdd={handleVariantAdd}
|
onVariantAdd={handleVariantAdd}
|
||||||
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
|
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
|
||||||
onVariantShow={variantId => () =>
|
onVariantShow={variantId => () =>
|
||||||
|
|
|
@ -39,6 +39,7 @@ const props: ProductUpdatePageProps = {
|
||||||
onVariantReorder: () => undefined,
|
onVariantReorder: () => undefined,
|
||||||
onVariantShow: () => undefined,
|
onVariantShow: () => undefined,
|
||||||
onVariantsAdd: () => undefined,
|
onVariantsAdd: () => undefined,
|
||||||
|
onWarehouseConfigure: () => undefined,
|
||||||
placeholderImage,
|
placeholderImage,
|
||||||
product,
|
product,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
|
|
Loading…
Reference in a new issue