Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type {
CheckoutStep,
CriticalCheckoutError,
ExpressCheckoutPaymentMethod,
FulfillmentDetailsTab,
UserAddressMode,
Expand All @@ -10,6 +9,7 @@ import {
CheckoutStepState,
} from "Apps/Order2/Routes/Checkout/CheckoutContext/types"
import type { CheckoutErrorBannerProps } from "Apps/Order2/Routes/Checkout/Components/CheckoutErrorBanner"
import type { CheckoutModalError } from "Apps/Order2/Routes/Checkout/Components/CheckoutModal"
import { useBuildInitialSteps } from "Apps/Order2/Routes/Checkout/Hooks/useBuildInitialSteps"
import { useCheckoutTracking } from "Apps/Order2/Routes/Checkout/Hooks/useCheckoutTracking"
import { useRouter } from "System/Hooks/useRouter"
Expand Down Expand Up @@ -75,7 +75,7 @@ export interface Order2CheckoutModel {
isLoading: boolean
/** Order is redirecting to the details page */
expressCheckoutSubmitting: boolean
criticalCheckoutError: CriticalCheckoutError | null
checkoutModalError: CheckoutModalError | null
expressCheckoutPaymentMethods: ExpressCheckoutPaymentMethod[] | null
steps: CheckoutStep[]
activeFulfillmentDetailsTab: FulfillmentDetailsTab | null
Expand Down Expand Up @@ -106,7 +106,7 @@ export interface Order2CheckoutModel {
editPayment: Action<this>
setOfferAmountComplete: Action<this>
editOfferAmount: Action<this>
setCriticalCheckoutError: Action<this, CriticalCheckoutError | null>
setCheckoutModalError: Action<this, CheckoutModalError | null>
setLoadingComplete: Action<this>
setPaymentComplete: Action<this>
setConfirmationToken: Action<
Expand All @@ -133,7 +133,7 @@ export const Order2CheckoutContext: ReturnType<
// Initial state with defaults
isLoading: true,
expressCheckoutSubmitting: false,
criticalCheckoutError: null,
checkoutModalError: null,
expressCheckoutPaymentMethods: null,
activeFulfillmentDetailsTab: null,
confirmationToken: null,
Expand Down Expand Up @@ -190,8 +190,8 @@ export const Order2CheckoutContext: ReturnType<
},
),

setCriticalCheckoutError: action((state, error) => {
state.criticalCheckoutError = error
setCheckoutModalError: action((state, error) => {
state.checkoutModalError = error
}),

setLoadingComplete: action(state => {
Expand Down Expand Up @@ -536,7 +536,7 @@ export const Order2CheckoutContextProvider: React.FC<
// Default values
isLoading: true,
expressCheckoutSubmitting: false,
criticalCheckoutError: null,
checkoutModalError: null,
expressCheckoutPaymentMethods: null,
activeFulfillmentDetailsTab: null,
confirmationToken: null,
Expand Down Expand Up @@ -603,7 +603,7 @@ const initialStateForOrder = (
return {
isLoading: true,
expressCheckoutSubmitting: false,
criticalCheckoutError: null,
checkoutModalError: null,
expressCheckoutPaymentMethods: null,
activeFulfillmentDetailsTab: fulfillmentComplete
? (activeFulfillmentDetailsTab as FulfillmentDetailsTab)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe("Order2CheckoutContext", () => {
expect.arrayContaining([
"isLoading",
"expressCheckoutSubmitting",
"criticalCheckoutError",
"checkoutModalError",
"expressCheckoutPaymentMethods",
"steps",
"activeFulfillmentDetailsTab",
Expand All @@ -156,7 +156,7 @@ describe("Order2CheckoutContext", () => {
"setDeliveryOptionComplete",
"editDeliveryOption",
"editPayment",
"setCriticalCheckoutError",
"setCheckoutModalError",
"setLoadingComplete",
"setPaymentComplete",
"setConfirmationToken",
Expand All @@ -175,7 +175,7 @@ describe("Order2CheckoutContext", () => {
expect(state).toMatchObject({
isLoading: true,
expressCheckoutSubmitting: false,
criticalCheckoutError: null,
checkoutModalError: null,
expressCheckoutPaymentMethods: null,
activeFulfillmentDetailsTab: null,
confirmationToken: null,
Expand Down
6 changes: 0 additions & 6 deletions src/Apps/Order2/Routes/Checkout/CheckoutContext/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ export type CheckoutStep = {
state: CheckoutStepState
}

export type CriticalCheckoutError =
| "missing_line_item_data"
| "loading_timeout"
| "artwork_version_mismatch"
| "artwork_not_for_sale"

export type ExpressCheckoutPaymentMethod = "applePay" | "googlePay"

export type FulfillmentDetailsTab = "DELIVERY" | "PICKUP"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,73 @@
import { Button, Flex, ModalDialog, Text } from "@artsy/palette"
import { useCheckoutContext } from "Apps/Order2/Routes/Checkout/Hooks/useCheckoutContext"
import { RouterLink } from "System/Components/RouterLink"
import { useRouter } from "System/Hooks/useRouter"

// TODO: Tracking
export enum CheckoutModalError {
LOADING_TIMEOUT = "loading_timeout",
ARTWORK_VERSION_MISMATCH = "artwork_version_mismatch",
ARTWORK_NOT_FOR_SALE = "artwork_not_for_sale",
SUBMIT_ERROR = "submit_error",
OTHER_ERROR = "other_error",
}

export const CriticalErrorModal: React.FC<{
error: string | null
export const CheckoutModal: React.FC<{
error: CheckoutModalError | null
}> = ({ error }) => {
const { artworkPath } = useCheckoutContext()
const { artworkPath, setCheckoutModalError } = useCheckoutContext()
const { router } = useRouter()

if (!error) {
return null
}

const sendToArtworkScreen = () => {
router.replace(artworkPath)
}

const handleReload = () => {
window.location.reload()
}

// Determine if reload should be available based on error type
// Determine modal behavior based on error type
let canReload = false
let canDismiss = false

let title = "Checkout Error"
let description: string

switch (error) {
case "loading_timeout":
case CheckoutModalError.LOADING_TIMEOUT:
description = "There was an error loading your checkout."
canReload = true
break
case "artwork_version_mismatch":
case CheckoutModalError.ARTWORK_VERSION_MISMATCH:
title = "Work has been updated"
description =
"Something about the work changed since you started checkout. Please review the work before submitting your order."
break
case "artwork_not_for_sale":
case CheckoutModalError.ARTWORK_NOT_FOR_SALE:
title = "Not available"
description = "Sorry, the work is no longer available."
break
case CheckoutModalError.SUBMIT_ERROR:
title = "An error occurred"
description =
"Something went wrong while submitting your order. Please try again."
canDismiss = true
break
default:
description =
"There was an error with your checkout. Please return to the artwork and try again."
}

const handleClose = () => {
if (canDismiss) {
setCheckoutModalError(null)
} else {
router.replace(artworkPath)
}
}

const primaryButtonText = canDismiss ? "Continue" : "Return to Artwork"

return (
<ModalDialog title={title} width="450px" onClose={sendToArtworkScreen}>
<ModalDialog title={title} width="450px" onClose={handleClose}>
<Text variant="sm" mb={2}>
{description}
</Text>
Expand All @@ -59,11 +77,9 @@ export const CriticalErrorModal: React.FC<{
Reload
</Button>
)}
<RouterLink to={artworkPath}>
<Button variant="primaryBlack" onClick={sendToArtworkScreen}>
Return to Artwork
</Button>
</RouterLink>
<Button variant="primaryBlack" onClick={handleClose}>
{primaryButtonText}
</Button>
</Flex>
</ModalDialog>
)
Expand Down
21 changes: 5 additions & 16 deletions src/Apps/Order2/Routes/Checkout/Components/Order2ReviewStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { Box, Button, Flex, Image, Message, Spacer, Text } from "@artsy/palette"
import { useStripe } from "@stripe/react-stripe-js"
import { useArtworkDimensions } from "Apps/Artwork/useArtworkDimensions"
import { validateAndExtractOrderResponse } from "Apps/Order/Components/ExpressCheckout/Util/mutationHandling"
import { type Dialog, injectDialog } from "Apps/Order/Dialogs"
import {
CheckoutStepName,
CheckoutStepState,
} from "Apps/Order2/Routes/Checkout/CheckoutContext/types"
import { CheckoutModalError } from "Apps/Order2/Routes/Checkout/Components/CheckoutModal"
import { Order2CheckoutPricingBreakdown } from "Apps/Order2/Routes/Checkout/Components/Order2CheckoutPricingBreakdown"
import { useCheckoutContext } from "Apps/Order2/Routes/Checkout/Hooks/useCheckoutContext"
import { useOrder2SubmitOrderMutation } from "Apps/Order2/Routes/Checkout/Mutations/useOrder2SubmitOrderMutation"
Expand All @@ -26,12 +26,10 @@ const logger = createLogger("Order2ReviewStep.tsx")

interface Order2ReviewStepProps {
order: Order2ReviewStep_order$key
dialog: Dialog
}

const Order2ReviewStepComponent: React.FC<Order2ReviewStepProps> = ({
export const Order2ReviewStep: React.FC<Order2ReviewStepProps> = ({
order,
dialog,
}) => {
const orderData = useFragment(FRAGMENT, order)
const isOffer = orderData.mode === "OFFER"
Expand All @@ -47,7 +45,7 @@ const Order2ReviewStepComponent: React.FC<Order2ReviewStepProps> = ({
redirectToOrderDetails,
checkoutTracking,
artworkPath,
setCriticalCheckoutError,
setCheckoutModalError,
} = useCheckoutContext()

const artworkData = extractLineItemMetadata(orderData.lineItems[0]!)
Expand All @@ -67,18 +65,11 @@ const Order2ReviewStepComponent: React.FC<Order2ReviewStepProps> = ({
})

if (error.code === "insufficient_inventory") {
setCriticalCheckoutError("artwork_not_for_sale")
setCheckoutModalError(CheckoutModalError.ARTWORK_NOT_FOR_SALE)
return
}

const title = "An error occurred"
const message =
"Something went wrong while submitting your order. Please try again."

dialog.showErrorDialog({
title: title,
message: message,
})
setCheckoutModalError(CheckoutModalError.SUBMIT_ERROR)
}

const handleClick = async () => {
Expand Down Expand Up @@ -217,8 +208,6 @@ const Order2ReviewStepComponent: React.FC<Order2ReviewStepProps> = ({
)
}

export const Order2ReviewStep = injectDialog(Order2ReviewStepComponent)

const extractLineItemMetadata = (
lineItem: NonNullable<Order2ReviewStep_order$data["lineItems"][number]>,
) => {
Expand Down
Loading