Merge branch 'master' of github.com:mirumee/saleor-dashboard into refactor-zip-codes-to-postal-codes

This commit is contained in:
Tomasz Szymanski 2021-01-07 13:26:06 +01:00
commit afbdf0a500
36 changed files with 2516 additions and 939 deletions

42
.github/workflows/deploy-staging.yaml vendored Normal file
View file

@ -0,0 +1,42 @@
name: Deploy to staging
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-20.04
env:
API_URI: https://master.staging.saleor.cloud/graphql
APP_MOUNT_URI: /dashboard/
STATIC_URL: /dashboard/static/
steps:
- uses: actions/checkout@v2
- name: Package
run: |
npm ci
npm run build
- uses: actions/upload-artifact@v2
with:
name: build
path: build
deploy:
needs:
- build
runs-on: ubuntu-20.04
steps:
- uses: actions/download-artifact@v2
with:
name: build
path: build
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_STAGING_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_STAGING_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy
run: |
aws s3 sync build/dashboard s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/saleor-master-staging/static/
aws s3 cp build/dashboard/index.html s3://${{ secrets.AWS_STAGING_DEPLOYMENT_BUCKET }}/saleor-master-staging/
aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_STAGING_CF_DIST_ID }} --paths "/dashboard*"

View file

@ -2200,40 +2200,48 @@
"context": "dialog title", "context": "dialog title",
"string": "Add Address" "string": "Add Address"
}, },
"src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_1090326769": { "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_addAddress": {
"context": "customer's address book, header",
"string": "{fullName}'s Address Book"
},
"src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_1428369222": {
"string": "This customer doesnt have any adresses added to his address book. You can add address using the button below."
},
"src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_1484733755": {
"string": "There is no address to show for this customer"
},
"src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_3623935073": {
"context": "button", "context": "button",
"string": "Add address" "string": "Add address"
}, },
"src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_489918044": { "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_doesntHaveAddresses": {
"string": "This customer doesnt have any adresses added to his address book. You can add address using the button below."
},
"src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_fullNameAddress": {
"context": "customer's address book, header",
"string": "{fullName}'s Address Book"
},
"src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_fullNameDetail": {
"context": "customer details, header", "context": "customer details, header",
"string": "{fullName} Details" "string": "{fullName} Details"
}, },
"src_dot_customers_dot_components_dot_CustomerAddress_dot_1224809208": { "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_noAddressToShow": {
"string": "There is no address to show for this customer"
},
"src_dot_customers_dot_components_dot_CustomerAddress_dot_defaultAddress": {
"string": "Default Address" "string": "Default Address"
}, },
"src_dot_customers_dot_components_dot_CustomerAddress_dot_1578192486": { "src_dot_customers_dot_components_dot_CustomerAddress_dot_defaultBillingAddress": {
"string": "Default Billing Address" "string": "Default Billing Address"
}, },
"src_dot_customers_dot_components_dot_CustomerAddress_dot_2131178753": { "src_dot_customers_dot_components_dot_CustomerAddress_dot_defaultShippingAddress": {
"context": "button", "string": "Default Shipping Address"
"string": "Set as default shipping address"
}, },
"src_dot_customers_dot_components_dot_CustomerAddress_dot_3096438859": { "src_dot_customers_dot_components_dot_CustomerAddress_dot_deleteAddress": {
"context": "button",
"string": "Delete Address"
},
"src_dot_customers_dot_components_dot_CustomerAddress_dot_editAddress": {
"context": "button",
"string": "Edit Address"
},
"src_dot_customers_dot_components_dot_CustomerAddress_dot_setDefaultBilling": {
"context": "button", "context": "button",
"string": "Set as default billing address" "string": "Set as default billing address"
}, },
"src_dot_customers_dot_components_dot_CustomerAddress_dot_4109348993": { "src_dot_customers_dot_components_dot_CustomerAddress_dot_setDefaultShipping": {
"string": "Default Shipping Address" "context": "button",
"string": "Set as default shipping address"
}, },
"src_dot_customers_dot_components_dot_CustomerAddresses_dot_1967111456": { "src_dot_customers_dot_components_dot_CustomerAddresses_dot_1967111456": {
"context": "header", "context": "header",
@ -5380,6 +5388,9 @@
"src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_4190792473": { "src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_4190792473": {
"string": "Actions" "string": "Actions"
}, },
"src_dot_shipping_dot_components_dot_ShippingRateInfo_dot_579967655": {
"string": "Shipping rate name"
},
"src_dot_shipping_dot_components_dot_ShippingRateZipCodeRangeRemoveDialog_dot_3640694505": { "src_dot_shipping_dot_components_dot_ShippingRateZipCodeRangeRemoveDialog_dot_3640694505": {
"string": "Are you sure you want to remove this postal code rule?" "string": "Are you sure you want to remove this postal code rule?"
}, },
@ -5430,33 +5441,47 @@
"src_dot_shipping_dot_components_dot_ShippingZoneCountriesAssignDialog_dot_3510295703": { "src_dot_shipping_dot_components_dot_ShippingZoneCountriesAssignDialog_dot_3510295703": {
"string": "Search Countries" "string": "Search Countries"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_2364051773": { "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_countries": {
"string": "Currently, there are no countries assigned to this shipping zone" "context": "country list header",
},
"src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_3109712047": {
"string": "Countries" "string": "Countries"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_4049462680": { "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_createZone": {
"context": "header", "context": "section header",
"string": "Create New Shipping Zone" "string": "Create New Shipping Zone"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_4270729636": { "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_defaultZone": {
"string": "This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones" "string": "This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_1325966144": { "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_noCountriesAssigned": {
"string": "Shipping"
},
"src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_2364051773": {
"string": "Currently, there are no countries assigned to this shipping zone" "string": "Currently, there are no countries assigned to this shipping zone"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_3109712047": { "src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_countries": {
"context": "country list header",
"string": "Countries" "string": "Countries"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_4270729636": { "src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_defaultZone": {
"string": "This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones" "string": "This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_579967655": { "src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_noCountriesAssigned": {
"string": "Shipping rate name" "string": "Currently, there are no countries assigned to this shipping zone"
},
"src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_shipping": {
"context": "shipping section header",
"string": "Shipping"
},
"src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_1470703814": {
"context": "field placeholder",
"string": "Description of a shipping zone."
},
"src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_1560416099": {
"string": "Shipping zone name"
},
"src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_3374163063": {
"string": "Description"
},
"src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_3877274856": {
"context": "character limit",
"string": "{numberOfCharacters} of {maxCharacters} characters"
}, },
"src_dot_shipping_dot_components_dot_ShippingZoneRatesCreatePage_dot_1161979494": { "src_dot_shipping_dot_components_dot_ShippingZoneRatesCreatePage_dot_1161979494": {
"context": "page title", "context": "page title",

View file

@ -4782,6 +4782,7 @@ type ShippingZone implements Node & ObjectWithMetadata {
countries: [CountryDisplay] countries: [CountryDisplay]
shippingMethods: [ShippingMethod] shippingMethods: [ShippingMethod]
warehouses: [Warehouse] warehouses: [Warehouse]
description: String
} }
type ShippingZoneBulkDelete { type ShippingZoneBulkDelete {
@ -4809,6 +4810,7 @@ type ShippingZoneCreate {
input ShippingZoneCreateInput { input ShippingZoneCreateInput {
name: String name: String
description: String
countries: [String] countries: [String]
default: Boolean default: Boolean
addWarehouses: [ID] addWarehouses: [ID]
@ -4828,6 +4830,7 @@ type ShippingZoneUpdate {
input ShippingZoneUpdateInput { input ShippingZoneUpdateInput {
name: String name: String
description: String
countries: [String] countries: [String]
default: Boolean default: Boolean
addWarehouses: [ID] addWarehouses: [ID]

View file

@ -21,6 +21,7 @@ const AddressFormatter: React.FC<AddressFormatterProps> = ({ address }) => {
<Typography component="p"> <Typography component="p">
{address.firstName} {address.lastName} {address.firstName} {address.lastName}
</Typography> </Typography>
<Typography component="p">{address.phone}</Typography>
{address.companyName && ( {address.companyName && (
<Typography component="p">{address.companyName}</Typography> <Typography component="p">{address.companyName}</Typography>
)} )}

View file

@ -1,15 +1,12 @@
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent"; import CardContent from "@material-ui/core/CardContent";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import AddressFormatter from "@saleor/components/AddressFormatter"; import AddressFormatter from "@saleor/components/AddressFormatter";
import CardMenu from "@saleor/components/CardMenu"; import CardMenu from "@saleor/components/CardMenu";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { buttonMessages } from "@saleor/intl";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import { AddressTypeEnum } from "../../../types/globalTypes"; import { AddressTypeEnum } from "../../../types/globalTypes";
import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses"; import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses";
@ -25,6 +22,34 @@ export interface CustomerAddressProps {
onSetAsDefault: (type: AddressTypeEnum) => void; onSetAsDefault: (type: AddressTypeEnum) => void;
} }
const messages = defineMessages({
defaultAddress: {
defaultMessage: "Default Address"
},
defaultShippingAddress: {
defaultMessage: "Default Shipping Address"
},
defaultBillingAddress: {
defaultMessage: "Default Billing Address"
},
setDefaultShipping: {
defaultMessage: "Set as default shipping address",
description: "button"
},
setDefaultBilling: {
defaultMessage: "Set as default billing address",
description: "button"
},
editAddress: {
defaultMessage: "Edit Address",
description: "button"
},
deleteAddress: {
defaultMessage: "Delete Address",
description: "button"
}
});
const useStyles = makeStyles( const useStyles = makeStyles(
{ {
actions: { actions: {
@ -64,17 +89,11 @@ const CustomerAddress: React.FC<CustomerAddressProps> = props => {
address ? ( address ? (
<> <>
{isDefaultBillingAddress && isDefaultShippingAddress {isDefaultBillingAddress && isDefaultShippingAddress
? intl.formatMessage({ ? intl.formatMessage(messages.defaultAddress)
defaultMessage: "Default Address"
})
: isDefaultShippingAddress : isDefaultShippingAddress
? intl.formatMessage({ ? intl.formatMessage(messages.defaultShippingAddress)
defaultMessage: "Default Shipping Address"
})
: isDefaultBillingAddress : isDefaultBillingAddress
? intl.formatMessage({ ? intl.formatMessage(messages.defaultBillingAddress)
defaultMessage: "Default Billing Address"
})
: null} : null}
</> </>
) : ( ) : (
@ -87,18 +106,20 @@ const CustomerAddress: React.FC<CustomerAddressProps> = props => {
disabled={disabled} disabled={disabled}
menuItems={[ menuItems={[
{ {
label: intl.formatMessage({ label: intl.formatMessage(messages.setDefaultShipping),
defaultMessage: "Set as default shipping address",
description: "button"
}),
onSelect: () => onSetAsDefault(AddressTypeEnum.SHIPPING) onSelect: () => onSetAsDefault(AddressTypeEnum.SHIPPING)
}, },
{ {
label: intl.formatMessage({ label: intl.formatMessage(messages.setDefaultBilling),
defaultMessage: "Set as default billing address",
description: "button"
}),
onSelect: () => onSetAsDefault(AddressTypeEnum.BILLING) onSelect: () => onSetAsDefault(AddressTypeEnum.BILLING)
},
{
label: intl.formatMessage(messages.editAddress),
onSelect: () => onEdit()
},
{
label: intl.formatMessage(messages.deleteAddress),
onSelect: () => onRemove()
} }
]} ]}
/> />
@ -107,16 +128,6 @@ const CustomerAddress: React.FC<CustomerAddressProps> = props => {
<CardContent> <CardContent>
<AddressFormatter address={address} /> <AddressFormatter address={address} />
</CardContent> </CardContent>
<div className={classes.actionsContainer}>
<CardActions className={classes.actions}>
<Button color="primary" disabled={disabled} onClick={onEdit}>
<FormattedMessage {...buttonMessages.edit} />
</Button>
<Button color="primary" disabled={disabled} onClick={onRemove}>
<FormattedMessage {...buttonMessages.delete} />
</Button>
</CardActions>
</div>
</Card> </Card>
); );
}; };

View file

@ -14,7 +14,6 @@ import useAddressValidation from "@saleor/hooks/useAddressValidation";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc";
import { AddressInput } from "@saleor/types/globalTypes"; import { AddressInput } from "@saleor/types/globalTypes";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import React from "react"; import React from "react";
@ -59,7 +58,7 @@ const CustomerAddressDialog = withStyles(
onConfirm onConfirm
}: CustomerAddressDialogProps & WithStyles<typeof styles>) => { }: CustomerAddressDialogProps & WithStyles<typeof styles>) => {
const [countryDisplayName, setCountryDisplayName] = useStateFromProps( const [countryDisplayName, setCountryDisplayName] = useStateFromProps(
maybe(() => address.country.country, "") address?.country.country || ""
); );
const { const {
errors: validationErrors, errors: validationErrors,
@ -71,27 +70,24 @@ const CustomerAddressDialog = withStyles(
); );
const initialForm: AddressTypeInput = { const initialForm: AddressTypeInput = {
city: maybe(() => address.city, ""), city: address?.city || "",
cityArea: maybe(() => address.cityArea, ""), cityArea: address?.cityArea || "",
companyName: maybe(() => address.companyName, ""), companyName: address?.companyName || "",
country: maybe(() => address.country.code, ""), country: address?.country.code || "",
countryArea: maybe(() => address.countryArea, ""), countryArea: address?.countryArea || "",
firstName: maybe(() => address.firstName, ""), firstName: address?.firstName || "",
lastName: maybe(() => address.lastName, ""), lastName: address?.lastName || "",
phone: maybe(() => address.phone, ""), phone: address?.phone || "",
postalCode: maybe(() => address.postalCode, ""), postalCode: address?.postalCode || "",
streetAddress1: maybe(() => address.streetAddress1, ""), streetAddress1: address?.streetAddress1 || "",
streetAddress2: maybe(() => address.streetAddress2, "") streetAddress2: address?.streetAddress2 || ""
}; };
const countryChoices = maybe( const countryChoices =
() => countries?.map(country => ({
countries.map(country => ({
label: country.label, label: country.label,
value: country.code value: country.code
})), })) || [];
[]
);
return ( return (
<Dialog <Dialog

View file

@ -4,10 +4,10 @@ import Typography from "@material-ui/core/Typography";
import AppHeader from "@saleor/components/AppHeader"; import AppHeader from "@saleor/components/AppHeader";
import Container from "@saleor/components/Container"; import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import { renderCollection } from "@saleor/misc";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import { maybe, renderCollection } from "../../../misc";
import { AddressTypeEnum } from "../../../types/globalTypes"; import { AddressTypeEnum } from "../../../types/globalTypes";
import { CustomerAddresses_user } from "../../types/CustomerAddresses"; import { CustomerAddresses_user } from "../../types/CustomerAddresses";
import CustomerAddress from "../CustomerAddress/CustomerAddress"; import CustomerAddress from "../CustomerAddress/CustomerAddress";
@ -22,6 +22,28 @@ export interface CustomerAddressListPageProps {
onSetAsDefault: (id: string, type: AddressTypeEnum) => void; onSetAsDefault: (id: string, type: AddressTypeEnum) => void;
} }
const messages = defineMessages({
fullNameAddress: {
defaultMessage: "{fullName}'s Address Book",
description: "customer's address book, header"
},
fullNameDetail: {
defaultMessage: "{fullName} Details",
description: "customer details, header"
},
addAddress: {
defaultMessage: "Add address",
description: "button"
},
noAddressToShow: {
defaultMessage: "There is no address to show for this customer"
},
doesntHaveAddresses: {
defaultMessage:
"This customer doesnt have any adresses added to his address book. You can add address using the button below."
}
});
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ theme => ({
addButton: { addButton: {
@ -36,10 +58,15 @@ const useStyles = makeStyles(
width: 600 width: 600
}, },
root: { root: {
columnGap: theme.spacing(3),
display: "grid", display: "grid",
gap: `${theme.spacing(3)}px`,
gridTemplateColumns: "repeat(3, 1fr)", gridTemplateColumns: "repeat(3, 1fr)",
rowGap: theme.spacing(3) [theme.breakpoints.down("md")]: {
gridTemplateColumns: "repeat(2, 1fr)"
},
[theme.breakpoints.down("sm")]: {
gridTemplateColumns: "repeat(1, 1fr)"
}
} }
}), }),
{ name: "CustomerAddressListPage" } { name: "CustomerAddressListPage" }
@ -59,50 +86,30 @@ const CustomerAddressListPage: React.FC<CustomerAddressListPageProps> = props =>
const intl = useIntl(); const intl = useIntl();
const isEmpty = maybe(() => customer.addresses.length) === 0; const isEmpty = customer?.addresses?.length === 0;
const fullName = maybe( const fullName = [customer?.firstName, customer?.lastName].join(" ") || "...";
() => [customer.firstName, customer.lastName].join(" "),
"..."
);
return ( return (
<Container> <Container>
<AppHeader onBack={onBack}> <AppHeader onBack={onBack}>
<FormattedMessage {intl.formatMessage(messages.fullNameDetail, { fullName })}
defaultMessage="{fullName} Details"
description="customer details, header"
values={{
fullName
}}
/>
</AppHeader> </AppHeader>
{!isEmpty && ( {!isEmpty && (
<PageHeader <PageHeader
title={intl.formatMessage( title={intl.formatMessage(messages.fullNameAddress, { fullName })}
{
defaultMessage: "{fullName}'s Address Book",
description: "customer's address book, header"
},
{
fullName
}
)}
> >
<Button color="primary" variant="contained" onClick={onAdd}> <Button color="primary" variant="contained" onClick={onAdd}>
<FormattedMessage {intl.formatMessage(messages.addAddress)}
defaultMessage="Add address"
description="button"
/>
</Button> </Button>
</PageHeader> </PageHeader>
)} )}
{isEmpty ? ( {isEmpty ? (
<div className={classes.empty}> <div className={classes.empty}>
<Typography variant="h5"> <Typography variant="h5">
<FormattedMessage defaultMessage="There is no address to show for this customer" /> {intl.formatMessage(messages.noAddressToShow)}
</Typography> </Typography>
<Typography className={classes.description}> <Typography className={classes.description}>
<FormattedMessage defaultMessage="This customer doesnt have any adresses added to his address book. You can add address using the button below." /> {intl.formatMessage(messages.doesntHaveAddresses)}
</Typography> </Typography>
<Button <Button
className={classes.addButton} className={classes.addButton}
@ -110,36 +117,28 @@ const CustomerAddressListPage: React.FC<CustomerAddressListPageProps> = props =>
variant="contained" variant="contained"
onClick={onAdd} onClick={onAdd}
> >
<FormattedMessage {intl.formatMessage(messages.addAddress)}
defaultMessage="Add address"
description="button"
/>
</Button> </Button>
</div> </div>
) : ( ) : (
<div className={classes.root}> <div className={classes.root}>
{renderCollection( {renderCollection(customer?.addresses, (address, addressNumber) => (
maybe(() => customer.addresses),
(address, addressNumber) => (
<CustomerAddress <CustomerAddress
address={address} address={address}
addressNumber={addressNumber + 1} addressNumber={addressNumber + 1}
disabled={disabled} disabled={disabled}
isDefaultBillingAddress={ isDefaultBillingAddress={
maybe(() => customer.defaultBillingAddress.id) === customer?.defaultBillingAddress?.id === address?.id
maybe(() => address.id)
} }
isDefaultShippingAddress={ isDefaultShippingAddress={
maybe(() => customer.defaultShippingAddress.id) === customer?.defaultShippingAddress?.id === address?.id
maybe(() => address.id)
} }
onEdit={() => onEdit(address.id)} onEdit={() => onEdit(address.id)}
onRemove={() => onRemove(address.id)} onRemove={() => onRemove(address.id)}
onSetAsDefault={type => onSetAsDefault(address.id, type)} onSetAsDefault={type => onSetAsDefault(address.id, type)}
key={maybe(() => address.id, "skeleton")} key={address?.id || "skeleton"}
/> />
) ))}
)}
</div> </div>
)} )}
</Container> </Container>

View file

@ -4,11 +4,15 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import Container from "@saleor/components/Container"; import Container from "@saleor/components/Container";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import Grid from "@saleor/components/Grid"; import Grid from "@saleor/components/Grid";
import Metadata from "@saleor/components/Metadata/Metadata";
import { MetadataFormData } from "@saleor/components/Metadata/types";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment"; import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment";
import { SubmitPromise } from "@saleor/hooks/useForm"; import { SubmitPromise } from "@saleor/hooks/useForm";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { mapMetadataItemToInput } from "@saleor/utils/maps";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -20,7 +24,7 @@ import CustomerInfo from "../CustomerInfo";
import CustomerOrders from "../CustomerOrders"; import CustomerOrders from "../CustomerOrders";
import CustomerStats from "../CustomerStats"; import CustomerStats from "../CustomerStats";
export interface CustomerDetailsPageFormData { export interface CustomerDetailsPageFormData extends MetadataFormData {
firstName: string; firstName: string;
lastName: string; lastName: string;
email: string; email: string;
@ -55,19 +59,26 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
}: CustomerDetailsPageProps) => { }: CustomerDetailsPageProps) => {
const intl = useIntl(); const intl = useIntl();
const initialForm: CustomerDetailsPageFormData = {
email: customer?.email || "",
firstName: customer?.firstName || "",
isActive: customer?.isActive || false,
lastName: customer?.lastName || "",
note: customer?.note || "",
metadata: customer?.metadata.map(mapMetadataItemToInput),
privateMetadata: customer?.privateMetadata.map(mapMetadataItemToInput)
};
const {
makeChangeHandler: makeMetadataChangeHandler
} = useMetadataChangeTrigger();
return (
<Form initial={initialForm} onSubmit={onSubmit} confirmLeave>
{({ change, data, hasChanged, submit }) => {
const changeMetadata = makeMetadataChangeHandler(change);
return ( return (
<Form
initial={{
email: maybe(() => customer.email, ""),
firstName: maybe(() => customer.firstName, ""),
isActive: maybe(() => customer.isActive, false),
lastName: maybe(() => customer.lastName, ""),
note: maybe(() => customer.note, "")
}}
onSubmit={onSubmit}
confirmLeave
>
{({ change, data, hasChanged, submit }) => (
<Container> <Container>
<AppHeader onBack={onBack}> <AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.customers)} {intl.formatMessage(sectionNames.customers)}
@ -97,6 +108,8 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
onViewAllOrdersClick={onViewAllOrdersClick} onViewAllOrdersClick={onViewAllOrdersClick}
onRowClick={onRowClick} onRowClick={onRowClick}
/> />
<CardSpacer />
<Metadata data={data} onChange={changeMetadata} />
</div> </div>
<div> <div>
<CustomerAddresses <CustomerAddresses
@ -116,7 +129,8 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
onDelete={onDelete} onDelete={onDelete}
/> />
</Container> </Container>
)} );
}}
</Form> </Form>
); );
}; };

View file

@ -946,6 +946,8 @@ export const customerList: ListCustomers_customers_edges_node[] = [
]; ];
export const customer: CustomerDetails_user & CustomerAddresses_user = { export const customer: CustomerDetails_user & CustomerAddresses_user = {
__typename: "User", __typename: "User",
metadata: [],
privateMetadata: [],
addresses: [ addresses: [
{ {
__typename: "Address", __typename: "Address",

View file

@ -8,6 +8,18 @@ import { PaymentChargeStatusEnum } from "./../../types/globalTypes";
// GraphQL query operation: CustomerDetails // GraphQL query operation: CustomerDetails
// ==================================================== // ====================================================
export interface CustomerDetails_user_metadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface CustomerDetails_user_privateMetadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface CustomerDetails_user_defaultShippingAddress_country { export interface CustomerDetails_user_defaultShippingAddress_country {
__typename: "CountryDisplay"; __typename: "CountryDisplay";
code: string; code: string;
@ -104,6 +116,8 @@ export interface CustomerDetails_user {
email: string; email: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
metadata: (CustomerDetails_user_metadata | null)[];
privateMetadata: (CustomerDetails_user_privateMetadata | null)[];
dateJoined: any; dateJoined: any;
lastLogin: any | null; lastLogin: any | null;
defaultShippingAddress: CustomerDetails_user_defaultShippingAddress | null; defaultShippingAddress: CustomerDetails_user_defaultShippingAddress | null;

View file

@ -14,6 +14,18 @@ export interface UpdateCustomer_customerUpdate_errors {
field: string | null; field: string | null;
} }
export interface UpdateCustomer_customerUpdate_user_metadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface UpdateCustomer_customerUpdate_user_privateMetadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface UpdateCustomer_customerUpdate_user_defaultShippingAddress_country { export interface UpdateCustomer_customerUpdate_user_defaultShippingAddress_country {
__typename: "CountryDisplay"; __typename: "CountryDisplay";
code: string; code: string;
@ -64,6 +76,8 @@ export interface UpdateCustomer_customerUpdate_user {
email: string; email: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
metadata: (UpdateCustomer_customerUpdate_user_metadata | null)[];
privateMetadata: (UpdateCustomer_customerUpdate_user_privateMetadata | null)[];
dateJoined: any; dateJoined: any;
lastLogin: any | null; lastLogin: any | null;
defaultShippingAddress: UpdateCustomer_customerUpdate_user_defaultShippingAddress | null; defaultShippingAddress: UpdateCustomer_customerUpdate_user_defaultShippingAddress | null;

View file

@ -9,7 +9,6 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { maybe } from "../../misc";
import CustomerAddressDialog from "../components/CustomerAddressDialog"; import CustomerAddressDialog from "../components/CustomerAddressDialog";
import CustomerAddressListPage from "../components/CustomerAddressListPage"; import CustomerAddressListPage from "../components/CustomerAddressListPage";
import { import {
@ -102,23 +101,20 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
{(removeCustomerAddress, removeCustomerAddressOpts) => ( {(removeCustomerAddress, removeCustomerAddressOpts) => (
<TypedCustomerAddressesQuery variables={{ id }}> <TypedCustomerAddressesQuery variables={{ id }}>
{customerData => { {customerData => {
const countryChoices = maybe( const countryChoices =
() => shop?.countries?.map(country => ({
shop.countries.map(country => ({
code: country.code, code: country.code,
label: country.country label: country.country
})), })) || [];
[]
);
return ( return (
<> <>
<WindowTitle <WindowTitle
title={maybe(() => customerData.data.user.email)} title={customerData?.data?.user.email}
/> />
<CustomerAddressListPage <CustomerAddressListPage
customer={maybe(() => customerData.data.user)} customer={customerData?.data?.user}
disabled={customerData.loading} disabled={customerData?.loading}
onAdd={() => openModal("add")} onAdd={() => openModal("add")}
onBack={() => navigate(customerUrl(id))} onBack={() => navigate(customerUrl(id))}
onEdit={id => onEdit={id =>
@ -143,12 +139,10 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
createCustomerAddressOpts.status createCustomerAddressOpts.status
} }
countries={countryChoices} countries={countryChoices}
errors={maybe( errors={
() => createCustomerAddressOpts?.data?.addressCreate
createCustomerAddressOpts.data.addressCreate .errors || []
.errors, }
[]
)}
open={params.action === "add"} open={params.action === "add"}
variant="create" variant="create"
onClose={closeModal} onClose={closeModal}
@ -162,21 +156,17 @@ const CustomerAddresses: React.FC<CustomerAddressesProps> = ({
} }
/> />
<CustomerAddressDialog <CustomerAddressDialog
address={maybe(() => address={customerData?.data?.user.addresses.find(
customerData.data.user.addresses.find(
addr => addr.id === params.id addr => addr.id === params.id
)
)} )}
confirmButtonState={ confirmButtonState={
updateCustomerAddressOpts.status updateCustomerAddressOpts.status
} }
countries={countryChoices} countries={countryChoices}
errors={maybe( errors={
() => updateCustomerAddressOpts?.data?.addressUpdate
updateCustomerAddressOpts.data.addressUpdate .errors || []
.errors, }
[]
)}
open={params.action === "edit"} open={params.action === "edit"}
variant="edit" variant="edit"
onClose={closeModal} onClose={closeModal}

View file

@ -5,6 +5,11 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
import {
useMetadataUpdate,
usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -78,7 +83,10 @@ export const CustomerDetailsView: React.FC<CustomerDetailsViewProps> = ({
return <NotFoundPage onBack={handleBack} />; return <NotFoundPage onBack={handleBack} />;
} }
const handleSubmit = async ( const [updateMetadata] = useMetadataUpdate({});
const [updatePrivateMetadata] = usePrivateMetadataUpdate({});
const updateData = async (
data: CustomerDetailsPageFormData data: CustomerDetailsPageFormData
) => { ) => {
const result = await updateCustomer({ const result = await updateCustomer({
@ -97,6 +105,13 @@ export const CustomerDetailsView: React.FC<CustomerDetailsViewProps> = ({
return result.data.customerUpdate.errors; return result.data.customerUpdate.errors;
}; };
const handleSubmit = createMetadataUpdateHandler(
user,
updateData,
variables => updateMetadata({ variables }),
variables => updatePrivateMetadata({ variables })
);
return ( return (
<> <>
<WindowTitle <WindowTitle

View file

@ -1,6 +1,7 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import { fragmentAddress } from "./address"; import { fragmentAddress } from "./address";
import { metadataFragment } from "./metadata";
export const customerFragment = gql` export const customerFragment = gql`
fragment CustomerFragment on User { fragment CustomerFragment on User {
@ -12,10 +13,12 @@ export const customerFragment = gql`
`; `;
export const customerDetailsFragment = gql` export const customerDetailsFragment = gql`
${metadataFragment}
${customerFragment} ${customerFragment}
${fragmentAddress} ${fragmentAddress}
fragment CustomerDetailsFragment on User { fragment CustomerDetailsFragment on User {
...CustomerFragment ...CustomerFragment
...MetadataFragment
dateJoined dateJoined
lastLogin lastLogin
defaultShippingAddress { defaultShippingAddress {

View file

@ -13,6 +13,7 @@ export const shippingZoneFragment = gql`
country country
} }
name name
description
} }
`; `;

View file

@ -6,6 +6,18 @@
// GraphQL fragment: CustomerDetailsFragment // GraphQL fragment: CustomerDetailsFragment
// ==================================================== // ====================================================
export interface CustomerDetailsFragment_metadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface CustomerDetailsFragment_privateMetadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface CustomerDetailsFragment_defaultShippingAddress_country { export interface CustomerDetailsFragment_defaultShippingAddress_country {
__typename: "CountryDisplay"; __typename: "CountryDisplay";
code: string; code: string;
@ -56,6 +68,8 @@ export interface CustomerDetailsFragment {
email: string; email: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
metadata: (CustomerDetailsFragment_metadata | null)[];
privateMetadata: (CustomerDetailsFragment_privateMetadata | null)[];
dateJoined: any; dateJoined: any;
lastLogin: any | null; lastLogin: any | null;
defaultShippingAddress: CustomerDetailsFragment_defaultShippingAddress | null; defaultShippingAddress: CustomerDetailsFragment_defaultShippingAddress | null;

View file

@ -117,6 +117,7 @@ export interface ShippingZoneDetailsFragment {
id: string; id: string;
countries: (ShippingZoneDetailsFragment_countries | null)[] | null; countries: (ShippingZoneDetailsFragment_countries | null)[] | null;
name: string; name: string;
description: string | null;
default: boolean; default: boolean;
shippingMethods: (ShippingZoneDetailsFragment_shippingMethods | null)[] | null; shippingMethods: (ShippingZoneDetailsFragment_shippingMethods | null)[] | null;
warehouses: (ShippingZoneDetailsFragment_warehouses | null)[] | null; warehouses: (ShippingZoneDetailsFragment_warehouses | null)[] | null;

View file

@ -31,4 +31,5 @@ export interface ShippingZoneFragment {
id: string; id: string;
countries: (ShippingZoneFragment_countries | null)[] | null; countries: (ShippingZoneFragment_countries | null)[] | null;
name: string; name: string;
description: string | null;
} }

View file

@ -197,17 +197,17 @@ export const transformOrderStatus = (
}; };
export const transformAddressToForm = (data: AddressType) => ({ export const transformAddressToForm = (data: AddressType) => ({
city: maybe(() => data.city, ""), city: data?.city || "",
cityArea: maybe(() => data.cityArea, ""), cityArea: data?.cityArea || "",
companyName: maybe(() => data.companyName, ""), companyName: data?.companyName || "",
country: maybe(() => data.country.code, ""), country: data?.country?.code || "",
countryArea: maybe(() => data.countryArea, ""), countryArea: data?.countryArea || "",
firstName: maybe(() => data.firstName, ""), firstName: data?.firstName || "",
lastName: maybe(() => data.lastName, ""), lastName: data?.lastName || "",
phone: maybe(() => data.phone, ""), phone: data?.phone || "",
postalCode: maybe(() => data.postalCode, ""), postalCode: data?.postalCode || "",
streetAddress1: maybe(() => data.streetAddress1, ""), streetAddress1: data?.streetAddress1 || "",
streetAddress2: maybe(() => data.streetAddress2, "") streetAddress2: data?.streetAddress2 || ""
}); });
export function maybe<T>(exp: () => T): T | undefined; export function maybe<T>(exp: () => T): T | undefined;

View file

@ -0,0 +1,52 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import CardTitle from "@saleor/components/CardTitle";
import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment";
import { commonMessages } from "@saleor/intl";
import { getFormErrors } from "@saleor/utils/errors";
import getShippingErrorMessage from "@saleor/utils/errors/shipping";
import React from "react";
import { useIntl } from "react-intl";
export interface ShippingRateInfoProps {
data: Record<"name", string>;
disabled: boolean;
errors: ShippingErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void;
}
const ShippingRateInfo: React.FC<ShippingRateInfoProps> = ({
data,
disabled,
errors,
onChange
}) => {
const intl = useIntl();
const formErrors = getFormErrors(["name"], errors);
return (
<Card>
<CardTitle
title={intl.formatMessage(commonMessages.generalInformations)}
/>
<CardContent>
<TextField
disabled={disabled}
error={!!formErrors.name}
fullWidth
helperText={getShippingErrorMessage(formErrors.name, intl)}
label={intl.formatMessage({
defaultMessage: "Shipping rate name"
})}
name="name"
value={data.name}
onChange={onChange}
/>
</CardContent>
</Card>
);
};
ShippingRateInfo.displayName = "ShippingRateInfo";
export default ShippingRateInfo;

View file

@ -0,0 +1,2 @@
export { default } from "./ShippingRateInfo";
export * from "./ShippingRateInfo";

View file

@ -11,7 +11,7 @@ import { CountryFragment } from "@saleor/fragments/types/CountryFragment";
import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import ShippingZoneCountriesAssignDialog from "../ShippingZoneCountriesAssignDialog"; import ShippingZoneCountriesAssignDialog from "../ShippingZoneCountriesAssignDialog";
import ShippingZoneInfo from "../ShippingZoneInfo"; import ShippingZoneInfo from "../ShippingZoneInfo";
@ -19,9 +19,29 @@ import ShippingZoneInfo from "../ShippingZoneInfo";
export interface FormData { export interface FormData {
countries: string[]; countries: string[];
default: boolean; default: boolean;
description: string;
name: string; name: string;
} }
const messages = defineMessages({
countries: {
defaultMessage: "Countries",
description: "country list header"
},
createZone: {
defaultMessage: "Create New Shipping Zone",
description: "section header"
},
defaultZone: {
defaultMessage:
"This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones"
},
noCountriesAssigned: {
defaultMessage:
"Currently, there are no countries assigned to this shipping zone"
}
});
export interface ShippingZoneCreatePageProps { export interface ShippingZoneCreatePageProps {
countries: CountryFragment[]; countries: CountryFragment[];
disabled: boolean; disabled: boolean;
@ -46,6 +66,7 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
const initialForm: FormData = { const initialForm: FormData = {
countries: [], countries: [],
default: false, default: false,
description: "",
name: "" name: ""
}; };
@ -57,12 +78,7 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
<AppHeader onBack={onBack}> <AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.shipping)} {intl.formatMessage(sectionNames.shipping)}
</AppHeader> </AppHeader>
<PageHeader <PageHeader title={intl.formatMessage(messages.createZone)} />
title={intl.formatMessage({
defaultMessage: "Create New Shipping Zone",
description: "header"
})}
/>
<Grid> <Grid>
<div> <div>
<ShippingZoneInfo <ShippingZoneInfo
@ -79,14 +95,8 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
disabled={disabled} disabled={disabled}
emptyText={ emptyText={
data.default data.default
? intl.formatMessage({ ? intl.formatMessage(messages.defaultZone)
defaultMessage: : intl.formatMessage(messages.noCountriesAssigned)
"This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones"
})
: intl.formatMessage({
defaultMessage:
"Currently, there are no countries assigned to this shipping zone"
})
} }
onCountryAssign={toggleModal} onCountryAssign={toggleModal}
onCountryUnassign={countryCode => onCountryUnassign={countryCode =>
@ -99,9 +109,7 @@ const ShippingZoneCreatePage: React.FC<ShippingZoneCreatePageProps> = ({
} }
} as any) } as any)
} }
title={intl.formatMessage({ title={intl.formatMessage(messages.countries)}
defaultMessage: "Countries"
})}
/> />
</div> </div>
</Grid> </Grid>

View file

@ -22,7 +22,7 @@ import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAu
import { mapMetadataItemToInput } from "@saleor/utils/maps"; import { mapMetadataItemToInput } from "@saleor/utils/maps";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { defineMessages, FormattedMessage, useIntl } from "react-intl";
import { getStringOrPlaceholder } from "../../../misc"; import { getStringOrPlaceholder } from "../../../misc";
import { ChannelProps, FetchMoreProps, SearchProps } from "../../../types"; import { ChannelProps, FetchMoreProps, SearchProps } from "../../../types";
@ -33,9 +33,29 @@ import ShippingZoneWarehouses from "../ShippingZoneWarehouses";
export interface FormData extends MetadataFormData { export interface FormData extends MetadataFormData {
name: string; name: string;
description: string;
warehouses: string[]; warehouses: string[];
} }
const messages = defineMessages({
countries: {
defaultMessage: "Countries",
description: "country list header"
},
defaultZone: {
defaultMessage:
"This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones"
},
noCountriesAssigned: {
defaultMessage:
"Currently, there are no countries assigned to this shipping zone"
},
shipping: {
defaultMessage: "Shipping",
description: "shipping section header"
}
});
export interface ShippingZoneDetailsPageProps export interface ShippingZoneDetailsPageProps
extends FetchMoreProps, extends FetchMoreProps,
SearchProps, SearchProps,
@ -93,6 +113,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
const intl = useIntl(); const intl = useIntl();
const initialForm: FormData = { const initialForm: FormData = {
description: shippingZone?.description || "",
metadata: shippingZone?.metadata.map(mapMetadataItemToInput), metadata: shippingZone?.metadata.map(mapMetadataItemToInput),
name: shippingZone?.name || "", name: shippingZone?.name || "",
privateMetadata: shippingZone?.privateMetadata.map(mapMetadataItemToInput), privateMetadata: shippingZone?.privateMetadata.map(mapMetadataItemToInput),
@ -128,7 +149,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
return ( return (
<Container> <Container>
<AppHeader onBack={onBack}> <AppHeader onBack={onBack}>
<FormattedMessage defaultMessage="Shipping" /> <FormattedMessage {...messages.shipping} />
</AppHeader> </AppHeader>
<PageHeader title={shippingZone?.name} /> <PageHeader title={shippingZone?.name} />
<Grid> <Grid>
@ -147,20 +168,12 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
shippingZone?.default === undefined shippingZone?.default === undefined
? undefined ? undefined
: shippingZone.default : shippingZone.default
? intl.formatMessage({ ? intl.formatMessage(messages.defaultZone)
defaultMessage: : intl.formatMessage(messages.noCountriesAssigned)
"This is default shipping zone, which means that it covers all of the countries which are not assigned to other shipping zones"
})
: intl.formatMessage({
defaultMessage:
"Currently, there are no countries assigned to this shipping zone"
})
)} )}
onCountryAssign={onCountryAdd} onCountryAssign={onCountryAdd}
onCountryUnassign={onCountryRemove} onCountryUnassign={onCountryRemove}
title={intl.formatMessage({ title={intl.formatMessage(messages.countries)}
defaultMessage: "Countries"
})}
/> />
<CardSpacer /> <CardSpacer />
<ShippingZoneRates <ShippingZoneRates

View file

@ -1,21 +1,40 @@
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent"; import CardContent from "@material-ui/core/CardContent";
import makeStyles from "@material-ui/core/styles/makeStyles";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import CardSpacer from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getFormErrors } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import getShippingErrorMessage from "@saleor/utils/errors/shipping"; import getShippingErrorMessage from "@saleor/utils/errors/shipping";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
export interface ShippingZoneInfoProps { export interface ShippingZoneInfoProps {
data: Record<"name", string>; data: Record<"name" | "description", string>;
disabled: boolean; disabled: boolean;
errors: ShippingErrorFragment[]; errors: ShippingErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
const useStyles = makeStyles(
{
label: {
flex: 1
},
labelContainer: {
"& span": {
paddingRight: 30
},
display: "flex"
}
},
{ name: "ShippingZoneCreatePage" }
);
const MAX_DESCRIPTION_LENGTH = 300;
const ShippingZoneInfo: React.FC<ShippingZoneInfoProps> = ({ const ShippingZoneInfo: React.FC<ShippingZoneInfoProps> = ({
data, data,
disabled, disabled,
@ -23,6 +42,7 @@ const ShippingZoneInfo: React.FC<ShippingZoneInfoProps> = ({
onChange onChange
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({});
const formErrors = getFormErrors(["name"], errors); const formErrors = getFormErrors(["name"], errors);
@ -38,12 +58,51 @@ const ShippingZoneInfo: React.FC<ShippingZoneInfoProps> = ({
fullWidth fullWidth
helperText={getShippingErrorMessage(formErrors.name, intl)} helperText={getShippingErrorMessage(formErrors.name, intl)}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Shipping rate name" defaultMessage: "Shipping zone name"
})} })}
name="name" name="name"
value={data.name} value={data.name}
onChange={onChange} onChange={onChange}
/> />
<CardSpacer />
<TextField
error={data.description.length > MAX_DESCRIPTION_LENGTH}
name={"description"}
label={
<div className={classes.labelContainer}>
<div className={classes.label}>
<FormattedMessage defaultMessage="Description" />
</div>
{data.description?.length > 0 && (
<span>
<FormattedMessage
defaultMessage="{numberOfCharacters} of {maxCharacters} characters"
description="character limit"
values={{
maxCharacters: MAX_DESCRIPTION_LENGTH,
numberOfCharacters: data.description.length
}}
/>
</span>
)}
</div>
}
InputProps={{
inputProps: {
maxLength: MAX_DESCRIPTION_LENGTH
}
}}
value={data.description}
onChange={onChange}
disabled={disabled}
fullWidth
multiline
placeholder={intl.formatMessage({
defaultMessage: "Description of a shipping zone.",
description: "field placeholder"
})}
rows={10}
/>
</CardContent> </CardContent>
</Card> </Card>
); );

View file

@ -37,15 +37,15 @@ const useStyles = makeStyles(
paddingRight: 24, paddingRight: 24,
width: ICONBUTTON_SIZE + theme.spacing(0.5) width: ICONBUTTON_SIZE + theme.spacing(0.5)
}, },
buttonColumn: {
padding: "4px 0",
width: "62px"
},
nameColumn: { nameColumn: {
width: "auto" width: "auto"
}, },
valueColumn: { valueColumn: {
width: "auto" width: "auto"
},
buttonColumn: {
width: "62px",
padding: "4px 0"
} }
}), }),
{ name: "ShippingZoneRates" } { name: "ShippingZoneRates" }

View file

@ -15,7 +15,7 @@ import { validatePrice } from "@saleor/products/utils/validation";
import OrderValue from "@saleor/shipping/components/OrderValue"; import OrderValue from "@saleor/shipping/components/OrderValue";
import OrderWeight from "@saleor/shipping/components/OrderWeight"; import OrderWeight from "@saleor/shipping/components/OrderWeight";
import PricingCard from "@saleor/shipping/components/PricingCard"; import PricingCard from "@saleor/shipping/components/PricingCard";
import ShippingZoneInfo from "@saleor/shipping/components/ShippingZoneInfo"; import ShippingRateInfo from "@saleor/shipping/components/ShippingRateInfo";
import { createChannelsChangeHandler } from "@saleor/shipping/handlers"; import { createChannelsChangeHandler } from "@saleor/shipping/handlers";
import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; import { ShippingMethodTypeEnum } from "@saleor/types/globalTypes";
import React from "react"; import React from "react";
@ -116,7 +116,7 @@ export const ShippingZoneRatesCreatePage: React.FC<ShippingZoneRatesCreatePagePr
/> />
<Grid> <Grid>
<div> <div>
<ShippingZoneInfo <ShippingRateInfo
data={data} data={data}
disabled={disabled} disabled={disabled}
errors={errors} errors={errors}

View file

@ -17,7 +17,7 @@ import OrderValue from "@saleor/shipping/components/OrderValue";
import OrderWeight from "@saleor/shipping/components/OrderWeight"; import OrderWeight from "@saleor/shipping/components/OrderWeight";
import PricingCard from "@saleor/shipping/components/PricingCard"; import PricingCard from "@saleor/shipping/components/PricingCard";
import ShippingMethodProducts from "@saleor/shipping/components/ShippingMethodProducts"; import ShippingMethodProducts from "@saleor/shipping/components/ShippingMethodProducts";
import ShippingZoneInfo from "@saleor/shipping/components/ShippingZoneInfo"; import ShippingRateInfo from "@saleor/shipping/components/ShippingRateInfo";
import { createChannelsChangeHandler } from "@saleor/shipping/handlers"; import { createChannelsChangeHandler } from "@saleor/shipping/handlers";
import { ShippingZone_shippingZone_shippingMethods } from "@saleor/shipping/types/ShippingZone"; import { ShippingZone_shippingZone_shippingMethods } from "@saleor/shipping/types/ShippingZone";
import { ListActions, ListProps } from "@saleor/types"; import { ListActions, ListProps } from "@saleor/types";
@ -124,7 +124,7 @@ export const ShippingZoneRatesPage: React.FC<ShippingZoneRatesPageProps> = ({
<PageHeader title={rate?.name} /> <PageHeader title={rate?.name} />
<Grid> <Grid>
<div> <div>
<ShippingZoneInfo <ShippingRateInfo
data={data} data={data}
disabled={disabled} disabled={disabled}
errors={errors} errors={errors}

View file

@ -102,10 +102,9 @@ const ShippingZoneZipCodes: React.FC<ShippingZoneZipCodesProps> = ({
alignTop alignTop
choices={[ choices={[
{ {
disabled: true,
label: ( label: (
<div className={classes.option}> <div className={classes.option}>
<Typography color="textSecondary" variant="body1"> <Typography variant="body1">
<FormattedMessage <FormattedMessage
defaultMessage="Exclude postal codes" defaultMessage="Exclude postal codes"
description="action" description="action"
@ -119,9 +118,10 @@ const ShippingZoneZipCodes: React.FC<ShippingZoneZipCodesProps> = ({
value: ZipCodeInclusion.Exclude value: ZipCodeInclusion.Exclude
}, },
{ {
disabled: true,
label: ( label: (
<div className={classes.option}> <div className={classes.option}>
<Typography variant="body1"> <Typography color="textSecondary" variant="body1">
<FormattedMessage <FormattedMessage
defaultMessage="Include postal codes" defaultMessage="Include postal codes"
description="action" description="action"

View file

@ -264,6 +264,7 @@ export const shippingZones: ShippingZoneFragment[] = [
country: "Wielka Brytania" country: "Wielka Brytania"
} }
], ],
description: "Shipping zone description",
id: "U2hpcHBpbmdab25lOjE=", id: "U2hpcHBpbmdab25lOjE=",
metadata: [], metadata: [],
name: "Europe", name: "Europe",
@ -418,6 +419,7 @@ export const shippingZones: ShippingZoneFragment[] = [
country: "Wallis i Futuna" country: "Wallis i Futuna"
} }
], ],
description: "Shipping zone description",
id: "U2hpcHBpbmdab25lOjI=", id: "U2hpcHBpbmdab25lOjI=",
metadata: [], metadata: [],
name: "Oceania", name: "Oceania",
@ -425,7 +427,6 @@ export const shippingZones: ShippingZoneFragment[] = [
}, },
{ {
__typename: "ShippingZone", __typename: "ShippingZone",
countries: [ countries: [
{ {
__typename: "CountryDisplay", __typename: "CountryDisplay",
@ -683,6 +684,7 @@ export const shippingZones: ShippingZoneFragment[] = [
country: "Jemen" country: "Jemen"
} }
], ],
description: "Shipping zone description",
id: "U2hpcHBpbmdab25lOjM=", id: "U2hpcHBpbmdab25lOjM=",
metadata: [], metadata: [],
name: "Asia", name: "Asia",
@ -690,7 +692,6 @@ export const shippingZones: ShippingZoneFragment[] = [
}, },
{ {
__typename: "ShippingZone", __typename: "ShippingZone",
countries: [ countries: [
{ {
__typename: "CountryDisplay", __typename: "CountryDisplay",
@ -978,6 +979,7 @@ export const shippingZones: ShippingZoneFragment[] = [
country: "Wyspy Dziewicze Stanów Zjednoczonych" country: "Wyspy Dziewicze Stanów Zjednoczonych"
} }
], ],
description: "Shipping zone description",
id: "U2hpcHBpbmdab25lOjQ=", id: "U2hpcHBpbmdab25lOjQ=",
metadata: [], metadata: [],
name: "Americas", name: "Americas",
@ -985,7 +987,6 @@ export const shippingZones: ShippingZoneFragment[] = [
}, },
{ {
__typename: "ShippingZone", __typename: "ShippingZone",
countries: [ countries: [
{ {
__typename: "CountryDisplay", __typename: "CountryDisplay",
@ -1289,6 +1290,7 @@ export const shippingZones: ShippingZoneFragment[] = [
country: "Zimbabwe" country: "Zimbabwe"
} }
], ],
description: "Shipping zone description",
id: "U2hpcHBpbmdab25lOjU=", id: "U2hpcHBpbmdab25lOjU=",
metadata: [], metadata: [],
name: "Africa", name: "Africa",
@ -1556,6 +1558,7 @@ export const shippingZone: ShippingZone_shippingZone = {
} }
], ],
default: false, default: false,
description: "Shipping zone description",
id: "U2hpcHBpbmdab25lOjE=", id: "U2hpcHBpbmdab25lOjE=",
metadata: [], metadata: [],
name: "Europe", name: "Europe",

View file

@ -123,6 +123,7 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone {
id: string; id: string;
countries: (CreateShippingRate_shippingPriceCreate_shippingZone_countries | null)[] | null; countries: (CreateShippingRate_shippingPriceCreate_shippingZone_countries | null)[] | null;
name: string; name: string;
description: string | null;
default: boolean; default: boolean;
shippingMethods: (CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods | null)[] | null; shippingMethods: (CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods | null)[] | null;
warehouses: (CreateShippingRate_shippingPriceCreate_shippingZone_warehouses | null)[] | null; warehouses: (CreateShippingRate_shippingPriceCreate_shippingZone_warehouses | null)[] | null;

View file

@ -123,6 +123,7 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone {
id: string; id: string;
countries: (DeleteShippingRate_shippingPriceDelete_shippingZone_countries | null)[] | null; countries: (DeleteShippingRate_shippingPriceDelete_shippingZone_countries | null)[] | null;
name: string; name: string;
description: string | null;
default: boolean; default: boolean;
shippingMethods: (DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods | null)[] | null; shippingMethods: (DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods | null)[] | null;
warehouses: (DeleteShippingRate_shippingPriceDelete_shippingZone_warehouses | null)[] | null; warehouses: (DeleteShippingRate_shippingPriceDelete_shippingZone_warehouses | null)[] | null;

View file

@ -149,6 +149,7 @@ export interface ShippingZone_shippingZone {
id: string; id: string;
countries: (ShippingZone_shippingZone_countries | null)[] | null; countries: (ShippingZone_shippingZone_countries | null)[] | null;
name: string; name: string;
description: string | null;
default: boolean; default: boolean;
shippingMethods: (ShippingZone_shippingZone_shippingMethods | null)[] | null; shippingMethods: (ShippingZone_shippingZone_shippingMethods | null)[] | null;
warehouses: (ShippingZone_shippingZone_warehouses | null)[] | null; warehouses: (ShippingZone_shippingZone_warehouses | null)[] | null;

View file

@ -31,6 +31,7 @@ export interface ShippingZones_shippingZones_edges_node {
id: string; id: string;
countries: (ShippingZones_shippingZones_edges_node_countries | null)[] | null; countries: (ShippingZones_shippingZones_edges_node_countries | null)[] | null;
name: string; name: string;
description: string | null;
} }
export interface ShippingZones_shippingZones_edges { export interface ShippingZones_shippingZones_edges {

View file

@ -144,6 +144,7 @@ const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
id, id,
input: { input: {
addWarehouses: warehouseDiff.added, addWarehouses: warehouseDiff.added,
description: submitData.description,
name: submitData.name, name: submitData.name,
removeWarehouses: warehouseDiff.removed removeWarehouses: warehouseDiff.removed
} }

File diff suppressed because it is too large Load diff

View file

@ -1710,6 +1710,7 @@ export interface ShippingZipCodeRulesCreateInputRange {
export interface ShippingZoneCreateInput { export interface ShippingZoneCreateInput {
name?: string | null; name?: string | null;
description?: string | null;
countries?: (string | null)[] | null; countries?: (string | null)[] | null;
default?: boolean | null; default?: boolean | null;
addWarehouses?: (string | null)[] | null; addWarehouses?: (string | null)[] | null;
@ -1717,6 +1718,7 @@ export interface ShippingZoneCreateInput {
export interface ShippingZoneUpdateInput { export interface ShippingZoneUpdateInput {
name?: string | null; name?: string | null;
description?: string | null;
countries?: (string | null)[] | null; countries?: (string | null)[] | null;
default?: boolean | null; default?: boolean | null;
addWarehouses?: (string | null)[] | null; addWarehouses?: (string | null)[] | null;