[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/AvailabilityBadges.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { PropsWithChildren } from 'react'
import React, { useState } from 'react'
import { useIntl } from 'react-intl'
import { ProductSummaryContext } from 'vtex.product-summary-context'
import { useCssHandles } from 'vtex.css-handles'

import messages from './messages'
import { useShippingOptionState } from './context'
import PickupModal from './PickupModal'

const CSS_HANDLES = [
  'availabilityBadgeCircle',
  'availabilityPickupButton',
] as const

const BADGE_TYPES = {
  DELIVERY: 'delivery',
  PICKUP: 'pickup-in-point',
} as const

const BADGE_STYLES = {
  AVAILABLE: 'bg-green',
  UNAVAILABLE: 'bg-white ba',
} as const

type Props = {
  isAvailable: boolean
}

const Badge = ({ children, isAvailable }: PropsWithChildren<Props>) => {
  const handle = useCssHandles(CSS_HANDLES)

  return (
    <div className="flex items-center mt2">
      <div
        className={`${handle.availabilityBadgeCircle} br-pill mr3 ${
          isAvailable ? BADGE_STYLES.AVAILABLE : BADGE_STYLES.UNAVAILABLE
        }`}
      />
      <p className="ma0 f6 mid-gray truncate">{children}</p>
    </div>
  )
}

const AvailabilityBadges = () => {
  const handle = useCssHandles(CSS_HANDLES)
  const intl = useIntl()

  const [isPickupModalOpen, setIsPickupModalOpen] = useState(false)
  const { zipcode } = useShippingOptionState()
  const {
    product: { deliveryPromisesBadges },
  } = ProductSummaryContext.useProductSummary()

  if (!zipcode || !deliveryPromisesBadges) {
    return null
  }

  const findBadgeByType = (
    type: (typeof BADGE_TYPES)[keyof typeof BADGE_TYPES]
  ) => deliveryPromisesBadges.find((badge) => badge.typeName === type)

  const deliveryBadge = findBadgeByType(BADGE_TYPES.DELIVERY)
  const pickupBadge = findBadgeByType(BADGE_TYPES.PICKUP)

  const handleContainerEvents = (
    e: React.MouseEvent | React.KeyboardEvent
  ): void => {
    e.preventDefault()
    e.stopPropagation()
  }

  return (
    <div
      onClick={handleContainerEvents}
      onKeyDown={(e) => e.key === 'Enter' && handleContainerEvents(e)}
      role="presentation"
    >
      {deliveryBadge ? (
        <Badge isAvailable>
          {intl.formatMessage(messages.deliveryAvailableBadge)}
        </Badge>
      ) : (
        <Badge isAvailable={false}>
          {intl.formatMessage(messages.deliveryUnavailableBadge)}
        </Badge>
      )}
      {pickupBadge ? (
        <Badge isAvailable>
          {`${intl.formatMessage(messages.pickupAtBadge)} `}
          <button
            onClick={() => setIsPickupModalOpen(true)}
            className={handle.availabilityPickupButton}
          >
            {pickupBadge.pickupName}
          </button>
        </Badge>
      ) : (
        <Badge isAvailable={false}>
          {intl.formatMessage(messages.pickupUnavailableBadge)}
        </Badge>
      )}

      <PickupModal
        isOpen={isPickupModalOpen}
        onClose={() => setIsPickupModalOpen(false)}
      />
    </div>
  )
}

export default AvailabilityBadges

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/store/interfaces.json from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "shipping-option-location-selector": {
    "component": "index"
  },
  "availability-badges": {
    "component": "AvailabilityBadges"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/context/ShippingOptionContext.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ReactNode } from 'react'
import React, { createContext, useContext } from 'react'

import ErrorBoundary from './ErrorBoundary'
import { ShippingOptionProviderCore } from './ShippingOptionProviderCore'
import type { CartItem } from '../components/UnavailableItemsModal'

export type ShippingMethod = 'delivery' | 'pickup-in-point'

export type ZipCodeError = {
  code: string
  message: string
}

export interface State {
  zipcode?: string
  pickups: Pickup[]
  selectedPickup?: Pickup
  geoCoordinates?: number[]
  countryCode?: string
  city?: string
  isLoading: boolean
  shippingOption?: ShippingMethod
  addressLabel?: string
  submitErrorMessage?: ZipCodeError
  areThereUnavailableCartItems: boolean
  unavailableCartItems: CartItem[]
  unavailabilityMessage?: string
}

interface UpdateZipCode {
  type: 'UPDATE_ZIPCODE'
  args: { zipcode: string; reload?: boolean }
}

interface UpdatePickup {
  type: 'UPDATE_PICKUP'
  args: { pickup: Pickup; canUnselect?: boolean }
}

interface SelectDeliveryShippingOption {
  type: 'SELECT_DELIVERY_SHIPPING_OPTION'
}

interface AbortUnavailableItemsAction {
  type: 'ABORT_UNAVAILABLE_ITEMS_ACTION'
}

interface ContinueUnavailableItemsAction {
  type: 'CONTINUE_UNAVAILABLE_ITEMS_ACTION'
}

interface ResetShippingOption {
  type: 'RESET_SHIPPING_OPTION'
}

export type ShippingOptionActions =
  | UpdateZipCode
  | UpdatePickup
  | SelectDeliveryShippingOption
  | AbortUnavailableItemsAction
  | ContinueUnavailableItemsAction
  | ResetShippingOption

const DEFAULT_STATE: State = {
  pickups: [],
  isLoading: true,
  areThereUnavailableCartItems: false,
  unavailableCartItems: [],
}

const ShippingOptionStateContext = createContext<State>(DEFAULT_STATE)
const ShippingOptionDispatchContext = createContext(
  (_: ShippingOptionActions) => {}
)

interface Props {
  children?: ReactNode
}

const ShippingOptionProvider = ({ children }: Props) => {
  return (
    <ErrorBoundary fallback={children}>
      <ShippingOptionProviderCore>{children}</ShippingOptionProviderCore>
    </ErrorBoundary>
  )
}

const useShippingOptionState = () => useContext(ShippingOptionStateContext)

const useShippingOptionDispatch = () =>
  useContext(ShippingOptionDispatchContext)

export {
  ShippingOptionProvider,
  useShippingOptionState,
  useShippingOptionDispatch,
  ShippingOptionStateContext,
  ShippingOptionDispatchContext,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/context/ShippingOptionProviderCore.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ReactNode } from 'react'
import React from 'react'

import {
  ShippingOptionDispatchContext,
  ShippingOptionStateContext,
} from './ShippingOptionContext'
import { useShippingOption } from './useShippingOption'

interface Props {
  children?: ReactNode
}

export const ShippingOptionProviderCore = ({ children }: Props) => {
  const { dispatch, state } = useShippingOption()

  return (
    <ShippingOptionStateContext.Provider value={state}>
      <ShippingOptionDispatchContext.Provider value={dispatch}>
        {children}
      </ShippingOptionDispatchContext.Provider>
    </ShippingOptionStateContext.Provider>
  )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/context/useShippingOption.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable no-restricted-globals */
import { useRuntime, useSSR } from 'vtex.render-runtime'
import { useCallback, useEffect, useState } from 'react'
import { useIntl } from 'react-intl'
import { useOrderItems } from 'vtex.order-items/OrderItems'
import { usePixelEventCallback } from 'vtex.pixel-manager'

import {
  getAddress,
  getCatalogCount,
  getPickups,
  updateOrderForm,
  updateSession,
  getCartProducts,
  removeCartProductsById,
  validateProductAvailability,
  validateProductAvailabilityByPickup,
  validateProductAvailabilityByDelivery,
} from '../client'
import type { CartItem, CartProduct } from '../components/UnavailableItemsModal'
import { getCountryCode, getFacetsData, getOrderFormId } from '../utils/cookie'
import messages from '../messages'
import type {
  ShippingMethod,
  ShippingOptionActions,
  ZipCodeError,
} from './ShippingOptionContext'
import {
  SHIPPING_MODAL_PIXEL_EVENT_ID,
  PRODUCTS_NOT_FOUND_ERROR_CODE,
} from '../constants'

export const useShippingOption = () => {
  const [zipcode, setZipCode] = useState<string>()
  const [isLoading, setIsLoading] = useState(true)
  const [countryCode, setCountryCode] = useState<string>()
  const [submitErrorMessage, setSubmitErrorMessage] = useState<ZipCodeError>()

  const [city, setCity] = useState<string>()
  const [pickups, setPickups] = useState<Pickup[]>([])
  const [selectedPickup, setSelectedPickup] = useState<Pickup>()
  const [geoCoordinates, setGeoCoordinates] = useState<number[]>()
  const [addressLabel, setAddressLabel] = useState<string>()
  const [shippingOption, setShippingOption] = useState<ShippingMethod>()
  const [unavailableCartItems, setUnavailableCartItems] = useState<CartItem[]>(
    []
  )

  const [pendingAddToCartItem, setPendingAddToCartItem] = useState<any>()

  const [unavailabilityMessage, setUnavailabilityMessage] = useState<string>()

  const [
    actionInterruptedByCartValidation,
    setActionInterruptedByCartValidation,
  ] = useState<() => void>()

  const { account } = useRuntime()
  const isSSR = useSSR()
  const intl = useIntl()
  const { addItems } = useOrderItems()

  usePixelEventCallback({
    eventId: SHIPPING_MODAL_PIXEL_EVENT_ID,
    handler: (event: any) => {
      setPendingAddToCartItem(event.data.addToCartInfo)
    },
  })

  const fetchPickups = useCallback(
    async (
      country: string,
      selectedZipcode: string,
      coordinates: number[],
      shippingMethod?: ShippingMethod,
      keepLoading = false
    ) => {
      const responsePickups = await getPickups(
        country,
        selectedZipcode,
        account
      )

      const pickupsFormatted = responsePickups?.items.filter(
        (pickup: Pickup) => pickup.pickupPoint.isActive
      )

      setPickups(pickupsFormatted ?? [])

      if (pickupsFormatted.length === 0) {
        setIsLoading(false)

        return
      }

      const pickupPointId = getFacetsData('pickupPoint')

      // Only auto-select pickup if there's already a shipping method or saved pickup preference
      if (pickupPointId || shippingMethod === 'pickup-in-point') {
        const [defaultPickup] = pickupsFormatted

        const pickup = pickupPointId
          ? pickupsFormatted.find(
              (p: Pickup) => p.pickupPoint.id === pickupPointId
            ) || defaultPickup
          : defaultPickup

        setSelectedPickup(pickup)

        await updateSession(
          country,
          selectedZipcode,
          coordinates,
          pickup,
          shippingMethod
        )
      } else {
        // Don't auto-select pickup - let user choose
        await updateSession(
          country,
          selectedZipcode,
          coordinates,
          undefined,
          shippingMethod
        )
      }

      if (!keepLoading) {
        setIsLoading(false)
      }
    },
    [account]
  )

  useEffect(() => {
    if (isSSR) {
      return
    }

    const segmentZipCode = getFacetsData('zip-code')
    const segmentCountryCode = getCountryCode()
    const segmentShippingOption = getFacetsData('shipping') as ShippingMethod

    setZipCode(segmentZipCode)
    setShippingOption(segmentShippingOption)
    setCountryCode(segmentCountryCode)

    if (segmentZipCode) {
      try {
        getAddress(segmentCountryCode, segmentZipCode, account).then((res) => {
          setCity(res.city)
          setGeoCoordinates(res.geoCoordinates)
          fetchPickups(
            segmentCountryCode,
            segmentZipCode,
            res.geoCoordinates,
            segmentShippingOption
          )
        })
      } catch {
        setIsLoading(false)
      }
    } else {
      setIsLoading(false)
    }
  }, [account, isSSR, fetchPickups])

  const onError = (code: string, message: string) => {
    setSubmitErrorMessage({ code, message })
    setIsLoading(false)

    setTimeout(() => {
      setSubmitErrorMessage(undefined)
    }, 3000)
  }

  const validateCartItems = async (
    validationHandler: (products: string[]) => Promise<any>
  ) => {
    setIsLoading(true)

    try {
      const orderFormId = getOrderFormId()

      const products = await getCartProducts(orderFormId)

      const productIds = products.map((product: CartProduct) => product.id)

      const { unavailableProducts } = await validationHandler(productIds)

      const unavailableItems = products
        .map((product: CartProduct, id: number) => ({
          cartItemIndex: id,
          product,
        }))
        .filter((item: any) =>
          unavailableProducts.some((id: string) => id === item.product.id)
        )

      setUnavailableCartItems(unavailableItems)

      setIsLoading(false)

      return unavailableItems
    } catch {
      setIsLoading(false)
      setUnavailableCartItems([])

      return []
    }
  }

  const resetUnavailableCartItems = async () => {
    setUnavailableCartItems([])
  }

  const removeUnavailableItems = async () => {
    const orderFormId = getOrderFormId()

    await removeCartProductsById(
      orderFormId,
      unavailableCartItems.map((item) => item.cartItemIndex)
    )

    if (actionInterruptedByCartValidation) {
      actionInterruptedByCartValidation()
    }
  }

  const submitZipcode = async (selectedZipcode: string, reload = true) => {
    if (!selectedZipcode) {
      onError(
        'POSTAL_CODE_NOT_FOUND',
        intl.formatMessage(messages.postalCodeInputPlaceHolder)
      )

      return
    }

    if (!countryCode) {
      return
    }

    setIsLoading(true)

    try {
      const { geoCoordinates: coordinates, city: cityName } = await getAddress(
        countryCode,
        selectedZipcode,
        account
      )

      if (coordinates.length === 0) {
        onError(
          'INVALID_POSTAL_CODE',
          intl.formatMessage(messages.postalCodeInputError)
        )

        return
      }

      const { total } = await getCatalogCount(selectedZipcode, coordinates)

      if (total === 0) {
        onError(
          PRODUCTS_NOT_FOUND_ERROR_CODE,
          intl.formatMessage(messages.noPickupsStateDescription, {
            postalCode: ` ${selectedZipcode}`,
          })
        )

        return
      }

      const orderFormId = getOrderFormId()

      if (orderFormId) {
        await updateOrderForm(countryCode, selectedZipcode, orderFormId)
      }

      setCity(cityName)
      setGeoCoordinates(coordinates)
      setZipCode(selectedZipcode)

      await updateSession(
        countryCode,
        selectedZipcode,
        coordinates,
        selectedPickup
        // Removed automatic 'delivery' default - let user choose shipping method
      )

      await fetchPickups(
        countryCode,
        selectedZipcode,
        coordinates,
        shippingOption,
        true
      )
    } catch {
      onError(
        'INVALID_POSTAL_CODE',
        intl.formatMessage(messages.postalCodeInputError)
      )

      return
    }

    setShippingOption(undefined)
    setSelectedPickup(undefined)

    if (!reload) {
      setIsLoading(false)
    }

    if (reload) {
      location.reload()
    }
  }

  const selectPickup = async (pickup: Pickup, canUnselect = true) => {
    if (!countryCode || !zipcode || !geoCoordinates) {
      return
    }

    let shippingMethod = 'pickup-in-point'
    let pickupUpdated = pickup

    if (
      canUnselect &&
      shippingOption === 'pickup-in-point' &&
      pickup.pickupPoint.id === selectedPickup?.pickupPoint.id
    ) {
      shippingMethod = ''
      pickupUpdated = pickups[0]
    }

    setSelectedPickup(pickupUpdated)

    await updateSession(
      countryCode,
      zipcode!,
      geoCoordinates!,
      pickupUpdated,
      shippingMethod
    )

    location.reload()
  }

  const selectDeliveryShippingOption = async () => {
    if (!countryCode || !zipcode || !geoCoordinates) {
      return
    }

    await updateSession(
      countryCode,
      zipcode,
      geoCoordinates,
      selectedPickup,
      'delivery'
    )

    location.reload()
  }

  useEffect(() => {
    setAddressLabel(city ? `${city}, ${zipcode}` : zipcode)
  }, [zipcode, city])

  const dispatch = async (action: ShippingOptionActions) => {
    switch (action.type) {
      case 'UPDATE_ZIPCODE': {
        const { zipcode: zipcodeSelected, reload } = action.args

        const unavailableItems = await validateCartItems(
          async (products: string[]) =>
            validateProductAvailability(
              zipcodeSelected,
              countryCode!,
              products,
              account
            )
        )

        if (unavailableItems.length === 0) {
          submitZipcode(zipcodeSelected, reload)
          break
        }

        setUnavailabilityMessage(
          intl.formatMessage(messages.unavailableItemsModalDescription, {
            addressLabel,
          })
        )

        setActionInterruptedByCartValidation(
          () => () => submitZipcode(zipcodeSelected, reload)
        )

        break
      }

      case 'UPDATE_PICKUP': {
        const { pickup, canUnselect } = action.args

        setUnavailabilityMessage('pickup')

        const unavailableItems = await validateCartItems(
          async (products: string[]) =>
            validateProductAvailabilityByPickup(pickup.pickupPoint.id, products)
        )

        if (unavailableItems.length === 0) {
          selectPickup(pickup, canUnselect)

          if (pendingAddToCartItem) {
            await addItems(
              pendingAddToCartItem.skuItems,
              pendingAddToCartItem.options
            )

            setPendingAddToCartItem(undefined)
          }

          break
        }

        setUnavailabilityMessage(
          intl.formatMessage(
            messages.unavailableItemsModalForPickupDescription,
            {
              pickupLabel: selectedPickup?.pickupPoint.friendlyName,
            }
          )
        )

        setActionInterruptedByCartValidation(() => () => selectPickup(pickup))

        break
      }

      case 'SELECT_DELIVERY_SHIPPING_OPTION': {
        setUnavailabilityMessage('delivery')

        const unavailableItems = await validateCartItems(
          async (products: string[]) =>
            validateProductAvailabilityByDelivery(
              zipcode!,
              countryCode!,
              products,
              account
            )
        )

        if (unavailableItems.length === 0) {
          selectDeliveryShippingOption()

          if (pendingAddToCartItem) {
            await addItems(
              pendingAddToCartItem.skuItems,
              pendingAddToCartItem.options
            )

            setPendingAddToCartItem(undefined)
          }

          break
        }

        setUnavailabilityMessage(
          intl.formatMessage(
            messages.unavailableItemsModalForDeliveryDescription,
            {
              addressLabel,
            }
          )
        )

        setActionInterruptedByCartValidation(
          () => () => selectDeliveryShippingOption()
        )

        break
      }

      case 'ABORT_UNAVAILABLE_ITEMS_ACTION': {
        resetUnavailableCartItems()
        break
      }

      case 'CONTINUE_UNAVAILABLE_ITEMS_ACTION': {
        removeUnavailableItems()
        break
      }

      case 'RESET_SHIPPING_OPTION': {
        if (!countryCode || !zipcode || !geoCoordinates) {
          return
        }

        // Reset shipping option to undefined (no selection)
        await updateSession(
          countryCode,
          zipcode,
          geoCoordinates,
          selectedPickup
          // No shipping option parameter = reset to no selection
        )

        location.reload()

        break
      }

      default:
        break
    }
  }

  const areThereUnavailableCartItems = unavailableCartItems.length > 0

  return {
    dispatch,
    state: {
      zipcode,
      isLoading,
      countryCode,
      submitErrorMessage,
      city,
      pickups,
      selectedPickup,
      geoCoordinates,
      addressLabel,
      shippingOption,
      areThereUnavailableCartItems,
      unavailableCartItems,
      unavailabilityMessage,
    },
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/context/ErrorBoundary.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { ReactNode } from 'react'
import { Component } from 'react'

interface Props {
  children?: ReactNode
  fallback?: ReactNode
}

interface State {
  hasError: boolean
}

class ErrorBoundary extends Component<Props, State> {
  public state: State = {
    hasError: false,
  }

  public static getDerivedStateFromError(_: Error): State {
    // Update state so the next render will show the fallback UI.
    return { hasError: true }
  }

  public render() {
    if (this.state.hasError) {
      return this.props.fallback
    }

    return this.props.children
  }
}

export default ErrorBoundary

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.css-handles.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.css-handles' {
  export const useCssHandles
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.store-drawer.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.store-drawer*' {
  export const Drawer
  export const DrawerHeader
  export const DrawerCloseButton
  export const useDrawer
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.render-runtime.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.render-runtime' {
  export const useSSR
  export const useRuntime
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/gql.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.gql' {
  import type { DocumentNode } from 'graphql'

  const Schema: DocumentNode

  export default Schema
}

declare module '*.graphql' {
  import type { DocumentNode } from 'graphql'

  const schema: DocumentNode

  export default schema
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.order-items.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.order-items'
declare module 'vtex.order-items/*'

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.device-detector.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.device-detector' {
  export const useDevice
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/global.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare interface Pickup {
  distance: number
  pickupPoint: {
    id: string
    friendlyName: string
    address: {
      neighborhood: string
      street: string
      postalCode: string
      city: string
      number: string
      state: string
    }
    isActive: boolean
  }
}

declare type CallToAction = 'popover-button' | 'popover-input' | 'modal'

declare type ShippingSelection = 'only-pickup' | 'delivery-and-pickup'

declare type Mode = 'default' | 'icon'

interface StageContent {
  [key: string]: { title: string; content: React.JSX.Element }
}

declare global {
  interface Window {
    addressLabel?: stirng
    pickupPointLabel?: string
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/css.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  type Styles = {
    [selector: string]: string
  }

  const styles: Styles

  export default styles
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.pixel-manager.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.pixel-manager' {
  export const usePixel
  export const usePixelEventCallback
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.product-summary-context.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.product-summary-context' {
  export const ProductSummaryContext: {
    useProductSummary: () => {
      product: Product
    }
  }
}

type Product = {
  deliveryPromisesBadges?: BadgeItem[]
}

type BadgeItem = {
  typeName: string
  pickupId: string
  pickupName: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.address-form.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.address-form' {
  export const AddressRules
  export const helpers
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/typings/vtex.styleguide.d.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.styleguide' {
  export const IconClose
  export const IconClear
  export const IconArrowBack
  export const Input
  export const Spinner
  export const Button
  export const Link
  export const Modal
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/utils/debounce.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const debounce = (callback: (value?: any) => void, wait: number) => {
  let timeoutId: number

  return (value?: any) => {
    window.clearTimeout(timeoutId)
    timeoutId = window.setTimeout(() => {
      callback(value)
    }, wait)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/utils/cookie.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { SHIPPING_INFO_COOKIE } from '../constants'

export function getCookie(name: string) {
  const value = `; ${document.cookie}`
  const parts = value.split(`; ${name}=`)

  if (parts.length === 2) {
    return parts.pop()?.split(';').shift()
  }

  return undefined
}

export function setCookie(name: string, val: string) {
  const date = new Date()
  const value = val

  date.setTime(date.getTime() + 30 * 60 * 1000)

  document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`
}

export function getFacetsData(facetsDataTarget: string) {
  // __RUNTIME__.segmentToken is not reliable for the facets. It might not be updated. For this reason we must try to get the info from our custom cookie first

  let facets = getCookie(SHIPPING_INFO_COOKIE) ?? ''

  if (!facets) {
    const segment = (window as any)?.__RUNTIME__.segmentToken

    if (!segment) {
      return
    }

    facets = JSON.parse(atob(segment)).facets
  }

  if (!facets) {
    return
  }

  const facetsTarget = facets
    .replace(/:/g, ';')
    .split(';')
    .find((facet: string) => facet.indexOf(facetsDataTarget) > -1)

  if (!facetsTarget) {
    return
  }

  const [, data] = facetsTarget.split('=')

  if (data && data[data.length - 1] === ';') {
    return data.substring(0, data.length - 1)
  }

  return data
}

export function getCountryCode() {
  const segment = (window as any)?.__RUNTIME__.segmentToken

  if (!segment) {
    return
  }

  const { countryCode } = JSON.parse(atob(segment))

  return countryCode
}

export function getOrderFormId() {
  const orderForm = localStorage.getItem('orderform')

  if (!orderForm) {
    return
  }

  return JSON.parse(orderForm || '{}').id
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/PinSlashIcon.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  color?: string
  width?: string
  height?: string
}

const PinSlashIcon = ({
  color = '#000000',
  width = '48',
  height = '48',
}: Props) => {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width={width}
      height={height}
      viewBox="0 0 48 48"
      fill="none"
    >
      <path
        d="M23.7664 15.0459C24.5882 15.071 25.3638 15.2115 26.1675 15.369C25.5598 16.0586 24.7498 16.75 24.1679 17.3744C23.4998 18 22.5172 18.8882 21.8519 19.5489C21.2853 20.2827 21.4643 20 20.9998 20.5C20.2498 21.3064 19.6865 21.8884 19.3295 22.2511C19.0774 22.5 18.7498 22.75 18.6083 22.8981C18.5271 22.9505 18.4095 22.8972 18.3158 22.8772C18.1166 22.6198 18.046 22.3487 18.0083 22.0288C17.8172 20.4071 18.3674 18.4866 19.3789 17.2097C20.5423 15.741 21.9663 15.2557 23.7664 15.0459Z"
        fill={color}
      />
      <path
        d="M22.9762 4.04928C27.2645 3.78994 30.5658 5.19817 34.1785 7.30007C33.5204 8.06057 32.8481 8.80829 32.1616 9.54322C29.1674 7.88762 26.4711 6.80697 22.9727 7.08522C19.1849 7.38647 15.7872 9.06312 13.315 11.9673C10.664 15.0815 9.78313 18.8711 10.1107 22.8784C10.5394 25.3465 11.4575 26.8704 12.4564 29.0893C12.0233 29.5556 11.5917 30.0259 11.1439 30.4782C10.894 30.7305 10.6525 30.9671 10.2895 30.9693C8.63043 29.4154 7.29908 24.8089 7.09848 22.6032C7.07248 22.3226 7.05341 22.0415 7.04128 21.76C7.02914 21.4784 7.02396 21.1968 7.02573 20.915C7.02753 20.6332 7.03626 20.3516 7.05193 20.0702C7.06759 19.7889 7.09019 19.5081 7.11973 19.2278C7.14923 18.9476 7.18564 18.6682 7.22898 18.3898C7.27228 18.1113 7.32243 17.8341 7.37943 17.5581C7.43643 17.2822 7.50019 17.0078 7.57073 16.7349C7.64126 16.4621 7.71848 16.1912 7.80238 15.9222C7.88631 15.6531 7.97681 15.3864 8.07388 15.1218C8.17094 14.8573 8.27448 14.5953 8.38448 14.3358C8.49448 14.0764 8.61081 13.8198 8.73348 13.5661C8.85614 13.3124 8.98499 13.0619 9.12003 12.8145C9.25506 12.5672 9.39611 12.3233 9.54318 12.083C9.69024 11.8426 9.84314 11.606 10.0019 11.3731C10.1606 11.1403 10.325 10.9115 10.4951 10.6868C10.6651 10.4621 10.8406 10.2417 11.0215 10.0256C14.0919 6.35042 18.2676 4.4664 22.9762 4.04928Z"
        fill={color}
      />
      <path
        d="M42.272 3.99805C42.731 4.00972 43.2756 4.16659 43.6143 4.48696C43.8071 4.66932 43.9396 4.93067 43.9401 5.19878C43.9436 6.78453 39.5925 10.5183 38.3288 11.764C39.9499 14.7207 41.0528 17.7971 40.9852 21.222C40.8519 27.966 37.2803 31.7061 33.0792 36.3669C31.3086 38.3312 29.5436 40.3725 27.6339 42.2033C26.5703 43.223 25.5411 44.0075 24.0025 43.9686C23.1874 43.948 22.4522 43.7582 21.7523 43.3328C19.771 42.1286 16.1518 37.6712 14.3493 35.7829C13.3088 36.6658 12.3559 37.7473 11.3857 38.7128C10.2639 39.825 7.266 43.1911 6.13045 43.7814C5.66585 44.0229 5.05825 43.9917 4.56951 43.8308C4.21515 43.7142 4.11426 43.529 3.95439 43.2208C3.93392 42.8709 3.97229 42.5795 4.10468 42.2528C4.33516 41.6839 4.73232 41.1596 5.14395 40.7094C6.88785 38.8024 8.83705 37.0321 10.6664 35.2032L22.0461 23.8124L34.6149 11.2408C36.7735 9.08258 38.8869 6.82123 41.1279 4.75121C41.4847 4.42161 41.8217 4.18775 42.272 3.99805ZM35.9544 14.2136C34.0915 15.9561 31.6334 18.1261 30.0889 20.1033C29.8614 22.2626 29.5366 24.2052 27.741 25.6527C26.3547 26.7702 24.7947 26.9772 23.0718 27.0815C21.1476 28.9074 18.0771 31.6868 16.5238 33.6916C18.1168 35.3133 19.5588 37.0745 21.1535 38.6929C21.8361 39.4254 22.9413 40.919 24.0103 40.9502C24.5814 40.9668 25.1597 40.3768 25.5487 40.003C28.3967 37.2665 35.2719 29.9324 36.8446 26.6032C37.5885 25.0283 37.9521 22.9838 37.9819 21.262C38.0071 19.8052 37.3653 15.5263 36.3291 14.3956C36.2353 14.2932 36.0938 14.2208 35.9544 14.2136Z"
        fill={color}
      />
    </svg>
  )
}

export default PinSlashIcon

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/PinIcon.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'

interface Props {
  filled?: boolean
  width?: number
  height?: number
}

const CSS_HANDLES = ['pinIconPath', 'pinIconSVG'] as const

const PinIcon = ({ filled = true, width = 16, height = 16 }: Props) => {
  const handles = useCssHandles(CSS_HANDLES)

  return filled ? (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      xmlnsXlink="http://www.w3.org/1999/xlink"
      version="1.1"
      width={width}
      height={height}
      viewBox="0 0 256 256"
      xmlSpace="preserve"
      className={handles.pinIconSVG}
    >
      <defs />
      <g
        transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)"
        style={{
          stroke: 'none',
          strokeWidth: 0,
          strokeDasharray: 'none',
          strokeLinecap: 'butt',
          strokeLinejoin: 'miter',
          strokeMiterlimit: 10,
          fill: 'none',
          fillRule: 'nonzero',
          opacity: 1,
        }}
      >
        <path
          style={{
            stroke: 'none',
            strokeWidth: 1,
            strokeDasharray: 'none',
            strokeLinecap: 'butt',
            strokeLinejoin: 'miter',
            strokeMiterlimit: 10,
            fill: '#727273',
            fillRule: 'nonzero',
            opacity: 1,
          }}
          d="M 45 0 C 25.463 0 9.625 15.838 9.625 35.375 c 0 8.722 3.171 16.693 8.404 22.861 L 45 90 l 26.97 -31.765 c 5.233 -6.167 8.404 -14.139 8.404 -22.861 C 80.375 15.838 64.537 0 45 0 z M 45 48.705 c -8.035 0 -14.548 -6.513 -14.548 -14.548 c 0 -8.035 6.513 -14.548 14.548 -14.548 s 14.548 6.513 14.548 14.548 C 59.548 42.192 53.035 48.705 45 48.705 z"
          transform=" matrix(1 0 0 1 0 0) "
          strokeLinecap="round"
          className={handles.pinIconPath}
        />
      </g>
    </svg>
  ) : (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width={width}
      height={height}
      viewBox="0 0 25 25"
      fill="none"
      className={handles.pinIconSVG}
    >
      <path
        d="M12.7082 1.04199C13.8985 1.08233 15.0692 1.3569 16.1533 1.85001C17.2375 2.34312 18.2138 3.04509 19.0264 3.91578C19.8391 4.78648 20.4721 5.80882 20.8894 6.92435C21.3066 8.03988 21.4999 9.22671 21.4582 10.417C21.4582 15.6253 14.5832 23.9587 12.4998 23.9587C10.4165 23.9587 3.5415 15.417 3.5415 10.417C3.55147 7.96947 4.51399 5.62199 6.22511 3.87198C7.93623 2.12198 10.2615 1.10695 12.7082 1.04199ZM12.4998 13.542C12.9931 13.5448 13.4819 13.4496 13.9382 13.2622C14.3944 13.0747 14.8089 12.7986 15.1576 12.4498C15.5064 12.101 15.7825 11.6865 15.97 11.2303C16.1575 10.7741 16.2526 10.2852 16.2498 9.79199C16.2319 8.83834 15.859 7.92565 15.2041 7.23221C14.5492 6.53877 13.6592 6.1144 12.7082 6.04199C12.1992 6.01665 11.6903 6.09474 11.2124 6.27155C10.7344 6.44836 10.2972 6.72022 9.92727 7.07071C9.5573 7.4212 9.26223 7.84305 9.05986 8.31077C8.85749 8.77849 8.75202 9.28237 8.74984 9.79199C8.81348 10.7656 9.229 11.6829 9.91895 12.3729C10.6089 13.0628 11.5262 13.4783 12.4998 13.542Z"
        stroke="#505050"
        strokeWidth="1.5"
        strokeMiterlimit="10"
        className={handles.pinIconPath}
      />
    </svg>
  )
}

export default PinIcon

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/PostalCodeHelpLink.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useState } from 'react'
import { useIntl } from 'react-intl'
import { AddressRules, helpers } from 'vtex.address-form'
import { Link } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import messages from '../messages'
import { getCountryCode } from '../utils/cookie'

const POSTAL_CODE_FIELD = 'postalCode'

const CSS_HANDLES = ['postalCodeHelpLink'] as const

interface Props {
  rules?: any
}

const HelpLink = helpers.injectRules(({ rules }: Props) => {
  const intl = useIntl()
  const handles = useCssHandles(CSS_HANDLES)

  let postalCodeFinderURL

  if (rules) {
    const postalCodeField = rules.fields?.find(
      (field: any) => field.name === POSTAL_CODE_FIELD
    )

    postalCodeFinderURL = postalCodeField?.forgottenURL
  }

  if (!postalCodeFinderURL) {
    return null
  }

  return (
    <Link
      href={postalCodeFinderURL}
      target="_blank"
      className={`${handles.postalCodeHelpLink}`}
    >
      {intl.formatMessage(messages.popoverPostalCodeLink)}
    </Link>
  )
})

const PostalCodeHelpLink = () => {
  const [countryCode, setCountryCode] = useState('')

  useEffect(() => {
    setCountryCode(getCountryCode())
  }, [])

  return (
    <AddressRules country={countryCode} shouldUseIOFetching>
      <HelpLink />
    </AddressRules>
  )
}

export default PostalCodeHelpLink

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/PostalCodeInput.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { Input, IconClear } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import '../styles.css'

const CSS_HANDLES = [
  'postalCodeInputClearButton',
  'postalCodeInputContainer',
] as const

interface Props {
  onSubmit: (zipcode: string) => void
  onChange: (zipcode: string) => void
  zipcode: string
  errorMessage?: string
  showClearButton?: boolean
  placeholder?: string
}

const postalCodeInputClearButton = {
  backgroundColor: 'unset',
  width: '32px',
  height: '32px',
}

const PostalCodeInput = ({
  zipcode,
  errorMessage,
  onSubmit,
  onChange,
  showClearButton = false,
  placeholder,
}: Props) => {
  const handles = useCssHandles(CSS_HANDLES)

  return (
    <div className={`w-100 ${handles.postalCodeInputContainer}`}>
      <Input
        autFocus
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          onChange(e.target.value.replace(/[^0-9]/g, ''))
        }
        onKeyDown={(e: { key: string }) => {
          if (e.key === 'Enter') {
            onSubmit(zipcode ?? '')
          }
        }}
        value={zipcode}
        errorMessage={errorMessage}
        placeholder={placeholder}
        suffix={
          showClearButton ? (
            <button
              style={postalCodeInputClearButton}
              className={`bn pointer flex justify-center items-center pa3 ${handles.postalCodeInputClearButton}`}
              onClick={() => {
                onChange('')
              }}
            >
              <IconClear />
            </button>
          ) : null
        }
      />
    </div>
  )
}

export default PostalCodeInput

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/PickupModal.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { useIntl } from 'react-intl'

import Modal from './Modal'
import messages from '../messages'
import PickupSelection from './PickupSelection'

interface Props {
  isOpen: boolean
  onClose: () => void
  pickupProps: React.ComponentProps<typeof PickupSelection>
}

const PickupModal = ({ isOpen, onClose, pickupProps }: Props) => {
  const intl = useIntl()

  const {
    onSelectPickup,
    onSubmit,
    pickups,
    inputErrorMessage,
    selectedPickup,
    selectedZipcode,
    isLoading,
  } = pickupProps

  return (
    <Modal
      showArrowBack={false}
      isTopCloseButton={false}
      title={intl.formatMessage(messages.deliverToButtonPlaceholder)}
      isOpen={isOpen}
      onClose={onClose}
    >
      <PickupSelection
        onSelectPickup={onSelectPickup}
        onSubmit={onSubmit}
        pickups={pickups}
        inputErrorMessage={inputErrorMessage}
        selectedPickup={selectedPickup}
        selectedZipcode={selectedZipcode}
        isLoading={isLoading}
      />
    </Modal>
  )
}

export default PickupModal

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/ShippingOptionButton.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useRef } from 'react'
import { useCssHandles } from 'vtex.css-handles'
import { Spinner } from 'vtex.styleguide'
import { usePopoverStore } from '@ariakit/react'

import DeliveryPopover from './DeliveryPopover'

const CSS_HANDLES = [
  'buttonWrapper',
  'buttonLabel',
  'buttonValue',
  'shippingButtonContainer',
] as const

interface ShippingOptionButtonProps {
  onClick: () => void
  loading: boolean
  placeholder: string
  value?: React.ReactNode
  selectedZipcode?: string
  onSubmit?: (zipCode: string) => void
  inputErrorMessage?: string
  callToAction?: CallToAction
  mode: Mode
  icon: React.ReactNode
  showLocationDetectorButton?: boolean
}

const ShippingOptionButton = ({
  onClick,
  loading,
  value,
  placeholder,
  selectedZipcode,
  onSubmit,
  inputErrorMessage,
  callToAction,
  mode,
  icon,
  showLocationDetectorButton,
}: ShippingOptionButtonProps) => {
  const handles = useCssHandles(CSS_HANDLES)
  const popoverStore = usePopoverStore({ defaultOpen: false })
  const anchorRef = useRef(null)

  const popoverOverlay =
    callToAction === 'popover-button' || callToAction === 'popover-input'
      ? callToAction
      : undefined

  useEffect(() => {
    if (anchorRef.current) {
      popoverStore.setAnchorElement(anchorRef.current)
    }
  }, [popoverStore])

  return (
    <div
      className={`${handles.shippingButtonContainer} flex items-center h-100`}
    >
      <button
        ref={anchorRef}
        onClick={onClick}
        className={`${handles.buttonWrapper} flex items-center br3 pt4 pr4 pb4 pl0 b--none`}
      >
        {loading ? (
          <div className="ml4">
            <Spinner size={14} />
          </div>
        ) : mode === 'default' ? (
          <p className={`${handles.buttonValue} ma0 f6 fw6 c-action-primary`}>
            {value ?? placeholder}
          </p>
        ) : (
          icon
        )}
      </button>
      {popoverOverlay && (
        <DeliveryPopover
          onClick={onClick}
          onSubmit={onSubmit ?? (() => {})}
          isLoading={loading}
          inputErrorMessage={inputErrorMessage}
          selectedZipcode={selectedZipcode}
          variant={popoverOverlay}
          popoverStore={popoverStore}
          showLocationDetectorButton={showLocationDetectorButton}
        />
      )}
    </div>
  )
}

export default ShippingOptionButton

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/LocationDetectorButton.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useState } from 'react'
import { useRuntime } from 'vtex.render-runtime'
import { useIntl } from 'react-intl'
import { useCssHandles } from 'vtex.css-handles'

import EmptyState from './EmptyState'
import PinIcon from './PinIcon'
import messages from '../messages'

const CSS_HANDLES = [
  'locationDetectorButton',
  'locationDetectorButtonContainer',
  'locationDetectorButtonIcon',
] as const

const getGeolocation = async (): Promise<any> => {
  if (!navigator?.geolocation) {
    throw new Error('Geolocation not supported')
  }

  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(resolve, reject)
  })
}

const LocationDetectorButton: React.FC = () => {
  const [regionId, setRegionId] = useState<string>('')
  const [error, setError] = useState<boolean>(false)
  const handles = useCssHandles(CSS_HANDLES)

  const {
    culture: { country },
    route: { path, queryString },
  } = useRuntime()

  const intl = useIntl()

  useEffect(() => {
    const getCurrentPosition = async (): Promise<void> => {
      try {
        const position = await getGeolocation()
        const { latitude, longitude } = position.coords

        const response = await fetch(
          `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}`
        )

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }

        const data = await response.json()
        const postcode = data.address?.postcode || data.address?.postal_code

        if (postcode) {
          const cep = postcode.replace(/-/g, '')
          const newRegionId = btoa(`vtex:${country}:${cep}`)

          setRegionId(newRegionId)
          setError(false)
        }
      } catch (err) {
        setError(true)
      }
    }

    if (regionId || !navigator?.geolocation) return

    getCurrentPosition()
  }, [country, regionId])

  if (queryString?.region_id) {
    return null
  }

  if (!regionId || error) {
    return (
      <div className={`${handles.locationDetectorButtonContainer}`}>
        <EmptyState
          description={intl.formatMessage(
            error
              ? messages.LocationDetectorButtonErrorDescription
              : messages.LocationDetectorButtonLoadingDescription
          )}
          variant="secondary"
          iconProps={{ useIcon: !!error, width: '20', height: '20' }}
        />
      </div>
    )
  }

  const separator = Object.keys(queryString).length === 0 ? '?' : '&'
  const href = `${path}${separator}region_id=${regionId}`

  return (
    <a
      href={href}
      className={`${handles.locationDetectorButton} no-underline flex items-center c-link hover-c-link`}
    >
      <span className={handles.locationDetectorButtonIcon}>
        <PinIcon filled={false} />
      </span>
      {intl.formatMessage(messages.LocationDetectorButtonTitle)}
    </a>
  )
}

export default LocationDetectorButton

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/DeliveryPopover.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { useEffect, useState } from 'react'
import type { PopoverStore } from '@ariakit/react'
import { Popover, PopoverArrow } from '@ariakit/react'
import { useIntl } from 'react-intl'
import { Button } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import PostalCodeInput from './PostalCodeInput'
import messages from '../messages'
import PostalCodeHelpLink from './PostalCodeHelpLink'
import LocationDetectorButton from './LocationDetectorButton'

const CSS_HANDLES = [
  'deliveryPopover',
  'deliveryPopoverText',
  'deliveryPopoverArrow',
  'popoverInputContainer',
] as const

interface DeliveryPopoverProps {
  onClick: () => void
  variant?: 'popover-button' | 'popover-input'
  onSubmit: (zipcode: string) => void
  isLoading?: boolean
  inputErrorMessage?: string
  popoverStore: PopoverStore
  selectedZipcode?: string
  showLocationDetectorButton?: boolean
}

const DeliveryPopover = ({
  onClick,
  variant = 'popover-input',
  onSubmit,
  isLoading,
  inputErrorMessage,
  popoverStore,
  selectedZipcode,
  showLocationDetectorButton,
}: DeliveryPopoverProps) => {
  const [zipcode, setZipcode] = useState<string>('')
  const [alreadyOpen, setAlreadyOpen] = useState<boolean>(false)
  const handles = useCssHandles(CSS_HANDLES)
  const intl = useIntl()

  const isFirstLoading = !zipcode && isLoading

  const openPopover = !isFirstLoading && !selectedZipcode && !alreadyOpen

  useEffect(() => {
    if (openPopover) {
      popoverStore.setOpen(true)
      setAlreadyOpen(true)
    }
  }, [openPopover, popoverStore])

  const handlePopoverClick = () => {
    onClick()
    popoverStore.setOpen(false)
  }

  return (
    <Popover
      className={handles.deliveryPopover}
      hideOnInteractOutside
      autoFocusOnShow={false}
      store={popoverStore}
    >
      <p className={`${handles.deliveryPopoverText} ma0`}>
        {`${intl.formatMessage(messages.popoverDescription)} `}
        <PostalCodeHelpLink />
      </p>

      {variant === 'popover-button' ? (
        <Button onClick={handlePopoverClick}>
          {intl.formatMessage(messages.popoverButtonLabel)}
        </Button>
      ) : (
        <div className={`${handles.popoverInputContainer} flex`}>
          <PostalCodeInput
            onChange={(value: string) => setZipcode(value)}
            zipcode={zipcode}
            onSubmit={onSubmit}
            errorMessage={inputErrorMessage}
            showClearButton={false}
            placeholder={intl.formatMessage(
              messages.popoverPostalCodeInputPlaceHolder
            )}
          />
          <Button isLoading={isLoading} onClick={() => onSubmit(zipcode)}>
            {intl.formatMessage(messages.popoverSubmitButtonLabel)}
          </Button>
        </div>
      )}

      {showLocationDetectorButton && <LocationDetectorButton />}

      <PopoverArrow className="deliveryPopoverArrow" />
    </Popover>
  )
}

export default DeliveryPopover

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/Modal.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { PropsWithChildren } from 'react'
import React from 'react'
import ReactModalComponent from 'react-modal'
import { useCssHandles } from 'vtex.css-handles'
import { useDevice } from 'vtex.device-detector'
import { IconClose, IconArrowBack } from 'vtex.styleguide'

// Work around type issues with react-modal in React 16
const ReactModal = ReactModalComponent as any

interface Props {
  onClose: () => void
  isOpen: boolean
  title: string
  showArrowBack: boolean
  isTopCloseButton: boolean
  nonDismissible?: boolean
  nonDismissibleModal?: boolean // Add support for the existing prop name
  onArrowBack?: () => void
}

const customStyles = {
  content: {
    top: '50%',
    left: '50%',
    right: 'auto',
    bottom: 'auto',
    marginRight: '-50%',
    padding: '40px 40px 64px 40px',
    transform: 'translate(-50%, -50%)',
    borderRadius: '12px',
    minHeight: '388px',
    maxHeight: '640px',
    width: 'unset',
    minWidth: 'unset',
    display: 'flex',
    'flex-direction': 'column',
  },
  overlay: {
    backgroundColor: 'rgba(3, 4, 78, 0.55)',
    zIndex: '99999',
    width: '100vw',
    height: '100vh',
  },
}

const CSS_HANDLES = [
  'modalBackButton',
  'modalCloseButton',
  'shippingMethodModalTitle',
]

const headerActionButtonStyle: React.CSSProperties = {
  cursor: 'pointer',
  backgroundColor: 'unset',
  border: 0,
}

const Modal = ({
  children,
  onClose,
  isOpen,
  title,
  onArrowBack,
  showArrowBack,
  isTopCloseButton,
  nonDismissible = false,
  nonDismissibleModal = false,
}: PropsWithChildren<Props>) => {
  const handles = useCssHandles(CSS_HANDLES)
  const { isMobile } = useDevice()

  // Support both prop names for backward compatibility
  const isNonDismissible = nonDismissible || nonDismissibleModal

  if (isMobile) {
    customStyles.content.width = '100%'
  } else {
    customStyles.content.width = '522px'
    customStyles.content.minWidth = '522px'
  }

  if (isNonDismissible) {
    customStyles.content.padding = '64px 40px'
  } else {
    customStyles.content.padding = '40px 40px 64px 40px'
  }

  return (
    <ReactModal
      style={customStyles}
      isOpen={isOpen}
      onRequestClose={isNonDismissible ? undefined : onClose}
      shouldCloseOnOverlayClick={!isNonDismissible}
      shouldCloseOnEsc={!isNonDismissible}
      ariaHideApp={false}
    >
      <>
        <div
          className={`flex justify-between items-center mb3 ${
            isTopCloseButton ? 'flex-column-reverse' : 'flex-row'
          }`}
        >
          <div className="flex items-center self-start flex-row">
            {showArrowBack && onArrowBack && (
              <button
                className={`pa0 mr4 ${handles.modalBackButton}`}
                onClick={onArrowBack}
              >
                <IconArrowBack />
              </button>
            )}
            <p className={`f3 fw6 ma0 ${handles.shippingMethodModalTitle}`}>
              {title}
            </p>
          </div>

          {!isNonDismissible && (
            <div className="flex justify-end self-end">
              <button
                onClick={onClose}
                className={handles.modalCloseButton}
                style={headerActionButtonStyle}
              >
                <IconClose size={24} />
              </button>
            </div>
          )}
        </div>
        {children}
      </>
    </ReactModal>
  )
}

export default Modal

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/EmptyState.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { Button } from 'vtex.styleguide'
import { useCssHandles } from 'vtex.css-handles'

import PinSlashIcon from './PinSlashIcon'

const CSS_HANDLES = ['noPickupsStateContent'] as const

interface Props {
  onClick?: () => void
  title?: string
  description?: string
  buttonLabel?: string
  variant?: 'primary' | 'secondary'
  iconProps?: IconProps
}

interface IconProps {
  width?: string
  height?: string
  useIcon?: boolean
}

const EmptyState = ({
  onClick,
  title,
  description,
  buttonLabel,
  variant = 'primary',
  iconProps = { useIcon: true },
}: Props) => {
  const handle = useCssHandles(CSS_HANDLES)

  return (
    <div className="flex-auto flex flex-column">
      <div
        style={{ margin: 'auto 0' }}
        className={`flex ${title ? 'flex-column' : 'flex-row'} ${
          variant === 'primary' ? 'items-center' : 'items-start'
        } ${handle.noPickupsStateContent}`}
      >
        {iconProps.useIcon && (
          <PinSlashIcon
            color={variant === 'primary' ? '#979899' : '#000000'}
            width={iconProps?.width}
            height={iconProps?.height}
          />
        )}
        {title && <p className="f3 fw6 mb0 mt5">{title}</p>}
        {description && (
          <p className="t-small mt1 ml3 mb0 c-muted-1">{description}</p>
        )}
      </div>
      {onClick && (
        <div className="mt8">
          <Button block onClick={onClick}>
            {buttonLabel}
          </Button>
        </div>
      )}
    </div>
  )
}

export default EmptyState

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/__tests__/AvailabilityBadges.test.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { ProductSummaryContext } from 'vtex.product-summary-context'
import { render } from '@vtex/test-tools/react'
import * as reactIntl from 'react-intl'

import AvailabilityBadges from '../AvailabilityBadges'

const messages = {
  'store/shipping-option-zipcode.availabilityBadge.deliveryAvailable':
    'Delivery available',
  'store/shipping-option-zipcode.availabilityBadge.deliveryUnavailable':
    'Delivery unavailable',
  'store/shipping-option-zipcode.availabilityBadge.pickupAt': 'Pickup at',
  'store/shipping-option-zipcode.availabilityBadge.pickupUnavailable':
    'Pickup unavailable',
} as const

const COMMON_BADGE_STYLE = 'br-pill mr3'
const AVAILABLE_BADGE_STYLE = 'bg-green'
const UNAVAILABLE_BADGE_STYLE = ['bg-white', 'ba']

const mockUseShippingOptionState = jest.fn()
const mockUseProductSummary =
  ProductSummaryContext.useProductSummary as jest.Mock

const mockIntl = {
  formatMessage: ({ id }: { id: string }) =>
    messages[id as keyof typeof messages] || id,
} as reactIntl.IntlShape

jest.mock('../context', () => ({
  useShippingOptionState: () => mockUseShippingOptionState(),
}))

jest.mock('../PickupModal', () => {
  const MockPickupModal = (props: { isOpen: boolean; onClose: () => void }) => (
    <div
      data-testid="pickup-modal"
      onClick={props.onClose}
      onKeyDown={(e) => e.key === 'Enter' && props.onClose()}
      role="button"
      tabIndex={0}
    >
      {props.isOpen ? 'Modal Open' : 'Modal Closed'}
    </div>
  )

  return MockPickupModal
})

describe('AvailabilityBadges', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    mockUseProductSummary.mockReturnValue({
      product: { deliveryPromisesBadges: [] },
    })

    jest.spyOn(reactIntl, 'useIntl').mockImplementation(() => mockIntl)
  })

  describe('Initial render conditions', () => {
    it('renders nothing if zipcode is missing', () => {
      mockUseShippingOptionState.mockReturnValue({ zipcode: null })

      const { container } = render(<AvailabilityBadges />)

      expect(container.firstChild).toBeNull()
    })

    it('renders nothing if deliveryPromisesBadges is null', () => {
      mockUseShippingOptionState.mockReturnValue({ zipcode: '12345' })
      mockUseProductSummary.mockReturnValue({
        product: { deliveryPromisesBadges: null },
      })

      const { container } = render(<AvailabilityBadges />)

      expect(container.firstChild).toBeNull()
    })
  })

  describe('Delivery badge states and styles', () => {
    beforeEach(() => {
      mockUseShippingOptionState.mockReturnValue({ zipcode: '12345' })
    })

    it('renders unavailable badges if deliveryPromisesBadges is empty', () => {
      // Setup mock data
      mockUseProductSummary.mockReturnValue({
        product: { deliveryPromisesBadges: [] },
      })

      // Render component and get text elements
      const { getByText } = render(<AvailabilityBadges />)
      const deliveryText = getByText('Delivery unavailable')
      const pickupText = getByText('Pickup unavailable')

      // Check if text elements are rendered
      expect(deliveryText).toBeInTheDocument()
      expect(pickupText).toBeInTheDocument()

      // Get badge elements
      const deliveryBadge = deliveryText.closest('div')?.querySelector('div')
      const pickupBadge = pickupText.closest('div')?.querySelector('div')

      // Check delivery badge styles (should be unavailable)
      expect(deliveryBadge).toHaveClass(
        COMMON_BADGE_STYLE,
        ...UNAVAILABLE_BADGE_STYLE
      )
      expect(deliveryBadge).not.toHaveClass(AVAILABLE_BADGE_STYLE)

      // Check pickup badge styles (should be unavailable)
      expect(pickupBadge).toHaveClass(
        COMMON_BADGE_STYLE,
        ...UNAVAILABLE_BADGE_STYLE
      )
      expect(pickupBadge).not.toHaveClass(AVAILABLE_BADGE_STYLE)
    })

    it('renders available badges when only delivery is available', () => {
      mockUseProductSummary.mockReturnValue({
        product: {
          deliveryPromisesBadges: [{ typeName: 'delivery' }],
        },
      })

      const { getByText } = render(<AvailabilityBadges />)

      const deliveryText = getByText('Delivery available')
      const pickupText = getByText('Pickup unavailable')

      expect(deliveryText).toBeInTheDocument()
      expect(pickupText).toBeInTheDocument()

      // Get badge elements and check their styles
      const deliveryBadge = deliveryText.closest('div')?.querySelector('div')
      const pickupBadge = pickupText.closest('div')?.querySelector('div')

      // Delivery badge should be styled as available
      expect(deliveryBadge).toHaveClass(
        COMMON_BADGE_STYLE,
        AVAILABLE_BADGE_STYLE
      )
      expect(deliveryBadge).not.toHaveClass(...UNAVAILABLE_BADGE_STYLE)

      // Pickup badge should be styled as unavailable
      expect(pickupBadge).toHaveClass(
        COMMON_BADGE_STYLE,
        ...UNAVAILABLE_BADGE_STYLE
      )
      expect(pickupBadge).not.toHaveClass(AVAILABLE_BADGE_STYLE)
    })

    it('renders both available badges and pickup button', () => {
      mockUseProductSummary.mockReturnValue({
        product: {
          deliveryPromisesBadges: [
            { typeName: 'delivery' },
            { typeName: 'pickup-in-point', pickupName: 'Store 1' },
          ],
        },
      })

      const { getByText } = render(<AvailabilityBadges />)

      const deliveryText = getByText('Delivery available')
      const pickupText = getByText('Pickup at')
      const pickupLocation = getByText('Store 1')

      expect(deliveryText).toBeInTheDocument()
      expect(pickupText).toBeInTheDocument()
      expect(pickupLocation).toBeInTheDocument()

      // Get badge elements and check their styles
      const deliveryBadge = deliveryText.closest('div')?.querySelector('div')
      const pickupBadge = pickupText.closest('div')?.querySelector('div')

      // Both badges should be styled as available
      expect(deliveryBadge).toHaveClass(
        COMMON_BADGE_STYLE,
        AVAILABLE_BADGE_STYLE
      )
      expect(deliveryBadge).not.toHaveClass(...UNAVAILABLE_BADGE_STYLE)

      expect(pickupBadge).toHaveClass(COMMON_BADGE_STYLE, AVAILABLE_BADGE_STYLE)
      expect(pickupBadge).not.toHaveClass(...UNAVAILABLE_BADGE_STYLE)
    })
  })

  describe('Pickup modal behavior', () => {
    beforeEach(() => {
      mockUseShippingOptionState.mockReturnValue({ zipcode: '12345' })
    })

    it('opens pickup modal when clicking on pickup location', () => {
      mockUseProductSummary.mockReturnValue({
        product: {
          deliveryPromisesBadges: [
            { typeName: 'pickup-in-point', pickupName: 'Store 1' },
          ],
        },
      })

      // Render component and get elements
      const { getByText, getByTestId } = render(<AvailabilityBadges />)
      const pickupButton = getByText('Store 1')

      // Trigger click event
      pickupButton.click()

      // Check if modal is open
      expect(getByTestId('pickup-modal')).toHaveTextContent('Modal Open')
    })

    it('closes pickup modal when clicking close button', () => {
      mockUseProductSummary.mockReturnValue({
        product: {
          deliveryPromisesBadges: [
            { typeName: 'pickup-in-point', pickupName: 'Store 1' },
          ],
        },
      })

      // Render component and get elements
      const { getByText, getByTestId } = render(<AvailabilityBadges />)
      const pickupButton = getByText('Store 1')

      // Open modal
      pickupButton.click()
      const modal = getByTestId('pickup-modal')

      // Close modal
      modal.click()

      // Check if modal is closed
      expect(modal).toHaveTextContent('Modal Closed')
    })
  })

  describe('Event handling', () => {
    beforeEach(() => {
      mockUseShippingOptionState.mockReturnValue({ zipcode: '12345' })
    })

    it('stops event propagation when clicking on the container', () => {
      mockUseProductSummary.mockReturnValue({
        product: { deliveryPromisesBadges: [] },
      })

      // Render component and get container element
      const { container } = render(<AvailabilityBadges />)
      const divElement = container.firstChild as HTMLElement

      // Create click event and spy on stopPropagation
      const clickEvent = new MouseEvent('click', { bubbles: true })
      const stopPropagation = jest.spyOn(clickEvent, 'stopPropagation')

      // Trigger click event
      divElement.dispatchEvent(clickEvent)

      // Check if event propagation was stopped
      expect(stopPropagation).toHaveBeenCalled()
    })

    it('allows clicking on pickup button without event propagation', () => {
      // Setup mock data
      mockUseProductSummary.mockReturnValue({
        product: {
          deliveryPromisesBadges: [
            { typeName: 'pickup-in-point', pickupName: 'Store 1' },
          ],
        },
      })

      // Render component and get elements
      const { getByText, getByTestId } = render(<AvailabilityBadges />)
      const pickupButton = getByText('Store 1')

      // Trigger click event
      pickupButton.click()

      // Check if modal was opened
      expect(getByTestId('pickup-modal')).toBeInTheDocument()
    })
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/__tests__/UseShippingOption.test.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { render, fireEvent, waitFor } from '@vtex/test-tools/react'
import * as reactIntl from 'react-intl'

import { useShippingOption } from '../context/useShippingOption'
import * as client from '../client'

const mockIntl = {
  formatMessage: ({ id }: { id: string }) => String(id),
} as unknown as reactIntl.IntlShape

jest.spyOn(reactIntl, 'useIntl').mockImplementation(() => mockIntl)

function ActionRunner({
  actions,
}: {
  actions: Array<{ type: string; args?: unknown }>
}) {
  const { dispatch } = useShippingOption()

  return (
    <button
      data-testid="btn"
      onClick={async () => {
        for (const action of actions) {
          // eslint-disable-next-line no-await-in-loop
          await dispatch(action as never)
        }
      }}
    >
      go
    </button>
  )
}

describe('useShippingOption actions and behavior', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    jest.spyOn(client, 'updateSession').mockResolvedValue(undefined)
    jest.spyOn(client, 'getAddress').mockResolvedValue({
      city: 'City',
      geoCoordinates: [1, 2],
    } as never)

    jest
      .spyOn(client, 'getCatalogCount')
      .mockResolvedValue({ total: 1 } as never)
    jest.spyOn(client, 'updateOrderForm').mockResolvedValue(undefined as never)
    jest.spyOn(client, 'getCartProducts').mockResolvedValue([] as never)
    jest
      .spyOn(client, 'validateProductAvailability')
      .mockResolvedValue({ unavailableProducts: [] } as never)

    const renderRuntime = jest.requireMock('vtex.render-runtime') as {
      useSSR: () => boolean
      useRuntime: () => { account: string }
    }

    jest.spyOn(renderRuntime, 'useSSR').mockReturnValue(false)
    jest
      .spyOn(renderRuntime, 'useRuntime')
      .mockReturnValue({ account: 'store' })

    const cookie = jest.requireMock('../utils/cookie') as {
      getCountryCode: () => string
      getFacetsData: (k: unknown) => unknown
    }

    jest.spyOn(cookie, 'getCountryCode').mockReturnValue('BR')
    jest
      .spyOn(cookie, 'getFacetsData')
      .mockImplementation((key: unknown) =>
        key === 'zip-code' ? '12345-678' : undefined
      )

    const reloadMock = jest.fn()

    Object.defineProperty(window, 'location', {
      configurable: true,
      value: { ...window.location, reload: reloadMock },
    })
  })

  it.each([
    [
      'UPDATE_ZIPCODE + SELECT_DELIVERY_SHIPPING_OPTION triggers reload',
      [
        {
          type: 'UPDATE_ZIPCODE',
          args: { zipcode: '12345-678', reload: true },
        },
        { type: 'SELECT_DELIVERY_SHIPPING_OPTION' },
      ],
    ],
    [
      'UPDATE_ZIPCODE + RESET_SHIPPING_OPTION triggers reload',
      [
        {
          type: 'UPDATE_ZIPCODE',
          args: { zipcode: '12345-678', reload: true },
        },
        { type: 'RESET_SHIPPING_OPTION' },
      ],
    ],
  ])('%s', async (_title, actions) => {
    const { getByTestId } = render(<ActionRunner actions={actions} />)

    fireEvent.click(getByTestId('btn'))

    await waitFor(() => {
      expect(getByTestId('btn')).toBeTruthy()
    })
  })

  it('UPDATE_PICKUP sets selected pickup via explicit action', async () => {
    const actions = [
      { type: 'UPDATE_ZIPCODE', args: { zipcode: '12345-678', reload: false } },
      {
        type: 'UPDATE_PICKUP',
        args: {
          pickup: {
            pickupPoint: {
              isActive: true,
              id: 'p1',
              friendlyName: 'Store 1',
            },
          },
        },
      },
    ]

    const { getByTestId } = render(<ActionRunner actions={actions} />)

    fireEvent.click(getByTestId('btn'))

    await waitFor(() => {
      expect(getByTestId('btn')).toBeTruthy()
    })
  })

  it('smoke: RESET_SHIPPING_OPTION executes without errors', async () => {
    function ResetComponent() {
      const { dispatch } = useShippingOption()

      return (
        <button
          data-testid="reset"
          onClick={() => dispatch({ type: 'RESET_SHIPPING_OPTION' } as never)}
        >
          Reset
        </button>
      )
    }

    const { getByTestId } = render(<ResetComponent />)

    fireEvent.click(getByTestId('reset'))

    await waitFor(() => {
      expect(getByTestId('reset')).toBeTruthy()
    })
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/__tests__/LocationDetectorButton.test.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { render, waitFor } from '@vtex/test-tools/react'
import * as reactIntl from 'react-intl'

import LocationDetectorButton from '../components/LocationDetectorButton'

const messages = {
  'store/shipping-option-zipcode.LocationDetectorButton.title':
    'Use my location',
  'store/shipping-option-zipcode.LocationDetectorButtonLoading.description':
    'Detecting location...',
  'store/shipping-option-zipcode.LocationDetectorButtonError.description':
    'Location detection failed',
} as const

// Mock geolocation
const mockGeolocation = {
  getCurrentPosition: jest.fn(),
  watchPosition: jest.fn(),
  clearWatch: jest.fn(),
}

// Mock fetch
const mockFetch = jest.fn()

// Mock useRuntime
const mockUseRuntime = jest.fn()

// Mock useCssHandles
const mockUseCssHandles = jest.fn()

// Mock intl
const mockIntl = {
  formatMessage: ({ id }: { id: string }) =>
    messages[id as keyof typeof messages] || id,
} as reactIntl.IntlShape

// Mock modules
jest.mock('vtex.render-runtime', () => ({
  useRuntime: () => mockUseRuntime(),
}))

jest.mock('vtex.css-handles', () => ({
  useCssHandles: () => mockUseCssHandles(),
}))

jest.mock('../components/EmptyState', () => {
  const MockEmptyState = ({ description }: { description: string }) => (
    <div data-testid="empty-state">{description}</div>
  )

  return MockEmptyState
})

jest.mock('../components/PinIcon', () => {
  const MockPinIcon = ({ filled }: { filled: boolean }) => (
    <span
      data-testid="pin-icon"
      data-filled={filled}
      role="img"
      aria-label="pin icon"
    />
  )

  return MockPinIcon
})

describe('LocationDetectorButton', () => {
  // Shared test data
  const mockCoordinates = { latitude: -23.5505, longitude: -46.6333 }
  const mockPostcode = '01310-100'

  // Helper functions
  const mockSuccessfulGeolocation = () => {
    mockGeolocation.getCurrentPosition.mockImplementation((success) => {
      success({ coords: mockCoordinates })
    })
  }

  const mockGeolocationError = (errorMessage = 'Geolocation denied') => {
    mockGeolocation.getCurrentPosition.mockImplementation((_, error) => {
      error(new Error(errorMessage))
    })
  }

  const mockSuccessfulFetch = (postcode = mockPostcode) => {
    mockFetch.mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ address: { postcode } }),
    })
  }

  const mockFailedFetch = (status = 500) => {
    mockFetch.mockResolvedValue({ ok: false, status })
  }

  // Helper functions that return render results for simpler usage
  const renderWithSuccessfulLocationDetection = () => {
    mockSuccessfulGeolocation()
    mockSuccessfulFetch()

    return render(<LocationDetectorButton />)
  }

  const renderWithGeolocationError = () => {
    mockGeolocationError()

    return render(<LocationDetectorButton />)
  }

  const renderWithAPIError = () => {
    mockSuccessfulGeolocation()
    mockFailedFetch()

    return render(<LocationDetectorButton />)
  }

  beforeEach(() => {
    jest.clearAllMocks()

    // Setup default mocks
    Object.defineProperty(global, 'navigator', {
      value: { geolocation: mockGeolocation },
      writable: true,
    })

    global.fetch = mockFetch

    mockUseRuntime.mockReturnValue({
      culture: { country: 'BRA' },
      route: { path: '/products', queryString: {} },
    })

    mockUseCssHandles.mockReturnValue({
      locationDetectorButton: 'locationDetectorButton',
      locationDetectorButtonContainer: 'locationDetectorButtonContainer',
      locationDetectorButtonIcon: 'locationDetectorButtonIcon',
    })

    jest.spyOn(reactIntl, 'useIntl').mockImplementation(() => mockIntl)

    // Mock btoa
    jest.spyOn(global, 'btoa').mockImplementation((str) => `encoded-${str}`)
  })

  afterEach(() => {
    jest.restoreAllMocks()
  })

  describe('Initial render conditions', () => {
    it('renders nothing when region_id already exists in queryString', () => {
      mockUseRuntime.mockReturnValue({
        culture: { country: 'BRA' },
        route: {
          path: '/products',
          queryString: { region_id: 'existing-region' },
        },
      })

      const { container } = render(<LocationDetectorButton />)

      expect(container.firstChild).toBeNull()
    })

    it('renders loading state initially when no regionId is set', () => {
      const { getByTestId, getByText } = render(<LocationDetectorButton />)

      expect(getByTestId('empty-state')).toBeInTheDocument()
      expect(getByText('Detecting location...')).toBeInTheDocument()
    })

    it('renders loading state when geolocation is not supported', () => {
      Object.defineProperty(global, 'navigator', {
        value: {},
        writable: true,
      })

      const { getByTestId } = render(<LocationDetectorButton />)

      expect(getByTestId('empty-state')).toBeInTheDocument()
    })
  })

  describe('Geolocation functionality', () => {
    it('calls geolocation API on mount when available', () => {
      render(<LocationDetectorButton />)

      expect(mockGeolocation.getCurrentPosition).toHaveBeenCalledWith(
        expect.any(Function),
        expect.any(Function)
      )
    })

    it('renders location button after successful geolocation and API call', async () => {
      const { getByText, getByTestId } = renderWithSuccessfulLocationDetection()

      await waitFor(() => {
        expect(getByText('Use my location')).toBeInTheDocument()
        expect(getByTestId('pin-icon')).toBeInTheDocument()
      })
    })

    it('handles geolocation error gracefully', async () => {
      const { getByTestId, getByText } = renderWithGeolocationError()

      await waitFor(() => {
        expect(getByTestId('empty-state')).toBeInTheDocument()
        expect(getByText('Location detection failed')).toBeInTheDocument()
      })
    })

    it('handles API error gracefully', async () => {
      const { getByTestId, getByText } = renderWithAPIError()

      await waitFor(() => {
        expect(getByTestId('empty-state')).toBeInTheDocument()
        expect(getByText('Location detection failed')).toBeInTheDocument()
      })
    })

    it('shows error state with icon when location detection fails', async () => {
      const { container } = renderWithGeolocationError()

      await waitFor(() => {
        const emptyState = container.querySelector(
          '[data-testid="empty-state"]'
        )

        expect(emptyState).toBeInTheDocument()
      })
    })

    it('maintains error state after geolocation failure', async () => {
      const { getByText } = renderWithGeolocationError()

      await waitFor(() => {
        expect(getByText('Location detection failed')).toBeInTheDocument()
      })

      // Error state should persist since the component doesn't automatically retry
      expect(getByText('Location detection failed')).toBeInTheDocument()
    })
  })

  describe('Link generation and navigation', () => {
    // All tests in this group use successful location detection
    beforeEach(() => {
      mockSuccessfulGeolocation()
      mockSuccessfulFetch()
    })

    it('generates correct href with ? separator for empty queryString', async () => {
      mockUseRuntime.mockReturnValue({
        culture: { country: 'BRA' },
        route: { path: '/products', queryString: {} },
      })

      const { getByRole } = render(<LocationDetectorButton />)

      await waitFor(() => {
        const link = getByRole('link')

        expect(link).toHaveAttribute(
          'href',
          '/products?region_id=encoded-vtex:BRA:01310100'
        )
      })
    })

    it('generates correct href with & separator for existing queryString', async () => {
      mockUseRuntime.mockReturnValue({
        culture: { country: 'BRA' },
        route: { path: '/products', queryString: { category: 'electronics' } },
      })

      const { getByRole } = render(<LocationDetectorButton />)

      await waitFor(() => {
        const link = getByRole('link')

        expect(link).toHaveAttribute(
          'href',
          '/products&region_id=encoded-vtex:BRA:01310100'
        )
      })
    })

    it('handles postal codes with hyphens correctly', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: () =>
          Promise.resolve({
            address: { postcode: '01310-100' },
          }),
      })

      const { getByRole } = render(<LocationDetectorButton />)

      await waitFor(() => {
        const link = getByRole('link')

        // Should remove hyphens from postal code
        expect(link).toHaveAttribute(
          'href',
          '/products?region_id=encoded-vtex:BRA:01310100'
        )
      })
    })

    it('handles alternative postal_code field from API', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: () =>
          Promise.resolve({
            address: { postal_code: '90210' },
          }),
      })

      const { getByRole } = render(<LocationDetectorButton />)

      await waitFor(() => {
        const link = getByRole('link')

        expect(link).toHaveAttribute(
          'href',
          '/products?region_id=encoded-vtex:BRA:90210'
        )
      })
    })
  })

  describe('CSS handles and styling', () => {
    it('applies CSS handles correctly', async () => {
      const { container } = renderWithSuccessfulLocationDetection()

      // Check loading container handle
      expect(
        container.querySelector('.locationDetectorButtonContainer')
      ).toBeInTheDocument()

      // Check button and icon handles after successful load
      await waitFor(() => {
        expect(
          container.querySelector('.locationDetectorButton')
        ).toBeInTheDocument()
        expect(
          container.querySelector('.locationDetectorButtonIcon')
        ).toBeInTheDocument()
      })
    })
  })

  describe('Component behavior edge cases', () => {
    it('handles missing postcode in API response', async () => {
      mockSuccessfulGeolocation()
      mockFetch.mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ address: {} }), // No postcode
      })

      const { getByTestId } = render(<LocationDetectorButton />)

      await waitFor(() => {
        // Should continue showing loading state when no postcode (not an error)
        expect(getByTestId('empty-state')).toBeInTheDocument()
      })
    })
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/__tests__/Client.test.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { getCatalogCount } from '../client'

describe('client.getCatalogCount', () => {
  beforeEach(() => {
    jest.clearAllMocks()
  })

  it('uses credentials: "omit" and does not attach cookies', async () => {
    const mockFetch = jest
      .fn()
      .mockResolvedValue({ json: () => Promise.resolve({}) })

    ;(global as any).fetch = mockFetch

    await getCatalogCount('12345-678', [0, 0])

    expect(mockFetch).toHaveBeenCalledTimes(1)
    // eslint-disable-next-line prefer-destructuring
    const [, options] = mockFetch.mock.calls[0]

    expect(options).toMatchObject({
      method: 'GET',
      credentials: 'omit',
      headers: { 'Content-Type': 'application/json' },
    })

    // Ensure no Cookie header is forwarded
    expect(options.headers).not.toHaveProperty('Cookie')
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/__tests__/ShippingOptionContext.test.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import { screen, render } from '@vtex/test-tools/react'

import * as useShippingOption from '../context/useShippingOption'
import * as ShippingOptionProviderCore from '../context/ShippingOptionProviderCore'
import { ShippingOptionProvider } from '../context/ShippingOptionContext'

jest.mock('vtex.order-items')
jest.mock('vtex.pixel-manager')

// Note: console.errors are expected in these tests because we are testing error boundaries
// React always logs errors to console even when they are caught by error boundaries
describe('ShippingOptionContext should render its children even when', () => {
  it('useShippingOption throws an error', () => {
    const mock = jest
      .spyOn(useShippingOption, 'useShippingOption')
      .mockImplementation(() => {
        throw new Error('Test error in useShippingOption')
      })

    render(
      <ShippingOptionProvider>
        <span>Children message</span>
      </ShippingOptionProvider>
    )

    mock.mockRestore()

    expect(screen.queryByText('Children message')).toBeInTheDocument()
  })

  it('ShippingOptionContextCore throws an error', () => {
    const mock = jest
      .spyOn(ShippingOptionProviderCore, 'ShippingOptionProviderCore')
      .mockImplementation(() => {
        throw new Error('Test error in ShippingOptionProviderCore')
      })

    render(
      <ShippingOptionProvider>
        <span>Children message</span>
      </ShippingOptionProvider>
    )

    mock.mockRestore()

    expect(screen.queryByText('Children message')).toBeInTheDocument()
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/hooks/useShippingOptions.ts from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable no-restricted-globals */
import { useCallback, useEffect, useState } from 'react'
import { useRuntime, useSSR } from 'vtex.render-runtime'
import { useIntl } from 'react-intl'
import { usePixel } from 'vtex.pixel-manager'

import { getCountryCode, getFacetsData, getOrderFormId } from '../utils/cookie'
import messages from '../messages'
import {
  getAddress,
  getCartProducts,
  getPickups,
  removeCartProductsById,
  updateOrderForm,
  updateSession,
} from '../client'
import type { CartItem, CartProduct } from '../components/UnavailableItemsModal'

declare let window: any

const useShippingOptions = () => {
  const [inputZipCode, setInputZipCode] = useState<string>()
  const [selectedZipCode, setSelectedZipCode] = useState<string | null>()
  const [isLoading, setIsLoading] = useState(true)
  const [isPageLoading, setIsPageLoading] = useState(true)
  const [countryCode, setCountryCode] = useState<string>()
  const [inputErrorMessage, setInputErrorMessage] = useState<string>()
  const [city, setCity] = useState<string>()
  const [pickups, setPickups] = useState<Pickup[]>([])
  const [selectedPickup, setSelectecPickup] = useState<Pickup>()
  const [geoCoordinates, setGeoCoordinates] = useState<number[]>()
  const [shippingOption, setShippingOption] = useState<
    'delivery' | 'pickup-in-point'
  >()

  const [unavailableCartItems, setUnavailableCartItems] = useState<CartItem[]>(
    []
  )

  const isSSR = useSSR()
  const { account } = useRuntime()
  const intl = useIntl()
  const { push } = usePixel()

  const fetchPickups = useCallback(
    async (country: string, zipCode: string, coordinates: number[]) => {
      const responsePickups = await getPickups(country, zipCode, account)

      setPickups(responsePickups.items)

      if (responsePickups?.items?.length === 0) {
        setIsLoading(false)

        return
      }

      const pickupPointId = getFacetsData('pickupPoint')

      if (pickupPointId) {
        const pickup = responsePickups.items.find(
          (p: any) => p.pickupPoint.id === pickupPointId
        )

        setSelectecPickup(pickup)
        await updateSession(
          country,
          zipCode,
          coordinates,
          pickup,
          shippingOption
        )
      }

      setIsLoading(false)
    },
    [account, shippingOption]
  )

  useEffect(() => {
    if (isSSR) {
      return
    }

    const segmentZipCode = getFacetsData('zip-code')
    const segmentCountryCode = getCountryCode()
    const segmentShippingOption = getFacetsData('shipping')

    setSelectedZipCode(segmentZipCode ?? null)
    setInputZipCode(segmentZipCode)
    setCountryCode(segmentCountryCode)
    setShippingOption(segmentShippingOption as 'delivery' | 'pickup-in-point')

    if (segmentZipCode) {
      getAddress(segmentCountryCode, segmentZipCode, account).then((res) => {
        setCity(res.city)
        setGeoCoordinates(res.geoCoordinates)
        fetchPickups(segmentCountryCode, segmentZipCode, res.geoCoordinates)
      })
    } else {
      setIsLoading(false)
    }
  }, [account, isSSR, fetchPickups])

  const onError = (message: string) => {
    setInputErrorMessage(message)
    setIsLoading(false)

    setTimeout(() => {
      setInputErrorMessage(undefined)
    }, 3000)
  }

  const onSelectPickup = async (pickup: Pickup, shouldPersistFacet = true) => {
    setSelectecPickup(pickup)

    if (!countryCode || !selectedZipCode || !geoCoordinates) {
      return
    }

    await updateSession(
      countryCode,
      selectedZipCode,
      geoCoordinates,
      pickup,
      shouldPersistFacet ? 'pickup-in-point' : shippingOption
    )

    location.reload()
  }

  const handleIsLoading = () => {
    setIsLoading(true)
    setIsPageLoading(false)
  }

  const validateCartItems = async () => {
    const orderFormId = getOrderFormId()

    const products = await getCartProducts(orderFormId)

    // IMPORTANT: validate products here
    // Task: TIS-189
    const unavailableItems = products.map(
      (product: CartProduct, id: number) => ({
        cartItemIndex: id,
        product,
      })
    )

    setUnavailableCartItems(unavailableItems)

    return unavailableItems
  }

  const onSubmit = async (reload = true) => {
    if (!countryCode) {
      return false
    }

    if (!inputZipCode) {
      onError(intl.formatMessage(messages.postalCodeInputPlaceHolder))

      return false
    }

    setPickups([])
    setCity(undefined)

    handleIsLoading()

    const orderFormId = getOrderFormId()

    if (orderFormId) {
      await updateOrderForm(countryCode, inputZipCode, orderFormId)
    }

    const { geoCoordinates: coordinates } = await getAddress(
      countryCode,
      inputZipCode,
      account
    )

    if (coordinates.length === 0) {
      onError(intl.formatMessage(messages.postalCodeInputError))

      return false
    }

    const unavailableItems = await validateCartItems()

    if (unavailableItems.length > 0) {
      setIsLoading(false)

      return false
    }

    setGeoCoordinates(coordinates)

    setSelectedZipCode(inputZipCode)

    await updateSession(countryCode, inputZipCode, coordinates)

    await fetchPickups(countryCode, inputZipCode, coordinates)

    if (!reload) {
      setIsLoading(false)
    }

    if (reload) {
      location.reload()
    }

    return true
  }

  const removeUnavailableItems = async () => {
    const orderFormId = getOrderFormId()

    await removeCartProductsById(
      orderFormId,
      unavailableCartItems.map((item) => item.cartItemIndex)
    )

    onSubmit()
  }

  const onChange = (zipCode?: string) => {
    setInputZipCode(zipCode)
  }

  const addressLabel = city ? `${selectedZipCode}, ${city}` : selectedZipCode

  useEffect(() => {
    window.addressLabel = addressLabel
    push({
      id: 'shipping-option-addressLabel',
      label: addressLabel,
    })
  }, [addressLabel, push])

  const pickupPointLabel = selectedPickup
    ? selectedPickup.pickupPoint.friendlyName
    : ''

  useEffect(() => {
    window.pickupPointLabel = pickupPointLabel
    push({
      id: 'shipping-option-pickupPointLabel',
      label: pickupPointLabel,
    })
  }, [pickupPointLabel, push])

  const isFirstLoading = isPageLoading && isLoading

  return {
    zipCode: inputZipCode,
    selectedZipCode,
    isLoading,
    isFirstLoading,
    inputErrorMessage,
    onSubmit,
    onChange,
    addressLabel,
    city,
    pickups,
    selectedPickup,
    onSelectPickup,
    geoCoordinates,
    shippingOption,
    countryCode,
    unavailableCartItems,
    removeUnavailableItems,
  }
}

export default useShippingOptions

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/UnavailableItemsModal/index.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable no-restricted-globals */
import React, { useState } from 'react'
import { Button } from 'vtex.styleguide'
import { useIntl } from 'react-intl'

import Modal from '../Modal'
import ProductItem from './ProductItem'
import messages from '../../messages'

export type CartProduct = { id: string; name: string; imageUrl: string }

export type CartItem = {
  cartItemIndex: number
  product: CartProduct
}

interface Props {
  isOpen: boolean
  onClose: () => void
  onTryAgain: () => void
  onRemoveItems: () => void
  unavailableCartItems: CartItem[]
  unavailabilityMessage?: string
}

const UnavailableItemsModal = ({
  isOpen,
  onClose,
  onTryAgain,
  onRemoveItems,
  unavailableCartItems,
  unavailabilityMessage,
}: Props) => {
  const intl = useIntl()

  const [isLoading, setIsLoading] = useState(false)

  const handleRemoveItemsClick = async () => {
    setIsLoading(true)
    await onRemoveItems()

    onClose()
  }

  return (
    <Modal
      showArrowBack={false}
      isTopCloseButton={false}
      title={intl.formatMessage(messages.unavailableItemsModalTitle)}
      isOpen={isOpen}
      onClose={onClose}
      nonDismissible
    >
      <div className="flex-auto flex flex-column justify-between mt0">
        <p className="mid-gray ma0">{unavailabilityMessage}</p>
        <div className="mv7 overflow-auto">
          {unavailableCartItems.map(({ product }) => (
            <ProductItem
              key={product.id}
              imageUrl={product.imageUrl}
              productName={product.name}
            />
          ))}
        </div>
        <div style={{ gap: '.75rem' }} className="flex flex-column">
          <Button
            className="mb3"
            isLoading={isLoading}
            onClick={handleRemoveItemsClick}
          >
            {intl.formatMessage(messages.unavailableItemsModalRemoveButton)}
          </Button>
          <Button
            isLoading={isLoading}
            variation="secondary"
            onClick={onTryAgain}
          >
            {intl.formatMessage(messages.unavailableItemsModalRetryButton)}
          </Button>
        </div>
      </div>
    </Modal>
  )
}

export default UnavailableItemsModal

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of shipping-option-components/react/components/UnavailableItemsModal/ProductItem.tsx from the shipping-option-components VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'

interface Props {
  imageUrl: string
  productName: string
}

const ProductItem = ({ imageUrl, productName }: Props) => {
  return (
    <div className="flex flex-row pa5 bg-base t-body c-on-base br3 b--muted-4 ba mb5">
      <img
        className="br2"
        style={{ width: '64px', height: '64px' }}
        src={imageUrl}
        alt={productName}
      />
      <p className="ml6">{productName}</p>
    </div>
  )
}

export default ProductItem

```