diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml new file mode 100644 index 000000000..20b90e74c --- /dev/null +++ b/.github/workflows/deploy-staging.yaml @@ -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*" \ No newline at end of file diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 60d203bbe..b75ed8344 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -2200,40 +2200,48 @@ "context": "dialog title", "string": "Add Address" }, - "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_1090326769": { - "context": "customer's address book, header", - "string": "{fullName}'s Address Book" - }, - "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_1428369222": { - "string": "This customer doesn’t 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": { + "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_addAddress": { "context": "button", "string": "Add address" }, - "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_489918044": { + "src_dot_customers_dot_components_dot_CustomerAddressListPage_dot_doesntHaveAddresses": { + "string": "This customer doesn’t 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", "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" }, - "src_dot_customers_dot_components_dot_CustomerAddress_dot_1578192486": { + "src_dot_customers_dot_components_dot_CustomerAddress_dot_defaultBillingAddress": { "string": "Default Billing Address" }, - "src_dot_customers_dot_components_dot_CustomerAddress_dot_2131178753": { - "context": "button", - "string": "Set as default shipping address" + "src_dot_customers_dot_components_dot_CustomerAddress_dot_defaultShippingAddress": { + "string": "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", "string": "Set as default billing address" }, - "src_dot_customers_dot_components_dot_CustomerAddress_dot_4109348993": { - "string": "Default Shipping Address" + "src_dot_customers_dot_components_dot_CustomerAddress_dot_setDefaultShipping": { + "context": "button", + "string": "Set as default shipping address" }, "src_dot_customers_dot_components_dot_CustomerAddresses_dot_1967111456": { "context": "header", @@ -5380,6 +5388,9 @@ "src_dot_shipping_dot_components_dot_ShippingMethodProducts_dot_4190792473": { "string": "Actions" }, + "src_dot_shipping_dot_components_dot_ShippingRateInfo_dot_579967655": { + "string": "Shipping rate name" + }, "src_dot_shipping_dot_components_dot_ShippingRateZipCodeRangeRemoveDialog_dot_3640694505": { "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": { "string": "Search Countries" }, - "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_2364051773": { - "string": "Currently, there are no countries assigned to this shipping zone" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_3109712047": { + "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_countries": { + "context": "country list header", "string": "Countries" }, - "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_4049462680": { - "context": "header", + "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_createZone": { + "context": "section header", "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" }, - "src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_1325966144": { - "string": "Shipping" - }, - "src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_2364051773": { + "src_dot_shipping_dot_components_dot_ShippingZoneCreatePage_dot_noCountriesAssigned": { "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" }, - "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" }, - "src_dot_shipping_dot_components_dot_ShippingZoneInfo_dot_579967655": { - "string": "Shipping rate name" + "src_dot_shipping_dot_components_dot_ShippingZoneDetailsPage_dot_noCountriesAssigned": { + "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": { "context": "page title", diff --git a/schema.graphql b/schema.graphql index aa914316b..0bdaec696 100644 --- a/schema.graphql +++ b/schema.graphql @@ -4782,6 +4782,7 @@ type ShippingZone implements Node & ObjectWithMetadata { countries: [CountryDisplay] shippingMethods: [ShippingMethod] warehouses: [Warehouse] + description: String } type ShippingZoneBulkDelete { @@ -4809,6 +4810,7 @@ type ShippingZoneCreate { input ShippingZoneCreateInput { name: String + description: String countries: [String] default: Boolean addWarehouses: [ID] @@ -4828,6 +4830,7 @@ type ShippingZoneUpdate { input ShippingZoneUpdateInput { name: String + description: String countries: [String] default: Boolean addWarehouses: [ID] diff --git a/src/components/AddressFormatter/AddressFormatter.tsx b/src/components/AddressFormatter/AddressFormatter.tsx index a7f7f59ac..86db5e226 100644 --- a/src/components/AddressFormatter/AddressFormatter.tsx +++ b/src/components/AddressFormatter/AddressFormatter.tsx @@ -21,6 +21,7 @@ const AddressFormatter: React.FC = ({ address }) => { {address.firstName} {address.lastName} + {address.phone} {address.companyName && ( {address.companyName} )} diff --git a/src/customers/components/CustomerAddress/CustomerAddress.tsx b/src/customers/components/CustomerAddress/CustomerAddress.tsx index 7a40c0988..900ccb803 100644 --- a/src/customers/components/CustomerAddress/CustomerAddress.tsx +++ b/src/customers/components/CustomerAddress/CustomerAddress.tsx @@ -1,15 +1,12 @@ -import Button from "@material-ui/core/Button"; import Card from "@material-ui/core/Card"; -import CardActions from "@material-ui/core/CardActions"; import CardContent from "@material-ui/core/CardContent"; import { makeStyles } from "@material-ui/core/styles"; import AddressFormatter from "@saleor/components/AddressFormatter"; import CardMenu from "@saleor/components/CardMenu"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; -import { buttonMessages } from "@saleor/intl"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { defineMessages, useIntl } from "react-intl"; import { AddressTypeEnum } from "../../../types/globalTypes"; import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses"; @@ -25,6 +22,34 @@ export interface CustomerAddressProps { 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( { actions: { @@ -64,17 +89,11 @@ const CustomerAddress: React.FC = props => { address ? ( <> {isDefaultBillingAddress && isDefaultShippingAddress - ? intl.formatMessage({ - defaultMessage: "Default Address" - }) + ? intl.formatMessage(messages.defaultAddress) : isDefaultShippingAddress - ? intl.formatMessage({ - defaultMessage: "Default Shipping Address" - }) + ? intl.formatMessage(messages.defaultShippingAddress) : isDefaultBillingAddress - ? intl.formatMessage({ - defaultMessage: "Default Billing Address" - }) + ? intl.formatMessage(messages.defaultBillingAddress) : null} ) : ( @@ -87,18 +106,20 @@ const CustomerAddress: React.FC = props => { disabled={disabled} menuItems={[ { - label: intl.formatMessage({ - defaultMessage: "Set as default shipping address", - description: "button" - }), + label: intl.formatMessage(messages.setDefaultShipping), onSelect: () => onSetAsDefault(AddressTypeEnum.SHIPPING) }, { - label: intl.formatMessage({ - defaultMessage: "Set as default billing address", - description: "button" - }), + label: intl.formatMessage(messages.setDefaultBilling), 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 = props => { -
- - - - -
); }; diff --git a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx index f60e53dfd..3ca004fa4 100644 --- a/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx +++ b/src/customers/components/CustomerAddressDialog/CustomerAddressDialog.tsx @@ -14,7 +14,6 @@ import useAddressValidation from "@saleor/hooks/useAddressValidation"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import useStateFromProps from "@saleor/hooks/useStateFromProps"; import { buttonMessages } from "@saleor/intl"; -import { maybe } from "@saleor/misc"; import { AddressInput } from "@saleor/types/globalTypes"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import React from "react"; @@ -59,7 +58,7 @@ const CustomerAddressDialog = withStyles( onConfirm }: CustomerAddressDialogProps & WithStyles) => { const [countryDisplayName, setCountryDisplayName] = useStateFromProps( - maybe(() => address.country.country, "") + address?.country.country || "" ); const { errors: validationErrors, @@ -71,27 +70,24 @@ const CustomerAddressDialog = withStyles( ); const initialForm: AddressTypeInput = { - city: maybe(() => address.city, ""), - cityArea: maybe(() => address.cityArea, ""), - companyName: maybe(() => address.companyName, ""), - country: maybe(() => address.country.code, ""), - countryArea: maybe(() => address.countryArea, ""), - firstName: maybe(() => address.firstName, ""), - lastName: maybe(() => address.lastName, ""), - phone: maybe(() => address.phone, ""), - postalCode: maybe(() => address.postalCode, ""), - streetAddress1: maybe(() => address.streetAddress1, ""), - streetAddress2: maybe(() => address.streetAddress2, "") + city: address?.city || "", + cityArea: address?.cityArea || "", + companyName: address?.companyName || "", + country: address?.country.code || "", + countryArea: address?.countryArea || "", + firstName: address?.firstName || "", + lastName: address?.lastName || "", + phone: address?.phone || "", + postalCode: address?.postalCode || "", + streetAddress1: address?.streetAddress1 || "", + streetAddress2: address?.streetAddress2 || "" }; - const countryChoices = maybe( - () => - countries.map(country => ({ - label: country.label, - value: country.code - })), - [] - ); + const countryChoices = + countries?.map(country => ({ + label: country.label, + value: country.code + })) || []; return ( 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 doesn’t have any adresses added to his address book. You can add address using the button below." + } +}); + const useStyles = makeStyles( theme => ({ addButton: { @@ -36,10 +58,15 @@ const useStyles = makeStyles( width: 600 }, root: { - columnGap: theme.spacing(3), display: "grid", + gap: `${theme.spacing(3)}px`, 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" } @@ -59,50 +86,30 @@ const CustomerAddressListPage: React.FC = props => const intl = useIntl(); - const isEmpty = maybe(() => customer.addresses.length) === 0; - const fullName = maybe( - () => [customer.firstName, customer.lastName].join(" "), - "..." - ); + const isEmpty = customer?.addresses?.length === 0; + const fullName = [customer?.firstName, customer?.lastName].join(" ") || "..."; return ( - + {intl.formatMessage(messages.fullNameDetail, { fullName })} {!isEmpty && ( )} {isEmpty ? (
- + {intl.formatMessage(messages.noAddressToShow)} - + {intl.formatMessage(messages.doesntHaveAddresses)}
) : (
- {renderCollection( - maybe(() => customer.addresses), - (address, addressNumber) => ( - customer.defaultBillingAddress.id) === - maybe(() => address.id) - } - isDefaultShippingAddress={ - maybe(() => customer.defaultShippingAddress.id) === - maybe(() => address.id) - } - onEdit={() => onEdit(address.id)} - onRemove={() => onRemove(address.id)} - onSetAsDefault={type => onSetAsDefault(address.id, type)} - key={maybe(() => address.id, "skeleton")} - /> - ) - )} + {renderCollection(customer?.addresses, (address, addressNumber) => ( + onEdit(address.id)} + onRemove={() => onRemove(address.id)} + onSetAsDefault={type => onSetAsDefault(address.id, type)} + key={address?.id || "skeleton"} + /> + ))}
)}
diff --git a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx index 0cc6b19c5..f4a2d8ae3 100644 --- a/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx +++ b/src/customers/components/CustomerDetailsPage/CustomerDetailsPage.tsx @@ -4,11 +4,15 @@ import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; import Container from "@saleor/components/Container"; import Form from "@saleor/components/Form"; import Grid from "@saleor/components/Grid"; +import Metadata from "@saleor/components/Metadata/Metadata"; +import { MetadataFormData } from "@saleor/components/Metadata/types"; import PageHeader from "@saleor/components/PageHeader"; import SaveButtonBar from "@saleor/components/SaveButtonBar"; import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment"; import { SubmitPromise } from "@saleor/hooks/useForm"; import { sectionNames } from "@saleor/intl"; +import { mapMetadataItemToInput } from "@saleor/utils/maps"; +import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; import { useIntl } from "react-intl"; @@ -20,7 +24,7 @@ import CustomerInfo from "../CustomerInfo"; import CustomerOrders from "../CustomerOrders"; import CustomerStats from "../CustomerStats"; -export interface CustomerDetailsPageFormData { +export interface CustomerDetailsPageFormData extends MetadataFormData { firstName: string; lastName: string; email: string; @@ -55,68 +59,78 @@ const CustomerDetailsPage: React.FC = ({ }: CustomerDetailsPageProps) => { 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 ( -
customer.email, ""), - firstName: maybe(() => customer.firstName, ""), - isActive: maybe(() => customer.isActive, false), - lastName: maybe(() => customer.lastName, ""), - note: maybe(() => customer.note, "") + + {({ change, data, hasChanged, submit }) => { + const changeMetadata = makeMetadataChangeHandler(change); + + return ( + + + {intl.formatMessage(sectionNames.customers)} + + + +
+ + + + + + customer.orders.edges.map(edge => edge.node) + )} + onViewAllOrdersClick={onViewAllOrdersClick} + onRowClick={onRowClick} + /> + + +
+
+ + + +
+
+ +
+ ); }} - onSubmit={onSubmit} - confirmLeave - > - {({ change, data, hasChanged, submit }) => ( - - - {intl.formatMessage(sectionNames.customers)} - - - -
- - - - - - customer.orders.edges.map(edge => edge.node) - )} - onViewAllOrdersClick={onViewAllOrdersClick} - onRowClick={onRowClick} - /> -
-
- - - -
-
- -
- )}
); }; diff --git a/src/customers/fixtures.ts b/src/customers/fixtures.ts index e3f2eb94d..018da8138 100644 --- a/src/customers/fixtures.ts +++ b/src/customers/fixtures.ts @@ -946,6 +946,8 @@ export const customerList: ListCustomers_customers_edges_node[] = [ ]; export const customer: CustomerDetails_user & CustomerAddresses_user = { __typename: "User", + metadata: [], + privateMetadata: [], addresses: [ { __typename: "Address", diff --git a/src/customers/types/CustomerDetails.ts b/src/customers/types/CustomerDetails.ts index 84cd2e038..840e0a996 100644 --- a/src/customers/types/CustomerDetails.ts +++ b/src/customers/types/CustomerDetails.ts @@ -8,6 +8,18 @@ import { PaymentChargeStatusEnum } from "./../../types/globalTypes"; // 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 { __typename: "CountryDisplay"; code: string; @@ -104,6 +116,8 @@ export interface CustomerDetails_user { email: string; firstName: string; lastName: string; + metadata: (CustomerDetails_user_metadata | null)[]; + privateMetadata: (CustomerDetails_user_privateMetadata | null)[]; dateJoined: any; lastLogin: any | null; defaultShippingAddress: CustomerDetails_user_defaultShippingAddress | null; diff --git a/src/customers/types/UpdateCustomer.ts b/src/customers/types/UpdateCustomer.ts index 61f7051c2..500a0c01c 100644 --- a/src/customers/types/UpdateCustomer.ts +++ b/src/customers/types/UpdateCustomer.ts @@ -14,6 +14,18 @@ export interface UpdateCustomer_customerUpdate_errors { 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 { __typename: "CountryDisplay"; code: string; @@ -64,6 +76,8 @@ export interface UpdateCustomer_customerUpdate_user { email: string; firstName: string; lastName: string; + metadata: (UpdateCustomer_customerUpdate_user_metadata | null)[]; + privateMetadata: (UpdateCustomer_customerUpdate_user_privateMetadata | null)[]; dateJoined: any; lastLogin: any | null; defaultShippingAddress: UpdateCustomer_customerUpdate_user_defaultShippingAddress | null; diff --git a/src/customers/views/CustomerAddresses.tsx b/src/customers/views/CustomerAddresses.tsx index cf1a7975a..ef971ddce 100644 --- a/src/customers/views/CustomerAddresses.tsx +++ b/src/customers/views/CustomerAddresses.tsx @@ -9,7 +9,6 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { maybe } from "../../misc"; import CustomerAddressDialog from "../components/CustomerAddressDialog"; import CustomerAddressListPage from "../components/CustomerAddressListPage"; import { @@ -102,23 +101,20 @@ const CustomerAddresses: React.FC = ({ {(removeCustomerAddress, removeCustomerAddressOpts) => ( {customerData => { - const countryChoices = maybe( - () => - shop.countries.map(country => ({ - code: country.code, - label: country.country - })), - [] - ); + const countryChoices = + shop?.countries?.map(country => ({ + code: country.code, + label: country.country + })) || []; return ( <> customerData.data.user.email)} + title={customerData?.data?.user.email} /> customerData.data.user)} - disabled={customerData.loading} + customer={customerData?.data?.user} + disabled={customerData?.loading} onAdd={() => openModal("add")} onBack={() => navigate(customerUrl(id))} onEdit={id => @@ -143,12 +139,10 @@ const CustomerAddresses: React.FC = ({ createCustomerAddressOpts.status } countries={countryChoices} - errors={maybe( - () => - createCustomerAddressOpts.data.addressCreate - .errors, - [] - )} + errors={ + createCustomerAddressOpts?.data?.addressCreate + .errors || [] + } open={params.action === "add"} variant="create" onClose={closeModal} @@ -162,21 +156,17 @@ const CustomerAddresses: React.FC = ({ } /> - customerData.data.user.addresses.find( - addr => addr.id === params.id - ) + address={customerData?.data?.user.addresses.find( + addr => addr.id === params.id )} confirmButtonState={ updateCustomerAddressOpts.status } countries={countryChoices} - errors={maybe( - () => - updateCustomerAddressOpts.data.addressUpdate - .errors, - [] - )} + errors={ + updateCustomerAddressOpts?.data?.addressUpdate + .errors || [] + } open={params.action === "edit"} variant="edit" onClose={closeModal} diff --git a/src/customers/views/CustomerDetails.tsx b/src/customers/views/CustomerDetails.tsx index 765c471e0..5f535d3b8 100644 --- a/src/customers/views/CustomerDetails.tsx +++ b/src/customers/views/CustomerDetails.tsx @@ -5,6 +5,11 @@ import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; 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 { FormattedMessage, useIntl } from "react-intl"; @@ -78,7 +83,10 @@ export const CustomerDetailsView: React.FC = ({ return ; } - const handleSubmit = async ( + const [updateMetadata] = useMetadataUpdate({}); + const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); + + const updateData = async ( data: CustomerDetailsPageFormData ) => { const result = await updateCustomer({ @@ -97,6 +105,13 @@ export const CustomerDetailsView: React.FC = ({ return result.data.customerUpdate.errors; }; + const handleSubmit = createMetadataUpdateHandler( + user, + updateData, + variables => updateMetadata({ variables }), + variables => updatePrivateMetadata({ variables }) + ); + return ( <> ({ - city: maybe(() => data.city, ""), - cityArea: maybe(() => data.cityArea, ""), - companyName: maybe(() => data.companyName, ""), - country: maybe(() => data.country.code, ""), - countryArea: maybe(() => data.countryArea, ""), - firstName: maybe(() => data.firstName, ""), - lastName: maybe(() => data.lastName, ""), - phone: maybe(() => data.phone, ""), - postalCode: maybe(() => data.postalCode, ""), - streetAddress1: maybe(() => data.streetAddress1, ""), - streetAddress2: maybe(() => data.streetAddress2, "") + city: data?.city || "", + cityArea: data?.cityArea || "", + companyName: data?.companyName || "", + country: data?.country?.code || "", + countryArea: data?.countryArea || "", + firstName: data?.firstName || "", + lastName: data?.lastName || "", + phone: data?.phone || "", + postalCode: data?.postalCode || "", + streetAddress1: data?.streetAddress1 || "", + streetAddress2: data?.streetAddress2 || "" }); export function maybe(exp: () => T): T | undefined; diff --git a/src/shipping/components/ShippingRateInfo/ShippingRateInfo.tsx b/src/shipping/components/ShippingRateInfo/ShippingRateInfo.tsx new file mode 100644 index 000000000..b66937bee --- /dev/null +++ b/src/shipping/components/ShippingRateInfo/ShippingRateInfo.tsx @@ -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) => void; +} + +const ShippingRateInfo: React.FC = ({ + data, + disabled, + errors, + onChange +}) => { + const intl = useIntl(); + + const formErrors = getFormErrors(["name"], errors); + + return ( + + + + + + + ); +}; +ShippingRateInfo.displayName = "ShippingRateInfo"; +export default ShippingRateInfo; diff --git a/src/shipping/components/ShippingRateInfo/index.ts b/src/shipping/components/ShippingRateInfo/index.ts new file mode 100644 index 000000000..8002507b2 --- /dev/null +++ b/src/shipping/components/ShippingRateInfo/index.ts @@ -0,0 +1,2 @@ +export { default } from "./ShippingRateInfo"; +export * from "./ShippingRateInfo"; diff --git a/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx b/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx index a357ef81d..c221a5c1c 100644 --- a/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx +++ b/src/shipping/components/ShippingZoneCreatePage/ShippingZoneCreatePage.tsx @@ -11,7 +11,7 @@ import { CountryFragment } from "@saleor/fragments/types/CountryFragment"; import { ShippingErrorFragment } from "@saleor/fragments/types/ShippingErrorFragment"; import { sectionNames } from "@saleor/intl"; import React from "react"; -import { useIntl } from "react-intl"; +import { defineMessages, useIntl } from "react-intl"; import ShippingZoneCountriesAssignDialog from "../ShippingZoneCountriesAssignDialog"; import ShippingZoneInfo from "../ShippingZoneInfo"; @@ -19,9 +19,29 @@ import ShippingZoneInfo from "../ShippingZoneInfo"; export interface FormData { countries: string[]; default: boolean; + description: 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 { countries: CountryFragment[]; disabled: boolean; @@ -46,6 +66,7 @@ const ShippingZoneCreatePage: React.FC = ({ const initialForm: FormData = { countries: [], default: false, + description: "", name: "" }; @@ -57,12 +78,7 @@ const ShippingZoneCreatePage: React.FC = ({ {intl.formatMessage(sectionNames.shipping)} - +
= ({ disabled={disabled} emptyText={ data.default - ? intl.formatMessage({ - defaultMessage: - "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" - }) + ? intl.formatMessage(messages.defaultZone) + : intl.formatMessage(messages.noCountriesAssigned) } onCountryAssign={toggleModal} onCountryUnassign={countryCode => @@ -99,9 +109,7 @@ const ShippingZoneCreatePage: React.FC = ({ } } as any) } - title={intl.formatMessage({ - defaultMessage: "Countries" - })} + title={intl.formatMessage(messages.countries)} />
diff --git a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx index 204ee2bdb..0c7a55b0e 100644 --- a/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx +++ b/src/shipping/components/ShippingZoneDetailsPage/ShippingZoneDetailsPage.tsx @@ -22,7 +22,7 @@ import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAu import { mapMetadataItemToInput } from "@saleor/utils/maps"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { defineMessages, FormattedMessage, useIntl } from "react-intl"; import { getStringOrPlaceholder } from "../../../misc"; import { ChannelProps, FetchMoreProps, SearchProps } from "../../../types"; @@ -33,9 +33,29 @@ import ShippingZoneWarehouses from "../ShippingZoneWarehouses"; export interface FormData extends MetadataFormData { name: string; + description: 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 extends FetchMoreProps, SearchProps, @@ -93,6 +113,7 @@ const ShippingZoneDetailsPage: React.FC = ({ const intl = useIntl(); const initialForm: FormData = { + description: shippingZone?.description || "", metadata: shippingZone?.metadata.map(mapMetadataItemToInput), name: shippingZone?.name || "", privateMetadata: shippingZone?.privateMetadata.map(mapMetadataItemToInput), @@ -128,7 +149,7 @@ const ShippingZoneDetailsPage: React.FC = ({ return ( - + @@ -147,20 +168,12 @@ const ShippingZoneDetailsPage: React.FC = ({ shippingZone?.default === undefined ? undefined : shippingZone.default - ? intl.formatMessage({ - defaultMessage: - "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" - }) + ? intl.formatMessage(messages.defaultZone) + : intl.formatMessage(messages.noCountriesAssigned) )} onCountryAssign={onCountryAdd} onCountryUnassign={onCountryRemove} - title={intl.formatMessage({ - defaultMessage: "Countries" - })} + title={intl.formatMessage(messages.countries)} /> ; + data: Record<"name" | "description", string>; disabled: boolean; errors: ShippingErrorFragment[]; onChange: (event: React.ChangeEvent) => void; } +const useStyles = makeStyles( + { + label: { + flex: 1 + }, + labelContainer: { + "& span": { + paddingRight: 30 + }, + display: "flex" + } + }, + { name: "ShippingZoneCreatePage" } +); + +const MAX_DESCRIPTION_LENGTH = 300; + const ShippingZoneInfo: React.FC = ({ data, disabled, @@ -23,6 +42,7 @@ const ShippingZoneInfo: React.FC = ({ onChange }) => { const intl = useIntl(); + const classes = useStyles({}); const formErrors = getFormErrors(["name"], errors); @@ -38,12 +58,51 @@ const ShippingZoneInfo: React.FC = ({ fullWidth helperText={getShippingErrorMessage(formErrors.name, intl)} label={intl.formatMessage({ - defaultMessage: "Shipping rate name" + defaultMessage: "Shipping zone name" })} name="name" value={data.name} onChange={onChange} /> + + MAX_DESCRIPTION_LENGTH} + name={"description"} + label={ +
+
+ +
+ {data.description?.length > 0 && ( + + + + )} +
+ } + 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} + /> ); diff --git a/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx b/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx index a11037b6c..623fbbad5 100644 --- a/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx +++ b/src/shipping/components/ShippingZoneRates/ShippingZoneRates.tsx @@ -37,15 +37,15 @@ const useStyles = makeStyles( paddingRight: 24, width: ICONBUTTON_SIZE + theme.spacing(0.5) }, + buttonColumn: { + padding: "4px 0", + width: "62px" + }, nameColumn: { width: "auto" }, valueColumn: { width: "auto" - }, - buttonColumn: { - width: "62px", - padding: "4px 0" } }), { name: "ShippingZoneRates" } diff --git a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx index c390d76e1..952c9c421 100644 --- a/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx +++ b/src/shipping/components/ShippingZoneRatesCreatePage/ShippingZoneRatesCreatePage.tsx @@ -15,7 +15,7 @@ import { validatePrice } from "@saleor/products/utils/validation"; import OrderValue from "@saleor/shipping/components/OrderValue"; import OrderWeight from "@saleor/shipping/components/OrderWeight"; 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 { ShippingMethodTypeEnum } from "@saleor/types/globalTypes"; import React from "react"; @@ -116,7 +116,7 @@ export const ShippingZoneRatesCreatePage: React.FC
- = ({
- = ({ alignTop choices={[ { - disabled: true, label: (
- + = ({ value: ZipCodeInclusion.Exclude }, { + disabled: true, label: (
- + = ({ id, input: { addWarehouses: warehouseDiff.added, + description: submitData.description, name: submitData.name, removeWarehouses: warehouseDiff.removed } diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index a9c60fc1e..0066ea8c4 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -1899,6 +1899,9 @@ exports[`Storyshots Generics / AddressFormatter default 1`] = ` > Elizabeth Vaughn

+

@@ -15851,19 +15854,17 @@ exports[`Storyshots Shipping / ShippingZoneRatesCreatePage page create price 1`] role="radiogroup" >

-
- Exclude postal codes -
-
- Added postal codes will be excluded from using this delivery methods. If none are added all postal codes will be able to use that shipping rate -
-
- - -
+ + + + +
Timmy Macejkovic

+

+ +41 460-907-9374 +

@@ -56149,36 +56130,6 @@ exports[`Storyshots Views / Customers / Address Book default 1`] = `

-
-
- - -
-
@@ -56198,7 +56149,7 @@ exports[`Storyshots Views / Customers / Address Book loading 1`] = `
- ...'s Address Book + 's Address Book
-
-
- - -
-
@@ -58962,6 +58881,128 @@ exports[`Storyshots Views / Customers / Customer details default 1`] = ` +
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
Elizabeth Vaughn

+

@@ -59519,6 +59563,128 @@ exports[`Storyshots Views / Customers / Customer details different addresses 1`]

+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
Elizabeth Vaughn

+

@@ -59607,6 +59776,9 @@ exports[`Storyshots Views / Customers / Customer details different addresses 1`] > Elizabeth Vaughn

+

@@ -60128,6 +60300,128 @@ exports[`Storyshots Views / Customers / Customer details form errors 1`] = `

+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
Elizabeth Vaughn

+

@@ -60701,6 +60998,80 @@ exports[`Storyshots Views / Customers / Customer details loading 1`] = `

+
+
+
+ + Metadata + +
+
+
+
+
+ + ‌ + +
+
+
+
+
+ + Private Metadata + +
+
+
+
+
+ + ‌ + +
+
+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
Elizabeth Vaughn

+

@@ -61778,6 +62274,128 @@ exports[`Storyshots Views / Customers / Customer details never placed order 1`]

+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
Elizabeth Vaughn

+

@@ -62329,6 +62950,128 @@ exports[`Storyshots Views / Customers / Customer details no address at all 1`] =

+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
Elizabeth Vaughn

+

@@ -63418,6 +64286,128 @@ exports[`Storyshots Views / Customers / Customer details no default shipping add

+
+
+
+ + Metadata + +
+
+
+
+ +
+
+
+ + Private Metadata + +
+
+
+
+
Elizabeth Vaughn

+

@@ -201865,7 +202858,7 @@ exports[`Storyshots Views / Shipping / Create shipping zone default 1`] = ` class="MuiFormLabel-root-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-outlined-id" data-shrink="false" > - Shipping rate name + Shipping zone name

+
+
+ +
+ + +
+
- Shipping rate name + Shipping zone name
+
+
+ +
+ + +
+
- Shipping rate name + Shipping zone name
+
+
+ +
+