Refactor leave action in product update

This commit is contained in:
Dawid Tarasiuk 2020-09-28 17:37:55 +02:00
parent 4aefafda9c
commit b9bccd07f3
5 changed files with 56 additions and 50 deletions

View file

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

View file

@ -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,

View file

@ -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>
</> </>
); );

View file

@ -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 => () =>

View file

@ -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",