From 9e8f453c99c3c950f2e2be93cc51cb1febb368f9 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Wed, 16 Sep 2020 13:55:49 +0200 Subject: [PATCH 01/16] Add no warehouses info in variant creator --- locale/defaultMessages.json | 4 + .../ProductVariantCreatorStock.tsx | 370 +++++++++--------- 2 files changed, 196 insertions(+), 178 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 546019980..3721f20ef 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -302,6 +302,10 @@ "context": "variant stock, header", "string": "Stock" }, + "productVariantCreatorWarehouseSectionDescription": { + "context": "no warehouses info", + "string": "There are no warehouses set up for your store. You can configure your variants without providing stock quantites." + }, "productVariantCreatorWarehouseSectionHeader": { "context": "header", "string": "Warehouses" diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx index 51770851a..11066b09b 100644 --- a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx @@ -123,192 +123,206 @@ const ProductVariantCreatorStock: React.FC = pr })} /> - {warehouses.length > 1 && ( + {!warehouses.length ? ( + + + + ) : ( <> - - - - - acc + attr.values.length, - 0 - ) - }} - /> - -
- {warehouses.map(warehouse => ( - a === b - )} - name={`warehouse:${warehouse.id}`} - label={warehouse.name} - onChange={() => onWarehouseToggle(warehouse.id)} - key={warehouse.id} - /> - ))} -
- -
- - - )} - - - - - } - label={intl.formatMessage({ - defaultMessage: "Apply single stock to all SKUs" - })} - onChange={() => onApplyToAllChange("all")} - /> - {data.stock.mode === "all" && ( -
- {data.warehouses.map((warehouseId, warehouseIndex) => ( -
- - { - warehouses.find(warehouse => warehouse.id === warehouseId) - .name - } - - - onApplyToAllStockChange( - parseInt(event.target.value, 10), - warehouseIndex - ) - } + {warehouses.length > 1 && ( + <> + + + + + acc + attr.values.length, + 0 + ) + }} + /> + +
+ {warehouses.map(warehouse => ( + a === b + )} + name={`warehouse:${warehouse.id}`} + label={warehouse.name} + onChange={() => onWarehouseToggle(warehouse.id)} + key={warehouse.id} + /> + ))}
- ))} -
- )} - - } - label={intl.formatMessage({ - defaultMessage: "Apply unique stock by attribute to each SKU" - })} - onChange={() => onApplyToAllChange("attribute")} - /> - {data.stock.mode === "attribute" && ( - <> - - onAttributeSelect(event.target.value)} + +
+ + + )} + + - {stockAttributeValues && ( - <> -
- -
-
-
- {data.stock.attribute && - data.warehouses.map(warehouseId => ( - - { - warehouses.find( - warehouse => warehouse.id === warehouseId - ).name - } - - ))} - {stockAttributeValues.map(attributeValue => ( - - {attributeValue.name} - {data.warehouses.map( - (warehouseId, warehouseIndex) => ( - value.slug === attributeValue.slug - ).value[warehouseIndex] - } - onChange={event => - onAttributeValueChange( - attributeValue.slug, - parseInt(event.target.value, 10), - warehouseIndex - ) - } - key={warehouseId} - /> - ) - )} - - ))} + + + } + label={intl.formatMessage({ + defaultMessage: "Apply single stock to all SKUs" + })} + onChange={() => onApplyToAllChange("all")} + /> + {data.stock.mode === "all" && ( +
+ {data.warehouses.map((warehouseId, warehouseIndex) => ( +
+ + { + warehouses.find( + warehouse => warehouse.id === warehouseId + ).name + } + + + onApplyToAllStockChange( + parseInt(event.target.value, 10), + warehouseIndex + ) + } + />
-
+ ))} +
+ )} + + } + label={intl.formatMessage({ + defaultMessage: "Apply unique stock by attribute to each SKU" + })} + onChange={() => onApplyToAllChange("attribute")} + /> + {data.stock.mode === "attribute" && ( + <> + + onAttributeSelect(event.target.value)} + /> + {stockAttributeValues && ( + <> +
+ +
+
+
+ {data.stock.attribute && + data.warehouses.map(warehouseId => ( + + { + warehouses.find( + warehouse => warehouse.id === warehouseId + ).name + } + + ))} + {stockAttributeValues.map(attributeValue => ( + + {attributeValue.name} + {data.warehouses.map( + (warehouseId, warehouseIndex) => ( + + value.slug === attributeValue.slug + ).value[warehouseIndex] + } + onChange={event => + onAttributeValueChange( + attributeValue.slug, + parseInt(event.target.value, 10), + warehouseIndex + ) + } + key={warehouseId} + /> + ) + )} + + ))} +
+
+ + )} + + )} + {data.stock.mode === "attribute" && !!data.stock.attribute && ( + <> + +
)} - - )} - {data.stock.mode === "attribute" && !!data.stock.attribute && ( - <> -
- - )} - - } - label={intl.formatMessage({ - defaultMessage: "Skip stock for now" - })} - onChange={() => onApplyToAllChange("skip")} - /> - + } + label={intl.formatMessage({ + defaultMessage: "Skip stock for now" + })} + onChange={() => onApplyToAllChange("skip")} + /> + + + )} ); From 4cb5a1114c4fb8566e4cdd7fb6c67427a5dbe6d7 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Wed, 16 Sep 2020 14:13:13 +0200 Subject: [PATCH 02/16] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4e55add7..968d274a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ All notable, unreleased changes to this project will be documented in this file. - Update schema with PositiveDecimal type - #695 by @AlicjaSzu - Add error info when fetching taxes - #701 by @dominik-zeglen - Fix return to previous page on screen size change - #710 by @orzechdev +- Fix updating order details on address change #711 - by @orzechdev +- Add no warehouses info in variant creator #713 - by @orzechdev - Add variants reordering possibility - #716 by @orzechdev - Fix avatar change button - #719 by @orzechdev - Add slug field to product, collection, category & page details (update and create) - #720 by @mmarkusik @@ -104,7 +106,6 @@ All notable, unreleased changes to this project will be documented in this file. - Update product stock management to newest design - #515 by @dominik-zeglen - Handle untracked products - #523 by @dominik-zeglen - Display correct error if there were no graphql errors - #525 by @dominik-zeglen -- Fix updating order details on address change #711 - by @orzechdev ## 2.0.0 From 6065c5ded6fdbcc65f21f530a538da0b448c8324 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Wed, 16 Sep 2020 19:13:08 +0200 Subject: [PATCH 03/16] Add no warehouses info in variant and product details --- locale/defaultMessages.json | 22 +- src/categories/views/CategoryDetails.tsx | 2 +- .../LeaveScreenDialog/LeaveScreenDialog.tsx | 54 ++++ src/components/LeaveScreenDialog/index.ts | 2 + .../Navigator/modes/commands/actions.ts | 2 +- .../ProductCreatePage/ProductCreatePage.tsx | 4 + .../ProductStocks/ProductStocks.tsx | 241 +++++++++++------- .../ProductUpdatePage/ProductUpdatePage.tsx | 4 + .../ProductVariantCreatePage.tsx | 6 +- .../ProductVariantCreatorStock.tsx | 2 +- .../ProductVariantPage/ProductVariantPage.tsx | 6 +- src/products/index.tsx | 27 +- src/products/urls.ts | 22 +- src/products/views/ProductCreate.tsx | 32 ++- .../views/ProductList/ProductList.tsx | 2 +- .../views/ProductUpdate/ProductUpdate.tsx | 10 + src/products/views/ProductVariant.tsx | 12 +- src/products/views/ProductVariantCreate.tsx | 29 ++- .../stories/products/ProductCreatePage.tsx | 3 + .../stories/products/ProductUpdatePage.tsx | 1 + .../products/ProductVariantCreatePage.tsx | 4 + .../stories/products/ProductVariantPage.tsx | 3 + 22 files changed, 370 insertions(+), 120 deletions(-) create mode 100644 src/components/LeaveScreenDialog/LeaveScreenDialog.tsx create mode 100644 src/components/LeaveScreenDialog/index.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 3721f20ef..640cd5f64 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -304,7 +304,7 @@ }, "productVariantCreatorWarehouseSectionDescription": { "context": "no warehouses info", - "string": "There are no warehouses set up for your store. You can configure your variants without providing stock quantites." + "string": "There are no warehouses set up for your store. You can configure variants without providing stock quantities." }, "productVariantCreatorWarehouseSectionHeader": { "context": "header", @@ -314,6 +314,22 @@ "context": "optional field", "string": "Optional" }, + "productVariantWarehouseSectionDescription": { + "context": "no warehouses info", + "string": "There are no warehouses set up for your store. To add stock quantity to the variant please" + }, + "productVariantWarehouseSectionDescriptionLink": { + "context": "no warehouses info", + "string": "configure a warehouse" + }, + "productWarehouseSectionDescription": { + "context": "no warehouses info", + "string": "There are no warehouses set up for your store. To add stock quantity to the product please" + }, + "productWarehouseSectionDescriptionLink": { + "context": "no warehouses info", + "string": "configure a warehouse" + }, "saleDetailsPageCategoriesQuantity": { "context": "number of categories", "string": "Categories ({quantity})" @@ -1577,6 +1593,10 @@ "context": "button", "string": "{languageName} - {languageCode}" }, + "src_dot_components_dot_LeaveScreenDialog_dot_3281163715": { + "context": "leaving screen warning message", + "string": "You're leaving this screen. Do you want to save previously made changes?" + }, "src_dot_components_dot_ListField_dot_3099331554": { "context": "button", "string": "Add" diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index cd52af91c..c24e11b89 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -211,7 +211,7 @@ export const CategoryDetails: React.FC = ({ disabled={loading} errors={updateResult.data?.categoryUpdate.errors || []} onAddCategory={() => navigate(categoryAddUrl(id))} - onAddProduct={() => navigate(productAddUrl)} + onAddProduct={() => navigate(productAddUrl())} onBack={() => navigate( maybe(() => categoryUrl(data.category.parent.id), categoryListUrl()) diff --git a/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx b/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx new file mode 100644 index 000000000..e15aba9f8 --- /dev/null +++ b/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx @@ -0,0 +1,54 @@ +import Button from "@material-ui/core/Button"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import { buttonMessages } from "@saleor/intl"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import ConfirmButton, { ConfirmButtonTransitionState } from "../ConfirmButton"; +import Form from "../Form"; + +export interface LeaveScreenDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + onClose: () => void; + onSubmit: () => void; +} + +const LeaveScreenDialog: React.FC = ({ + confirmButtonState, + onClose, + onSubmit, + open +}) => ( + +
+ {({ submit }) => ( + <> + + + + + + + + + + + )} +
+
+); +LeaveScreenDialog.displayName = "LeaveScreenDialog"; +export default LeaveScreenDialog; diff --git a/src/components/LeaveScreenDialog/index.ts b/src/components/LeaveScreenDialog/index.ts new file mode 100644 index 000000000..c6bfa632e --- /dev/null +++ b/src/components/LeaveScreenDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from "./LeaveScreenDialog"; +export * from "./LeaveScreenDialog"; diff --git a/src/components/Navigator/modes/commands/actions.ts b/src/components/Navigator/modes/commands/actions.ts index a10c60c5d..0e8fb31ca 100644 --- a/src/components/Navigator/modes/commands/actions.ts +++ b/src/components/Navigator/modes/commands/actions.ts @@ -46,7 +46,7 @@ export function searchInCommands( { label: intl.formatMessage(messages.createProduct), onClick: () => { - navigate(productAddUrl); + navigate(productAddUrl()); return false; } }, diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 031abec20..b96d702fd 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -101,6 +101,7 @@ interface ProductCreatePageProps { fetchCategories: (data: string) => void; fetchCollections: (data: string) => void; fetchProductTypes: (data: string) => void; + onWarehouseConfigure: () => void; onBack?(); onSubmit?(data: ProductCreatePageSubmitData); } @@ -122,6 +123,7 @@ export const ProductCreatePage: React.FC = ({ warehouses, taxTypes, onBack, + onWarehouseConfigure, fetchProductTypes, weightUnit, onSubmit @@ -297,6 +299,7 @@ export const ProductCreatePage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx index 1f36f6601..cab8dfe8f 100644 --- a/src/products/components/ProductStocks/ProductStocks.tsx +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -21,6 +21,7 @@ import CardTitle from "@saleor/components/CardTitle"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import FormSpacer from "@saleor/components/FormSpacer"; import Hr from "@saleor/components/Hr"; +import Link from "@saleor/components/Link"; import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment"; import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment"; import { FormChange } from "@saleor/hooks/useForm"; @@ -41,12 +42,14 @@ export interface ProductStocksProps { data: ProductStockFormData; disabled: boolean; errors: ProductErrorFragment[]; + hasVariants: boolean; stocks: ProductStockInput[]; warehouses: WarehouseFragment[]; onChange: FormsetChange; onFormDataChange: FormChange; onWarehouseStockAdd: (warehouseId: string) => void; onWarehouseStockDelete: (warehouseId: string) => void; + onWarehouseConfigure: () => void; } const useStyles = makeStyles( @@ -75,6 +78,9 @@ const useStyles = makeStyles( marginBottom: theme.spacing(2) } }, + noWarehouseInfo: { + marginTop: theme.spacing() + }, paper: { padding: theme.spacing(2) }, @@ -105,13 +111,15 @@ const useStyles = makeStyles( const ProductStocks: React.FC = ({ data, disabled, + hasVariants, errors, stocks, warehouses, onChange, onFormDataChange, onWarehouseStockAdd, - onWarehouseStockDelete + onWarehouseStockDelete, + onWarehouseConfigure }) => { const classes = useStyles({}); const intl = useIntl(); @@ -178,108 +186,147 @@ const ProductStocks: React.FC = ({
+ {!warehouses.length && ( + + {hasVariants ? ( + <> + {" "} + + + + + ) : ( + <> + {" "} + + + + + )} + + )} - - - - - - - - - - - - - - {renderCollection(stocks, stock => ( - - {stock.label} - - onChange(stock.id, event.target.value)} - value={stock.value} + {warehouses.length > 0 && ( +
+ + + + - - onWarehouseStockDelete(stock.id)} - > - - + + + - ))} - {warehousesToAssign.length > 0 && ( - - - - + + {renderCollection(stocks, stock => ( + + {stock.label} + + onChange(stock.id, event.target.value)} + value={stock.value} /> - - - - setExpansionState(false)}> -
- setExpansionState(!isExpanded)} - > - - - - {({ TransitionProps }) => ( - - - {warehousesToAssign.map(warehouse => ( - - onWarehouseStockAdd(warehouse.id) - } - > - {warehouse.name} - - ))} - - - )} - -
-
-
-
- )} - -
+ + + onWarehouseStockDelete(stock.id)} + > + + + + + ))} + {warehousesToAssign.length > 0 && ( + + + + + + + + setExpansionState(false)} + > +
+ setExpansionState(!isExpanded)} + > + + + + {({ TransitionProps }) => ( + + + {warehousesToAssign.map(warehouse => ( + + onWarehouseStockAdd(warehouse.id) + } + > + {warehouse.name} + + ))} + + + )} + +
+
+
+
+ )} + + + )} ); }; diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 79252b195..13e56879d 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -87,6 +87,7 @@ export interface ProductUpdatePageProps extends ListActions { onSubmit?(data: ProductUpdatePageSubmitData); onVariantAdd?(); onSetDefaultVariant(); + onWarehouseConfigure(); } export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData { @@ -128,6 +129,7 @@ export const ProductUpdatePage: React.FC = ({ onSetDefaultVariant, onVariantShow, onVariantReorder, + onWarehouseConfigure, isChecked, selected, toggle, @@ -346,6 +348,7 @@ export const ProductUpdatePage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> )} diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 1237d8b98..8c345e4f2 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -58,6 +58,7 @@ interface ProductVariantCreatePageProps { onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onVariantClick: (variantId: string) => void; onVariantReorder: ReorderAction; + onWarehouseConfigure: () => void; } const ProductVariantCreatePage: React.FC = ({ @@ -72,7 +73,8 @@ const ProductVariantCreatePage: React.FC = ({ onBack, onSubmit, onVariantClick, - onVariantReorder + onVariantReorder, + onWarehouseConfigure }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -165,6 +167,7 @@ const ProductVariantCreatePage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx index 11066b09b..c07bfa598 100644 --- a/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreatorStock.tsx @@ -126,7 +126,7 @@ const ProductVariantCreatorStock: React.FC = pr {!warehouses.length ? ( diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 4d7643521..d875acf0d 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -70,6 +70,7 @@ interface ProductVariantPageProps { onImageSelect(id: string); onVariantClick(variantId: string); onSetDefaultVariant(); + onWarehouseConfigure(); } const ProductVariantPage: React.FC = ({ @@ -88,7 +89,8 @@ const ProductVariantPage: React.FC = ({ onSubmit, onVariantClick, onVariantReorder, - onSetDefaultVariant + onSetDefaultVariant, + onWarehouseConfigure }) => { const attributeInput = React.useMemo( () => getAttributeInputFromVariant(variant), @@ -240,6 +242,7 @@ const ProductVariantPage: React.FC = ({ = ({ triggerChange(); removeStock(id); }} + onWarehouseConfigure={onWarehouseConfigure} /> diff --git a/src/products/index.tsx b/src/products/index.tsx index 46b4df70d..2855010e8 100644 --- a/src/products/index.tsx +++ b/src/products/index.tsx @@ -9,6 +9,7 @@ import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; import { productAddPath, + ProductAddUrlQueryParams, productImagePath, ProductImageUrlQueryParams, productListPath, @@ -17,11 +18,12 @@ import { productPath, ProductUrlQueryParams, productVariantAddPath, + ProductVariantAddUrlQueryParams, productVariantCreatorPath, productVariantEditPath, ProductVariantEditUrlQueryParams } from "./urls"; -import ProductCreate from "./views/ProductCreate"; +import ProductCreateComponent from "./views/ProductCreate"; import ProductImageComponent from "./views/ProductImage"; import ProductListComponent from "./views/ProductList"; import ProductUpdateComponent from "./views/ProductUpdate"; @@ -45,6 +47,13 @@ const ProductList: React.FC> = ({ location }) => { return ; }; +const ProductCreate: React.FC = () => { + const qs = parseQs(location.search.substr(1)); + const params: ProductAddUrlQueryParams = qs; + + return ; +}; + const ProductUpdate: React.FC> = ({ match }) => { const qs = parseQs(location.search.substr(1)); const params: ProductUrlQueryParams = qs; @@ -91,11 +100,17 @@ const ProductImage: React.FC> = ({ const ProductVariantCreate: React.FC> = ({ match -}) => ( - -); +}) => { + const qs = parseQs(location.search.substr(1)); + const params: ProductVariantAddUrlQueryParams = qs; + + return ( + + ); +}; const ProductVariantCreator: React.FC; +export const productAddUrl = (params?: ProductAddUrlQueryParams) => + productAddPath + "?" + stringifyQs(params); export const productListPath = productSection; export type ProductListUrlDialog = @@ -66,14 +69,14 @@ export const productListUrl = (params?: ProductListUrlQueryParams): string => productListPath + "?" + stringifyQs(params); export const productPath = (id: string) => urlJoin(productSection + id); -export type ProductUrlDialog = "remove" | "remove-variants"; +export type ProductUrlDialog = "remove" | "remove-variants" | "leave-screen"; export type ProductUrlQueryParams = BulkAction & Dialog; export const productUrl = (id: string, params?: ProductUrlQueryParams) => productPath(encodeURIComponent(id)) + "?" + stringifyQs(params); export const productVariantEditPath = (productId: string, variantId: string) => urlJoin(productSection, productId, "variant", variantId); -export type ProductVariantEditUrlDialog = "remove"; +export type ProductVariantEditUrlDialog = "remove" | "leave-screen"; export type ProductVariantEditUrlQueryParams = Dialog< ProductVariantEditUrlDialog >; @@ -96,8 +99,17 @@ export const productVariantCreatorUrl = (productId: string) => export const productVariantAddPath = (productId: string) => urlJoin(productSection, productId, "variant/add"); -export const productVariantAddUrl = (productId: string): string => - productVariantAddPath(encodeURIComponent(productId)); +export type ProductVariantAddUrlDialog = "leave-screen"; +export type ProductVariantAddUrlQueryParams = Dialog< + ProductVariantAddUrlDialog +>; +export const productVariantAddUrl = ( + productId: string, + params?: ProductVariantAddUrlQueryParams +): string => + productVariantAddPath(encodeURIComponent(productId)) + + "?" + + stringifyQs(params); export const productImagePath = (productId: string, imageId: string) => urlJoin(productSection, productId, "image", imageId); diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 138e24e38..ff7c342a7 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -1,3 +1,4 @@ +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; @@ -8,12 +9,14 @@ import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; import { useTaxTypeList } from "@saleor/taxes/queries"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseListPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -25,9 +28,22 @@ import { useProductCreateMutation, useProductSetAvailabilityForPurchase } from "../mutations"; -import { productListUrl, productUrl } from "../urls"; +import { + productAddUrl, + ProductAddUrlDialog, + ProductAddUrlQueryParams, + productListUrl, + productUrl +} from "../urls"; -export const ProductCreateView: React.FC = () => { +interface ProductCreateViewProps { + params: ProductAddUrlQueryParams; +} + +export const ProductCreateView: React.FC = ({ + params +}) => { + const { action } = params; const navigate = useNavigator(); const notify = useNotifier(); const shop = useShop(); @@ -90,6 +106,11 @@ export const ProductCreateView: React.FC = () => { } }); + const [openModal, closeModal] = createDialogActionHandlers< + ProductAddUrlDialog, + ProductAddUrlQueryParams + >(navigate, productAddUrl, params); + const handleCreate = async (formData: ProductCreatePageSubmitData) => { const result = await productCreate({ variables: { @@ -196,12 +217,19 @@ export const ProductCreateView: React.FC = () => { loading: searchProductTypesOpts.loading, onFetchMore: loadMoreProductTypes }} + onWarehouseConfigure={() => openModal("leave-screen")} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } taxTypes={taxTypes.data?.taxTypes || []} weightUnit={shop?.defaultWeightUnit} /> + navigate(warehouseListPath)} + onClose={closeModal} + open={action === "leave-screen"} + confirmButtonState="default" + /> ); }; diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index e25ca42eb..e032cc944 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -344,7 +344,7 @@ export const ProductList: React.FC = ({ params }) => { () => attributes.data.availableInGrid.pageInfo.hasNextPage, false )} - onAdd={() => navigate(productAddUrl)} + onAdd={() => navigate(productAddUrl())} disabled={loading} products={maybe(() => data.products.edges.map(edge => edge.node))} onFetchMore={() => diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 4b03c68d6..7713b9e0d 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -3,6 +3,7 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; import ActionDialog from "@saleor/components/ActionDialog"; +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; @@ -33,6 +34,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseListPath } from "@saleor/warehouses/urls"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -64,6 +66,7 @@ interface ProductUpdateProps { } export const ProductUpdate: React.FC = ({ id, params }) => { + const { action } = params; const navigate = useNavigator(); const notify = useNotifier(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( @@ -312,6 +315,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onImageUpload={handleImageUpload} onImageEdit={handleImageEdit} onImageDelete={handleImageDelete} + onWarehouseConfigure={() => openModal("leave-screen")} toolbar={ = ({ id, params }) => { /> + navigate(warehouseListPath)} + onClose={closeModal} + open={action === "leave-screen"} + confirmButtonState="default" + /> ); }; diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index 845a92246..d972cce90 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -1,4 +1,5 @@ import placeholderImg from "@assets/images/placeholder255x255.png"; +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; @@ -13,6 +14,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseListPath } from "@saleor/warehouses/urls"; import React, { useEffect, useState } from "react"; import { useIntl } from "react-intl"; @@ -51,6 +53,7 @@ export const ProductVariant: React.FC = ({ productId, params }) => { + const { action } = params; const shop = useShop(); const navigate = useNavigator(); const notify = useNotifier(); @@ -78,7 +81,7 @@ export const ProductVariant: React.FC = ({ const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); - const [openModal] = createDialogActionHandlers< + const [openModal, closeModal] = createDialogActionHandlers< ProductVariantEditUrlDialog, ProductVariantEditUrlQueryParams >( @@ -220,6 +223,7 @@ export const ProductVariant: React.FC = ({ navigate(productVariantEditUrl(productId, variantId)); }} onVariantReorder={handleVariantReorder} + onWarehouseConfigure={() => openModal("leave-screen")} /> = ({ open={params.action === "remove"} name={data?.productVariant?.name} /> + navigate(warehouseListPath)} + onClose={closeModal} + open={action === "leave-screen"} + confirmButtonState="default" + /> ); }; diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 6d4df390d..b758ac83e 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -1,15 +1,18 @@ +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; +import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; +import { warehouseListPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -22,16 +25,26 @@ import { useVariantCreateMutation } from "../mutations"; import { useProductVariantCreateQuery } from "../queries"; -import { productListUrl, productUrl, productVariantEditUrl } from "../urls"; +import { + productListUrl, + productUrl, + productVariantAddUrl, + ProductVariantAddUrlDialog, + ProductVariantAddUrlQueryParams, + productVariantEditUrl +} from "../urls"; import { createVariantReorderHandler } from "./ProductUpdate/handlers"; interface ProductVariantCreateProps { productId: string; + params: ProductVariantAddUrlQueryParams; } export const ProductVariant: React.FC = ({ - productId + productId, + params }) => { + const { action } = params; const navigate = useNavigator(); const notify = useNotifier(); const shop = useShop(); @@ -82,6 +95,11 @@ export const ProductVariant: React.FC = ({ reorderProductVariants({ variables }) ); + const [openModal, closeModal] = createDialogActionHandlers< + ProductVariantAddUrlDialog, + ProductVariantAddUrlQueryParams + >(navigate, params => productVariantAddUrl(productId, params), params); + const handleBack = () => navigate(productUrl(productId)); const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => { const result = await variantCreate({ @@ -143,12 +161,19 @@ export const ProductVariant: React.FC = ({ onSubmit={handleSubmit} onVariantClick={handleVariantClick} onVariantReorder={handleVariantReorder} + onWarehouseConfigure={() => openModal("leave-screen")} saveButtonBarState={variantCreateResult.status} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } weightUnit={shop?.defaultWeightUnit} /> + navigate(warehouseListPath)} + onClose={closeModal} + open={action === "leave-screen"} + confirmButtonState="default" + /> ); }; diff --git a/src/storybook/stories/products/ProductCreatePage.tsx b/src/storybook/stories/products/ProductCreatePage.tsx index babd8504a..ef80d8076 100644 --- a/src/storybook/stories/products/ProductCreatePage.tsx +++ b/src/storybook/stories/products/ProductCreatePage.tsx @@ -37,6 +37,7 @@ storiesOf("Views / Products / Create product", module) warehouses={warehouseList} taxTypes={taxTypes} weightUnit="kg" + onWarehouseConfigure={() => undefined} /> )) .add("When loading", () => ( @@ -60,6 +61,7 @@ storiesOf("Views / Products / Create product", module) warehouses={undefined} taxTypes={taxTypes} weightUnit="kg" + onWarehouseConfigure={() => undefined} /> )) .add("form errors", () => ( @@ -89,5 +91,6 @@ storiesOf("Views / Products / Create product", module) warehouses={warehouseList} taxTypes={taxTypes} weightUnit="kg" + onWarehouseConfigure={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index c44841a1b..980d68a84 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -39,6 +39,7 @@ const props: ProductUpdatePageProps = { onVariantReorder: () => undefined, onVariantShow: () => undefined, onVariantsAdd: () => undefined, + onWarehouseConfigure: () => undefined, placeholderImage, product, saveButtonBarState: "default", diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index d8578724d..d0fdbea77 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -26,6 +26,7 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("with errors", () => ( @@ -58,6 +59,7 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("when loading data", () => ( @@ -74,6 +76,7 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("add first variant", () => ( @@ -93,5 +96,6 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index 038c03372..a70095d78 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -28,6 +28,7 @@ storiesOf("Views / Products / Product variant details", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("when loading data", () => ( @@ -47,6 +48,7 @@ storiesOf("Views / Products / Product variant details", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("attribute errors", () => ( @@ -82,5 +84,6 @@ storiesOf("Views / Products / Product variant details", module) ...error }))} warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )); From 1aaa5c5c3a91261760f6a1b0e36c559bdb1f8ca1 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Mon, 21 Sep 2020 18:29:50 +0200 Subject: [PATCH 04/16] Add redirect on warehouse configuration action --- .../LeaveScreenDialog/LeaveScreenDialog.tsx | 58 ++++++++++--------- src/intl.ts | 8 +++ .../ProductCreatePage/ProductCreatePage.tsx | 31 ++++++++-- .../ProductUpdatePage/ProductUpdatePage.tsx | 30 +++++++++- .../ProductVariantCreatePage.tsx | 33 ++++++++++- .../ProductVariantPage/ProductVariantPage.tsx | 29 +++++++++- src/products/views/ProductCreate.tsx | 55 +++++++++--------- .../views/ProductUpdate/ProductUpdate.tsx | 37 ++++++++---- src/products/views/ProductVariant.tsx | 31 ++++++++-- src/products/views/ProductVariantCreate.tsx | 49 +++++++++------- .../stories/products/ProductCreatePage.tsx | 3 - .../stories/products/ProductUpdatePage.tsx | 1 - .../products/ProductVariantCreatePage.tsx | 4 -- .../stories/products/ProductVariantPage.tsx | 3 - 14 files changed, 257 insertions(+), 115 deletions(-) diff --git a/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx b/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx index e15aba9f8..6f64856db 100644 --- a/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx +++ b/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx @@ -2,52 +2,54 @@ import Button from "@material-ui/core/Button"; import Dialog from "@material-ui/core/Dialog"; import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; import { buttonMessages } from "@saleor/intl"; import React from "react"; import { FormattedMessage } from "react-intl"; import ConfirmButton, { ConfirmButtonTransitionState } from "../ConfirmButton"; -import Form from "../Form"; export interface LeaveScreenDialogProps { confirmButtonState: ConfirmButtonTransitionState; open: boolean; onClose: () => void; - onSubmit: () => void; + onSaveChanges: () => void; + onRejectChanges: () => void; } const LeaveScreenDialog: React.FC = ({ confirmButtonState, onClose, - onSubmit, + onSaveChanges, + onRejectChanges, open }) => ( -
- {({ submit }) => ( - <> - - - - - - - - - - - )} -
+ + + + + + + + + + + +
); LeaveScreenDialog.displayName = "LeaveScreenDialog"; diff --git a/src/intl.ts b/src/intl.ts index 6fe6a3e68..18fd659d2 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -136,6 +136,10 @@ export const buttonMessages = defineMessages({ defaultMessage: "Next", description: "go to next step, button" }, + no: { + defaultMessage: "No", + description: "button" + }, ok: { defaultMessage: "OK", description: "button" @@ -163,6 +167,10 @@ export const buttonMessages = defineMessages({ undo: { defaultMessage: "Undo", description: "button" + }, + yes: { + defaultMessage: "Yes", + description: "button" } }); diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index b96d702fd..62167700f 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -5,6 +5,7 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import Metadata, { MetadataFormData } from "@saleor/components/Metadata"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import PageHeader from "@saleor/components/PageHeader"; @@ -78,6 +79,8 @@ export interface ProductCreatePageSubmitData extends FormData { stocks: ProductStockInput[]; } +export type ProductCreatePageSubmitNextAction = "warehouse-configure"; + interface ProductCreatePageProps { errors: ProductErrorFragment[]; collections: SearchCollections_search_edges_node[]; @@ -98,12 +101,14 @@ interface ProductCreatePageProps { weightUnit: string; warehouses: SearchWarehouses_search_edges_node[]; taxTypes: TaxTypeFragment[]; + submitNextAction?: ProductCreatePageSubmitNextAction; fetchCategories: (data: string) => void; fetchCollections: (data: string) => void; fetchProductTypes: (data: string) => void; - onWarehouseConfigure: () => void; onBack?(); onSubmit?(data: ProductCreatePageSubmitData); + onSubmitReject?(nextAction?: ProductCreatePageSubmitNextAction); + setSubmitNextAction?(nextAction: ProductCreatePageSubmitNextAction); } export const ProductCreatePage: React.FC = ({ @@ -123,10 +128,12 @@ export const ProductCreatePage: React.FC = ({ warehouses, taxTypes, onBack, - onWarehouseConfigure, fetchProductTypes, weightUnit, - onSubmit + onSubmit, + onSubmitReject, + submitNextAction, + setSubmitNextAction }: ProductCreatePageProps) => { const intl = useIntl(); const localizeDate = useDateLocalize(); @@ -323,7 +330,12 @@ export const ProductCreatePage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={onWarehouseConfigure} + onWarehouseConfigure={() => { + setSubmitNextAction("warehouse-configure"); + if (disabled || !onSubmit || !hasChanged) { + onSubmitReject("warehouse-configure"); + } + }} /> @@ -412,6 +424,17 @@ export const ProductCreatePage: React.FC = ({ state={saveButtonBarState} disabled={disabled || !onSubmit || !hasChanged} /> + { + submit(); + }} + onRejectChanges={() => { + onSubmitReject("warehouse-configure"); + }} + onClose={() => setSubmitNextAction(null)} + open={submitNextAction === "warehouse-configure"} + confirmButtonState={saveButtonBarState} + /> ); }} diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 13e56879d..6efc10bd1 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -5,6 +5,7 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import Metadata from "@saleor/components/Metadata/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; @@ -56,6 +57,8 @@ import ProductStocks, { ProductStockInput } from "../ProductStocks"; import ProductTaxes from "../ProductTaxes"; import ProductVariants from "../ProductVariants"; +export type ProductUpdatePageSubmitNextAction = "warehouse-configure"; + export interface ProductUpdatePageProps extends ListActions { defaultWeightUnit: string; errors: ProductErrorFragment[]; @@ -72,6 +75,7 @@ export interface ProductUpdatePageProps extends ListActions { saveButtonBarState: ConfirmButtonTransitionState; warehouses: WarehouseFragment[]; taxTypes: TaxTypeFragment[]; + submitNextAction?: ProductUpdatePageSubmitNextAction; fetchCategories: (query: string) => void; fetchCollections: (query: string) => void; onVariantsAdd: () => void; @@ -85,6 +89,8 @@ export interface ProductUpdatePageProps extends ListActions { onImageUpload(file: File); onSeoClick?(); onSubmit?(data: ProductUpdatePageSubmitData); + onSubmitReject?(nextAction?: ProductUpdatePageSubmitNextAction); + setSubmitNextAction?(nextAction: ProductUpdatePageSubmitNextAction); onVariantAdd?(); onSetDefaultVariant(); onWarehouseConfigure(); @@ -124,17 +130,19 @@ export const ProductUpdatePage: React.FC = ({ onImageUpload, onSeoClick, onSubmit, + onSubmitReject, onVariantAdd, onVariantsAdd, onSetDefaultVariant, onVariantShow, onVariantReorder, - onWarehouseConfigure, isChecked, selected, toggle, toggleAll, - toolbar + toolbar, + submitNextAction, + setSubmitNextAction }) => { const intl = useIntl(); const localizeDate = useDateLocalize(); @@ -372,7 +380,12 @@ export const ProductUpdatePage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={onWarehouseConfigure} + onWarehouseConfigure={() => { + setSubmitNextAction("warehouse-configure"); + if (disabled || !onSubmit || !hasChanged) { + onSubmitReject("warehouse-configure"); + } + }} /> )} @@ -462,6 +475,17 @@ export const ProductUpdatePage: React.FC = ({ state={saveButtonBarState} disabled={disabled || !hasChanged} /> + { + submit(); + }} + onRejectChanges={() => { + onSubmitReject("warehouse-configure"); + }} + onClose={() => setSubmitNextAction(null)} + open={submitNextAction === "warehouse-configure"} + confirmButtonState={saveButtonBarState} + /> ); diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 8c345e4f2..2f7c5dceb 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -4,6 +4,7 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import Metadata, { MetadataFormData } from "@saleor/components/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; @@ -45,6 +46,8 @@ export interface ProductVariantCreatePageSubmitData stocks: ProductStockInput[]; } +export type ProductVariantCreatePageSubmitNextAction = "warehouse-configure"; + interface ProductVariantCreatePageProps { currencySymbol: string; disabled: boolean; @@ -54,11 +57,17 @@ interface ProductVariantCreatePageProps { saveButtonBarState: ConfirmButtonTransitionState; warehouses: SearchWarehouses_search_edges_node[]; weightUnit: string; + submitNextAction?: ProductVariantCreatePageSubmitNextAction; onBack: () => void; onSubmit: (data: ProductVariantCreatePageSubmitData) => void; + onSubmitReject?: ( + nextAction?: ProductVariantCreatePageSubmitNextAction + ) => void; + setSubmitNextAction?: ( + nextAction: ProductVariantCreatePageSubmitNextAction + ) => void; onVariantClick: (variantId: string) => void; onVariantReorder: ReorderAction; - onWarehouseConfigure: () => void; } const ProductVariantCreatePage: React.FC = ({ @@ -72,9 +81,11 @@ const ProductVariantCreatePage: React.FC = ({ weightUnit, onBack, onSubmit, + onSubmitReject, onVariantClick, onVariantReorder, - onWarehouseConfigure + submitNextAction, + setSubmitNextAction }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -190,7 +201,12 @@ const ProductVariantCreatePage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={onWarehouseConfigure} + onWarehouseConfigure={() => { + setSubmitNextAction("warehouse-configure"); + if (disabled || !onSubmit || !hasChanged) { + onSubmitReject("warehouse-configure"); + } + }} /> @@ -212,6 +228,17 @@ const ProductVariantCreatePage: React.FC = ({ onCancel={onBack} onSave={submit} /> + { + submit(); + }} + onRejectChanges={() => { + onSubmitReject("warehouse-configure"); + }} + onClose={() => setSubmitNextAction(null)} + open={submitNextAction === "warehouse-configure"} + confirmButtonState={saveButtonBarState} + /> ); }} diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index d875acf0d..84237a75e 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -4,6 +4,7 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; +import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import { MetadataFormData } from "@saleor/components/Metadata"; import Metadata from "@saleor/components/Metadata/Metadata"; import PageHeader from "@saleor/components/PageHeader"; @@ -53,6 +54,8 @@ export interface ProductVariantPageSubmitData removeStocks: string[]; } +export type ProductVariantPageSubmitNextAction = "warehouse-configure"; + interface ProductVariantPageProps { defaultWeightUnit: string; variant?: ProductVariant; @@ -62,11 +65,14 @@ interface ProductVariantPageProps { placeholderImage?: string; header: string; warehouses: WarehouseFragment[]; + submitNextAction?: ProductVariantPageSubmitNextAction; onVariantReorder: ReorderAction; onAdd(); onBack(); onDelete(); onSubmit(data: ProductVariantPageSubmitData); + onSubmitReject?(nextAction?: ProductVariantPageSubmitNextAction); + setSubmitNextAction?(nextAction: ProductVariantPageSubmitNextAction); onImageSelect(id: string); onVariantClick(variantId: string); onSetDefaultVariant(); @@ -87,10 +93,13 @@ const ProductVariantPage: React.FC = ({ onDelete, onImageSelect, onSubmit, + onSubmitReject, onVariantClick, onVariantReorder, onSetDefaultVariant, - onWarehouseConfigure + onWarehouseConfigure, + submitNextAction, + setSubmitNextAction }) => { const attributeInput = React.useMemo( () => getAttributeInputFromVariant(variant), @@ -266,7 +275,12 @@ const ProductVariantPage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={onWarehouseConfigure} + onWarehouseConfigure={() => { + setSubmitNextAction("warehouse-configure"); + if (!onSubmit || !hasChanged) { + onSubmitReject("warehouse-configure"); + } + }} /> @@ -279,6 +293,17 @@ const ProductVariantPage: React.FC = ({ onDelete={onDelete} onSave={submit} /> + { + submit(); + }} + onRejectChanges={() => { + onSubmitReject("warehouse-configure"); + }} + onClose={() => setSubmitNextAction(null)} + open={submitNextAction === "warehouse-configure"} + confirmButtonState={saveButtonBarState} + /> ); }} diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index ff7c342a7..28c618ee2 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -1,4 +1,3 @@ -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import useNavigator from "@saleor/hooks/useNavigator"; @@ -22,28 +21,20 @@ import { useIntl } from "react-intl"; import { decimal, weight } from "../../misc"; import ProductCreatePage, { - ProductCreatePageSubmitData + ProductCreatePageSubmitData, + ProductCreatePageSubmitNextAction } from "../components/ProductCreatePage"; import { useProductCreateMutation, useProductSetAvailabilityForPurchase } from "../mutations"; -import { - productAddUrl, - ProductAddUrlDialog, - ProductAddUrlQueryParams, - productListUrl, - productUrl -} from "../urls"; +import { ProductAddUrlQueryParams, productListUrl, productUrl } from "../urls"; interface ProductCreateViewProps { params: ProductAddUrlQueryParams; } -export const ProductCreateView: React.FC = ({ - params -}) => { - const { action } = params; +export const ProductCreateView: React.FC = ({}) => { const navigate = useNavigator(); const notify = useNotifier(); const shop = useShop(); @@ -87,7 +78,7 @@ export const ProductCreateView: React.FC = ({ ] = useProductSetAvailabilityForPurchase({ onCompleted: data => { const errors = data?.productSetAvailabilityForPurchase?.errors; - if (errors?.length === 0) { + if (errors?.length === 0 && !submitNextAction) { navigate(productUrl(data.productSetAvailabilityForPurchase.product.id)); } } @@ -106,11 +97,6 @@ export const ProductCreateView: React.FC = ({ } }); - const [openModal, closeModal] = createDialogActionHandlers< - ProductAddUrlDialog, - ProductAddUrlQueryParams - >(navigate, productAddUrl, params); - const handleCreate = async (formData: ProductCreatePageSubmitData) => { const result = await productCreate({ variables: { @@ -171,6 +157,18 @@ export const ProductCreateView: React.FC = ({ updatePrivateMetadata ); + const [submitNextAction, setSubmitNextAction] = React.useState< + ProductCreatePageSubmitNextAction + >(null); + const handleSubmitNextAction = ( + nextAction?: ProductCreatePageSubmitNextAction + ) => { + const action = nextAction || submitNextAction; + if (action === "warehouse-configure") { + navigate(warehouseListPath); + } + }; + return ( <> = ({ edge => edge.node )} onBack={handleBack} - onSubmit={handleSubmit} + onSubmit={async data => { + const errors = await handleSubmit(data); + if (errors?.length === 0) { + handleSubmitNextAction(); + } else { + setSubmitNextAction(null); + } + }} + onSubmitReject={handleSubmitNextAction} saveButtonBarState={productCreateOpts.status} fetchMoreCategories={{ hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, @@ -217,19 +223,14 @@ export const ProductCreateView: React.FC = ({ loading: searchProductTypesOpts.loading, onFetchMore: loadMoreProductTypes }} - onWarehouseConfigure={() => openModal("leave-screen")} + submitNextAction={submitNextAction} + setSubmitNextAction={setSubmitNextAction} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } taxTypes={taxTypes.data?.taxTypes || []} weightUnit={shop?.defaultWeightUnit} /> - navigate(warehouseListPath)} - onClose={closeModal} - open={action === "leave-screen"} - confirmButtonState="default" - /> ); }; diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 7713b9e0d..07d76ca3d 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -3,7 +3,6 @@ import DialogContentText from "@material-ui/core/DialogContentText"; import IconButton from "@material-ui/core/IconButton"; import DeleteIcon from "@material-ui/icons/Delete"; import ActionDialog from "@saleor/components/ActionDialog"; -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; @@ -39,7 +38,9 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { getMutationState, maybe } from "../../../misc"; -import ProductUpdatePage from "../../components/ProductUpdatePage"; +import ProductUpdatePage, { + ProductUpdatePageSubmitNextAction +} from "../../components/ProductUpdatePage"; import { useProductDetails } from "../../queries"; import { ProductImageCreateVariables } from "../../types/ProductImageCreate"; import { ProductUpdate as ProductUpdateMutationResult } from "../../types/ProductUpdate"; @@ -66,7 +67,6 @@ interface ProductUpdateProps { } export const ProductUpdate: React.FC = ({ id, params }) => { - const { action } = params; const navigate = useNavigator(); const notify = useNotifier(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( @@ -281,6 +281,18 @@ export const ProductUpdate: React.FC = ({ id, params }) => { null ); + const [submitNextAction, setSubmitNextAction] = React.useState< + ProductUpdatePageSubmitNextAction + >(null); + const handleSubmitNextAction = ( + nextAction?: ProductUpdatePageSubmitNextAction + ) => { + const action = nextAction || submitNextAction; + if (action === "warehouse-configure") { + navigate(warehouseListPath); + } + }; + return ( <> data.product.name)} /> @@ -306,7 +318,15 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onBack={handleBack} onDelete={() => openModal("remove")} onImageReorder={handleImageReorder} - onSubmit={handleSubmit} + onSubmit={async data => { + const errors = await handleSubmit(data); + if (errors?.length === 0) { + handleSubmitNextAction(); + } else { + setSubmitNextAction(null); + } + }} + onSubmitReject={handleSubmitNextAction} onVariantAdd={handleVariantAdd} onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => @@ -315,7 +335,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onImageUpload={handleImageUpload} onImageEdit={handleImageEdit} onImageDelete={handleImageDelete} - onWarehouseConfigure={() => openModal("leave-screen")} toolbar={ = ({ id, params }) => { loading: searchCollectionsOpts.loading, onFetchMore: loadMoreCollections }} + submitNextAction={submitNextAction} + setSubmitNextAction={setSubmitNextAction} /> = ({ id, params }) => { /> - navigate(warehouseListPath)} - onClose={closeModal} - open={action === "leave-screen"} - confirmButtonState="default" - /> ); }; diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index d972cce90..0e54674c2 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -21,7 +21,8 @@ import { useIntl } from "react-intl"; import { decimal, weight } from "../../misc"; import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog"; import ProductVariantPage, { - ProductVariantPageSubmitData + ProductVariantPageSubmitData, + ProductVariantPageSubmitNextAction } from "../components/ProductVariantPage"; import { useProductVariantReorderMutation, @@ -199,6 +200,18 @@ export const ProductVariant: React.FC = ({ variables => updatePrivateMetadata({ variables }) ); + const [submitNextAction, setSubmitNextAction] = React.useState< + ProductVariantPageSubmitNextAction + >(null); + const handleSubmitNextAction = ( + nextAction?: ProductVariantPageSubmitNextAction + ) => { + const action = nextAction || submitNextAction; + if (action === "warehouse-configure") { + navigate(warehouseListPath); + } + }; + return ( <> @@ -218,12 +231,21 @@ export const ProductVariant: React.FC = ({ onBack={handleBack} onDelete={() => openModal("remove")} onImageSelect={handleImageSelect} - onSubmit={handleSubmit} + onSubmit={async data => { + const errors = await handleSubmit(data); + if (errors?.length === 0) { + handleSubmitNextAction(); + } else { + setSubmitNextAction(null); + } + }} + onSubmitReject={handleSubmitNextAction} onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} onVariantReorder={handleVariantReorder} - onWarehouseConfigure={() => openModal("leave-screen")} + submitNextAction={submitNextAction} + setSubmitNextAction={setSubmitNextAction} /> = ({ name={data?.productVariant?.name} /> navigate(warehouseListPath)} + onSaveChanges={() => navigate(warehouseListPath)} + onRejectChanges={() => navigate(warehouseListPath)} onClose={closeModal} open={action === "leave-screen"} confirmButtonState="default" diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index b758ac83e..1e441c46b 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -1,11 +1,9 @@ -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; -import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, @@ -18,7 +16,8 @@ import { useIntl } from "react-intl"; import { decimal, weight } from "../../misc"; import ProductVariantCreatePage, { - ProductVariantCreatePageSubmitData + ProductVariantCreatePageSubmitData, + ProductVariantCreatePageSubmitNextAction } from "../components/ProductVariantCreatePage"; import { useProductVariantReorderMutation, @@ -28,8 +27,6 @@ import { useProductVariantCreateQuery } from "../queries"; import { productListUrl, productUrl, - productVariantAddUrl, - ProductVariantAddUrlDialog, ProductVariantAddUrlQueryParams, productVariantEditUrl } from "../urls"; @@ -41,10 +38,8 @@ interface ProductVariantCreateProps { } export const ProductVariant: React.FC = ({ - productId, - params + productId }) => { - const { action } = params; const navigate = useNavigator(); const notify = useNotifier(); const shop = useShop(); @@ -63,7 +58,7 @@ export const ProductVariant: React.FC = ({ const [variantCreate, variantCreateResult] = useVariantCreateMutation({ onCompleted: data => { - if (data.productVariantCreate.errors.length === 0) { + if (data.productVariantCreate.errors.length === 0 && !submitNextAction) { notify({ status: "success", text: intl.formatMessage(commonMessages.savedChanges) @@ -95,11 +90,6 @@ export const ProductVariant: React.FC = ({ reorderProductVariants({ variables }) ); - const [openModal, closeModal] = createDialogActionHandlers< - ProductVariantAddUrlDialog, - ProductVariantAddUrlQueryParams - >(navigate, params => productVariantAddUrl(productId, params), params); - const handleBack = () => navigate(productUrl(productId)); const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => { const result = await variantCreate({ @@ -135,6 +125,18 @@ export const ProductVariant: React.FC = ({ const handleVariantClick = (id: string) => navigate(productVariantEditUrl(productId, id)); + const [submitNextAction, setSubmitNextAction] = React.useState< + ProductVariantCreatePageSubmitNextAction + >(null); + const handleSubmitNextAction = ( + nextAction?: ProductVariantCreatePageSubmitNextAction + ) => { + const action = nextAction || submitNextAction; + if (action === "warehouse-configure") { + navigate(warehouseListPath); + } + }; + const disableForm = productLoading || variantCreateResult.loading || @@ -158,21 +160,24 @@ export const ProductVariant: React.FC = ({ })} product={data?.product} onBack={handleBack} - onSubmit={handleSubmit} + onSubmit={async data => { + const errors = await handleSubmit(data); + if (errors?.length === 0) { + handleSubmitNextAction(); + } else { + setSubmitNextAction(null); + } + }} + onSubmitReject={handleSubmitNextAction} onVariantClick={handleVariantClick} onVariantReorder={handleVariantReorder} - onWarehouseConfigure={() => openModal("leave-screen")} saveButtonBarState={variantCreateResult.status} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } weightUnit={shop?.defaultWeightUnit} - /> - navigate(warehouseListPath)} - onClose={closeModal} - open={action === "leave-screen"} - confirmButtonState="default" + submitNextAction={submitNextAction} + setSubmitNextAction={setSubmitNextAction} /> ); diff --git a/src/storybook/stories/products/ProductCreatePage.tsx b/src/storybook/stories/products/ProductCreatePage.tsx index ef80d8076..babd8504a 100644 --- a/src/storybook/stories/products/ProductCreatePage.tsx +++ b/src/storybook/stories/products/ProductCreatePage.tsx @@ -37,7 +37,6 @@ storiesOf("Views / Products / Create product", module) warehouses={warehouseList} taxTypes={taxTypes} weightUnit="kg" - onWarehouseConfigure={() => undefined} /> )) .add("When loading", () => ( @@ -61,7 +60,6 @@ storiesOf("Views / Products / Create product", module) warehouses={undefined} taxTypes={taxTypes} weightUnit="kg" - onWarehouseConfigure={() => undefined} /> )) .add("form errors", () => ( @@ -91,6 +89,5 @@ storiesOf("Views / Products / Create product", module) warehouses={warehouseList} taxTypes={taxTypes} weightUnit="kg" - onWarehouseConfigure={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index 980d68a84..c44841a1b 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -39,7 +39,6 @@ const props: ProductUpdatePageProps = { onVariantReorder: () => undefined, onVariantShow: () => undefined, onVariantsAdd: () => undefined, - onWarehouseConfigure: () => undefined, placeholderImage, product, saveButtonBarState: "default", diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index d0fdbea77..d8578724d 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -26,7 +26,6 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} - onWarehouseConfigure={() => undefined} /> )) .add("with errors", () => ( @@ -59,7 +58,6 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} - onWarehouseConfigure={() => undefined} /> )) .add("when loading data", () => ( @@ -76,7 +74,6 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} - onWarehouseConfigure={() => undefined} /> )) .add("add first variant", () => ( @@ -96,6 +93,5 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} - onWarehouseConfigure={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index a70095d78..038c03372 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -28,7 +28,6 @@ storiesOf("Views / Products / Product variant details", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} - onWarehouseConfigure={() => undefined} /> )) .add("when loading data", () => ( @@ -48,7 +47,6 @@ storiesOf("Views / Products / Product variant details", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} - onWarehouseConfigure={() => undefined} /> )) .add("attribute errors", () => ( @@ -84,6 +82,5 @@ storiesOf("Views / Products / Product variant details", module) ...error }))} warehouses={warehouseList} - onWarehouseConfigure={() => undefined} /> )); From e8e1162698c5b65625cf12b9d57aca880c720a2b Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 22 Sep 2020 12:32:17 +0200 Subject: [PATCH 05/16] Update submit next action logic --- .../ProductCreatePage/ProductCreatePage.tsx | 44 ++++++---- .../ProductUpdatePage/ProductUpdatePage.tsx | 81 +++++++++++-------- .../ProductVariantCreatePage.tsx | 47 ++++++----- .../ProductVariantPage/ProductVariantPage.tsx | 60 ++++++++------ src/products/views/ProductCreate.tsx | 17 ++-- .../views/ProductUpdate/ProductUpdate.tsx | 8 +- src/products/views/ProductVariant.tsx | 8 +- src/products/views/ProductVariantCreate.tsx | 17 ++-- src/utils/handlers/metadataCreateHandler.ts | 11 ++- 9 files changed, 168 insertions(+), 125 deletions(-) diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index 62167700f..f0baedfb2 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -106,9 +106,11 @@ interface ProductCreatePageProps { fetchCollections: (data: string) => void; fetchProductTypes: (data: string) => void; onBack?(); - onSubmit?(data: ProductCreatePageSubmitData); - onSubmitReject?(nextAction?: ProductCreatePageSubmitNextAction); - setSubmitNextAction?(nextAction: ProductCreatePageSubmitNextAction); + onSubmit?( + data: ProductCreatePageSubmitData, + nextAction?: ProductCreatePageSubmitNextAction + ); + onSubmitSkip?(nextAction?: ProductCreatePageSubmitNextAction); } export const ProductCreatePage: React.FC = ({ @@ -131,9 +133,7 @@ export const ProductCreatePage: React.FC = ({ fetchProductTypes, weightUnit, onSubmit, - onSubmitReject, - submitNextAction, - setSubmitNextAction + onSubmitSkip }: ProductCreatePageProps) => { const intl = useIntl(); const localizeDate = useDateLocalize(); @@ -210,12 +210,21 @@ export const ProductCreatePage: React.FC = ({ value: taxType.taxCode })) || []; - const handleSubmit = (data: FormData) => - onSubmit({ - ...data, - attributes, - stocks - }); + const [modalWithAction, setModalWithAction] = React.useState< + ProductCreatePageSubmitNextAction + >(null); + + const handleSubmit = (data: FormData) => { + onSubmit( + { + ...data, + attributes, + stocks + }, + modalWithAction + ); + setModalWithAction(null); + }; return (
@@ -331,9 +340,10 @@ export const ProductCreatePage: React.FC = ({ removeStock(id); }} onWarehouseConfigure={() => { - setSubmitNextAction("warehouse-configure"); if (disabled || !onSubmit || !hasChanged) { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); + } else { + setModalWithAction("warehouse-configure"); } }} /> @@ -429,10 +439,10 @@ export const ProductCreatePage: React.FC = ({ submit(); }} onRejectChanges={() => { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); }} - onClose={() => setSubmitNextAction(null)} - open={submitNextAction === "warehouse-configure"} + onClose={() => setModalWithAction(null)} + open={modalWithAction === "warehouse-configure"} confirmButtonState={saveButtonBarState} /> diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 6efc10bd1..0032769af 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -88,9 +88,11 @@ export interface ProductUpdatePageProps extends ListActions { onImageReorder?(event: { oldIndex: number; newIndex: number }); onImageUpload(file: File); onSeoClick?(); - onSubmit?(data: ProductUpdatePageSubmitData); - onSubmitReject?(nextAction?: ProductUpdatePageSubmitNextAction); - setSubmitNextAction?(nextAction: ProductUpdatePageSubmitNextAction); + onSubmit?( + data: ProductUpdatePageSubmitData, + nextAction?: ProductUpdatePageSubmitNextAction + ); + onSubmitSkip?(nextAction?: ProductUpdatePageSubmitNextAction); onVariantAdd?(); onSetDefaultVariant(); onWarehouseConfigure(); @@ -130,7 +132,7 @@ export const ProductUpdatePage: React.FC = ({ onImageUpload, onSeoClick, onSubmit, - onSubmitReject, + onSubmitSkip, onVariantAdd, onVariantsAdd, onSetDefaultVariant, @@ -140,9 +142,7 @@ export const ProductUpdatePage: React.FC = ({ selected, toggle, toggleAll, - toolbar, - submitNextAction, - setSubmitNextAction + toolbar }) => { const intl = useIntl(); const localizeDate = useDateLocalize(); @@ -201,6 +201,10 @@ export const ProductUpdatePage: React.FC = ({ value: taxType.taxCode })) || []; + const [modalWithAction, setModalWithAction] = React.useState< + ProductUpdatePageSubmitNextAction + >(null); + const handleSubmit = (data: ProductUpdatePageFormData) => { const metadata = isMetadataModified ? data.metadata : undefined; const privateMetadata = isPrivateMetadataModified @@ -208,15 +212,18 @@ export const ProductUpdatePage: React.FC = ({ : undefined; if (product.productType.hasVariants) { - onSubmit({ - ...data, - addStocks: [], - attributes, - metadata, - privateMetadata, - removeStocks: [], - updateStocks: [] - }); + onSubmit( + { + ...data, + addStocks: [], + attributes, + metadata, + privateMetadata, + removeStocks: [], + updateStocks: [] + }, + modalWithAction + ); } else { const dataStocks = stocks.map(stock => stock.id); const variantStocks = product.variants[0]?.stocks.map( @@ -224,20 +231,25 @@ export const ProductUpdatePage: React.FC = ({ ); const stockDiff = diff(variantStocks, dataStocks); - onSubmit({ - ...data, - addStocks: stocks.filter(stock => - stockDiff.added.some(addedStock => addedStock === stock.id) - ), - attributes, - metadata, - privateMetadata, - removeStocks: stockDiff.removed, - updateStocks: stocks.filter( - stock => !stockDiff.added.some(addedStock => addedStock === stock.id) - ) - }); + onSubmit( + { + ...data, + addStocks: stocks.filter(stock => + stockDiff.added.some(addedStock => addedStock === stock.id) + ), + attributes, + metadata, + privateMetadata, + removeStocks: stockDiff.removed, + updateStocks: stocks.filter( + stock => + !stockDiff.added.some(addedStock => addedStock === stock.id) + ) + }, + modalWithAction + ); } + setModalWithAction(null); }; return ( @@ -381,9 +393,10 @@ export const ProductUpdatePage: React.FC = ({ removeStock(id); }} onWarehouseConfigure={() => { - setSubmitNextAction("warehouse-configure"); if (disabled || !onSubmit || !hasChanged) { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); + } else { + setModalWithAction("warehouse-configure"); } }} /> @@ -480,10 +493,10 @@ export const ProductUpdatePage: React.FC = ({ submit(); }} onRejectChanges={() => { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); }} - onClose={() => setSubmitNextAction(null)} - open={submitNextAction === "warehouse-configure"} + onClose={() => setModalWithAction(null)} + open={modalWithAction === "warehouse-configure"} confirmButtonState={saveButtonBarState} /> diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 2f7c5dceb..7117a09bf 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -57,14 +57,13 @@ interface ProductVariantCreatePageProps { saveButtonBarState: ConfirmButtonTransitionState; warehouses: SearchWarehouses_search_edges_node[]; weightUnit: string; - submitNextAction?: ProductVariantCreatePageSubmitNextAction; onBack: () => void; - onSubmit: (data: ProductVariantCreatePageSubmitData) => void; - onSubmitReject?: ( + onSubmit: ( + data: ProductVariantCreatePageSubmitData, nextAction?: ProductVariantCreatePageSubmitNextAction ) => void; - setSubmitNextAction?: ( - nextAction: ProductVariantCreatePageSubmitNextAction + onSubmitSkip?: ( + nextAction?: ProductVariantCreatePageSubmitNextAction ) => void; onVariantClick: (variantId: string) => void; onVariantReorder: ReorderAction; @@ -81,11 +80,9 @@ const ProductVariantCreatePage: React.FC = ({ weightUnit, onBack, onSubmit, - onSubmitReject, + onSubmitSkip, onVariantClick, - onVariantReorder, - submitNextAction, - setSubmitNextAction + onVariantReorder }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -117,12 +114,21 @@ const ProductVariantCreatePage: React.FC = ({ weight: "" }; - const handleSubmit = (data: ProductVariantCreatePageFormData) => - onSubmit({ - ...data, - attributes, - stocks - }); + const [modalWithAction, setModalWithAction] = React.useState< + ProductVariantCreatePageSubmitNextAction + >(null); + + const handleSubmit = (data: ProductVariantCreatePageFormData) => { + onSubmit( + { + ...data, + attributes, + stocks + }, + modalWithAction + ); + setModalWithAction(null); + }; return ( @@ -202,9 +208,10 @@ const ProductVariantCreatePage: React.FC = ({ removeStock(id); }} onWarehouseConfigure={() => { - setSubmitNextAction("warehouse-configure"); if (disabled || !onSubmit || !hasChanged) { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); + } else { + setModalWithAction("warehouse-configure"); } }} /> @@ -233,10 +240,10 @@ const ProductVariantCreatePage: React.FC = ({ submit(); }} onRejectChanges={() => { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); }} - onClose={() => setSubmitNextAction(null)} - open={submitNextAction === "warehouse-configure"} + onClose={() => setModalWithAction(null)} + open={modalWithAction === "warehouse-configure"} confirmButtonState={saveButtonBarState} /> diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 84237a75e..111556daa 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -65,14 +65,15 @@ interface ProductVariantPageProps { placeholderImage?: string; header: string; warehouses: WarehouseFragment[]; - submitNextAction?: ProductVariantPageSubmitNextAction; onVariantReorder: ReorderAction; onAdd(); onBack(); onDelete(); - onSubmit(data: ProductVariantPageSubmitData); - onSubmitReject?(nextAction?: ProductVariantPageSubmitNextAction); - setSubmitNextAction?(nextAction: ProductVariantPageSubmitNextAction); + onSubmit( + data: ProductVariantPageSubmitData, + nextAction?: ProductVariantPageSubmitNextAction + ); + onSubmitSkip?(nextAction?: ProductVariantPageSubmitNextAction); onImageSelect(id: string); onVariantClick(variantId: string); onSetDefaultVariant(); @@ -93,13 +94,11 @@ const ProductVariantPage: React.FC = ({ onDelete, onImageSelect, onSubmit, - onSubmitReject, + onSubmitSkip, onVariantClick, onVariantReorder, onSetDefaultVariant, - onWarehouseConfigure, - submitNextAction, - setSubmitNextAction + onWarehouseConfigure }) => { const attributeInput = React.useMemo( () => getAttributeInputFromVariant(variant), @@ -149,6 +148,10 @@ const ProductVariantPage: React.FC = ({ weight: variant?.weight?.value.toString() || "" }; + const [modalWithAction, setModalWithAction] = React.useState< + ProductVariantPageSubmitNextAction + >(null); + const handleSubmit = (data: ProductVariantPageFormData) => { const dataStocks = stocks.map(stock => stock.id); const variantStocks = variant.stocks.map(stock => stock.warehouse.id); @@ -158,19 +161,23 @@ const ProductVariantPage: React.FC = ({ ? data.privateMetadata : undefined; - onSubmit({ - ...data, - addStocks: stocks.filter(stock => - stockDiff.added.some(addedStock => addedStock === stock.id) - ), - attributes, - metadata, - privateMetadata, - removeStocks: stockDiff.removed, - updateStocks: stocks.filter( - stock => !stockDiff.added.some(addedStock => addedStock === stock.id) - ) - }); + onSubmit( + { + ...data, + addStocks: stocks.filter(stock => + stockDiff.added.some(addedStock => addedStock === stock.id) + ), + attributes, + metadata, + privateMetadata, + removeStocks: stockDiff.removed, + updateStocks: stocks.filter( + stock => !stockDiff.added.some(addedStock => addedStock === stock.id) + ) + }, + modalWithAction + ); + setModalWithAction(null); }; return ( @@ -276,9 +283,10 @@ const ProductVariantPage: React.FC = ({ removeStock(id); }} onWarehouseConfigure={() => { - setSubmitNextAction("warehouse-configure"); if (!onSubmit || !hasChanged) { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); + } else { + setModalWithAction("warehouse-configure"); } }} /> @@ -298,10 +306,10 @@ const ProductVariantPage: React.FC = ({ submit(); }} onRejectChanges={() => { - onSubmitReject("warehouse-configure"); + onSubmitSkip("warehouse-configure"); }} - onClose={() => setSubmitNextAction(null)} - open={submitNextAction === "warehouse-configure"} + onClose={() => setModalWithAction(null)} + open={modalWithAction === "warehouse-configure"} confirmButtonState={saveButtonBarState} /> diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 28c618ee2..9bee2017e 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -97,7 +97,12 @@ export const ProductCreateView: React.FC = ({}) => { } }); - const handleCreate = async (formData: ProductCreatePageSubmitData) => { + const handleCreate = async ( + formData: ProductCreatePageSubmitData, + nextAction?: ProductCreatePageSubmitNextAction + ) => { + setSubmitNextAction(nextAction); + const result = await productCreate({ variables: { input: { @@ -198,15 +203,15 @@ export const ProductCreateView: React.FC = ({}) => { edge => edge.node )} onBack={handleBack} - onSubmit={async data => { - const errors = await handleSubmit(data); + onSubmit={async (data, nextAction) => { + const errors = await handleSubmit(data, nextAction); if (errors?.length === 0) { - handleSubmitNextAction(); + handleSubmitNextAction(nextAction); } else { setSubmitNextAction(null); } }} - onSubmitReject={handleSubmitNextAction} + onSubmitSkip={handleSubmitNextAction} saveButtonBarState={productCreateOpts.status} fetchMoreCategories={{ hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, @@ -223,8 +228,6 @@ export const ProductCreateView: React.FC = ({}) => { loading: searchProductTypesOpts.loading, onFetchMore: loadMoreProductTypes }} - submitNextAction={submitNextAction} - setSubmitNextAction={setSubmitNextAction} warehouses={ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 07d76ca3d..55b7971a4 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -318,15 +318,15 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onBack={handleBack} onDelete={() => openModal("remove")} onImageReorder={handleImageReorder} - onSubmit={async data => { + onSubmit={async (data, nextAction) => { const errors = await handleSubmit(data); if (errors?.length === 0) { - handleSubmitNextAction(); + handleSubmitNextAction(nextAction); } else { setSubmitNextAction(null); } }} - onSubmitReject={handleSubmitNextAction} + onSubmitSkip={handleSubmitNextAction} onVariantAdd={handleVariantAdd} onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => @@ -365,8 +365,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { loading: searchCollectionsOpts.loading, onFetchMore: loadMoreCollections }} - submitNextAction={submitNextAction} - setSubmitNextAction={setSubmitNextAction} /> = ({ onBack={handleBack} onDelete={() => openModal("remove")} onImageSelect={handleImageSelect} - onSubmit={async data => { + onSubmit={async (data, nextAction) => { const errors = await handleSubmit(data); if (errors?.length === 0) { - handleSubmitNextAction(); + handleSubmitNextAction(nextAction); } else { setSubmitNextAction(null); } }} - onSubmitReject={handleSubmitNextAction} + onSubmitSkip={handleSubmitNextAction} onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} onVariantReorder={handleVariantReorder} - submitNextAction={submitNextAction} - setSubmitNextAction={setSubmitNextAction} /> = ({ ); const handleBack = () => navigate(productUrl(productId)); - const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => { + const handleCreate = async ( + formData: ProductVariantCreatePageSubmitData, + nextAction?: ProductVariantCreatePageSubmitNextAction + ) => { + setSubmitNextAction(nextAction); + const result = await variantCreate({ variables: { input: { @@ -160,15 +165,15 @@ export const ProductVariant: React.FC = ({ })} product={data?.product} onBack={handleBack} - onSubmit={async data => { - const errors = await handleSubmit(data); + onSubmit={async (data, nextAction) => { + const errors = await handleSubmit(data, nextAction); if (errors?.length === 0) { - handleSubmitNextAction(); + handleSubmitNextAction(nextAction); } else { setSubmitNextAction(null); } }} - onSubmitReject={handleSubmitNextAction} + onSubmitSkip={handleSubmitNextAction} onVariantClick={handleVariantClick} onVariantReorder={handleVariantReorder} saveButtonBarState={variantCreateResult.status} @@ -176,8 +181,6 @@ export const ProductVariant: React.FC = ({ warehouses.data?.warehouses.edges.map(edge => edge.node) || [] } weightUnit={shop?.defaultWeightUnit} - submitNextAction={submitNextAction} - setSubmitNextAction={setSubmitNextAction} /> ); diff --git a/src/utils/handlers/metadataCreateHandler.ts b/src/utils/handlers/metadataCreateHandler.ts index e24491b80..73bb5a619 100644 --- a/src/utils/handlers/metadataCreateHandler.ts +++ b/src/utils/handlers/metadataCreateHandler.ts @@ -10,16 +10,19 @@ import { UpdatePrivateMetadataVariables } from "../metadata/types/UpdatePrivateMetadata"; -function createMetadataCreateHandler( - create: (data: T) => Promise, +function createMetadataCreateHandler< + T extends MetadataFormData, + N extends string +>( + create: (data: T, nextAction?: N) => Promise, setMetadata: MutationFunction, setPrivateMetadata: MutationFunction< UpdatePrivateMetadata, UpdatePrivateMetadataVariables > ) { - return async (data: T) => { - const id = await create(data); + return async (data: T, nextAction?: N) => { + const id = await create(data, nextAction); if (id === null) { return null; From 6d39899db3d3fdb1ec9540b0426797c5d6f9a512 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 22 Sep 2020 12:50:28 +0200 Subject: [PATCH 06/16] Revert changes of product urls --- src/categories/views/CategoryDetails.tsx | 2 +- .../Navigator/modes/commands/actions.ts | 2 +- src/products/index.tsx | 27 +++++-------------- src/products/urls.ts | 22 ++++----------- src/products/views/ProductCreate.tsx | 8 ++---- .../views/ProductList/ProductList.tsx | 2 +- src/products/views/ProductVariant.tsx | 11 +------- src/products/views/ProductVariantCreate.tsx | 8 +----- 8 files changed, 18 insertions(+), 64 deletions(-) diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index c24e11b89..cd52af91c 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -211,7 +211,7 @@ export const CategoryDetails: React.FC = ({ disabled={loading} errors={updateResult.data?.categoryUpdate.errors || []} onAddCategory={() => navigate(categoryAddUrl(id))} - onAddProduct={() => navigate(productAddUrl())} + onAddProduct={() => navigate(productAddUrl)} onBack={() => navigate( maybe(() => categoryUrl(data.category.parent.id), categoryListUrl()) diff --git a/src/components/Navigator/modes/commands/actions.ts b/src/components/Navigator/modes/commands/actions.ts index 0e8fb31ca..a10c60c5d 100644 --- a/src/components/Navigator/modes/commands/actions.ts +++ b/src/components/Navigator/modes/commands/actions.ts @@ -46,7 +46,7 @@ export function searchInCommands( { label: intl.formatMessage(messages.createProduct), onClick: () => { - navigate(productAddUrl()); + navigate(productAddUrl); return false; } }, diff --git a/src/products/index.tsx b/src/products/index.tsx index 2855010e8..46b4df70d 100644 --- a/src/products/index.tsx +++ b/src/products/index.tsx @@ -9,7 +9,6 @@ import { Route, RouteComponentProps, Switch } from "react-router-dom"; import { WindowTitle } from "../components/WindowTitle"; import { productAddPath, - ProductAddUrlQueryParams, productImagePath, ProductImageUrlQueryParams, productListPath, @@ -18,12 +17,11 @@ import { productPath, ProductUrlQueryParams, productVariantAddPath, - ProductVariantAddUrlQueryParams, productVariantCreatorPath, productVariantEditPath, ProductVariantEditUrlQueryParams } from "./urls"; -import ProductCreateComponent from "./views/ProductCreate"; +import ProductCreate from "./views/ProductCreate"; import ProductImageComponent from "./views/ProductImage"; import ProductListComponent from "./views/ProductList"; import ProductUpdateComponent from "./views/ProductUpdate"; @@ -47,13 +45,6 @@ const ProductList: React.FC> = ({ location }) => { return ; }; -const ProductCreate: React.FC = () => { - const qs = parseQs(location.search.substr(1)); - const params: ProductAddUrlQueryParams = qs; - - return ; -}; - const ProductUpdate: React.FC> = ({ match }) => { const qs = parseQs(location.search.substr(1)); const params: ProductUrlQueryParams = qs; @@ -100,17 +91,11 @@ const ProductImage: React.FC> = ({ const ProductVariantCreate: React.FC> = ({ match -}) => { - const qs = parseQs(location.search.substr(1)); - const params: ProductVariantAddUrlQueryParams = qs; - - return ( - - ); -}; +}) => ( + +); const ProductVariantCreator: React.FC; -export const productAddUrl = (params?: ProductAddUrlQueryParams) => - productAddPath + "?" + stringifyQs(params); +export const productAddUrl = productAddPath; export const productListPath = productSection; export type ProductListUrlDialog = @@ -69,14 +66,14 @@ export const productListUrl = (params?: ProductListUrlQueryParams): string => productListPath + "?" + stringifyQs(params); export const productPath = (id: string) => urlJoin(productSection + id); -export type ProductUrlDialog = "remove" | "remove-variants" | "leave-screen"; +export type ProductUrlDialog = "remove" | "remove-variants"; export type ProductUrlQueryParams = BulkAction & Dialog; export const productUrl = (id: string, params?: ProductUrlQueryParams) => productPath(encodeURIComponent(id)) + "?" + stringifyQs(params); export const productVariantEditPath = (productId: string, variantId: string) => urlJoin(productSection, productId, "variant", variantId); -export type ProductVariantEditUrlDialog = "remove" | "leave-screen"; +export type ProductVariantEditUrlDialog = "remove"; export type ProductVariantEditUrlQueryParams = Dialog< ProductVariantEditUrlDialog >; @@ -99,17 +96,8 @@ export const productVariantCreatorUrl = (productId: string) => export const productVariantAddPath = (productId: string) => urlJoin(productSection, productId, "variant/add"); -export type ProductVariantAddUrlDialog = "leave-screen"; -export type ProductVariantAddUrlQueryParams = Dialog< - ProductVariantAddUrlDialog ->; -export const productVariantAddUrl = ( - productId: string, - params?: ProductVariantAddUrlQueryParams -): string => - productVariantAddPath(encodeURIComponent(productId)) + - "?" + - stringifyQs(params); +export const productVariantAddUrl = (productId: string): string => + productVariantAddPath(encodeURIComponent(productId)); export const productImagePath = (productId: string, imageId: string) => urlJoin(productSection, productId, "image", imageId); diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 9bee2017e..546dc2618 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -28,13 +28,9 @@ import { useProductCreateMutation, useProductSetAvailabilityForPurchase } from "../mutations"; -import { ProductAddUrlQueryParams, productListUrl, productUrl } from "../urls"; +import { productListUrl, productUrl } from "../urls"; -interface ProductCreateViewProps { - params: ProductAddUrlQueryParams; -} - -export const ProductCreateView: React.FC = ({}) => { +export const ProductCreateView: React.FC = () => { const navigate = useNavigator(); const notify = useNotifier(); const shop = useShop(); diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index e032cc944..e25ca42eb 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -344,7 +344,7 @@ export const ProductList: React.FC = ({ params }) => { () => attributes.data.availableInGrid.pageInfo.hasNextPage, false )} - onAdd={() => navigate(productAddUrl())} + onAdd={() => navigate(productAddUrl)} disabled={loading} products={maybe(() => data.products.edges.map(edge => edge.node))} onFetchMore={() => diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index 6af6c19fd..419a0b2ca 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -1,5 +1,4 @@ import placeholderImg from "@assets/images/placeholder255x255.png"; -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; @@ -54,7 +53,6 @@ export const ProductVariant: React.FC = ({ productId, params }) => { - const { action } = params; const shop = useShop(); const navigate = useNavigator(); const notify = useNotifier(); @@ -82,7 +80,7 @@ export const ProductVariant: React.FC = ({ const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); - const [openModal, closeModal] = createDialogActionHandlers< + const [openModal] = createDialogActionHandlers< ProductVariantEditUrlDialog, ProductVariantEditUrlQueryParams >( @@ -258,13 +256,6 @@ export const ProductVariant: React.FC = ({ open={params.action === "remove"} name={data?.productVariant?.name} /> - navigate(warehouseListPath)} - onRejectChanges={() => navigate(warehouseListPath)} - onClose={closeModal} - open={action === "leave-screen"} - confirmButtonState="default" - /> ); }; diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 48107bb30..fbf1c2422 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -24,17 +24,11 @@ import { useVariantCreateMutation } from "../mutations"; import { useProductVariantCreateQuery } from "../queries"; -import { - productListUrl, - productUrl, - ProductVariantAddUrlQueryParams, - productVariantEditUrl -} from "../urls"; +import { productListUrl, productUrl, productVariantEditUrl } from "../urls"; import { createVariantReorderHandler } from "./ProductUpdate/handlers"; interface ProductVariantCreateProps { productId: string; - params: ProductVariantAddUrlQueryParams; } export const ProductVariant: React.FC = ({ From 73fe19011b8d725e4e7866d11b5db331cfe4d8bd Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 22 Sep 2020 13:02:19 +0200 Subject: [PATCH 07/16] Update submit next action handling --- .../views/ProductUpdate/ProductUpdate.tsx | 8 +------- src/products/views/ProductVariant.tsx | 8 +------- src/products/views/ProductVariantCreate.tsx | 16 +++++++++------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 55b7971a4..6321b4e57 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -281,14 +281,10 @@ export const ProductUpdate: React.FC = ({ id, params }) => { null ); - const [submitNextAction, setSubmitNextAction] = React.useState< - ProductUpdatePageSubmitNextAction - >(null); const handleSubmitNextAction = ( nextAction?: ProductUpdatePageSubmitNextAction ) => { - const action = nextAction || submitNextAction; - if (action === "warehouse-configure") { + if (nextAction === "warehouse-configure") { navigate(warehouseListPath); } }; @@ -322,8 +318,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { const errors = await handleSubmit(data); if (errors?.length === 0) { handleSubmitNextAction(nextAction); - } else { - setSubmitNextAction(null); } }} onSubmitSkip={handleSubmitNextAction} diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index 419a0b2ca..d5870eeda 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -198,14 +198,10 @@ export const ProductVariant: React.FC = ({ variables => updatePrivateMetadata({ variables }) ); - const [submitNextAction, setSubmitNextAction] = React.useState< - ProductVariantPageSubmitNextAction - >(null); const handleSubmitNextAction = ( nextAction?: ProductVariantPageSubmitNextAction ) => { - const action = nextAction || submitNextAction; - if (action === "warehouse-configure") { + if (nextAction === "warehouse-configure") { navigate(warehouseListPath); } }; @@ -233,8 +229,6 @@ export const ProductVariant: React.FC = ({ const errors = await handleSubmit(data); if (errors?.length === 0) { handleSubmitNextAction(nextAction); - } else { - setSubmitNextAction(null); } }} onSubmitSkip={handleSubmitNextAction} diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index fbf1c2422..23d4f9ea1 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -52,17 +52,19 @@ export const ProductVariant: React.FC = ({ const [variantCreate, variantCreateResult] = useVariantCreateMutation({ onCompleted: data => { - if (data.productVariantCreate.errors.length === 0 && !submitNextAction) { + if (data.productVariantCreate.errors.length === 0) { notify({ status: "success", text: intl.formatMessage(commonMessages.savedChanges) }); - navigate( - productVariantEditUrl( - productId, - data.productVariantCreate.productVariant.id - ) - ); + if (!submitNextAction) { + navigate( + productVariantEditUrl( + productId, + data.productVariantCreate.productVariant.id + ) + ); + } } } }); From ce812aaab43a051203fa577ffbdc6d91db9ad401 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 22 Sep 2020 13:30:05 +0200 Subject: [PATCH 08/16] Fix translations --- locale/defaultMessages.json | 8 ++++++-- src/components/LeaveScreenDialog/LeaveScreenDialog.tsx | 6 +++--- src/intl.ts | 8 -------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 640cd5f64..e8ee128be 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1593,9 +1593,13 @@ "context": "button", "string": "{languageName} - {languageCode}" }, - "src_dot_components_dot_LeaveScreenDialog_dot_3281163715": { + "src_dot_components_dot_LeaveScreenDialog_dot_1295493095": { + "context": "leaving screen warning header", + "string": "You're leaving this screen" + }, + "src_dot_components_dot_LeaveScreenDialog_dot_4066894204": { "context": "leaving screen warning message", - "string": "You're leaving this screen. Do you want to save previously made changes?" + "string": "Do you want to save previously made changes?" }, "src_dot_components_dot_ListField_dot_3099331554": { "context": "button", diff --git a/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx b/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx index 6f64856db..5b9803442 100644 --- a/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx +++ b/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx @@ -3,7 +3,7 @@ import Dialog from "@material-ui/core/Dialog"; import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogTitle from "@material-ui/core/DialogTitle"; -import { buttonMessages } from "@saleor/intl"; +import { commonMessages } from "@saleor/intl"; import React from "react"; import { FormattedMessage } from "react-intl"; @@ -39,7 +39,7 @@ const LeaveScreenDialog: React.FC = ({ = ({ variant="contained" onClick={onSaveChanges} > - + diff --git a/src/intl.ts b/src/intl.ts index 18fd659d2..6fe6a3e68 100644 --- a/src/intl.ts +++ b/src/intl.ts @@ -136,10 +136,6 @@ export const buttonMessages = defineMessages({ defaultMessage: "Next", description: "go to next step, button" }, - no: { - defaultMessage: "No", - description: "button" - }, ok: { defaultMessage: "OK", description: "button" @@ -167,10 +163,6 @@ export const buttonMessages = defineMessages({ undo: { defaultMessage: "Undo", description: "button" - }, - yes: { - defaultMessage: "Yes", - description: "button" } }); From 9d306863f088e884bbf09e67a255609c7b7283ec Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 22 Sep 2020 13:53:37 +0200 Subject: [PATCH 09/16] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 968d274a1..32fa439f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add error info when fetching taxes - #701 by @dominik-zeglen - Fix return to previous page on screen size change - #710 by @orzechdev - Fix updating order details on address change #711 - by @orzechdev -- Add no warehouses info in variant creator #713 - by @orzechdev +- Add no warehouses info when working with stock quantities #713 - by @orzechdev - Add variants reordering possibility - #716 by @orzechdev - Fix avatar change button - #719 by @orzechdev - Add slug field to product, collection, category & page details (update and create) - #720 by @mmarkusik From 4aefafda9ca956dbc941622a88af63b52023cae8 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Mon, 28 Sep 2020 14:47:52 +0200 Subject: [PATCH 10/16] Remove unused callback --- .../components/ProductUpdatePage/ProductUpdatePage.tsx | 1 - .../components/ProductVariantPage/ProductVariantPage.tsx | 4 +--- src/products/views/ProductCreate.tsx | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 0032769af..48d50cf6c 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -95,7 +95,6 @@ export interface ProductUpdatePageProps extends ListActions { onSubmitSkip?(nextAction?: ProductUpdatePageSubmitNextAction); onVariantAdd?(); onSetDefaultVariant(); - onWarehouseConfigure(); } export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData { diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index 111556daa..a2eecb180 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -77,7 +77,6 @@ interface ProductVariantPageProps { onImageSelect(id: string); onVariantClick(variantId: string); onSetDefaultVariant(); - onWarehouseConfigure(); } const ProductVariantPage: React.FC = ({ @@ -97,8 +96,7 @@ const ProductVariantPage: React.FC = ({ onSubmitSkip, onVariantClick, onVariantReorder, - onSetDefaultVariant, - onWarehouseConfigure + onSetDefaultVariant }) => { const attributeInput = React.useMemo( () => getAttributeInputFromVariant(variant), diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 546dc2618..8bf072c74 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -8,7 +8,6 @@ import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; import { useTaxTypeList } from "@saleor/taxes/queries"; -import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataCreateHandler from "@saleor/utils/handlers/metadataCreateHandler"; import { useMetadataUpdate, From b9bccd07f30a6067172ff5eedaf45318db16a3da Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Mon, 28 Sep 2020 17:37:55 +0200 Subject: [PATCH 11/16] Refactor leave action in product update --- src/components/Form/Form.tsx | 17 +++++- src/hooks/useForm.ts | 18 ++++++- .../ProductUpdatePage/ProductUpdatePage.tsx | 52 +++++++------------ .../views/ProductUpdate/ProductUpdate.tsx | 18 ++----- .../stories/products/ProductUpdatePage.tsx | 1 + 5 files changed, 56 insertions(+), 50 deletions(-) diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index 2721f678a..d7ae58fcb 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -1,12 +1,14 @@ import useForm, { UseFormResult } from "@saleor/hooks/useForm"; import React from "react"; +import LeaveScreenDialog from "../LeaveScreenDialog"; + export interface FormProps { children: (props: UseFormResult) => React.ReactNode; confirmLeave?: boolean; initial?: T; resetOnSubmit?: boolean; - onSubmit?: (data: T) => void; + onSubmit?: (data: T, nextAction: () => void) => void; } function Form(props: FormProps) { @@ -32,7 +34,18 @@ function Form(props: FormProps) { submit(); } - return {children(renderProps)}; + return ( +
+ {children(renderProps)} + renderProps.askToLeave(null)} + open={!!renderProps.leaveModal} + confirmButtonState="default" + /> + + ); } Form.displayName = "Form"; diff --git a/src/hooks/useForm.ts b/src/hooks/useForm.ts index 752030949..a08c8971b 100644 --- a/src/hooks/useForm.ts +++ b/src/hooks/useForm.ts @@ -14,6 +14,9 @@ export interface ChangeEvent { export type FormChange = (event: ChangeEvent, cb?: () => void) => void; export interface UseFormResult { + askToLeave: (action: () => void | null) => void; + leaveAction: () => void | null; + leaveModal: boolean; change: FormChange; data: T; hasChanged: boolean; @@ -51,13 +54,15 @@ function handleRefresh( function useForm( initial: T, - onSubmit: (data: T) => void + onSubmit: (data: T, nextAction: () => void) => void ): UseFormResult { const [hasChanged, setChanged] = useState(false); const [data, setData] = useStateFromProps(initial, { mergeFunc: merge, onRefresh: newData => handleRefresh(data, newData, setChanged) }); + const [leaveModal, setLeaveModal] = useState(false); + const [leaveAction, setLeaveAction] = useState<() => void>(null); function toggleValue(event: ChangeEvent, cb?: () => void) { const { name, value } = event.target; @@ -107,17 +112,26 @@ function useForm( } function submit() { - return onSubmit(data); + onSubmit(data, leaveAction); + setLeaveModal(false); } function triggerChange() { setChanged(true); } + function askToLeave(action: () => void | null) { + setLeaveModal(() => !!action); + setLeaveAction(() => action); + } + return { + askToLeave, change, data, hasChanged, + leaveAction, + leaveModal, reset, set, submit, diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index 48d50cf6c..b263bd57e 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -5,7 +5,6 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import Metadata from "@saleor/components/Metadata/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; @@ -57,8 +56,6 @@ import ProductStocks, { ProductStockInput } from "../ProductStocks"; import ProductTaxes from "../ProductTaxes"; import ProductVariants from "../ProductVariants"; -export type ProductUpdatePageSubmitNextAction = "warehouse-configure"; - export interface ProductUpdatePageProps extends ListActions { defaultWeightUnit: string; errors: ProductErrorFragment[]; @@ -75,7 +72,6 @@ export interface ProductUpdatePageProps extends ListActions { saveButtonBarState: ConfirmButtonTransitionState; warehouses: WarehouseFragment[]; taxTypes: TaxTypeFragment[]; - submitNextAction?: ProductUpdatePageSubmitNextAction; fetchCategories: (query: string) => void; fetchCollections: (query: string) => void; onVariantsAdd: () => void; @@ -88,13 +84,10 @@ export interface ProductUpdatePageProps extends ListActions { onImageReorder?(event: { oldIndex: number; newIndex: number }); onImageUpload(file: File); onSeoClick?(); - onSubmit?( - data: ProductUpdatePageSubmitData, - nextAction?: ProductUpdatePageSubmitNextAction - ); - onSubmitSkip?(nextAction?: ProductUpdatePageSubmitNextAction); + onSubmit?(data: ProductUpdatePageSubmitData, nextAction?: () => void); onVariantAdd?(); onSetDefaultVariant(); + onWarehouseConfigure(); } export interface ProductUpdatePageSubmitData extends ProductUpdatePageFormData { @@ -131,12 +124,12 @@ export const ProductUpdatePage: React.FC = ({ onImageUpload, onSeoClick, onSubmit, - onSubmitSkip, onVariantAdd, onVariantsAdd, onSetDefaultVariant, onVariantShow, onVariantReorder, + onWarehouseConfigure, isChecked, selected, toggle, @@ -200,11 +193,10 @@ export const ProductUpdatePage: React.FC = ({ value: taxType.taxCode })) || []; - const [modalWithAction, setModalWithAction] = React.useState< - ProductUpdatePageSubmitNextAction - >(null); - - const handleSubmit = (data: ProductUpdatePageFormData) => { + const handleSubmit = ( + data: ProductUpdatePageFormData, + nextAction: () => void + ) => { const metadata = isMetadataModified ? data.metadata : undefined; const privateMetadata = isPrivateMetadataModified ? data.privateMetadata @@ -221,7 +213,7 @@ export const ProductUpdatePage: React.FC = ({ removeStocks: [], updateStocks: [] }, - modalWithAction + nextAction ); } else { const dataStocks = stocks.map(stock => stock.id); @@ -245,15 +237,22 @@ export const ProductUpdatePage: React.FC = ({ !stockDiff.added.some(addedStock => addedStock === stock.id) ) }, - modalWithAction + nextAction ); } - setModalWithAction(null); }; return (
- {({ change, data, hasChanged, submit, triggerChange, toggleValue }) => { + {({ + change, + data, + hasChanged, + submit, + triggerChange, + toggleValue, + askToLeave + }) => { const handleCollectionSelect = createMultiAutocompleteSelectHandler( toggleValue, setSelectedCollections, @@ -393,9 +392,9 @@ export const ProductUpdatePage: React.FC = ({ }} onWarehouseConfigure={() => { if (disabled || !onSubmit || !hasChanged) { - onSubmitSkip("warehouse-configure"); + onWarehouseConfigure(); } else { - setModalWithAction("warehouse-configure"); + askToLeave(onWarehouseConfigure); } }} /> @@ -487,17 +486,6 @@ export const ProductUpdatePage: React.FC = ({ state={saveButtonBarState} disabled={disabled || !hasChanged} /> - { - submit(); - }} - onRejectChanges={() => { - onSubmitSkip("warehouse-configure"); - }} - onClose={() => setModalWithAction(null)} - open={modalWithAction === "warehouse-configure"} - confirmButtonState={saveButtonBarState} - /> ); diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 6321b4e57..6e1225941 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -38,9 +38,7 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { getMutationState, maybe } from "../../../misc"; -import ProductUpdatePage, { - ProductUpdatePageSubmitNextAction -} from "../../components/ProductUpdatePage"; +import ProductUpdatePage from "../../components/ProductUpdatePage"; import { useProductDetails } from "../../queries"; import { ProductImageCreateVariables } from "../../types/ProductImageCreate"; import { ProductUpdate as ProductUpdateMutationResult } from "../../types/ProductUpdate"; @@ -281,14 +279,6 @@ export const ProductUpdate: React.FC = ({ id, params }) => { null ); - const handleSubmitNextAction = ( - nextAction?: ProductUpdatePageSubmitNextAction - ) => { - if (nextAction === "warehouse-configure") { - navigate(warehouseListPath); - } - }; - return ( <> data.product.name)} /> @@ -316,11 +306,11 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onImageReorder={handleImageReorder} onSubmit={async (data, nextAction) => { const errors = await handleSubmit(data); - if (errors?.length === 0) { - handleSubmitNextAction(nextAction); + if (errors?.length === 0 && nextAction) { + nextAction(); } }} - onSubmitSkip={handleSubmitNextAction} + onWarehouseConfigure={() => navigate(warehouseListPath)} onVariantAdd={handleVariantAdd} onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => diff --git a/src/storybook/stories/products/ProductUpdatePage.tsx b/src/storybook/stories/products/ProductUpdatePage.tsx index c44841a1b..980d68a84 100644 --- a/src/storybook/stories/products/ProductUpdatePage.tsx +++ b/src/storybook/stories/products/ProductUpdatePage.tsx @@ -39,6 +39,7 @@ const props: ProductUpdatePageProps = { onVariantReorder: () => undefined, onVariantShow: () => undefined, onVariantsAdd: () => undefined, + onWarehouseConfigure: () => undefined, placeholderImage, product, saveButtonBarState: "default", From 4b017811571053a101cfdf89858aa36153420ff2 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 29 Sep 2020 11:06:42 +0200 Subject: [PATCH 12/16] Use ref to persist leave action --- src/components/Form/Form.tsx | 2 +- src/hooks/useForm.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index d7ae58fcb..58802b200 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -39,7 +39,7 @@ function Form(props: FormProps) { {children(renderProps)} renderProps.askToLeave(null)} open={!!renderProps.leaveModal} confirmButtonState="default" diff --git a/src/hooks/useForm.ts b/src/hooks/useForm.ts index a08c8971b..9b18c77da 100644 --- a/src/hooks/useForm.ts +++ b/src/hooks/useForm.ts @@ -1,6 +1,6 @@ import { toggle } from "@saleor/utils/lists"; import isEqual from "lodash-es/isEqual"; -import { useState } from "react"; +import { MutableRefObject, useRef, useState } from "react"; import useStateFromProps from "./useStateFromProps"; @@ -15,7 +15,7 @@ export type FormChange = (event: ChangeEvent, cb?: () => void) => void; export interface UseFormResult { askToLeave: (action: () => void | null) => void; - leaveAction: () => void | null; + leaveAction: MutableRefObject<() => void | null>; leaveModal: boolean; change: FormChange; data: T; @@ -62,7 +62,7 @@ function useForm( onRefresh: newData => handleRefresh(data, newData, setChanged) }); const [leaveModal, setLeaveModal] = useState(false); - const [leaveAction, setLeaveAction] = useState<() => void>(null); + const leaveAction = useRef<() => void>(null); function toggleValue(event: ChangeEvent, cb?: () => void) { const { name, value } = event.target; @@ -112,7 +112,7 @@ function useForm( } function submit() { - onSubmit(data, leaveAction); + onSubmit(data, leaveAction.current); setLeaveModal(false); } @@ -122,7 +122,7 @@ function useForm( function askToLeave(action: () => void | null) { setLeaveModal(() => !!action); - setLeaveAction(() => action); + leaveAction.current = action; } return { From bf18651f7df0359f1adbd02183d048a20c9a06b9 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 29 Sep 2020 11:54:26 +0200 Subject: [PATCH 13/16] Remove leave screen dialog --- locale/defaultMessages.json | 8 -- src/components/Form/Form.tsx | 17 +---- .../LeaveScreenDialog/LeaveScreenDialog.tsx | 56 -------------- src/components/LeaveScreenDialog/index.ts | 2 - src/hooks/useForm.ts | 20 +---- .../ProductCreatePage/ProductCreatePage.tsx | 53 +++---------- .../ProductUpdatePage/ProductUpdatePage.tsx | 76 +++++++------------ .../ProductVariantCreatePage.tsx | 56 +++----------- .../ProductVariantPage/ProductVariantPage.tsx | 67 +++++----------- src/products/views/ProductCreate.tsx | 35 ++------- .../views/ProductUpdate/ProductUpdate.tsx | 7 +- src/products/views/ProductVariant.tsx | 20 +---- src/products/views/ProductVariantCreate.tsx | 47 +++--------- .../stories/products/ProductCreatePage.tsx | 3 + .../products/ProductVariantCreatePage.tsx | 4 + .../stories/products/ProductVariantPage.tsx | 3 + src/utils/handlers/metadataCreateHandler.ts | 11 +-- 17 files changed, 103 insertions(+), 382 deletions(-) delete mode 100644 src/components/LeaveScreenDialog/LeaveScreenDialog.tsx delete mode 100644 src/components/LeaveScreenDialog/index.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index e8ee128be..34a2ebe2f 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -1593,14 +1593,6 @@ "context": "button", "string": "{languageName} - {languageCode}" }, - "src_dot_components_dot_LeaveScreenDialog_dot_1295493095": { - "context": "leaving screen warning header", - "string": "You're leaving this screen" - }, - "src_dot_components_dot_LeaveScreenDialog_dot_4066894204": { - "context": "leaving screen warning message", - "string": "Do you want to save previously made changes?" - }, "src_dot_components_dot_ListField_dot_3099331554": { "context": "button", "string": "Add" diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index 58802b200..2721f678a 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -1,14 +1,12 @@ import useForm, { UseFormResult } from "@saleor/hooks/useForm"; import React from "react"; -import LeaveScreenDialog from "../LeaveScreenDialog"; - export interface FormProps { children: (props: UseFormResult) => React.ReactNode; confirmLeave?: boolean; initial?: T; resetOnSubmit?: boolean; - onSubmit?: (data: T, nextAction: () => void) => void; + onSubmit?: (data: T) => void; } function Form(props: FormProps) { @@ -34,18 +32,7 @@ function Form(props: FormProps) { submit(); } - return ( - - {children(renderProps)} - renderProps.askToLeave(null)} - open={!!renderProps.leaveModal} - confirmButtonState="default" - /> - - ); + return
{children(renderProps)}
; } Form.displayName = "Form"; diff --git a/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx b/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx deleted file mode 100644 index 5b9803442..000000000 --- a/src/components/LeaveScreenDialog/LeaveScreenDialog.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import Button from "@material-ui/core/Button"; -import Dialog from "@material-ui/core/Dialog"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import { commonMessages } from "@saleor/intl"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -import ConfirmButton, { ConfirmButtonTransitionState } from "../ConfirmButton"; - -export interface LeaveScreenDialogProps { - confirmButtonState: ConfirmButtonTransitionState; - open: boolean; - onClose: () => void; - onSaveChanges: () => void; - onRejectChanges: () => void; -} - -const LeaveScreenDialog: React.FC = ({ - confirmButtonState, - onClose, - onSaveChanges, - onRejectChanges, - open -}) => ( - - - - - - - - - - - - - - -); -LeaveScreenDialog.displayName = "LeaveScreenDialog"; -export default LeaveScreenDialog; diff --git a/src/components/LeaveScreenDialog/index.ts b/src/components/LeaveScreenDialog/index.ts deleted file mode 100644 index c6bfa632e..000000000 --- a/src/components/LeaveScreenDialog/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./LeaveScreenDialog"; -export * from "./LeaveScreenDialog"; diff --git a/src/hooks/useForm.ts b/src/hooks/useForm.ts index 9b18c77da..752030949 100644 --- a/src/hooks/useForm.ts +++ b/src/hooks/useForm.ts @@ -1,6 +1,6 @@ import { toggle } from "@saleor/utils/lists"; import isEqual from "lodash-es/isEqual"; -import { MutableRefObject, useRef, useState } from "react"; +import { useState } from "react"; import useStateFromProps from "./useStateFromProps"; @@ -14,9 +14,6 @@ export interface ChangeEvent { export type FormChange = (event: ChangeEvent, cb?: () => void) => void; export interface UseFormResult { - askToLeave: (action: () => void | null) => void; - leaveAction: MutableRefObject<() => void | null>; - leaveModal: boolean; change: FormChange; data: T; hasChanged: boolean; @@ -54,15 +51,13 @@ function handleRefresh( function useForm( initial: T, - onSubmit: (data: T, nextAction: () => void) => void + onSubmit: (data: T) => void ): UseFormResult { const [hasChanged, setChanged] = useState(false); const [data, setData] = useStateFromProps(initial, { mergeFunc: merge, onRefresh: newData => handleRefresh(data, newData, setChanged) }); - const [leaveModal, setLeaveModal] = useState(false); - const leaveAction = useRef<() => void>(null); function toggleValue(event: ChangeEvent, cb?: () => void) { const { name, value } = event.target; @@ -112,26 +107,17 @@ function useForm( } function submit() { - onSubmit(data, leaveAction.current); - setLeaveModal(false); + return onSubmit(data); } function triggerChange() { setChanged(true); } - function askToLeave(action: () => void | null) { - setLeaveModal(() => !!action); - leaveAction.current = action; - } - return { - askToLeave, change, data, hasChanged, - leaveAction, - leaveModal, reset, set, submit, diff --git a/src/products/components/ProductCreatePage/ProductCreatePage.tsx b/src/products/components/ProductCreatePage/ProductCreatePage.tsx index f0baedfb2..21464797b 100644 --- a/src/products/components/ProductCreatePage/ProductCreatePage.tsx +++ b/src/products/components/ProductCreatePage/ProductCreatePage.tsx @@ -5,7 +5,6 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import Metadata, { MetadataFormData } from "@saleor/components/Metadata"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import PageHeader from "@saleor/components/PageHeader"; @@ -79,8 +78,6 @@ export interface ProductCreatePageSubmitData extends FormData { stocks: ProductStockInput[]; } -export type ProductCreatePageSubmitNextAction = "warehouse-configure"; - interface ProductCreatePageProps { errors: ProductErrorFragment[]; collections: SearchCollections_search_edges_node[]; @@ -101,16 +98,12 @@ interface ProductCreatePageProps { weightUnit: string; warehouses: SearchWarehouses_search_edges_node[]; taxTypes: TaxTypeFragment[]; - submitNextAction?: ProductCreatePageSubmitNextAction; fetchCategories: (data: string) => void; fetchCollections: (data: string) => void; fetchProductTypes: (data: string) => void; + onWarehouseConfigure: () => void; onBack?(); - onSubmit?( - data: ProductCreatePageSubmitData, - nextAction?: ProductCreatePageSubmitNextAction - ); - onSubmitSkip?(nextAction?: ProductCreatePageSubmitNextAction); + onSubmit?(data: ProductCreatePageSubmitData); } export const ProductCreatePage: React.FC = ({ @@ -133,7 +126,7 @@ export const ProductCreatePage: React.FC = ({ fetchProductTypes, weightUnit, onSubmit, - onSubmitSkip + onWarehouseConfigure }: ProductCreatePageProps) => { const intl = useIntl(); const localizeDate = useDateLocalize(); @@ -210,21 +203,12 @@ export const ProductCreatePage: React.FC = ({ value: taxType.taxCode })) || []; - const [modalWithAction, setModalWithAction] = React.useState< - ProductCreatePageSubmitNextAction - >(null); - - const handleSubmit = (data: FormData) => { - onSubmit( - { - ...data, - attributes, - stocks - }, - modalWithAction - ); - setModalWithAction(null); - }; + const handleSubmit = (data: FormData) => + onSubmit({ + ...data, + attributes, + stocks + }); return (
@@ -339,13 +323,7 @@ export const ProductCreatePage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={() => { - if (disabled || !onSubmit || !hasChanged) { - onSubmitSkip("warehouse-configure"); - } else { - setModalWithAction("warehouse-configure"); - } - }} + onWarehouseConfigure={onWarehouseConfigure} /> @@ -434,17 +412,6 @@ export const ProductCreatePage: React.FC = ({ state={saveButtonBarState} disabled={disabled || !onSubmit || !hasChanged} /> - { - submit(); - }} - onRejectChanges={() => { - onSubmitSkip("warehouse-configure"); - }} - onClose={() => setModalWithAction(null)} - open={modalWithAction === "warehouse-configure"} - confirmButtonState={saveButtonBarState} - /> ); }} diff --git a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx index b263bd57e..13e56879d 100644 --- a/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx +++ b/src/products/components/ProductUpdatePage/ProductUpdatePage.tsx @@ -84,7 +84,7 @@ export interface ProductUpdatePageProps extends ListActions { onImageReorder?(event: { oldIndex: number; newIndex: number }); onImageUpload(file: File); onSeoClick?(); - onSubmit?(data: ProductUpdatePageSubmitData, nextAction?: () => void); + onSubmit?(data: ProductUpdatePageSubmitData); onVariantAdd?(); onSetDefaultVariant(); onWarehouseConfigure(); @@ -193,28 +193,22 @@ export const ProductUpdatePage: React.FC = ({ value: taxType.taxCode })) || []; - const handleSubmit = ( - data: ProductUpdatePageFormData, - nextAction: () => void - ) => { + const handleSubmit = (data: ProductUpdatePageFormData) => { const metadata = isMetadataModified ? data.metadata : undefined; const privateMetadata = isPrivateMetadataModified ? data.privateMetadata : undefined; if (product.productType.hasVariants) { - onSubmit( - { - ...data, - addStocks: [], - attributes, - metadata, - privateMetadata, - removeStocks: [], - updateStocks: [] - }, - nextAction - ); + onSubmit({ + ...data, + addStocks: [], + attributes, + metadata, + privateMetadata, + removeStocks: [], + updateStocks: [] + }); } else { const dataStocks = stocks.map(stock => stock.id); const variantStocks = product.variants[0]?.stocks.map( @@ -222,37 +216,25 @@ export const ProductUpdatePage: React.FC = ({ ); const stockDiff = diff(variantStocks, dataStocks); - onSubmit( - { - ...data, - addStocks: stocks.filter(stock => - stockDiff.added.some(addedStock => addedStock === stock.id) - ), - attributes, - metadata, - privateMetadata, - removeStocks: stockDiff.removed, - updateStocks: stocks.filter( - stock => - !stockDiff.added.some(addedStock => addedStock === stock.id) - ) - }, - nextAction - ); + onSubmit({ + ...data, + addStocks: stocks.filter(stock => + stockDiff.added.some(addedStock => addedStock === stock.id) + ), + attributes, + metadata, + privateMetadata, + removeStocks: stockDiff.removed, + updateStocks: stocks.filter( + stock => !stockDiff.added.some(addedStock => addedStock === stock.id) + ) + }); } }; return ( - {({ - change, - data, - hasChanged, - submit, - triggerChange, - toggleValue, - askToLeave - }) => { + {({ change, data, hasChanged, submit, triggerChange, toggleValue }) => { const handleCollectionSelect = createMultiAutocompleteSelectHandler( toggleValue, setSelectedCollections, @@ -390,13 +372,7 @@ export const ProductUpdatePage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={() => { - if (disabled || !onSubmit || !hasChanged) { - onWarehouseConfigure(); - } else { - askToLeave(onWarehouseConfigure); - } - }} + onWarehouseConfigure={onWarehouseConfigure} /> )} diff --git a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx index 7117a09bf..8c345e4f2 100644 --- a/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx +++ b/src/products/components/ProductVariantCreatePage/ProductVariantCreatePage.tsx @@ -4,7 +4,6 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import Metadata, { MetadataFormData } from "@saleor/components/Metadata"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; @@ -46,8 +45,6 @@ export interface ProductVariantCreatePageSubmitData stocks: ProductStockInput[]; } -export type ProductVariantCreatePageSubmitNextAction = "warehouse-configure"; - interface ProductVariantCreatePageProps { currencySymbol: string; disabled: boolean; @@ -58,15 +55,10 @@ interface ProductVariantCreatePageProps { warehouses: SearchWarehouses_search_edges_node[]; weightUnit: string; onBack: () => void; - onSubmit: ( - data: ProductVariantCreatePageSubmitData, - nextAction?: ProductVariantCreatePageSubmitNextAction - ) => void; - onSubmitSkip?: ( - nextAction?: ProductVariantCreatePageSubmitNextAction - ) => void; + onSubmit: (data: ProductVariantCreatePageSubmitData) => void; onVariantClick: (variantId: string) => void; onVariantReorder: ReorderAction; + onWarehouseConfigure: () => void; } const ProductVariantCreatePage: React.FC = ({ @@ -80,9 +72,9 @@ const ProductVariantCreatePage: React.FC = ({ weightUnit, onBack, onSubmit, - onSubmitSkip, onVariantClick, - onVariantReorder + onVariantReorder, + onWarehouseConfigure }) => { const intl = useIntl(); const attributeInput = React.useMemo( @@ -114,21 +106,12 @@ const ProductVariantCreatePage: React.FC = ({ weight: "" }; - const [modalWithAction, setModalWithAction] = React.useState< - ProductVariantCreatePageSubmitNextAction - >(null); - - const handleSubmit = (data: ProductVariantCreatePageFormData) => { - onSubmit( - { - ...data, - attributes, - stocks - }, - modalWithAction - ); - setModalWithAction(null); - }; + const handleSubmit = (data: ProductVariantCreatePageFormData) => + onSubmit({ + ...data, + attributes, + stocks + }); return ( @@ -207,13 +190,7 @@ const ProductVariantCreatePage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={() => { - if (disabled || !onSubmit || !hasChanged) { - onSubmitSkip("warehouse-configure"); - } else { - setModalWithAction("warehouse-configure"); - } - }} + onWarehouseConfigure={onWarehouseConfigure} /> @@ -235,17 +212,6 @@ const ProductVariantCreatePage: React.FC = ({ onCancel={onBack} onSave={submit} /> - { - submit(); - }} - onRejectChanges={() => { - onSubmitSkip("warehouse-configure"); - }} - onClose={() => setModalWithAction(null)} - open={modalWithAction === "warehouse-configure"} - confirmButtonState={saveButtonBarState} - /> ); }} diff --git a/src/products/components/ProductVariantPage/ProductVariantPage.tsx b/src/products/components/ProductVariantPage/ProductVariantPage.tsx index a2eecb180..d875acf0d 100644 --- a/src/products/components/ProductVariantPage/ProductVariantPage.tsx +++ b/src/products/components/ProductVariantPage/ProductVariantPage.tsx @@ -4,7 +4,6 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; -import LeaveScreenDialog from "@saleor/components/LeaveScreenDialog"; import { MetadataFormData } from "@saleor/components/Metadata"; import Metadata from "@saleor/components/Metadata/Metadata"; import PageHeader from "@saleor/components/PageHeader"; @@ -54,8 +53,6 @@ export interface ProductVariantPageSubmitData removeStocks: string[]; } -export type ProductVariantPageSubmitNextAction = "warehouse-configure"; - interface ProductVariantPageProps { defaultWeightUnit: string; variant?: ProductVariant; @@ -69,14 +66,11 @@ interface ProductVariantPageProps { onAdd(); onBack(); onDelete(); - onSubmit( - data: ProductVariantPageSubmitData, - nextAction?: ProductVariantPageSubmitNextAction - ); - onSubmitSkip?(nextAction?: ProductVariantPageSubmitNextAction); + onSubmit(data: ProductVariantPageSubmitData); onImageSelect(id: string); onVariantClick(variantId: string); onSetDefaultVariant(); + onWarehouseConfigure(); } const ProductVariantPage: React.FC = ({ @@ -93,10 +87,10 @@ const ProductVariantPage: React.FC = ({ onDelete, onImageSelect, onSubmit, - onSubmitSkip, onVariantClick, onVariantReorder, - onSetDefaultVariant + onSetDefaultVariant, + onWarehouseConfigure }) => { const attributeInput = React.useMemo( () => getAttributeInputFromVariant(variant), @@ -146,10 +140,6 @@ const ProductVariantPage: React.FC = ({ weight: variant?.weight?.value.toString() || "" }; - const [modalWithAction, setModalWithAction] = React.useState< - ProductVariantPageSubmitNextAction - >(null); - const handleSubmit = (data: ProductVariantPageFormData) => { const dataStocks = stocks.map(stock => stock.id); const variantStocks = variant.stocks.map(stock => stock.warehouse.id); @@ -159,23 +149,19 @@ const ProductVariantPage: React.FC = ({ ? data.privateMetadata : undefined; - onSubmit( - { - ...data, - addStocks: stocks.filter(stock => - stockDiff.added.some(addedStock => addedStock === stock.id) - ), - attributes, - metadata, - privateMetadata, - removeStocks: stockDiff.removed, - updateStocks: stocks.filter( - stock => !stockDiff.added.some(addedStock => addedStock === stock.id) - ) - }, - modalWithAction - ); - setModalWithAction(null); + onSubmit({ + ...data, + addStocks: stocks.filter(stock => + stockDiff.added.some(addedStock => addedStock === stock.id) + ), + attributes, + metadata, + privateMetadata, + removeStocks: stockDiff.removed, + updateStocks: stocks.filter( + stock => !stockDiff.added.some(addedStock => addedStock === stock.id) + ) + }); }; return ( @@ -280,13 +266,7 @@ const ProductVariantPage: React.FC = ({ triggerChange(); removeStock(id); }} - onWarehouseConfigure={() => { - if (!onSubmit || !hasChanged) { - onSubmitSkip("warehouse-configure"); - } else { - setModalWithAction("warehouse-configure"); - } - }} + onWarehouseConfigure={onWarehouseConfigure} /> @@ -299,17 +279,6 @@ const ProductVariantPage: React.FC = ({ onDelete={onDelete} onSave={submit} /> - { - submit(); - }} - onRejectChanges={() => { - onSubmitSkip("warehouse-configure"); - }} - onClose={() => setModalWithAction(null)} - open={modalWithAction === "warehouse-configure"} - confirmButtonState={saveButtonBarState} - /> ); }} diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 8bf072c74..168b21bc3 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -20,8 +20,7 @@ import { useIntl } from "react-intl"; import { decimal, weight } from "../../misc"; import ProductCreatePage, { - ProductCreatePageSubmitData, - ProductCreatePageSubmitNextAction + ProductCreatePageSubmitData } from "../components/ProductCreatePage"; import { useProductCreateMutation, @@ -73,7 +72,7 @@ export const ProductCreateView: React.FC = () => { ] = useProductSetAvailabilityForPurchase({ onCompleted: data => { const errors = data?.productSetAvailabilityForPurchase?.errors; - if (errors?.length === 0 && !submitNextAction) { + if (errors?.length === 0) { navigate(productUrl(data.productSetAvailabilityForPurchase.product.id)); } } @@ -92,12 +91,7 @@ export const ProductCreateView: React.FC = () => { } }); - const handleCreate = async ( - formData: ProductCreatePageSubmitData, - nextAction?: ProductCreatePageSubmitNextAction - ) => { - setSubmitNextAction(nextAction); - + const handleCreate = async (formData: ProductCreatePageSubmitData) => { const result = await productCreate({ variables: { input: { @@ -157,18 +151,6 @@ export const ProductCreateView: React.FC = () => { updatePrivateMetadata ); - const [submitNextAction, setSubmitNextAction] = React.useState< - ProductCreatePageSubmitNextAction - >(null); - const handleSubmitNextAction = ( - nextAction?: ProductCreatePageSubmitNextAction - ) => { - const action = nextAction || submitNextAction; - if (action === "warehouse-configure") { - navigate(warehouseListPath); - } - }; - return ( <> { edge => edge.node )} onBack={handleBack} - onSubmit={async (data, nextAction) => { - const errors = await handleSubmit(data, nextAction); - if (errors?.length === 0) { - handleSubmitNextAction(nextAction); - } else { - setSubmitNextAction(null); - } - }} - onSubmitSkip={handleSubmitNextAction} + onSubmit={handleSubmit} + onWarehouseConfigure={() => navigate(warehouseListPath)} saveButtonBarState={productCreateOpts.status} fetchMoreCategories={{ hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index 6e1225941..ae12fe679 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -304,12 +304,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onBack={handleBack} onDelete={() => openModal("remove")} onImageReorder={handleImageReorder} - onSubmit={async (data, nextAction) => { - const errors = await handleSubmit(data); - if (errors?.length === 0 && nextAction) { - nextAction(); - } - }} + onSubmit={handleSubmit} onWarehouseConfigure={() => navigate(warehouseListPath)} onVariantAdd={handleVariantAdd} onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index d5870eeda..acbc500c5 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -20,8 +20,7 @@ import { useIntl } from "react-intl"; import { decimal, weight } from "../../misc"; import ProductVariantDeleteDialog from "../components/ProductVariantDeleteDialog"; import ProductVariantPage, { - ProductVariantPageSubmitData, - ProductVariantPageSubmitNextAction + ProductVariantPageSubmitData } from "../components/ProductVariantPage"; import { useProductVariantReorderMutation, @@ -198,14 +197,6 @@ export const ProductVariant: React.FC = ({ variables => updatePrivateMetadata({ variables }) ); - const handleSubmitNextAction = ( - nextAction?: ProductVariantPageSubmitNextAction - ) => { - if (nextAction === "warehouse-configure") { - navigate(warehouseListPath); - } - }; - return ( <> @@ -225,13 +216,8 @@ export const ProductVariant: React.FC = ({ onBack={handleBack} onDelete={() => openModal("remove")} onImageSelect={handleImageSelect} - onSubmit={async (data, nextAction) => { - const errors = await handleSubmit(data); - if (errors?.length === 0) { - handleSubmitNextAction(nextAction); - } - }} - onSubmitSkip={handleSubmitNextAction} + onSubmit={handleSubmit} + onWarehouseConfigure={() => navigate(warehouseListPath)} onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 23d4f9ea1..06c9faa3a 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -16,8 +16,7 @@ import { useIntl } from "react-intl"; import { decimal, weight } from "../../misc"; import ProductVariantCreatePage, { - ProductVariantCreatePageSubmitData, - ProductVariantCreatePageSubmitNextAction + ProductVariantCreatePageSubmitData } from "../components/ProductVariantCreatePage"; import { useProductVariantReorderMutation, @@ -57,14 +56,12 @@ export const ProductVariant: React.FC = ({ status: "success", text: intl.formatMessage(commonMessages.savedChanges) }); - if (!submitNextAction) { - navigate( - productVariantEditUrl( - productId, - data.productVariantCreate.productVariant.id - ) - ); - } + navigate( + productVariantEditUrl( + productId, + data.productVariantCreate.productVariant.id + ) + ); } } }); @@ -87,12 +84,7 @@ export const ProductVariant: React.FC = ({ ); const handleBack = () => navigate(productUrl(productId)); - const handleCreate = async ( - formData: ProductVariantCreatePageSubmitData, - nextAction?: ProductVariantCreatePageSubmitNextAction - ) => { - setSubmitNextAction(nextAction); - + const handleCreate = async (formData: ProductVariantCreatePageSubmitData) => { const result = await variantCreate({ variables: { input: { @@ -126,18 +118,6 @@ export const ProductVariant: React.FC = ({ const handleVariantClick = (id: string) => navigate(productVariantEditUrl(productId, id)); - const [submitNextAction, setSubmitNextAction] = React.useState< - ProductVariantCreatePageSubmitNextAction - >(null); - const handleSubmitNextAction = ( - nextAction?: ProductVariantCreatePageSubmitNextAction - ) => { - const action = nextAction || submitNextAction; - if (action === "warehouse-configure") { - navigate(warehouseListPath); - } - }; - const disableForm = productLoading || variantCreateResult.loading || @@ -161,16 +141,9 @@ export const ProductVariant: React.FC = ({ })} product={data?.product} onBack={handleBack} - onSubmit={async (data, nextAction) => { - const errors = await handleSubmit(data, nextAction); - if (errors?.length === 0) { - handleSubmitNextAction(nextAction); - } else { - setSubmitNextAction(null); - } - }} - onSubmitSkip={handleSubmitNextAction} + onSubmit={handleSubmit} onVariantClick={handleVariantClick} + onWarehouseConfigure={() => navigate(warehouseListPath)} onVariantReorder={handleVariantReorder} saveButtonBarState={variantCreateResult.status} warehouses={ diff --git a/src/storybook/stories/products/ProductCreatePage.tsx b/src/storybook/stories/products/ProductCreatePage.tsx index babd8504a..df02a860f 100644 --- a/src/storybook/stories/products/ProductCreatePage.tsx +++ b/src/storybook/stories/products/ProductCreatePage.tsx @@ -35,6 +35,7 @@ storiesOf("Views / Products / Create product", module) onSubmit={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} taxTypes={taxTypes} weightUnit="kg" /> @@ -58,6 +59,7 @@ storiesOf("Views / Products / Create product", module) onSubmit={() => undefined} saveButtonBarState="default" warehouses={undefined} + onWarehouseConfigure={() => undefined} taxTypes={taxTypes} weightUnit="kg" /> @@ -87,6 +89,7 @@ storiesOf("Views / Products / Create product", module) onSubmit={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} taxTypes={taxTypes} weightUnit="kg" /> diff --git a/src/storybook/stories/products/ProductVariantCreatePage.tsx b/src/storybook/stories/products/ProductVariantCreatePage.tsx index d8578724d..d0fdbea77 100644 --- a/src/storybook/stories/products/ProductVariantCreatePage.tsx +++ b/src/storybook/stories/products/ProductVariantCreatePage.tsx @@ -26,6 +26,7 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("with errors", () => ( @@ -58,6 +59,7 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("when loading data", () => ( @@ -74,6 +76,7 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("add first variant", () => ( @@ -93,5 +96,6 @@ storiesOf("Views / Products / Create product variant", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )); diff --git a/src/storybook/stories/products/ProductVariantPage.tsx b/src/storybook/stories/products/ProductVariantPage.tsx index 038c03372..a70095d78 100644 --- a/src/storybook/stories/products/ProductVariantPage.tsx +++ b/src/storybook/stories/products/ProductVariantPage.tsx @@ -28,6 +28,7 @@ storiesOf("Views / Products / Product variant details", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("when loading data", () => ( @@ -47,6 +48,7 @@ storiesOf("Views / Products / Product variant details", module) onVariantReorder={() => undefined} saveButtonBarState="default" warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )) .add("attribute errors", () => ( @@ -82,5 +84,6 @@ storiesOf("Views / Products / Product variant details", module) ...error }))} warehouses={warehouseList} + onWarehouseConfigure={() => undefined} /> )); diff --git a/src/utils/handlers/metadataCreateHandler.ts b/src/utils/handlers/metadataCreateHandler.ts index 73bb5a619..e24491b80 100644 --- a/src/utils/handlers/metadataCreateHandler.ts +++ b/src/utils/handlers/metadataCreateHandler.ts @@ -10,19 +10,16 @@ import { UpdatePrivateMetadataVariables } from "../metadata/types/UpdatePrivateMetadata"; -function createMetadataCreateHandler< - T extends MetadataFormData, - N extends string ->( - create: (data: T, nextAction?: N) => Promise, +function createMetadataCreateHandler( + create: (data: T) => Promise, setMetadata: MutationFunction, setPrivateMetadata: MutationFunction< UpdatePrivateMetadata, UpdatePrivateMetadataVariables > ) { - return async (data: T, nextAction?: N) => { - const id = await create(data, nextAction); + return async (data: T) => { + const id = await create(data); if (id === null) { return null; From dc43578d5adcba1aae04a63c94858a67541d5731 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 29 Sep 2020 15:18:07 +0200 Subject: [PATCH 14/16] Update configure warehouse link --- locale/defaultMessages.json | 4 +- .../ProductStocks/ProductStocks.tsx | 44 +++++++++++-------- src/products/views/ProductCreate.tsx | 4 +- .../views/ProductUpdate/ProductUpdate.tsx | 4 +- src/products/views/ProductVariant.tsx | 4 +- src/products/views/ProductVariantCreate.tsx | 4 +- 6 files changed, 36 insertions(+), 28 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 34a2ebe2f..e0f9d7b51 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -316,7 +316,7 @@ }, "productVariantWarehouseSectionDescription": { "context": "no warehouses info", - "string": "There are no warehouses set up for your store. To add stock quantity to the variant please" + "string": "There are no warehouses set up for your store. To add stock quantity to the variant please {configureWarehouseUrl}" }, "productVariantWarehouseSectionDescriptionLink": { "context": "no warehouses info", @@ -324,7 +324,7 @@ }, "productWarehouseSectionDescription": { "context": "no warehouses info", - "string": "There are no warehouses set up for your store. To add stock quantity to the product please" + "string": "There are no warehouses set up for your store. To add stock quantity to the product please {configureWarehouseUrl}" }, "productWarehouseSectionDescriptionLink": { "context": "no warehouses info", diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx index cab8dfe8f..81ed77dc1 100644 --- a/src/products/components/ProductStocks/ProductStocks.tsx +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -191,32 +191,40 @@ const ProductStocks: React.FC = ({ {hasVariants ? ( <> {" "} - - - + values={{ + configureWarehouseUrl: ( + + + + ) + }} + /> ) : ( <> {" "} - - - + values={{ + configureWarehouseUrl: ( + + + + ) + }} + /> )} diff --git a/src/products/views/ProductCreate.tsx b/src/products/views/ProductCreate.tsx index 168b21bc3..b5df23fcd 100644 --- a/src/products/views/ProductCreate.tsx +++ b/src/products/views/ProductCreate.tsx @@ -14,7 +14,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; -import { warehouseListPath } from "@saleor/warehouses/urls"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -181,7 +181,7 @@ export const ProductCreateView: React.FC = () => { )} onBack={handleBack} onSubmit={handleSubmit} - onWarehouseConfigure={() => navigate(warehouseListPath)} + onWarehouseConfigure={() => navigate(warehouseAddPath)} saveButtonBarState={productCreateOpts.status} fetchMoreCategories={{ hasMore: searchCategoryOpts.data?.search.pageInfo.hasNextPage, diff --git a/src/products/views/ProductUpdate/ProductUpdate.tsx b/src/products/views/ProductUpdate/ProductUpdate.tsx index ae12fe679..294db7989 100644 --- a/src/products/views/ProductUpdate/ProductUpdate.tsx +++ b/src/products/views/ProductUpdate/ProductUpdate.tsx @@ -33,7 +33,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; -import { warehouseListPath } from "@saleor/warehouses/urls"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; @@ -305,7 +305,7 @@ export const ProductUpdate: React.FC = ({ id, params }) => { onDelete={() => openModal("remove")} onImageReorder={handleImageReorder} onSubmit={handleSubmit} - onWarehouseConfigure={() => navigate(warehouseListPath)} + onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantAdd={handleVariantAdd} onVariantsAdd={() => navigate(productVariantCreatorUrl(id))} onVariantShow={variantId => () => diff --git a/src/products/views/ProductVariant.tsx b/src/products/views/ProductVariant.tsx index acbc500c5..e2325f729 100644 --- a/src/products/views/ProductVariant.tsx +++ b/src/products/views/ProductVariant.tsx @@ -13,7 +13,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; -import { warehouseListPath } from "@saleor/warehouses/urls"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React, { useEffect, useState } from "react"; import { useIntl } from "react-intl"; @@ -217,7 +217,7 @@ export const ProductVariant: React.FC = ({ onDelete={() => openModal("remove")} onImageSelect={handleImageSelect} onSubmit={handleSubmit} - onWarehouseConfigure={() => navigate(warehouseListPath)} + onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantClick={variantId => { navigate(productVariantEditUrl(productId, variantId)); }} diff --git a/src/products/views/ProductVariantCreate.tsx b/src/products/views/ProductVariantCreate.tsx index 06c9faa3a..80516e2b2 100644 --- a/src/products/views/ProductVariantCreate.tsx +++ b/src/products/views/ProductVariantCreate.tsx @@ -10,7 +10,7 @@ import { usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { useWarehouseList } from "@saleor/warehouses/queries"; -import { warehouseListPath } from "@saleor/warehouses/urls"; +import { warehouseAddPath } from "@saleor/warehouses/urls"; import React from "react"; import { useIntl } from "react-intl"; @@ -143,7 +143,7 @@ export const ProductVariant: React.FC = ({ onBack={handleBack} onSubmit={handleSubmit} onVariantClick={handleVariantClick} - onWarehouseConfigure={() => navigate(warehouseListPath)} + onWarehouseConfigure={() => navigate(warehouseAddPath)} onVariantReorder={handleVariantReorder} saveButtonBarState={variantCreateResult.status} warehouses={ From 61723bd045e5594e29a105292cfc921ee054dcb0 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 29 Sep 2020 17:40:06 +0200 Subject: [PATCH 15/16] Fix warehouse configure link translation --- locale/defaultMessages.json | 12 ++-------- .../ProductStocks/ProductStocks.tsx | 24 +++++-------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index e0f9d7b51..435914cf8 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -316,19 +316,11 @@ }, "productVariantWarehouseSectionDescription": { "context": "no warehouses info", - "string": "There are no warehouses set up for your store. To add stock quantity to the variant please {configureWarehouseUrl}" - }, - "productVariantWarehouseSectionDescriptionLink": { - "context": "no warehouses info", - "string": "configure a warehouse" + "string": "There are no warehouses set up for your store. To add stock quantity to the variant please configure a warehouse" }, "productWarehouseSectionDescription": { "context": "no warehouses info", - "string": "There are no warehouses set up for your store. To add stock quantity to the product please {configureWarehouseUrl}" - }, - "productWarehouseSectionDescriptionLink": { - "context": "no warehouses info", - "string": "configure a warehouse" + "string": "There are no warehouses set up for your store. To add stock quantity to the product please configure a warehouse" }, "saleDetailsPageCategoriesQuantity": { "context": "number of categories", diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx index 81ed77dc1..9a8d430db 100644 --- a/src/products/components/ProductStocks/ProductStocks.tsx +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -191,18 +191,12 @@ const ProductStocks: React.FC = ({ {hasVariants ? ( <> - - + a: chunks => ( + {chunks} ) }} /> @@ -210,18 +204,12 @@ const ProductStocks: React.FC = ({ ) : ( <> - - + a: chunks => ( + {chunks} ) }} /> From 992816c641f91976f811f99615be140c471710b4 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Thu, 8 Oct 2020 11:54:15 +0200 Subject: [PATCH 16/16] Update storybook with no warehouses case --- .../ProductVariantCreator.stories.tsx | 11 + .../__snapshots__/Stories.test.ts.snap | 4924 +++++++++++++++++ .../stories/products/ProductUpdatePage.tsx | 19 + .../products/ProductVariantCreatePage.tsx | 17 + .../stories/products/ProductVariantPage.tsx | 19 + 5 files changed, 4990 insertions(+) diff --git a/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx b/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx index fa936506a..806d3bb75 100644 --- a/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx +++ b/src/products/components/ProductVariantCreatorPage/ProductVariantCreator.stories.tsx @@ -140,6 +140,17 @@ storiesOf( step={ProductVariantCreatorStep.prices} warehouses={[props.warehouses[0]]} /> + )) + .add("ship when no warehouses", () => ( + )); storiesOf("Views / Products / Create multiple variants / summary", module) diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 4efe9fdd0..318c5d806 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -134014,6 +134014,407 @@ exports[`Storyshots Views / Products / Create multiple variants / prices and SKU
`; +exports[`Storyshots Views / Products / Create multiple variants / prices and SKUs ship when no warehouses 1`] = ` +
+
+
+
+ + Price + +
+
+
+
+
+
+ +
+
+ +
+ + USD + +
+
+
+ +
+
+
+
+
+ Choose attribute +
+
+
+
+ +
+
+ Box Size +
+ + + +
+
+
+
+
+
+
+
+
+ 100g +
+
+
+
+ +
+ + USD + +
+
+
+
+
+
+
+
+
+ 500g +
+
+
+
+ +
+ + USD + +
+
+
+
+
+
+
+
+
+ + Stock and Warehousing + +
+
+
+
+
+
+ There are no warehouses set up for your store. You can configure variants without providing stock quantities. +
+
+
+
+
+`; + exports[`Storyshots Views / Products / Create multiple variants / summary default 1`] = `
`; +exports[`Storyshots Views / Products / Create product variant no warehouses 1`] = ` +
+ +
+
+
+ Add variant +
+
+
+
+
+
+
+
+
+ + Variants + +
+
+
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
+ New Variant +
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+

+ Optional +

+
+
+
+
+
+
+
+
+ + Shipping + +
+
+
+
+
+
+
+ +
+ +
+
+ kg +
+
+ +
+
+
+
+
+
+
+
+ + Inventory + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+ + Quantity + +
+
+
+ There are no warehouses set up for your store. To add stock quantity to the variant please + + configure a warehouse + +
+
+
+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+ +
+
+
+ +
+`; + exports[`Storyshots Views / Products / Create product variant when loading data 1`] = `
`; +exports[`Storyshots Views / Products / Product edit no stock, no variants and no warehouses 1`] = ` +
+
+
+
+
+ Ergonomic Plastic Bacon +
+
+
+
+
+
+
+
+
+ + General Information + +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ Description +
+
+ +
+
+
+
+
+
+ + + bold + + +
+
+
+
+ + + italic + + +
+
+
+
+ + + strikethrough + + +
+
+

+
+ + + h1 + + +
+

+

+
+ + + h2 + + +
+

+

+
+ + + h3 + + +
+

+
+
+ + + blockquote + + +
+
+
    +
  • +
    + + + ul + + +
    +
  • +
+
    +
  1. +
    + + + ol + + +
    +
  2. +
+
+
+ + + link + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Images + +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+ Id sit dolores adipisci +
+
+
+
+
+
+
+
+ + Attributes + +
+
+
+
+
+
+
+
+ 2 Attributes +
+
+ +
+
+
+
+
+ Borders +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ Legacy +
+
+
+
+
+ + +
+
+
+
+
+
+ Auto Loan Account +
+ +
+
+
+
+
+
+
+
+
+
+ + Pricing + +
+
+
+
+
+
+
+ +
+ +
+
+ USD +
+
+ +
+
+
+
+
+
+
+
+ + Shipping + +
+
+
+
+
+
+
+ +
+ +
+
+ KG +
+
+ +
+
+
+
+
+
+
+
+ + Inventory + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+ + Quantity + +
+
+
+ There are no warehouses set up for your store. To add stock quantity to the product please + + configure a warehouse + +
+
+
+
+
+
+ + Search Engine Preview + +
+ +
+
+
+
+
+
+ Add search engine title and description to make this product easier to find +
+
+
+
+
+
+ + Metadata + +
+
+
+
+ + + + + + + + + + + + +