{"version":3,"sources":["../../../src/primitives/Scheduling/BookingFlow.tsx"],"names":[],"mappings":"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","file":"BookingFlow.mjs","sourcesContent":["import {\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n  type FormEvent,\n  type ReactNode,\n} from 'react';\nimport {\n  useScheduling,\n  type UseSchedulingOptions,\n  type UseSchedulingResult,\n} from '../../hooks/useScheduling';\nimport type {\n  SchedulingResource,\n  SchedulingService,\n  SchedulingSlot,\n  SchedulingState,\n} from '../../types/scheduling';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\nconst BookingFlowContext = createContext<UseSchedulingResult | null>(null);\n\nfunction useBookingFlowContext(): UseSchedulingResult {\n  const ctx = useContext(BookingFlowContext);\n  if (!ctx) {\n    throw new Error('<BookingFlow.*> must be rendered inside <BookingFlow>.');\n  }\n  return ctx;\n}\n\n// ─── Root ─────────────────────────────────────────────────────────────────────\n\nexport interface BookingFlowProps extends UseSchedulingOptions {\n  className?: string;\n  children: ReactNode;\n  /**\n   * Optionally pass an externally-managed scheduling result (e.g. a hook\n   * lifted into a parent island so a CTA button can read state too). When\n   * omitted, the root creates its own via useScheduling().\n   */\n  scheduling?: UseSchedulingResult;\n}\n\nfunction BookingFlowRoot({\n  className,\n  children,\n  scheduling: external,\n  ...hookOpts\n}: BookingFlowProps) {\n  // Hooks can't be called conditionally, so the internal hook always runs.\n  // When an external hook is supplied, suppress its initial /services fetch\n  // so we don't fire every catalog request twice.\n  const internal = useScheduling(\n    external ? { skipInitialLoad: true } : hookOpts,\n  );\n  const value = external ?? internal;\n  return (\n    <BookingFlowContext.Provider value={value}>\n      <div className={className}>{children}</div>\n    </BookingFlowContext.Provider>\n  );\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nconst STAGE_ORDER = [\n  'idle',\n  'service_selected',\n  'resource_selected',\n  'slot_selected',\n  'details_filled',\n  'submitting',\n  'confirmed',\n  'error',\n] as const;\n\ntype Stage = (typeof STAGE_ORDER)[number];\n\nfunction stageIndex(stage: Stage): number {\n  const idx = STAGE_ORDER.indexOf(stage);\n  return idx === -1 ? -1 : idx;\n}\n\nfunction reachedAtLeast(state: SchedulingState, stage: Stage): boolean {\n  return stageIndex(state.status as Stage) >= stageIndex(stage);\n}\n\n// ─── Service picker ───────────────────────────────────────────────────────────\n\nexport interface BookingFlowServiceProps {\n  className?: string;\n  /**\n   * Render override. Receives the full list of services and a `pick` handler.\n   * If omitted, the picker renders one button per service.\n   */\n  children?: (\n    services: SchedulingService[],\n    pick: (s: SchedulingService) => void,\n  ) => ReactNode;\n  itemClassName?: string;\n  /** Renderer for a single service tile, used by the default body. */\n  renderItem?: (service: SchedulingService, pick: () => void) => ReactNode;\n  /** Render when `services.length === 0` and not loading. Defaults to `null`. */\n  emptyState?: ReactNode;\n}\n\nfunction ServicePicker({\n  className,\n  children,\n  itemClassName,\n  renderItem,\n  emptyState = null,\n}: BookingFlowServiceProps) {\n  const flow = useBookingFlowContext();\n  // Visible only on idle — once a service is picked the resource picker\n  // takes over. Consumers can render `<BookingFlow.Service />` first in\n  // flow order and let it auto-hide as the state advances.\n  if (flow.state.status !== 'idle') return null;\n\n  if (children) {\n    return (\n      <div className={className}>{children(flow.services, flow.pickService)}</div>\n    );\n  }\n\n  if (flow.services.length === 0) {\n    return <div className={className}>{emptyState}</div>;\n  }\n\n  return (\n    <div className={className}>\n      {flow.services.map((service) => {\n        const pick = () => flow.pickService(service);\n        if (renderItem) return <div key={service.id}>{renderItem(service, pick)}</div>;\n        return (\n          <button\n            key={service.id}\n            type=\"button\"\n            className={itemClassName}\n            onClick={pick}\n          >\n            <span>{service.name}</span>\n            {service.duration_minutes ? (\n              <span>{service.duration_minutes}m</span>\n            ) : null}\n            {typeof service.price_cents === 'number' && service.price_cents > 0 ? (\n              <span>{formatPrice(service.price_cents)}</span>\n            ) : null}\n          </button>\n        );\n      })}\n    </div>\n  );\n}\n\n// ─── Resource picker ──────────────────────────────────────────────────────────\n\nexport interface BookingFlowResourceProps {\n  className?: string;\n  itemClassName?: string;\n  anyResourceLabel?: ReactNode;\n  /** Custom renderer for the whole list. */\n  children?: (\n    resources: SchedulingResource[],\n    pick: (r: SchedulingResource | null) => void,\n  ) => ReactNode;\n  renderItem?: (resource: SchedulingResource, pick: () => void) => ReactNode;\n  emptyState?: ReactNode;\n}\n\nfunction ResourcePicker({\n  className,\n  itemClassName,\n  anyResourceLabel,\n  children,\n  renderItem,\n  emptyState = null,\n}: BookingFlowResourceProps) {\n  const flow = useBookingFlowContext();\n\n  // Visible when the customer just picked a service and we're waiting for\n  // them to pick a resource. Hide once they advance, or if the hook\n  // auto-advanced because there's only one resource.\n  if (flow.state.status !== 'service_selected') return null;\n\n  if (children) {\n    return (\n      <div className={className}>{children(flow.resources, flow.pickResource)}</div>\n    );\n  }\n\n  if (flow.resources.length === 0) {\n    return <div className={className}>{emptyState}</div>;\n  }\n\n  return (\n    <div className={className}>\n      {anyResourceLabel ? (\n        <button\n          type=\"button\"\n          className={itemClassName}\n          onClick={() => flow.pickResource(null)}\n        >\n          {anyResourceLabel}\n        </button>\n      ) : null}\n      {flow.resources.map((resource) => {\n        const pick = () => flow.pickResource(resource);\n        if (renderItem) return <div key={resource.id}>{renderItem(resource, pick)}</div>;\n        return (\n          <button\n            key={resource.id}\n            type=\"button\"\n            className={itemClassName}\n            onClick={pick}\n          >\n            <span>{resource.name}</span>\n            {resource.bio ? <small>{resource.bio}</small> : null}\n          </button>\n        );\n      })}\n    </div>\n  );\n}\n\n// ─── Date/time picker ─────────────────────────────────────────────────────────\n\nexport interface BookingFlowDateTimeProps {\n  className?: string;\n  /** Default: 7 (one week strip from `from`). */\n  windowDays?: number;\n  /** Default: today's UTC midnight. */\n  from?: Date;\n  itemClassName?: string;\n  /** Format a slot for the default button label. */\n  formatSlot?: (slot: SchedulingSlot) => ReactNode;\n  /** Custom whole-list renderer. */\n  children?: (\n    slots: SchedulingSlot[],\n    pick: (slot: SchedulingSlot) => void,\n  ) => ReactNode;\n  emptyState?: ReactNode;\n}\n\nfunction DateTimePicker({\n  className,\n  windowDays = 7,\n  from,\n  itemClassName,\n  formatSlot,\n  children,\n  emptyState = null,\n}: BookingFlowDateTimeProps) {\n  const flow = useBookingFlowContext();\n\n  // Compute window once per (resource_selected | slot_selected) state so we\n  // don't refetch on every render.\n  const range = useMemo(() => {\n    const start = from ?? new Date();\n    start.setUTCHours(0, 0, 0, 0);\n    const end = new Date(start);\n    end.setUTCDate(end.getUTCDate() + windowDays);\n    return { from: start.toISOString(), to: end.toISOString() };\n  }, [from, windowDays]);\n\n  // Refetch availability whenever a fresh resource (or \"any\") is selected,\n  // OR when the customer steps back from slot_selected to resource_selected.\n  // We intentionally don't refetch every render to avoid hammering the API.\n  const stage = flow.state.status;\n  useEffect(() => {\n    if (stage !== 'resource_selected') return;\n    flow.loadAvailability(range.from, range.to);\n    // Hook's loadAvailability is stable enough; range memoized above.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [stage, range.from, range.to]);\n\n  if (stage !== 'resource_selected' && stage !== 'slot_selected') return null;\n\n  if (children) {\n    return <div className={className}>{children(flow.slots, flow.pickSlot)}</div>;\n  }\n\n  if (flow.slots.length === 0) {\n    return <div className={className}>{emptyState}</div>;\n  }\n\n  return (\n    <div className={className}>\n      {flow.slots.map((slot) => (\n        <button\n          key={`${slot.resource_id}-${slot.start}`}\n          type=\"button\"\n          className={itemClassName}\n          onClick={() => flow.pickSlot(slot)}\n        >\n          {formatSlot ? formatSlot(slot) : defaultFormatSlot(slot)}\n        </button>\n      ))}\n    </div>\n  );\n}\n\n// ─── Details form ─────────────────────────────────────────────────────────────\n\nexport interface BookingFlowDetailsProps {\n  className?: string;\n  inputClassName?: string;\n  submitClassName?: string;\n  submitLabel?: ReactNode;\n  /** Show a phone field. Defaults to true. */\n  includePhone?: boolean;\n  /** Show a notes field. Defaults to true. */\n  includeNotes?: boolean;\n  /** Pre-fill values (e.g. signed-in customer). */\n  defaults?: Partial<{\n    customer_name: string;\n    customer_email: string;\n    customer_phone: string;\n    notes: string;\n  }>;\n  /** Custom error renderer. */\n  renderError?: (error: string) => ReactNode;\n}\n\nfunction DetailsForm({\n  className,\n  inputClassName,\n  submitClassName,\n  submitLabel,\n  includePhone = true,\n  includeNotes = true,\n  defaults,\n  renderError,\n}: BookingFlowDetailsProps) {\n  const flow = useBookingFlowContext();\n  const stage = flow.state.status;\n  const visible =\n    stage === 'slot_selected' ||\n    stage === 'details_filled' ||\n    stage === 'submitting';\n  if (!visible) return null;\n\n  const handle = (e: FormEvent<HTMLFormElement>) => {\n    e.preventDefault();\n    if (stage === 'submitting') return;\n    const fd = new FormData(e.currentTarget);\n    flow.setDetails({\n      customer_name: String(fd.get('customer_name') ?? '').trim(),\n      customer_email: String(fd.get('customer_email') ?? '').trim(),\n      customer_phone: includePhone\n        ? String(fd.get('customer_phone') ?? '').trim() || undefined\n        : undefined,\n      notes: includeNotes\n        ? String(fd.get('notes') ?? '').trim() || undefined\n        : undefined,\n    });\n    // submit() reads the latest state (it does its own setState callback peek).\n    void flow.submit();\n  };\n\n  const submitting = stage === 'submitting';\n\n  return (\n    <form className={className} onSubmit={handle} noValidate>\n      <input\n        type=\"text\"\n        name=\"customer_name\"\n        autoComplete=\"name\"\n        required\n        defaultValue={defaults?.customer_name ?? ''}\n        placeholder=\"Your name\"\n        className={inputClassName}\n      />\n      <input\n        type=\"email\"\n        name=\"customer_email\"\n        autoComplete=\"email\"\n        required\n        defaultValue={defaults?.customer_email ?? ''}\n        placeholder=\"Email\"\n        className={inputClassName}\n      />\n      {includePhone ? (\n        <input\n          type=\"tel\"\n          name=\"customer_phone\"\n          autoComplete=\"tel\"\n          defaultValue={defaults?.customer_phone ?? ''}\n          placeholder=\"Phone (optional)\"\n          className={inputClassName}\n        />\n      ) : null}\n      {includeNotes ? (\n        <textarea\n          name=\"notes\"\n          defaultValue={defaults?.notes ?? ''}\n          placeholder=\"Anything we should know? (optional)\"\n          className={inputClassName}\n        />\n      ) : null}\n      {flow.error ? (\n        renderError ? (\n          renderError(flow.error)\n        ) : (\n          <div role=\"alert\">{flow.error}</div>\n        )\n      ) : null}\n      <button type=\"submit\" disabled={submitting} className={submitClassName}>\n        {submitLabel ?? (submitting ? 'Booking…' : 'Confirm booking')}\n      </button>\n    </form>\n  );\n}\n\n// ─── Confirmation ─────────────────────────────────────────────────────────────\n\nexport interface BookingFlowConfirmProps {\n  className?: string;\n  /** Custom renderer. Receives the confirmed state. */\n  children?: (\n    state: Extract<SchedulingState, { status: 'confirmed' }>,\n  ) => ReactNode;\n}\n\nfunction Confirmation({ className, children }: BookingFlowConfirmProps) {\n  const flow = useBookingFlowContext();\n  if (flow.state.status !== 'confirmed') return null;\n  if (children) return <div className={className}>{children(flow.state)}</div>;\n\n  const { service, resource, slot, booking } = flow.state;\n  return (\n    <div className={className}>\n      <h3>You're booked.</h3>\n      <p>\n        {service.name}\n        {resource ? ` with ${resource.name}` : ''} on{' '}\n        <strong>{defaultFormatSlot(slot)}</strong>.\n      </p>\n      {booking.calendar_event_link ? (\n        <p>\n          <a\n            href={booking.calendar_event_link}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n          >\n            Open in Google Calendar\n          </a>\n        </p>\n      ) : null}\n      {booking.googleSkipped ? (\n        <small>(Calendar sync skipped — you'll still get an email.)</small>\n      ) : null}\n    </div>\n  );\n}\n\n// ─── Default formatters (no CSS framework imports allowed) ────────────────────\n\nfunction defaultFormatSlot(slot: SchedulingSlot): string {\n  try {\n    const start = new Date(slot.start);\n    return start.toLocaleString(undefined, {\n      weekday: 'short',\n      month: 'short',\n      day: 'numeric',\n      hour: 'numeric',\n      minute: '2-digit',\n    });\n  } catch {\n    return slot.start;\n  }\n}\n\nfunction formatPrice(cents: number): string {\n  return (cents / 100).toLocaleString(undefined, {\n    style: 'currency',\n    currency: 'USD',\n  });\n}\n\n// ─── Compound export ──────────────────────────────────────────────────────────\n\nexport const BookingFlow = Object.assign(BookingFlowRoot, {\n  Service: ServicePicker,\n  Resource: ResourcePicker,\n  DateTime: DateTimePicker,\n  Details: DetailsForm,\n  Confirm: Confirmation,\n});\n"]}