Add warehouse create
This commit is contained in:
parent
74ea099824
commit
2a7829c0f7
14 changed files with 491 additions and 9 deletions
|
@ -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"
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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", () => <WarehouseCreatePage {...props} />)
|
||||
.add("loading", () => (
|
||||
<WarehouseCreatePage {...props} service={undefined} disabled={true} />
|
||||
))
|
||||
.add("form errors", () => (
|
||||
<WarehouseCreatePage
|
||||
{...props}
|
||||
errors={([
|
||||
"name",
|
||||
"city",
|
||||
"cityArea",
|
||||
"companyName",
|
||||
"country",
|
||||
"countryArea",
|
||||
"phone",
|
||||
"postalCode",
|
||||
"streetAddress1",
|
||||
"streetAddress2"
|
||||
] as Array<keyof WarehouseCreatePageFormData>).map(field =>
|
||||
formError(field)
|
||||
)}
|
||||
/>
|
||||
));
|
|
@ -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<WarehouseCreatePageProps> = ({
|
||||
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<WarehouseCreatePageFormData>(onSubmit);
|
||||
|
||||
return (
|
||||
<Form
|
||||
initial={initialForm}
|
||||
errors={[...apiErrors, ...validationErrors]}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ change, data, errors, submit }) => {
|
||||
const countryChoices = mapCountriesToChoices(
|
||||
maybe(() => shop.countries, [])
|
||||
);
|
||||
const handleCountryChange = createSingleAutocompleteSelectHandler(
|
||||
change,
|
||||
setDisplayCountry,
|
||||
countryChoices
|
||||
);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Grid>
|
||||
<div>
|
||||
<WarehouseInfo
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CompanyAddressInput
|
||||
countries={countryChoices}
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
displayCountry={displayCountry}
|
||||
errors={errors}
|
||||
header={intl.formatMessage({
|
||||
defaultMessage: "Address Information",
|
||||
description: "warehouse"
|
||||
})}
|
||||
onChange={change}
|
||||
onCountryChange={handleCountryChange}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<SaveButtonBar
|
||||
disabled={disabled}
|
||||
onCancel={onBack}
|
||||
onSave={submit}
|
||||
state={saveButtonBarState}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
WarehouseCreatePage.displayName = "WarehouseCreatePage";
|
||||
export default WarehouseCreatePage;
|
2
src/warehouses/components/WarehouseCreatePage/index.ts
Normal file
2
src/warehouses/components/WarehouseCreatePage/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./WarehouseCreatePage";
|
||||
export * from "./WarehouseCreatePage";
|
50
src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx
Normal file
50
src/warehouses/components/WarehouseInfo/WarehouseInfo.tsx
Normal file
|
@ -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<WarehouseInfoProps> = ({
|
||||
data,
|
||||
disabled,
|
||||
errors,
|
||||
onChange
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage(commonMessages.generalInformations)}
|
||||
/>
|
||||
<CardContent>
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!errors.name}
|
||||
fullWidth
|
||||
helperText={errors.name}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Warehouse Name"
|
||||
})}
|
||||
name={"name" as keyof typeof data}
|
||||
value={data.name}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
WarehouseInfo.displayName = "WarehouseInfo";
|
||||
export default WarehouseInfo;
|
2
src/warehouses/components/WarehouseInfo/index.ts
Normal file
2
src/warehouses/components/WarehouseInfo/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./WarehouseInfo";
|
||||
export * from "./WarehouseInfo";
|
|
@ -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<RouteComponentProps> = ({ location }) => {
|
||||
const qs = parseQs(location.search.substr(1));
|
||||
|
@ -29,12 +31,6 @@ const WarehouseList: React.FC<RouteComponentProps> = ({ location }) => {
|
|||
return <WarehouseListComponent params={params} />;
|
||||
};
|
||||
|
||||
// const WarehouseCreate: React.FC<RouteComponentProps<{}>> = ({ location }) => {
|
||||
// const qs = parseQs(location.search.substr(1));
|
||||
// const params: WarehouseAddUrlQueryParams = qs;
|
||||
// return <WarehouseCreateComponent params={params} />;
|
||||
// };
|
||||
|
||||
// const WarehouseDetails: React.FC<RouteComponentProps<{ id: string }>> = ({
|
||||
// location,
|
||||
// match
|
||||
|
@ -57,8 +53,8 @@ export const WarehouseSection: React.FC = () => {
|
|||
<WindowTitle title={intl.formatMessage(sectionNames.warehouses)} />
|
||||
<Switch>
|
||||
<Route exact path={warehouseListPath} component={WarehouseList} />
|
||||
{/* <Route exact path={warehouseAddPath} component={WarehouseCreate} />
|
||||
<Route path={warehousePath(":id")} component={WarehouseDetails} /> */}
|
||||
<Route exact path={warehouseAddPath} component={WarehouseCreate} />
|
||||
{/* <Route path={warehousePath(":id")} component={WarehouseDetails} /> */}
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}
|
||||
|
|
75
src/warehouses/types/WarehouseCreate.ts
Normal file
75
src/warehouses/types/WarehouseCreate.ts
Normal file
|
@ -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;
|
||||
}
|
53
src/warehouses/types/WarehouseDetailsFragment.ts
Normal file
53
src/warehouses/types/WarehouseDetailsFragment.ts
Normal file
|
@ -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;
|
||||
}
|
60
src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx
Normal file
60
src/warehouses/views/WarehouseCreate/WarehouseCreate.tsx
Normal file
|
@ -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 (
|
||||
<WarehouseCreatePage
|
||||
onBack={() => 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;
|
2
src/warehouses/views/WarehouseCreate/index.ts
Normal file
2
src/warehouses/views/WarehouseCreate/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default } from "./WarehouseCreate";
|
||||
export * from "./WarehouseCreate";
|
Loading…
Reference in a new issue