From 2a7829c0f76de998d8ae45190c2cc835e0a27126 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Thu, 30 Jan 2020 15:34:27 +0100 Subject: [PATCH] Add warehouse create --- src/fixtures.ts | 20 +++ src/types/globalTypes.ts | 19 +++ .../WarehouseCreatePage.stories.tsx | 48 +++++++ .../WarehouseCreatePage.tsx | 119 ++++++++++++++++++ .../components/WarehouseCreatePage/index.ts | 2 + .../WarehouseInfo/WarehouseInfo.tsx | 50 ++++++++ .../components/WarehouseInfo/index.ts | 2 + src/warehouses/index.tsx | 14 +-- src/warehouses/mutations.ts | 24 ++++ src/warehouses/queries.ts | 12 ++ src/warehouses/types/WarehouseCreate.ts | 75 +++++++++++ .../types/WarehouseDetailsFragment.ts | 53 ++++++++ .../views/WarehouseCreate/WarehouseCreate.tsx | 60 +++++++++ src/warehouses/views/WarehouseCreate/index.ts | 2 + 14 files changed, 491 insertions(+), 9 deletions(-) create mode 100644 src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.stories.tsx create mode 100644 src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.tsx create mode 100644 src/warehouses/components/WarehouseCreatePage/index.ts create mode 100644 src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx create mode 100644 src/warehouses/components/WarehouseInfo/index.ts create mode 100644 src/warehouses/types/WarehouseCreate.ts create mode 100644 src/warehouses/types/WarehouseDetailsFragment.ts create mode 100644 src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx create mode 100644 src/warehouses/views/WarehouseCreate/index.ts diff --git a/src/fixtures.ts b/src/fixtures.ts index ebb678030..3394f4a92 100644 --- a/src/fixtures.ts +++ b/src/fixtures.ts @@ -485,3 +485,23 @@ export const adminUserPermissions: User_userPermissions[] = [ name: "Manage customers." } ]; + +export const address = { + __typename: "Address", + city: "Port Danielshire", + cityArea: "", + companyName: "", + country: { + __typename: "CountryDisplay", + code: "SE", + country: "Szwecja" + }, + countryArea: "", + firstName: "Elizabeth", + id: "QWRkcmVzczoy", + lastName: "Vaughn", + phone: "", + postalCode: "52203", + streetAddress1: "419 Ruiz Orchard Apt. 199", + streetAddress2: "0238 Cremin Freeway" +}; diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 48507817e..53faacc0a 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -1419,6 +1419,25 @@ export interface VoucherSortingInput { field: VoucherSortField; } +export interface WarehouseAddressInput { + streetAddress1: string; + streetAddress2?: string | null; + city: string; + cityArea?: string | null; + postalCode?: string | null; + country: CountryCode; + countryArea?: string | null; + phone?: string | null; +} + +export interface WarehouseCreateInput { + name: string; + companyName?: string | null; + shippingZones?: (string | null)[] | null; + email?: string | null; + address: WarehouseAddressInput; +} + export interface WarehouseFilterInput { search?: string | null; } diff --git a/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.stories.tsx b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.stories.tsx new file mode 100644 index 000000000..9f25b855b --- /dev/null +++ b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.stories.tsx @@ -0,0 +1,48 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import { address, permissions } from "@saleor/fixtures"; +import Decorator from "@saleor/storybook/Decorator"; +import { formError } from "@saleor/storybook/misc"; +import { warehouseList } from "../../fixtures"; +import WarehouseCreatePage, { + WarehouseCreatePageProps, + WarehouseCreatePageFormData +} from "./WarehouseCreatePage"; + +const props: WarehouseCreatePageProps = { + disabled: false, + errors: [], + saveButtonBarState: "default", + onBack: () => undefined, + onSubmit: () => undefined, + warehouse: { + ...warehouseList[0], + address + } +}; +storiesOf("Views / Warehouses / Create warehouse", module) + .addDecorator(Decorator) + .add("default", () => ) + .add("loading", () => ( + + )) + .add("form errors", () => ( + ).map(field => + formError(field) + )} + /> + )); diff --git a/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.tsx b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.tsx new file mode 100644 index 000000000..2024cca42 --- /dev/null +++ b/src/warehouses/components/WarehouseCreatePage/WarehouseCreatePage.tsx @@ -0,0 +1,119 @@ +import React from "react"; +import { useIntl } from "react-intl"; + +import Container from "@saleor/components/Container"; +import Form from "@saleor/components/Form"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import { UserError } from "@saleor/types"; +import Grid from "@saleor/components/Grid"; +import CardSpacer from "@saleor/components/CardSpacer"; +import CompanyAddressInput from "@saleor/components/CompanyAddressInput"; +import { AddressTypeInput } from "@saleor/customers/types"; +import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; +import { mapCountriesToChoices } from "@saleor/utils/maps"; +import useAddressValidation from "@saleor/hooks/useAddressValidation"; +import useStateFromProps from "@saleor/hooks/useStateFromProps"; +import { maybe } from "@saleor/misc"; +import { ShopInfo_shop } from "@saleor/components/Shop/types/ShopInfo"; +import WarehouseInfo from "../WarehouseInfo"; + +export interface WarehouseCreatePageFormData extends AddressTypeInput { + name: string; +} +export interface WarehouseCreatePageProps { + disabled: boolean; + errors: UserError[]; + saveButtonBarState: ConfirmButtonTransitionState; + shop: ShopInfo_shop; + onBack: () => void; + onSubmit: (data: WarehouseCreatePageFormData) => void; +} + +const initialForm: WarehouseCreatePageFormData = { + city: "", + companyName: "", + country: "", + countryArea: "", + name: "", + phone: "", + postalCode: "", + streetAddress1: "", + streetAddress2: "" +}; + +const WarehouseCreatePage: React.FC = ({ + disabled, + errors: apiErrors, + saveButtonBarState, + shop, + onBack, + onSubmit +}) => { + const intl = useIntl(); + const [displayCountry, setDisplayCountry] = useStateFromProps( + maybe(() => shop.companyAddress.country.code, "") + ); + + const { + errors: validationErrors, + submit: handleSubmit + } = useAddressValidation(onSubmit); + + return ( +
+ {({ change, data, errors, submit }) => { + const countryChoices = mapCountriesToChoices( + maybe(() => shop.countries, []) + ); + const handleCountryChange = createSingleAutocompleteSelectHandler( + change, + setDisplayCountry, + countryChoices + ); + + return ( + + +
+ + + +
+
+ +
+ ); + }} +
+ ); +}; + +WarehouseCreatePage.displayName = "WarehouseCreatePage"; +export default WarehouseCreatePage; diff --git a/src/warehouses/components/WarehouseCreatePage/index.ts b/src/warehouses/components/WarehouseCreatePage/index.ts new file mode 100644 index 000000000..b9bd23b20 --- /dev/null +++ b/src/warehouses/components/WarehouseCreatePage/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseCreatePage"; +export * from "./WarehouseCreatePage"; diff --git a/src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx b/src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx new file mode 100644 index 000000000..56b402e08 --- /dev/null +++ b/src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import TextField from "@material-ui/core/TextField"; +import { useIntl } from "react-intl"; +import CardTitle from "@saleor/components/CardTitle"; +import { commonMessages } from "@saleor/intl"; +import { FormChange } from "@saleor/hooks/useForm"; +import { FormErrors } from "@saleor/types"; + +export interface WarehouseInfoProps { + data: Record<"name", string>; + disabled: boolean; + errors: FormErrors<"name">; + onChange: FormChange; +} + +const WarehouseInfo: React.FC = ({ + data, + disabled, + errors, + onChange +}) => { + const intl = useIntl(); + + return ( + + + + + + + ); +}; + +WarehouseInfo.displayName = "WarehouseInfo"; +export default WarehouseInfo; diff --git a/src/warehouses/components/WarehouseInfo/index.ts b/src/warehouses/components/WarehouseInfo/index.ts new file mode 100644 index 000000000..3296772d7 --- /dev/null +++ b/src/warehouses/components/WarehouseInfo/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseInfo"; +export * from "./WarehouseInfo"; diff --git a/src/warehouses/index.tsx b/src/warehouses/index.tsx index 8d06f0040..0b37a9da5 100644 --- a/src/warehouses/index.tsx +++ b/src/warehouses/index.tsx @@ -13,11 +13,13 @@ import { WarehouseListUrlQueryParams, // warehousePath, // WarehouseUrlQueryParams, - WarehouseListUrlSortField + WarehouseListUrlSortField, + warehouseAddPath } from "./urls"; // import WarehouseCreateComponent from "./views/WarehouseCreate"; // import WarehouseDetailsComponent from "./views/WarehouseDetails"; import WarehouseListComponent from "./views/WarehouseList"; +import WarehouseCreate from "./views/WarehouseCreate"; const WarehouseList: React.FC = ({ location }) => { const qs = parseQs(location.search.substr(1)); @@ -29,12 +31,6 @@ const WarehouseList: React.FC = ({ location }) => { return ; }; -// const WarehouseCreate: React.FC> = ({ location }) => { -// const qs = parseQs(location.search.substr(1)); -// const params: WarehouseAddUrlQueryParams = qs; -// return ; -// }; - // const WarehouseDetails: React.FC> = ({ // location, // match @@ -57,8 +53,8 @@ export const WarehouseSection: React.FC = () => { - {/* - */} + + {/* */} ); diff --git a/src/warehouses/mutations.ts b/src/warehouses/mutations.ts index fa1da647b..9611cac88 100644 --- a/src/warehouses/mutations.ts +++ b/src/warehouses/mutations.ts @@ -1,10 +1,15 @@ import gql from "graphql-tag"; import makeMutation from "@saleor/hooks/makeMutation"; +import { + WarehouseCreate, + WarehouseCreateVariables +} from "./types/WarehouseCreate"; import { WarehouseDelete, WarehouseDeleteVariables } from "./types/WarehouseDelete"; +import { warehouseDetailsFragment } from "./queries"; const deleteWarehouse = gql` mutation WarehouseDelete($id: ID!) { @@ -20,3 +25,22 @@ export const useWarehouseDelete = makeMutation< WarehouseDelete, WarehouseDeleteVariables >(deleteWarehouse); + +const createWarehouse = gql` + ${warehouseDetailsFragment} + mutation WarehouseCreate($input: WarehouseCreateInput!) { + createWarehouse(input: $input) { + errors { + field + message + } + warehouse { + ...WarehouseDetailsFragment + } + } + } +`; +export const useWarehouseCreate = makeMutation< + WarehouseCreate, + WarehouseCreateVariables +>(createWarehouse); diff --git a/src/warehouses/queries.ts b/src/warehouses/queries.ts index 1cfd8b5ac..d75984c68 100644 --- a/src/warehouses/queries.ts +++ b/src/warehouses/queries.ts @@ -2,6 +2,7 @@ import gql from "graphql-tag"; import makeQuery from "@saleor/hooks/makeQuery"; import { pageInfoFragment } from "@saleor/queries"; +import { fragmentAddress } from "@saleor/orders/queries"; import { WarehouseList, WarehouseListVariables } from "./types/WarehouseList"; export const warehouseFragment = gql` @@ -19,6 +20,17 @@ export const warehouseFragment = gql` } `; +export const warehouseDetailsFragment = gql` + ${fragmentAddress} + ${warehouseFragment} + fragment WarehouseDetailsFragment on Warehouse { + ...WarehouseFragment + address { + ...AddressFragment + } + } +`; + const warehouseList = gql` ${warehouseFragment} ${pageInfoFragment} diff --git a/src/warehouses/types/WarehouseCreate.ts b/src/warehouses/types/WarehouseCreate.ts new file mode 100644 index 000000000..5caeb87b9 --- /dev/null +++ b/src/warehouses/types/WarehouseCreate.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { WarehouseCreateInput } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: WarehouseCreate +// ==================================================== + +export interface WarehouseCreate_createWarehouse_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface WarehouseCreate_createWarehouse_warehouse_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseCreate_createWarehouse_warehouse_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseCreate_createWarehouse_warehouse_shippingZones_edges_node; +} + +export interface WarehouseCreate_createWarehouse_warehouse_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseCreate_createWarehouse_warehouse_shippingZones_edges[]; +} + +export interface WarehouseCreate_createWarehouse_warehouse_address_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface WarehouseCreate_createWarehouse_warehouse_address { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: WarehouseCreate_createWarehouse_warehouse_address_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface WarehouseCreate_createWarehouse_warehouse { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseCreate_createWarehouse_warehouse_shippingZones; + address: WarehouseCreate_createWarehouse_warehouse_address; +} + +export interface WarehouseCreate_createWarehouse { + __typename: "WarehouseCreate"; + errors: WarehouseCreate_createWarehouse_errors[] | null; + warehouse: WarehouseCreate_createWarehouse_warehouse | null; +} + +export interface WarehouseCreate { + createWarehouse: WarehouseCreate_createWarehouse | null; +} + +export interface WarehouseCreateVariables { + input: WarehouseCreateInput; +} diff --git a/src/warehouses/types/WarehouseDetailsFragment.ts b/src/warehouses/types/WarehouseDetailsFragment.ts new file mode 100644 index 000000000..fae00ac31 --- /dev/null +++ b/src/warehouses/types/WarehouseDetailsFragment.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL fragment: WarehouseDetailsFragment +// ==================================================== + +export interface WarehouseDetailsFragment_shippingZones_edges_node { + __typename: "ShippingZone"; + id: string; + name: string; +} + +export interface WarehouseDetailsFragment_shippingZones_edges { + __typename: "ShippingZoneCountableEdge"; + node: WarehouseDetailsFragment_shippingZones_edges_node; +} + +export interface WarehouseDetailsFragment_shippingZones { + __typename: "ShippingZoneCountableConnection"; + edges: WarehouseDetailsFragment_shippingZones_edges[]; +} + +export interface WarehouseDetailsFragment_address_country { + __typename: "CountryDisplay"; + code: string; + country: string; +} + +export interface WarehouseDetailsFragment_address { + __typename: "Address"; + city: string; + cityArea: string; + companyName: string; + country: WarehouseDetailsFragment_address_country; + countryArea: string; + firstName: string; + id: string; + lastName: string; + phone: string | null; + postalCode: string; + streetAddress1: string; + streetAddress2: string; +} + +export interface WarehouseDetailsFragment { + __typename: "Warehouse"; + id: string; + name: string; + shippingZones: WarehouseDetailsFragment_shippingZones; + address: WarehouseDetailsFragment_address; +} diff --git a/src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx b/src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx new file mode 100644 index 000000000..921a0ee8f --- /dev/null +++ b/src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import { useIntl } from "react-intl"; + +import WarehouseCreatePage from "@saleor/warehouses/components/WarehouseCreatePage"; +import useNavigator from "@saleor/hooks/useNavigator"; +import { warehouseListUrl, warehouseUrl } from "@saleor/warehouses/urls"; +import { useWarehouseCreate } from "@saleor/warehouses/mutations"; +import { commonMessages } from "@saleor/intl"; +import useNotifier from "@saleor/hooks/useNotifier"; +import { maybe, findValueInEnum, getMutationStatus } from "@saleor/misc"; +import { CountryCode } from "@saleor/types/globalTypes"; +import useShop from "@saleor/hooks/useShop"; + +const WarehouseCreate: React.FC = () => { + const intl = useIntl(); + const navigate = useNavigator(); + const notify = useNotifier(); + const shop = useShop(); + const [createWarehouse, createWarehouseOpts] = useWarehouseCreate({ + onCompleted: data => { + if (data.createWarehouse.errors.length === 0) { + navigate(warehouseUrl(data.createWarehouse.warehouse.id)); + notify({ text: intl.formatMessage(commonMessages.savedChanges) }); + } + } + }); + const createWarehouseTransitionState = getMutationStatus(createWarehouseOpts); + + return ( + navigate(warehouseListUrl())} + disabled={createWarehouseOpts.loading} + errors={maybe(() => createWarehouseOpts.data.createWarehouse.errors, [])} + shop={shop} + onSubmit={data => + createWarehouse({ + variables: { + input: { + address: { + city: data.city, + cityArea: data.cityArea, + country: findValueInEnum(data.country, CountryCode), + countryArea: data.countryArea, + phone: data.phone, + postalCode: data.postalCode, + streetAddress1: data.streetAddress1, + streetAddress2: data.streetAddress2 + }, + name: data.name + } + } + }) + } + saveButtonBarState={createWarehouseTransitionState} + /> + ); +}; + +WarehouseCreate.displayName = "WarehouseCreate"; +export default WarehouseCreate; diff --git a/src/warehouses/views/WarehouseCreate/index.ts b/src/warehouses/views/WarehouseCreate/index.ts new file mode 100644 index 000000000..d73de272c --- /dev/null +++ b/src/warehouses/views/WarehouseCreate/index.ts @@ -0,0 +1,2 @@ +export { default } from "./WarehouseCreate"; +export * from "./WarehouseCreate";