diff --git a/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx b/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx index 823b8967aec..910abf187a7 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx @@ -13,7 +13,7 @@ import { Banner, } from "@artsy/palette" -import { Formik, FormikHelpers, FormikProps } from "formik" +import { Form, Formik, FormikHelpers, FormikProps } from "formik" import { AddressModalFields } from "Components/Address/AddressModalFields" import { @@ -49,6 +49,10 @@ export const AddressModal: React.FC = ({ const [showDialog, setShowDialog] = useState(false) const shippingContext = useShippingContext() + // Formik context from parent form is available via ShippingContext + // ( or useFormikContext() ) + const formikContext = shippingContext.state.formHelpers + const createSavedAddress = useCreateSavedAddress().submitMutation const deleteSavedAddress = useDeleteSavedAddress().submitMutation const updateSavedAddress = useUpdateSavedAddress().submitMutation @@ -213,78 +217,72 @@ export const AddressModal: React.FC = ({ return ( <> - - {(formik: FormikProps) => ( -
- {createUpdateError && ( - - {createUpdateError} - - )} - - - - - - - - - - {!initialAddress?.isDefault && ( - { - formik.setFieldValue("isDefault", selected) - }} - selected={formik.values?.isDefault} - data-test="setAsDefault" - > - Set as default - - )} - - {modalAction.type === "editUserAddress" && ( - - setShowDialog(true)} - > - - Delete address - - - - )} - - - + + Delete address + + + )} -
+ + +
{showDialog && ( diff --git a/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx b/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx index e65860e01f8..7c111012f46 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx @@ -26,6 +26,7 @@ import { ShippingContextProps } from "Apps/Order/Routes/Shipping2/ShippingContex import { useHandleUserAddressUpdates } from "Apps/Order/Routes/Shipping2/Hooks/useHandleUserAddressUpdates" import { useRouter } from "System/Router/useRouter" import { useOrderTracking } from "Apps/Order/Hooks/useOrderTracking" +import { FormikHelpers } from "formik" const logger = createLogger("Routes/Shipping2/FulfillmentDetails.tsx") @@ -154,6 +155,118 @@ export const FulfillmentDetails: FC = ({ setVerifyAddressNow(false) } + const submitFulfillmentDetailsWithAddressVerificationAndUpdates = async ( + values: FulfillmentValues, + helpers: FormikHelpers + ) => { + // Assume the form environment will handle validation. If the nested + // SavedAddress formik context is handling values, the outer form validation + // will not be triggered (Currently by passing an empty validation schema). + // Alternative: Putting all form values in a single context? + + // Trigger address verification and return early if appropriate + if (shouldVerifyAddressOnSubmit(values)) { + setVerifyAddressNow(true) + return + } + + try { + let fulfillmentMutationValues: CommerceSetShippingInput + let requiresArtsyShippingToDestination: boolean + + shippingContext.actions.setIsPerformingOperation(true) + + if (values.fulfillmentType === FulfillmentType.PICKUP) { + fulfillmentMutationValues = { + id: orderData.internalID, + fulfillmentType: FulfillmentType.PICKUP, + phoneNumber: values.attributes.phoneNumber, + shipping: { + addressLine1: "", + addressLine2: "", + country: "", + name: "", + city: "", + postalCode: "", + region: "", + phoneNumber: "", + }, + } + } else { + requiresArtsyShippingToDestination = shippingContext.orderData.requiresArtsyShippingTo( + values.attributes.country + ) + + const { phoneNumber, ...addressValues } = values.attributes + + // TODO: Ponder, should we be using the same values for the fulfillment mutation + // in all cases, or should we use the result of the user address action if it is + // used? We are assuming the result of the user address action + // will be consistent with the values in the form which seems fair. OTOH the + // result there could include a saved address ID... Seems unumportant for today. + + fulfillmentMutationValues = { + id: orderData.internalID, + fulfillmentType: requiresArtsyShippingToDestination + ? "SHIP_ARTA" + : FulfillmentType.SHIP, + phoneNumber, + shipping: { ...addressValues, phoneNumber: "" }, + } + + if (values.meta.addressVerifiedBy) { + fulfillmentMutationValues.addressVerifiedBy = + values.meta.addressVerifiedBy + } + + switch (values.meta.savedAddressAction?.action) { + case "edit": { + const userAddressID = values.meta.savedAddressAction.addressID + // Do & await the user update address mutation + break + } + case "create": { + // Do & await the user create address mutation + break + } + default: { + // Do nothing, user address is not persisted + // TODO: Where/does it need to be un-saved? Can we combine that logic with + // the delete address mutation logic from SavedAddresses? + } + } + } + const result = await saveFulfillmentDetails.submitMutation({ + variables: { input: fulfillmentMutationValues }, + }) + + const orderOrError = result.commerceSetShipping?.orderOrError + + if (orderOrError?.__typename === "CommerceOrderWithMutationFailure") { + shippingContext.actions.setIsPerformingOperation(false) + + shippingContext.actions.handleExchangeError(orderOrError.error, logger) + return + } + + handleFulfillmentDetailsSaved({ + requiresArtsyShipping: requiresArtsyShippingToDestination, + }) + } catch (error) { + orderTracking.errorMessageViewed({ + error_code: null, + title: "An error occurred", + message: + "Something went wrong. Please try again or contact orders@artsy.net.", + flow: "user selects a shipping option", + }) + + shippingContext.actions.dialog.showErrorDialog() + } finally { + shippingContext.actions.setIsPerformingOperation(false) + } + } + const submitFulfillmentDetails = async ({ performUserAddressUpdates, formValues, @@ -245,17 +358,17 @@ export const FulfillmentDetails: FC = ({ } } - const handleSubmit = values => { - if (shouldVerifyAddressOnSubmit(values)) { - setVerifyAddressNow(true) - return - } else { - return submitFulfillmentDetails({ - performUserAddressUpdates: shippingMode === "new_address", - formValues: values, - }) - } - } + // const handleSubmit = values => { + // if (shouldVerifyAddressOnSubmit(values)) { + // setVerifyAddressNow(true) + // return + // } else { + // return submitFulfillmentDetails({ + // performUserAddressUpdates: shippingMode === "new_address", + // formValues: values, + // }) + // } + // } return ( { expect(screen.getByTestId("AddressForm_country")).toBeEnabled() }) - it("sets shipping on order, saves address on user and advances to payment", async () => { + it.only("sets shipping on order, saves address on user and advances to payment", async () => { const { mockResolveLastOperation } = renderWithRelay( { CommerceOrder: () => ({ ...order, __id: order.id }), @@ -1762,7 +1762,7 @@ describe("Shipping", () => { }) describe("with saved addresses", () => { - it("re-saves an already-saved shipping address on load to refresh shipping quotes without saving address", async () => { + it.only("re-saves an already-saved shipping address on load to refresh shipping quotes without saving address", async () => { const { mockResolveLastOperation } = renderWithRelay( { CommerceOrder: () => BuyOrderWithArtaShippingDetails, @@ -2111,7 +2111,7 @@ describe("Shipping", () => { }) }) - it("selects a different shipping quote and saves it", async () => { + it.only("selects a different shipping quote and saves it", async () => { const { mockResolveLastOperation } = renderWithRelay( { // Simulate the condition with an order with saved shipping quotes diff --git a/src/Apps/Order/Routes/Shipping2/index.tsx b/src/Apps/Order/Routes/Shipping2/index.tsx index 436fc5ce00d..f52d64de128 100644 --- a/src/Apps/Order/Routes/Shipping2/index.tsx +++ b/src/Apps/Order/Routes/Shipping2/index.tsx @@ -25,9 +25,9 @@ import { useSelectFirstShippingQuote } from "Apps/Order/Routes/Shipping2/Hooks/u import { CollapseDetails } from "Apps/Order/Routes/Shipping2/Components/CollapseDetails" export type ShippingStage = + | "advance_on_click" | "fulfillment_details" | "shipping_quotes" - | "advance_on_click" export interface ShippingProps { order: Shipping2_order$data