import {useCallback} from 'react' import {useShopIntent} from '../../internal/useShopIntent' import type { AddToCartProductVariantParams, AddToCartProductVariantResult, IntentResult, } from '@shopify/shop-minis-platform/actions' /** * Params accepted by `useAddToCart().addToCart()`. Alias for the shared * `AddToCartProductVariantParams` intent payload. * * @publicDocs */ export type AddToCartParams = AddToCartProductVariantParams /** * Successful payload returned from `useAddToCart().addToCart()`. Alias for * `AddToCartProductVariantResult`. * * @publicDocs */ export type AddToCartResult = AddToCartProductVariantResult export interface UseAddToCartReturns { /** * Adds a product variant to the user's cart. * * If `productVariantId` is supplied, the variant is added directly. If * only `productId` is supplied, the native variant selector sheet opens * over the WebView and the user picks the variant; the host then adds it * on confirm. * * Resolves with: * - `{code: 'ok', data: {outcome: 'added_to_cart', ...}}` once the * variant has been added. * - `{code: 'ok', data: {outcome: 'navigated_to_product', productId}}` * if the product is a referral (off-catalog) product and the host * sent the user to its PDP instead of adding it to the cart. The * shopper is now on the PDP — the mini should treat this as a * successful hand-off, not a failure. * - `{code: 'closed'}` when the user dismisses the sheet. * - `{code: 'error', message}` if the host add-to-cart fails. * * Always switch on `data.outcome` to distinguish the two `'ok'` shapes. */ addToCart: (params: AddToCartParams) => Promise> } /** * Adds a product variant to the user's cart, optionally prompting the user * to pick a variant first via a native bottom sheet rendered on top of the * mini WebView. * * Behavior: * * - When `productVariantId` is supplied (and `forceShow` is `false`), the * host adds the variant to the cart directly without showing UI. * - When `productVariantId` is omitted and the product has a single * selectable variant, the host short-circuits and adds it directly. The * success payload sets `source: 'auto'`. * - Otherwise the sheet opens, the user picks a variant and quantity, and * the host adds it on confirm. `source: 'user'`. * - For referral (off-catalog) products the host cannot add to its cart; * it navigates the user to the merchant's PDP and resolves with * `{outcome: 'navigated_to_product'}`. `AddToCartButton` also handles * this case at the component level so most callers never observe it. * * Internally this is a typed wrapper around the * `add_to_cart:shopify/ProductVariant` intent. * * @example * ```tsx * const {addToCart} = useAddToCart() * * // direct ATC \u2014 mini knows the variant * await addToCart({productId, productVariantId, quantity: 2}) * * // prompt the user to pick a variant * const result = await addToCart({productId}) * if (result.code === 'ok') { * if (result.data.outcome === 'added_to_cart') { * // result.data.productVariantId, .quantity, .source * } else { * // referral hand-off; user is now on the PDP * // result.data.outcome === 'navigated_to_product' * // result.data.productId * } * } else if (result.code === 'closed') { * // user dismissed without adding * } else { * // result.message * } * ``` * * @publicDocs */ export function useAddToCart(): UseAddToCartReturns { const {invokeQuery} = useShopIntent() const addToCart = useCallback( async (params: AddToCartParams): Promise> => { const result = await invokeQuery({ action: 'add_to_cart', type: 'shopify/ProductVariant', // Input context (productId, productVariantId, ...) lives in `data`. // `value` is omitted: there is no pre-existing cart-line resource // being acted on \u2014 we're creating one. data: params, }) // The bridge returns untyped data; the host handler guarantees the // shape matches AddToCartResult for this action+type. return result as IntentResult }, [invokeQuery] ) return {addToCart} } /** * The `useAddToCart` hook adds a product variant to the user's cart, * optionally opening a native variant selector sheet first when the mini * only knows the product (not the variant). Returns an `IntentResult` so * the mini can distinguish success, user-dismiss, and error. * * @publicDocs */ export type UseAddToCartGeneratedType = () => UseAddToCartReturns